The Open Group Base Specifications Issue 6
IEEE Std 1003.1, 2004 Edition
Copyright © 2001-2004 The IEEE and The Open Group
A newer edition of this document exists here

B.3 System Interfaces

See the RATIONALE sections on the individual reference pages.

B.3.1 Examples for Spawn

The following long examples are provided in the Rationale (Informative) volume of IEEE Std 1003.1-2001 as a supplement to the reference page for posix_spawn().

Example Library Implementation of Spawn

The posix_spawn() or posix_spawnp() functions provide the following:

The posix_spawn() or posix_spawnp() functions do not cover every possible use of the fork() function, but they do span the common applications: typical use by a shell and a login utility.

The price for an application is that before it calls posix_spawn() or posix_spawnp(), the parent must adjust to a state that posix_spawn() or posix_spawnp() can map to the desired state for the child. Environment changes require the parent to save some of its state and restore it afterwards. The effective behavior of a successful invocation of posix_spawn() is as if the operation were implemented with POSIX operations as follows:

#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

/* #include <spawn.h> */ /*******************************************/ /* Things that could be defined in spawn.h */ /*******************************************/ typedef struct { short posix_attr_flags; #define POSIX_SPAWN_SETPGROUP 0x1 #define POSIX_SPAWN_SETSIGMASK 0x2 #define POSIX_SPAWN_SETSIGDEF 0x4 #define POSIX_SPAWN_SETSCHEDULER 0x8 #define POSIX_SPAWN_SETSCHEDPARAM 0x10 #define POSIX_SPAWN_RESETIDS 0x20 pid_t posix_attr_pgroup; sigset_t posix_attr_sigmask; sigset_t posix_attr_sigdefault; int posix_attr_schedpolicy; struct sched_param posix_attr_schedparam; } posix_spawnattr_t;
typedef char *posix_spawn_file_actions_t;
int posix_spawn_file_actions_init( posix_spawn_file_actions_t *file_actions); int posix_spawn_file_actions_destroy( posix_spawn_file_actions_t *file_actions); int posix_spawn_file_actions_addclose( posix_spawn_file_actions_t *file_actions, int fildes); int posix_spawn_file_actions_adddup2( posix_spawn_file_actions_t *file_actions, int fildes, int newfildes); int posix_spawn_file_actions_addopen( posix_spawn_file_actions_t *file_actions, int fildes, const char *path, int oflag, mode_t mode); int posix_spawnattr_init(posix_spawnattr_t *attr); int posix_spawnattr_destroy(posix_spawnattr_t *attr); int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *lags); int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags); int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, pid_t *pgroup); int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup); int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr, int *schedpolicy); int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy); int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr, struct sched_param *schedparam); int posix_spawnattr_setschedparam(posix_spawnattr_t *attr, const struct sched_param *schedparam); int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr, sigset_t *sigmask); int posix_spawnattr_setsigmask(posix_spawnattr_t *attr, const sigset_t *sigmask); int posix_spawnattr_getdefault(const posix_spawnattr_t *attr, sigset_t *sigdefault); int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr, const sigset_t *sigdefault); int posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); int posix_spawnp(pid_t *pid, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]);
/*****************************************/ /* Example posix_spawn() library routine */ /*****************************************/ int posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { /* Create process */ if ((*pid = fork()) == (pid_t) 0) { /* This is the child process */ /* Worry about process group */ if (attrp->posix_attr_flags & POSIX_SPAWN_SETPGROUP) { /* Override inherited process group */ if (setpgid(0, attrp->posix_attr_pgroup) != 0) { /* Failed */ exit(127); } }
/* Worry about thread signal mask */ if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGMASK) { /* Set the signal mask (can't fail) */ sigprocmask(SIG_SETMASK, &attrp->posix_attr_sigmask, NULL); }
/* Worry about resetting effective user and group IDs */ if (attrp->posix_attr_flags & POSIX_SPAWN_RESETIDS) { /* None of these can fail for this case. */ setuid(getuid()); setgid(getgid()); }
/* Worry about defaulted signals */ if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGDEF) { struct sigaction deflt; sigset_t all_signals;
int s;
/* Construct default signal action */ deflt.sa_handler = SIG_DFL; deflt.sa_flags = 0;
/* Construct the set of all signals */ sigfillset(&all_signals);
/* Loop for all signals */ for (s = 0; sigismember(&all_signals, s); s++) { /* Signal to be defaulted? */ if (sigismember(&attrp->posix_attr_sigdefault, s)) { /* Yes; default this signal */ if (sigaction(s, &deflt, NULL) == -1) { /* Failed */ exit(127); } } } }
/* Worry about the fds if they are to be mapped */ if (file_actions != NULL) { /* Loop for all actions in object file_actions */ /* (implementation dives beneath abstraction) */ char *p = *file_actions;
while (*p != '\0') { if (strncmp(p, "close(", 6) == 0) { int fd;
if (sscanf(p + 6, "%d)", &fd) != 1) { exit(127); } if (close(fd) == -1) exit(127); } else if (strncmp(p, "dup2(", 5) == 0) { int fd, newfd;
if (sscanf(p + 5, "%d,%d)", &fd, &newfd) != 2) { exit(127); } if (dup2(fd, newfd) == -1) exit(127); } else if (strncmp(p, "open(", 5) == 0) { int fd, oflag; mode_t mode; int tempfd; char path[1000]; /* Should be dynamic */ char *q;
if (sscanf(p + 5, "%d,", &fd) != 1) { exit(127); } p = strchr(p, ',') + 1; q = strchr(p, '*'); if (q == NULL) exit(127); strncpy(path, p, q - p); path[q - p] = '\0'; if (sscanf(q + 1, "%o,%o)", &oflag, &mode) != 2) { exit(127); } if (close(fd) == -1) { if (errno != EBADF) exit(127); } tempfd = open(path, oflag, mode); if (tempfd == -1) exit(127); if (tempfd != fd) { if (dup2(tempfd, fd) == -1) { exit(127); } if (close(tempfd) == -1) { exit(127); } } } else { exit(127); } p = strchr(p, ')') + 1; } }
/* Worry about setting new scheduling policy and parameters */ if (attrp->posix_attr_flags & POSIX_SPAWN_SETSCHEDULER) { if (sched_setscheduler(0, attrp->posix_attr_schedpolicy, &attrp->posix_attr_schedparam) == -1) { exit(127); } }
/* Worry about setting only new scheduling parameters */ if (attrp->posix_attr_flags & POSIX_SPAWN_SETSCHEDPARAM) { if (sched_setparam(0, &attrp->posix_attr_schedparam) == -1) { exit(127); } }
/* Now execute the program at path */ /* Any fd that still has FD_CLOEXEC set will be closed */ execve(path, argv, envp); exit(127); /* exec failed */ } else { /* This is the parent (calling) process */ if (*pid == (pid_t) - 1) return errno; return 0; } }
/*******************************************************/ /* Here is a crude but effective implementation of the */ /* file action object operators which store actions as */ /* concatenated token-separated strings. */ /*******************************************************/ /* Create object with no actions. */ int posix_spawn_file_actions_init( posix_spawn_file_actions_t *file_actions) { *file_actions = malloc(sizeof(char)); if (*file_actions == NULL) return ENOMEM; strcpy(*file_actions, ""); return 0; }
/* Free object storage and make invalid. */ int posix_spawn_file_actions_destroy( posix_spawn_file_actions_t *file_actions) { free(*file_actions); *file_actions = NULL; return 0; }
/* Add a new action string to object. */ static int add_to_file_actions( posix_spawn_file_actions_t *file_actions, char *new_action) { *file_actions = realloc (*file_actions, strlen(*file_actions) + strlen(new_action) + 1); if (*file_actions == NULL) return ENOMEM; strcat(*file_actions, new_action); return 0; }
/* Add a close action to object. */ int posix_spawn_file_actions_addclose( posix_spawn_file_actions_t *file_actions, int fildes) { char temp[100];
sprintf(temp, "close(%d)", fildes); return add_to_file_actions(file_actions, temp); }
/* Add a dup2 action to object. */ int posix_spawn_file_actions_adddup2( posix_spawn_file_actions_t *file_actions, int fildes, int newfildes) { char temp[100];
sprintf(temp, "dup2(%d,%d)", fildes, newfildes); return add_to_file_actions(file_actions, temp); }
/* Add an open action to object. */ int posix_spawn_file_actions_addopen( posix_spawn_file_actions_t *file_actions, int fildes, const char *path, int oflag, mode_t mode) { char temp[100];
sprintf(temp, "open(%d,%s*%o,%o)", fildes, path, oflag, mode); return add_to_file_actions(file_actions, temp); }
/*******************************************************/ /* Here is a crude but effective implementation of the */ /* spawn attributes object functions which manipulate */ /* the individual attributes. */ /*******************************************************/ /* Initialize object with default values. */ int posix_spawnattr_init(posix_spawnattr_t *attr) { attr->posix_attr_flags = 0; attr->posix_attr_pgroup = 0; /* Default value of signal mask is the parent's signal mask; */ /* other values are also allowed */ sigprocmask(0, NULL, &attr->posix_attr_sigmask); sigemptyset(&attr->posix_attr_sigdefault); /* Default values of scheduling attr inherited from the parent; */ /* other values are also allowed */ attr->posix_attr_schedpolicy = sched_getscheduler(0); sched_getparam(0, &attr->posix_attr_schedparam); return 0; }
int posix_spawnattr_destroy(posix_spawnattr_t *attr) { /* No action needed */ return 0; }
int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) { *flags = attr->posix_attr_flags; return 0; }
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) { attr->posix_attr_flags = flags; return 0; }
int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, pid_t *pgroup) { *pgroup = attr->posix_attr_pgroup; return 0; }
int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup) { attr->posix_attr_pgroup = pgroup; return 0; }
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr, int *schedpolicy) { *schedpolicy = attr->posix_attr_schedpolicy; return 0; }
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) { attr->posix_attr_schedpolicy = schedpolicy; return 0; }
int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr, struct sched_param *schedparam) { *schedparam = attr->posix_attr_schedparam; return 0; }
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr, const struct sched_param *schedparam) { attr->posix_attr_schedparam = *schedparam; return 0; }
int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr, sigset_t *sigmask) { *sigmask = attr->posix_attr_sigmask; return 0; }
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr, const sigset_t *sigmask) { attr->posix_attr_sigmask = *sigmask; return 0; }
int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr, sigset_t *sigdefault) { *sigdefault = attr->posix_attr_sigdefault; return 0; }
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr, const sigset_t *sigdefault) { attr->posix_attr_sigdefault = *sigdefault; return 0; }
I/O Redirection with Spawn

I/O redirection with posix_spawn() or posix_spawnp() is accomplished by crafting a file_actions argument to effect the desired redirection. Such a redirection follows the general outline of the following example:

/* To redirect new standard output (fd 1) to a file, */
/* and redirect new standard input (fd 0) from my fd socket_pair[1], */
/* and close my fd socket_pair[0] in the new process. */
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_init(&file_actions);
posix_spawn_file_actions_addopen(&file_actions, 1, "newout", ...);
posix_spawn_file_actions_dup2(&file_actions, socket_pair[1], 0);
posix_spawn_file_actions_close(&file_actions, socket_pair[0]);
posix_spawn_file_actions_close(&file_actions, socket_pair[1]);
posix_spawn(..., &file_actions, ...);
posix_spawn_file_actions_destroy(&file_actions);

Spawning a Process Under a New User ID

Spawning a process under a new user ID follows the outline shown in the following example:

Save = getuid();
setuid(newid);
posix_spawn(...);
setuid(Save);


UNIX ® is a registered Trademark of The Open Group.
POSIX ® is a registered Trademark of The IEEE.
[ Main Index | XBD | XCU | XSH | XRAT ]