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