~<: add "- [HERE-delimiter]" for pasting etc. (Ralph Corderoy)
[s-mailx.git] / popen.c
blob67acbd83b5d4ef3e714545a8d9b7fa2eeb709a5d
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 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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 | n_O_NOFOLLOW | O_TRUNC},
118 {"wx", O_WRONLY | O_CREAT | O_EXCL},
119 {"a", O_WRONLY | O_APPEND | O_CREAT | n_O_NOFOLLOW},
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, <, n_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;
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 /* Ensure the I/O library doesn't optimize the fseek(3) away! */
187 if(!n_real_seek(fpp->fp, fpp->offset, SEEK_SET)){
188 outfd = errno;
189 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
190 n_shexp_quote_cp(fpp->realfile, FAL0), strerror(outfd));
191 goto jleave;
194 if ((fpp->flags & FP_MASK) == FP_MAILDIR) {
195 rv = maildir_append(fpp->realfile, fpp->fp, fpp->offset);
196 goto jleave;
199 outfd = open(fpp->realfile,
200 ((fpp->omode | O_CREAT | (fpp->omode & O_APPEND ? 0 : O_TRUNC) |
201 n_O_NOFOLLOW) & ~O_EXCL), 0666);
202 if (outfd == -1) {
203 outfd = errno;
204 n_err(_("Fatal: cannot create %s: %s\n"),
205 n_shexp_quote_cp(fpp->realfile, FAL0), strerror(outfd));
206 goto jleave;
209 cmd[2] = NULL;
210 switch (fpp->flags & FP_MASK) {
211 case FP_GZIP:
212 cmd[0] = "gzip"; cmd[1] = "-c"; break;
213 case FP_BZIP2:
214 cmd[0] = "bzip2"; cmd[1] = "-c"; break;
215 case FP_XZ:
216 cmd[0] = "xz"; cmd[1] = "-c"; break;
217 default:
218 cmd[0] = "cat"; cmd[1] = NULL; break;
219 case FP_HOOK:
220 cmd[0] = ok_vlook(SHELL);
221 cmd[1] = "-c";
222 cmd[2] = fpp->save_cmd;
224 if (run_command(cmd[0], 0, fileno(fpp->fp), outfd,
225 cmd[1], cmd[2], NULL, NULL) >= 0)
226 rv = OKAY;
228 close(outfd);
229 jleave:
230 NYD_LEAVE;
231 return rv;
234 static int
235 _file_load(int flags, int infd, int outfd, char const *load_cmd)
237 char const *cmd[3];
238 int rv;
239 NYD_ENTER;
241 cmd[2] = NULL;
242 switch (flags & FP_MASK) {
243 case FP_GZIP: cmd[0] = "gzip"; cmd[1] = "-cd"; break;
244 case FP_BZIP2: cmd[0] = "bzip2"; cmd[1] = "-cd"; break;
245 case FP_XZ: cmd[0] = "xz"; cmd[1] = "-cd"; break;
246 default: cmd[0] = "cat"; cmd[1] = NULL; break;
247 case FP_HOOK:
248 cmd[0] = ok_vlook(SHELL);
249 cmd[1] = "-c";
250 cmd[2] = load_cmd;
251 break;
252 case FP_MAILDIR:
253 rv = 0;
254 goto jleave;
257 rv = run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL);
258 jleave:
259 NYD_LEAVE;
260 return rv;
263 static enum okay
264 unregister_file(FILE *fp, struct termios **tiosp)
266 struct fp **pp, *p;
267 enum okay rv = OKAY;
268 NYD_ENTER;
270 if (tiosp)
271 *tiosp = NULL;
273 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
274 if (p->fp == fp) {
275 switch (p->flags & FP_MASK) {
276 case FP_RAW:
277 case FP_PIPE:
278 break;
279 default:
280 rv = _file_save(p);
281 break;
283 if ((p->flags & FP_UNLINK) && unlink(p->realfile))
284 rv = STOP;
286 *pp = p->link;
287 if (p->save_cmd != NULL)
288 free(p->save_cmd);
289 if (p->realfile != NULL)
290 free(p->realfile);
291 if (p->flags & FP_TERMIOS) {
292 if (tiosp != NULL)
293 *tiosp = p->fp_tios;
294 else
295 free(p->fp_tios);
297 free(p);
298 goto jleave;
300 DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
301 rv = STOP;
302 jleave:
303 NYD_LEAVE;
304 return rv;
307 static int
308 file_pid(FILE *fp)
310 int rv;
311 struct fp *p;
312 NYD2_ENTER;
314 rv = -1;
315 for (p = fp_head; p; p = p->link)
316 if (p->fp == fp) {
317 rv = p->pid;
318 break;
320 NYD2_LEAVE;
321 return rv;
324 static void
325 a_popen_jobsigs_up(void){
326 sigset_t nset, oset;
327 NYD2_ENTER;
329 sigfillset(&nset);
331 sigprocmask(SIG_BLOCK, &nset, &oset);
332 a_popen_otstp = safe_signal(SIGTSTP, &a_popen_jobsig);
333 a_popen_ottin = safe_signal(SIGTTIN, &a_popen_jobsig);
334 a_popen_ottou = safe_signal(SIGTTOU, &a_popen_jobsig);
336 /* This assumes oset contains nothing but SIGCHLD, so to say */
337 sigdelset(&oset, SIGTSTP);
338 sigdelset(&oset, SIGTTIN);
339 sigdelset(&oset, SIGTTOU);
340 sigprocmask(SIG_SETMASK, &oset, NULL);
341 NYD2_LEAVE;
344 static void
345 a_popen_jobsigs_down(void){
346 sigset_t nset, oset;
347 NYD2_ENTER;
349 sigfillset(&nset);
351 sigprocmask(SIG_BLOCK, &nset, &oset);
352 safe_signal(SIGTSTP, a_popen_otstp);
353 safe_signal(SIGTTIN, a_popen_ottin);
354 safe_signal(SIGTTOU, a_popen_ottou);
356 sigaddset(&oset, SIGTSTP);
357 sigaddset(&oset, SIGTTIN);
358 sigaddset(&oset, SIGTTOU);
359 sigprocmask(SIG_SETMASK, &oset, NULL);
360 NYD2_LEAVE;
363 static void
364 a_popen_jobsig(int sig){
365 sighandler_type oldact;
366 sigset_t nset;
367 bool_t hadsig;
368 NYD_X; /* Signal handler */
370 hadsig = (a_popen_hadsig != 0);
371 a_popen_hadsig = 1;
372 if(!hadsig)
373 n_TERMCAP_SUSPEND(TRU1);
375 oldact = safe_signal(sig, SIG_DFL);
377 sigemptyset(&nset);
378 sigaddset(&nset, sig);
379 sigprocmask(SIG_UNBLOCK, &nset, NULL);
380 n_raise(sig);
381 sigprocmask(SIG_BLOCK, &nset, NULL);
383 safe_signal(sig, oldact);
386 static void
387 _sigchld(int signo)
389 pid_t pid;
390 int status;
391 struct child *cp;
392 NYD_X; /* Signal handler */
393 n_UNUSED(signo);
395 for (;;) {
396 pid = waitpid(-1, &status, WNOHANG);
397 if (pid <= 0) {
398 if (pid == -1 && errno == EINTR)
399 continue;
400 break;
403 if ((cp = _findchild(pid, FAL0)) != NULL) {
404 if (cp->free)
405 cp->pid = -1; /* XXX Was _delchild(cp);# */
406 else {
407 cp->done = 1;
408 cp->status = status;
414 static struct child *
415 _findchild(int pid, bool_t create)
417 struct child **cpp;
418 NYD_ENTER;
420 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
421 cpp = &(*cpp)->link)
424 if (*cpp == NULL && create) {
425 *cpp = smalloc(sizeof **cpp);
426 (*cpp)->pid = pid;
427 (*cpp)->done = (*cpp)->free = 0;
428 (*cpp)->link = NULL;
430 NYD_LEAVE;
431 return *cpp;
434 static void
435 _delchild(struct child *cp)
437 struct child **cpp;
438 NYD_ENTER;
440 cpp = &_popen_child;
441 for (;;) {
442 if (*cpp == cp) {
443 *cpp = cp->link;
444 free(cp);
445 break;
447 if (*(cpp = &(*cpp)->link) == NULL) {
448 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
449 break;
452 NYD_LEAVE;
455 FL void
456 command_manager_start(void)
458 struct sigaction nact, oact;
459 NYD_ENTER;
461 nact.sa_handler = &_sigchld;
462 sigemptyset(&nact.sa_mask);
463 nact.sa_flags = SA_RESTART
464 #ifdef SA_NOCLDSTOP
465 | SA_NOCLDSTOP
466 #endif
468 if (sigaction(SIGCHLD, &nact, &oact) != 0)
469 n_panic(_("Cannot install signal handler for child process management"));
470 NYD_LEAVE;
473 FL FILE *
474 safe_fopen(char const *file, char const *oflags, int *xflags)
476 int osflags, fd;
477 FILE *fp = NULL;
478 NYD2_ENTER; /* (only for Fopen() and once in lex.c) */
480 if (scan_mode(oflags, &osflags) < 0)
481 goto jleave;
482 osflags |= _O_CLOEXEC;
483 if (xflags != NULL)
484 *xflags = osflags;
486 if ((fd = open(file, osflags, 0666)) == -1)
487 goto jleave;
488 _CLOEXEC_SET(fd);
490 fp = fdopen(fd, oflags);
491 jleave:
492 NYD2_LEAVE;
493 return fp;
496 FL FILE *
497 Fopen(char const *file, char const *oflags)
499 FILE *fp;
500 int osflags;
501 NYD_ENTER;
503 if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
504 register_file(fp, osflags, 0, FP_RAW, NULL, 0L, NULL, NULL);
505 NYD_LEAVE;
506 return fp;
509 FL FILE *
510 Fdopen(int fd, char const *oflags, bool_t nocloexec)
512 FILE *fp;
513 int osflags;
514 NYD_ENTER;
516 scan_mode(oflags, &osflags);
517 if (!nocloexec)
518 osflags |= _O_CLOEXEC; /* Ensured to be set by caller as documented! */
520 if ((fp = fdopen(fd, oflags)) != NULL)
521 register_file(fp, osflags, 0, FP_RAW, NULL, 0L, NULL, NULL);
522 NYD_LEAVE;
523 return fp;
526 FL int
527 Fclose(FILE *fp)
529 int i = 0;
530 NYD_ENTER;
532 if (unregister_file(fp, NULL) == OKAY)
533 i |= 1;
534 if (fclose(fp) == 0)
535 i |= 2;
536 NYD_LEAVE;
537 return (i == 3 ? 0 : EOF);
540 FL FILE *
541 Zopen(char const *file, char const *oflags) /* FIXME MESS! */
543 FILE *rv = NULL;
544 char const *cload = NULL, *csave = NULL;
545 int flags, osflags, mode, infd;
546 enum oflags rof;
547 long offset;
548 enum protocol p;
549 NYD_ENTER;
551 if (scan_mode(oflags, &osflags) < 0)
552 goto jleave;
554 flags = 0;
555 rof = OF_RDWR | OF_UNLINK;
556 if (osflags & O_APPEND)
557 rof |= OF_APPEND;
558 mode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
560 if ((osflags & O_APPEND) && ((p = which_protocol(file)) == PROTO_MAILDIR)) {
561 flags |= FP_MAILDIR;
562 osflags = O_RDWR | O_APPEND | O_CREAT | n_O_NOFOLLOW;
563 infd = -1;
564 } else {
565 char const *ext;
567 if ((ext = strrchr(file, '.')) != NULL) {
568 if (!asccasecmp(ext, ".gz"))
569 flags |= FP_GZIP;
570 else if (!asccasecmp(ext, ".xz")) {
571 flags |= FP_XZ;
572 osflags &= ~O_APPEND;
573 rof &= ~OF_APPEND;
574 } else if (!asccasecmp(ext, ".bz2")) {
575 flags |= FP_BZIP2;
576 osflags &= ~O_APPEND;
577 rof &= ~OF_APPEND;
578 } else {
579 #undef _X1
580 #define _X1 "file-hook-load-"
581 #undef _X2
582 #define _X2 "file-hook-save-"
583 size_t l = strlen(++ext);
584 char *vbuf = ac_alloc(l + n_MAX(sizeof(_X1), sizeof(_X2)));
586 memcpy(vbuf, _X1, sizeof(_X1) -1);
587 memcpy(vbuf + sizeof(_X1) -1, ext, l);
588 vbuf[sizeof(_X1) -1 + l] = '\0';
589 cload = n_var_vlook(vbuf, FAL0);
590 memcpy(vbuf, _X2, sizeof(_X2) -1);
591 memcpy(vbuf + sizeof(_X2) -1, ext, l);
592 vbuf[sizeof(_X2) -1 + l] = '\0';
593 csave = n_var_vlook(vbuf, FAL0);
594 #undef _X2
595 #undef _X1
596 ac_free(vbuf);
598 if ((csave != NULL) && (cload != NULL)) {
599 flags |= FP_HOOK;
600 osflags &= ~O_APPEND;
601 rof &= ~OF_APPEND;
602 } else if ((csave != NULL) | (cload != NULL)) {
603 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
604 "Treating as plain text!"), ext);
605 goto jraw;
606 } else
607 goto jraw;
609 } else {
610 jraw:
611 /*flags |= FP_RAW;*/
612 rv = Fopen(file, oflags);
613 goto jleave;
616 if ((infd = open(file, (mode & W_OK) ? O_RDWR : O_RDONLY)) == -1 &&
617 (!(osflags & O_CREAT) || errno != ENOENT))
618 goto jleave;
621 /* Note rv is not yet register_file()d, fclose() it in error path! */
622 if ((rv = Ftmp(NULL, "zopen", rof)) == NULL) {
623 n_perr(_("tmpfile"), 0);
624 goto jerr;
627 if (flags & FP_MAILDIR)
629 else if (infd >= 0) {
630 if (_file_load(flags, infd, fileno(rv), cload) < 0) {
631 jerr:
632 if (rv != NULL)
633 fclose(rv);
634 rv = NULL;
635 if (infd >= 0)
636 close(infd);
637 goto jleave;
639 } else {
640 if ((infd = creat(file, 0666)) == -1) {
641 fclose(rv);
642 rv = NULL;
643 goto jleave;
647 if (infd >= 0)
648 close(infd);
649 fflush(rv);
651 if (!(osflags & O_APPEND))
652 rewind(rv);
653 if ((offset = ftell(rv)) == -1) {
654 Fclose(rv);
655 rv = NULL;
656 goto jleave;
659 register_file(rv, osflags, 0, flags, file, offset, csave, NULL);
660 jleave:
661 NYD_LEAVE;
662 return rv;
665 FL FILE *
666 Ftmp(char **fn, char const *namehint, enum oflags oflags)
668 /* The 8 is arbitrary but leaves room for a six character suffix (the
669 * POSIX minimum path length is 14, though we don't check that XXX).
670 * 8 should be more than sufficient given that we use base64url encoding
671 * for our random string */
672 enum {_RANDCHARS = 8u};
674 char *cp_base, *cp;
675 size_t maxname, xlen, i;
676 char const *tmpdir;
677 int osoflags, fd, e;
678 bool_t relesigs;
679 FILE *fp;
680 NYD_ENTER;
682 assert(namehint != NULL);
683 assert((oflags & OF_WRONLY) || (oflags & OF_RDWR));
684 assert(!(oflags & OF_RDONLY));
685 assert(!(oflags & OF_REGISTER_UNLINK) || (oflags & OF_REGISTER));
687 fp = NULL;
688 relesigs = FAL0;
689 e = 0;
690 tmpdir = ok_vlook(TMPDIR);
691 maxname = NAME_MAX;
692 #ifdef HAVE_PATHCONF
693 { long pc;
695 if ((pc = pathconf(tmpdir, _PC_NAME_MAX)) != -1)
696 maxname = (size_t)pc;
698 #endif
700 if ((oflags & OF_SUFFIX) && *namehint != '\0') {
701 if ((xlen = strlen(namehint)) > maxname - _RANDCHARS) {
702 errno = ENAMETOOLONG;
703 goto jleave;
705 } else
706 xlen = 0;
708 /* Prepare the template string once, then iterate over the random range */
709 cp_base =
710 cp = smalloc(strlen(tmpdir) + 1 + maxname +1);
711 cp = sstpcpy(cp, tmpdir);
712 *cp++ = '/';
714 char *x = sstpcpy(cp, VAL_UAGENT);
715 *x++ = '-';
716 if (!(oflags & OF_SUFFIX))
717 x = sstpcpy(x, namehint);
719 i = PTR2SIZE(x - cp);
720 if (i > maxname - xlen - _RANDCHARS) {
721 size_t j = maxname - xlen - _RANDCHARS;
722 x -= i - j;
723 i = j;
726 if ((oflags & OF_SUFFIX) && xlen > 0)
727 memcpy(x + _RANDCHARS, namehint, xlen);
729 x[xlen + _RANDCHARS] = '\0';
730 cp = x;
733 osoflags = O_CREAT | O_EXCL | _O_CLOEXEC;
734 osoflags |= (oflags & OF_WRONLY) ? O_WRONLY : O_RDWR;
735 if (oflags & OF_APPEND)
736 osoflags |= O_APPEND;
738 for (i = 0;; ++i) {
739 memcpy(cp, getrandstring(_RANDCHARS), _RANDCHARS);
741 hold_all_sigs();
742 relesigs = TRU1;
744 if ((fd = open(cp_base, osoflags, 0600)) != -1) {
745 _CLOEXEC_SET(fd);
746 break;
748 if (i >= FTMP_OPEN_TRIES) {
749 e = errno;
750 goto jfree;
752 relesigs = FAL0;
753 rele_all_sigs();
756 if (oflags & OF_REGISTER) {
757 char const *osflags = (oflags & OF_RDWR ? "w+" : "w");
758 int osflagbits;
760 scan_mode(osflags, &osflagbits); /* TODO osoflags&xy ?!!? */
761 if ((fp = fdopen(fd, osflags)) != NULL)
762 register_file(fp, osflagbits | _O_CLOEXEC, 0,
763 (FP_RAW | (oflags & OF_REGISTER_UNLINK ? FP_UNLINK : 0)),
764 cp_base, 0L, NULL, NULL);
765 } else
766 fp = fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"));
768 if (fp == NULL || (oflags & OF_UNLINK)) {
769 e = errno;
770 unlink(cp_base);
771 goto jfree;
774 if (fn != NULL)
775 *fn = cp_base;
776 else
777 free(cp_base);
778 jleave:
779 if (relesigs && (fp == NULL || !(oflags & OF_HOLDSIGS)))
780 rele_all_sigs();
781 if (fp == NULL)
782 errno = e;
783 NYD_LEAVE;
784 return fp;
785 jfree:
786 if ((cp = cp_base) != NULL)
787 free(cp);
788 goto jleave;
791 FL void
792 Ftmp_release(char **fn)
794 char *cp;
795 NYD_ENTER;
797 cp = *fn;
798 *fn = NULL;
799 if (cp != NULL) {
800 unlink(cp);
801 rele_all_sigs();
802 free(cp);
804 NYD_LEAVE;
807 FL void
808 Ftmp_free(char **fn) /* TODO DROP: OF_REGISTER_FREEPATH! */
810 char *cp;
811 NYD_ENTER;
813 cp = *fn;
814 *fn = NULL;
815 if (cp != NULL)
816 free(cp);
817 NYD_LEAVE;
820 FL bool_t
821 pipe_cloexec(int fd[2])
823 bool_t rv = FAL0;
824 NYD_ENTER;
826 #ifdef HAVE_PIPE2
827 if (pipe2(fd, O_CLOEXEC) == -1)
828 goto jleave;
829 #else
830 if (pipe(fd) == -1)
831 goto jleave;
832 (void)fcntl(fd[0], F_SETFD, FD_CLOEXEC);
833 (void)fcntl(fd[1], F_SETFD, FD_CLOEXEC);
834 #endif
835 rv = TRU1;
836 jleave:
837 NYD_LEAVE;
838 return rv;
841 FL FILE *
842 Popen(char const *cmd, char const *mode, char const *sh,
843 char const **env_addon, int newfd1)
845 struct termios *tiosp;
846 int p[2], myside, hisside, fd0, fd1, pid;
847 char mod[2] = {'0', '\0'};
848 sigset_t nset;
849 FILE *rv = NULL;
850 NYD_ENTER;
852 /* First clean up child structures */
853 { sigset_t oset;
854 struct child **cpp, *cp;
856 sigfillset(&nset);
857 sigprocmask(SIG_BLOCK, &nset, &oset);
859 for (cpp = &_popen_child; *cpp != NULL;) {
860 if ((*cpp)->pid == -1) {
861 cp = *cpp;
862 *cpp = cp->link;
863 free(cp);
864 } else
865 cpp = &(*cpp)->link;
868 sigprocmask(SIG_SETMASK, &oset, NULL);
871 if (!pipe_cloexec(p))
872 goto jleave;
874 if (*mode == 'r') {
875 myside = p[READ];
876 fd0 = COMMAND_FD_PASS;
877 hisside = fd1 = p[WRITE];
878 mod[0] = *mode;
879 } else if (*mode == 'W') {
880 myside = p[WRITE];
881 hisside = fd0 = p[READ];
882 fd1 = newfd1;
883 mod[0] = 'w';
884 } else {
885 myside = p[WRITE];
886 hisside = fd0 = p[READ];
887 fd1 = COMMAND_FD_PASS;
888 mod[0] = 'w';
891 /* In interactive mode both STDIN and STDOUT point to the terminal. If we
892 * pass through the TTY restore terminal attributes after pipe returns.
893 * XXX It shouldn't matter which FD we actually use in this case */
894 if ((n_psonce & n_PSO_INTERACTIVE) && (fd0 == COMMAND_FD_PASS ||
895 fd1 == COMMAND_FD_PASS)) {
896 tiosp = smalloc(sizeof *tiosp);
897 tcgetattr(STDIN_FILENO, tiosp);
898 n_TERMCAP_SUSPEND(TRU1);
899 } else
900 tiosp = NULL;
902 sigemptyset(&nset);
904 if (cmd == (char*)-1) {
905 if ((pid = fork_child()) == -1)
906 n_perr(_("fork"), 0);
907 else if (pid == 0) {
908 union {char const *ccp; int (*ptf)(void); int es;} u;
909 prepare_child(&nset, fd0, fd1);
910 close(p[READ]);
911 close(p[WRITE]);
912 u.ccp = sh;
913 u.es = (*u.ptf)();
914 _exit(u.es);
916 } else if (sh == NULL) {
917 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
918 } else {
919 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
921 if (pid < 0) {
922 close(p[READ]);
923 close(p[WRITE]);
924 goto jleave;
926 close(hisside);
927 if ((rv = fdopen(myside, mod)) != NULL)
928 register_file(rv, 0, pid,
929 (tiosp == NULL ? FP_PIPE : FP_PIPE | FP_TERMIOS),
930 NULL, 0L, NULL, tiosp);
931 else
932 close(myside);
933 jleave:
934 NYD_LEAVE;
935 return rv;
938 FL bool_t
939 Pclose(FILE *ptr, bool_t dowait)
941 struct termios *tiosp;
942 sigset_t nset, oset;
943 int pid;
944 bool_t rv = FAL0;
945 NYD_ENTER;
947 pid = file_pid(ptr);
948 if (pid < 0)
949 goto jleave;
951 unregister_file(ptr, &tiosp);
952 fclose(ptr);
954 if (dowait) {
955 sigemptyset(&nset);
956 sigaddset(&nset, SIGINT);
957 sigaddset(&nset, SIGHUP);
958 sigprocmask(SIG_BLOCK, &nset, &oset);
959 rv = wait_child(pid, NULL);
960 if (tiosp != NULL) {
961 n_TERMCAP_RESUME(TRU1);
962 tcsetattr(STDIN_FILENO, TCSAFLUSH, tiosp);
964 sigprocmask(SIG_SETMASK, &oset, NULL);
965 } else {
966 free_child(pid);
967 rv = TRU1;
969 if (tiosp != NULL)
970 free(tiosp);
971 jleave:
972 NYD_LEAVE;
973 return rv;
976 FL FILE *
977 n_pager_open(void)
979 char const *env_add[2], *pager;
980 FILE *rv;
981 NYD_ENTER;
983 assert(n_psonce & n_PSO_INTERACTIVE);
985 pager = n_pager_get(env_add + 0);
986 env_add[1] = NULL;
988 if ((rv = Popen(pager, "w", NULL, env_add, COMMAND_FD_PASS)) == NULL)
989 n_perr(pager, 0);
990 NYD_LEAVE;
991 return rv;
994 FL bool_t
995 n_pager_close(FILE *fp)
997 sighandler_type sh;
998 bool_t rv;
999 NYD_ENTER;
1001 sh = safe_signal(SIGPIPE, SIG_IGN);
1002 rv = Pclose(fp, TRU1);
1003 safe_signal(SIGPIPE, sh);
1004 NYD_LEAVE;
1005 return rv;
1008 FL void
1009 close_all_files(void)
1011 NYD_ENTER;
1012 while (fp_head != NULL)
1013 if ((fp_head->flags & FP_MASK) == FP_PIPE)
1014 Pclose(fp_head->fp, TRU1);
1015 else
1016 Fclose(fp_head->fp);
1017 NYD_LEAVE;
1020 FL int
1021 fork_child(void)
1023 struct child *cp;
1024 int pid;
1025 NYD_ENTER;
1027 cp = _findchild(0, TRU1);
1029 if ((cp->pid = pid = fork()) == -1) {
1030 _delchild(cp);
1031 n_perr(_("fork"), 0);
1033 NYD_LEAVE;
1034 return pid;
1037 FL int
1038 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
1039 char const *a0, char const *a1, char const *a2, char const **env_addon)
1041 sigset_t nset, oset;
1042 sighandler_type soldint;
1043 bool_t tio_set;
1044 int rv;
1045 NYD_ENTER;
1047 /* TODO Of course this is a joke given that during a "p*" the PAGER may
1048 * TODO be up and running while we play around like this... but i guess
1049 * TODO this can't be helped at all unless we perform complete and true
1050 * TODO process group separation and ensure we don't deadlock us out
1051 * TODO via TTY jobcontrol signal storms (could this really happen?).
1052 * TODO Or have a builtin pager. Or query any necessity BEFORE we start
1053 * TODO any action, and shall we find we need to run programs dump it
1054 * TODO all into a temporary file which is then passed through to the
1055 * TODO PAGER. Ugh. That still won't help for "needsterminal" anyway */
1056 if ((tio_set = ((n_psonce & n_PSO_INTERACTIVE) &&
1057 (infd == COMMAND_FD_PASS || outfd == COMMAND_FD_PASS)))) {
1058 /* TODO Simply ignore SIGINT then, it surely will be ment for the program
1059 * TODO which takes the terminal */
1060 soldint = safe_signal(SIGINT, SIG_IGN);
1061 tcgetattr((n_psonce & n_PSO_TTYIN ? STDIN_FILENO : STDOUT_FILENO),
1062 &a_popen_tios);
1063 n_TERMCAP_SUSPEND(FAL0);
1064 sigfillset(&nset);
1065 sigdelset(&nset, SIGCHLD);
1066 sigdelset(&nset, SIGINT);
1067 /* sigdelset(&nset, SIGPIPE); TODO would need a handler */
1068 sigprocmask(SIG_BLOCK, &nset, &oset);
1069 a_popen_hadsig = 0;
1070 a_popen_jobsigs_up();
1073 if ((rv = start_command(cmd, mask, infd, outfd, a0, a1, a2, env_addon)) < 0)
1074 rv = -1;
1075 else {
1076 if (wait_child(rv, NULL))
1077 rv = 0;
1078 else {
1079 if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1080 n_err(_("Fatal error in process\n"));
1081 rv = -1;
1085 if (tio_set) {
1086 a_popen_jobsigs_down();
1087 tio_set = ((n_psonce & n_PSO_TTYIN) != 0);
1088 n_TERMCAP_RESUME(a_popen_hadsig ? TRU1 : FAL0);
1089 tcsetattr((tio_set ? STDIN_FILENO : STDOUT_FILENO),
1090 (tio_set ? TCSAFLUSH : TCSADRAIN), &a_popen_tios);
1091 if(soldint != SIG_IGN)
1092 safe_signal(SIGINT, soldint);
1093 sigprocmask(SIG_SETMASK, &oset, NULL);
1095 NYD_LEAVE;
1096 return rv;
1099 FL int
1100 start_command(char const *cmd, sigset_t *mask, int infd, int outfd,
1101 char const *a0, char const *a1, char const *a2,
1102 char const **env_addon)
1104 int rv;
1105 NYD_ENTER;
1107 if ((rv = fork_child()) == -1) {
1108 n_perr(_("fork"), 0);
1109 rv = -1;
1110 } else if (rv == 0) {
1111 char *argv[128];
1112 int i;
1114 if (env_addon != NULL) { /* TODO env_addon; should have struct child */
1115 extern char **environ;
1116 size_t ei, ei_orig, ai, ai_orig;
1117 char **env;
1119 /* TODO note we don't check the POSIX limit:
1120 * the total space used to store the environment and the arguments to
1121 * the process is limited to {ARG_MAX} bytes */
1122 for (ei = 0; environ[ei] != NULL; ++ei)
1124 ei_orig = ei;
1125 for (ai = 0; env_addon[ai] != NULL; ++ai)
1127 ai_orig = ai;
1128 env = ac_alloc(sizeof(*env) * (ei + ai +1));
1129 memcpy(env, environ, sizeof(*env) * ei);
1131 /* Replace all those keys that yet exist */
1132 while (ai-- > 0) {
1133 char const *ee, *kvs;
1134 size_t kl;
1136 ee = env_addon[ai];
1137 kvs = strchr(ee, '=');
1138 assert(kvs != NULL);
1139 kl = PTR2SIZE(kvs - ee);
1140 assert(kl > 0);
1141 for (ei = ei_orig; ei-- > 0;) {
1142 char const *ekvs = strchr(env[ei], '=');
1143 if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
1144 !memcmp(ee, env[ei], kl)) {
1145 env[ei] = n_UNCONST(ee);
1146 env_addon[ai] = NULL;
1147 break;
1152 /* And append the rest */
1153 for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
1154 if (env_addon[ai] != NULL)
1155 env[ei++] = n_UNCONST(env_addon[ai]);
1157 env[ei] = NULL;
1158 environ = env;
1161 i = (int)getrawlist(FAL0, argv, n_NELEM(argv), cmd, strlen(cmd));
1163 if ((argv[i++] = n_UNCONST(a0)) != NULL &&
1164 (argv[i++] = n_UNCONST(a1)) != NULL &&
1165 (argv[i++] = n_UNCONST(a2)) != NULL)
1166 argv[i] = NULL;
1167 prepare_child(mask, infd, outfd);
1168 execvp(argv[0], argv);
1169 perror(argv[0]);
1170 _exit(n_EXIT_ERR);
1172 NYD_LEAVE;
1173 return rv;
1176 FL void
1177 prepare_child(sigset_t *nset, int infd, int outfd)
1179 int i;
1180 sigset_t fset;
1181 NYD_ENTER;
1183 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
1184 /* TODO WHAT IS WITH STDERR_FILENO DAMN? */
1185 if ((i = (infd == COMMAND_FD_NULL)))
1186 infd = open("/dev/null", O_RDONLY);
1187 if (infd >= 0) {
1188 dup2(infd, STDIN_FILENO);
1189 if (i)
1190 close(infd);
1193 if ((i = (outfd == COMMAND_FD_NULL)))
1194 outfd = open("/dev/null", O_WRONLY);
1195 if (outfd >= 0) {
1196 dup2(outfd, STDOUT_FILENO);
1197 if (i)
1198 close(outfd);
1201 if (nset) {
1202 for (i = 1; i < NSIG; ++i)
1203 if (sigismember(nset, i))
1204 safe_signal(i, SIG_IGN);
1205 if (!sigismember(nset, SIGINT))
1206 safe_signal(SIGINT, SIG_DFL);
1209 sigemptyset(&fset);
1210 sigprocmask(SIG_SETMASK, &fset, NULL);
1211 NYD_LEAVE;
1214 FL void
1215 free_child(int pid)
1217 sigset_t nset, oset;
1218 struct child *cp;
1219 NYD_ENTER;
1221 sigemptyset(&nset);
1222 sigaddset(&nset, SIGCHLD);
1223 sigprocmask(SIG_BLOCK, &nset, &oset);
1225 if ((cp = _findchild(pid, FAL0)) != NULL) {
1226 if (cp->done)
1227 _delchild(cp);
1228 else
1229 cp->free = 1;
1232 sigprocmask(SIG_SETMASK, &oset, NULL);
1233 NYD_LEAVE;
1236 FL bool_t
1237 wait_child(int pid, int *wait_status)
1239 sigset_t nset, oset;
1240 struct child *cp;
1241 int ws;
1242 bool_t rv;
1243 NYD_ENTER;
1245 sigemptyset(&nset);
1246 sigaddset(&nset, SIGCHLD);
1247 sigprocmask(SIG_BLOCK, &nset, &oset);
1249 cp = _findchild(pid, FAL0);
1250 if (cp != NULL) {
1251 while (!cp->done)
1252 sigsuspend(&oset);
1253 ws = cp->status;
1254 _delchild(cp);
1255 } else
1256 ws = 0;
1258 sigprocmask(SIG_SETMASK, &oset, NULL);
1260 if (wait_status != NULL)
1261 *wait_status = ws;
1262 rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
1263 NYD_LEAVE;
1264 return rv;
1267 /* s-it-mode */