1 1.27 christos /* $NetBSD: popen.c,v 1.27 2012/04/29 23:50:22 christos Exp $ */ 2 1.4 christos 3 1.1 cgd /* 4 1.3 deraadt * Copyright (c) 1980, 1993 5 1.3 deraadt * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.17 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.7 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.4 christos #if 0 35 1.4 christos static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93"; 36 1.4 christos #else 37 1.27 christos __RCSID("$NetBSD: popen.c,v 1.27 2012/04/29 23:50:22 christos Exp $"); 38 1.4 christos #endif 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.20 christos #include <errno.h> 42 1.16 christos #include <fcntl.h> 43 1.16 christos #include <stdarg.h> 44 1.20 christos #include <util.h> 45 1.20 christos #include <sys/wait.h> 46 1.20 christos 47 1.20 christos #include "rcv.h" 48 1.3 deraadt #include "extern.h" 49 1.25 christos #include "sig.h" 50 1.1 cgd 51 1.1 cgd #define READ 0 52 1.1 cgd #define WRITE 1 53 1.1 cgd 54 1.1 cgd struct fp { 55 1.1 cgd FILE *fp; 56 1.1 cgd int pipe; 57 1.16 christos pid_t pid; 58 1.1 cgd struct fp *link; 59 1.1 cgd }; 60 1.1 cgd static struct fp *fp_head; 61 1.1 cgd 62 1.3 deraadt struct child { 63 1.16 christos pid_t pid; 64 1.3 deraadt char done; 65 1.3 deraadt char free; 66 1.9 christos int status; 67 1.3 deraadt struct child *link; 68 1.3 deraadt }; 69 1.16 christos static struct child *child, *child_freelist = NULL; 70 1.26 christos static struct child *findchild(pid_t, int); 71 1.16 christos 72 1.3 deraadt 73 1.23 christos #if 0 /* XXX - debugging stuff. This should go away eventually! */ 74 1.22 christos static void 75 1.22 christos show_one_file(FILE *fo, struct fp *fpp) 76 1.22 christos { 77 1.22 christos (void)fprintf(fo, ">>> fp: %p, pipe: %d, pid: %d, link: %p\n", 78 1.22 christos fpp->fp, fpp->pipe, fpp->pid, fpp->link); 79 1.22 christos } 80 1.22 christos 81 1.22 christos void show_all_files(FILE *fo); 82 1.22 christos __unused 83 1.22 christos PUBLIC void 84 1.22 christos show_all_files(FILE *fo) 85 1.22 christos { 86 1.22 christos struct fp *fpp; 87 1.23 christos 88 1.22 christos (void)fprintf(fo, ">> FILES\n"); 89 1.22 christos for (fpp = fp_head; fpp; fpp = fpp->link) 90 1.22 christos show_one_file(fo, fpp); 91 1.22 christos (void)fprintf(fo, ">> -------\n"); 92 1.22 christos (void)fflush(fo); 93 1.22 christos } 94 1.22 christos #endif /* end debugging stuff */ 95 1.22 christos 96 1.22 christos 97 1.22 christos static void 98 1.22 christos unregister_file(FILE *fp) 99 1.22 christos { 100 1.22 christos struct fp **pp, *p; 101 1.22 christos 102 1.22 christos for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link) 103 1.22 christos if (p->fp == fp) { 104 1.22 christos *pp = p->link; 105 1.22 christos (void)free(p); 106 1.22 christos return; 107 1.22 christos } 108 1.26 christos errx(EXIT_FAILURE, "Invalid file pointer"); 109 1.22 christos } 110 1.22 christos 111 1.22 christos PUBLIC void 112 1.22 christos register_file(FILE *fp, int pipefd, pid_t pid) 113 1.22 christos { 114 1.22 christos struct fp *fpp; 115 1.22 christos 116 1.23 christos fpp = emalloc(sizeof(*fpp)); 117 1.22 christos fpp->fp = fp; 118 1.22 christos fpp->pipe = pipefd; 119 1.22 christos fpp->pid = pid; 120 1.22 christos fpp->link = fp_head; 121 1.22 christos fp_head = fpp; 122 1.22 christos } 123 1.22 christos 124 1.22 christos PUBLIC FILE * 125 1.18 christos Fopen(const char *fn, const char *mode) 126 1.1 cgd { 127 1.1 cgd FILE *fp; 128 1.1 cgd 129 1.27 christos if ((fp = fopen(fn, mode)) != NULL) 130 1.3 deraadt register_file(fp, 0, 0); 131 1.1 cgd return fp; 132 1.1 cgd } 133 1.1 cgd 134 1.22 christos PUBLIC FILE * 135 1.18 christos Fdopen(int fd, const char *mode) 136 1.1 cgd { 137 1.1 cgd FILE *fp; 138 1.1 cgd 139 1.27 christos if ((fp = fdopen(fd, mode)) != NULL) 140 1.3 deraadt register_file(fp, 0, 0); 141 1.1 cgd return fp; 142 1.1 cgd } 143 1.1 cgd 144 1.22 christos PUBLIC int 145 1.10 wiz Fclose(FILE *fp) 146 1.1 cgd { 147 1.25 christos 148 1.25 christos if (fp == NULL) 149 1.25 christos return 0; 150 1.25 christos 151 1.1 cgd unregister_file(fp); 152 1.1 cgd return fclose(fp); 153 1.1 cgd } 154 1.1 cgd 155 1.22 christos PUBLIC void 156 1.22 christos prepare_child(sigset_t *nset, int infd, int outfd) 157 1.22 christos { 158 1.22 christos int i; 159 1.22 christos sigset_t eset; 160 1.22 christos 161 1.22 christos /* 162 1.22 christos * All file descriptors other than 0, 1, and 2 are supposed to be 163 1.22 christos * close-on-exec. 164 1.22 christos */ 165 1.22 christos if (infd > 0) { 166 1.22 christos (void)dup2(infd, 0); 167 1.22 christos } else if (infd != 0) { 168 1.22 christos /* we don't want the child stealing my stdin input */ 169 1.22 christos (void)close(0); 170 1.22 christos (void)open(_PATH_DEVNULL, O_RDONLY, 0); 171 1.22 christos } 172 1.22 christos if (outfd >= 0 && outfd != 1) 173 1.22 christos (void)dup2(outfd, 1); 174 1.25 christos 175 1.22 christos if (nset != NULL) { 176 1.25 christos for (i = 1; i < NSIG; i++) { 177 1.22 christos if (sigismember(nset, i)) 178 1.22 christos (void)signal(i, SIG_IGN); 179 1.25 christos } 180 1.25 christos if (!sigismember(nset, SIGINT)) 181 1.25 christos (void)signal(SIGINT, SIG_DFL); 182 1.25 christos (void)sigemptyset(&eset); 183 1.25 christos (void)sigprocmask(SIG_SETMASK, &eset, NULL); 184 1.22 christos } 185 1.22 christos } 186 1.22 christos 187 1.22 christos /* 188 1.22 christos * Run a command without a shell, with optional arguments and splicing 189 1.22 christos * of stdin (-1 means none) and stdout. The command name can be a sequence 190 1.22 christos * of words. 191 1.22 christos * Signals must be handled by the caller. 192 1.22 christos * "nset" contains the signals to ignore in the new process. 193 1.22 christos * SIGINT is enabled unless it's in "nset". 194 1.22 christos */ 195 1.22 christos static pid_t 196 1.22 christos start_commandv(const char *cmd, sigset_t *nset, int infd, int outfd, 197 1.22 christos va_list args) 198 1.22 christos { 199 1.22 christos pid_t pid; 200 1.22 christos 201 1.25 christos sig_check(); 202 1.22 christos if ((pid = fork()) < 0) { 203 1.22 christos warn("fork"); 204 1.22 christos return -1; 205 1.22 christos } 206 1.22 christos if (pid == 0) { 207 1.22 christos char *argv[100]; 208 1.25 christos size_t i; 209 1.22 christos 210 1.25 christos i = getrawlist(cmd, argv, (int)__arraycount(argv)); 211 1.25 christos while (i < __arraycount(argv) - 1 && 212 1.25 christos (argv[i++] = va_arg(args, char *)) != NULL) 213 1.22 christos continue; 214 1.22 christos argv[i] = NULL; 215 1.22 christos prepare_child(nset, infd, outfd); 216 1.22 christos (void)execvp(argv[0], argv); 217 1.22 christos warn("%s", argv[0]); 218 1.22 christos _exit(1); 219 1.22 christos } 220 1.26 christos (void)findchild(pid, 0); 221 1.22 christos return pid; 222 1.22 christos } 223 1.22 christos 224 1.22 christos PUBLIC int 225 1.22 christos start_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...) 226 1.22 christos { 227 1.22 christos va_list args; 228 1.22 christos int r; 229 1.22 christos 230 1.22 christos va_start(args, outfd); 231 1.22 christos r = start_commandv(cmd, nset, infd, outfd, args); 232 1.22 christos va_end(args); 233 1.22 christos return r; 234 1.22 christos } 235 1.22 christos 236 1.22 christos PUBLIC FILE * 237 1.18 christos Popen(const char *cmd, const char *mode) 238 1.1 cgd { 239 1.1 cgd int p[2]; 240 1.1 cgd int myside, hisside, fd0, fd1; 241 1.16 christos pid_t pid; 242 1.4 christos sigset_t nset; 243 1.1 cgd FILE *fp; 244 1.20 christos char *shellcmd; 245 1.1 cgd 246 1.27 christos if (pipe2(p, O_CLOEXEC) < 0) 247 1.1 cgd return NULL; 248 1.1 cgd if (*mode == 'r') { 249 1.1 cgd myside = p[READ]; 250 1.16 christos hisside = fd0 = fd1 = p[WRITE]; 251 1.1 cgd } else { 252 1.1 cgd myside = p[WRITE]; 253 1.1 cgd hisside = fd0 = p[READ]; 254 1.1 cgd fd1 = -1; 255 1.1 cgd } 256 1.19 christos (void)sigemptyset(&nset); 257 1.22 christos if ((shellcmd = value(ENAME_SHELL)) == NULL) 258 1.20 christos shellcmd = __UNCONST(_PATH_CSHELL); 259 1.20 christos pid = start_command(shellcmd, &nset, fd0, fd1, "-c", cmd, NULL); 260 1.16 christos if (pid < 0) { 261 1.16 christos (void)close(p[READ]); 262 1.16 christos (void)close(p[WRITE]); 263 1.1 cgd return NULL; 264 1.1 cgd } 265 1.13 wiz (void)close(hisside); 266 1.1 cgd if ((fp = fdopen(myside, mode)) != NULL) 267 1.3 deraadt register_file(fp, 1, pid); 268 1.1 cgd return fp; 269 1.1 cgd } 270 1.1 cgd 271 1.22 christos static struct child * 272 1.22 christos findchild(pid_t pid, int dont_alloc) 273 1.22 christos { 274 1.22 christos struct child **cpp; 275 1.22 christos 276 1.22 christos for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid; 277 1.22 christos cpp = &(*cpp)->link) 278 1.22 christos continue; 279 1.22 christos if (*cpp == NULL) { 280 1.22 christos if (dont_alloc) 281 1.22 christos return NULL; 282 1.22 christos if (child_freelist) { 283 1.22 christos *cpp = child_freelist; 284 1.22 christos child_freelist = (*cpp)->link; 285 1.22 christos } else 286 1.24 christos *cpp = emalloc(sizeof(**cpp)); 287 1.22 christos 288 1.22 christos (*cpp)->pid = pid; 289 1.22 christos (*cpp)->done = (*cpp)->free = 0; 290 1.22 christos (*cpp)->link = NULL; 291 1.22 christos } 292 1.22 christos return *cpp; 293 1.22 christos } 294 1.22 christos 295 1.22 christos static void 296 1.22 christos delchild(struct child *cp) 297 1.22 christos { 298 1.22 christos struct child **cpp; 299 1.22 christos 300 1.22 christos for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link) 301 1.22 christos continue; 302 1.22 christos *cpp = cp->link; 303 1.22 christos cp->link = child_freelist; 304 1.22 christos child_freelist = cp; 305 1.22 christos } 306 1.22 christos 307 1.22 christos /* 308 1.22 christos * Wait for a specific child to die. 309 1.22 christos */ 310 1.22 christos PUBLIC int 311 1.22 christos wait_child(pid_t pid) 312 1.22 christos { 313 1.22 christos struct child *cp; 314 1.22 christos sigset_t nset, oset; 315 1.22 christos pid_t rv = 0; 316 1.22 christos 317 1.22 christos (void)sigemptyset(&nset); 318 1.22 christos (void)sigaddset(&nset, SIGCHLD); 319 1.22 christos (void)sigprocmask(SIG_BLOCK, &nset, &oset); 320 1.22 christos /* 321 1.22 christos * If we have not already waited on the pid (via sigchild) 322 1.22 christos * wait on it now. Otherwise, use the wait status stashed 323 1.22 christos * by sigchild. 324 1.22 christos */ 325 1.22 christos cp = findchild(pid, 1); 326 1.22 christos if (cp == NULL || !cp->done) 327 1.22 christos rv = waitpid(pid, &wait_status, 0); 328 1.22 christos else 329 1.22 christos wait_status = cp->status; 330 1.22 christos if (cp != NULL) 331 1.22 christos delchild(cp); 332 1.22 christos (void)sigprocmask(SIG_SETMASK, &oset, NULL); 333 1.22 christos if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status))) 334 1.22 christos return -1; 335 1.22 christos else 336 1.22 christos return 0; 337 1.22 christos } 338 1.22 christos 339 1.22 christos static pid_t 340 1.22 christos file_pid(FILE *fp) 341 1.22 christos { 342 1.22 christos struct fp *p; 343 1.22 christos 344 1.22 christos for (p = fp_head; p; p = p->link) 345 1.22 christos if (p->fp == fp) 346 1.22 christos return p->pid; 347 1.26 christos errx(EXIT_FAILURE, "Invalid file pointer"); 348 1.22 christos /*NOTREACHED*/ 349 1.22 christos } 350 1.22 christos 351 1.22 christos PUBLIC int 352 1.10 wiz Pclose(FILE *ptr) 353 1.1 cgd { 354 1.1 cgd int i; 355 1.4 christos sigset_t nset, oset; 356 1.1 cgd 357 1.25 christos if (ptr == NULL) 358 1.25 christos return 0; 359 1.25 christos 360 1.3 deraadt i = file_pid(ptr); 361 1.1 cgd unregister_file(ptr); 362 1.13 wiz (void)fclose(ptr); 363 1.19 christos (void)sigemptyset(&nset); 364 1.19 christos (void)sigaddset(&nset, SIGINT); 365 1.19 christos (void)sigaddset(&nset, SIGHUP); 366 1.19 christos (void)sigprocmask(SIG_BLOCK, &nset, &oset); 367 1.3 deraadt i = wait_child(i); 368 1.19 christos (void)sigprocmask(SIG_SETMASK, &oset, NULL); 369 1.1 cgd return i; 370 1.1 cgd } 371 1.1 cgd 372 1.22 christos PUBLIC void 373 1.10 wiz close_all_files(void) 374 1.1 cgd { 375 1.20 christos while (fp_head) 376 1.20 christos if (fp_head->pipe) 377 1.20 christos (void)Pclose(fp_head->fp); 378 1.20 christos else 379 1.20 christos (void)Fclose(fp_head->fp); 380 1.20 christos } 381 1.20 christos 382 1.22 christos PUBLIC FILE * 383 1.20 christos last_registered_file(int last_pipe) 384 1.20 christos { 385 1.20 christos struct fp *fpp; 386 1.23 christos 387 1.20 christos if (last_pipe == 0) 388 1.20 christos return fp_head ? fp_head->fp : NULL; 389 1.20 christos 390 1.20 christos for (fpp = fp_head; fpp; fpp = fpp->link) 391 1.20 christos if (fpp->pipe) 392 1.20 christos return fpp->fp; 393 1.20 christos return NULL; 394 1.20 christos } 395 1.20 christos 396 1.22 christos PUBLIC void 397 1.20 christos close_top_files(FILE *fp_stop) 398 1.20 christos { 399 1.20 christos while (fp_head && fp_head->fp != fp_stop) 400 1.1 cgd if (fp_head->pipe) 401 1.13 wiz (void)Pclose(fp_head->fp); 402 1.1 cgd else 403 1.13 wiz (void)Fclose(fp_head->fp); 404 1.1 cgd } 405 1.1 cgd 406 1.22 christos #ifdef MIME_SUPPORT 407 1.22 christos PUBLIC void 408 1.20 christos flush_files(FILE *fo, int only_pipes) 409 1.20 christos { 410 1.20 christos struct fp *fpp; 411 1.23 christos 412 1.20 christos if (fo) 413 1.20 christos (void)fflush(fo); 414 1.20 christos 415 1.20 christos for (fpp = fp_head; fpp; fpp = fpp->link) 416 1.20 christos if (!only_pipes || fpp->pipe) 417 1.20 christos (void)fflush(fpp->fp); 418 1.20 christos 419 1.20 christos (void)fflush(stdout); 420 1.20 christos } 421 1.22 christos #endif /* MIME_SUPPORT */ 422 1.20 christos 423 1.22 christos static int 424 1.22 christos wait_command(pid_t pid) 425 1.1 cgd { 426 1.1 cgd 427 1.22 christos if (wait_child(pid) < 0) { 428 1.22 christos (void)puts("Fatal error in process."); 429 1.1 cgd return -1; 430 1.1 cgd } 431 1.22 christos return 0; 432 1.1 cgd } 433 1.1 cgd 434 1.22 christos PUBLIC int 435 1.18 christos run_command(const char *cmd, sigset_t *nset, int infd, int outfd, ...) 436 1.16 christos { 437 1.16 christos pid_t pid; 438 1.16 christos va_list args; 439 1.25 christos int rval; 440 1.16 christos 441 1.25 christos #ifdef BROKEN_EXEC_TTY_RESTORE 442 1.25 christos struct termios ttybuf; 443 1.25 christos int tcrval; 444 1.25 christos /* 445 1.25 christos * XXX - grab the tty settings as currently they can get 446 1.25 christos * trashed by emacs-21 when suspending with bash-3.2.25 as the 447 1.25 christos * shell. 448 1.25 christos * 449 1.25 christos * 1) from the mail editor, start "emacs -nw" (21.4) 450 1.25 christos * 2) suspend emacs to the shell (bash 3.2.25) 451 1.25 christos * 3) resume emacs 452 1.25 christos * 4) exit emacs back to the mail editor 453 1.25 christos * 5) discover the tty is screwed: the mail editor is no 454 1.25 christos * longer receiving characters 455 1.25 christos * 456 1.25 christos * - This occurs on both i386 and amd64. 457 1.25 christos * - This did _NOT_ occur before 4.99.10. 458 1.25 christos * - This does _NOT_ occur if the editor is vi(1) or if the shell 459 1.25 christos * is /bin/sh. 460 1.25 christos * - This _DOES_ happen with the old mail(1) from 2006-01-01 (long 461 1.25 christos * before my changes). 462 1.25 christos * 463 1.25 christos * This is the commit that introduced this "feature": 464 1.25 christos * http://mail-index.netbsd.org/source-changes/2007/02/09/0020.html 465 1.25 christos */ 466 1.25 christos if ((tcrval = tcgetattr(fileno(stdin), &ttybuf)) == -1) 467 1.25 christos warn("tcgetattr"); 468 1.25 christos #endif 469 1.16 christos va_start(args, outfd); 470 1.16 christos pid = start_commandv(cmd, nset, infd, outfd, args); 471 1.16 christos va_end(args); 472 1.16 christos if (pid < 0) 473 1.16 christos return -1; 474 1.25 christos rval = wait_command(pid); 475 1.25 christos #ifdef BROKEN_EXEC_TTY_RESTORE 476 1.25 christos if (tcrval != -1 && tcsetattr(fileno(stdin), TCSADRAIN, &ttybuf) == -1) 477 1.25 christos warn("tcsetattr"); 478 1.25 christos #endif 479 1.25 christos return rval; 480 1.25 christos 481 1.16 christos } 482 1.16 christos 483 1.19 christos /*ARGSUSED*/ 484 1.22 christos PUBLIC void 485 1.20 christos sigchild(int signo __unused) 486 1.1 cgd { 487 1.16 christos pid_t pid; 488 1.9 christos int status; 489 1.7 lukem struct child *cp; 490 1.25 christos int save_errno; 491 1.1 cgd 492 1.25 christos save_errno = errno; 493 1.25 christos while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) { 494 1.25 christos cp = findchild(pid, 1); /* async-signal-safe: we don't alloc */ 495 1.16 christos if (!cp) 496 1.16 christos continue; 497 1.1 cgd if (cp->free) 498 1.25 christos delchild(cp); /* async-signal-safe: list changes */ 499 1.1 cgd else { 500 1.1 cgd cp->done = 1; 501 1.1 cgd cp->status = status; 502 1.1 cgd } 503 1.1 cgd } 504 1.16 christos errno = save_errno; 505 1.1 cgd } 506 1.1 cgd 507 1.1 cgd /* 508 1.1 cgd * Mark a child as don't care. 509 1.1 cgd */ 510 1.22 christos PUBLIC void 511 1.16 christos free_child(pid_t pid) 512 1.1 cgd { 513 1.16 christos struct child *cp; 514 1.4 christos sigset_t nset, oset; 515 1.16 christos 516 1.19 christos (void)sigemptyset(&nset); 517 1.19 christos (void)sigaddset(&nset, SIGCHLD); 518 1.19 christos (void)sigprocmask(SIG_BLOCK, &nset, &oset); 519 1.16 christos if ((cp = findchild(pid, 0)) != NULL) { 520 1.16 christos if (cp->done) 521 1.16 christos delchild(cp); 522 1.16 christos else 523 1.16 christos cp->free = 1; 524 1.16 christos } 525 1.19 christos (void)sigprocmask(SIG_SETMASK, &oset, NULL); 526 1.1 cgd } 527