1 /* $NetBSD: popen.c,v 1.14 2002/03/05 21:29:30 wiz Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 __RCSID("$NetBSD: popen.c,v 1.14 2002/03/05 21:29:30 wiz Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include "rcv.h" 46 #include "extern.h" 47 48 #define READ 0 49 #define WRITE 1 50 51 struct fp { 52 FILE *fp; 53 int pipe; 54 int pid; 55 struct fp *link; 56 }; 57 static struct fp *fp_head; 58 59 struct child { 60 int pid; 61 char done; 62 char free; 63 int status; 64 struct child *link; 65 }; 66 static struct child *child; 67 static struct child *findchild(int); 68 static void delchild(struct child *); 69 static int file_pid(FILE *); 70 71 FILE * 72 Fopen(char *fn, char *mode) 73 { 74 FILE *fp; 75 76 if ((fp = fopen(fn, mode)) != NULL) { 77 register_file(fp, 0, 0); 78 (void)fcntl(fileno(fp), F_SETFD, 1); 79 } 80 return fp; 81 } 82 83 FILE * 84 Fdopen(int fd, char *mode) 85 { 86 FILE *fp; 87 88 if ((fp = fdopen(fd, mode)) != NULL) { 89 register_file(fp, 0, 0); 90 (void)fcntl(fileno(fp), F_SETFD, 1); 91 } 92 return fp; 93 } 94 95 int 96 Fclose(FILE *fp) 97 { 98 unregister_file(fp); 99 return fclose(fp); 100 } 101 102 FILE * 103 Popen(char *cmd, char *mode) 104 { 105 int p[2]; 106 int myside, hisside, fd0, fd1; 107 int pid; 108 sigset_t nset; 109 FILE *fp; 110 111 if (pipe(p) < 0) 112 return NULL; 113 (void)fcntl(p[READ], F_SETFD, 1); 114 (void)fcntl(p[WRITE], F_SETFD, 1); 115 if (*mode == 'r') { 116 myside = p[READ]; 117 fd0 = -1; 118 hisside = fd1 = p[WRITE]; 119 } else { 120 myside = p[WRITE]; 121 hisside = fd0 = p[READ]; 122 fd1 = -1; 123 } 124 sigemptyset(&nset); 125 if ((pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL)) < 0) { 126 close(p[READ]); 127 close(p[WRITE]); 128 return NULL; 129 } 130 (void)close(hisside); 131 if ((fp = fdopen(myside, mode)) != NULL) 132 register_file(fp, 1, pid); 133 return fp; 134 } 135 136 int 137 Pclose(FILE *ptr) 138 { 139 int i; 140 sigset_t nset, oset; 141 142 i = file_pid(ptr); 143 unregister_file(ptr); 144 (void)fclose(ptr); 145 sigemptyset(&nset); 146 sigaddset(&nset, SIGINT); 147 sigaddset(&nset, SIGHUP); 148 sigprocmask(SIG_BLOCK, &nset, &oset); 149 i = wait_child(i); 150 sigprocmask(SIG_SETMASK, &oset, NULL); 151 return i; 152 } 153 154 void 155 close_all_files(void) 156 { 157 158 while (fp_head) 159 if (fp_head->pipe) 160 (void)Pclose(fp_head->fp); 161 else 162 (void)Fclose(fp_head->fp); 163 } 164 165 void 166 register_file(FILE *fp, int pipefd, int pid) 167 { 168 struct fp *fpp; 169 170 if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL) 171 errx(1, "Out of memory"); 172 fpp->fp = fp; 173 fpp->pipe = pipefd; 174 fpp->pid = pid; 175 fpp->link = fp_head; 176 fp_head = fpp; 177 } 178 179 void 180 unregister_file(FILE *fp) 181 { 182 struct fp **pp, *p; 183 184 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link) 185 if (p->fp == fp) { 186 *pp = p->link; 187 free((char *) p); 188 return; 189 } 190 errx(1, "Invalid file pointer"); 191 } 192 193 static int 194 file_pid(FILE *fp) 195 { 196 struct fp *p; 197 198 for (p = fp_head; p; p = p->link) 199 if (p->fp == fp) 200 return (p->pid); 201 errx(1, "Invalid file pointer"); 202 /*NOTREACHED*/ 203 } 204 205 /* 206 * Run a command without a shell, with optional arguments and splicing 207 * of stdin and stdout. The command name can be a sequence of words. 208 * Signals must be handled by the caller. 209 * "Mask" contains the signals to ignore in the new process. 210 * SIGINT is enabled unless it's in the mask. 211 */ 212 /*VARARGS4*/ 213 int 214 run_command(char *cmd, sigset_t *mask, int infd, int outfd, char *a0, 215 char *a1, char *a2) 216 { 217 int pid; 218 219 if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0) 220 return -1; 221 return wait_command(pid); 222 } 223 224 /*VARARGS4*/ 225 int 226 start_command(char *cmd, sigset_t *mask, int infd, int outfd, 227 char *a0, char *a1, char *a2) 228 { 229 int pid; 230 231 if ((pid = vfork()) < 0) { 232 warn("fork"); 233 return -1; 234 } 235 if (pid == 0) { 236 char *argv[100]; 237 int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv); 238 239 if ((argv[i++] = a0) != NULL && 240 (argv[i++] = a1) != NULL && 241 (argv[i++] = a2) != NULL) 242 argv[i] = NULL; 243 prepare_child(mask, infd, outfd); 244 execvp(argv[0], argv); 245 warn("%s", argv[0]); 246 _exit(1); 247 } 248 return pid; 249 } 250 251 void 252 prepare_child(sigset_t *nset, int infd, int outfd) 253 { 254 int i; 255 sigset_t eset; 256 257 /* 258 * All file descriptors other than 0, 1, and 2 are supposed to be 259 * close-on-exec. 260 */ 261 if (infd >= 0) 262 dup2(infd, 0); 263 if (outfd >= 0) 264 dup2(outfd, 1); 265 for (i = 1; i < NSIG; i++) 266 if (nset != NULL && sigismember(nset, i)) 267 (void)signal(i, SIG_IGN); 268 if (nset == NULL || !sigismember(nset, SIGINT)) 269 (void)signal(SIGINT, SIG_DFL); 270 sigemptyset(&eset); 271 (void)sigprocmask(SIG_SETMASK, &eset, NULL); 272 } 273 274 int 275 wait_command(int pid) 276 { 277 278 if (wait_child(pid) < 0) { 279 printf("Fatal error in process.\n"); 280 return -1; 281 } 282 return 0; 283 } 284 285 static struct child * 286 findchild(int pid) 287 { 288 struct child **cpp; 289 290 for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid; 291 cpp = &(*cpp)->link) 292 ; 293 if (*cpp == NULL) { 294 *cpp = (struct child *) malloc(sizeof (struct child)); 295 (*cpp)->pid = pid; 296 (*cpp)->done = (*cpp)->free = 0; 297 (*cpp)->link = NULL; 298 } 299 return *cpp; 300 } 301 302 static void 303 delchild(struct child *cp) 304 { 305 struct child **cpp; 306 307 for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link) 308 ; 309 *cpp = cp->link; 310 free((char *) cp); 311 } 312 313 void 314 sigchild(int signo) 315 { 316 int pid; 317 int status; 318 struct child *cp; 319 320 while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { 321 cp = findchild(pid); 322 if (cp->free) 323 delchild(cp); 324 else { 325 cp->done = 1; 326 cp->status = status; 327 } 328 } 329 } 330 331 int wait_status; 332 333 /* 334 * Wait for a specific child to die. 335 */ 336 int 337 wait_child(int pid) 338 { 339 sigset_t nset, oset; 340 struct child *cp = findchild(pid); 341 sigemptyset(&nset); 342 sigaddset(&nset, SIGCHLD); 343 sigprocmask(SIG_BLOCK, &nset, &oset); 344 345 while (!cp->done) 346 sigsuspend(&oset); 347 wait_status = cp->status; 348 delchild(cp); 349 sigprocmask(SIG_SETMASK, &oset, NULL); 350 return wait_status ? -1 : 0; 351 } 352 353 /* 354 * Mark a child as don't care. 355 */ 356 void 357 free_child(int pid) 358 { 359 sigset_t nset, oset; 360 struct child *cp = findchild(pid); 361 sigemptyset(&nset); 362 sigaddset(&nset, SIGCHLD); 363 sigprocmask(SIG_BLOCK, &nset, &oset); 364 365 if (cp->done) 366 delchild(cp); 367 else 368 cp->free = 1; 369 sigprocmask(SIG_SETMASK, &oset, NULL); 370 } 371