Cherish
Cherish

Reputation: 187

create a new process to execute ls command

I want to write a program which will create a new process and in that child process, it should execute the command: ls. In the meanwhile, the parent should wait for the child to die. However, my code does not work.

Please help me thank you very much!

int main()
{
    char * read;
    size_t size;

    getline(&read , &size , stdin);
    read[strlen(read)-1] = '\0';
    printf("%s\n" , read);
    int status;
    pid_t f;
    if((f = fork()) == 0)   
    {
        execvp(read , &read);
        exit(0);
    }
    else
    {   
        wait(&status);
    }       
}

Upvotes: 2

Views: 13748

Answers (3)

Nominal Animal
Nominal Animal

Reputation: 39396

Consider the following:

#define  _GNU_SOURCE
#define  _POSIX_C_SOURCE 200809L

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    char   *input_arg[2];
    char   *input_str = NULL;
    size_t  input_len = 0;
    char  **args;
    ssize_t len;
    size_t  n;
    pid_t   child, p;
    int     status;

    if (argc < 2) {

        /* No command line parameters. Read command from stdin. */
        len = getline(&input_str, &input_len, stdin);

        /* Find length excluding the newline at end. */
        if (len > (ssize_t)0)
            n = strcspn(input_str, "\r\n");
        else
            n = 0;

        if (n > (size_t)0) {

            /* Terminate input command before the newline. */
            input_str[n] = '\0';

        } else {

            fprintf(stderr, "No input, no command.\n");
            return 1;
        }

        input_arg[0] = input_str;
        input_arg[1] = NULL;
        args = input_arg;

    } else {

        /* Use command line parameters */
        argv[argc] = NULL;
        args = argv + 1;
    }

    child = fork();
    if (child == (pid_t)-1) {
        fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
        return 1;
    }

    if (!child) {
        /* This is the child process. */

        errno = ENOENT;
        execvp(args[0], args);

        fprintf(stderr, "%s: %s.\n", args[0], strerror(errno));
        exit(127);
    }

    do {
        p = waitpid(child, &status, 0);
    } while (p == (pid_t)-1 && errno == EINTR);
    if (p == (pid_t)-1) {
        fprintf(stderr, "Lost child process: %s.\n", strerror(errno));
        return 127;

    }
    if (p != child) {
        fprintf(stderr, "waitpid() library bug occurred.\n");
        return 127;

    }

    if (WIFEXITED(status)) {
       if (!WEXITSTATUS(status))
           fprintf(stderr, "Command successful.\n");
       else
           fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
       return WEXITSTATUS(status);
    }

    if (WIFSIGNALED(status)) {
        fprintf(stderr, "Command died by signal %s.\n", strsignal(WTERMSIG(status)));
        return 126;
    }

    fprintf(stderr, "Command died from unknown causes.\n");
    return 125;
}

The above uses the command line parameters if specified, otherwise it reads one from the standard input. Because the standard input is not tokenized, you can only supply the command name, no parameters. If you enlarge the input_arg[] array into

    char   *input_arg[4];

and modify the assignment into

    input_arg[0] = "/bin/sh";
    input_arg[1] = "-c";
    input_arg[2] = input_str;
    input_arg[3] = NULL;
    args = input_arg;

then the input string will be processed using the /bin/sh shell, just like popen() does.

You can also use len = getdelim(&input_str, &input_len, '\0', stdin); and remove the input_str[n] = '\0'; assignment to allow multiline input; the shell should handle those fine, as long as it is short enough to fit in the command line argument buffer (maximum length depends on your OS).

The rules how shells split input into separate commands and parameters are rather complex, and you should not try to emulate them. Instead, find a simple way for the user to specify the parameters separately (like the command-line parameter case), or use the shell to do it for you. If you don't do any splitting, you will probably need to remove the newline at the end of the input line.

The point to note is that for execvp(file, args), args[0] is the name the application sees (as $0 or argv[0]), and args[1] is the first parameter. Each parameter is terminated by NUL (\0) just like strings are normally in C, and the args pointer array must end with a NULL pointer. If there are no parameters, then args[1] == NULL.

Upvotes: 1

Jeegar Patel
Jeegar Patel

Reputation: 27230

why dont you just use system command...

#include <stdio.h>
#include <stdlib.h>


int main ()
{
  int i;
  printf ("Executing command ls...\n");
  i=system ("ls");
  printf ("The value returned was: %d.\n",i);
  return 0;
}

Update:

#include  <stdio.h>
#include  <sys/types.h>
#include <stdlib.h>

void  main(void)
{
     pid_t  pid;

     pid = fork();
     if (pid == 0) // this is child process
     {
      int i;
      printf ("Executing command ls...\n");
      i=system ("ls");
      printf ("The value returned was: %d.\n",i);
     }
     else // this is paraent process 
     {
      int status=0
      wait(&status);
      printf ("Child process is returned with: %d.\n",status);
      }
}

Upvotes: 0

hmjd
hmjd

Reputation: 122001

From man 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.

You need to use an array of char* and set the last element to NULL.

I am unsure what the getline() is reading but I guess it is the directory to be lsd. The first argument to execvp() should be ls and the second argument the array of char*.

Upvotes: 3

Related Questions