Fork and Execvp Not Executing Bash Commands From C correctly.

I had been looking at page 75 of this online book:

I've used the system function in C to call bash commands before, but wanted to learn this way too. The solution in the book worked perfectly. However, I tried changing the simple "ls -l /" they used to this one-liner:

lsof 2>&1 | grep log | awk '/openbox/ { print $10 }' | sed '1d'

If you're using a red hat based system instead of debian, you'll probably have to change the $10 to $9 in the above.

However, I'm getting the following errors:

# ./cbash2              
Done with main program.
pyro# lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
      Output information may be incomplete.
lsof: status error on 2>&1: No such file or directory
lsof: status error on |: No such file or directory
lsof: status error on grep: No such file or directory
lsof: status error on log: No such file or directory
lsof: status error on |: No such file or directory
lsof: status error on awk: No such file or directory
lsof: status error on '/openbox: No such file or directory
lsof: status error on { print $10 }': No such file or directory
lsof: status error on |: No such file or directory
lsof: status error on sed: No such file or directory
lsof: status error on '1d': No such file or directory
lsof 4.86
 latest revision: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/
 latest FAQ: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/FAQ
 latest man page: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/lsof_man
 usage: [-?abhKlnNoOPRtUvVX] [+|-c c] [+|-d s] [+D D] [+|-f[gG]] [+|-e s]
 [-F [f]] [-g ] [-i ] [+|-L [l]] [+m [m]] [+|-M] [-o [o]] [-p s]
[+|-r [t]] [-s [p:s]] [-S [t]] [-T [t]] [-u s] [+|-w] [-x [fl]] [--] [names]

Here is the code I'm working with now:

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

// Spawn a child process for the bash part later:
int spawn(char* program, char** arg_list)
{
  pid_t child_pid;

  // Duplicate this process:
  child_pid = fork();
  if (child_pid !=0)
    // This is the parent process.
    return child_pid;
  else {
    // Execute "program" which will call bash:
    execvp (program, arg_list);
    // execvp will only return if an error occurs
    fprintf (stderr, "an error occurred in execvp\n");
    abort();
  }
}

int main()
{
  //Argument list to pass bash commands:
  char* arg_list[] = {
     "lsof",    // argv[0], the name of the program.
     "2>&1",
     "|",
     "grep",
     "log",
     "|",
     "awk",
     "'/openbox/",
     "{ print $10 }'",
     "|",
     "sed",
     "'1d'",
     NULL       // Arg list must end with a null.
  };

  // Spawn the child process running bash.
  spawn("lsof", arg_list);

  printf("Done with main program.\n");

  return 0;
}

Any advice much appreciated!

First, note that you can cut a couple of processes out of your pipe line by using:

lsof 2>&1 | awk '/log/ && /openbox/ && c++ { print $10 }'

instead of:

lsof 2>&1 | grep log | awk '/openbox/ { print $10 }' | sed '1d'

Then a few comments about your C code:

  1. The fork() function returns 0 in the child (if a child is created successfully) and returns the PID of the child in the parent (if a child is created successfully), or returns -1 in the parent (if a child could not be created). Your code assumes that fork() never fails! Don't make that assumption.
  2. Operands like 2>&1 , | , and grep are shell directives; not operands to be passed to the lsof utility.
  3. Instead of executing lsof , you probably need to execute sh with arguments -c and lsof 2>&1 | awk '/log/ && /openbox/ && c++ { print $10 }' .
1 Like

To expand on what Don Cragun already said:

A commandline like

command1 | command2

is not "a process" but in fact the shell creates two such processes: one for "command1", one for "command2". You need to execute the procedure of using execvp() or fork() for both of them because both are processes in their own right. Only then you need a provision for the pipeline functionality.

A (shell-) pipeline is to connect STDOUT of the one process to the STDIN of the other. But this means there are already two process-environments with such file descriptors that can be connected. Here is a thread that might give you the picture: http://www.unix.com/programming/204135-trouble-pipes-chat-client-linux.html.

I hope that helps.

bakunin

1 Like

Thank you both! Its working now and the awk one-liner Don Cragun suggest is much cleaner. Thanks again!