1 /***************************************************************************
2     begin       : Sat Dec 27 2003
3     copyright   : (C) 2019 by Martin Preuss
4     email       : martin@libchipcard.de
5 
6 
7  ***************************************************************************
8  *                                                                         *
9  *   This library is free software; you can redistribute it and/or         *
10  *   modify it under the terms of the GNU Lesser General Public            *
11  *   License as published by the Free Software Foundation; either          *
12  *   version 2.1 of the License, or (at your option) any later version.    *
13  *                                                                         *
14  *   This library is distributed in the hope that it will be useful,       *
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
17  *   Lesser General Public License for more details.                       *
18  *                                                                         *
19  *   You should have received a copy of the GNU Lesser General Public      *
20  *   License along with this library; if not, write to the Free Software   *
21  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
22  *   MA  02111-1307  USA                                                   *
23  *                                                                         *
24  ***************************************************************************/
25 
26 
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30 
31 
32 
33 #include "process_p.h"
34 #include "syncio_file_l.h"
35 
36 #include <gwenhywfar/misc.h>
37 #include <gwenhywfar/debug.h>
38 #include <gwenhywfar/text.h>
39 
40 #include <stdlib.h>
41 #include <sys/types.h>
42 #include <sys/wait.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <signal.h>
48 
49 
50 static GWEN_PROCESS *GWEN_Process_ProcessList=0;
51 
52 
GWEN_Process_ModuleInit(void)53 int GWEN_Process_ModuleInit(void)
54 {
55   return 0;
56 }
57 
58 
59 
GWEN_Process_ModuleFini(void)60 int GWEN_Process_ModuleFini(void)
61 {
62   GWEN_PROCESS *pr, *prnext;
63 
64   pr=GWEN_Process_ProcessList;
65   while (pr) {
66     prnext=pr->next;
67 
68     pr->usage=1;
69     GWEN_Process_free(pr);
70     pr=prnext;
71   } /* while */
72   return 0;
73 }
74 
75 
76 
77 #if 0
78 GWEN_PROCESS *GWEN_Process_FindProcess(pid_t pid)
79 {
80   GWEN_PROCESS *pr;
81 
82   pr=GWEN_Process_ProcessList;
83   while (pr) {
84     if (pr->pid==pid)
85       return pr;
86     pr=pr->next;
87   } /* while */
88   return 0;
89 }
90 
91 
92 
93 void GWEN_Process_SignalHandler(int s)
94 {
95   int status;
96   pid_t pid;
97 
98   switch (s) {
99   case SIGCHLD:
100     /* try to get the status */
101     pid=waitpid(0, &status, WNOHANG);
102     if (pid==-1) {
103       DBG_DEBUG(GWEN_LOGDOMAIN, "waitdpid(%d): %s", 0, strerror(errno));
104     }
105     else if (pid==0) {
106       /* process still running ?! */
107       DBG_DEBUG(GWEN_LOGDOMAIN, "Got a SIGCHLD but no child terminated ??");
108     }
109     else {
110       GWEN_PROCESS *pr;
111 
112       /* som process terminated */
113       pr=GWEN_Process_FindProcess(pid);
114       if (!pr) {
115         DBG_NOTICE(GWEN_LOGDOMAIN, "No infomation about process \"%d\" available", (int)pid);
116       }
117       else {
118         GWEN_Process_MakeState(pr, status);
119         /* remove from list. If this process data is not used by the
120          * aplication it will now be freed, otherwise only the usage
121          * counter is decremented */
122         GWEN_Process_free(pr);
123       }
124     }
125     break;
126 
127   default:
128     DBG_ERROR(GWEN_LOGDOMAIN, "Got unhandled signal \"%d\"", s);
129     break;
130   } /* switch */
131 
132 }
133 #endif
134 
135 
136 
GWEN_Process_new(void)137 GWEN_PROCESS *GWEN_Process_new(void)
138 {
139   GWEN_PROCESS *pr;
140 
141   GWEN_NEW_OBJECT(GWEN_PROCESS, pr);
142   pr->state=GWEN_ProcessStateNotStarted;
143   pr->pid=-1;
144   pr->pflags=GWEN_PROCESS_FLAGS_DEFAULT;
145   pr->usage=1;
146   GWEN_LIST_ADD(GWEN_PROCESS, pr, &GWEN_Process_ProcessList);
147   return pr;
148 }
149 
150 
151 
GWEN_Process_free(GWEN_PROCESS * pr)152 void GWEN_Process_free(GWEN_PROCESS *pr)
153 {
154   if (pr) {
155     assert(pr->usage);
156     if (--(pr->usage)==0) {
157       /* unlink from list */
158       GWEN_LIST_DEL(GWEN_PROCESS, pr, &GWEN_Process_ProcessList);
159       GWEN_SyncIo_free(pr->stdIn);
160       GWEN_SyncIo_free(pr->stdOut);
161       GWEN_SyncIo_free(pr->stdErr);
162       GWEN_FREE_OBJECT(pr);
163     }
164   }
165 }
166 
167 
168 
GWEN_Process_Start(GWEN_PROCESS * pr,const char * prg,const char * args)169 GWEN_PROCESS_STATE GWEN_Process_Start(GWEN_PROCESS *pr,
170                                       const char *prg,
171                                       const char *args)
172 {
173   pid_t pid;
174   char *argv[32];
175   int argc;
176   const char *p, *p2;
177   GWEN_BUFFER *wbuf;
178 
179   assert(pr);
180 
181   if (GWEN_Process_Redirect(pr)) {
182     DBG_ERROR(GWEN_LOGDOMAIN, "Could not setup redirections");
183     pr->state=GWEN_ProcessStateNotStarted;
184     pr->pid=-1;
185     return GWEN_ProcessStateNotStarted;
186   }
187 
188   pid=fork();
189   if (pid==-1) {
190     /* error in fork */
191     pr->state=GWEN_ProcessStateNotStarted;
192     pr->pid=-1;
193 
194     /* close all pipes */
195     if (pr->filesStdin[0]!=-1) {
196       close(pr->filesStdin[0]);
197       close(pr->filesStdin[1]);
198     }
199     if (pr->filesStdout[0]!=-1) {
200       close(pr->filesStdout[0]);
201       close(pr->filesStdout[1]);
202     }
203     if (pr->filesStderr[0]!=-1) {
204       close(pr->filesStderr[0]);
205       close(pr->filesStderr[1]);
206     }
207 
208     return GWEN_ProcessStateNotStarted;
209   }
210   else if (pid!=0) {
211     /* parent */
212     DBG_INFO(GWEN_LOGDOMAIN, "Process started with id %d", (int)pid);
213     pr->state=GWEN_ProcessStateRunning;
214     pr->pid=pid;
215 
216     /* setup redirections */
217     if (pr->filesStdin[0]!=-1) {
218       close(pr->filesStdin[1]);
219       pr->stdIn=GWEN_SyncIo_File_fromFd(pr->filesStdin[0]);
220     }
221     if (pr->filesStdout[0]!=-1) {
222       close(pr->filesStdout[1]);
223       pr->stdOut=GWEN_SyncIo_File_fromFd(pr->filesStdout[0]);
224     }
225     if (pr->filesStderr[0]!=-1) {
226       close(pr->filesStderr[1]);
227       pr->stdErr=GWEN_SyncIo_File_fromFd(pr->filesStdout[0]);
228     }
229 
230     return GWEN_ProcessStateRunning;
231   }
232   /* child, build arguments */
233   argc=0;
234 
235   DBG_DEBUG(GWEN_LOGDOMAIN, "I'm the child process");
236 
237   /* setup redirections */
238   if (pr->filesStdin[0]!=-1) {
239     close(pr->filesStdin[0]);
240     close(0);
241     if (dup(pr->filesStdin[1])==-1) {
242       DBG_ERROR(GWEN_LOGDOMAIN, "Could not setup redirection");
243     }
244   }
245   if (pr->filesStdout[0]!=-1) {
246     close(pr->filesStdout[0]);
247     close(1);
248     if (dup(pr->filesStdout[1])==-1) {
249       DBG_ERROR(GWEN_LOGDOMAIN, "Could not setup redirection");
250     }
251   }
252   if (pr->filesStderr[0]!=-1) {
253     close(pr->filesStderr[0]);
254     close(2);
255     if (dup(pr->filesStderr[1])==-1) {
256       DBG_ERROR(GWEN_LOGDOMAIN, "Could not setup redirection");
257     }
258   }
259 
260   argv[0]=strdup(prg);
261   argc++;
262   p=args;
263   wbuf=GWEN_Buffer_new(0, 256, 0, 1);
264   while (argc<32 && *p) {
265     while (*p && isspace((int)*p))
266       p++;
267     if (!(*p))
268       break;
269     if (GWEN_Text_GetWordToBuffer(p, " ",
270                                   wbuf,
271                                   GWEN_TEXT_FLAGS_NULL_IS_DELIMITER |
272                                   GWEN_TEXT_FLAGS_DEL_QUOTES |
273                                   GWEN_TEXT_FLAGS_CHECK_BACKSLASH,
274                                   &p))
275       break;
276 
277     p2=GWEN_Buffer_GetStart(wbuf);
278 
279     argv[argc]=strdup(p2);
280     GWEN_Buffer_Reset(wbuf);
281     argc++;
282   } /* while */
283   GWEN_Buffer_free(wbuf);
284   argv[argc]=0;
285 
286   /* parameters ready, exec */
287   execvp(prg, argv);
288   /* if we reach this point an error occurred */
289   DBG_ERROR(GWEN_LOGDOMAIN, "Could not start program \"%s\": %s",
290             prg, strerror(errno));
291   exit(EXIT_FAILURE);
292 }
293 
294 
295 
GWEN_Process_GetState(GWEN_PROCESS * pr,int w)296 GWEN_PROCESS_STATE GWEN_Process_GetState(GWEN_PROCESS *pr, int w)
297 {
298   int rv;
299   int status;
300 
301   assert(pr);
302   /* try to get the status */
303   rv=waitpid(pr->pid, &status, w?0:WNOHANG);
304   if (rv==-1) {
305     DBG_ERROR(GWEN_LOGDOMAIN, "waitdpid(%d): %s", (int)pr->pid, strerror(errno));
306     return GWEN_ProcessStateUnknown;
307   }
308   else if (rv==0) {
309     /* process still running */
310     return GWEN_ProcessStateRunning;
311   }
312   else {
313     return GWEN_Process_MakeState(pr, status);
314   }
315 }
316 
317 
318 
GWEN_Process_MakeState(GWEN_PROCESS * pr,int status)319 GWEN_PROCESS_STATE GWEN_Process_MakeState(GWEN_PROCESS *pr, int status)
320 {
321   /* process has terminated for any reason */
322   if (WIFEXITED(status)) {
323     /* normal termination */
324     DBG_INFO(GWEN_LOGDOMAIN, "Process %d exited with %d",
325              (int)pr->pid, WEXITSTATUS(status));
326     pr->state=GWEN_ProcessStateExited;
327     pr->pid=-1;
328     /* store result code */
329     pr->result=WEXITSTATUS(status);
330     return pr->state;
331   } /* if exited normally */
332   else if (WIFSIGNALED(status)) {
333     /* uncaught signal */
334     DBG_ERROR(GWEN_LOGDOMAIN, "Process %d terminated by signal %d",
335               (int)pr->pid, WTERMSIG(status));
336     pr->state=GWEN_ProcessStateAborted;
337     pr->pid=-1;
338     return pr->state;
339   } /* if terminated by signal */
340   else if (WIFSTOPPED(status)) {
341     /* process stopped by signal */
342     DBG_ERROR(GWEN_LOGDOMAIN, "Process %d stopped by signal %d",
343               (int)pr->pid, WSTOPSIG(status));
344     pr->state=GWEN_ProcessStateStopped;
345     pr->pid=-1;
346     return pr->state;
347   }
348   else {
349     DBG_ERROR(GWEN_LOGDOMAIN, "Unhandled status, assume process %d isn't running (%08x)",
350               (int)pr->pid, (unsigned int)status);
351     return GWEN_ProcessStateUnknown;
352   }
353 }
354 
355 
356 
GWEN_Process_CheckState(GWEN_PROCESS * pr)357 GWEN_PROCESS_STATE GWEN_Process_CheckState(GWEN_PROCESS *pr)
358 {
359   assert(pr);
360 
361   if (pr->pid==-1)
362     /* we already know the state, return it */
363     return pr->state;
364 
365   /* otherwise try to get the status */
366   return GWEN_Process_GetState(pr, 0);
367 }
368 
369 
370 
GWEN_Process_GetResult(GWEN_PROCESS * pr)371 int GWEN_Process_GetResult(GWEN_PROCESS *pr)
372 {
373   assert(pr);
374   if (GWEN_Process_CheckState(pr)==GWEN_ProcessStateExited)
375     return pr->result;
376   else
377     return -1;
378 }
379 
380 
381 
GWEN_Process_Wait(GWEN_PROCESS * pr)382 int GWEN_Process_Wait(GWEN_PROCESS *pr)
383 {
384   GWEN_PROCESS_STATE pst;
385 
386   assert(pr);
387 
388   if (pr->state!=GWEN_ProcessStateRunning)
389     /* process is not running, so return */
390     return 0;
391 
392   if (pr->pid==-1) {
393     /* process is running, but we have no pid ?! */
394     DBG_ERROR(GWEN_LOGDOMAIN, "Process is running but we don't have its pid");
395     return -1;
396   }
397   pst=GWEN_Process_GetState(pr, 1);
398   if (pst==GWEN_ProcessStateUnknown)
399     return -1;
400   return 0;
401 }
402 
403 
404 
GWEN_Process_Terminate(GWEN_PROCESS * pr)405 int GWEN_Process_Terminate(GWEN_PROCESS *pr)
406 {
407   assert(pr);
408 
409   if (pr->state!=GWEN_ProcessStateRunning) {
410     /* process is not running, so return */
411     DBG_INFO(GWEN_LOGDOMAIN, "Process is not running, doing nothing");
412     return 0;
413   }
414 
415   if (pr->pid==-1) {
416     /* process is running, but we have no pid ?! */
417     DBG_ERROR(GWEN_LOGDOMAIN, "Process is running but we don't have its pid");
418     return -1;
419   }
420 
421   /* kill process */
422   if (kill(pr->pid, SIGKILL)) {
423     DBG_ERROR(GWEN_LOGDOMAIN, "Error on kill(%d, SIGKILL): %s",
424               (int)pr->pid, strerror(errno));
425     return -1;
426   }
427   /* wait for process to respond to kill signal (should not take long) */
428   return GWEN_Process_Wait(pr);
429 }
430 
431 
432 
GWEN_Process_GetFlags(const GWEN_PROCESS * pr)433 uint32_t GWEN_Process_GetFlags(const GWEN_PROCESS *pr)
434 {
435   assert(pr);
436   return pr->pflags;
437 }
438 
439 
440 
GWEN_Process_SetFlags(GWEN_PROCESS * pr,uint32_t f)441 void GWEN_Process_SetFlags(GWEN_PROCESS *pr, uint32_t f)
442 {
443   assert(pr);
444   pr->pflags=f;
445 }
446 
447 
448 
GWEN_Process_AddFlags(GWEN_PROCESS * pr,uint32_t f)449 void GWEN_Process_AddFlags(GWEN_PROCESS *pr, uint32_t f)
450 {
451   assert(pr);
452   pr->pflags|=f;
453 }
454 
455 
456 
GWEN_Process_SubFlags(GWEN_PROCESS * pr,uint32_t f)457 void GWEN_Process_SubFlags(GWEN_PROCESS *pr, uint32_t f)
458 {
459   assert(pr);
460   pr->pflags&=~f;
461 }
462 
463 
464 
GWEN_Process_GetStdin(const GWEN_PROCESS * pr)465 GWEN_SYNCIO *GWEN_Process_GetStdin(const GWEN_PROCESS *pr)
466 {
467   assert(pr);
468   return pr->stdIn;
469 }
470 
471 
472 
GWEN_Process_GetStdout(const GWEN_PROCESS * pr)473 GWEN_SYNCIO *GWEN_Process_GetStdout(const GWEN_PROCESS *pr)
474 {
475   assert(pr);
476   return pr->stdOut;
477 }
478 
479 
480 
GWEN_Process_GetStderr(const GWEN_PROCESS * pr)481 GWEN_SYNCIO *GWEN_Process_GetStderr(const GWEN_PROCESS *pr)
482 {
483   assert(pr);
484   return pr->stdErr;
485 }
486 
487 
488 
GWEN_Process_Redirect(GWEN_PROCESS * pr)489 int GWEN_Process_Redirect(GWEN_PROCESS *pr)
490 {
491   assert(pr);
492 
493   pr->filesStdin[0]=-1;
494   pr->filesStdout[0]=-1;
495   pr->filesStderr[0]=-1;
496 
497   if (pr->pflags & GWEN_PROCESS_FLAGS_REDIR_STDIN) {
498     int filedes[2];
499 
500     DBG_DEBUG(GWEN_LOGDOMAIN, "Redirecting stdin");
501     if (pipe(filedes)) {
502       DBG_ERROR(GWEN_LOGDOMAIN, "pipe(): %s", strerror(errno));
503       return -1;
504     }
505     pr->filesStdin[0]=filedes[1];
506     pr->filesStdin[1]=filedes[0];
507   }
508 
509   if (pr->pflags & GWEN_PROCESS_FLAGS_REDIR_STDOUT) {
510     int filedes[2];
511 
512     DBG_DEBUG(GWEN_LOGDOMAIN, "Redirecting stdout");
513     if (pipe(filedes)) {
514       DBG_ERROR(GWEN_LOGDOMAIN, "pipe(): %s", strerror(errno));
515       return -1;
516     }
517     pr->filesStdout[0]=filedes[0];
518     pr->filesStdout[1]=filedes[1];
519   }
520 
521   if (pr->pflags & GWEN_PROCESS_FLAGS_REDIR_STDERR) {
522     int filedes[2];
523 
524     DBG_DEBUG(GWEN_LOGDOMAIN, "Redirecting stderr");
525     if (pipe(filedes)) {
526       DBG_ERROR(GWEN_LOGDOMAIN, "pipe(): %s", strerror(errno));
527       return -1;
528     }
529     pr->filesStderr[0]=filedes[0];
530     pr->filesStderr[1]=filedes[1];
531   }
532 
533   return 0;
534 }
535 
536 
537 
538 
539 
540 
541 
542 
543 
544 
545 
546 
547 
548 
549 
550 
551 
552 
553 
554 
555 
556 
557 
558 
559 
560 
561 
562 
563 
564 
565