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 2006 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 77 while (waitpid(pid, NULL, 0) == -1) { 78 if (errno != EINTR) 79 break; 80 } 81 return (NULL); 82 } 83 84 /* 85 * Cancellation cleanup handler. 86 * If we were cancelled in waitpid(), create a daemon thread to 87 * reap our abandoned child. No other thread can do this for us. 88 * It would be better if there were a system call to disinherit 89 * a child process (give it to init, just as though we exited). 90 */ 91 static void 92 cleanup(void *arg) 93 { 94 cleanup_t *cup = arg; 95 96 if (cup->pid != 0) { /* we were cancelled; abandoning our pid */ 97 (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL); 98 (void) thr_create(NULL, 0, 99 reapchild, (void *)(uintptr_t)cup->pid, 100 THR_DAEMON, NULL); 101 } 102 103 lmutex_lock(&sys_lock); 104 if (--sys_count == 0) { /* leaving system() */ 105 /* 106 * There are no remaining threads in system(), so 107 * restore the SIGINT and SIGQUIT signal actions. 108 */ 109 (void) sigaction(SIGINT, &sys_ibuf, NULL); 110 (void) sigaction(SIGQUIT, &sys_qbuf, NULL); 111 } 112 lmutex_unlock(&sys_lock); 113 114 (void) thr_sigsetmask(SIG_SETMASK, &cup->savemask, NULL); 115 } 116 117 int 118 system(const char *cmd) 119 { 120 cleanup_t cu; 121 pid_t w; 122 int status; 123 int error; 124 sigset_t mask; 125 struct stat64 buf; 126 const char *shpath; 127 char *argv[4]; 128 posix_spawnattr_t attr; 129 static const char *sun_path = "/bin/sh"; 130 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 131 static const char *shell = "sh"; 132 133 shpath = __xpg4? xpg4_path : sun_path; 134 135 if (cmd == NULL) { 136 if (stat64(shpath, &buf) != 0) { 137 return (0); 138 } else if (getuid() == buf.st_uid) { 139 /* exec for user */ 140 if ((buf.st_mode & 0100) == 0) 141 return (0); 142 } else if (getgid() == buf.st_gid) { 143 /* exec for group */ 144 if ((buf.st_mode & 0010) == 0) 145 return (0); 146 } else if ((buf.st_mode & 0001) == 0) { /* exec for others */ 147 return (0); 148 } 149 return (1); 150 } 151 152 /* 153 * Initialize the posix_spawn() attributes structure. 154 * The setting of POSIX_SPAWN_WAITPID_NP ensures that no 155 * wait-for-multiple wait() operation will reap our child 156 * and that the child will not be automatically reaped due 157 * to the disposition of SIGCHLD being set to be ignored. 158 * Only a specific wait for the specific pid will be able 159 * to reap the child. Since no other thread knows the pid 160 * of our child, this should be safe enough. 161 */ 162 error = posix_spawnattr_init(&attr); 163 if (error == 0) 164 error = posix_spawnattr_setflags(&attr, 165 POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | 166 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP); 167 168 /* 169 * The POSIX spec for system() requires us to block SIGCHLD, 170 * the rationale being that the process's signal handler for 171 * SIGCHLD, if any, should not be called when our child exits. 172 * This doesn't work for a multithreaded process because some 173 * other thread could receive the SIGCHLD. 174 * 175 * The above setting of POSIX_SPAWN_NOSIGCHLD_NP ensures that no 176 * SIGCHLD signal will be posted for our child when it exits, so 177 * we don't have to block SIGCHLD to meet the intent of the spec. 178 * We block SIGCHLD anyway, just because the spec requires it. 179 */ 180 (void) sigemptyset(&mask); 181 (void) sigaddset(&mask, SIGCHLD); 182 (void) thr_sigsetmask(SIG_BLOCK, &mask, &cu.savemask); 183 /* 184 * Tell posix_spawn() to restore the signal mask in the child. 185 */ 186 if (error == 0) 187 error = posix_spawnattr_setsigmask(&attr, &cu.savemask); 188 189 /* 190 * We are required to set the disposition of SIGINT and SIGQUIT 191 * to be ignored for the duration of the system() operation. 192 * 193 * We allow more than one thread to call system() concurrently by 194 * keeping a count of such threads. The signal actions are set 195 * to SIG_IGN when the first thread calls system(). They are 196 * restored in cleanup() when the last thread exits system(). 197 * 198 * However, system() is still MT-unsafe because sigaction() has 199 * a process-wide effect and some other thread may also be 200 * setting the signal actions for SIGINT or SIGQUIT. 201 */ 202 lmutex_lock(&sys_lock); 203 if (sys_count++ == 0) { 204 (void) sigaction(SIGINT, &ignore, &sys_ibuf); 205 (void) sigaction(SIGQUIT, &ignore, &sys_qbuf); 206 } 207 lmutex_unlock(&sys_lock); 208 209 /* 210 * If SIGINT and SIGQUIT were not already SIG_IGN, tell 211 * posix_spawn() to make them SIG_DFL in the child, 212 * else leave them as SIG_IGN in the child. 213 */ 214 (void) sigemptyset(&mask); 215 if (sys_ibuf.sa_handler != SIG_IGN) 216 (void) sigaddset(&mask, SIGINT); 217 if (sys_qbuf.sa_handler != SIG_IGN) 218 (void) sigaddset(&mask, SIGQUIT); 219 if (error == 0) 220 error = posix_spawnattr_setsigdefault(&attr, &mask); 221 222 argv[0] = (char *)shell; 223 argv[1] = "-c"; 224 argv[2] = (char *)cmd; 225 argv[3] = NULL; 226 if (error == 0) 227 error = posix_spawn(&cu.pid, shpath, NULL, &attr, 228 (char *const *)argv, (char *const *)environ); 229 230 (void) posix_spawnattr_destroy(&attr); 231 232 if (error) { 233 errno = error; 234 status = -1; 235 } else { 236 /* 237 * system() is a cancellation point. 238 * Call waitpid_cancel() rather than _waitpid() to make 239 * sure that we actually perform the cancellation logic. 240 */ 241 pthread_cleanup_push(cleanup, &cu); 242 do { 243 w = waitpid_cancel(cu.pid, &status, 0); 244 } while (w == -1 && errno == EINTR); 245 pthread_cleanup_pop(0); 246 if (w == -1) 247 status = -1; 248 } 249 error = errno; 250 cu.pid = 0; 251 cleanup(&cu); 252 errno = error; 253 254 return (status); 255 } 256