C execvp usage, what am I missing? - c

I am trying to make a program that uses shell commands for a school project. I am able to compile and run the code without errors, but when I input a command such as ls, noting happens. I think I am missing something with the execvp.
I have been trying to use various configurations of inputs.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>
#include <stdlib.h>
#define MAX_LINE 80 /* Maximum length of a command */
int main(void) {
char args[MAX_LINE / 2 + 1]; /* command line arguments */
int should_run = 1; /* Flag to determine when to exit the program */
pid_t pid;
char *myCmd;
char *tokens[40];
pid = fork();
while (should_run) {
printf("osh>");
fflush(stdout);
scanf("%s", myCmd);
int i = 0;
char *token = strtok(myCmd, " ");
while (token != NULL) {
tokens[i] = token;
i++;
token = strtok(NULL, " ");
}
if (pid < 0) {
printf("Fork Failed\n");
//exit(1);
} else
if (pid == 0) {
execvp(tokens[0], tokens);
//exit(1);
} else {
if (strcmp(tokens[i - 1], "&")) {
waitpid(pid, NULL, 0);
}
}
}
return 0;
}

In addition to the serious issues with command input and formatting of execvp() arguments called out in another answer, you are fork()ing in the wrong place. You need to fork() a new child for each command the shell runs, so the fork call should go inside the loop. Moreover, it should go after printing the prompt and reading a command, because you want only the parent to do those things, not the children. And you do not need both parent and child to parse the command. Although you could rely on the child to do that, it would be more conventional to do it in the parent. But certainly not in both.
It is conceivable that your program would still seem to work despite the other errors (though that would not be any justification for failing to fix them), but the fork() placement issue is absolutely breaking. It will cause unexpected extra prompting, and the shell will never attempt to execute more than one command (though you might not notice, in part because you do not check the return values of several key function calls).

Writing to unallocated memory:
char* myCmd;
only allocates memory for the pointer. The pointer has an indeterminate value i.e. it may be pointing to anything and
any attempt to dereference a pointer with a bad value would result in undefined behaviour.
Automatically and dynamically allocated objects are initialized only
if an initial value is explicitly specified; otherwise they initially
have indeterminate values (typically, whatever bit pattern happens to
be present in the storage, which might not even represent a valid
value for that type).
The subsequent call to scanf() then invokes undefined behaviour, because no memory was ever allocated for the string.
scanf("%s", myCmd);
Possible fix:
If no command is ever going to exceed 80 characters, as stated in this comment:
define MAX_LINE 80 /*Maximum length of a command*/
You can allocate an array[80] of char and then pass it to scanf().
And to limit input, you could specify a width specifier like so:
scanf("%79s", myCmd);
Note:
Don't use scanf(). Use fgets(). With scanf(), it will only grab the first whitespace separated token (e.g. for input of Hello World, scanf() will only return Hello). — #Craig Estey
The array of pointers to execvp shall be null-terminated:
The execv(), execvp(), and execvpe() functions provide an array of
pointers to null-terminated strings that represent the argument list
available to the new program. The first argument, by convention,
should point to the filename associated with the file being executed.
The array of pointers must be terminated by a NULL pointer.
Code is missing:
tokens[i] = NULL;
after the while loop.
Aside: A shell in C - Tutorial and Beej's guide to UNIX Interprocess Communication might help to elaborate on what #John pointed out in his answer.

Related

Double pointer as argument to execvp()

