Add terminfo(5) support (via WANT_TERMCAP_PREFER_TERMINFO)..
[s-mailx.git] / popen.c
blob506e2519cd193156e1740f0d778f8e9920eae9b3
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 long offset;
53 int omode;
54 int pipe;
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_MASK = (1<<6) - 1,
64 /* TODO FP_UNLINK: should be in a separated process so that unlinking
65 * TODO the temporary "garbage" is "safe"(r than it is like that) */
66 FP_UNLINK = 1<<6
67 } flags;
70 struct child {
71 int pid;
72 char done;
73 char free;
74 int status;
75 struct child *link;
78 static struct fp *fp_head;
79 static struct child *_popen_child;
81 /* TODO Rather temporary: deal with job control with FD_PASS */
82 static struct termios a_popen_tios;
83 static sighandler_type a_popen_otstp, a_popen_ottin, a_popen_ottou;
84 static volatile int a_popen_hadsig;
86 static int scan_mode(char const *mode, int *omode);
87 static void register_file(FILE *fp, int omode, int ispipe, int pid,
88 int flags, char const *realfile, long offset,
89 char const *save_cmd);
90 static enum okay _file_save(struct fp *fpp);
91 static int _file_load(int flags, int infd, int outfd,
92 char const *load_cmd);
93 static enum okay unregister_file(FILE *fp);
94 static int file_pid(FILE *fp);
96 /* TODO Rather temporary: deal with job control with FD_PASS */
97 static void a_popen_jobsigs_up(void);
98 static void a_popen_jobsigs_down(void);
99 static void a_popen_jobsig(int sig);
101 /* Handle SIGCHLD */
102 static void _sigchld(int signo);
104 static struct child *_findchild(int pid, bool_t create);
105 static void _delchild(struct child *cp);
107 static int
108 scan_mode(char const *mode, int *omode)
110 static struct {
111 char const mode[4];
112 int omode;
113 } const maps[] = {
114 {"r", O_RDONLY},
115 {"w", O_WRONLY | O_CREAT | O_TRUNC},
116 {"wx", O_WRONLY | O_CREAT | O_EXCL},
117 {"a", O_WRONLY | O_APPEND | O_CREAT},
118 {"a+", O_RDWR | O_APPEND},
119 {"r+", O_RDWR},
120 {"w+", O_RDWR | O_CREAT | O_EXCL}
123 int i;
124 NYD2_ENTER;
126 for (i = 0; UICMP(z, i, <, NELEM(maps)); ++i)
127 if (!strcmp(maps[i].mode, mode)) {
128 *omode = maps[i].omode;
129 i = 0;
130 goto jleave;
133 n_alert(_("Internal error: bad stdio open mode %s"), mode);
134 errno = EINVAL;
135 *omode = 0; /* (silence CC) */
136 i = -1;
137 jleave:
138 NYD2_LEAVE;
139 return i;
142 static void
143 register_file(FILE *fp, int omode, int ispipe, int pid, int flags,
144 char const *realfile, long offset, char const *save_cmd)
146 struct fp *fpp;
147 NYD_ENTER;
149 assert(!(flags & FP_UNLINK) || realfile != NULL);
151 fpp = smalloc(sizeof *fpp);
152 fpp->fp = fp;
153 fpp->omode = omode;
154 fpp->pipe = ispipe;
155 fpp->pid = pid;
156 fpp->link = fp_head;
157 fpp->flags = flags;
158 fpp->realfile = (realfile != NULL) ? sstrdup(realfile) : NULL;
159 fpp->save_cmd = (save_cmd != NULL) ? sstrdup(save_cmd) : NULL;
160 fpp->offset = offset;
161 fp_head = fpp;
162 NYD_LEAVE;
165 static enum okay
166 _file_save(struct fp *fpp)
168 char const *cmd[3];
169 int outfd, infd;
170 enum okay rv;
171 NYD_ENTER;
173 if (fpp->omode == O_RDONLY) {
174 rv = OKAY;
175 goto jleave;
177 rv = STOP;
179 fflush(fpp->fp);
180 clearerr(fpp->fp);
182 if ((fpp->flags & FP_MASK) == FP_MAILDIR) {
183 if (fseek(fpp->fp, fpp->offset, SEEK_SET) == -1) {
184 outfd = errno;
185 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
186 fpp->realfile, strerror(outfd));
187 goto jleave;
189 rv = maildir_append(fpp->realfile, fpp->fp, fpp->offset);
190 goto jleave;
193 /* Ensure the I/O library doesn't optimize the fseek(3) away! */
194 if(lseek(infd = fileno(fpp->fp), fpp->offset, SEEK_SET) == -1){
195 outfd = errno;
196 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
197 fpp->realfile, strerror(outfd));
198 goto jleave;
201 outfd = open(fpp->realfile,
202 ((fpp->omode | O_CREAT | (fpp->omode & O_APPEND ? 0 : O_TRUNC))
203 & ~O_EXCL), 0666);
204 if (outfd == -1) {
205 outfd = errno;
206 n_err(_("Fatal: cannot create %s: %s\n"),
207 fpp->realfile, strerror(outfd));
208 goto jleave;
211 cmd[2] = NULL;
212 switch (fpp->flags & FP_MASK) {
213 case FP_GZIP:
214 cmd[0] = "gzip"; cmd[1] = "-c"; break;
215 case FP_BZIP2:
216 cmd[0] = "bzip2"; cmd[1] = "-c"; break;
217 case FP_XZ:
218 cmd[0] = "xz"; cmd[1] = "-c"; break;
219 default:
220 cmd[0] = "cat"; cmd[1] = NULL; break;
221 case FP_HOOK:
222 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
223 cmd[0] = XSHELL;
224 cmd[1] = "-c";
225 cmd[2] = fpp->save_cmd;
227 if (run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL) >= 0)
228 rv = OKAY;
230 close(outfd);
231 jleave:
232 NYD_LEAVE;
233 return rv;
236 static int
237 _file_load(int flags, int infd, int outfd, char const *load_cmd)
239 char const *cmd[3];
240 int rv;
241 NYD_ENTER;
243 cmd[2] = NULL;
244 switch (flags & FP_MASK) {
245 case FP_GZIP: cmd[0] = "gzip"; cmd[1] = "-cd"; break;
246 case FP_BZIP2: cmd[0] = "bzip2"; cmd[1] = "-cd"; break;
247 case FP_XZ: cmd[0] = "xz"; cmd[1] = "-cd"; break;
248 default: cmd[0] = "cat"; cmd[1] = NULL; break;
249 case FP_HOOK:
250 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
251 cmd[0] = XSHELL;
252 cmd[1] = "-c";
253 cmd[2] = load_cmd;
254 break;
255 case FP_MAILDIR:
256 rv = 0;
257 goto jleave;
260 rv = run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL);
261 jleave:
262 NYD_LEAVE;
263 return rv;
266 static enum okay
267 unregister_file(FILE *fp)
269 struct fp **pp, *p;
270 enum okay rv = OKAY;
271 NYD_ENTER;
273 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
274 if (p->fp == fp) {
275 if ((p->flags & FP_MASK) != FP_RAW) /* TODO ;} */
276 rv = _file_save(p);
277 if (p->flags & FP_UNLINK && unlink(p->realfile))
278 rv = STOP;
279 *pp = p->link;
280 if (p->save_cmd != NULL)
281 free(p->save_cmd);
282 if (p->realfile != NULL)
283 free(p->realfile);
284 free(p);
285 goto jleave;
287 DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
288 rv = STOP;
289 jleave:
290 NYD_LEAVE;
291 return rv;
294 static int
295 file_pid(FILE *fp)
297 int rv;
298 struct fp *p;
299 NYD2_ENTER;
301 rv = -1;
302 for (p = fp_head; p; p = p->link)
303 if (p->fp == fp) {
304 rv = p->pid;
305 break;
307 NYD2_LEAVE;
308 return rv;
311 static void
312 a_popen_jobsigs_up(void){
313 sigset_t nset, oset;
314 NYD2_ENTER;
316 sigfillset(&nset);
318 sigprocmask(SIG_BLOCK, &nset, &oset);
319 a_popen_otstp = safe_signal(SIGTSTP, &a_popen_jobsig);
320 a_popen_ottin = safe_signal(SIGTTIN, &a_popen_jobsig);
321 a_popen_ottou = safe_signal(SIGTTOU, &a_popen_jobsig);
323 /* This assumes oset contains nothing but SIGCHLD, so to say */
324 sigdelset(&oset, SIGTSTP);
325 sigdelset(&oset, SIGTTIN);
326 sigdelset(&oset, SIGTTOU);
327 sigprocmask(SIG_SETMASK, &oset, NULL);
328 NYD2_LEAVE;
331 static void
332 a_popen_jobsigs_down(void){
333 sigset_t nset, oset;
334 NYD2_ENTER;
336 sigfillset(&nset);
338 sigprocmask(SIG_BLOCK, &nset, &oset);
339 safe_signal(SIGTSTP, a_popen_otstp);
340 safe_signal(SIGTTIN, a_popen_ottin);
341 safe_signal(SIGTTOU, a_popen_ottou);
343 sigaddset(&oset, SIGTSTP);
344 sigaddset(&oset, SIGTTIN);
345 sigaddset(&oset, SIGTTOU);
346 sigprocmask(SIG_SETMASK, &oset, NULL);
347 NYD2_LEAVE;
350 static void
351 a_popen_jobsig(int sig){
352 sighandler_type oldact;
353 sigset_t nset;
354 bool_t hadsig;
355 NYD_X; /* Signal handler */
357 hadsig = (a_popen_hadsig != 0);
358 a_popen_hadsig = 1;
359 if(!hadsig)
360 n_TERMCAP_SUSPEND(TRU1);
362 oldact = safe_signal(sig, SIG_DFL);
364 sigemptyset(&nset);
365 sigaddset(&nset, sig);
366 sigprocmask(SIG_UNBLOCK, &nset, NULL);
367 n_raise(sig);
368 sigprocmask(SIG_BLOCK, &nset, NULL);
370 safe_signal(sig, oldact);
373 static void
374 _sigchld(int signo)
376 pid_t pid;
377 int status;
378 struct child *cp;
379 NYD_X; /* Signal handler */
380 UNUSED(signo);
382 for (;;) {
383 pid = waitpid(-1, &status, WNOHANG);
384 if (pid <= 0) {
385 if (pid == -1 && errno == EINTR)
386 continue;
387 break;
390 if ((cp = _findchild(pid, FAL0)) != NULL) {
391 if (cp->free)
392 cp->pid = -1; /* XXX Was _delchild(cp);# */
393 else {
394 cp->done = 1;
395 cp->status = status;
401 static struct child *
402 _findchild(int pid, bool_t create)
404 struct child **cpp;
405 NYD_ENTER;
407 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
408 cpp = &(*cpp)->link)
411 if (*cpp == NULL && create) {
412 *cpp = smalloc(sizeof **cpp);
413 (*cpp)->pid = pid;
414 (*cpp)->done = (*cpp)->free = 0;
415 (*cpp)->link = NULL;
417 NYD_LEAVE;
418 return *cpp;
421 static void
422 _delchild(struct child *cp)
424 struct child **cpp;
425 NYD_ENTER;
427 cpp = &_popen_child;
428 for (;;) {
429 if (*cpp == cp) {
430 *cpp = cp->link;
431 free(cp);
432 break;
434 if (*(cpp = &(*cpp)->link) == NULL) {
435 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
436 break;
439 NYD_LEAVE;
442 FL void
443 command_manager_start(void)
445 struct sigaction nact, oact;
446 NYD_ENTER;
448 nact.sa_handler = &_sigchld;
449 sigemptyset(&nact.sa_mask);
450 nact.sa_flags = 0
451 #ifdef SA_RESTART
452 | SA_RESTART
453 #endif
454 #ifdef SA_NOCLDSTOP
455 | SA_NOCLDSTOP
456 #endif
458 if (sigaction(SIGCHLD, &nact, &oact) != 0)
459 n_panic(_("Cannot install signal handler for child process management"));
460 NYD_LEAVE;
463 FL FILE *
464 safe_fopen(char const *file, char const *oflags, int *xflags)
466 int osflags, fd;
467 FILE *fp = NULL;
468 NYD2_ENTER; /* (only for Fopen() and once in lex.c) */
470 if (scan_mode(oflags, &osflags) < 0)
471 goto jleave;
472 osflags |= _O_CLOEXEC;
473 if (xflags != NULL)
474 *xflags = osflags;
476 if ((fd = open(file, osflags, 0666)) == -1)
477 goto jleave;
478 _CLOEXEC_SET(fd);
480 fp = fdopen(fd, oflags);
481 jleave:
482 NYD2_LEAVE;
483 return fp;
486 FL FILE *
487 Fopen(char const *file, char const *oflags)
489 FILE *fp;
490 int osflags;
491 NYD_ENTER;
493 if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
494 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
495 NYD_LEAVE;
496 return fp;
499 FL FILE *
500 Fdopen(int fd, char const *oflags, bool_t nocloexec)
502 FILE *fp;
503 int osflags;
504 NYD_ENTER;
506 scan_mode(oflags, &osflags);
507 if (!nocloexec)
508 osflags |= _O_CLOEXEC; /* Ensured to be set by caller as documented! */
510 if ((fp = fdopen(fd, oflags)) != NULL)
511 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
512 NYD_LEAVE;
513 return fp;
516 FL int
517 Fclose(FILE *fp)
519 int i = 0;
520 NYD_ENTER;
522 if (unregister_file(fp) == OKAY)
523 i |= 1;
524 if (fclose(fp) == 0)
525 i |= 2;
526 NYD_LEAVE;
527 return (i == 3 ? 0 : EOF);
530 FL FILE *
531 Zopen(char const *file, char const *oflags) /* FIXME MESS! */
533 FILE *rv = NULL;
534 char const *cload = NULL, *csave = NULL;
535 int flags, osflags, mode, infd;
536 enum oflags rof;
537 long offset;
538 enum protocol p;
539 NYD_ENTER;
541 if (scan_mode(oflags, &osflags) < 0)
542 goto jleave;
544 flags = 0;
545 rof = OF_RDWR | OF_UNLINK;
546 if (osflags & O_APPEND)
547 rof |= OF_APPEND;
548 mode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
550 if ((osflags & O_APPEND) && ((p = which_protocol(file)) == PROTO_MAILDIR)) {
551 flags |= FP_MAILDIR;
552 osflags = O_RDWR | O_APPEND | O_CREAT;
553 infd = -1;
554 } else {
555 char const *ext;
557 if ((ext = strrchr(file, '.')) != NULL) {
558 if (!asccasecmp(ext, ".gz"))
559 flags |= FP_GZIP;
560 else if (!asccasecmp(ext, ".xz")) {
561 flags |= FP_XZ;
562 osflags &= ~O_APPEND;
563 rof &= ~OF_APPEND;
564 } else if (!asccasecmp(ext, ".bz2")) {
565 flags |= FP_BZIP2;
566 osflags &= ~O_APPEND;
567 rof &= ~OF_APPEND;
568 } else {
569 #undef _X1
570 #define _X1 "file-hook-load-"
571 #undef _X2
572 #define _X2 "file-hook-save-"
573 size_t l = strlen(++ext);
574 char *vbuf = ac_alloc(l + MAX(sizeof(_X1), sizeof(_X2)));
576 memcpy(vbuf, _X1, sizeof(_X1) -1);
577 memcpy(vbuf + sizeof(_X1) -1, ext, l);
578 vbuf[sizeof(_X1) -1 + l] = '\0';
579 cload = vok_vlook(vbuf);
580 memcpy(vbuf, _X2, sizeof(_X2) -1);
581 memcpy(vbuf + sizeof(_X2) -1, ext, l);
582 vbuf[sizeof(_X2) -1 + l] = '\0';
583 csave = vok_vlook(vbuf);
584 #undef _X2
585 #undef _X1
586 ac_free(vbuf);
588 if ((csave != NULL) && (cload != NULL)) {
589 flags |= FP_HOOK;
590 osflags &= ~O_APPEND;
591 rof &= ~OF_APPEND;
592 } else if ((csave != NULL) | (cload != NULL)) {
593 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
594 "Treating as plain text!"), ext);
595 goto jraw;
596 } else
597 goto jraw;
599 } else {
600 jraw:
601 /*flags |= FP_RAW;*/
602 rv = Fopen(file, oflags);
603 goto jleave;
606 if ((infd = open(file, (mode & W_OK) ? O_RDWR : O_RDONLY)) == -1 &&
607 (!(osflags & O_CREAT) || errno != ENOENT))
608 goto jleave;
611 /* Note rv is not yet register_file()d, fclose() it in error path! */
612 if ((rv = Ftmp(NULL, "zopen", rof)) == NULL) {
613 n_perr(_("tmpfile"), 0);
614 goto jerr;
617 if (flags & FP_MAILDIR)
619 else if (infd >= 0) {
620 if (_file_load(flags, infd, fileno(rv), cload) < 0) {
621 jerr:
622 if (rv != NULL)
623 fclose(rv);
624 rv = NULL;
625 if (infd >= 0)
626 close(infd);
627 goto jleave;
629 } else {
630 if ((infd = creat(file, 0666)) == -1) {
631 fclose(rv);
632 rv = NULL;
633 goto jleave;
637 if (infd >= 0)
638 close(infd);
639 fflush(rv);
641 if (!(osflags & O_APPEND))
642 rewind(rv);
643 if ((offset = ftell(rv)) == -1) {
644 Fclose(rv);
645 rv = NULL;
646 goto jleave;
649 register_file(rv, osflags, 0, 0, flags, file, offset, csave);
650 jleave:
651 NYD_LEAVE;
652 return rv;
655 FL FILE *
656 Ftmp(char **fn, char const *namehint, enum oflags oflags)
658 /* The 8 is arbitrary but leaves room for a six character suffix (the
659 * POSIX minimum path length is 14, though we don't check that XXX).
660 * 8 should be more than sufficient given that we use base64url encoding
661 * for our random string */
662 enum {_RANDCHARS = 8};
664 size_t maxname, xlen, i;
665 char *cp_base, *cp;
666 int osoflags, fd, e;
667 bool_t relesigs;
668 FILE *fp;
669 NYD_ENTER;
671 assert(namehint != NULL);
672 assert((oflags & OF_WRONLY) || (oflags & OF_RDWR));
673 assert(!(oflags & OF_RDONLY));
674 assert(!(oflags & OF_REGISTER_UNLINK) || (oflags & OF_REGISTER));
676 fp = NULL;
677 relesigs = FAL0;
678 e = 0;
679 maxname = NAME_MAX;
680 #ifdef HAVE_PATHCONF
681 { long pc;
683 if ((pc = pathconf(tempdir, _PC_NAME_MAX)) != -1)
684 maxname = (size_t)pc;
686 #endif
688 if ((oflags & OF_SUFFIX) && *namehint != '\0') {
689 if ((xlen = strlen(namehint)) > maxname - _RANDCHARS) {
690 errno = ENAMETOOLONG;
691 goto jleave;
693 } else
694 xlen = 0;
696 /* Prepare the template string once, then iterate over the random range */
697 cp_base =
698 cp = smalloc(strlen(tempdir) + 1 + maxname +1);
699 cp = sstpcpy(cp, tempdir);
700 *cp++ = '/';
702 char *x = sstpcpy(cp, UAGENT);
703 *x++ = '-';
704 if (!(oflags & OF_SUFFIX))
705 x = sstpcpy(x, namehint);
707 i = PTR2SIZE(x - cp);
708 if (i > maxname - xlen - _RANDCHARS) {
709 size_t j = maxname - xlen - _RANDCHARS;
710 x -= i - j;
711 i = j;
714 if ((oflags & OF_SUFFIX) && xlen > 0)
715 memcpy(x + _RANDCHARS, namehint, xlen);
717 x[xlen + _RANDCHARS] = '\0';
718 cp = x;
721 osoflags = O_CREAT | O_EXCL | _O_CLOEXEC;
722 osoflags |= (oflags & OF_WRONLY) ? O_WRONLY : O_RDWR;
723 if (oflags & OF_APPEND)
724 osoflags |= O_APPEND;
726 for (i = 0;; ++i) {
727 memcpy(cp, getrandstring(_RANDCHARS), _RANDCHARS);
729 hold_all_sigs();
730 relesigs = TRU1;
732 if ((fd = open(cp_base, osoflags, 0600)) != -1) {
733 _CLOEXEC_SET(fd);
734 break;
736 if (i >= FTMP_OPEN_TRIES) {
737 e = errno;
738 goto jfree;
740 relesigs = FAL0;
741 rele_all_sigs();
744 if (oflags & OF_REGISTER) {
745 char const *osflags = (oflags & OF_RDWR ? "w+" : "w");
746 int osflagbits;
748 scan_mode(osflags, &osflagbits); /* TODO osoflags&xy ?!!? */
749 if ((fp = fdopen(fd, osflags)) != NULL)
750 register_file(fp, osflagbits | _O_CLOEXEC, 0, 0,
751 (FP_RAW | (oflags & OF_REGISTER_UNLINK ? FP_UNLINK : 0)),
752 cp_base, 0L, NULL);
753 } else
754 fp = fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"));
756 if (fp == NULL || (oflags & OF_UNLINK)) {
757 e = errno;
758 unlink(cp_base);
759 goto jfree;
762 if (fn != NULL)
763 *fn = cp_base;
764 else
765 free(cp_base);
766 jleave:
767 if (relesigs && (fp == NULL || !(oflags & OF_HOLDSIGS)))
768 rele_all_sigs();
769 if (fp == NULL)
770 errno = e;
771 NYD_LEAVE;
772 return fp;
773 jfree:
774 if ((cp = cp_base) != NULL)
775 free(cp);
776 goto jleave;
779 FL void
780 Ftmp_release(char **fn)
782 char *cp;
783 NYD_ENTER;
785 cp = *fn;
786 *fn = NULL;
787 if (cp != NULL) {
788 unlink(cp);
789 rele_all_sigs();
790 free(cp);
792 NYD_LEAVE;
795 FL void
796 Ftmp_free(char **fn) /* TODO DROP: OF_REGISTER_FREEPATH! */
798 char *cp;
799 NYD_ENTER;
801 cp = *fn;
802 *fn = NULL;
803 if (cp != NULL)
804 free(cp);
805 NYD_LEAVE;
808 FL bool_t
809 pipe_cloexec(int fd[2])
811 bool_t rv = FAL0;
812 NYD_ENTER;
814 #ifdef HAVE_PIPE2
815 if (pipe2(fd, O_CLOEXEC) == -1)
816 goto jleave;
817 #else
818 if (pipe(fd) == -1)
819 goto jleave;
820 (void)fcntl(fd[0], F_SETFD, FD_CLOEXEC);
821 (void)fcntl(fd[1], F_SETFD, FD_CLOEXEC);
822 #endif
823 rv = TRU1;
824 jleave:
825 NYD_LEAVE;
826 return rv;
829 FL FILE *
830 Popen(char const *cmd, char const *mode, char const *sh,
831 char const **env_addon, int newfd1)
833 int p[2], myside, hisside, fd0, fd1, pid;
834 char mod[2] = {'0', '\0'};
835 sigset_t nset;
836 FILE *rv = NULL;
837 NYD_ENTER;
839 /* First clean up child structures */
840 { sigset_t oset;
841 struct child **cpp, *cp;
843 sigfillset(&nset);
844 sigprocmask(SIG_BLOCK, &nset, &oset);
846 for (cpp = &_popen_child; *cpp != NULL;) {
847 if ((*cpp)->pid == -1) {
848 cp = *cpp;
849 *cpp = cp->link;
850 free(cp);
851 } else
852 cpp = &(*cpp)->link;
855 sigprocmask(SIG_SETMASK, &oset, NULL);
858 if (!pipe_cloexec(p))
859 goto jleave;
861 if (*mode == 'r') {
862 myside = p[READ];
863 fd0 = -1;
864 hisside = fd1 = p[WRITE];
865 mod[0] = *mode;
866 } else if (*mode == 'W') {
867 myside = p[WRITE];
868 hisside = fd0 = p[READ];
869 fd1 = newfd1;
870 mod[0] = 'w';
871 } else {
872 myside = p[WRITE];
873 hisside = fd0 = p[READ];
874 fd1 = -1;
875 mod[0] = 'w';
878 sigemptyset(&nset);
880 if (cmd == (char*)-1) {
881 if ((pid = fork_child()) == -1)
882 n_perr(_("fork"), 0);
883 else if (pid == 0) {
884 union {char const *ccp; int (*ptf)(void); int es;} u;
885 prepare_child(&nset, fd0, fd1);
886 close(p[READ]);
887 close(p[WRITE]);
888 u.ccp = sh;
889 u.es = (*u.ptf)();
890 _exit(u.es);
892 } else if (sh == NULL) {
893 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
894 } else {
895 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
897 if (pid < 0) {
898 close(p[READ]);
899 close(p[WRITE]);
900 goto jleave;
902 close(hisside);
903 if ((rv = fdopen(myside, mod)) != NULL)
904 register_file(rv, 0, 1, pid, FP_RAW, NULL, 0L, NULL);
905 else
906 close(myside);
907 jleave:
908 NYD_LEAVE;
909 return rv;
912 FL bool_t
913 Pclose(FILE *ptr, bool_t dowait)
915 sigset_t nset, oset;
916 int pid;
917 bool_t rv = FAL0;
918 NYD_ENTER;
920 pid = file_pid(ptr);
921 if (pid < 0)
922 goto jleave;
923 unregister_file(ptr);
924 fclose(ptr);
925 if (dowait) {
926 sigemptyset(&nset);
927 sigaddset(&nset, SIGINT);
928 sigaddset(&nset, SIGHUP);
929 sigprocmask(SIG_BLOCK, &nset, &oset);
930 rv = wait_child(pid, NULL);
931 sigprocmask(SIG_SETMASK, &oset, NULL);
932 } else {
933 free_child(pid);
934 rv = TRU1;
936 jleave:
937 NYD_LEAVE;
938 return rv;
941 FL void
942 close_all_files(void)
944 NYD_ENTER;
945 while (fp_head != NULL)
946 if (fp_head->pipe)
947 Pclose(fp_head->fp, TRU1);
948 else
949 Fclose(fp_head->fp);
950 NYD_LEAVE;
953 FL int
954 fork_child(void)
956 struct child *cp;
957 int pid;
958 NYD_ENTER;
960 cp = _findchild(0, TRU1);
962 if ((cp->pid = pid = fork()) == -1) {
963 _delchild(cp);
964 n_perr(_("fork"), 0);
966 NYD_LEAVE;
967 return pid;
970 FL int
971 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
972 char const *a0, char const *a1, char const *a2, char const **env_addon)
974 sigset_t nset, oset;
975 bool_t tio_set;
976 int rv;
977 NYD_ENTER;
979 /* TODO Of course this is a joke given that during a "p*" the PAGER may
980 * TODO be up and running while we play around like this... but i guess
981 * TODO this can't be helped at all unless we perform complete and true
982 * TODO process group separation and ensure we don't deadlock us out
983 * TODO via TTY jobcontrol signal storms (could this really happen?).
984 * TODO Or have a builtin pager. Or query any necessity BEFORE we start
985 * TODO any action, and shall we find we need to run programs dump it
986 * TODO all into a temporary file which is then passed through to the
987 * TODO PAGER. Ugh. That still won't help for "needsterminal" anyway */
988 if ((tio_set = ((options & OPT_INTERACTIVE) &&
989 (infd == COMMAND_FD_PASS || outfd == COMMAND_FD_PASS)))) {
990 tcgetattr((options & OPT_TTYIN ? STDIN_FILENO : STDOUT_FILENO), &a_popen_tios);
991 n_TERMCAP_SUSPEND(FAL0);
992 sigfillset(&nset);
993 sigdelset(&nset, SIGCHLD);
994 /* sigdelset(&nset, SIGPIPE); TODO would need a handler */
995 sigprocmask(SIG_BLOCK, &nset, &oset);
996 a_popen_hadsig = 0;
997 a_popen_jobsigs_up();
1000 if ((rv = start_command(cmd, mask, infd, outfd, a0, a1, a2, env_addon)) < 0)
1001 rv = -1;
1002 else {
1003 if (wait_child(rv, NULL))
1004 rv = 0;
1005 else {
1006 if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1007 n_err(_("Fatal error in process\n"));
1008 rv = -1;
1012 if (tio_set) {
1013 a_popen_jobsigs_down();
1014 tio_set = ((options & OPT_TTYIN) != 0);
1015 n_TERMCAP_RESUME(a_popen_hadsig ? TRU1 : FAL0);
1016 tcsetattr((tio_set ? STDIN_FILENO : STDOUT_FILENO),
1017 (tio_set ? TCSAFLUSH : TCSADRAIN), &a_popen_tios);
1018 sigprocmask(SIG_SETMASK, &oset, NULL);
1020 NYD_LEAVE;
1021 return rv;
1024 FL int
1025 start_command(char const *cmd, sigset_t *mask, int infd, int outfd,
1026 char const *a0, char const *a1, char const *a2,
1027 char const **env_addon)
1029 int rv;
1030 NYD_ENTER;
1032 if ((rv = fork_child()) == -1) {
1033 n_perr(_("fork"), 0);
1034 rv = -1;
1035 } else if (rv == 0) {
1036 char *argv[128];
1037 int i;
1039 if (env_addon != NULL) { /* TODO env_addon; should have struct child */
1040 extern char **environ;
1041 size_t ei, ei_orig, ai, ai_orig;
1042 char **env;
1044 /* TODO note we don't check the POSIX limit:
1045 * the total space used to store the environment and the arguments to
1046 * the process is limited to {ARG_MAX} bytes */
1047 for (ei = 0; environ[ei] != NULL; ++ei)
1049 ei_orig = ei;
1050 for (ai = 0; env_addon[ai] != NULL; ++ai)
1052 ai_orig = ai;
1053 env = ac_alloc(sizeof(*env) * (ei + ai +1));
1054 memcpy(env, environ, sizeof(*env) * ei);
1056 /* Replace all those keys that yet exist */
1057 while (ai-- > 0) {
1058 char const *ee, *kvs;
1059 size_t kl;
1061 ee = env_addon[ai];
1062 kvs = strchr(ee, '=');
1063 assert(kvs != NULL);
1064 kl = PTR2SIZE(kvs - ee);
1065 assert(kl > 0);
1066 for (ei = ei_orig; ei-- > 0;) {
1067 char const *ekvs = strchr(env[ei], '=');
1068 if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
1069 !memcmp(ee, env[ei], kl)) {
1070 env[ei] = UNCONST(ee);
1071 env_addon[ai] = NULL;
1072 break;
1077 /* And append the rest */
1078 for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
1079 if (env_addon[ai] != NULL)
1080 env[ei++] = UNCONST(env_addon[ai]);
1082 env[ei] = NULL;
1083 environ = env;
1086 i = getrawlist(cmd, strlen(cmd), argv, NELEM(argv), 0);
1088 if ((argv[i++] = UNCONST(a0)) != NULL &&
1089 (argv[i++] = UNCONST(a1)) != NULL &&
1090 (argv[i++] = UNCONST(a2)) != NULL)
1091 argv[i] = NULL;
1092 prepare_child(mask, infd, outfd);
1093 execvp(argv[0], argv);
1094 perror(argv[0]);
1095 _exit(EXIT_ERR);
1097 NYD_LEAVE;
1098 return rv;
1101 FL void
1102 prepare_child(sigset_t *nset, int infd, int outfd)
1104 int i;
1105 sigset_t fset;
1106 NYD_ENTER;
1108 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
1109 /* TODO WHAT IS WITH STDERR_FILENO DAMN? */
1110 if ((i = (infd == COMMAND_FD_NULL)))
1111 infd = open("/dev/null", O_RDONLY);
1112 if (infd >= 0) {
1113 dup2(infd, STDIN_FILENO);
1114 if (i)
1115 close(infd);
1118 if ((i = (outfd == COMMAND_FD_NULL)))
1119 outfd = open("/dev/null", O_WRONLY);
1120 if (outfd >= 0) {
1121 dup2(outfd, STDOUT_FILENO);
1122 if (i)
1123 close(outfd);
1126 if (nset) {
1127 for (i = 1; i < NSIG; ++i)
1128 if (sigismember(nset, i))
1129 safe_signal(i, SIG_IGN);
1130 if (!sigismember(nset, SIGINT))
1131 safe_signal(SIGINT, SIG_DFL);
1134 sigemptyset(&fset);
1135 sigprocmask(SIG_SETMASK, &fset, NULL);
1136 NYD_LEAVE;
1139 FL void
1140 free_child(int pid)
1142 sigset_t nset, oset;
1143 struct child *cp;
1144 NYD_ENTER;
1146 sigemptyset(&nset);
1147 sigaddset(&nset, SIGCHLD);
1148 sigprocmask(SIG_BLOCK, &nset, &oset);
1150 if ((cp = _findchild(pid, FAL0)) != NULL) {
1151 if (cp->done)
1152 _delchild(cp);
1153 else
1154 cp->free = 1;
1157 sigprocmask(SIG_SETMASK, &oset, NULL);
1158 NYD_LEAVE;
1161 FL bool_t
1162 wait_child(int pid, int *wait_status)
1164 sigset_t nset, oset;
1165 struct child *cp;
1166 int ws;
1167 bool_t rv;
1168 NYD_ENTER;
1170 sigemptyset(&nset);
1171 sigaddset(&nset, SIGCHLD);
1172 sigprocmask(SIG_BLOCK, &nset, &oset);
1174 cp = _findchild(pid, FAL0);
1175 if (cp != NULL) {
1176 while (!cp->done)
1177 sigsuspend(&oset);
1178 ws = cp->status;
1179 _delchild(cp);
1180 } else
1181 ws = 0;
1183 sigprocmask(SIG_SETMASK, &oset, NULL);
1185 if (wait_status != NULL)
1186 *wait_status = ws;
1187 rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
1188 NYD_LEAVE;
1189 return rv;
1192 /* s-it-mode */