exit system.c, mutt_system goes into lib-sys/
[apps/madmutt.git] / lib-sys / unix.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5  *
6  * This file is part of mutt-ng, see http://www.muttng.org/.
7  * It's licensed under the GNU General Public License,
8  * please see the file GPL in the top level source directory.
9  */
10
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
15
16 #include <lib-lib/macros.h>
17 #include <lib-lib/mem.h>
18 #include <lib-lib/str.h>
19
20 #include "unix.h"
21 #include "mutt_signal.h"
22
23 #include <imap/imap.h> /* for imap_wait_keepalive EEEEK */
24
25
26 /* Extract the real name from /etc/passwd's GECOS field.
27  * When set, honor the regular expression in rx,
28  * otherwise assume that the GECOS field is a comma-separated list.
29  * Replace "&" by a capitalized version of the user's login name.
30  */
31 ssize_t mutt_gecos_name(char *dst, ssize_t n, struct passwd *pw, rx_t *rx)
32 {
33     const char *p, *end;
34     ssize_t len;
35
36     *dst = '\0';
37     len  = 0;
38
39     if (!pw->pw_gecos)
40         return 0;
41
42     if (rx) {
43         regmatch_t pat_match[1];
44
45         if (regexec(rx->rx, pw->pw_gecos, 1, pat_match, 0)) {
46             return 0;
47         }
48
49         p   = pw->pw_gecos + pat_match[0].rm_so;
50         end = pw->pw_gecos + pat_match[0].rm_so;
51     } else {
52         p   = pw->pw_gecos;
53         end = m_strchrnul(pw->pw_gecos, ',');
54     }
55
56     for (;;) {
57         const char *q = MIN(end, m_strchrnul(p, '&'));
58
59         len += m_strncpy(dst + len, n - len, p, q - p);
60         p = q + 1;
61
62         if (!p[-1] || p >= end)
63             break;
64
65         /* p[0] == '&' */
66         len += m_strcpy(dst + len, n - len, pw->pw_name);
67     }
68
69     return len;
70 }
71
72 int _mutt_system(const char *cmd, int flags)
73 {
74     int rc = -1;
75     struct sigaction act;
76     struct sigaction oldtstp;
77     struct sigaction oldcont;
78     sigset_t set;
79     pid_t pid;
80
81     if (m_strisempty(cmd))
82         return 0;
83
84     /* must ignore SIGINT and SIGQUIT */
85     mutt_block_signals_system();
86
87     /* also don't want to be stopped right now */
88     if (flags & M_DETACH_PROCESS) {
89         sigemptyset(&set);
90         sigaddset(&set, SIGTSTP);
91         sigprocmask(SIG_BLOCK, &set, NULL);
92     } else {
93         act.sa_handler = SIG_DFL;
94         /* we want to restart the waitpid() below */
95 #ifdef SA_RESTART
96         act.sa_flags = SA_RESTART;
97 #endif
98         sigemptyset(&act.sa_mask);
99         sigaction(SIGTSTP, &act, &oldtstp);
100         sigaction(SIGCONT, &act, &oldcont);
101     }
102
103     pid = fork ();
104     if (pid == 0) {
105         act.sa_flags = 0;
106
107         if (flags & M_DETACH_PROCESS) {
108             /* give up controlling terminal */
109             setsid();
110
111             switch (fork()) {
112                 int fd, nb_fds;
113
114               case 0:
115                 nb_fds = getdtablesize();
116                 for (fd = 0; fd < nb_fds; fd++) {
117                     close(fd);
118                 }
119                 chdir ("/");
120                 act.sa_handler = SIG_DFL;
121                 sigaction(SIGCHLD, &act, NULL);
122                 break;
123
124               case -1:
125                 _exit(127);
126
127               default:
128                 _exit(0);
129             }
130         }
131
132         /* reset signals for the child; not really needed, but... */
133         mutt_unblock_signals_system(0);
134         act.sa_handler = SIG_DFL;
135         act.sa_flags = 0;
136         sigemptyset(&act.sa_mask);
137         sigaction(SIGTERM, &act, NULL);
138         sigaction(SIGTSTP, &act, NULL);
139         sigaction(SIGCONT, &act, NULL);
140
141         execl("/bin/sh", "sh", "-c", cmd, NULL);
142         _exit(127);                /* execl error */
143     } else
144     if (pid != -1) {
145         rc = imap_wait_keepalive(pid);
146     }
147
148     sigaction(SIGCONT, &oldcont, NULL);
149     sigaction(SIGTSTP, &oldtstp, NULL);
150
151     /* reset SIGINT, SIGQUIT and SIGCHLD */
152     mutt_unblock_signals_system(1);
153     if (flags & M_DETACH_PROCESS) {
154         sigprocmask (SIG_UNBLOCK, &set, NULL);
155     }
156
157     return (pid > 0 && WIFEXITED(rc)) ? WEXITSTATUS(rc) : -1;
158 }