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 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* Copyright (c) 1988 AT&T */ 30 /* All Rights Reserved */ 31 32 #include "synonyms.h" 33 #include "mtlib.h" 34 #include <sys/types.h> 35 #include <sys/wait.h> 36 #include <signal.h> 37 #include <stdlib.h> 38 #include <wait.h> 39 #include <sys/stat.h> 40 #include <unistd.h> 41 #include <memory.h> 42 #include <thread.h> 43 #include <pthread.h> 44 #include <errno.h> 45 #include <synch.h> 46 #include <spawn.h> 47 #include "libc.h" 48 49 extern const char **environ; 50 51 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ 52 extern const sigset_t maskset; /* all maskable signals */ 53 54 static mutex_t sys_lock = DEFAULTMUTEX; /* protects the following */ 55 static uint_t sys_count = 0; /* number of threads in system() */ 56 static struct sigaction sys_ibuf; /* saved SIGINT sigaction */ 57 static struct sigaction sys_qbuf; /* saved SIGQUIT sigaction */ 58 static struct sigaction ignore = {0, {SIG_IGN}, {0}}; 59 60 /* 61 * Things needed by the cancellation cleanup handler. 62 */ 63 typedef struct { 64 sigset_t savemask; /* saved signal mask */ 65 pid_t pid; /* if nonzero, the child's pid */ 66 } cleanup_t; 67 68 /* 69 * Daemon thread whose sole function is to reap an abandoned child. 70 * Also invoked from pclose() (see port/stdio/popen.c). 71 */ 72 void * 73 reapchild(void *arg) 74 { 75 pid_t pid = (pid_t)(uintptr_t)arg; 76 int cancel_state; 77 78 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); 79 while (waitpid(pid, NULL, 0) == -1) { 80 if (errno != EINTR) 81 break; 82 } 83 (void) pthread_setcancelstate(cancel_state, NULL); 84 return (NULL); 85 } 86 87 /* 88 * Cancellation cleanup handler. 89 * If we were cancelled in waitpid(), create a daemon thread to 90 * reap our abandoned child. No other thread can do this for us. 91 * It would be better if there were a system call to disinherit 92 * a child process (give it to init, just as though we exited). 93 */ 94 static void 95 cleanup(void *arg) 96 { 97 cleanup_t *cup = arg; 98 99 if (cup->pid != 0) { /* we were cancelled; abandoning our pid */ 100 (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL); 101 (void) thr_create(NULL, 0, 102 reapchild, (void *)(uintptr_t)cup->pid, 103 THR_DAEMON, NULL); 104 } 105 106 lmutex_lock(&sys_lock); 107 if (--sys_count == 0) { /* leaving system() */ 108 /* 109 * There are no remaining threads in system(), so 110 * restore the SIGINT and SIGQUIT signal actions. 111 */ 112 (void) sigaction(SIGINT, &sys_ibuf, NULL); 113 (void) sigaction(SIGQUIT, &sys_qbuf, NULL); 114 } 115 lmutex_unlock(&sys_lock); 116 117 (void) thr_sigsetmask(SIG_SETMASK, &cup->savemask, NULL); 118 } 119 120 int 121 system(const char *cmd) 122 { 123 cleanup_t cu; 124 pid_t w; 125 int status; 126 int error; 127 sigset_t mask; 128 struct stat64 buf; 129 const char *shpath; 130 char *argv[4]; 131 posix_spawnattr_t attr; 132 static const char *sun_path = "/bin/sh"; 133 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 134 static const char *shell = "sh"; 135 136 shpath = __xpg4? xpg4_path : sun_path; 137 138 if (cmd == NULL) { 139 if (stat64(shpath, &buf) != 0) { 140 return (0); 141 } else if (getuid() == buf.st_uid) { 142 /* exec for user */ 143 if ((buf.st_mode & 0100) == 0) 144 return (0); 145 } else if (getgid() == buf.st_gid) { 146 /* exec for group */ 147 if ((buf.st_mode & 0010) == 0) 148 return (0); 149 } else if ((buf.st_mode & 0001) == 0) { /* exec for others */ 150 return (0); 151 } 152 return (1); 153 } 154 155 /* 156 * Initialize the posix_spawn() attributes structure. 157 * The setting of POSIX_SPAWN_WAITPID_NP ensures that no 158 * wait-for-multiple wait() operation will reap our child 159 * and that the child will not be automatically reaped due 160 * to the disposition of SIGCHLD being set to be ignored. 161 * Only a specific wait for the specific pid will be able 162 * to reap the child. Since no other thread knows the pid 163 * of our child, this should be safe enough. 164 */ 165 error = posix_spawnattr_init(&attr); 166 if (error == 0) 167 error = posix_spawnattr_setflags(&attr, 168 POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | 169 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP); 170 171 /* 172 * The POSIX spec for system() requires us to block SIGCHLD, 173 * the rationale being that the process's signal handler for 174 * SIGCHLD, if any, should not be called when our child exits. 175 * This doesn't work for a multithreaded process because some 176 * other thread could receive the SIGCHLD. 177 * 178 * The above setting of POSIX_SPAWN_NOSIGCHLD_NP ensures that no 179 * SIGCHLD signal will be posted for our child when it exits, so 180 * we don't have to block SIGCHLD to meet the intent of the spec. 181 * We block SIGCHLD anyway, just because the spec requires it. 182 */ 183 (void) sigemptyset(&mask); 184 (void) sigaddset(&mask, SIGCHLD); 185 (void) thr_sigsetmask(SIG_BLOCK, &mask, &cu.savemask); 186 /* 187 * Tell posix_spawn() to restore the signal mask in the child. 188 */ 189 if (error == 0) 190 error = posix_spawnattr_setsigmask(&attr, &cu.savemask); 191 192 /* 193 * We are required to set the disposition of SIGINT and SIGQUIT 194 * to be ignored for the duration of the system() operation. 195 * 196 * We allow more than one thread to call system() concurrently by 197 * keeping a count of such threads. The signal actions are set 198 * to SIG_IGN when the first thread calls system(). They are 199 * restored in cleanup() when the last thread exits system(). 200 * 201 * However, system() is still MT-unsafe because sigaction() has 202 * a process-wide effect and some other thread may also be 203 * setting the signal actions for SIGINT or SIGQUIT. 204 */ 205 lmutex_lock(&sys_lock); 206 if (sys_count++ == 0) { 207 (void) sigaction(SIGINT, &ignore, &sys_ibuf); 208 (void) sigaction(SIGQUIT, &ignore, &sys_qbuf); 209 } 210 lmutex_unlock(&sys_lock); 211 212 /* 213 * If SIGINT and SIGQUIT were not already SIG_IGN, tell 214 * posix_spawn() to make them SIG_DFL in the child, 215 * else leave them as SIG_IGN in the child. 216 */ 217 (void) sigemptyset(&mask); 218 if (sys_ibuf.sa_handler != SIG_IGN) 219 (void) sigaddset(&mask, SIGINT); 220 if (sys_qbuf.sa_handler != SIG_IGN) 221 (void) sigaddset(&mask, SIGQUIT); 222 if (error == 0) 223 error = posix_spawnattr_setsigdefault(&attr, &mask); 224 225 argv[0] = (char *)shell; 226 argv[1] = "-c"; 227 argv[2] = (char *)cmd; 228 argv[3] = NULL; 229 if (error == 0) 230 error = posix_spawn(&cu.pid, shpath, NULL, &attr, 231 (char *const *)argv, (char *const *)environ); 232 233 (void) posix_spawnattr_destroy(&attr); 234 235 if (error) { 236 errno = error; 237 status = -1; 238 } else { 239 /* 240 * system() is a cancellation point and so is waitpid(). 241 */ 242 pthread_cleanup_push(cleanup, &cu); 243 do { 244 w = waitpid(cu.pid, &status, 0); 245 } while (w == -1 && errno == EINTR); 246 pthread_cleanup_pop(0); 247 if (w == -1) 248 status = -1; 249 } 250 error = errno; 251 cu.pid = 0; 252 cleanup(&cu); 253 errno = error; 254 255 return (status); 256 } 257