No longer trap fatal signals
[notion/jeffpc.git] / libmainloop / exec.c
blob71d36531f241ebb023ea22ee3fd6b6300587bcc7
1 /*
2 * notion/mainloop/exec.c
4 * Copyright (c) the Notion team 2013.
5 * Copyright (c) Tuomo Valkonen 1999-2009.
7 * See the included file LICENSE for details.
8 */
10 #include <limits.h>
11 #include <sys/types.h>
12 #include <sys/signal.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <signal.h>
16 #include <time.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <errno.h>
22 #include <libtu/output.h>
23 #include <libtu/misc.h>
24 #include <libtu/locale.h>
25 #include <libtu/types.h>
27 #include "select.h"
28 #include "exec.h"
31 /*{{{ Exec/spawn/fork */
33 #define SHELL_PATH "/bin/sh"
34 #define SHELL_NAME "sh"
35 #define SHELL_ARG "-c"
38 void mainloop_do_exec(const char *cmd)
40 char *argv[4];
42 argv[0]=SHELL_NAME;
43 argv[1]=SHELL_ARG;
44 argv[2]=(char*)cmd; /* stupid execve... */
45 argv[3]=NULL;
46 execvp(SHELL_PATH, argv);
50 static int mypipe(int *fds)
52 int r=pipe(fds);
53 if(r==0){
54 cloexec_braindamage_fix(fds[0]);
55 cloexec_braindamage_fix(fds[1]);
56 }else{
57 warn_err_obj("pipe()");
59 return r;
63 static bool unblock(int fd)
65 int fl=fcntl(fd, F_GETFL);
66 if(fl!=-1)
67 fl=fcntl(fd, F_SETFL, fl|O_NONBLOCK);
68 return (fd!=-1);
72 static void duppipe(int fd, int idx, int *fds)
74 close(fd);
75 dup(fds[idx]);
76 close(fds[0]);
77 close(fds[1]);
81 pid_t mainloop_fork(void (*fn)(void *p), void *fnp,
82 int *infd, int *outfd, int *errfd)
84 int pid;
85 int infds[2];
86 int outfds[2];
87 int errfds[2];
89 if(infd!=NULL){
90 if(mypipe(infds)!=0)
91 return -1;
94 if(outfd!=NULL){
95 if(mypipe(outfds)!=0)
96 goto err1;
99 if(errfd!=NULL){
100 if(mypipe(errfds)!=0)
101 goto err2;
105 pid=fork();
107 if(pid<0)
108 goto err3;
110 if(pid!=0){
111 // We're the parent
113 if(outfd!=NULL){
114 if(!unblock(outfds[0]))
115 goto err3;
116 *outfd=outfds[0];
117 close(outfds[1]);
120 if(errfd!=NULL){
121 if(!unblock(errfds[0]))
122 goto err3;
123 *errfd=errfds[0];
124 close(errfds[1]);
127 if(infd!=NULL){
128 *infd=infds[1];
129 close(infds[0]);
132 return pid;
133 } else {
134 // We're the child
136 if(infd!=NULL)
137 duppipe(0, 0, infds);
138 if(outfd!=NULL)
139 duppipe(1, 1, outfds);
140 if(errfd!=NULL)
141 duppipe(2, 1, errfds);
143 fn(fnp);
145 abort();
148 err3:
149 warn_err();
150 if(errfd!=NULL){
151 close(errfds[0]);
152 close(errfds[1]);
154 err2:
155 if(outfd!=NULL){
156 close(outfds[0]);
157 close(outfds[1]);
159 err1:
160 if(infd!=NULL){
161 close(infds[0]);
162 close(infds[1]);
164 return -1;
168 typedef struct{
169 const char *cmd;
170 void (*initenv)(void *p);
171 void *initenvp;
172 } SpawnP;
175 static void do_spawn(void *spawnp)
177 SpawnP *p=(SpawnP*)spawnp;
179 if(p->initenv)
180 p->initenv(p->initenvp);
181 mainloop_do_exec(p->cmd);
185 pid_t mainloop_do_spawn(const char *cmd,
186 void (*initenv)(void *p), void *p,
187 int *infd, int *outfd, int *errfd)
189 SpawnP spawnp;
191 spawnp.cmd=cmd;
192 spawnp.initenv=initenv;
193 spawnp.initenvp=p;
195 return mainloop_fork(do_spawn, (void*)&spawnp, infd, outfd, errfd);
199 pid_t mainloop_spawn(const char *cmd)
201 return mainloop_do_spawn(cmd, NULL, NULL, NULL, NULL, NULL);
205 /*}}}*/
208 /*{{{ popen_bgread */
211 #define BL 1024
213 bool mainloop_process_pipe_extlfn(int fd, ExtlFn fn)
215 char buf[BL];
216 int n;
218 n=read(fd, buf, BL-1);
219 if(n<0){
220 if(errno==EAGAIN || errno==EINTR)
221 return TRUE;
222 n=0;
223 warn_err_obj(TR("reading a pipe"));
224 return FALSE;
225 }else if(n>0){
226 buf[n]='\0';
227 extl_call(fn, "s", NULL, &buf);
228 return TRUE;
229 }else/* if(n==0)*/{
230 /* Call with no argument/NULL string to signify EOF */
231 extl_call(fn, NULL, NULL);
232 return FALSE;
237 static void process_pipe(int fd, void *p)
239 if(!mainloop_process_pipe_extlfn(fd, *(ExtlFn*)p)){
240 /* We get here on EOL or if the handler failed */
241 mainloop_unregister_input_fd(fd);
242 close(fd);
243 extl_unref_fn(*(ExtlFn*)p);
244 free(p);
249 bool mainloop_register_input_fd_extlfn(int fd, ExtlFn fn)
251 ExtlFn *p=ALLOC(ExtlFn);
252 if(p!=NULL){
253 *(ExtlFn*)p=extl_ref_fn(fn);
254 if(mainloop_register_input_fd(fd, p, process_pipe))
255 return TRUE;
256 extl_unref_fn(*(ExtlFn*)p);
257 free(p);
259 return FALSE;
263 pid_t mainloop_popen_bgread(const char *cmd,
264 void (*initenv)(void *p), void *p,
265 ExtlFn handler, ExtlFn errhandler)
267 pid_t pid=-1;
268 int fd=-1, errfd=-1;
269 ExtlFn none=extl_fn_none();
271 pid=mainloop_do_spawn(cmd, initenv, p, NULL,
272 (handler!=none ? &fd : NULL),
273 (errhandler!=none ? &errfd : NULL));
275 if(pid>0){
276 if(handler!=none){
277 if(!mainloop_register_input_fd_extlfn(fd, handler))
278 goto err;
280 if(errhandler!=extl_fn_none()){
281 if(!mainloop_register_input_fd_extlfn(errfd, errhandler))
282 goto err;
286 return pid;
288 err:
289 if(fd>=0)
290 close(fd);
291 if(errfd>=0)
292 close(errfd);
293 return -1;
297 /*}}}*/
300 /*{{{ Misc. */
303 void cloexec_braindamage_fix(int fd)
305 fcntl(fd, F_SETFD, FD_CLOEXEC);
309 /*}}}*/