While I think I have the grasp on how fork()
, exec()
, wait()
and pid
work in C, I have yet to find a way how to run a personal program from within a program.
Here's my code:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h> /* for fork() */
#include<sys/types.h> /* for pid_t */
#include<sys/wait.h> /* fpr wait() */
int main(int argc, char* argv[])
{
char fileName[255];
pid_t pid;
switch (pid = fork()) {
case -1: //Did not fork properly
perror("fork");
break;
case 0: //child
execv(fileName[0],fileName);
puts("Oh my. If this prints, execv() must have failed");
exit(EXIT_FAILURE);
break;
default: //parent
//Infinite Loop
while (1) {
printf(" %s > ", argv[0]);
scanf("%s", fileName); // gets filename
if (fileName[0] == '\0') continue;
printf("\n Entered file: %s",fileName); // prints the fileName
waitpid(pid,0,0); /* wait for child to exit() */
break;
}
}
return 0;
}
My questions are the following:
I want to take a string as an input and I want to limit its scope to 255 characters. Is char fileName[255]
and then scanf("%s", fileName);
the way to go? Should I use getLine()
or some other function instead?
Let's say that the input is taken correctly. How do I execute say an existing hello world program. Will the input be stored in *argv[]
? I found out that in a different program I could use
static char *argv[] = { "echo", "Foo is my name." , NULL };
execv("/bin/echo", argv);
in order to echo "Foo is my name.". Can I do something similar with a helloWorld program?
You're passing a single character as the command name, and the name string as the start of a list of arguments — as if the prototype for execv()
were .int execv(char cmd, char *args)
The actual prototype is: int execv(char *cmd, char **args)
, so you need:
char *args[2] = { fileName, 0 };
execv(args[0], args);
I assume you set fileName
to a meaningful value somewhere — that isn't shown. For example, it might be "./local_program"
. It will be treated as the pathname of the executable.
If you want to read the name, then you can use fgets()
or getline()
, but you'll need to remove the newline:
if (fgets(fileName, sizeof(fileName), stdin) != 0)
{
fileName[strcspn(fileName, "\n")] = '\0';
…as before…
}
or:
char *fileName = 0;
size_t length = 0;
if (getline(&fileName, &length, stdin) != -1) /* Not EOF! */
{
fileName[strcspn(fileName, "\n")] = '\0';
…as before…
}
free(fileName);
The use of strcspn()
avoids having to special case overly long command lines such that there isn't a newline in the fileName
. Note that this does not attempt to split the input line into a command name and arguments at spaces or anything fancy like that. That's the next level of shell implementation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
char fileName[256];
if (fgets(fileName, sizeof(fileName), stdin) != 0)
{
fileName[strcspn(fileName, "\n")] = '\0';
char *args[129];
char **argv = args;
char *cmd = fileName;
const char *whisp = " \t\f\r\b\n";
/* Ugh — strtok()? OK while only handling white space separators */
char *token;
while ((token = strtok(cmd, whisp)) != 0)
{
*argv++ = token;
cmd = 0;
}
*argv = 0;
execv(args[0], args);
fprintf(stderr, "Oops!\n");
}
return 1;
}
I don't need to check for overflow of the args
array because 256 characters of input, minus terminating null, cannot be split to produce more than 128 single-character arguments, each separated from the adjacent ones by a single white space character. Using strtok()
is a temporary band-aid. As soon as you need to deal with real shell syntax (pipes, I/O redirections, quoted strings with spaces, etc), strtok()
is woefully the wrong tool. (It — strtok()
— is also the wrong function to use in any library function whatsoever. Use POSIX strtok_r()
on Unix or Microsoft's strtok_s()
on Windows if you must use a strtok()
-like function in library code.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With