I have this in my parent.c file
int main()
{
int n = 6;
int pid;
int status;
char* command = "./child";
for (i=1; i<=n; i++){
if((pid = fork()) == 0) {
execvp(command, NULL);
}
wait(&status);
}
My child.c file looks like this
int main(int argc, char *argv[]){
char *processnum = argv[0];
printf("This is the child %s\n", processnum);
return 0;
}
I basically just ran
gcc -o parent parent.c
gcc -o child child.c
./parent
This prints outs "This is the child (null)" 6 times, which is what I expect. But I want to be able to pass a parameter as I run child, in this case the process number.
So I changed my parent.c to look like this
for (i=1; i<=n; i++){
if(i == 1){
char* args = "1";
}
if(i == 2){
char* args = "2";
}
if(i == 3){
char* args = "3";
}
if(i == 4){
char* args = "4";
}
if(i == 5){
char* args = "5";
}
if(i == 6){
char* args = "6";
}
if((pid = fork()) == 0) {
execvp(command, args);
}
wait(&status);
}
What I thought would happen is that my program would print "This is the child 1", "This is the child 2" etc...
However, what actually happened is that the program seemed to run parent.c numerous times (I put a print statement at the start of parent.c and the output printed that statement like 20 times) instead of child.c
Can anyone explain why this is happening? Is there another way I can pass a parameter to child.c?
Thanks
here is the critical excerpt from the man page for execvp()
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.
There are several issues with your program as it stands. First, as described by others, the second argument to execvp is a char ** type ... it ends up as the argv in the exec'ed program.
Second, the
if(i == 1){
char* args = "1";
}
...
code sets a variable args whose scope ends at the next line. You must declare the args in a scope in which you want to use it.
Third, converting a number to a string like this is very limited and tedious! You would do better using the standard C function sprintf (or even better snprintf).
Here is an updated parent.c:
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int n = 6;
int pid;
int status;
char* command = "./child";
char *child_args[2];
char number_buff[32]; // more than big enough to hold any number!
child_args[1] = NULL;
for (int i=1; i<=n; i++){
sprintf(number_buff, "%d", i);
child_args[0] = number_buff;
if((pid = fork()) == 0) {
execvp(command, child_args);
}
wait(&status);
}
return 0;
}
Can anyone explain why this is happening? Is there another way I can pass a parameter to child.c?
You should be passing a char* array to execvp, not a char*. As mentioned in the comments, the last element of the array should be a null pointer. The following should work:
char* args[] = {"1\0", NULL};
execvp(command, args);
I'm guessing that execvp is failing since it can't dereference args as a char**, and thus the forked process is continuing the loop as a parent. You should be checking the return value of execvp to see that the function call worked.
Also, command and args should be null-terminated. I suggest using the function itoa to convert an int to a c string.
Related
Working on a project for a class. We're supposed to write a C shell. I've found a bunch of good examples, but for the life of me, I can't get my version to generate any output.
It keeps printing the prompt, but nothing else.
I've followed along with some examples near-verbatim trying to fix this, but still nothing.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/wait.h>
/******************************************
#brief Fork a child to execute the command using execvp. The parent should wait for the child to terminate
#param args Null terminated list of arguments (including program).
#return returns 1, to continue execution and 0 to terminate the MyShell prompt.
******************************************/
int execute(char **args)
{
pid_t pid;
int status;
if ((pid = fork()) < 0)
{
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0)
{
if (execvp(*args, args) < 0) {
printf("*** ERROR: exec failed\n");
exit(1);
}
}
else
{
while (wait(&status) != pid)
;
}
return 0;
}
/******************************************
#brief gets the input from the prompt and splits it into tokens. Prepares the arguments for execvp
#return returns char** args to be used by execvp
******************************************/
char** parse(void)
{
//Get the string and store it. Remove newline.
char strIn[255];
printf("MyShell>");
fgets(strIn, sizeof(strIn), stdin);
strIn[strlen(strIn)-1]='\0';
//Array, token, counters.
char *args[20];
char *tok;
int i = 0;
int count = 0;
//Allocate array.
for(i = 0; i < 20; i++)
{
args[i] = (char*)malloc(20 * sizeof(char));
}
//Fill in args[0]
tok = strtok(strIn, " ");
strcpy(args[count++], tok);
//Fill in array with tokens.
while (tok != NULL)
{
tok = strtok(NULL, " ");
if (tok == NULL)
break;
strcpy(args[count++], tok);
}
//Null terminate.
args[count] = NULL;
return args;
}
/******************************************
#brief Main function should run infinitely until terminated manually using CTRL+C or typing in the exit command
It should call the parse() and execute() functions
#param argc Argument count.
#param argv Argument vector.
#return status code
******************************************/
int main(int argc, char **argv)
{
bool run = true;
while(run)
{
char** argArr = parse();
execute(argArr);
}
return 0;
}
The output, regardless of what I do, is:
MyShell>
MyShell>
MyShell>
Can someone tell me where I went wrong?
parse() returns a pointer to the local array args. Since the lifetime of args ends when parse() returns, any attempt to use the return value of parse() is undefined behavior. You should allocate that array with malloc() instead (and free it later!).
What happens in my test is that the compiler notices that the return value of parse() can't legally be used (and it gives a warning!! which you should read and pay attention to!!), so it just returns NULL instead. When the child dereferences this pointer as *args to get the first argument for execvp, it segfaults and dies without calling execvp(). You could detect this if you checked the status returned by wait(), but you don't. So it just looks as if the child didn't do anything.
Oh, bonus bug: when end-of-file occurs on stdin (e.g. if you hit Ctrl-D), the string returned by fgets() will be empty and strIn[strlen(strIn)-1]='\0'; will store a null byte out of bounds. You need to test the return value of fgets().
I am working on a home made shell (very simple shell). I have decided to take the route of using execvp as my path is not a changeable element for my shell. I am running into an issue with coming up with the logic on how to fork and exec multiple processes at once.
My program should work with a command as such:
ls ; echo hello ; cat shell.c
Where each ";" indicates that we would like to run these processes at once simultaneously. So on our terminal output we should get a mix of these commands working at once.
To elaborate I'd like to explain how my program works:
A. Intake full command line into char array with a grab line function
B. Split the char array received from the last function by delimiters and place into an array of char arrays (pointer to pointer).
C. If one of the elements in our array of char arrays is ";" we can assume that multi commands are necessary. This is where I have trouble.
I have gotten as far as to know exactly how many processes I need to fork and such, but I cannot seem to wrap my head around how to pass all of these functions plus their arguments to the execvp function at once. Should I use a temp array? I know this shouldn't be this complicated but for some reason I cannot figure it out. I'm submitting my launch function below, which intakes an array of char arrays and executes accordingly based on my "multiCommand" variable which is set when multi commands are needed (by my split line function)
int launch(char **args){
pid_t pid;
int status;
int i = 0;
if(strcmp(args[0], "quit") == 0){
exit(EXIT_SUCCESS);
}
if(strcmp(args[0], ";") != 0){
printf("Essential Command Found : %s\n", args[0]);
numFork++;
}
if(multiCommand == 1){
//Handle Multicommands here
printf("Multi Commands Handling Here\n");
for(; i < elements - 1; i++){
if(strcmp(args[i], ";") == 0){
if((i + 1) < elements){
printf("Essential Command Found : %s\n", args[i + 1]);
numFork++;
}
}
}
//This is where I need to figure out what to do
printf("Fork: %d times\n", numFork);
}else if (multiCommand == 0){
pid = fork();
if(pid == 0){
execvp(args[0], args);
}else{
wait(&status);
}
}
multiCommand = 0;
elements = 0;
return 1;
}
The general idea would be to have a for loop over the different commands and fork each of them.
E.g.
for(int i = 0; i < commandCount; i++) {
int pid = fork();
if(pid == 0) { //this is the child (don't forget to check for errors and what-not)
execCommand(all, of, the, info, needed);
}
}
You can easily get the different commands using strtok().
Here's an example:
#include <string.h>
#include <stdio.h>
int main() {
char input[] = "abc;def;ghi";
char *token = strtok(input, ";");
while(token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ";");
}
return 0;
}
Output:
abc
def
ghi
The final function will look something like this:
char *token = strtok(input, ";");
while(token != NULL) {
int pid = fork();
if(pid == 0) {
//token is one command
//parse the different parts here
execCommand(args, to, exec);
}
token = strtok(NULL, ";");
}
I have an assignment, to create a simple linux shell using exec() that runs basic commands (e.g ls, ps) without arguments. When i run the code the execv is not working.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char argv[100];
char* path= "/bin/";
char progpath[20];
while(1)
{
printf("My shell>> ");
gets(argv);
if(strcmp(argv, "exit\n")==0){
break;
}
strcpy(progpath, path);
strcat(progpath, argv);
for(int i=0; i<strlen(progpath); i++){
if(progpath[i]=='\n'){
progpath[i]='\0';
}
int pid= fork();
if(pid==0){
execvp(progpath,argv);
exit(1);
}else{
wait(NULL);
}
return 0;
}
}
}
gets(argv) is expecting a char array, not a pointer to a array of char arrays.
change
char* argv[100];
to
char argv[100];
And then
strcat(progpath, argv[0]);
to
strcat(progpath, argv);
Note also that gets etc, is assuming you are not going to provide too many characters to fit in the array, so if the user enters a value that will be more than the 14 characters then progpath will overflow.
You are missing includes for fork, wait etc - likely to be
#include <sys/types.h>
#include <unistd.h>
After that, why for(int i=0; i<strlen(progpath); i++) and run the execvp each character of the progpath ? I assume you are meaning to have to the closing brackets before then.
for(int i=0; i<strlen(progpath); i++){
if(progpath[i]=='\n'){
progpath[i]='\0';
}
}
int pid= fork();
execva expects an array of char arrays, which is probally why you decided to use char *argv[] originally, but is not valid now - use one of the execl type functions instead.
Lastly, there is a chance that ls is a bashonly command - not a real command, so may not work anyway.
use
argv is declared as an array of unallocated strings and is used as an allocated string. As a first step you should remove the * in char* argv[100];.
Here is the general problem:
The program must fork() and wait() for the child to finish.
The child will exec() another program whose name is INPUT by the user.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
int status;
char input[BUFSIZ];
printf(">");
scanf("%s",input);
char *args[] = {"./lab1"};
pid_t pid = fork();
if(pid==0){
execvp(args[0],args);
}else if(pid<0){
perror("Fork fail");
}else{
wait(&status);
printf("My Child Information is: %d\n", pid);
}
return 0;
}
My problem is getting the user to input a program name to run (at the ">" prompt) and getting that input into execvp (or another exec() function if anyone has any ideas)
I'm going to hold off lambasting you for using scanf("%s") for now, though you should be aware it's really not robust code.
Your basic task here is going to be taking a character array entered by the user and somehow turning that into an array of character pointers suitable for passing to execvp.
You can use strtok to tokenise the input string into tokens separated by spaces, and malloc/realloc to ensure you have enough elements in an array to store the strings.
Alternatively, since you already have a potential buffer overflow issue, it may be good enough to just use a fixed size array.
For example, the following program shows one way of doing this, it uses a fixed string echo my hovercraft is full of eels and tokenises it to be suitable for execution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static char *myStrDup (char *str) {
char *other = malloc (strlen (str) + 1);
if (other != NULL)
strcpy (other, str);
return other;
}
int main (void) {
char inBuf[] = "echo my hovercraft is full of eels";
char *argv[100];
int argc = 0;
char *str = strtok (inBuf, " ");
while (str != NULL) {
argv[argc++] = myStrDup (str);
str = strtok (NULL, " ");
}
argv[argc] = NULL;
for (int i = 0; i < argc; i++)
printf ("Arg #%d = '%s'\n", i, argv[i]);
putchar ('\n');
execvp (argv[0], argv);
return 0;
}
Then it outputs the tokenised arguments and executes it:
Arg #0 = 'echo'
Arg #1 = 'my'
Arg #2 = 'hovercraft'
Arg #3 = 'is'
Arg #4 = 'full'
Arg #5 = 'of'
Arg #6 = 'eels'
my hovercraft is full of eels
For this Shell program i'm using the functions strtok (see fragmenta.h code) to parsing a string which is introduced by user.
I need to remove the blanks with strotk function and introduce those on a struct of an array of pointers. This are made in fragmenta.h
In the main program (shell.c), is necessary to introduce the string, this one is passed to fragmenta and stored on char **arg. After that, i use the execvp function to execute the command.
The problem is that the program store the whole command, but only execute the first individual command. For example, if we introduce "ls -al", only execute the ls command so i understand that is a problem on the pointer.
Main program shell.c
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include "fragmenta.h"
//
char cadena[50];
int pid;
int i, status;
char **arg;
pid_t pid;
//
main()
{
printf("minishell -> ");
printf("Introduce the command \n");
scanf("%[^\n]", cadena);
if (strcmp(cadena, "exit") == 0)
{
exit(0);
}
else
{
pid = fork();
if (pid == -1)
{
printf("Error in fork()\n");
exit(1);
}
else if (pid == 0) //child proccess
{
arg = fragmenta(cadena);
if (execvp(*arg, arg) < 0) /* execute the command */
{
printf("*** ERROR: exec failed\n");
exit(1);
}
}
else /* for the parent: */
{
while (wait(&status) != pid);
}
}
}
int len;
char *dest;
char *ptr;
char *aux;
char **fragmenta(const char *cadena)
{
//
char *token;
int i = 0;
//
len = strlen(cadena);
char *cadstr[len + 1];
dest = (char *)malloc((len + 1) * sizeof(char));
strcpy(dest, cadena);
//printf("Has introducido:%s\n",dest);
token = strtok(dest, " ");
while ( token != NULL)
{
cadstr[i] = malloc(strlen(token) + 1);
strcpy(cadstr[i], token);
token = strtok(NULL, " ");
i++;
}
*cadstr[i] = '\0';
ptr = *cadstr;
i = 0;
while (cadstr[i] != NULL)
{
//printf("almacenado: %s\n",cadstr[i]);
i++;
}
return &ptr;
}
You've got at least two problems.
The first one is this:
ptr=*cadstr;
You've gone through all that trouble to create an array of arguments, and then you just copy the first argument and return a pointer to that copy.
You could just get rid of ptr and return cadstr, except that it's a local variable, so as soon as the function returns, it can be overwritten or deallocated.
Since you're storing everything else in the universe as globals for some reason, the obvious fix to that is to make cadstr global too. (Of course you can't use a C99 runtime-length array that way, but since you've written your code to guarantee a buffer overrun if the input is more than 50 characters, you can safely just allocate it to 50 strings.)
A better solution would be to initialize a new array on the heap and copy all of cadstr into it. Or just initialize cadstr on the heap in the first place.
Second, you never append a NULL to the end of cadstr. Instead, you do this:
*cadstr[i] = '\0';
That leaves the last element in cadstr pointing to whatever uninitialized pointer it was pointing to, but modifies the first byte of whatever that is to be a 0. That could corrupt important memory, or cause a segfault, or be totally harmless, but the one thing it can't do is set cadstr[i] to point to NULL.
When you check this:
i = 0;
while (cadstr[i] != NULL)
i++;
… you only get out of that loop because of luck; you read right past the end of the allocated array and keep going until some other structure or some uninitialized memory happens to be sizeof(void*) 0s in a row.
And when you pass the same thing to execvp, who knows what it's going to do.
You're also declaring main without a prototype, which is deprecated, so you'll probably get a warning for it from any compiler that accepted the rest of your code. To fix that, just do int main().