(BWDIC!) Rewrite variable handling..
[s-mailx.git] / popen.c
blobb2e093cd7dcc82a75d60d72cc2dc04db55580cf8
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Handling of pipes, child processes, temporary files, file enwrapping.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. 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.
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.
35 #undef n_FILE
36 #define n_FILE popen
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <sys/wait.h>
44 #define READ 0
45 #define WRITE 1
47 struct fp {
48 FILE *fp;
49 struct fp *link;
50 char *realfile;
51 char *save_cmd;
52 struct termios *fp_tios;
53 long offset;
54 int omode;
55 int pid;
56 enum {
57 FP_RAW = 0,
58 FP_GZIP = 1<<0,
59 FP_XZ = 1<<1,
60 FP_BZIP2 = 1<<2,
61 FP_MAILDIR = 1<<4,
62 FP_HOOK = 1<<5,
63 FP_PIPE = 1<<6,
64 FP_MASK = (1<<7) - 1,
65 /* TODO FP_UNLINK: should be in a separated process so that unlinking
66 * TODO the temporary "garbage" is "safe"(r than it is like that) */
67 FP_UNLINK = 1<<9,
68 FP_TERMIOS = 1<<10
69 } flags;
72 struct child {
73 int pid;
74 char done;
75 char free;
76 int status;
77 struct child *link;
80 static struct fp *fp_head;
81 static struct child *_popen_child;
83 /* TODO Rather temporary: deal with job control with FD_PASS */
84 static struct termios a_popen_tios;
85 static sighandler_type a_popen_otstp, a_popen_ottin, a_popen_ottou;
86 static volatile int a_popen_hadsig;
88 static int scan_mode(char const *mode, int *omode);
89 static void register_file(FILE *fp, int omode, int pid,
90 int flags, char const *realfile, long offset,
91 char const *save_cmd, struct termios *tiosp);
92 static enum okay _file_save(struct fp *fpp);
93 static int _file_load(int flags, int infd, int outfd,
94 char const *load_cmd);
95 static enum okay unregister_file(FILE *fp, struct termios **tiosp);
96 static int file_pid(FILE *fp);
98 /* TODO Rather temporary: deal with job control with FD_PASS */
99 static void a_popen_jobsigs_up(void);
100 static void a_popen_jobsigs_down(void);
101 static void a_popen_jobsig(int sig);
103 /* Handle SIGCHLD */
104 static void _sigchld(int signo);
106 static struct child *_findchild(int pid, bool_t create);
107 static void _delchild(struct child *cp);
109 static int
110 scan_mode(char const *mode, int *omode)
112 static struct {
113 char const mode[4];
114 int omode;
115 } const maps[] = {
116 {"r", O_RDONLY},
117 {"w", O_WRONLY | O_CREAT | O_TRUNC},
118 {"wx", O_WRONLY | O_CREAT | O_EXCL},
119 {"a", O_WRONLY | O_APPEND | O_CREAT},
120 {"a+", O_RDWR | O_APPEND},
121 {"r+", O_RDWR},
122 {"w+", O_RDWR | O_CREAT | O_EXCL}
125 int i;
126 NYD2_ENTER;
128 for (i = 0; UICMP(z, i, <, NELEM(maps)); ++i)
129 if (!strcmp(maps[i].mode, mode)) {
130 *omode = maps[i].omode;
131 i = 0;
132 goto jleave;
135 n_alert(_("Internal error: bad stdio open mode %s"), mode);
136 errno = EINVAL;
137 *omode = 0; /* (silence CC) */
138 i = -1;
139 jleave:
140 NYD2_LEAVE;
141 return i;
144 static void
145 register_file(FILE *fp, int omode, int pid, int flags,
146 char const *realfile, long offset, char const *save_cmd,
147 struct termios *tiosp)
149 struct fp *fpp;
150 NYD_ENTER;
152 assert(!(flags & FP_UNLINK) || realfile != NULL);
153 assert(!(flags & FP_TERMIOS) || tiosp != NULL);
155 fpp = smalloc(sizeof *fpp);
156 fpp->fp = fp;
157 fpp->omode = omode;
158 fpp->pid = pid;
159 fpp->link = fp_head;
160 fpp->flags = flags;
161 fpp->realfile = (realfile != NULL) ? sstrdup(realfile) : NULL;
162 fpp->save_cmd = (save_cmd != NULL) ? sstrdup(save_cmd) : NULL;
163 fpp->fp_tios = tiosp;
164 fpp->offset = offset;
165 fp_head = fpp;
166 NYD_LEAVE;
169 static enum okay
170 _file_save(struct fp *fpp)
172 char const *cmd[3];
173 int outfd, infd;
174 enum okay rv;
175 NYD_ENTER;
177 if (fpp->omode == O_RDONLY) {
178 rv = OKAY;
179 goto jleave;
181 rv = STOP;
183 fflush(fpp->fp);
184 clearerr(fpp->fp);
186 if ((fpp->flags & FP_MASK) == FP_MAILDIR) {
187 if (fseek(fpp->fp, fpp->offset, SEEK_SET) == -1) {
188 outfd = errno;
189 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
190 fpp->realfile, strerror(outfd));
191 goto jleave;
193 rv = maildir_append(fpp->realfile, fpp->fp, fpp->offset);
194 goto jleave;
197 /* Ensure the I/O library doesn't optimize the fseek(3) away! */
198 if(lseek(infd = fileno(fpp->fp), fpp->offset, SEEK_SET) == -1){
199 outfd = errno;
200 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
201 fpp->realfile, strerror(outfd));
202 goto jleave;
205 outfd = open(fpp->realfile,
206 ((fpp->omode | O_CREAT | (fpp->omode & O_APPEND ? 0 : O_TRUNC))
207 & ~O_EXCL), 0666);
208 if (outfd == -1) {
209 outfd = errno;
210 n_err(_("Fatal: cannot create %s: %s\n"),
211 fpp->realfile, strerror(outfd));
212 goto jleave;
215 cmd[2] = NULL;
216 switch (fpp->flags & FP_MASK) {
217 case FP_GZIP:
218 cmd[0] = "gzip"; cmd[1] = "-c"; break;
219 case FP_BZIP2:
220 cmd[0] = "bzip2"; cmd[1] = "-c"; break;
221 case FP_XZ:
222 cmd[0] = "xz"; cmd[1] = "-c"; break;
223 default:
224 cmd[0] = "cat"; cmd[1] = NULL; break;
225 case FP_HOOK:
226 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
227 cmd[0] = XSHELL;
228 cmd[1] = "-c";
229 cmd[2] = fpp->save_cmd;
231 if (run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL) >= 0)
232 rv = OKAY;
234 close(outfd);
235 jleave:
236 NYD_LEAVE;
237 return rv;
240 static int
241 _file_load(int flags, int infd, int outfd, char const *load_cmd)
243 char const *cmd[3];
244 int rv;
245 NYD_ENTER;
247 cmd[2] = NULL;
248 switch (flags & FP_MASK) {
249 case FP_GZIP: cmd[0] = "gzip"; cmd[1] = "-cd"; break;
250 case FP_BZIP2: cmd[0] = "bzip2"; cmd[1] = "-cd"; break;
251 case FP_XZ: cmd[0] = "xz"; cmd[1] = "-cd"; break;
252 default: cmd[0] = "cat"; cmd[1] = NULL; break;
253 case FP_HOOK:
254 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
255 cmd[0] = XSHELL;
256 cmd[1] = "-c";
257 cmd[2] = load_cmd;
258 break;
259 case FP_MAILDIR:
260 rv = 0;
261 goto jleave;
264 rv = run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL);
265 jleave:
266 NYD_LEAVE;
267 return rv;
270 static enum okay
271 unregister_file(FILE *fp, struct termios **tiosp)
273 struct fp **pp, *p;
274 enum okay rv = OKAY;
275 NYD_ENTER;
277 if (tiosp)
278 *tiosp = NULL;
280 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
281 if (p->fp == fp) {
282 switch (p->flags & FP_MASK) {
283 case FP_RAW:
284 case FP_PIPE:
285 break;
286 default:
287 rv = _file_save(p);
288 break;
290 if ((p->flags & FP_UNLINK) && unlink(p->realfile))
291 rv = STOP;
293 *pp = p->link;
294 if (p->save_cmd != NULL)
295 free(p->save_cmd);
296 if (p->realfile != NULL)
297 free(p->realfile);
298 if (p->flags & FP_TERMIOS) {
299 if (tiosp != NULL)
300 *tiosp = p->fp_tios;
301 else
302 free(p->fp_tios);
304 free(p);
305 goto jleave;
307 DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
308 rv = STOP;
309 jleave:
310 NYD_LEAVE;
311 return rv;
314 static int
315 file_pid(FILE *fp)
317 int rv;
318 struct fp *p;
319 NYD2_ENTER;
321 rv = -1;
322 for (p = fp_head; p; p = p->link)
323 if (p->fp == fp) {
324 rv = p->pid;
325 break;
327 NYD2_LEAVE;
328 return rv;
331 static void
332 a_popen_jobsigs_up(void){
333 sigset_t nset, oset;
334 NYD2_ENTER;
336 sigfillset(&nset);
338 sigprocmask(SIG_BLOCK, &nset, &oset);
339 a_popen_otstp = safe_signal(SIGTSTP, &a_popen_jobsig);
340 a_popen_ottin = safe_signal(SIGTTIN, &a_popen_jobsig);
341 a_popen_ottou = safe_signal(SIGTTOU, &a_popen_jobsig);
343 /* This assumes oset contains nothing but SIGCHLD, so to say */
344 sigdelset(&oset, SIGTSTP);
345 sigdelset(&oset, SIGTTIN);
346 sigdelset(&oset, SIGTTOU);
347 sigprocmask(SIG_SETMASK, &oset, NULL);
348 NYD2_LEAVE;
351 static void
352 a_popen_jobsigs_down(void){
353 sigset_t nset, oset;
354 NYD2_ENTER;
356 sigfillset(&nset);
358 sigprocmask(SIG_BLOCK, &nset, &oset);
359 safe_signal(SIGTSTP, a_popen_otstp);
360 safe_signal(SIGTTIN, a_popen_ottin);
361 safe_signal(SIGTTOU, a_popen_ottou);
363 sigaddset(&oset, SIGTSTP);
364 sigaddset(&oset, SIGTTIN);
365 sigaddset(&oset, SIGTTOU);
366 sigprocmask(SIG_SETMASK, &oset, NULL);
367 NYD2_LEAVE;
370 static void
371 a_popen_jobsig(int sig){
372 sighandler_type oldact;
373 sigset_t nset;
374 bool_t hadsig;
375 NYD_X; /* Signal handler */
377 hadsig = (a_popen_hadsig != 0);
378 a_popen_hadsig = 1;
379 if(!hadsig)
380 n_TERMCAP_SUSPEND(TRU1);
382 oldact = safe_signal(sig, SIG_DFL);
384 sigemptyset(&nset);
385 sigaddset(&nset, sig);
386 sigprocmask(SIG_UNBLOCK, &nset, NULL);
387 n_raise(sig);
388 sigprocmask(SIG_BLOCK, &nset, NULL);
390 safe_signal(sig, oldact);
393 static void
394 _sigchld(int signo)
396 pid_t pid;
397 int status;
398 struct child *cp;
399 NYD_X; /* Signal handler */
400 UNUSED(signo);
402 for (;;) {
403 pid = waitpid(-1, &status, WNOHANG);
404 if (pid <= 0) {
405 if (pid == -1 && errno == EINTR)
406 continue;
407 break;
410 if ((cp = _findchild(pid, FAL0)) != NULL) {
411 if (cp->free)
412 cp->pid = -1; /* XXX Was _delchild(cp);# */
413 else {
414 cp->done = 1;
415 cp->status = status;
421 static struct child *
422 _findchild(int pid, bool_t create)
424 struct child **cpp;
425 NYD_ENTER;
427 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
428 cpp = &(*cpp)->link)
431 if (*cpp == NULL && create) {
432 *cpp = smalloc(sizeof **cpp);
433 (*cpp)->pid = pid;
434 (*cpp)->done = (*cpp)->free = 0;
435 (*cpp)->link = NULL;
437 NYD_LEAVE;
438 return *cpp;
441 static void
442 _delchild(struct child *cp)
444 struct child **cpp;
445 NYD_ENTER;
447 cpp = &_popen_child;
448 for (;;) {
449 if (*cpp == cp) {
450 *cpp = cp->link;
451 free(cp);
452 break;
454 if (*(cpp = &(*cpp)->link) == NULL) {
455 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
456 break;
459 NYD_LEAVE;
462 FL void
463 command_manager_start(void)
465 struct sigaction nact, oact;
466 NYD_ENTER;
468 nact.sa_handler = &_sigchld;
469 sigemptyset(&nact.sa_mask);
470 nact.sa_flags = SA_RESTART
471 #ifdef SA_NOCLDSTOP
472 | SA_NOCLDSTOP
473 #endif
475 if (sigaction(SIGCHLD, &nact, &oact) != 0)
476 n_panic(_("Cannot install signal handler for child process management"));
477 NYD_LEAVE;
480 FL FILE *
481 safe_fopen(char const *file, char const *oflags, int *xflags)
483 int osflags, fd;
484 FILE *fp = NULL;
485 NYD2_ENTER; /* (only for Fopen() and once in lex.c) */
487 if (scan_mode(oflags, &osflags) < 0)
488 goto jleave;
489 osflags |= _O_CLOEXEC;
490 if (xflags != NULL)
491 *xflags = osflags;
493 if ((fd = open(file, osflags, 0666)) == -1)
494 goto jleave;
495 _CLOEXEC_SET(fd);
497 fp = fdopen(fd, oflags);
498 jleave:
499 NYD2_LEAVE;
500 return fp;
503 FL FILE *
504 Fopen(char const *file, char const *oflags)
506 FILE *fp;
507 int osflags;
508 NYD_ENTER;
510 if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
511 register_file(fp, osflags, 0, FP_RAW, NULL, 0L, NULL, NULL);
512 NYD_LEAVE;
513 return fp;
516 FL FILE *
517 Fdopen(int fd, char const *oflags, bool_t nocloexec)
519 FILE *fp;
520 int osflags;
521 NYD_ENTER;
523 scan_mode(oflags, &osflags);
524 if (!nocloexec)
525 osflags |= _O_CLOEXEC; /* Ensured to be set by caller as documented! */
527 if ((fp = fdopen(fd, oflags)) != NULL)
528 register_file(fp, osflags, 0, FP_RAW, NULL, 0L, NULL, NULL);
529 NYD_LEAVE;
530 return fp;
533 FL int
534 Fclose(FILE *fp)
536 int i = 0;
537 NYD_ENTER;
539 if (unregister_file(fp, NULL) == OKAY)
540 i |= 1;
541 if (fclose(fp) == 0)
542 i |= 2;
543 NYD_LEAVE;
544 return (i == 3 ? 0 : EOF);
547 FL FILE *
548 Zopen(char const *file, char const *oflags) /* FIXME MESS! */
550 FILE *rv = NULL;
551 char const *cload = NULL, *csave = NULL;
552 int flags, osflags, mode, infd;
553 enum oflags rof;
554 long offset;
555 enum protocol p;
556 NYD_ENTER;
558 if (scan_mode(oflags, &osflags) < 0)
559 goto jleave;
561 flags = 0;
562 rof = OF_RDWR | OF_UNLINK;
563 if (osflags & O_APPEND)
564 rof |= OF_APPEND;
565 mode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
567 if ((osflags & O_APPEND) && ((p = which_protocol(file)) == PROTO_MAILDIR)) {
568 flags |= FP_MAILDIR;
569 osflags = O_RDWR | O_APPEND | O_CREAT;
570 infd = -1;
571 } else {
572 char const *ext;
574 if ((ext = strrchr(file, '.')) != NULL) {
575 if (!asccasecmp(ext, ".gz"))
576 flags |= FP_GZIP;
577 else if (!asccasecmp(ext, ".xz")) {
578 flags |= FP_XZ;
579 osflags &= ~O_APPEND;
580 rof &= ~OF_APPEND;
581 } else if (!asccasecmp(ext, ".bz2")) {
582 flags |= FP_BZIP2;
583 osflags &= ~O_APPEND;
584 rof &= ~OF_APPEND;
585 } else {
586 #undef _X1
587 #define _X1 "file-hook-load-"
588 #undef _X2
589 #define _X2 "file-hook-save-"
590 size_t l = strlen(++ext);
591 char *vbuf = ac_alloc(l + MAX(sizeof(_X1), sizeof(_X2)));
593 memcpy(vbuf, _X1, sizeof(_X1) -1);
594 memcpy(vbuf + sizeof(_X1) -1, ext, l);
595 vbuf[sizeof(_X1) -1 + l] = '\0';
596 cload = vok_vlook(vbuf);
597 memcpy(vbuf, _X2, sizeof(_X2) -1);
598 memcpy(vbuf + sizeof(_X2) -1, ext, l);
599 vbuf[sizeof(_X2) -1 + l] = '\0';
600 csave = vok_vlook(vbuf);
601 #undef _X2
602 #undef _X1
603 ac_free(vbuf);
605 if ((csave != NULL) && (cload != NULL)) {
606 flags |= FP_HOOK;
607 osflags &= ~O_APPEND;
608 rof &= ~OF_APPEND;
609 } else if ((csave != NULL) | (cload != NULL)) {
610 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
611 "Treating as plain text!"), ext);
612 goto jraw;
613 } else
614 goto jraw;
616 } else {
617 jraw:
618 /*flags |= FP_RAW;*/
619 rv = Fopen(file, oflags);
620 goto jleave;
623 if ((infd = open(file, (mode & W_OK) ? O_RDWR : O_RDONLY)) == -1 &&
624 (!(osflags & O_CREAT) || errno != ENOENT))
625 goto jleave;
628 /* Note rv is not yet register_file()d, fclose() it in error path! */
629 if ((rv = Ftmp(NULL, "zopen", rof)) == NULL) {
630 n_perr(_("tmpfile"), 0);
631 goto jerr;
634 if (flags & FP_MAILDIR)
636 else if (infd >= 0) {
637 if (_file_load(flags, infd, fileno(rv), cload) < 0) {
638 jerr:
639 if (rv != NULL)
640 fclose(rv);
641 rv = NULL;
642 if (infd >= 0)
643 close(infd);
644 goto jleave;
646 } else {
647 if ((infd = creat(file, 0666)) == -1) {
648 fclose(rv);
649 rv = NULL;
650 goto jleave;
654 if (infd >= 0)
655 close(infd);
656 fflush(rv);
658 if (!(osflags & O_APPEND))
659 rewind(rv);
660 if ((offset = ftell(rv)) == -1) {
661 Fclose(rv);
662 rv = NULL;
663 goto jleave;
666 register_file(rv, osflags, 0, flags, file, offset, csave, NULL);
667 jleave:
668 NYD_LEAVE;
669 return rv;
672 FL FILE *
673 Ftmp(char **fn, char const *namehint, enum oflags oflags)
675 /* The 8 is arbitrary but leaves room for a six character suffix (the
676 * POSIX minimum path length is 14, though we don't check that XXX).
677 * 8 should be more than sufficient given that we use base64url encoding
678 * for our random string */
679 enum {_RANDCHARS = 8};
681 size_t maxname, xlen, i;
682 char *cp_base, *cp;
683 int osoflags, fd, e;
684 bool_t relesigs;
685 FILE *fp;
686 NYD_ENTER;
688 assert(namehint != NULL);
689 assert((oflags & OF_WRONLY) || (oflags & OF_RDWR));
690 assert(!(oflags & OF_RDONLY));
691 assert(!(oflags & OF_REGISTER_UNLINK) || (oflags & OF_REGISTER));
693 fp = NULL;
694 relesigs = FAL0;
695 e = 0;
696 maxname = NAME_MAX;
697 #ifdef HAVE_PATHCONF
698 { long pc;
700 if ((pc = pathconf(tempdir, _PC_NAME_MAX)) != -1)
701 maxname = (size_t)pc;
703 #endif
705 if ((oflags & OF_SUFFIX) && *namehint != '\0') {
706 if ((xlen = strlen(namehint)) > maxname - _RANDCHARS) {
707 errno = ENAMETOOLONG;
708 goto jleave;
710 } else
711 xlen = 0;
713 /* Prepare the template string once, then iterate over the random range */
714 cp_base =
715 cp = smalloc(strlen(tempdir) + 1 + maxname +1);
716 cp = sstpcpy(cp, tempdir);
717 *cp++ = '/';
719 char *x = sstpcpy(cp, UAGENT);
720 *x++ = '-';
721 if (!(oflags & OF_SUFFIX))
722 x = sstpcpy(x, namehint);
724 i = PTR2SIZE(x - cp);
725 if (i > maxname - xlen - _RANDCHARS) {
726 size_t j = maxname - xlen - _RANDCHARS;
727 x -= i - j;
728 i = j;
731 if ((oflags & OF_SUFFIX) && xlen > 0)
732 memcpy(x + _RANDCHARS, namehint, xlen);
734 x[xlen + _RANDCHARS] = '\0';
735 cp = x;
738 osoflags = O_CREAT | O_EXCL | _O_CLOEXEC;
739 osoflags |= (oflags & OF_WRONLY) ? O_WRONLY : O_RDWR;
740 if (oflags & OF_APPEND)
741 osoflags |= O_APPEND;
743 for (i = 0;; ++i) {
744 memcpy(cp, getrandstring(_RANDCHARS), _RANDCHARS);
746 hold_all_sigs();
747 relesigs = TRU1;
749 if ((fd = open(cp_base, osoflags, 0600)) != -1) {
750 _CLOEXEC_SET(fd);
751 break;
753 if (i >= FTMP_OPEN_TRIES) {
754 e = errno;
755 goto jfree;
757 relesigs = FAL0;
758 rele_all_sigs();
761 if (oflags & OF_REGISTER) {
762 char const *osflags = (oflags & OF_RDWR ? "w+" : "w");
763 int osflagbits;
765 scan_mode(osflags, &osflagbits); /* TODO osoflags&xy ?!!? */
766 if ((fp = fdopen(fd, osflags)) != NULL)
767 register_file(fp, osflagbits | _O_CLOEXEC, 0,
768 (FP_RAW | (oflags & OF_REGISTER_UNLINK ? FP_UNLINK : 0)),
769 cp_base, 0L, NULL, NULL);
770 } else
771 fp = fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"));
773 if (fp == NULL || (oflags & OF_UNLINK)) {
774 e = errno;
775 unlink(cp_base);
776 goto jfree;
779 if (fn != NULL)
780 *fn = cp_base;
781 else
782 free(cp_base);
783 jleave:
784 if (relesigs && (fp == NULL || !(oflags & OF_HOLDSIGS)))
785 rele_all_sigs();
786 if (fp == NULL)
787 errno = e;
788 NYD_LEAVE;
789 return fp;
790 jfree:
791 if ((cp = cp_base) != NULL)
792 free(cp);
793 goto jleave;
796 FL void
797 Ftmp_release(char **fn)
799 char *cp;
800 NYD_ENTER;
802 cp = *fn;
803 *fn = NULL;
804 if (cp != NULL) {
805 unlink(cp);
806 rele_all_sigs();
807 free(cp);
809 NYD_LEAVE;
812 FL void
813 Ftmp_free(char **fn) /* TODO DROP: OF_REGISTER_FREEPATH! */
815 char *cp;
816 NYD_ENTER;
818 cp = *fn;
819 *fn = NULL;
820 if (cp != NULL)
821 free(cp);
822 NYD_LEAVE;
825 FL bool_t
826 pipe_cloexec(int fd[2])
828 bool_t rv = FAL0;
829 NYD_ENTER;
831 #ifdef HAVE_PIPE2
832 if (pipe2(fd, O_CLOEXEC) == -1)
833 goto jleave;
834 #else
835 if (pipe(fd) == -1)
836 goto jleave;
837 (void)fcntl(fd[0], F_SETFD, FD_CLOEXEC);
838 (void)fcntl(fd[1], F_SETFD, FD_CLOEXEC);
839 #endif
840 rv = TRU1;
841 jleave:
842 NYD_LEAVE;
843 return rv;
846 FL FILE *
847 Popen(char const *cmd, char const *mode, char const *sh,
848 char const **env_addon, int newfd1)
850 struct termios *tiosp;
851 int p[2], myside, hisside, fd0, fd1, pid;
852 char mod[2] = {'0', '\0'};
853 sigset_t nset;
854 FILE *rv = NULL;
855 NYD_ENTER;
857 /* First clean up child structures */
858 { sigset_t oset;
859 struct child **cpp, *cp;
861 sigfillset(&nset);
862 sigprocmask(SIG_BLOCK, &nset, &oset);
864 for (cpp = &_popen_child; *cpp != NULL;) {
865 if ((*cpp)->pid == -1) {
866 cp = *cpp;
867 *cpp = cp->link;
868 free(cp);
869 } else
870 cpp = &(*cpp)->link;
873 sigprocmask(SIG_SETMASK, &oset, NULL);
876 if (!pipe_cloexec(p))
877 goto jleave;
879 if (*mode == 'r') {
880 myside = p[READ];
881 fd0 = COMMAND_FD_PASS;
882 hisside = fd1 = p[WRITE];
883 mod[0] = *mode;
884 } else if (*mode == 'W') {
885 myside = p[WRITE];
886 hisside = fd0 = p[READ];
887 fd1 = newfd1;
888 mod[0] = 'w';
889 } else {
890 myside = p[WRITE];
891 hisside = fd0 = p[READ];
892 fd1 = COMMAND_FD_PASS;
893 mod[0] = 'w';
896 /* In interactive mode both STDIN and STDOUT point to the terminal. If we
897 * pass through the TTY restore terminal attributes after pipe returns.
898 * XXX It shouldn't matter which FD we actually use in this case */
899 if ((options & OPT_INTERACTIVE) && (fd0 == COMMAND_FD_PASS ||
900 fd1 == COMMAND_FD_PASS)) {
901 tiosp = smalloc(sizeof *tiosp);
902 tcgetattr(STDIN_FILENO, tiosp);
903 n_TERMCAP_SUSPEND(TRU1);
904 } else
905 tiosp = NULL;
907 sigemptyset(&nset);
909 if (cmd == (char*)-1) {
910 if ((pid = fork_child()) == -1)
911 n_perr(_("fork"), 0);
912 else if (pid == 0) {
913 union {char const *ccp; int (*ptf)(void); int es;} u;
914 prepare_child(&nset, fd0, fd1);
915 close(p[READ]);
916 close(p[WRITE]);
917 u.ccp = sh;
918 u.es = (*u.ptf)();
919 _exit(u.es);
921 } else if (sh == NULL) {
922 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
923 } else {
924 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
926 if (pid < 0) {
927 close(p[READ]);
928 close(p[WRITE]);
929 goto jleave;
931 close(hisside);
932 if ((rv = fdopen(myside, mod)) != NULL)
933 register_file(rv, 0, pid,
934 (tiosp == NULL ? FP_PIPE : FP_PIPE | FP_TERMIOS),
935 NULL, 0L, NULL, tiosp);
936 else
937 close(myside);
938 jleave:
939 NYD_LEAVE;
940 return rv;
943 FL bool_t
944 Pclose(FILE *ptr, bool_t dowait)
946 struct termios *tiosp;
947 sigset_t nset, oset;
948 int pid;
949 bool_t rv = FAL0;
950 NYD_ENTER;
952 pid = file_pid(ptr);
953 if (pid < 0)
954 goto jleave;
956 unregister_file(ptr, &tiosp);
957 fclose(ptr);
959 if (dowait) {
960 sigemptyset(&nset);
961 sigaddset(&nset, SIGINT);
962 sigaddset(&nset, SIGHUP);
963 sigprocmask(SIG_BLOCK, &nset, &oset);
964 rv = wait_child(pid, NULL);
965 if (tiosp != NULL) {
966 n_TERMCAP_RESUME(TRU1);
967 tcsetattr(STDIN_FILENO, TCSAFLUSH, tiosp);
969 sigprocmask(SIG_SETMASK, &oset, NULL);
970 } else {
971 free_child(pid);
972 rv = TRU1;
974 if (tiosp != NULL)
975 free(tiosp);
976 jleave:
977 NYD_LEAVE;
978 return rv;
981 FL FILE *
982 n_pager_open(void)
984 char const *env_add[2], *pager;
985 FILE *rv;
986 NYD_ENTER;
988 assert(options & OPT_INTERACTIVE);
990 pager = n_pager_get(env_add + 0);
991 env_add[1] = NULL;
993 if ((rv = Popen(pager, "w", NULL, env_add, COMMAND_FD_PASS)) == NULL)
994 n_perr(pager, 0);
995 NYD_LEAVE;
996 return rv;
999 FL bool_t
1000 n_pager_close(FILE *fp)
1002 sighandler_type sh;
1003 bool_t rv;
1004 NYD_ENTER;
1006 sh = safe_signal(SIGPIPE, SIG_IGN);
1007 rv = Pclose(fp, TRU1);
1008 safe_signal(SIGPIPE, sh);
1009 NYD_LEAVE;
1010 return rv;
1013 FL void
1014 close_all_files(void)
1016 NYD_ENTER;
1017 while (fp_head != NULL)
1018 if ((fp_head->flags & FP_MASK) == FP_PIPE)
1019 Pclose(fp_head->fp, TRU1);
1020 else
1021 Fclose(fp_head->fp);
1022 NYD_LEAVE;
1025 FL int
1026 fork_child(void)
1028 struct child *cp;
1029 int pid;
1030 NYD_ENTER;
1032 cp = _findchild(0, TRU1);
1034 if ((cp->pid = pid = fork()) == -1) {
1035 _delchild(cp);
1036 n_perr(_("fork"), 0);
1038 NYD_LEAVE;
1039 return pid;
1042 FL int
1043 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
1044 char const *a0, char const *a1, char const *a2, char const **env_addon)
1046 sigset_t nset, oset;
1047 bool_t tio_set;
1048 int rv;
1049 NYD_ENTER;
1051 /* TODO Of course this is a joke given that during a "p*" the PAGER may
1052 * TODO be up and running while we play around like this... but i guess
1053 * TODO this can't be helped at all unless we perform complete and true
1054 * TODO process group separation and ensure we don't deadlock us out
1055 * TODO via TTY jobcontrol signal storms (could this really happen?).
1056 * TODO Or have a builtin pager. Or query any necessity BEFORE we start
1057 * TODO any action, and shall we find we need to run programs dump it
1058 * TODO all into a temporary file which is then passed through to the
1059 * TODO PAGER. Ugh. That still won't help for "needsterminal" anyway */
1060 if ((tio_set = ((options & OPT_INTERACTIVE) &&
1061 (infd == COMMAND_FD_PASS || outfd == COMMAND_FD_PASS)))) {
1062 tcgetattr((options & OPT_TTYIN ? STDIN_FILENO : STDOUT_FILENO), &a_popen_tios);
1063 n_TERMCAP_SUSPEND(FAL0);
1064 sigfillset(&nset);
1065 sigdelset(&nset, SIGCHLD);
1066 /* sigdelset(&nset, SIGPIPE); TODO would need a handler */
1067 sigprocmask(SIG_BLOCK, &nset, &oset);
1068 a_popen_hadsig = 0;
1069 a_popen_jobsigs_up();
1072 if ((rv = start_command(cmd, mask, infd, outfd, a0, a1, a2, env_addon)) < 0)
1073 rv = -1;
1074 else {
1075 if (wait_child(rv, NULL))
1076 rv = 0;
1077 else {
1078 if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1079 n_err(_("Fatal error in process\n"));
1080 rv = -1;
1084 if (tio_set) {
1085 a_popen_jobsigs_down();
1086 tio_set = ((options & OPT_TTYIN) != 0);
1087 n_TERMCAP_RESUME(a_popen_hadsig ? TRU1 : FAL0);
1088 tcsetattr((tio_set ? STDIN_FILENO : STDOUT_FILENO),
1089 (tio_set ? TCSAFLUSH : TCSADRAIN), &a_popen_tios);
1090 sigprocmask(SIG_SETMASK, &oset, NULL);
1092 NYD_LEAVE;
1093 return rv;
1096 FL int
1097 start_command(char const *cmd, sigset_t *mask, int infd, int outfd,
1098 char const *a0, char const *a1, char const *a2,
1099 char const **env_addon)
1101 int rv;
1102 NYD_ENTER;
1104 if ((rv = fork_child()) == -1) {
1105 n_perr(_("fork"), 0);
1106 rv = -1;
1107 } else if (rv == 0) {
1108 char *argv[128];
1109 int i;
1111 if (env_addon != NULL) { /* TODO env_addon; should have struct child */
1112 extern char **environ;
1113 size_t ei, ei_orig, ai, ai_orig;
1114 char **env;
1116 /* TODO note we don't check the POSIX limit:
1117 * the total space used to store the environment and the arguments to
1118 * the process is limited to {ARG_MAX} bytes */
1119 for (ei = 0; environ[ei] != NULL; ++ei)
1121 ei_orig = ei;
1122 for (ai = 0; env_addon[ai] != NULL; ++ai)
1124 ai_orig = ai;
1125 env = ac_alloc(sizeof(*env) * (ei + ai +1));
1126 memcpy(env, environ, sizeof(*env) * ei);
1128 /* Replace all those keys that yet exist */
1129 while (ai-- > 0) {
1130 char const *ee, *kvs;
1131 size_t kl;
1133 ee = env_addon[ai];
1134 kvs = strchr(ee, '=');
1135 assert(kvs != NULL);
1136 kl = PTR2SIZE(kvs - ee);
1137 assert(kl > 0);
1138 for (ei = ei_orig; ei-- > 0;) {
1139 char const *ekvs = strchr(env[ei], '=');
1140 if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
1141 !memcmp(ee, env[ei], kl)) {
1142 env[ei] = UNCONST(ee);
1143 env_addon[ai] = NULL;
1144 break;
1149 /* And append the rest */
1150 for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
1151 if (env_addon[ai] != NULL)
1152 env[ei++] = UNCONST(env_addon[ai]);
1154 env[ei] = NULL;
1155 environ = env;
1158 i = getrawlist(cmd, strlen(cmd), argv, NELEM(argv), 0);
1160 if ((argv[i++] = UNCONST(a0)) != NULL &&
1161 (argv[i++] = UNCONST(a1)) != NULL &&
1162 (argv[i++] = UNCONST(a2)) != NULL)
1163 argv[i] = NULL;
1164 prepare_child(mask, infd, outfd);
1165 execvp(argv[0], argv);
1166 perror(argv[0]);
1167 _exit(EXIT_ERR);
1169 NYD_LEAVE;
1170 return rv;
1173 FL void
1174 prepare_child(sigset_t *nset, int infd, int outfd)
1176 int i;
1177 sigset_t fset;
1178 NYD_ENTER;
1180 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
1181 /* TODO WHAT IS WITH STDERR_FILENO DAMN? */
1182 if ((i = (infd == COMMAND_FD_NULL)))
1183 infd = open("/dev/null", O_RDONLY);
1184 if (infd >= 0) {
1185 dup2(infd, STDIN_FILENO);
1186 if (i)
1187 close(infd);
1190 if ((i = (outfd == COMMAND_FD_NULL)))
1191 outfd = open("/dev/null", O_WRONLY);
1192 if (outfd >= 0) {
1193 dup2(outfd, STDOUT_FILENO);
1194 if (i)
1195 close(outfd);
1198 if (nset) {
1199 for (i = 1; i < NSIG; ++i)
1200 if (sigismember(nset, i))
1201 safe_signal(i, SIG_IGN);
1202 if (!sigismember(nset, SIGINT))
1203 safe_signal(SIGINT, SIG_DFL);
1206 sigemptyset(&fset);
1207 sigprocmask(SIG_SETMASK, &fset, NULL);
1208 NYD_LEAVE;
1211 FL void
1212 free_child(int pid)
1214 sigset_t nset, oset;
1215 struct child *cp;
1216 NYD_ENTER;
1218 sigemptyset(&nset);
1219 sigaddset(&nset, SIGCHLD);
1220 sigprocmask(SIG_BLOCK, &nset, &oset);
1222 if ((cp = _findchild(pid, FAL0)) != NULL) {
1223 if (cp->done)
1224 _delchild(cp);
1225 else
1226 cp->free = 1;
1229 sigprocmask(SIG_SETMASK, &oset, NULL);
1230 NYD_LEAVE;
1233 FL bool_t
1234 wait_child(int pid, int *wait_status)
1236 sigset_t nset, oset;
1237 struct child *cp;
1238 int ws;
1239 bool_t rv;
1240 NYD_ENTER;
1242 sigemptyset(&nset);
1243 sigaddset(&nset, SIGCHLD);
1244 sigprocmask(SIG_BLOCK, &nset, &oset);
1246 cp = _findchild(pid, FAL0);
1247 if (cp != NULL) {
1248 while (!cp->done)
1249 sigsuspend(&oset);
1250 ws = cp->status;
1251 _delchild(cp);
1252 } else
1253 ws = 0;
1255 sigprocmask(SIG_SETMASK, &oset, NULL);
1257 if (wait_status != NULL)
1258 *wait_status = ws;
1259 rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
1260 NYD_LEAVE;
1261 return rv;
1264 /* s-it-mode */