I am trying to execute execvp() using a custom **tokens double pointer as input, instead of argv[] on a "create a custom shell" assignment, like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
char *token;
char **tokens = malloc(sizeof(char*)*512); //512 is for the maximum input-command length
char *command=malloc(sizeof(char)*512);
int i = 0;
pid_t child_pid;
int status;
//***********take input from user*************************************
fgets(command,512,stdin);
//***********parse input*********************************************
token = strtok(command," \t");
while( token != NULL ) {
tokens[i]=token;
i ++;
token = strtok(NULL, " \t");
}
child_pid = fork();
if(child_pid == 0) {
/* This is done by the child process. */
execvp(tokens[0], tokens);
} else {
waitpid(child_pid, &status, WUNTRACED);
}
}
The problem is definately on this line:
execvp(tokens[0], tokens);
and I just can't understand why it can't be executed and print to my stdout.
I have tried this:
execvp("ls", tokens);
and it works just fine.
And this:
printf("%s\n", tokens[0]);
with the output being (according to the test input: ls ):
ls
You have several problems in your code, including:
The array of argument pointers passed to execvp() must be terminated by a null pointer. You do not ensure that.
The string obtained via fgets will include all characters up to and including the line's newline, if the buffer is large enough to accommodate it. You do not include the newline among your token delimiter characters, so for a one-word command ls, the command passed to execvp() is equivalent to "ls\n", not "ls". It is unlikely (but not impossible) that ls\n is an available command on your machine.
You do not check the return value of execvp(), or of any of your other functions, nor do you handle any errors. execvp() is special in that it returns only if there is an error, but you would have saved yourself some confusion if you had handled that case by emitting an error message.
After I correct the first two of those, your program successfully runs an "ls" command for me.
You need to allocate the memory with sizeof(char *).
char **tokens = malloc(sizeof(char *)*512);
^^----------->Size of char pointer
As of now you are allocating sizeof(char) thus invoking undefined behavior.
Also consider the first comment pointed by #n.m

Bad address error with execvp

I'm trying to make a shell "bosh>" which takes in Unix commands and keep getting a bad address error. I know my code reads in the commands and parses them but for some reason, I cannot get them to execute, instead, I get a "bad address" error.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>
#define MAX_LINE 128
#define MAX_ARGS 10
int main(){
pid_t pid;
char command[MAX_LINE]; /*command line buffer*/
char *commandArgs[MAX_ARGS]; /*command line arg*/
int i;
char *sPtr=strtok(command," ");
int n=0;
printf("bosh>");
fgets(command, MAX_LINE-1,stdin);
command[strlen(command)-1]='\0';
while(strcmp(command,"quit")!=0)
{
n=0;
sPtr=strtok(command," ");
while(sPtr&&n<MAX_ARGS)
{
sPtr=strtok(NULL," ");
n++;
}
commandArgs[0]=malloc(strlen(command)+1);
strcpy(commandArgs[0],command);
if(fork()==0)
{
execvp(commandArgs[0],commandArgs);
perror("execvp failed");
exit(2);
}
pid=wait(NULL);
printf("%s",">" );
fgets(command, MAX_LINE-1,stdin);
command[strlen(command)-1]='\0';
}
printf("Command (%d) done\n", pid);
return 0;
}
These two lines are the culprit:
commandArgs[0]=malloc(strlen(command)+1);
strcpy(commandArgs[0],command);
First of all, malloc(strlen(...)) followed by strcpy is what the POSIX function strdup already does. But then, you don't need to even copy the string - it is enough to just store the pointer to the original string into commandArgs[0]:
commandArgs[0] = command;
But then, how does execvp how many arguments the command is going to take? If you read the manuals carefully, they'd say something like:
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers MUST be terminated by a NULL pointer.
Your argument array is not NULL-terminated. To fix it, use
commandArgs[0] = command;
commandArgs[1] = NULL; // !!!!
(Then you'd notice that you'd actually want to assign the arguments within the strtok parsing loop, so that you can actually assign all of the arguments into the commandArgs array; and compile with all warnings enabled and address those, and so forth).
You initialize sPtr in its declaration, which you do not need to do because you never use the initial value. But the initialization produces undefined behavior because it depends on the contents of the command array, which at that point are indeterminate.
The array passed as the second argument to execvp() is expected to contain a NULL pointer after the last argument. You do not ensure that yours does.
You furthermore appear to drop all arguments to the input command by failing to assign tokens to commandArgs[]. After tokenizing you do copy the first token (only) and assign the copy to the first element of commandArgs, but any other tokens are ignored.

Segmentation fault when trying to declare an array of strings

