]> git.decadent.org.uk Git - ion3.git/blob - libmainloop/exec.c
[svn-upgrade] Integrating new upstream version, ion3 (20071130)
[ion3.git] / libmainloop / exec.c
1 /*
2  * ion/mainloop/exec.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <limits.h>
10 #include <sys/types.h>
11 #include <sys/signal.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <time.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <errno.h>
20
21 #include <libtu/output.h>
22 #include <libtu/misc.h>
23 #include <libtu/locale.h>
24 #include <libtu/types.h>
25
26 #include "select.h"
27 #include "exec.h"
28
29
30 /*{{{ Exec/spawn/fork */
31
32 #define SHELL_PATH "/bin/sh"
33 #define SHELL_NAME "sh"
34 #define SHELL_ARG "-c"
35
36
37 void mainloop_do_exec(const char *cmd)
38 {
39     char *argv[4];
40
41     argv[0]=SHELL_NAME;
42     argv[1]=SHELL_ARG;
43     argv[2]=(char*)cmd; /* stupid execve... */
44     argv[3]=NULL;
45     execvp(SHELL_PATH, argv);
46 }
47
48
49 static int mypipe(int *fds)
50 {
51     int r=pipe(fds);
52     if(r==0){
53         cloexec_braindamage_fix(fds[0]);
54         cloexec_braindamage_fix(fds[1]);
55     }else{
56         warn_err_obj("pipe()");
57     }
58     return r;
59 }
60
61
62 static bool unblock(int fd)
63 {
64     int fl=fcntl(fd, F_GETFL);
65     if(fl!=-1)
66         fl=fcntl(fd, F_SETFL, fl|O_NONBLOCK);
67     return (fd!=-1);
68 }
69
70
71 static void duppipe(int fd, int idx, int *fds)
72 {
73     close(fd);
74     dup(fds[idx]);
75     close(fds[0]);
76     close(fds[1]);
77 }
78
79
80 pid_t mainloop_fork(void (*fn)(void *p), void *fnp,
81                     int *infd, int *outfd, int *errfd)
82 {
83     int pid;
84     int infds[2];
85     int outfds[2];
86     int errfds[2];
87     
88     if(infd!=NULL){
89         if(mypipe(infds)!=0)
90             return -1;
91     }
92
93     if(outfd!=NULL){
94         if(mypipe(outfds)!=0)
95             goto err1;
96     }
97
98     if(errfd!=NULL){
99         if(mypipe(errfds)!=0)
100             goto err2;
101     }
102     
103
104     pid=fork();
105     
106     if(pid<0)
107         goto err3;
108     
109     if(pid!=0){
110         if(outfd!=NULL){
111             if(!unblock(outfds[0]))
112                 goto err3;
113             *outfd=outfds[0];
114             close(outfds[1]);
115         }
116
117         if(errfd!=NULL){
118             if(!unblock(errfds[0]))
119                 goto err3;
120             *errfd=errfds[0];
121             close(errfds[1]);
122         }
123         
124         if(infd!=NULL){
125             *infd=infds[1];
126             close(infds[0]);
127         }
128         
129         return pid;
130     }
131
132     if(infd!=NULL)
133         duppipe(0, 0, infds);
134     if(outfd!=NULL)
135         duppipe(1, 1, outfds);
136     if(errfd!=NULL)
137         duppipe(2, 1, errfds);
138     
139     fn(fnp);
140
141     abort();
142
143 err3:
144     warn_err();
145     if(errfd!=NULL){
146         close(errfds[0]);
147         close(errfds[1]);
148     }
149 err2:
150     if(outfd!=NULL){
151         close(outfds[0]);
152         close(outfds[1]);
153     }
154 err1:    
155     if(infd!=NULL){
156         close(infds[0]);
157         close(infds[1]);
158     }
159     return -1;
160 }
161
162
163 typedef struct{
164     const char *cmd;
165     void (*initenv)(void *p);
166     void *initenvp;
167 } SpawnP;
168
169
170 static void do_spawn(void *spawnp)
171 {
172     SpawnP *p=(SpawnP*)spawnp;
173     
174     if(p->initenv)
175         p->initenv(p->initenvp);
176     mainloop_do_exec(p->cmd);
177 }
178
179
180 pid_t mainloop_do_spawn(const char *cmd, 
181                         void (*initenv)(void *p), void *p,
182                         int *infd, int *outfd, int *errfd)
183 {
184     SpawnP spawnp;
185     
186     spawnp.cmd=cmd;
187     spawnp.initenv=initenv;
188     spawnp.initenvp=p;
189     
190     return mainloop_fork(do_spawn, (void*)&spawnp, infd, outfd, errfd);
191 }
192
193
194 pid_t mainloop_spawn(const char *cmd)
195 {
196     return mainloop_do_spawn(cmd, NULL, NULL, NULL, NULL, NULL);
197 }
198
199
200 /*}}}*/
201
202
203 /*{{{ popen_bgread */
204
205
206 #define BL 1024
207
208 bool mainloop_process_pipe_extlfn(int fd, ExtlFn fn)
209 {
210     char buf[BL];
211     int n;
212     
213     n=read(fd, buf, BL-1);
214     if(n<0){
215         if(errno==EAGAIN || errno==EINTR)
216             return TRUE;
217         n=0;
218         warn_err_obj(TR("reading a pipe"));
219         return FALSE;
220     }else if(n>0){
221         buf[n]='\0';
222         extl_call(fn, "s", NULL, &buf);
223         return TRUE;
224     }else/* if(n==0)*/{
225         /* Call with no argument/NULL string to signify EOF */
226         extl_call(fn, NULL, NULL);
227         return FALSE;
228     }
229 }
230
231
232 static void process_pipe(int fd, void *p)
233 {
234     if(!mainloop_process_pipe_extlfn(fd, *(ExtlFn*)p)){
235         /* We get here on EOL or if the handler failed */
236         mainloop_unregister_input_fd(fd);
237         close(fd);
238         extl_unref_fn(*(ExtlFn*)p);
239         free(p);
240     }
241 }
242
243
244 bool mainloop_register_input_fd_extlfn(int fd, ExtlFn fn)
245 {
246     ExtlFn *p=ALLOC(ExtlFn);
247     if(p!=NULL){
248         *(ExtlFn*)p=extl_ref_fn(fn);
249         if(mainloop_register_input_fd(fd, p, process_pipe))
250             return TRUE;
251         extl_unref_fn(*(ExtlFn*)p);
252         free(p);
253     }
254     return FALSE;
255 }
256
257
258 pid_t mainloop_popen_bgread(const char *cmd, 
259                             void (*initenv)(void *p), void *p,
260                             ExtlFn handler, ExtlFn errhandler)
261 {
262     pid_t pid=-1;
263     int fd=-1, errfd=-1;
264     ExtlFn none=extl_fn_none();
265     
266     pid=mainloop_do_spawn(cmd, initenv, p, NULL, 
267                           (handler!=none ? &fd : NULL),
268                           (errhandler!=none ? &errfd : NULL));
269     
270     if(pid>0){
271         if(handler!=none){
272             if(!mainloop_register_input_fd_extlfn(fd, handler))
273                 goto err;
274         }
275         if(errhandler!=extl_fn_none()){
276             if(!mainloop_register_input_fd_extlfn(errfd, errhandler))
277                 goto err;
278         }
279     }
280     
281     return pid;
282
283 err:
284     if(fd>=0)
285         close(fd);
286     if(errfd>=0)
287         close(errfd);
288     return -1;
289 }
290
291
292 /*}}}*/
293
294
295 /*{{{ Misc. */
296
297
298 void cloexec_braindamage_fix(int fd)
299 {
300     fcntl(fd, F_SETFD, FD_CLOEXEC);
301 }
302
303
304 /*}}}*/
305