3301d959af3a463b778c5a15a16772c7da8959a6
[polyglot.git] / pipex_posix.c
1 #ifndef _WIN32
2
3 // includes
4
5 #include <string.h>
6 #include <stdlib.h>
7 #include <errno.h>
8 #include <wordexp.h>
9 #include <sys/wait.h>
10 #include "pipex.h"
11
12 // constants
13
14 static const unsigned int StringSize = 4096;
15
16 // prototypes
17
18 static void my_close(int fd);
19 static void my_dup2(int old_fd, int new_fd) ;
20
21 // functions
22
23 // pipex_open()
24
25 void pipex_open(pipex_t *pipex,
26                 const char *name,
27                 const char *working_dir,
28                 const char *command){
29     char string[StringSize];
30     int argc;
31     char * ptr;
32     char * argv[256];
33     int from_child[2], to_child[2];
34     wordexp_t p;
35     int i,ret;
36
37     pipex->pid=-1;
38     pipex->io->name=name;
39     pipex->quit_pending=FALSE;
40     pipex->command=command;
41     
42     if(command==NULL){
43         pipex->io->in_fd = STDIN_FILENO;
44         pipex->io->out_fd = STDOUT_FILENO;
45
46             // attach standard error to standard output
47         
48         my_dup2(STDOUT_FILENO,STDERR_FILENO);
49         io_init(pipex->io);
50     }else{
51     
52         // parse the command line and create the argument list
53 #if 0    
54         if (strlen(command) >= StringSize) my_fatal("pipex_open(): buffer overflow\n");
55         strcpy(string,command);
56         argc = 0;
57         
58         for (ptr = strtok(string," "); ptr != NULL; ptr = strtok(NULL," ")) {
59             argv[argc++] = ptr;
60         }
61
62         argv[argc] = NULL;
63 #else
64         //printf("command=[%s]\n",command);
65         //Buffer overflow alert
66         ret=wordexp(command, &p, 0);
67         if(ret!=0){
68           my_fatal("pipex_open(): %s: Unable to parse command.\n",command);
69         }
70         argc = p.we_wordc;
71         if(argc>=256-2){
72           my_fatal("pipex_open(): %s: Too many arguments.\n",command);
73         }
74         for(i=0;i<argc;i++){
75           argv[i] = p.we_wordv[i];
76         }
77         //      int i;
78         //for(i=0;i<argc;i++){
79         //  printf("[%s]",argv[i]);
80         //}
81         //printf("\n");
82         argv[argc] = NULL;
83 #endif        
84       // create the pipes
85         
86         if (pipe(from_child) == -1) {
87             my_fatal("pipex_open(): pipe(): %s\n",strerror(errno));
88         }
89         
90         if (pipe(to_child) == -1) {
91             my_fatal("pipex_open(): pipe(): %s\n",strerror(errno));
92         }
93         
94             // create the child process 
95         
96         pipex->pid = fork();
97         
98         if (pipex->pid == -1) {
99             
100             my_fatal("pipex_open(): fork(): %s\n",strerror(errno));
101             
102         } else if (pipex->pid == 0) {
103             
104                 // child 
105             
106                 // close unused pipe descriptors to avoid deadlocks
107             
108             my_close(from_child[0]);
109             my_close(to_child[1]);
110             
111                 // attach the pipe to standard input
112             
113             my_dup2(to_child[0],STDIN_FILENO);
114             my_close(to_child[0]);
115             
116                 // attach the pipe to standard output
117             
118             my_dup2(from_child[1],STDOUT_FILENO);
119             my_close(from_child[1]);
120             
121                 // attach standard error to standard output
122                 // commenting this out gives error messages on the console
123             
124             my_dup2(STDOUT_FILENO,STDERR_FILENO); 
125             
126             if(chdir(working_dir)){
127                 printf("%s pipex_open(): %s: %s\n",
128                        PIPEX_MAGIC,
129                        working_dir,
130                        strerror(errno));
131                 goto wait_for_eof;
132             }
133             
134             // launch the new executable file
135
136             execvp(argv[0],&argv[0]);
137             
138                 // execvp() only returns when an error has occured
139
140             printf("%s pipex_open(): execvp(): %s: %s\n",
141                    PIPEX_MAGIC,
142                    argv[0],
143                    strerror(errno));
144         wait_for_eof:
145             while(fgets(string,StringSize,stdin));
146             exit(EXIT_SUCCESS);
147             
148         } else { // pid > 0
149             
150             ASSERT(pipex->pid>0);
151             
152                 // parent 
153             
154                 // close unused pipe descriptors to avoid deadlocks
155             
156             my_close(from_child[1]);
157             my_close(to_child[0]);
158             
159                 // fill in the pipex struct
160             
161             pipex->io->in_fd = from_child[0];
162             pipex->io->out_fd = to_child[1];
163             pipex->state|=PIPEX_ACTIVE; // can we test if this really TRUE?
164             
165             io_init(pipex->io);
166         } 
167     }
168 }
169
170 void pipex_wait_event(pipex_t *pipex[]){
171
172     fd_set set[1];
173     pipex_t *p;
174     int fd_max;
175     int val;
176     pipex_t **q;
177
178     q=pipex;
179
180         // init
181
182    FD_ZERO(set);
183    fd_max = -1; // HACK
184    while((p=*(q++))!=NULL){
185        ASSERT(p->io->in_fd>=0);
186        FD_SET(p->io->in_fd,set);
187        if (p->io->in_fd > fd_max){
188            fd_max = p->io->in_fd;
189        }
190    }
191    
192    // wait for something to read (no timeout)
193
194    ASSERT(fd_max>=0);
195    val = select(fd_max+1,set,NULL,NULL,NULL);
196    if (val == -1 && errno != EINTR) my_fatal("pipex_wait_event(): select(): %s\n",strerror(errno));
197
198    q=pipex;
199    if (val > 0) {
200        while((p=*(q++))!=NULL){
201            if (FD_ISSET(p->io->in_fd,set) /*&& !io_line_ready(p->io)*/){
202                io_get_update(p->io); 
203            }
204        }
205    }    
206 }
207
208 // pipex_active()
209
210 bool pipex_active(pipex_t *pipex){
211     return (pipex->state&PIPEX_ACTIVE)!=0;
212 }
213
214 // pipex_eof()
215
216 bool pipex_eof(pipex_t *pipex){
217     return (pipex->state&PIPEX_EOF)!=0;
218 }
219
220
221 // pipex_set_priority()
222
223 void pipex_set_priority(pipex_t *pipex, int value){
224     if(pipex->pid!=-1){
225         setpriority(PRIO_PROCESS,pipex->pid,value);
226     }
227 }
228
229 // pipex_set_affinity()
230
231 void pipex_set_affinity(pipex_t *pipex, int value){
232     my_log("POLYGLOT Setting affinity is not yet implemented on posix\n");
233 }
234
235 // pipex_send_eof()
236
237 void pipex_send_eof(pipex_t *pipex){
238     io_close(pipex->io);
239 }
240
241 // pipex_exit()
242
243 /* This routine waits for kill_timeout milliseconds for 
244  * the process to exit by itself. If that doesn't
245  * happen it will kill the process.
246  */
247
248
249
250 void pipex_exit(pipex_t *pipex, int kill_timeout){
251     int status;
252     int elapsed_time;
253     bool exited;
254     int ret;
255
256     my_log("POLYGLOT Waiting for child process to exit.\n");
257
258     elapsed_time=0;
259     exited=FALSE;
260     ret=0;
261     while(elapsed_time<kill_timeout){
262       ret=waitpid(pipex->pid,&status,WNOHANG);
263       if(ret==0){
264         my_log("POLYGLOT Child has not exited yet. Sleeping %dms.\n", WAIT_GRANULARITY);
265         my_sleep(WAIT_GRANULARITY);
266         elapsed_time+=WAIT_GRANULARITY;
267       }else{
268         exited=TRUE;
269         break;
270       }
271     }
272     if(!exited){
273       my_log("POLYGLOT Child wouldn't exit by itself. Terminating it.\n");
274       kill(pipex->pid,SIGKILL);
275       waitpid(pipex->pid,&status,0);    
276     }
277     if(WIFEXITED(status)){
278       if(pipex->quit_pending){
279         my_log("POLYGLOT Child exited with status %d.\n",WEXITSTATUS(status));
280       }else{
281         // Suppress further messages.
282         pipex->quit_pending=TRUE;
283         my_fatal("pipex_exit(): %s: child exited with status %d.\n",pipex->command,WEXITSTATUS(status));
284       }
285     }else if(WIFSIGNALED(status)){
286       if(pipex->quit_pending){
287         my_log("POLYGLOT pipex_exit(): %s: child terminated with signal %d.\n",pipex->command,WTERMSIG(status));
288       }else{
289         // Suppress further messages.
290         pipex->quit_pending=TRUE;
291           my_fatal("pipex_exit(): %s: child terminated with signal %d.\n",pipex->command,WTERMSIG(status));
292       }
293     }
294     return;
295 }
296
297 // pipex_get_buffer()
298
299 char * pipex_get_buffer(pipex_t *pipex){
300   return pipex->io->in_buffer;
301 }
302
303 // pipex_readln()
304
305 bool pipex_readln(pipex_t *pipex, char *string){
306     while (!io_line_ready(pipex->io)) {
307         io_get_update(pipex->io);
308    }
309    if (!io_get_line(pipex->io,string,StringSize)) { // EOF
310        string[0]='\0';
311        pipex->state|=PIPEX_EOF;
312        return FALSE;
313    }
314    if(strncmp(PIPEX_MAGIC,string,strlen(PIPEX_MAGIC))==0){
315      my_fatal("%s\n",string+strlen(PIPEX_MAGIC)+1);
316    }
317
318    return TRUE;
319 }
320
321 // pipex_readln_nb()
322
323 bool pipex_readln_nb(pipex_t *pipex, char *string){
324
325     while(!pipex->io->in_eof && !io_line_ready(pipex->io) && io_peek(pipex->io)){
326       io_get_update(pipex->io);
327   }
328   
329   if(io_line_ready(pipex->io)){
330     return pipex_readln(pipex,string);
331   }else if(pipex->io->in_eof){
332     string[0]='\0';
333     pipex->state|=PIPEX_EOF;
334     return FALSE;
335   }else {  
336     string[0]='\0';
337     return FALSE;
338   }
339 }
340
341 // pipex_write()
342
343 void pipex_write(pipex_t *pipex, const char *string){
344        io_send_queue(pipex->io,"%s",string);
345 }
346
347
348 // pipex_writeln()
349
350 void pipex_writeln(pipex_t *pipex, const char *string){
351        io_send(pipex->io,"%s",string);
352 }
353
354 // my_close()
355
356 static void my_close(int fd) {
357
358    ASSERT(fd>=0);
359
360    if (close(fd) == -1) my_fatal("my_close(): close(): %s\n",strerror(errno));
361 }
362
363 // my_dup2()
364
365 static void my_dup2(int old_fd, int new_fd) {
366
367    ASSERT(old_fd>=0);
368    ASSERT(new_fd>=0);
369
370    if (dup2(old_fd,new_fd) == -1) my_fatal("my_dup2(): dup2(): %s\n",strerror(errno));
371 }
372
373
374 #endif