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 /* 24 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* Copyright (c) 1988 AT&T */ 31 /* All Rights Reserved */ 32 33 34 #include "synonyms.h" 35 #include "mtlib.h" 36 #include <sys/types.h> 37 #include <sys/wait.h> 38 #include <signal.h> 39 #include <stdlib.h> 40 #include <wait.h> 41 #include <sys/stat.h> 42 #include <unistd.h> 43 #include <memory.h> 44 #include <pthread.h> 45 #include <errno.h> 46 #include <synch.h> 47 #include <spawn.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 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; /* SIGINT */ 57 static struct sigaction sys_qbuf; /* SIGQUIT */ 58 static struct sigaction sys_cbuf; /* SIGCHLD */ 59 60 /* 61 * Cancellation cleanup handler. 62 */ 63 static void 64 cleanup(void *arg) 65 { 66 sigset_t *savemaskp = arg; 67 68 lmutex_lock(&sys_lock); 69 if (--sys_count == 0) { /* leaving system() */ 70 /* 71 * There are no remaining threads in system(), 72 * so restore the several signal actions. 73 */ 74 (void) sigaction(SIGINT, &sys_ibuf, NULL); 75 (void) sigaction(SIGQUIT, &sys_qbuf, NULL); 76 if (sys_cbuf.sa_handler == SIG_IGN || 77 (sys_cbuf.sa_flags & SA_NOCLDWAIT)) 78 (void) sigaction(SIGCHLD, &sys_cbuf, NULL); 79 } 80 lmutex_unlock(&sys_lock); 81 (void) sigprocmask(SIG_SETMASK, savemaskp, NULL); 82 } 83 84 int 85 system(const char *cmd) 86 { 87 pid_t pid; 88 pid_t w; 89 int status; 90 int error; 91 struct sigaction action; 92 sigset_t mask; 93 sigset_t savemask; 94 struct stat64 buf; 95 const char *shpath; 96 char *argvec[4]; 97 posix_spawnattr_t attr; 98 static const char *sun_path = "/bin/sh"; 99 static const char *xpg4_path = "/usr/xpg4/bin/sh"; 100 static const char *shell = "sh"; 101 102 shpath = __xpg4? xpg4_path : sun_path; 103 104 if (cmd == NULL) { 105 if (stat64(shpath, &buf) != 0) { 106 return (0); 107 } else if (getuid() == buf.st_uid) { 108 /* exec for user */ 109 if ((buf.st_mode & 0100) == 0) 110 return (0); 111 } else if (getgid() == buf.st_gid) { 112 /* exec for group */ 113 if ((buf.st_mode & 0010) == 0) 114 return (0); 115 } else if ((buf.st_mode & 0001) == 0) { /* exec for others */ 116 return (0); 117 } 118 return (1); 119 } 120 121 /* 122 * Initialize the posix_spawn() attributes structure. 123 */ 124 if ((error = posix_spawnattr_init(&attr)) != 0) { 125 errno = error; 126 return (-1); 127 } 128 error = posix_spawnattr_setflags(&attr, 129 POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF); 130 131 /* 132 * We are required to block SIGCHLD so that we don't cause 133 * the process's signal handler, if any, to be called. 134 * This doesn't really work for a multithreaded process 135 * because some other thread may receive the SIGCHLD. 136 */ 137 (void) sigemptyset(&mask); 138 (void) sigaddset(&mask, SIGCHLD); 139 (void) sigprocmask(SIG_BLOCK, &mask, &savemask); 140 /* 141 * Tell posix_spawn() to restore the signal mask in the child. 142 */ 143 if (error == 0) 144 error = posix_spawnattr_setsigmask(&attr, &savemask); 145 146 /* 147 * We are required to set the disposition of SIGINT and SIGQUIT 148 * to be ignored for the duration of the system() operation. 149 * 150 * We allow more than one thread to call system() concurrently by 151 * keeping a count of such threads. The signal actions are set 152 * to SIG_IGN when the first thread calls system(). They are 153 * restored in cleanup() when the last thread exits system(). 154 * 155 * However, system() is still MT-unsafe because sigaction() has 156 * a process-wide effect and some other thread may also be 157 * setting the signal actions for SIGINT or SIGQUIT. 158 */ 159 lmutex_lock(&sys_lock); 160 if (sys_count++ == 0) { 161 (void) memset(&action, 0, sizeof (action)); 162 action.sa_handler = SIG_IGN; 163 (void) sigaction(SIGINT, &action, &sys_ibuf); 164 (void) sigaction(SIGQUIT, &action, &sys_qbuf); 165 /* 166 * If the action for SIGCHLD is SIG_IGN, then set it to SIG_DFL 167 * so we can retrieve the status of the spawned-off shell. 168 * The execve() performed in posix_spawn() will set the action 169 * for SIGCHLD in the child process to SIG_DFL regardless, 170 * so this has no negative consequencies for the child. 171 * 172 * Note that this is not required by the SUSv3 standard. 173 * The standard permits this error: 174 * ECHILD The status of the child process created 175 * by system() is no longer available. 176 * So we could leave the action for SIGCHLD alone and 177 * still be standards-conforming, but this is the way 178 * the SunOS system() has always behaved (in fact it 179 * used to set the action to SIG_DFL unconditinally), 180 * so we retain this behavior here. 181 */ 182 (void) sigaction(SIGCHLD, NULL, &sys_cbuf); 183 if (sys_cbuf.sa_handler == SIG_IGN || 184 (sys_cbuf.sa_flags & SA_NOCLDWAIT)) { 185 action.sa_handler = SIG_DFL; 186 (void) sigaction(SIGCHLD, &action, NULL); 187 } 188 } 189 lmutex_unlock(&sys_lock); 190 191 /* 192 * If SIGINT and SIGQUIT were not already SIG_IGN, tell 193 * posix_spawn() to make them SIG_DFL in the child, 194 * else leave them as SIG_IGN in the child. 195 */ 196 (void) sigemptyset(&mask); 197 if (sys_ibuf.sa_handler != SIG_IGN) 198 (void) sigaddset(&mask, SIGINT); 199 if (sys_qbuf.sa_handler != SIG_IGN) 200 (void) sigaddset(&mask, SIGQUIT); 201 if (error == 0) 202 error = posix_spawnattr_setsigdefault(&attr, &mask); 203 204 argvec[0] = (char *)shell; 205 argvec[1] = "-c"; 206 argvec[2] = (char *)cmd; 207 argvec[3] = NULL; 208 if (error == 0) 209 error = posix_spawn(&pid, shpath, NULL, &attr, 210 (char *const *)argvec, (char *const *)environ); 211 212 (void) posix_spawnattr_destroy(&attr); 213 214 if (error) { 215 errno = error; 216 status = -1; 217 } else { 218 /* 219 * system() is a cancellation point. 220 * Call waitpid_cancel() rather than _waitpid() to make 221 * sure that we actually perform the cancellation logic. 222 */ 223 pthread_cleanup_push(cleanup, &savemask); 224 do { 225 w = waitpid_cancel(pid, &status, 0); 226 } while (w == -1 && errno == EINTR); 227 pthread_cleanup_pop(0); 228 if (w == -1) 229 status = -1; 230 } 231 cleanup(&savemask); 232 233 return (status); 234 } 235