External Processes Without Pipes

The previous section demonstrated an easy method to create a pipe to an external process and either read its output or feed it input. It often happens, however, that C programs need only to invoke another process, without using a pipe. The standard C library provides the system(3) function for this purpose.

#include <stdlib.h>

int system(const char *command);

In general, there are two ways to use the system(3) function: with a null argument or with a non-null command string argument.

Almost all systems document the fact that when system(3) is called with a null command pointer, the function call checks on the availability of the shell that would normally be used to carry out the command (system(3) for HPUX-11 does not mention this feature). The shell is considered available if it exists and is executable. If the shell is available, system(3) returns non-zero to indicate true. Otherwise, 0 indicates that no shell is available.

When the argument command is not a null pointer, it is expected to point to a null terminated C string containing a shell command to be executed as a child process. The function system(3) does not return until the indicated command has completed. The return status for this type of system(3) call is somewhat complicated, and is explained in full in Table 18.1 later in the chapter.

The shell program that is checked or invoked by the system(3) call varies somewhat with the UNIX platform. The following gives a partial list:

FreeBSD release 3.4 /bin/sh
SGI IRIX 6.5 /sbin/sh
HPUX-11 /usr/bin/sh
UnixWare 7 $SHELL or /bin/sh
Solaris 8 (native) /usr/bin/sh
Solaris 8 (standard) /usr/bin/ksh
IBM AIX 4.3 /usr/bin/sh
IBM AIX 4.3 (trusted) /usr/bin/tsh
Linux /bin/sh

The actual shell used on some platforms depends upon certain conditions. UnixWare 7 looks for the existence of the environment variable SHELL and uses that pathname for the shell. Otherwise it falls back to the default of /bin/sh. The choice for Solaris 8 depends upon whether it was compiled and linked to a particular standard. IBM's AIX 4.3 has a Trusted Computing Base for certain file system objects. If this feature is installed and enabled, the trusted shell /usr/bin/tsh can be invoked under some circumstances. Linux normally has /bin/sh linked to the GNU bash(1) shell.

Note

On some platforms, the signals SIGINT and SIGQUIT are ignored for the duration of the system(3) call. Furthermore, the signal SIGCHLD may be blocked until system(3) returns. IBM AIX 4.3 and HPUX-11 document this behavior.


Warning

Calling system(3) from a set-user-ID program is dangerous. The system(3) function uses fork(2) and exec(2) to invoke the new shell, and consequently it is possible for a security leak to occur (the current effective user and group ID values are saved by exec(2)). The shell and the commands invoked are subject to environment variable settings such as PATH and SHELL.


Review Table 12.2 in Chapter 12, "User ID, Password, and Group Management," if you are unclear how the current effective user and group ID values are affected by the exec(2) family of functions.

Interpreting system(3) Return Values

The return value for system(3) is complex when the command string is not null. It requires care to arrive at the correct conclusion. Table 18.1 contains a summary of the return values from system(3) when the command argument is not null. The errno value must be cleared to zero before calling system(3) to use this table. This permits the distinction between a failure to start the command and a command returning an exit code of 127.

Table 18.1. The system(3) Function Return Values
Return Value Check errno Description
0 No The function call was successful launching the command and command exited with a 0 exit status.
-1 Yes An error has occurred. Check the value of errno to determine the reason for failure.
127 Maybe If errno was cleared to 0 prior to calling system(3) and it is not 0 after the call, then an error has occurred while starting the new process. Check errno for the reason that the process could not be started. Otherwise, if errno has remained 0, then command executed and has returned exit code 127.
1-126 No These are return codes from command that has executed.
128-255 No These are return codes from command that has executed.

Invoking Commands

To illustrate the system(3) function and its complex return values, a program has been provided in Listing 18.3.

