Hi all,

I'm helping my girl out with her hw, (/me not knowing C at all, but someone has to help, right?)
I am supposed to read user input, and then try to execute a given command plus the arguments in every given $PATH.

I managed it all, but I get puzzled by the execv function, which I am supposed to be using, it doesn't produce any stdio, and I can't seem to find a way to make it work

a basic command

execv ("/bin/touch", "/tmp/test123", NULL);

doesn't do anything, neither do I see an exit code.
How do I use it?

Here's what I've got so far:

void get_path_exec(char cmd[50])
{
    char command[50] = {0} ; //full command with path included
    char *tempcmd = NULL;
    char *result = NULL; //path iterator
    char *com[] = {0}; //command with args, no path
    char *arg = NULL; //list of arguments, path and command excluded
    char delims[] = ":"; //path iteration delimiter
    char delims1[] = " "; //
    char * path; //variable to hold the full path delimited by ':'
    int pid, status; //process flow control
    int argcount = 0; //argumetn counter, where 0 is the command
   
    tempcmd=cmd; //assign to temp variable, so strtok doesn't ruin cmd
    path=getenv("PATH");
    if (path != NULL)
        printf("Path is %s \n", path);
    else exit(1);    
	
    arg = strtok( tempcmd, delims1 ); 
    com[0] = arg; //place the command into com[0]
    while( arg )
    {
	  argcount++;
	  arg = strtok(NULL,delims1);
	  com[argcount] = arg;
	  printf("arg is %s \n", arg);
	}
	argcount++;
	com[argcount]=NULL; //last value in args should be NULL, right?

    result = strtok( path, delims ); 
    while( result ) 
    {
       strcpy(command, "");
       strcat(command, result);
       strcat(command, "/");
       strcat(command,cmd);
       strcat(command,"\0"); //gather all parts of full path'd command - path, "/" and cmdname
//       printf("full command: %s \n\n", command);
       
       if (( pid = fork()) == 0 ) //create a new process
        {
            execv(command, com);
//below is a point I don't get
            printf("Something happened here, but what? %s \n Status is %d \n\n", result, status);
            exit(1);
        }
        else 
		{
           wait(&status);
           printf("Something else happened here %s \n Status after wait is %d", command, status);
           if (status != 0)
                printf("And something happened again? \n");
        }   
        result = strtok( NULL, delims );
    } 
}

The entire pid-fork-&status structure is just too vague for me, can anyone explain what happens on every step of the way?

Using execl, by the way, worked for me, but I have to use execv.


Thanks

Thanks, but the article uses execve, which is not an option for me in this case.

I was just looking for an explanation for line 46, 52 and 54 in my code

Thanks, but the article uses execve, which is not an option for me in this case.

I was just looking for an explanation for line 46, 52 and 54 in my code

First of all, what difference does this little 'e' make?

Second, it is not a C question, but rather Unix one. To help your GF you need first to understand the unix process model.

Third, thou shall not strtok.

Now, for your questions.
Line 46: a function from the exec family does not return if succeeds. If it does return it means it failed. This is what a printf is supposed to inform the user about. Keep in mind that the way the code is written, a status is garbage.
Line 52: a wait function waits for a child process to finish (not quite, but for the purpose of this assignment it is OK to assume so). Here the status tells how did it finish, and the code needs to analyze its value, which it half-heartedly attempts to do at line 54. Do 'man 2 wait' for the WIF* macros.

I hope I am not too late with this. There are many problems with this code I didn't even mention.

Edited 7 Years Ago by nezachem: n/a

Thanks Nezachem! I have changed quite a lot in the code, and now it does work, with a few exceptions unhandled (like segfaults when getting a permission denied), but those were not mentioned in the assignment anyway.

My main problem was with line 6, where I changed char *com[] to char* com[50], and that started working. I still have no idea why, this being far from my area of expertise, but the girl says she knows why, and that's good enough for me :)

strtok() was a requirement for this assignment, as well as execv(), can't do much about it.

I'm going to dwel on the man pages for wait() now, posting the current working version, in case you have the time to have a look and suggest improvements

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#define n 50;
#define str [n];
#define str_eq(s1,s2)  (!strcmp ((s1),(s2)))
void get_path(char *);
char *get_line(char *line, int size);


char *get_line(char *line, int size)
{
   if ( fgets(line, size, stdin) )
   {
      char *newline = strchr(line, '\n'); // check for trailing '\n' 
      if ( newline )
      {
         *newline =  '\0'; // overwrite the '\n' with a terminating null 
      }
   }
   return line;
}

