root/src/modules/external_proc.c

Revision ec0f79b5ee03bd254353c5cb634373649f79ebc2, 9.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

several warning (now error) fixes on Linux

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include <assert.h>
38 #include <sys/mman.h>
39 #include <poll.h>
40 #include <errno.h>
41 #ifdef HAVE_SYS_WAIT_H
42 #include <sys/wait.h>
43 #endif
44
45 #include "utils/noit_skiplist.h"
46 #include "utils/noit_log.h"
47 #include "external_proc.h"
48
49 static void finish_procs();
50 static noit_log_stream_t nlerr = NULL;
51 static noit_log_stream_t nldeb = NULL;
52 int in_fd, out_fd;
53
54 struct proc_state {
55   int64_t check_no;
56   int cancelled;
57   pid_t pid;
58   int32_t status;
59   char *path;
60   char **argv;
61   char **envp;
62   int stdout_fd;
63   int stderr_fd;
64 };
65
66 void proc_state_free(struct proc_state *ps) {
67   int i;
68   free(ps->path);
69   for(i=0; ps->argv[i]; i++)
70     free(ps->argv[i]);
71   free(ps->argv);
72   for(i=0; ps->envp[i]; i++)
73     free(ps->envp[i]);
74   free(ps->envp);
75   if(ps->stdout_fd >= 0) close(ps->stdout_fd);
76   if(ps->stderr_fd >= 0) close(ps->stderr_fd);
77 }
78
79 noit_skiplist active_procs;
80 noit_skiplist done_procs;
81
82 static int __proc_state_check_no(const void *av, const void *bv) {
83   struct proc_state *a = (struct proc_state *)av;
84   struct proc_state *b = (struct proc_state *)bv;
85   if(a->check_no == b->check_no) return 0;
86   if(a->check_no < b->check_no) return -1;
87   return 1;
88 }
89 static int __proc_state_check_no_key(const void *akv, const void *bv) {
90   int64_t *acheck_no = (int64_t *)akv;
91   struct proc_state *b = (struct proc_state *)bv;
92   if(*acheck_no == b->check_no) return 0;
93   if(*acheck_no < b->check_no) return -1;
94   return 1;
95 }
96
97 static int __proc_state_pid(const void *av, const void *bv) {
98   struct proc_state *a = (struct proc_state *)av;
99   struct proc_state *b = (struct proc_state *)bv;
100   if(a->pid == b->pid) return 0;
101   if(a->pid < b->pid) return -1;
102   return 1;
103 }
104 static int __proc_state_pid_key(const void *akv, const void *bv) {
105   pid_t *apid = (pid_t *)akv;
106   struct proc_state *b = (struct proc_state *)bv;
107   if(*apid == b->pid) return 0;
108   if(*apid < b->pid) return -1;
109   return 1;
110 }
111
112 static void external_sigchld(int sig) {
113   noit_skiplist_node *iter = NULL;
114   struct proc_state *ps;
115   int status = 0;
116   pid_t pid;
117   pid = waitpid(0, &status, WNOHANG);
118   ps = noit_skiplist_find_compare(&active_procs, &pid, &iter, __proc_state_pid);
119   noitL(nldeb, "reaped pid %d (check: %lld) -> %x\n",
120         pid, (long long int)(ps?ps->check_no:-1), status);
121   if(ps) {
122     ps->status = status;
123     noit_skiplist_remove_compare(&active_procs, &pid, NULL,  __proc_state_pid);
124     noit_skiplist_insert(&done_procs, ps);
125   }
126 }
127
128 static void fetch_and_kill_by_check(int64_t check_no) {
129   struct proc_state *ps;
130   ps = noit_skiplist_find(&active_procs, &check_no, NULL);
131   if(ps) {
132     ps->cancelled = 1;
133     kill(ps->pid, SIGKILL);
134   }
135 }
136
137 #define assert_read(fd, d, l) do { \
138   int len; \
139   while((len = read(fd,d,l)) == -1 && errno == EINTR) finish_procs(); \
140   assert(len == l); \
141 } while(0)
142 #define assert_write(fd, s, l) do { \
143   int len; \
144   while((len = write(fd,s,l)) == -1 && errno == EINTR) finish_procs(); \
145   assert(len == l); \
146 } while(0)
147
148 int write_out_backing_fd(int ofd, int bfd) {
149   char *mmap_buf;
150   u_int16_t outlen;
151   struct stat buf;
152
153   if(fstat(bfd, &buf) == -1) {
154     noitL(nldeb, "external: fstat error: %s\n", strerror(errno));
155     goto bail;
156   }
157   /* Our output length is limited to 64k (including a \0) */
158   /* So, we'll limit the mapping of the file to 0xfffe */
159   if(buf.st_size > 0xfffe) outlen = 0xfffe;
160   outlen = buf.st_size & 0xffff;
161   /* If we have no length, we can skip all this nonsense */
162   if(outlen == 0) goto bail;
163
164   mmap_buf = mmap(NULL, outlen, PROT_READ, MAP_SHARED, bfd, 0);
165   if(mmap_buf == (char *)-1) {
166     noitL(nldeb, "external: mmap error: %s\n", strerror(errno));
167     goto bail;
168   }
169   outlen++; /* no null on the end, but we're reporting one */
170   assert_write(ofd, &outlen, sizeof(outlen));
171   outlen--; /* set it back to write and munmap */
172   assert_write(ofd, mmap_buf, outlen);
173   assert_write(ofd, "", 1);
174   munmap(mmap_buf, outlen);
175   return outlen+1;
176
177  bail:
178   outlen = 1;
179   assert_write(ofd, &outlen, sizeof(outlen));
180   assert_write(ofd, "", 1);
181   return 1;
182 }
183
184 static void finish_procs() {
185   struct proc_state *ps;
186   noitL(noit_error, "%d done procs to cleanup\n", done_procs.size);
187   while((ps = noit_skiplist_pop(&done_procs, NULL)) != NULL) {
188     noitL(noit_error, "finished %lld/%d\n", (long long int)ps->check_no, ps->pid);
189     if(ps->cancelled == 0) {
190       assert_write(out_fd, &ps->check_no,
191                    sizeof(ps->check_no));
192       assert_write(out_fd, &ps->status,
193                    sizeof(ps->status));
194       write_out_backing_fd(out_fd, ps->stdout_fd);
195       write_out_backing_fd(out_fd, ps->stderr_fd);
196     }
197     proc_state_free(ps);
198   }
199 }
200
201 int external_proc_spawn(struct proc_state *ps) {
202   int i, stdin_fd;
203   char stdoutfile[PATH_MAX];
204   char stderrfile[PATH_MAX];
205
206   noitL(nldeb, "About to spawn: (%s)\n", ps->path);
207   strlcpy(stdoutfile, "/tmp/noitext.XXXXXX", PATH_MAX);
208   ps->stdout_fd = mkstemp(stdoutfile);
209   if(ps->stdout_fd < 0) goto prefork_fail;
210   unlink(stdoutfile);
211   strlcpy(stderrfile, "/tmp/noitext.XXXXXX", PATH_MAX);
212   ps->stderr_fd = mkstemp(stderrfile);
213   if(ps->stderr_fd < 0) goto prefork_fail;
214   unlink(stderrfile);
215   ps->pid = fork();
216   if(ps->pid == -1) goto prefork_fail;
217
218   /* Here.. fork has succeeded */
219   if(ps->pid) {
220     noit_skiplist_insert(&active_procs, ps);
221     return 0;
222   }
223   /* Run the process */
224   stdin_fd = open("/dev/null", O_RDONLY);
225   if(stdin_fd < 0) close(0);
226   else dup2(stdin_fd, 0);
227   dup2(ps->stdout_fd, 1);
228   dup2(ps->stderr_fd, 2);
229   /* Shut off everything but std{in,out,err} */
230   for(i=3;i<256;i++) close(i);
231   execve(ps->path, ps->argv, ps->envp);
232   exit(-1);
233  prefork_fail:
234   ps->status = -1;
235   noit_skiplist_insert(&done_procs, ps);
236   return -1;
237 }
238
239 int external_child(external_data_t *data) {
240   in_fd = data->pipe_n2e[0];
241   out_fd = data->pipe_e2n[1];
242   nlerr = data->nlerr;
243   nldeb = data->nldeb;
244
245   /* switch to / */
246   if(chdir("/") != 0) {
247     noitL(noit_error, "Failed chdir(\"/\"): %s\n", strerror(errno));
248     return -1;
249   }
250
251   signal(SIGCHLD, external_sigchld);
252   noit_skiplist_init(&active_procs);
253   noit_skiplist_set_compare(&active_procs, __proc_state_check_no,
254                             __proc_state_check_no_key);
255   noit_skiplist_add_index(&active_procs, __proc_state_pid,
256                           __proc_state_pid_key);
257   noit_skiplist_init(&done_procs);
258   noit_skiplist_set_compare(&done_procs, __proc_state_check_no,
259                             __proc_state_check_no_key);
260
261   while(1) {
262     struct pollfd pfd;
263     struct proc_state *proc_state;
264     int64_t check_no;
265     int16_t argcnt, *arglens, envcnt, *envlens;
266     int i;
267
268     /* We poll here so that we can be interrupted by the SIGCHLD */
269     pfd.fd = in_fd;
270     pfd.events = POLLIN;
271     while(poll(&pfd, 1, -1) == -1 && errno == EINTR) finish_procs();
272
273     assert_read(in_fd, &check_no, sizeof(check_no));
274     assert_read(in_fd, &argcnt, sizeof(argcnt));
275     if(argcnt == 0) {
276       /* cancellation */
277       fetch_and_kill_by_check(check_no);
278       continue;
279     }
280     assert(argcnt > 1);
281     proc_state = calloc(1, sizeof(*proc_state));
282     proc_state->stdout_fd = -1;
283     proc_state->stderr_fd = -1;
284     proc_state->check_no = check_no;
285
286     /* read in the argument lengths */
287     arglens = malloc(argcnt * sizeof(*arglens));
288     assert_read(in_fd, arglens, argcnt * sizeof(*arglens));
289     /* first string is the path, second is the first argv[0] */
290     /* we need to allocate argcnt + 1 (NULL), but the first is path */
291     proc_state->argv = malloc(argcnt * sizeof(*proc_state->argv));
292     /* read each string, first in path, second into argv[0], ... */
293     proc_state->path = malloc(arglens[0]);
294     assert_read(in_fd, proc_state->path, arglens[0]);
295     for(i=0; i<argcnt-1; i++) {
296       proc_state->argv[i] = malloc(arglens[i+1]);
297       assert_read(in_fd, proc_state->argv[i], arglens[i+1]);
298     }
299     proc_state->argv[i] = NULL;
300     free(arglens);
301
302     /* similar thing with envp, but no path trickery */
303     assert_read(in_fd, &envcnt, sizeof(envcnt));
304     envlens = malloc(envcnt * sizeof(*envlens));
305     assert_read(in_fd, envlens, envcnt * sizeof(*envlens));
306     proc_state->envp = malloc((envcnt+1) * sizeof(*proc_state->envp));
307     for(i=0; i<envcnt; i++) {
308       proc_state->envp[i] = malloc(envlens[i]);
309       assert_read(in_fd, proc_state->envp[i], envlens[i]);
310     }
311     proc_state->envp[i] = NULL;
312     free(envlens);
313
314     /* All set, this just needs to be run */
315     external_proc_spawn(proc_state);
316
317     finish_procs();
318   }
319 }
320
Note: See TracBrowser for help on using the browser.