In my program, I am trying to copy each argv[i] to keyword[i], but my program fails with a segmentation fault. What am I doing wrong?
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[])
{
//prototype
string keyword = "";
//int j;
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
keyword[i] = toupper(argv[1][i]);
printf("%i-- printing letters\n", keyword[i]);
}
}
As others have observed, you initialize variable keyword either as an empty string or as a pointer to an empty string literal, depending on the definition of type string. Either way, it is then valid to evaluate keyword[i] only for i equal to zero; any other value -- for read or write -- is out of bounds. Furthermore, in the latter (pointer to string literal) case, you must not attempt to modify the array keyword points to.
Note in particular that C does not automatically expand strings if you try to access an out of bounds element. Instead, an attempt to do so produces "undefined behavior", and a common way for that to manifest in such cases is in the form of a segmentation fault. You can view a segmentation fault as the system slapping down your program for attempting to access memory that does not belong to it.
Since you don't know a priori how long the argument string will be before you copy it, the most viable type for keyword is char *. I will use that type instead of string in what follows, for clarity.
If you indeed do want to make a copy of the argument, then by far the easiest way to do so is via the for-purpose function strdup():
char *keyword = strdup(argv[1]);
That allocates enough memory for a copy of its argument, including the terminator, copies it, and returns a pointer to the result. You are then obligated to free the resulting memory via the free() function when you're done with it. Having made a copy in that way, you can then upcase each element in place:
for (int i = 0, n = strlen(keyword); i < n; i++)
{
keyword[i] = toupper(keyword[i]);
printf("%c-- printing letters\n", keyword[i]);
}
Note, by the way, that the printf() format descriptor for a single character is %c, not %i. You must use that to print the characters as characters, rather than their integer values.
That's one of the simplest ways to write C code for what you're trying to do, though there are many variations. The only other one I'll offer for your consideration is to not copy the argument at all:
char *keyword = argv[1];
If you initialize keyword that way then you do not allocate any memory or make a copy; instead, you set keyword to point to the same string that argv[1] points to. You can modify that string in-place (though you cannot lengthen it), provided that you do not need to retain its original contents.
Before I wrap this up, I should also observe that your program does not check whether there actually is an argument. In the event that there is not (i.e. argc < 2), argv[1] either contains a null pointer (argc == 1) or is undefined (argc == 0; you're unlikely ever to run into this). Either way, your program produces undefined behavior in that case if it attempts to use argv[1] as if it were a pointer to a valid string. You should test for this case first off, and terminate with a diagnostic message if no program argument is available.
Your main problem: you're not allocating memory for your new string, (string keyword = "").
In C, every size that is not known at compilation time has to be dynamically allocated during run-time.
Also, you never check for missing parameters which may crash your program.
See code below for both fixes
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Usage: %s <word>\n", argv[0]);
return 1;
}
int length = strlen(argv[1]);
string keyword = malloc(length+1);
for(int i = 0, n = strlen(argv[1]); i < n; i++)
{
keyword[i] = toupper(argv[1][i]);
printf("%i-- printing letters\n", keyword[i]);
}
keyword[length]=0;
free(keyword);
}

scanf seg fault in C with char* array

