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