1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  * Copyright 2016 Joyent, Inc.
  26  */
  27 
  28 /*      Copyright (c) 1988 AT&T     */
  29 /*        All Rights Reserved   */
  30 
  31 #include "lint.h"
  32 #include "mtlib.h"
  33 #include <sys/types.h>
  34 #include <sys/wait.h>
  35 #include <signal.h>
  36 #include <stdlib.h>
  37 #include <wait.h>
  38 #include <sys/stat.h>
  39 #include <unistd.h>
  40 #include <memory.h>
  41 #include <thread.h>
  42 #include <pthread.h>
  43 #include <errno.h>
  44 #include <synch.h>
  45 #include <spawn.h>
  46 #include <paths.h>
  47 #include <zone.h>
  48 #include "libc.h"
  49 
  50 extern const char **_environ;
  51 
  52 extern int __xpg4;      /* defined in _xpg4.c; 0 if not xpg4-compiled program */
  53 extern const sigset_t maskset;          /* all maskable signals */
  54 
  55 static mutex_t sys_lock = DEFAULTMUTEX; /* protects the following */
  56 static uint_t sys_count = 0;            /* number of threads in system() */
  57 static struct sigaction sys_ibuf;       /* saved SIGINT sigaction */
  58 static struct sigaction sys_qbuf;       /* saved SIGQUIT sigaction */
  59 static struct sigaction ignore = {0, {SIG_IGN}, {0}};
  60 
  61 /*
  62  * Things needed by the cancellation cleanup handler.
  63  */
  64 typedef struct {
  65         sigset_t        savemask;       /* saved signal mask */
  66         pid_t           pid;            /* if nonzero, the child's pid */
  67 } cleanup_t;
  68 
  69 /*
  70  * Daemon thread whose sole function is to reap an abandoned child.
  71  * Also invoked from pclose() (see port/stdio/popen.c).
  72  */
  73 void *
  74 reapchild(void *arg)
  75 {
  76         pid_t pid = (pid_t)(uintptr_t)arg;
  77         int cancel_state;
  78 
  79         (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
  80         while (waitpid(pid, NULL, 0) == -1) {
  81                 if (errno != EINTR)
  82                         break;
  83         }
  84         (void) pthread_setcancelstate(cancel_state, NULL);
  85         return (NULL);
  86 }
  87 
  88 /*
  89  * Cancellation cleanup handler.
  90  * If we were cancelled in waitpid(), create a daemon thread to
  91  * reap our abandoned child.  No other thread can do this for us.
  92  * It would be better if there were a system call to disinherit
  93  * a child process (give it to init, just as though we exited).
  94  */
  95 static void
  96 cleanup(void *arg)
  97 {
  98         cleanup_t *cup = arg;
  99 
 100         if (cup->pid != 0) { /* we were cancelled; abandoning our pid */
 101                 (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
 102                 (void) thr_create(NULL, 0,
 103                     reapchild, (void *)(uintptr_t)cup->pid,
 104                     THR_DAEMON, NULL);
 105         }
 106 
 107         lmutex_lock(&sys_lock);
 108         if (--sys_count == 0) {         /* leaving system() */
 109                 /*
 110                  * There are no remaining threads in system(), so
 111                  * restore the SIGINT and SIGQUIT signal actions.
 112                  */
 113                 (void) sigaction(SIGINT, &sys_ibuf, NULL);
 114                 (void) sigaction(SIGQUIT, &sys_qbuf, NULL);
 115         }
 116         lmutex_unlock(&sys_lock);
 117 
 118         (void) thr_sigsetmask(SIG_SETMASK, &cup->savemask, NULL);
 119 }
 120 
 121 int
 122 system(const char *cmd)
 123 {
 124         cleanup_t cu;
 125         pid_t w;
 126         int status;
 127         int error;
 128         sigset_t mask;
 129         struct stat64 buf;
 130         char shpath[MAXPATHLEN];
 131         const char *zroot = zone_get_nroot();
 132         char *argv[4];
 133         posix_spawnattr_t attr;
 134         static const char *shell = "sh";
 135 
 136         /*
 137          * If executing in brand use native root.
 138          */
 139         (void) snprintf(shpath, sizeof (shpath), "%s%s",
 140             zroot != NULL ? zroot : "", _PATH_BSHELL);
 141 
 142         if (cmd == NULL) {
 143                 if (stat64(shpath, &buf) != 0) {
 144                         return (0);
 145                 } else if (getuid() == buf.st_uid) {
 146                         /* exec for user */
 147                         if ((buf.st_mode & 0100) == 0)
 148                                 return (0);
 149                 } else if (getgid() == buf.st_gid) {
 150                         /* exec for group */
 151                         if ((buf.st_mode & 0010) == 0)
 152                                 return (0);
 153                 } else if ((buf.st_mode & 0001) == 0) {     /* exec for others */
 154                         return (0);
 155                 }
 156                 return (1);
 157         }
 158 
 159         /*
 160          * Initialize the posix_spawn() attributes structure.
 161          *
 162          * The setting of POSIX_SPAWN_WAITPID_NP ensures that no
 163          * wait-for-multiple wait() operation will reap our child
 164          * and that the child will not be automatically reaped due
 165          * to the disposition of SIGCHLD being set to be ignored.
 166          * Only a specific wait for the specific pid will be able
 167          * to reap the child.  Since no other thread knows the pid
 168          * of our child, this should be safe enough.
 169          *
 170          * The POSIX_SPAWN_NOEXECERR_NP flag tells posix_spawn() not
 171          * to fail if the shell cannot be executed, but rather cause
 172          * a child to be created that simply performs _exit(127).
 173          * This is in order to satisfy the Posix requirement on system():
 174          *      The system function shall behave as if a child process were
 175          *      created using fork(), and the child process invoked the sh
 176          *      utility using execl().  If some error prevents the command
 177          *      language interpreter from executing after the child process
 178          *      is created, the return value from system() shall be as if
 179          *      the command language interpreter had terminated using
 180          *      exit(127) or _exit(127).
 181          */
 182         error = posix_spawnattr_init(&attr);
 183         if (error == 0)
 184                 error = posix_spawnattr_setflags(&attr,
 185                     POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF |
 186                     POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP |
 187                     POSIX_SPAWN_NOEXECERR_NP);
 188 
 189         /*
 190          * The POSIX spec for system() requires us to block SIGCHLD,
 191          * the rationale being that the process's signal handler for
 192          * SIGCHLD, if any, should not be called when our child exits.
 193          * This doesn't work for a multithreaded process because some
 194          * other thread could receive the SIGCHLD.
 195          *
 196          * The above setting of POSIX_SPAWN_NOSIGCHLD_NP ensures that no
 197          * SIGCHLD signal will be posted for our child when it exits, so
 198          * we don't have to block SIGCHLD to meet the intent of the spec.
 199          * We block SIGCHLD anyway, just because the spec requires it.
 200          */
 201         (void) sigemptyset(&mask);
 202         (void) sigaddset(&mask, SIGCHLD);
 203         (void) thr_sigsetmask(SIG_BLOCK, &mask, &cu.savemask);
 204         /*
 205          * Tell posix_spawn() to restore the signal mask in the child.
 206          */
 207         if (error == 0)
 208                 error = posix_spawnattr_setsigmask(&attr, &cu.savemask);
 209 
 210         /*
 211          * We are required to set the disposition of SIGINT and SIGQUIT
 212          * to be ignored for the duration of the system() operation.
 213          *
 214          * We allow more than one thread to call system() concurrently by
 215          * keeping a count of such threads.  The signal actions are set
 216          * to SIG_IGN when the first thread calls system().  They are
 217          * restored in cleanup() when the last thread exits system().
 218          *
 219          * However, system() is still MT-unsafe because sigaction() has
 220          * a process-wide effect and some other thread may also be
 221          * setting the signal actions for SIGINT or SIGQUIT.
 222          */
 223         lmutex_lock(&sys_lock);
 224         if (sys_count++ == 0) {
 225                 (void) sigaction(SIGINT, &ignore, &sys_ibuf);
 226                 (void) sigaction(SIGQUIT, &ignore, &sys_qbuf);
 227         }
 228         lmutex_unlock(&sys_lock);
 229 
 230         /*
 231          * If SIGINT and SIGQUIT were not already SIG_IGN, tell
 232          * posix_spawn() to make them SIG_DFL in the child,
 233          * else leave them as SIG_IGN in the child.
 234          */
 235         (void) sigemptyset(&mask);
 236         if (sys_ibuf.sa_handler != SIG_IGN)
 237                 (void) sigaddset(&mask, SIGINT);
 238         if (sys_qbuf.sa_handler != SIG_IGN)
 239                 (void) sigaddset(&mask, SIGQUIT);
 240         if (error == 0)
 241                 error = posix_spawnattr_setsigdefault(&attr, &mask);
 242 
 243         argv[0] = (char *)shell;
 244         argv[1] = "-c";
 245         argv[2] = (char *)cmd;
 246         argv[3] = NULL;
 247         if (error == 0)
 248                 error = posix_spawn(&cu.pid, shpath, NULL, &attr,
 249                     (char *const *)argv, (char *const *)_environ);
 250 
 251         (void) posix_spawnattr_destroy(&attr);
 252 
 253         if (error) {
 254                 errno = error;
 255                 status = -1;
 256         } else {
 257                 /*
 258                  * system() is a cancellation point and so is waitpid().
 259                  */
 260                 pthread_cleanup_push(cleanup, &cu);
 261                 do {
 262                         w = waitpid(cu.pid, &status, 0);
 263                 } while (w == -1 && errno == EINTR);
 264                 pthread_cleanup_pop(0);
 265                 if (w == -1)
 266                         status = -1;
 267         }
 268         error = errno;
 269         cu.pid = 0;
 270         cleanup(&cu);
 271         errno = error;
 272 
 273         return (status);
 274 }