Code Listing 18.3. smail.cā€”Example Program Using the system(3) Function
1:   /* smail.c */
2:
3:   #include <stdio.h>
4:   #include <stdlib.h>
5:   #include <unistd.h>
6:   #include <errno.h>
7:   #include <string.h>
8:   #include <pwd.h>
9:   #include <sys/types.h>
10:
11:  int
12:  main(int argc,char **argv) {
13:      struct passwd *pw = 0;      /* Password info */
14:      char cmd[256];              /* Command buffer */
15:      int rc;                     /* Command return code */
16:
17:      /*
18:       * Lookup our userid:
19:       */
20:      if ( !(pw = getpwuid(geteuid())) ) {
21:          fprintf(stderr,"%s: unknown userid
",strerror(errno));
22:          return 13;
23:      }
24:
25:      /*
26:       * Format command :
27:       */
28:      sprintf(cmd,"ps -l|mail -s 'PID %ld'%s",
29:          (long) getpid(),    /* Process ID */
30:          pw->pw_name);       /* User name */
31:
32:      /*
33:       * Run the command :
34:       */
35:      errno = 0;              /* Clear errno */
36:      rc = system(cmd);       /* Execute the command */
37:
38:      if ( rc == 127 && errno != 0 ) {
39:          /* Command failed to start */
40:          fprintf(stderr,"%s: starting system(%s)
",
41:              strerror(errno),cmd);
42:      } else if ( rc == -1 ) {
43:          /* Other errors occurred */
44:          fprintf(stderr,"%s: system(%s)
",
45:              strerror(errno),cmd);
46:      } else {
47:          printf("Command '%s'
  returned code %d
",cmd,rc);
48:          puts("Check your mail.");
49:      }
50:
51:      return 0;
52:  }

The smail.c program looks up your effective user ID in lines 20ā€“23. Then a command is formatted into character array cmd[] to list your current processes and email it to you (lines 28ā€“30). The actual process list and mailing occurs in lines 35 and 36, where cmd is carried out. Lines 38ā€“49 show how to make sense of the return code from system(3).

When the program in Listing 18.3 is compiled and executed under FreeBSD, the following results are obtained:

$ make smail
cc -c  -Wall smail.c
cc -o smail smail.o
$ ./smail
Command 'ps -l|mail -s 'PID 10424'ehg'
  returned code 0
Check your mail.
$

At this point, the program is telling you that mail has been sent. Now check your mail with the mail(1) command (the output lines from ps(1) have been shortened for readability):

$ mail
Mail version 8.1 6/6/93.  Type ? for help.
"/var/mail/ehg": 1 message 1 new
>N  1 ehg   Wed Jun 21 21:56  20/931   "PID 10424"
& 1
Message 1:
From ehg Wed Jun 21 21:56:26 2000
Date: Wed, 21 Jun 2000 21:56:26 -0400 (EDT)
From: "Earl H. Grey" <ehg>
To: ehg
Subject: PID 10424

  UID   PID  PPID CPU PRI NI  TIME    COMMAND
 1001 10200     1   0  10  0  0:00.05 -sh (sh)
 1001 10203 10202   0  10  0  0:00.20 -sh (sh)
 1001 10424 10203   1  10  0  0:00.01 ./smail
 1001 10425 10424   2  10  0  0:00.01 sh -c ps -l|mail -s 'PID 10424'
 1001 10426 10425   1  28  0  0:00.00 ps -l
 1001 10427 10425   2  28  0  0:00.00 sh -c ps -l|mail -s 'PID 10424'

& d 1
& q
$

Your message content may vary somewhat from the message shown here. The timing is always such that it appears that two processes are executing the command sh -c ps -l|mail -s 'PID 10424'. In fact, what you see here is a snapshot of how things appear after fork(2) has created a new process, but before it has been able to perform an exec(2) call. The following explains what you see in the message:

  • Process 10424 is the initial ./smail program that was started.

  • Process 10425 is the shell process that has been started because of the system(3) call. This shell process must execute the command ps -l|mail -s ehg.

  • Process 10426 is the ps(1) command that has been started by the shell (note its parent process ID is 10425).

  • Process 10427 was to be the mail(1) command. However, it shows the command line of the shell because the shell had not yet carried out the call to exec(2) before the ps(1) command took a snapshot. Had the exec(2) call taken place, you would have seen the command mail -s 'PID 10424'ehg instead.

If you are unfamiliar with fork(2) and exec(2) this may be difficult to understand. Chapter 19 will cover fork(2) and exec(2) in detail.

Scrutinizing the system(3) Function

Although the system(3) function is quite easy to use, it has drawbacks. One of them is the complex set of return values when the command string is not a null pointer (review Table 18.1).

The system(3) call is also considered a security risk, especially for programs that are setuid(2) or setgid(2). If this applies to your application, you would be wise to shun the system(3) call, and carefully craft fork(2) and exec(2) calls directly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.29.89