nail.1: more on sortability (Rudolf Sykora)..
[s-mailx.git] / popen.c
blobd2877c7fc8ce76dadf741a198dac2843a0ee8df3
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_IMAP = 1<<3,
62 FP_MAILDIR = 1<<4,
63 FP_HOOK = 1<<5,
64 FP_MASK = (1<<6) - 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<<6
68 } flags;
71 struct child {
72 int pid;
73 char done;
74 char free;
75 int status;
76 struct child *link;
79 static struct fp *fp_head;
80 static struct child *_popen_child;
82 /* TODO Rather temporary: deal with job control with FD_PASS */
83 static struct termios a_popen_tios;
84 static sighandler_type a_popen_otstp, a_popen_ottin, a_popen_ottou;
85 static volatile int a_popen_hadsig;
87 static int scan_mode(char const *mode, int *omode);
88 static void register_file(FILE *fp, int omode, int ispipe, int pid,
89 int flags, char const *realfile, long offset,
90 char const *save_cmd);
91 static enum okay _file_save(struct fp *fpp);
92 static int _file_load(int flags, int infd, int outfd,
93 char const *load_cmd);
94 static enum okay unregister_file(FILE *fp);
95 static int file_pid(FILE *fp);
97 /* TODO Rather temporary: deal with job control with FD_PASS */
98 static void a_popen_jobsigs_up(void);
99 static void a_popen_jobsigs_down(void);
100 static void a_popen_jobsig(int sig);
102 /* Handle SIGCHLD */
103 static void _sigchld(int signo);
105 static struct child *_findchild(int pid, bool_t create);
106 static void _delchild(struct child *cp);
108 static int
109 scan_mode(char const *mode, int *omode)
111 static struct {
112 char const mode[4];
113 int omode;
114 } const maps[] = {
115 {"r", O_RDONLY},
116 {"w", O_WRONLY | O_CREAT | O_TRUNC},
117 {"wx", O_WRONLY | O_CREAT | O_EXCL},
118 {"a", O_WRONLY | O_APPEND | O_CREAT},
119 {"a+", O_RDWR | O_APPEND},
120 {"r+", O_RDWR},
121 {"w+", O_RDWR | O_CREAT | O_EXCL}
124 int i;
125 NYD2_ENTER;
127 for (i = 0; UICMP(z, i, <, NELEM(maps)); ++i)
128 if (!strcmp(maps[i].mode, mode)) {
129 *omode = maps[i].omode;
130 i = 0;
131 goto jleave;
134 n_alert(_("Internal error: bad stdio open mode %s"), mode);
135 errno = EINVAL;
136 *omode = 0; /* (silence CC) */
137 i = -1;
138 jleave:
139 NYD2_LEAVE;
140 return i;
143 static void
144 register_file(FILE *fp, int omode, int ispipe, int pid, int flags,
145 char const *realfile, long offset, char const *save_cmd)
147 struct fp *fpp;
148 NYD_ENTER;
150 assert(!(flags & FP_UNLINK) || realfile != NULL);
152 fpp = smalloc(sizeof *fpp);
153 fpp->fp = fp;
154 fpp->omode = omode;
155 fpp->pipe = ispipe;
156 fpp->pid = pid;
157 fpp->link = fp_head;
158 fpp->flags = flags;
159 fpp->realfile = (realfile != NULL) ? sstrdup(realfile) : NULL;
160 fpp->save_cmd = (save_cmd != NULL) ? sstrdup(save_cmd) : NULL;
161 fpp->offset = offset;
162 fp_head = fpp;
163 NYD_LEAVE;
166 static enum okay
167 _file_save(struct fp *fpp)
169 char const *cmd[3];
170 int outfd, infd;
171 enum okay rv;
172 NYD_ENTER;
174 if (fpp->omode == O_RDONLY) {
175 rv = OKAY;
176 goto jleave;
178 rv = STOP;
180 fflush(fpp->fp);
181 clearerr(fpp->fp);
183 #ifdef HAVE_IMAP
184 if ((fpp->flags & FP_MASK) == FP_IMAP) {
185 rv = imap_append(fpp->realfile, fpp->fp);
186 goto jleave;
188 #endif
189 if ((fpp->flags & FP_MASK) == FP_MAILDIR) {
190 if (fseek(fpp->fp, fpp->offset, SEEK_SET) == -1) {
191 outfd = errno;
192 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
193 fpp->realfile, strerror(outfd));
194 goto jleave;
196 rv = maildir_append(fpp->realfile, fpp->fp, fpp->offset);
197 goto jleave;
200 /* Ensure the I/O library doesn't optimize the fseek(3) away! */
201 if(lseek(infd = fileno(fpp->fp), fpp->offset, SEEK_SET) == -1){
202 outfd = errno;
203 n_err(_("Fatal: cannot restore file position and save %s: %s\n"),
204 fpp->realfile, strerror(outfd));
205 goto jleave;
208 outfd = open(fpp->realfile,
209 ((fpp->omode | O_CREAT | (fpp->omode & O_APPEND ? 0 : O_TRUNC))
210 & ~O_EXCL), 0666);
211 if (outfd == -1) {
212 outfd = errno;
213 n_err(_("Fatal: cannot create %s: %s\n"),
214 fpp->realfile, strerror(outfd));
215 goto jleave;
218 cmd[2] = NULL;
219 switch (fpp->flags & FP_MASK) {
220 case FP_GZIP:
221 cmd[0] = "gzip"; cmd[1] = "-c"; break;
222 case FP_BZIP2:
223 cmd[0] = "bzip2"; cmd[1] = "-c"; break;
224 case FP_XZ:
225 cmd[0] = "xz"; cmd[1] = "-c"; break;
226 default:
227 cmd[0] = "cat"; cmd[1] = NULL; break;
228 case FP_HOOK:
229 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
230 cmd[0] = XSHELL;
231 cmd[1] = "-c";
232 cmd[2] = fpp->save_cmd;
234 if (run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL) >= 0)
235 rv = OKAY;
237 close(outfd);
238 jleave:
239 NYD_LEAVE;
240 return rv;
243 static int
244 _file_load(int flags, int infd, int outfd, char const *load_cmd)
246 char const *cmd[3];
247 int rv;
248 NYD_ENTER;
250 cmd[2] = NULL;
251 switch (flags & FP_MASK) {
252 case FP_GZIP: cmd[0] = "gzip"; cmd[1] = "-cd"; break;
253 case FP_BZIP2: cmd[0] = "bzip2"; cmd[1] = "-cd"; break;
254 case FP_XZ: cmd[0] = "xz"; cmd[1] = "-cd"; break;
255 default: cmd[0] = "cat"; cmd[1] = NULL; break;
256 case FP_HOOK:
257 if ((cmd[0] = ok_vlook(SHELL)) == NULL)
258 cmd[0] = XSHELL;
259 cmd[1] = "-c";
260 cmd[2] = load_cmd;
261 break;
262 case FP_MAILDIR:
263 case FP_IMAP:
264 rv = 0;
265 goto jleave;
268 rv = run_command(cmd[0], 0, infd, outfd, cmd[1], cmd[2], NULL, NULL);
269 jleave:
270 NYD_LEAVE;
271 return rv;
274 static enum okay
275 unregister_file(FILE *fp)
277 struct fp **pp, *p;
278 enum okay rv = OKAY;
279 NYD_ENTER;
281 for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
282 if (p->fp == fp) {
283 if ((p->flags & FP_MASK) != FP_RAW) /* TODO ;} */
284 rv = _file_save(p);
285 if (p->flags & FP_UNLINK && unlink(p->realfile))
286 rv = STOP;
287 *pp = p->link;
288 if (p->save_cmd != NULL)
289 free(p->save_cmd);
290 if (p->realfile != NULL)
291 free(p->realfile);
292 free(p);
293 goto jleave;
295 DBGOR(n_panic, n_alert)(_("Invalid file pointer"));
296 rv = STOP;
297 jleave:
298 NYD_LEAVE;
299 return rv;
302 static int
303 file_pid(FILE *fp)
305 int rv;
306 struct fp *p;
307 NYD2_ENTER;
309 rv = -1;
310 for (p = fp_head; p; p = p->link)
311 if (p->fp == fp) {
312 rv = p->pid;
313 break;
315 NYD2_LEAVE;
316 return rv;
319 static void
320 a_popen_jobsigs_up(void){
321 sigset_t nset, oset;
322 NYD2_ENTER;
324 sigfillset(&nset);
326 sigprocmask(SIG_BLOCK, &nset, &oset);
327 a_popen_otstp = safe_signal(SIGTSTP, &a_popen_jobsig);
328 a_popen_ottin = safe_signal(SIGTTIN, &a_popen_jobsig);
329 a_popen_ottou = safe_signal(SIGTTOU, &a_popen_jobsig);
331 /* This assumes oset contains nothing but SIGCHLD, so to say */
332 sigdelset(&oset, SIGTSTP);
333 sigdelset(&oset, SIGTTIN);
334 sigdelset(&oset, SIGTTOU);
335 sigprocmask(SIG_SETMASK, &oset, NULL);
336 NYD2_LEAVE;
339 static void
340 a_popen_jobsigs_down(void){
341 sigset_t nset, oset;
342 NYD2_ENTER;
344 sigfillset(&nset);
346 sigprocmask(SIG_BLOCK, &nset, &oset);
347 safe_signal(SIGTSTP, a_popen_otstp);
348 safe_signal(SIGTTIN, a_popen_ottin);
349 safe_signal(SIGTTOU, a_popen_ottou);
351 sigaddset(&oset, SIGTSTP);
352 sigaddset(&oset, SIGTTIN);
353 sigaddset(&oset, SIGTTOU);
354 sigprocmask(SIG_SETMASK, &oset, NULL);
355 NYD2_LEAVE;
358 static void
359 a_popen_jobsig(int sig){
360 sighandler_type oldact;
361 sigset_t nset;
362 bool_t hadsig;
363 NYD_X; /* Signal handler */
365 hadsig = (a_popen_hadsig != 0);
366 a_popen_hadsig = 1;
368 oldact = safe_signal(sig, SIG_DFL);
370 sigemptyset(&nset);
371 sigaddset(&nset, sig);
372 sigprocmask(SIG_UNBLOCK, &nset, NULL);
373 n_raise(sig);
374 sigprocmask(SIG_BLOCK, &nset, NULL);
376 safe_signal(sig, oldact);
379 static void
380 _sigchld(int signo)
382 pid_t pid;
383 int status;
384 struct child *cp;
385 NYD_X; /* Signal handler */
386 UNUSED(signo);
388 for (;;) {
389 pid = waitpid(-1, &status, WNOHANG);
390 if (pid <= 0) {
391 if (pid == -1 && errno == EINTR)
392 continue;
393 break;
396 if ((cp = _findchild(pid, FAL0)) != NULL) {
397 if (cp->free)
398 cp->pid = -1; /* XXX Was _delchild(cp);# */
399 else {
400 cp->done = 1;
401 cp->status = status;
407 static struct child *
408 _findchild(int pid, bool_t create)
410 struct child **cpp;
411 NYD_ENTER;
413 for (cpp = &_popen_child; *cpp != NULL && (*cpp)->pid != pid;
414 cpp = &(*cpp)->link)
417 if (*cpp == NULL && create) {
418 *cpp = smalloc(sizeof **cpp);
419 (*cpp)->pid = pid;
420 (*cpp)->done = (*cpp)->free = 0;
421 (*cpp)->link = NULL;
423 NYD_LEAVE;
424 return *cpp;
427 static void
428 _delchild(struct child *cp)
430 struct child **cpp;
431 NYD_ENTER;
433 cpp = &_popen_child;
434 for (;;) {
435 if (*cpp == cp) {
436 *cpp = cp->link;
437 free(cp);
438 break;
440 if (*(cpp = &(*cpp)->link) == NULL) {
441 DBG( n_err("! popen.c:_delchild(): implementation error\n"); )
442 break;
445 NYD_LEAVE;
448 FL void
449 command_manager_start(void)
451 struct sigaction nact, oact;
452 NYD_ENTER;
454 nact.sa_handler = &_sigchld;
455 sigemptyset(&nact.sa_mask);
456 nact.sa_flags = 0
457 #ifdef SA_RESTART
458 | SA_RESTART
459 #endif
460 #ifdef SA_NOCLDSTOP
461 | SA_NOCLDSTOP
462 #endif
464 if (sigaction(SIGCHLD, &nact, &oact) != 0)
465 n_panic(_("Cannot install signal handler for child process management"));
466 NYD_LEAVE;
469 FL FILE *
470 safe_fopen(char const *file, char const *oflags, int *xflags)
472 int osflags, fd;
473 FILE *fp = NULL;
474 NYD2_ENTER; /* (only for Fopen() and once in lex.c) */
476 if (scan_mode(oflags, &osflags) < 0)
477 goto jleave;
478 osflags |= _O_CLOEXEC;
479 if (xflags != NULL)
480 *xflags = osflags;
482 if ((fd = open(file, osflags, 0666)) == -1)
483 goto jleave;
484 _CLOEXEC_SET(fd);
486 fp = fdopen(fd, oflags);
487 jleave:
488 NYD2_LEAVE;
489 return fp;
492 FL FILE *
493 Fopen(char const *file, char const *oflags)
495 FILE *fp;
496 int osflags;
497 NYD_ENTER;
499 if ((fp = safe_fopen(file, oflags, &osflags)) != NULL)
500 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
501 NYD_LEAVE;
502 return fp;
505 FL FILE *
506 Fdopen(int fd, char const *oflags, bool_t nocloexec)
508 FILE *fp;
509 int osflags;
510 NYD_ENTER;
512 scan_mode(oflags, &osflags);
513 if (!nocloexec)
514 osflags |= _O_CLOEXEC; /* Ensured to be set by caller as documented! */
516 if ((fp = fdopen(fd, oflags)) != NULL)
517 register_file(fp, osflags, 0, 0, FP_RAW, NULL, 0L, NULL);
518 NYD_LEAVE;
519 return fp;
522 FL int
523 Fclose(FILE *fp)
525 int i = 0;
526 NYD_ENTER;
528 if (unregister_file(fp) == OKAY)
529 i |= 1;
530 if (fclose(fp) == 0)
531 i |= 2;
532 NYD_LEAVE;
533 return (i == 3 ? 0 : EOF);
536 FL FILE *
537 Zopen(char const *file, char const *oflags) /* FIXME MESS! */
539 FILE *rv = NULL;
540 char const *cload = NULL, *csave = NULL;
541 int flags, osflags, mode, infd;
542 enum oflags rof;
543 long offset;
544 enum protocol p;
545 NYD_ENTER;
547 if (scan_mode(oflags, &osflags) < 0)
548 goto jleave;
550 flags = 0;
551 rof = OF_RDWR | OF_UNLINK;
552 if (osflags & O_APPEND)
553 rof |= OF_APPEND;
554 mode = (osflags == O_RDONLY) ? R_OK : R_OK | W_OK;
556 if ((osflags & O_APPEND) && ((p = which_protocol(file)) == PROTO_IMAP ||
557 p == PROTO_MAILDIR)) {
558 flags |= (p == PROTO_IMAP) ? FP_IMAP : FP_MAILDIR;
559 osflags = O_RDWR | O_APPEND | O_CREAT;
560 infd = -1;
561 } else {
562 char const *ext;
564 if ((ext = strrchr(file, '.')) != NULL) {
565 if (!asccasecmp(ext, ".gz"))
566 flags |= FP_GZIP;
567 else if (!asccasecmp(ext, ".xz")) {
568 flags |= FP_XZ;
569 osflags &= ~O_APPEND;
570 rof &= ~OF_APPEND;
571 } else if (!asccasecmp(ext, ".bz2")) {
572 flags |= FP_BZIP2;
573 osflags &= ~O_APPEND;
574 rof &= ~OF_APPEND;
575 } else {
576 #undef _X1
577 #define _X1 "file-hook-load-"
578 #undef _X2
579 #define _X2 "file-hook-save-"
580 size_t l = strlen(++ext);
581 char *vbuf = ac_alloc(l + MAX(sizeof(_X1), sizeof(_X2)));
583 memcpy(vbuf, _X1, sizeof(_X1) -1);
584 memcpy(vbuf + sizeof(_X1) -1, ext, l);
585 vbuf[sizeof(_X1) -1 + l] = '\0';
586 cload = vok_vlook(vbuf);
587 memcpy(vbuf, _X2, sizeof(_X2) -1);
588 memcpy(vbuf + sizeof(_X2) -1, ext, l);
589 vbuf[sizeof(_X2) -1 + l] = '\0';
590 csave = vok_vlook(vbuf);
591 #undef _X2
592 #undef _X1
593 ac_free(vbuf);
595 if ((csave != NULL) && (cload != NULL)) {
596 flags |= FP_HOOK;
597 osflags &= ~O_APPEND;
598 rof &= ~OF_APPEND;
599 } else if ((csave != NULL) | (cload != NULL)) {
600 n_alert(_("Only one of *mailbox-(load|save)-%s* is set! "
601 "Treating as plain text!"), ext);
602 goto jraw;
603 } else
604 goto jraw;
606 } else {
607 jraw:
608 /*flags |= FP_RAW;*/
609 rv = Fopen(file, oflags);
610 goto jleave;
613 if ((infd = open(file, (mode & W_OK) ? O_RDWR : O_RDONLY)) == -1 &&
614 (!(osflags & O_CREAT) || errno != ENOENT))
615 goto jleave;
618 /* Note rv is not yet register_file()d, fclose() it in error path! */
619 if ((rv = Ftmp(NULL, "zopen", rof)) == NULL) {
620 n_perr(_("tmpfile"), 0);
621 goto jerr;
624 if (flags & (FP_IMAP | FP_MAILDIR))
626 else if (infd >= 0) {
627 if (_file_load(flags, infd, fileno(rv), cload) < 0) {
628 jerr:
629 if (rv != NULL)
630 fclose(rv);
631 rv = NULL;
632 if (infd >= 0)
633 close(infd);
634 goto jleave;
636 } else {
637 if ((infd = creat(file, 0666)) == -1) {
638 fclose(rv);
639 rv = NULL;
640 goto jleave;
644 if (infd >= 0)
645 close(infd);
646 fflush(rv);
648 if (!(osflags & O_APPEND))
649 rewind(rv);
650 if ((offset = ftell(rv)) == -1) {
651 Fclose(rv);
652 rv = NULL;
653 goto jleave;
656 register_file(rv, osflags, 0, 0, flags, file, offset, csave);
657 jleave:
658 NYD_LEAVE;
659 return rv;
662 FL FILE *
663 Ftmp(char **fn, char const *namehint, enum oflags oflags)
665 /* The 8 is arbitrary but leaves room for a six character suffix (the
666 * POSIX minimum path length is 14, though we don't check that XXX).
667 * 8 should be more than sufficient given that we use base64url encoding
668 * for our random string */
669 enum {_RANDCHARS = 8};
671 size_t maxname, xlen, i;
672 char *cp_base, *cp;
673 int osoflags, fd, e;
674 bool_t relesigs;
675 FILE *fp;
676 NYD_ENTER;
678 assert(namehint != NULL);
679 assert((oflags & OF_WRONLY) || (oflags & OF_RDWR));
680 assert(!(oflags & OF_RDONLY));
681 assert(!(oflags & OF_REGISTER_UNLINK) || (oflags & OF_REGISTER));
683 fp = NULL;
684 relesigs = FAL0;
685 e = 0;
686 maxname = NAME_MAX;
687 #ifdef HAVE_PATHCONF
688 { long pc;
690 if ((pc = pathconf(tempdir, _PC_NAME_MAX)) != -1)
691 maxname = (size_t)pc;
693 #endif
695 if ((oflags & OF_SUFFIX) && *namehint != '\0') {
696 if ((xlen = strlen(namehint)) > maxname - _RANDCHARS) {
697 errno = ENAMETOOLONG;
698 goto jleave;
700 } else
701 xlen = 0;
703 /* Prepare the template string once, then iterate over the random range */
704 cp_base =
705 cp = smalloc(strlen(tempdir) + 1 + maxname +1);
706 cp = sstpcpy(cp, tempdir);
707 *cp++ = '/';
709 char *x = sstpcpy(cp, UAGENT);
710 *x++ = '-';
711 if (!(oflags & OF_SUFFIX))
712 x = sstpcpy(x, namehint);
714 i = PTR2SIZE(x - cp);
715 if (i > maxname - xlen - _RANDCHARS) {
716 size_t j = maxname - xlen - _RANDCHARS;
717 x -= i - j;
718 i = j;
721 if ((oflags & OF_SUFFIX) && xlen > 0)
722 memcpy(x + _RANDCHARS, namehint, xlen);
724 x[xlen + _RANDCHARS] = '\0';
725 cp = x;
728 osoflags = O_CREAT | O_EXCL | _O_CLOEXEC;
729 osoflags |= (oflags & OF_WRONLY) ? O_WRONLY : O_RDWR;
730 if (oflags & OF_APPEND)
731 osoflags |= O_APPEND;
733 for (i = 0;; ++i) {
734 memcpy(cp, getrandstring(_RANDCHARS), _RANDCHARS);
736 hold_all_sigs();
737 relesigs = TRU1;
739 if ((fd = open(cp_base, osoflags, 0600)) != -1) {
740 _CLOEXEC_SET(fd);
741 break;
743 if (i >= FTMP_OPEN_TRIES) {
744 e = errno;
745 goto jfree;
747 relesigs = FAL0;
748 rele_all_sigs();
751 if (oflags & OF_REGISTER) {
752 char const *osflags = (oflags & OF_RDWR ? "w+" : "w");
753 int osflagbits;
755 scan_mode(osflags, &osflagbits); /* TODO osoflags&xy ?!!? */
756 if ((fp = fdopen(fd, osflags)) != NULL)
757 register_file(fp, osflagbits | _O_CLOEXEC, 0, 0,
758 (FP_RAW | (oflags & OF_REGISTER_UNLINK ? FP_UNLINK : 0)),
759 cp_base, 0L, NULL);
760 } else
761 fp = fdopen(fd, (oflags & OF_RDWR ? "w+" : "w"));
763 if (fp == NULL || (oflags & OF_UNLINK)) {
764 e = errno;
765 unlink(cp_base);
766 goto jfree;
769 if (fn != NULL)
770 *fn = cp_base;
771 else
772 free(cp_base);
773 jleave:
774 if (relesigs && (fp == NULL || !(oflags & OF_HOLDSIGS)))
775 rele_all_sigs();
776 if (fp == NULL)
777 errno = e;
778 NYD_LEAVE;
779 return fp;
780 jfree:
781 if ((cp = cp_base) != NULL)
782 free(cp);
783 goto jleave;
786 FL void
787 Ftmp_release(char **fn)
789 char *cp;
790 NYD_ENTER;
792 cp = *fn;
793 *fn = NULL;
794 if (cp != NULL) {
795 unlink(cp);
796 rele_all_sigs();
797 free(cp);
799 NYD_LEAVE;
802 FL void
803 Ftmp_free(char **fn) /* TODO DROP: OF_REGISTER_FREEPATH! */
805 char *cp;
806 NYD_ENTER;
808 cp = *fn;
809 *fn = NULL;
810 if (cp != NULL)
811 free(cp);
812 NYD_LEAVE;
815 FL bool_t
816 pipe_cloexec(int fd[2])
818 bool_t rv = FAL0;
819 NYD_ENTER;
821 #ifdef HAVE_PIPE2
822 if (pipe2(fd, O_CLOEXEC) == -1)
823 goto jleave;
824 #else
825 if (pipe(fd) == -1)
826 goto jleave;
827 (void)fcntl(fd[0], F_SETFD, FD_CLOEXEC);
828 (void)fcntl(fd[1], F_SETFD, FD_CLOEXEC);
829 #endif
830 rv = TRU1;
831 jleave:
832 NYD_LEAVE;
833 return rv;
836 FL FILE *
837 Popen(char const *cmd, char const *mode, char const *sh,
838 char const **env_addon, int newfd1)
840 int p[2], myside, hisside, fd0, fd1, pid;
841 char mod[2] = {'0', '\0'};
842 sigset_t nset;
843 FILE *rv = NULL;
844 NYD_ENTER;
846 /* First clean up child structures */
847 { sigset_t oset;
848 struct child **cpp, *cp;
850 sigfillset(&nset);
851 sigprocmask(SIG_BLOCK, &nset, &oset);
853 for (cpp = &_popen_child; *cpp != NULL;) {
854 if ((*cpp)->pid == -1) {
855 cp = *cpp;
856 *cpp = cp->link;
857 free(cp);
858 } else
859 cpp = &(*cpp)->link;
862 sigprocmask(SIG_SETMASK, &oset, NULL);
865 if (!pipe_cloexec(p))
866 goto jleave;
868 if (*mode == 'r') {
869 myside = p[READ];
870 fd0 = -1;
871 hisside = fd1 = p[WRITE];
872 mod[0] = *mode;
873 } else if (*mode == 'W') {
874 myside = p[WRITE];
875 hisside = fd0 = p[READ];
876 fd1 = newfd1;
877 mod[0] = 'w';
878 } else {
879 myside = p[WRITE];
880 hisside = fd0 = p[READ];
881 fd1 = -1;
882 mod[0] = 'w';
885 sigemptyset(&nset);
887 if (cmd == (char*)-1) {
888 if ((pid = fork_child()) == -1)
889 n_perr(_("fork"), 0);
890 else if (pid == 0) {
891 union {char const *ccp; int (*ptf)(void); int es;} u;
892 prepare_child(&nset, fd0, fd1);
893 close(p[READ]);
894 close(p[WRITE]);
895 u.ccp = sh;
896 u.es = (*u.ptf)();
897 _exit(u.es);
899 } else if (sh == NULL) {
900 pid = start_command(cmd, &nset, fd0, fd1, NULL, NULL, NULL, env_addon);
901 } else {
902 pid = start_command(sh, &nset, fd0, fd1, "-c", cmd, NULL, env_addon);
904 if (pid < 0) {
905 close(p[READ]);
906 close(p[WRITE]);
907 goto jleave;
909 close(hisside);
910 if ((rv = fdopen(myside, mod)) != NULL)
911 register_file(rv, 0, 1, pid, FP_RAW, NULL, 0L, NULL);
912 else
913 close(myside);
914 jleave:
915 NYD_LEAVE;
916 return rv;
919 FL bool_t
920 Pclose(FILE *ptr, bool_t dowait)
922 sigset_t nset, oset;
923 int pid;
924 bool_t rv = FAL0;
925 NYD_ENTER;
927 pid = file_pid(ptr);
928 if (pid < 0)
929 goto jleave;
930 unregister_file(ptr);
931 fclose(ptr);
932 if (dowait) {
933 sigemptyset(&nset);
934 sigaddset(&nset, SIGINT);
935 sigaddset(&nset, SIGHUP);
936 sigprocmask(SIG_BLOCK, &nset, &oset);
937 rv = wait_child(pid, NULL);
938 sigprocmask(SIG_SETMASK, &oset, NULL);
939 } else {
940 free_child(pid);
941 rv = TRU1;
943 jleave:
944 NYD_LEAVE;
945 return rv;
948 FL void
949 close_all_files(void)
951 NYD_ENTER;
952 while (fp_head != NULL)
953 if (fp_head->pipe)
954 Pclose(fp_head->fp, TRU1);
955 else
956 Fclose(fp_head->fp);
957 NYD_LEAVE;
960 FL int
961 fork_child(void)
963 struct child *cp;
964 int pid;
965 NYD_ENTER;
967 cp = _findchild(0, TRU1);
969 if ((cp->pid = pid = fork()) == -1) {
970 _delchild(cp);
971 n_perr(_("fork"), 0);
973 NYD_LEAVE;
974 return pid;
977 FL int
978 run_command(char const *cmd, sigset_t *mask, int infd, int outfd,
979 char const *a0, char const *a1, char const *a2, char const **env_addon)
981 sigset_t nset, oset;
982 bool_t tio_set;
983 int rv;
984 NYD_ENTER;
986 /* TODO Of course this is a joke given that during a "p*" the PAGER may
987 * TODO be up and running while we play around like this... but i guess
988 * TODO this can't be helped at all unless we perform complete and true
989 * TODO process group separation and ensure we don't deadlock us out
990 * TODO via TTY jobcontrol signal storms (could this really happen?).
991 * TODO Or have a builtin pager. Or query any necessity BEFORE we start
992 * TODO any action, and shall we find we need to run programs dump it
993 * TODO all into a temporary file which is then passed through to the
994 * TODO PAGER. Ugh. That still won't help for "needsterminal" anyway */
995 if ((tio_set = ((options & OPT_INTERACTIVE) &&
996 (infd == COMMAND_FD_PASS || outfd == COMMAND_FD_PASS)))) {
997 tcgetattr((options & OPT_TTYIN ? STDIN_FILENO : STDOUT_FILENO), &a_popen_tios);
998 sigfillset(&nset);
999 sigdelset(&nset, SIGCHLD);
1000 /* sigdelset(&nset, SIGPIPE); TODO would need a handler */
1001 sigprocmask(SIG_BLOCK, &nset, &oset);
1002 a_popen_hadsig = 0;
1003 a_popen_jobsigs_up();
1006 if ((rv = start_command(cmd, mask, infd, outfd, a0, a1, a2, env_addon)) < 0)
1007 rv = -1;
1008 else {
1009 if (wait_child(rv, NULL))
1010 rv = 0;
1011 else {
1012 if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1013 n_err(_("Fatal error in process\n"));
1014 rv = -1;
1018 if (tio_set) {
1019 a_popen_jobsigs_down();
1020 tio_set = ((options & OPT_TTYIN) != 0);
1021 tcsetattr((tio_set ? STDIN_FILENO : STDOUT_FILENO),
1022 (tio_set ? TCSAFLUSH : TCSADRAIN), &a_popen_tios);
1023 sigprocmask(SIG_SETMASK, &oset, NULL);
1025 NYD_LEAVE;
1026 return rv;
1029 FL int
1030 start_command(char const *cmd, sigset_t *mask, int infd, int outfd,
1031 char const *a0, char const *a1, char const *a2,
1032 char const **env_addon)
1034 int rv;
1035 NYD_ENTER;
1037 if ((rv = fork_child()) == -1) {
1038 n_perr(_("fork"), 0);
1039 rv = -1;
1040 } else if (rv == 0) {
1041 char *argv[128];
1042 int i;
1044 if (env_addon != NULL) { /* TODO env_addon; should have struct child */
1045 extern char **environ;
1046 size_t ei, ei_orig, ai, ai_orig;
1047 char **env;
1049 /* TODO note we don't check the POSIX limit:
1050 * the total space used to store the environment and the arguments to
1051 * the process is limited to {ARG_MAX} bytes */
1052 for (ei = 0; environ[ei] != NULL; ++ei)
1054 ei_orig = ei;
1055 for (ai = 0; env_addon[ai] != NULL; ++ai)
1057 ai_orig = ai;
1058 env = ac_alloc(sizeof(*env) * (ei + ai +1));
1059 memcpy(env, environ, sizeof(*env) * ei);
1061 /* Replace all those keys that yet exist */
1062 while (ai-- > 0) {
1063 char const *ee, *kvs;
1064 size_t kl;
1066 ee = env_addon[ai];
1067 kvs = strchr(ee, '=');
1068 assert(kvs != NULL);
1069 kl = PTR2SIZE(kvs - ee);
1070 assert(kl > 0);
1071 for (ei = ei_orig; ei-- > 0;) {
1072 char const *ekvs = strchr(env[ei], '=');
1073 if (ekvs != NULL && kl == PTR2SIZE(ekvs - env[ei]) &&
1074 !memcmp(ee, env[ei], kl)) {
1075 env[ei] = UNCONST(ee);
1076 env_addon[ai] = NULL;
1077 break;
1082 /* And append the rest */
1083 for (ei = ei_orig, ai = ai_orig; ai-- > 0;)
1084 if (env_addon[ai] != NULL)
1085 env[ei++] = UNCONST(env_addon[ai]);
1087 env[ei] = NULL;
1088 environ = env;
1091 i = getrawlist(cmd, strlen(cmd), argv, NELEM(argv), 0);
1093 if ((argv[i++] = UNCONST(a0)) != NULL &&
1094 (argv[i++] = UNCONST(a1)) != NULL &&
1095 (argv[i++] = UNCONST(a2)) != NULL)
1096 argv[i] = NULL;
1097 prepare_child(mask, infd, outfd);
1098 execvp(argv[0], argv);
1099 perror(argv[0]);
1100 _exit(EXIT_ERR);
1102 NYD_LEAVE;
1103 return rv;
1106 FL void
1107 prepare_child(sigset_t *nset, int infd, int outfd)
1109 int i;
1110 sigset_t fset;
1111 NYD_ENTER;
1113 /* All file descriptors other than 0, 1, and 2 are supposed to be cloexec */
1114 /* TODO WHAT IS WITH STDERR_FILENO DAMN? */
1115 if ((i = (infd == COMMAND_FD_NULL)))
1116 infd = open("/dev/null", O_RDONLY);
1117 if (infd >= 0) {
1118 dup2(infd, STDIN_FILENO);
1119 if (i)
1120 close(infd);
1123 if ((i = (outfd == COMMAND_FD_NULL)))
1124 outfd = open("/dev/null", O_WRONLY);
1125 if (outfd >= 0) {
1126 dup2(outfd, STDOUT_FILENO);
1127 if (i)
1128 close(outfd);
1131 if (nset) {
1132 for (i = 1; i < NSIG; ++i)
1133 if (sigismember(nset, i))
1134 safe_signal(i, SIG_IGN);
1135 if (!sigismember(nset, SIGINT))
1136 safe_signal(SIGINT, SIG_DFL);
1139 sigemptyset(&fset);
1140 sigprocmask(SIG_SETMASK, &fset, NULL);
1141 NYD_LEAVE;
1144 FL void
1145 free_child(int pid)
1147 sigset_t nset, oset;
1148 struct child *cp;
1149 NYD_ENTER;
1151 sigemptyset(&nset);
1152 sigaddset(&nset, SIGCHLD);
1153 sigprocmask(SIG_BLOCK, &nset, &oset);
1155 if ((cp = _findchild(pid, FAL0)) != NULL) {
1156 if (cp->done)
1157 _delchild(cp);
1158 else
1159 cp->free = 1;
1162 sigprocmask(SIG_SETMASK, &oset, NULL);
1163 NYD_LEAVE;
1166 FL bool_t
1167 wait_child(int pid, int *wait_status)
1169 sigset_t nset, oset;
1170 struct child *cp;
1171 int ws;
1172 bool_t rv;
1173 NYD_ENTER;
1175 sigemptyset(&nset);
1176 sigaddset(&nset, SIGCHLD);
1177 sigprocmask(SIG_BLOCK, &nset, &oset);
1179 cp = _findchild(pid, FAL0);
1180 if (cp != NULL) {
1181 while (!cp->done)
1182 sigsuspend(&oset);
1183 ws = cp->status;
1184 _delchild(cp);
1185 } else
1186 ws = 0;
1188 sigprocmask(SIG_SETMASK, &oset, NULL);
1190 if (wait_status != NULL)
1191 *wait_status = ws;
1192 rv = (WIFEXITED(ws) && WEXITSTATUS(ws) == 0);
1193 NYD_LEAVE;
1194 return rv;
1197 /* s-it-mode */