1 /* process.c
2  *
3  * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
4  * Copyright (C) 2008 Lars Uebernickel <larsuebernickel@gmx.de>
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 
32 int kidgeneration = 0;
33 
34 struct process {
35     char name[64];
36     pid_t pid;
37     int isgroup;
38 };
39 
40 #define MAX_CHILDS 4
41 struct process procs[MAX_CHILDS] = {
42     { "", -1, 0 },
43     { "", -1, 0 },
44     { "", -1, 0 },
45     { "", -1, 0 }
46 };
47 
add_process(const char * name,int pid,int isgroup)48 void add_process(const char *name, int pid, int isgroup)
49 {
50     int i;
51     for (i = 0; i < MAX_CHILDS; i++) {
52         if (procs[i].pid == -1) {
53             strlcpy(procs[i].name, name, 64);
54             procs[i].pid = pid;
55             procs[i].isgroup = isgroup;
56             return;
57         }
58     }
59     rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "Didn't think there would be that many child processes... Exiting.\n");
60 }
61 
find_process(int pid)62 int find_process(int pid)
63 {
64     int i;
65     for (i = 0; i < MAX_CHILDS; i++)
66         if (procs[i].pid == pid)
67             return i;
68     return -1;
69 }
70 
clear_proc_list()71 void clear_proc_list()
72 {
73     int i;
74     for (i = 0; i < MAX_CHILDS; i++)
75         procs[i].pid = -1;
76 }
77 
kill_all_processes()78 void kill_all_processes()
79 {
80     int i;
81 
82     for (i = 0; i < MAX_CHILDS; i++) {
83         if (procs[i].pid == -1)
84             continue;
85         _log("Killing %s\n", procs[i].name);
86         kill(procs[i].isgroup ? -procs[i].pid : procs[i].pid, 15);
87         sleep(1 << (3 - kidgeneration));
88         kill(procs[i].isgroup ? -procs[i].pid : procs[i].pid, 9);
89     }
90     clear_proc_list();
91 }
92 
_start_process(const char * name,int (* proc_func)(FILE *,FILE *,void *),void * user_arg,FILE ** pipe_in,FILE ** pipe_out,int createprocessgroup)93 static int _start_process(const char *name,
94                           int (*proc_func)(FILE *, FILE *, void *),
95                           void *user_arg, FILE **pipe_in, FILE **pipe_out,
96                           int createprocessgroup)
97 {
98     pid_t pid;
99     int pfdin[2], pfdout[2];
100     int ret;
101     FILE *in, *out;
102 
103     if (pipe_in)
104         pipe(pfdin);
105     if (pipe_out)
106         pipe(pfdout);
107 
108     _log("Starting process \"%s\" (generation %d)\n", name, kidgeneration +1);
109 
110     pid = fork();
111     if (pid < 0) {
112         _log("Could not fork for %s\n", name);
113         if (pipe_in) {
114             close(pfdin[0]);
115             close(pfdin[1]);
116         }
117         if (pipe_out) {
118             close(pfdout[0]);
119             close(pfdout[1]);
120         }
121         return -1;
122     }
123 
124     if (pid == 0) { /* Child */
125         if (pipe_in) {
126             close(pfdin[1]);
127             in = fdopen(pfdin[0], "r");
128         }
129         else
130             in = NULL;
131 
132         if (pipe_out) {
133             close(pfdout[0]);
134             out = fdopen(pfdout[1], "w");
135         }
136         else
137             out = NULL;
138 
139         if (createprocessgroup)
140             setpgid(0, 0);
141 
142         kidgeneration++;
143 
144         /* The subprocess list is only valid for the parent. Clear it. */
145         clear_proc_list();
146 
147         ret = proc_func(in, out, user_arg);
148         exit(ret);
149     }
150 
151     /* Parent */
152     if (pipe_in) {
153         close(pfdin[0]);
154         *pipe_in = fdopen(pfdin[1], "w");
155         if (!*pipe_in)
156             _log("fdopen: %s\n", strerror(errno));
157     }
158     if (pipe_out) {
159         close(pfdout[1]);
160         *pipe_out = fdopen(pfdout[0], "r");
161         if (!*pipe_out)
162             _log("fdopen: %s\n", strerror(errno));
163     }
164 
165     /* Add the child process to the list of open processes (to be able to kill
166      * them in case of a signal. */
167     add_process(name, pid, createprocessgroup);
168 
169     return pid;
170 }
171 
exec_command(FILE * in,FILE * out,void * cmd)172 int exec_command(FILE *in, FILE *out, void *cmd)
173 {
174     if (in && dup2(fileno(in), fileno(stdin)) < 0)
175         rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "%s: Could not dup stdin\n", (const char *)cmd);
176     if (out && dup2(fileno(out), fileno(stdout)) < 0)
177         rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS, "%s: Could not dup stdout\n", (const char *)cmd);
178 
179     execl(get_modern_shell(), get_modern_shell(), "-c", (const char *)cmd, (char *)NULL);
180 
181     _log("Error: Executing \"%s -c %s\" failed (%s).\n", get_modern_shell(), (const char *)cmd, strerror(errno));
182     return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
183 }
184 
start_system_process(const char * name,const char * command,FILE ** fdin,FILE ** fdout)185 int start_system_process(const char *name, const char *command, FILE **fdin, FILE **fdout)
186 {
187     return _start_process(name, exec_command, (void*)command, fdin, fdout, 1);
188 }
189 
start_process(const char * name,int (* proc_func)(FILE *,FILE *,void *),void * user_arg,FILE ** fdin,FILE ** fdout)190 int start_process(const char *name, int (*proc_func)(FILE *, FILE *, void *), void *user_arg, FILE **fdin, FILE **fdout)
191 {
192     return _start_process(name, proc_func, user_arg, fdin, fdout, 0);
193 }
194 
wait_for_process(int pid)195 int wait_for_process(int pid)
196 {
197     int i;
198     int status;
199 
200     i = find_process(pid);
201     if (i < 0) {
202         _log("No such process \"%d\"", pid);
203         return -1;
204     }
205 
206     waitpid(procs[i].pid, &status, 0);
207     if (WIFEXITED(status))
208         _log("%s exited with status %d\n", procs[i].name, WEXITSTATUS(status));
209     else if (WIFSIGNALED(status))
210         _log("%s received signal %d\n", procs[i].name, WTERMSIG(status));
211 
212     /* remove from process list */
213     procs[i].pid = -1;
214     return status;
215 }
216 
run_system_process(const char * name,const char * command)217 int run_system_process(const char *name, const char *command)
218 {
219     int pid = start_system_process(name, command, NULL, NULL);
220     return wait_for_process(pid);
221 }
222 
223