1 /* process.c
2  *
3  * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
4  * Copyright (C) 2008 Lars Karlitski (formerly Uebernickel) <lars@karlitski.net>
5  *
6  * This file is part of foomatic-rip.
7  *
8  * Foomatic-rip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Foomatic-rip is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 #include "foomaticrip.h"
25 #include "process.h"
26 #include <unistd.h>
27 #include "util.h"
28 #include <sys/wait.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <signal.h>
32 
33 int kidgeneration = 0;
34 
35 struct process {
36     char name[64];
37     pid_t pid;
38     int isgroup;
39 };
40 
41 #define MAX_CHILDS 4
42 struct process procs[MAX_CHILDS] = {
43     { "", -1, 0 },
44     { "", -1, 0 },
45     { "", -1, 0 },
46     { "", -1, 0 }
47 };
48 
add_process(const char * name,int pid,int isgroup)49 void add_process(const char *name, int pid, int isgroup)
50 {
51     int i;
52     for (i = 0; i < MAX_CHILDS; i++) {
53         if (procs[i].pid == -1) {
54             strlcpy(procs[i].name, name, 64);
55             procs[i].pid = pid;
56             procs[i].isgroup = isgroup;
57             return;
58         }
59     }
60     rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Didn't think there would be that many child processes... Exiting.\n");
61 }
62 
find_process(int pid)63 int find_process(int pid)
64 {
65     int i;
66     for (i = 0; i < MAX_CHILDS; i++)
67         if (procs[i].pid == pid)
68             return i;
69     return -1;
70 }
71 
clear_proc_list()72 void clear_proc_list()
73 {
74     int i;
75     for (i = 0; i < MAX_CHILDS; i++)
76         procs[i].pid = -1;
77 }
78 
kill_all_processes()79 void kill_all_processes()
80 {
81     int i;
82 
83     for (i = 0; i < MAX_CHILDS; i++) {
84         if (procs[i].pid == -1)
85             continue;
86         _log("Killing %s\n", procs[i].name);
87         kill(procs[i].isgroup ? -procs[i].pid : procs[i].pid, 15);
88         sleep(1 << (3 - kidgeneration));
89         kill(procs[i].isgroup ? -procs[i].pid : procs[i].pid, 9);
90     }
91     clear_proc_list();
92 }
93 
_start_process(const char * name,int (* proc_func)(FILE *,FILE *,void *),void * user_arg,FILE ** pipe_in,FILE ** pipe_out,int createprocessgroup)94 static pid_t _start_process(const char *name,
95                           int (*proc_func)(FILE *, FILE *, void *),
96                           void *user_arg, FILE **pipe_in, FILE **pipe_out,
97                           int createprocessgroup)
98 {
99     pid_t pid;
100     int pfdin[2], pfdout[2];
101     int ret;
102     FILE *in, *out;
103 
104     if (pipe_in)
105       if (pipe(pfdin) < 0)
106 	return -1;
107     if (pipe_out)
108       if (pipe(pfdout) < 0)
109 	return -1;
110 
111     _log("Starting process \"%s\" (generation %d)\n", name, kidgeneration +1);
112 
113     pid = fork();
114     if (pid < 0) {
115         _log("Could not fork for %s\n", name);
116         if (pipe_in) {
117             close(pfdin[0]);
118             close(pfdin[1]);
119         }
120         if (pipe_out) {
121             close(pfdout[0]);
122             close(pfdout[1]);
123         }
124         return -1;
125     }
126 
127     if (pid == 0) { /* Child */
128 
129         // Reset sigpipe behavior to default for all children
130         signal(SIGPIPE, SIG_DFL);
131 
132         if (pipe_in) {
133             close(pfdin[1]);
134             in = fdopen(pfdin[0], "r");
135         }
136         else
137             in = NULL;
138 
139         if (pipe_out) {
140             close(pfdout[0]);
141             out = fdopen(pfdout[1], "w");
142         }
143         else
144             out = NULL;
145 
146         if (createprocessgroup)
147             setpgid(0, 0);
148 
149         kidgeneration++;
150 
151         /* The subprocess list is only valid for the parent. Clear it. */
152         clear_proc_list();
153 
154         ret = proc_func(in, out, user_arg);
155         exit(ret);
156     }
157 
158     /* Parent */
159     if (pipe_in) {
160         close(pfdin[0]);
161         *pipe_in = fdopen(pfdin[1], "w");
162         if (!*pipe_in)
163             _log("fdopen: %s\n", strerror(errno));
164     }
165     if (pipe_out) {
166         close(pfdout[1]);
167         *pipe_out = fdopen(pfdout[0], "r");
168         if (!*pipe_out)
169             _log("fdopen: %s\n", strerror(errno));
170     }
171 
172     /* Add the child process to the list of open processes (to be able to kill
173      * them in case of a signal. */
174     add_process(name, pid, createprocessgroup);
175 
176     return pid;
177 }
178 
exec_command(FILE * in,FILE * out,void * cmd)179 int exec_command(FILE *in, FILE *out, void *cmd)
180 {
181     if (in && dup2(fileno(in), fileno(stdin)) < 0)
182         rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "%s: Could not dup stdin\n", (const char *)cmd);
183     if (out && dup2(fileno(out), fileno(stdout)) < 0)
184         rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "%s: Could not dup stdout\n", (const char *)cmd);
185 
186     execl(get_modern_shell(), get_modern_shell(), "-e", "-c", (const char *)cmd, (char *)NULL);
187 
188     _log("Error: Executing \"%s -c %s\" failed (%s).\n", get_modern_shell(), (const char *)cmd, strerror(errno));
189     return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
190 }
191 
start_system_process(const char * name,const char * command,FILE ** fdin,FILE ** fdout)192 pid_t start_system_process(const char *name, const char *command, FILE **fdin, FILE **fdout)
193 {
194     return _start_process(name, exec_command, (void*)command, fdin, fdout, 1);
195 }
196 
start_process(const char * name,int (* proc_func)(FILE *,FILE *,void *),void * user_arg,FILE ** fdin,FILE ** fdout)197 pid_t start_process(const char *name, int (*proc_func)(FILE *, FILE *, void *), void *user_arg, FILE **fdin, FILE **fdout)
198 {
199     return _start_process(name, proc_func, user_arg, fdin, fdout, 0);
200 }
201 
wait_for_process(int pid)202 int wait_for_process(int pid)
203 {
204     int i;
205     int status;
206 
207     i = find_process(pid);
208     if (i < 0) {
209         _log("No such process \"%d\"", pid);
210         return -1;
211     }
212 
213     waitpid(procs[i].pid, &status, 0);
214     if (WIFEXITED(status))
215         _log("%s exited with status %d\n", procs[i].name, WEXITSTATUS(status));
216     else if (WIFSIGNALED(status))
217         _log("%s received signal %d\n", procs[i].name, WTERMSIG(status));
218 
219     /* remove from process list */
220     procs[i].pid = -1;
221     return status;
222 }
223 
run_system_process(const char * name,const char * command)224 int run_system_process(const char *name, const char *command)
225 {
226     int pid = start_system_process(name, command, NULL, NULL);
227     return wait_for_process(pid);
228 }
229 
230