]> git.decadent.org.uk Git - ion3.git/blob - mod_statusbar/statusd-launch.c
1f597b2193883c6e67cf1a79d7cbd39b86f29be8
[ion3.git] / mod_statusbar / statusd-launch.c
1 /*
2  * ion/mod_statusbar/statusd-launch.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2008. 
5  *
6  * See the included file LICENSE for details.
7  */
8  
9 #include <sys/time.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <time.h>
13 #include <errno.h>
14
15 #include <libtu/minmax.h>
16 #include <libextl/readconfig.h>
17 #include <libmainloop/exec.h>
18 #include <libmainloop/select.h>
19 #include <libmainloop/signal.h>
20 #include <ioncore/saveload.h>
21 #include <ioncore/bindmaps.h>
22 #include <ioncore/global.h>
23 #include <ioncore/ioncore.h>
24
25 #include "statusbar.h"
26
27
28 #define CF_STATUSD_TIMEOUT_SEC 3
29
30 #define BL 1024
31
32 #define USEC 1000000
33
34
35 static bool process_pipe(int fd, ExtlFn fn, 
36                          bool *doneseen, bool *eagain)
37 {
38     char buf[BL];
39     int n;
40     bool fnret;
41     
42     *eagain=FALSE;
43     
44     n=read(fd, buf, BL-1);
45     
46     if(n<0){
47         if(errno==EAGAIN || errno==EINTR){
48             *eagain=(errno==EAGAIN);
49             return TRUE;
50         }
51         warn_err_obj(TR("reading a pipe"));
52         return FALSE;
53     }else if(n>0){
54         buf[n]='\0';
55         *doneseen=FALSE;
56         return extl_call(fn, "s", "b", &buf, doneseen);
57     }
58     
59     return FALSE;
60 }
61
62
63 static bool wait_statusd_init(int outfd, int errfd, ExtlFn dh, ExtlFn eh)
64 {
65     fd_set rfds;
66     struct timeval tv, endtime, now;
67     int nfds=maxof(outfd, errfd);
68     int retval;
69     bool dummy, doneseen, eagain=FALSE;
70     
71     if(mainloop_gettime(&endtime)!=0){
72         warn_err();
73         return FALSE;
74     }
75     
76     now=endtime;
77     endtime.tv_sec+=CF_STATUSD_TIMEOUT_SEC;
78     
79     while(1){
80         FD_ZERO(&rfds);
81
82         /* Calculate remaining time */
83         if(now.tv_sec>endtime.tv_sec){
84             goto timeout;
85         }else if(now.tv_sec==endtime.tv_sec){
86             if(now.tv_usec>=endtime.tv_usec)
87                 goto timeout;
88             tv.tv_sec=0;
89             tv.tv_usec=endtime.tv_usec-now.tv_usec;
90         }else{
91             tv.tv_usec=USEC+endtime.tv_usec-now.tv_usec;
92             tv.tv_sec=-1+endtime.tv_sec-now.tv_sec;
93             /* Kernel lameness tuner: */
94             tv.tv_sec+=tv.tv_usec/USEC;
95             tv.tv_usec%=USEC;
96         }
97         
98         FD_SET(outfd, &rfds);
99         FD_SET(errfd, &rfds);
100     
101         retval=select(nfds+1, &rfds, NULL, NULL, &tv);
102         if(retval>0){
103             if(FD_ISSET(errfd, &rfds)){
104                 if(!process_pipe(errfd, eh, &dummy, &eagain))
105                     return FALSE;
106             }
107             if(FD_ISSET(outfd, &rfds)){
108                 if(!process_pipe(outfd, dh, &doneseen, &eagain))
109                     return FALSE;
110                 if(doneseen){
111                     /* Read rest of errors. */
112                     bool ok;
113                     do{
114                         ok=process_pipe(errfd, eh, &dummy, &eagain);
115                     }while(ok && !eagain);
116                     return TRUE;
117                 }
118             }
119         }else if(retval==0){
120             goto timeout;
121         }
122         
123         if(mainloop_gettime(&now)!=0){
124             warn_err();
125             return FALSE;
126         }
127     }
128     
129     return TRUE;
130     
131 timeout:
132     /* Just complain to stderr, not startup error log, and do not fail.
133      * The system might just be a bit slow. We can continue, but without
134      * initial values for the meters, geometry adjustments may be necessary
135      * when we finally get that information.
136      */
137     ioncore_warn_nolog(TR("ion-statusd timed out."));
138     return TRUE;
139 }
140
141
142 EXTL_EXPORT
143 int mod_statusbar__launch_statusd(const char *cmd,
144                                   ExtlFn initdatahandler,
145                                   ExtlFn initerrhandler,
146                                   ExtlFn datahandler,
147                                   ExtlFn errhandler)
148 {
149     pid_t pid;
150     int outfd=-1, errfd=-1;
151     
152     if(cmd==NULL)
153         return -1;
154     
155     pid=mainloop_do_spawn(cmd, NULL, NULL,
156                           NULL, &outfd, &errfd);
157     
158     if(pid<0)
159         return -1;
160     
161     if(!wait_statusd_init(outfd, errfd, initdatahandler, initerrhandler))
162         goto err;
163     
164     if(!mainloop_register_input_fd_extlfn(outfd, datahandler))
165         goto err;
166     
167     if(!mainloop_register_input_fd_extlfn(errfd, errhandler))
168         goto err2;
169
170     return pid;
171     
172 err2:    
173     mainloop_unregister_input_fd(outfd);
174 err:    
175     close(outfd);
176     close(errfd);
177     return -1;
178 }
179