The Open Group Base Specifications Issue 6
IEEE Std 1003.1, 2004 Edition
Copyright © 2001-2004 The IEEE and The Open Group

### 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:

- Simply start a process executing a process image. This is the simplest application for process creation, and it may cover most executions of fork().

- Support I/O redirection, including pipes.

- Run the child under a user and group ID in the domain of the parent.

- Run the child at any priority in the domain of the parent.

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 ]