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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 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 33 #include "synonyms.h" 34 #include "mtlib.h" 35 36 /* 37 * system() is a cancellation point. 38 * Undefine waitpid so we call the real waitpid() rather than _waitpid(). 39 * This ensures that we actually perform the cancellation logic. 40 */ 41 #undef waitpid 42 43 #include <sys/types.h> 44 #include <sys/wait.h> 45 #include <signal.h> 46 #include <stdlib.h> 47 #include <wait.h> 48 #include <sys/stat.h> 49 #include <unistd.h> 50 #include <memory.h> 51 #include <pthread.h> 52 #include <errno.h> 53 #include <synch.h> 54 #include <spawn.h> 55 56 extern const char **environ; 57 58 extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ 59 60 static mutex_t sys_lock = DEFAULTMUTEX; /* protects the following */ 61 static uint_t sys_count = 0; /* number of threads in system() */ 62 static struct sigaction sys_ibuf; /* SIGINT */ 63 static struct sigaction sys_qbuf; /* SIGQUIT */ 64 static struct sigaction sys_cbuf; /* SIGCHLD */ 65 66 /* 67 * Cancellation cleanup handler. 68 */ 69 static void 70 cleanup(void *arg) 71 { 72 sigset_t *savemaskp = arg; 73 74 lmutex_lock(&sys_lock); 75 if (--sys_count == 0) { /* leaving system() */ 76 /* 77 * There are no remaining threads in system(), 78 * so restore the several signal actions. 79 */ 80 (void) sigaction(SIGINT, &sys_ibuf, NULL); 81 (void) sigaction(SIGQUIT, &sys_qbuf, NULL); 82 if (sys_cbuf.sa_handler == SIG_IGN || 83 (sys_cbuf.sa_flags & SA_NOCLDWAIT)) 84 (void) sigaction(SIGCHLD, &sys_cbuf, NULL); 85 } 86 lmutex_unlock(&sys_lock); 87 (void) sigprocmask(SIG_SETMASK, savemaskp, NULL); 88 } 89 90 int 91 system(const char *cmd) 92 { 93 pid_t pid; 94 pid_t w; 95 int status; 96 int error; 97 struct sigaction action; 98 sigset_t mask; 99 sigset_t savemask; 100 struct stat64 buf; 101 const char *shpath; 102 char *argvec[4]; 103 posix_spawnattr_t attr; 104 static const char *sun_path = "/bin/sh"; 105 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 106 static const char *shell = "sh"; 107 108 shpath = __xpg4? xpg4_path : sun_path; 109 110 if (cmd == NULL) { 111 if (stat64(shpath, &buf) != 0) { 112 return (0); 113 } else if (getuid() == buf.st_uid) { 114 /* exec for user */ 115 if ((buf.st_mode & 0100) == 0) 116 return (0); 117 } else if (getgid() == buf.st_gid) { 118 /* exec for group */ 119 if ((buf.st_mode & 0010) == 0) 120 return (0); 121 } else if ((buf.st_mode & 0001) == 0) { /* exec for others */ 122 return (0); 123 } 124 return (1); 125 } 126 127 /* 128 * Initialize the posix_spawn() attributes structure. 129 */ 130 if ((error = posix_spawnattr_init(&attr)) != 0) { 131 errno = error; 132 return (-1); 133 } 134 error = posix_spawnattr_setflags(&attr, 135 POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF); 136 137 /* 138 * We are required to block SIGCHLD so that we don't cause 139 * the process's signal handler, if any, to be called. 140 * This doesn't really work for a multithreaded process 141 * because some other thread may receive the SIGCHLD. 142 */ 143 (void) sigemptyset(&mask); 144 (void) sigaddset(&mask, SIGCHLD); 145 (void) sigprocmask(SIG_BLOCK, &mask, &savemask); 146 /* 147 * Tell posix_spawn() to restore the signal mask in the child. 148 */ 149 if (error == 0) 150 error = posix_spawnattr_setsigmask(&attr, &savemask); 151 152 /* 153 * We are required to set the disposition of SIGINT and SIGQUIT 154 * to be ignored for the duration of the system() operation. 155 * 156 * We allow more than one thread to call system() concurrently by 157 * keeping a count of such threads. The signal actions are set 158 * to SIG_IGN when the first thread calls system(). They are 159 * restored in cleanup() when the last thread exits system(). 160 * 161 * However, system() is still MT-unsafe because sigaction() has 162 * a process-wide effect and some other thread may also be 163 * setting the signal actions for SIGINT or SIGQUIT. 164 */ 165 lmutex_lock(&sys_lock); 166 if (sys_count++ == 0) { 167 (void) memset(&action, 0, sizeof (action)); 168 action.sa_handler = SIG_IGN; 169 (void) sigaction(SIGINT, &action, &sys_ibuf); 170 (void) sigaction(SIGQUIT, &action, &sys_qbuf); 171 /* 172 * If the action for SIGCHLD is SIG_IGN, then set it to SIG_DFL 173 * so we can retrieve the status of the spawned-off shell. 174 * The execve() performed in posix_spawn() will set the action 175 * for SIGCHLD in the child process to SIG_DFL regardless, 176 * so this has no negative consequencies for the child. 177 * 178 * Note that this is not required by the SUSv3 standard. 179 * The standard permits this error: 180 * ECHILD The status of the child process created 181 * by system() is no longer available. 182 * So we could leave the action for SIGCHLD alone and 183 * still be standards-conforming, but this is the way 184 * the SunOS system() has always behaved (in fact it 185 * used to set the action to SIG_DFL unconditinally), 186 * so we retain this behavior here. 187 */ 188 (void) sigaction(SIGCHLD, NULL, &sys_cbuf); 189 if (sys_cbuf.sa_handler == SIG_IGN || 190 (sys_cbuf.sa_flags & SA_NOCLDWAIT)) { 191 action.sa_handler = SIG_DFL; 192 (void) sigaction(SIGCHLD, &action, NULL); 193 } 194 } 195 lmutex_unlock(&sys_lock); 196 197 /* 198 * If SIGINT and SIGQUIT were not already SIG_IGN, tell 199 * posix_spawn() to make them SIG_DFL in the child, 200 * else leave them as SIG_IGN in the child. 201 */ 202 (void) sigemptyset(&mask); 203 if (sys_ibuf.sa_handler != SIG_IGN) 204 (void) sigaddset(&mask, SIGINT); 205 if (sys_qbuf.sa_handler != SIG_IGN) 206 (void) sigaddset(&mask, SIGQUIT); 207 if (error == 0) 208 error = posix_spawnattr_setsigdefault(&attr, &mask); 209 210 argvec[0] = (char *)shell; 211 argvec[1] = "-c"; 212 argvec[2] = (char *)cmd; 213 argvec[3] = NULL; 214 if (error == 0) 215 error = posix_spawn(&pid, shpath, NULL, &attr, 216 (char *const *)argvec, (char *const *)environ); 217 218 (void) posix_spawnattr_destroy(&attr); 219 220 if (error) { 221 errno = error; 222 status = -1; 223 } else { 224 pthread_cleanup_push(cleanup, &savemask); 225 do { 226 w = waitpid(pid, &status, 0); 227 } while (w == -1 && errno == EINTR); 228 pthread_cleanup_pop(0); 229 if (w == -1) 230 status = -1; 231 } 232 cleanup(&savemask); 233 234 return (status); 235 } 236