void get_path(char cmd[50])
{
    char command[50] = {0} ; //full command with path included
    char *tempcmd = NULL;
    char *commandptr = NULL;
    char *result = NULL; //path iterator
	char* com[50]; //command with args, no path
	char *arg = NULL; //list of arguments, path and command excluded
	char delims[] = ":"; //path iteration delimiter
	char delims1[] = " "; //
	char * path; //variable to hold the full path delimited by ':'
	int status; //process flow control
	int argcount = 0; //argumetn counter, where 0 is the command
    int pid;   
    char *cmd1; //just the command, no args or path
   
    tempcmd=cmd; //assign to temp variable, so strtok doesn't ruin cmd
    path=getenv("PATH");
    if (path != NULL)
        printf("%s \n", path);
    else exit(1);    
	
    arg = strtok( tempcmd, delims1 ); 
    com[0] = arg; //place the command into com[0]
    cmd1=arg;
    while( arg )
    {
	  argcount++;
	  arg = strtok(NULL,delims1);
	  com[argcount] = arg;
	}
	argcount++;
	com[argcount]=NULL; //last value in args should be NULL
    
    result = strtok( path, delims ); 
    while( result ) 
    {
       strcpy(command, "");
       strcat(command, result);
       strcat(command, "/");
       strcat(command,cmd1);
       commandptr=command;
    
       if (( pid = fork()) == 0 ) 
        {
            printf("Executing Command: %s \t ", command);
            execv(commandptr, com);
            if (! strerror(errno) == "No such file or directory")
            {
                printf("\n\t %s\n", strerror(errno)); //print Error 
                exit(1);    
            }    
            
        }
        else 
		{
           wait(&status);
           printf("2"); 
           if (status != 0)
                printf ("3");

        }   
        printf("\n------------------------------\n");
        result = strtok( NULL, delims ); 
    } 
}

void main()
{
	char cmd[50];
	char leave[]="leave";

  	while (!str_eq(cmd,leave))
  	{
		printf("Enter command: ");
        get_line(cmd, sizeof cmd);   
		if (!str_eq(cmd,leave))  
			get_path(cmd);
  	}
	
    printf("bye\n");
}

Thanks Nezachem! I have changed quite a lot in the code, and now it does work, with a few exceptions unhandled (like segfaults when getting a permission denied), but those were not mentioned in the assignment anyway.

My main problem was with line 6, where I changed char *com[] to char* com[50], and that started working. I still have no idea why, this being far from my area of expertise, but the girl says she knows why, and that's good enough for me :)

strtok() was a requirement for this assignment, as well as execv(), can't do much about it.

I'm going to dwel on the man pages for wait() now, posting the current working version, in case you have the time to have a look and suggest improvements

I am glad I helped. Few comments:
1.

tempcmd=cmd; //assign to temp variable, so strtok doesn't ruin cmd

doesn't do what the comment claims. The actual string containing cmd remains the same - it just gets accessed via another pointer - and strtok happily ruins it. What you need to achieve the stated goal is to

strncpy(tmp, cmd, sizeof(cmd));

2. Your path-finding logics seems broken. The code sequentially tries to run the command from each path component. Obviously, most of the times the command is not there, resulting in a "No such file or directory" error. Through my crystal ball I can see that your test at line 72 does not catch it. This is because in C the '==' operator does not compare string contents; it only test that the pointers point to the same place. Obviously, the string returned by strerror lives in a different place than your string literal, which makes the test to fail.
You may want to compare strings with strcmp, or, even better, compare errno with ENOENT (#include <errno.h>). But do not bail out right away - the executable may be there down the path. In fact, you shall break the loop when the command was invoked successfully!

3. The code would be much cleaner if you separate a path tokenization from the command search. Indeed, the path needs to be tokenized just once, in main(), before the loop; then pass the array of path components to the command handler.

4. I don't know if it is assumed by the assignment, but it is always a good form to test for absolute paths: if your command starts with '/' or './' or '../', do not do a path search at all.

Comments
Great!

Thanks nezachem, I do feel that the fuctions aren't exactly set up the best possible way, and I'll add the leading "/" checking as well.


//the girl doesn't want me to give her a one line solution - something like

ls -s myshell /bin/sh

:)

This question has already been answered. Start a new discussion instead.