I'm working my way through a book on operating systems, and some of the book's sample code is giving me a runtime segmentation fault. I'm somewhat new to C from Java, and I'm hoping someone can point me in the right direction.
The little program here is supposed to generate a simple shell, read a command in and fork a process to execute it. The problem is in the book's code itself, at the "scanf" function call; when I input something at the "osh>" prompt, I get a segmentation fault.
Form what I know about C, I think memory might need to be allocated for the args array, but since it's an array declared directly in the main function, I think I might not need to. I figure that if I did, it would be in the book's code.
Anyway, here's the code that generates the fault:
char* args[MAX_LINE/2 + 1]; /* command line (of 80) has max of 40 arguments */
int should_run = 1;
int i, upper;
while (should_run){
printf("osh>");
fflush(stdout);
scanf("%s", args); /* THIS CAUSES SEGFAULT */
char* localArgs[3];
char* pch;
/* ... */
Thanks in advance for the help. Learning memory management in C is quite the journey.
You are passing an array of pointers to scanf(), it expects an array of char.
An example of how to use scanf() correctly to scan a text string would be
char string[100];
if (scanf("%99s", string) == 1)
{
printf("scanned string: %s\n", string);
}
else
{
printf("error: unexepected error in `scanf()'.\n);
}
Read the link throughly to understand why I wrote this code like I did, if you do you will start to understand how scanf() works, and when you do you will start writing more robust programs, and probably stop using scanf() too.
char* args[MAX_LINE/2 + 1];
This is creating an array of size (MAX_LINE/2+1) of char pointers. If you want to use one of these char pointers as string, you must allocate them:
args[which_arg] = malloc(arg_max_length*sizeof(char));
And to read a text into it:
scanf("%s", args[which_arg]);

C program just exits after trying to write to stdout

so written a small function (part of a larger program) and when i run it and input "GET" it exits with a value of 1. to be honest i'm still grasping the concept of open read and write to stdout but not sure what i've done wrong here.
int input_arg()
{
MainStruct val; //variables are loaded from a config file to this structure
char *getInput;
char *fileInput;
FILE *loadfile;
char buffer[1024];
int n;
int defaultFile = val.def; //success.txt value read when fileparser.c is run
printf("http >> :");
fflush(NULL);
fscanf(stdin,"%s", getInput);
if (getInput == "GET")
{
loadfile = fopen(defaultFile, "r");
if (loadfile == NULL)
{
fprintf(stderr, "error loading default resource: PROGRAM WILL EXIT");
exit(0);
}
while ((n = read(loadfile, buffer, sizeof(buffer))) > 0) //reads file (not sure this should be a while loop)
{
if((write(STDOUT_FILENO, buffer, n)) < 0) //writes to stdout
{
perror("failed to display file to output");
close(loadfile);
exit(1);
}
}
}
}
for compiling purposes the val.def pointer is a string as below
char defaultFile = "success.txt";
unsure of what i am missing here. tried changing the structure pointer to a simple char string to see if it was anything there but didn't actually make any difference. i think the problem is with the while loop...i don't think it should be there, but i have yet to find an example where a while loop ISN'T used in a write to stdout scenario.
thanks
It crashes because you have not allocated any memory for getInput to point at. So the program will crash when it attempts to follow the pointer, which will not point to anything useful.
Either allocate memory dynamically with for example malloc, or replace it with a static buffer.
Also, you may want to look at strcmp for comparing strings. Comparing strings in C with == will not compare the strings lexically, instead it will only compare the pointers pointing at them.
fscanf(stdin,"%s", getInput);
getInput is never initialized or allocated memory.Fix it by allocating memory
getInput = malloc(200);
Your program has serious issues, the most important one is that you are using fopen() with read() and that is wrong.
The read() function takes and int as first parameter, which is a file descriptor tha you can create via the open() function, and not fopen() which returns a FILE * object, so change[1]
FILE *loadFile;
to
int loadFile;
and
loadFile = fopen(defaultFile, "r");
to
loadFile = open(defaultFile, O_RDONLY);
and then to check for failure
if (loadFile == -1) /* it failed to open check errno? perhaps... */
you must enable compiler warnings to prevent this kind of mistake, because the first parameter of read() in your program is of incompatible type.
The fscanf() function expects a valid pointer for each "%s" specifier, you are passing an unintialzed pointer to it, dereferencing it inside of scanf() is undefined behavior.
You need to allocate space for it, something like this should work
char inputBuffer[100];
if (fscanf(stdin, "%99s", inputBuffer) != 1)
thereWasAProblemGettingInput_DoNotUse_inputBuffer_InTheCodeThatFollows();
Note that:
I used inputBuffer as a name for the variable, though this doesn't affect the program execution or compilation at all, the readability matters.
Used "%99s" to prevent buffer overflow.
Checked the value returned by fscanf() to make sure that the inputBuffer has valid data and was properly initialized.
String comparison in c is not like in many other languages, in your code
if (getInput == "GET")
is comparing the addresses of getInput and the string literal "GET", which will not be the same unless you make getInput point to "GET", since you want to compare the contents you need
if (strcmp(inputBuffer, "GET") == 0)
instead, and do not forget to include the string.h header.
[1]Note that loadFile is also a bad choice for a variable name, it feels like a function name, inputFile would be more appropriate.

Resources