]> git.itanic.dy.fi Git - rrdd/blob - process.c
Remove redundant strerror usage
[rrdd] / process.c
1 #include <unistd.h>
2 #include <sys/select.h>
3
4 #include "process.h"
5 #include "debug.h"
6
7 static int child_count = 0;
8 static int parent_count = 0;
9 static int process_count = 0;
10
11 int get_child_count(void)
12 {
13         return child_count;
14 }
15
16 int get_parent_count(void)
17 {
18         return parent_count;
19 }
20
21 int get_sibling_count(void)
22 {
23         return process_count;
24 }
25
26 int do_fork(void)
27 {
28         int child;
29         child = fork();
30         if (child < 0) {
31                 pr_err("fork() failed: %m\n");
32                 return -1;
33         }
34
35         if (child) {
36                 child_count++;
37                 pr_info("Fork %d, child %d\n", child_count, child);
38                 return child;
39         }
40
41         process_count = child_count;
42         /* reset child's child count */
43         child_count = 0;
44         parent_count++;
45         return 0;
46 }
47
48 int harvest_zombies(int pid)
49 {
50         int status;
51
52         if (child_count == 0)
53                 return 0;
54
55         if (pid)
56                 pr_info("Waiting on pid %d, children left: %d\n", pid, 
57                         child_count);
58
59         pid = waitpid(pid, &status, 0);
60         if (pid < 0) {
61                 pr_err("Error on wait(): %m\n");
62                 child_count--; /* Decrement child count anyway */
63         }
64         else {
65                 child_count--;
66                 pr_info("pid %d: exit code %d. Children left: %d\n", pid,
67                         status, child_count);
68         }
69
70         return 1;
71 }
72
73 /*
74  * Runs a command cmd with params argv, connects stdin and stdout to
75  * readfd and writefd
76  *
77  * Returns the pid of the executed process
78  */
79 int run_piped(const char *cmd, char *const argv[],
80               int *stdinfd, int *stdoutfd, int *stderrfd)
81 {
82         int ifd[2], ofd[2], efd[2], pid;
83
84         pr_info("Running command %s\n", cmd);
85
86         if (stdinfd && pipe(ifd)) {
87                 pr_err("pipe() failed: %m\n");
88                 return -1;
89         }
90
91         if (stdoutfd && pipe(ofd)) {
92                 pr_err("pipe() failed: %m\n");
93                 return -1;
94         }
95
96         if (stderrfd && pipe(efd)) {
97                 pr_err("pipe() failed: %m\n");
98                 return -1;
99         }
100
101         pid = do_fork();
102         if (pid) { /* Parent side */
103                 if (stdinfd) {
104                         close(ifd[0]);
105                         *stdinfd = ifd[0];
106                 }
107
108                 if (stdoutfd) {
109                         close(ofd[1]);
110                         *stdoutfd = ofd[0];
111                 }
112
113                 if (stderrfd) {
114                         close(efd[1]);
115                         *stderrfd = efd[0];
116                 }
117
118                 return pid;
119         }
120
121         if (stdinfd) {
122                 close(ifd[1]);
123                 dup2(ifd[0], STDIN_FILENO);
124         }
125
126         if (stdoutfd) {
127                 close(ofd[0]);
128                 dup2(ofd[1], STDOUT_FILENO);
129         }
130
131         if (stderrfd) {
132                 close(efd[0]);
133                 dup2(efd[1], STDERR_FILENO);
134         }
135
136         /* Now we have redirected standard streams to parent process */
137         execvp(cmd, argv);
138         pr_err("Failed to execv command %s: %m\n", cmd);
139         exit(1);
140
141         return 0;
142 }
143
144 /*
145  * Runs a command cmd with params argv, connects stdin and stdout to
146  * readfd and writefd
147  *
148  * Returns the pid of the executed process
149  */
150 int run_piped_stream(const char *cmd, char *const argv[],
151                      FILE **stdinf, FILE **stdoutf, FILE **stderrf)
152 {
153         int ifd, ofd, efd, pid;
154         int *i, *o, *e;
155
156         if (stdinf)
157                 i = &ifd;
158         else 
159                 i = 0;
160         if (stdoutf)
161                 o = &ofd;
162         else 
163                 o = 0;
164         if (stderrf)
165                 e = &efd;
166         else 
167                 e = 0;
168
169         pid = run_piped(cmd, argv, i, o, e);
170
171         if (stdinf) {
172                 *stdinf = fdopen(ifd, "r");
173                 if (*stdinf == NULL) {
174                         pr_err("Error opening file stream for fd %d: %m\n",
175                                ifd);
176                         return -1;
177                 }
178         }
179
180         if (stdoutf) {
181                 *stdoutf = fdopen(ofd, "r");
182                 if (*stdoutf == NULL) {
183                         pr_err("Error opening file stream for fd %d: %m\n",
184                                ofd);
185                         return -1;
186                 }
187         }
188
189         if (stderrf) {
190                 *stderrf = fdopen(efd, "r");
191                 if (*stderrf == NULL) {
192                         pr_err("Error opening file stream for fd %d: %m\n",
193                                efd);
194                         return -1;
195                 }
196         }
197
198         return pid;
199 }
200
201 /*
202  * Forks a child and executes a command to run on parallel
203  */
204
205 #define max(a,b) (a) < (b) ? (b) : (a)
206 #define BUF_SIZE (128*1024)
207 int run(const char *cmd, char *const argv[])
208 {
209         int child, error;
210         int ofd, efd;
211         fd_set rfds;
212         int maxfd;
213         int eof = 0;
214         char stdoutstr[32], stderrstr[32], indent[16] = { "                " };
215
216         indent[get_parent_count() + 1] = 0;
217                 
218         if ((child = do_fork()))
219             return child;
220
221         child = run_piped(cmd, argv, NULL, &ofd, &efd);
222         snprintf(stdoutstr, 32, "%sstdout", green_color);
223         snprintf(stderrstr, 32, "%sstderr", red_color);
224
225         FD_ZERO(&rfds);
226         FD_SET(ofd, &rfds);
227         FD_SET(efd, &rfds);
228
229         while (!eof) {
230                 char *sptr , *eptr;
231                 char rbuf[BUF_SIZE];
232                 int bytes;
233                 char *typestr;
234
235                 maxfd = max(ofd, efd);
236                 error = select(maxfd, &rfds, NULL, NULL, NULL);
237
238                 if (error < 0) {
239                         printf("Error with select: %m\n");
240                         break;
241                 }
242
243                 if (FD_ISSET(ofd, &rfds)) {
244                         typestr = stdoutstr;
245                         bytes = read(ofd, rbuf, BUF_SIZE);
246                         goto print;
247                 }
248
249                 if (FD_ISSET(efd, &rfds)) {
250                         typestr = stderrstr;
251                         bytes = read(efd, rbuf, BUF_SIZE);
252                         goto print;
253                 }
254
255                 pr_err("select() returned unknown fd\n");
256                 break;
257
258 print:
259                 if (bytes < 0) {
260                         pr_err("read() failed: %m\n");
261                         break;
262                 }
263
264                 /*
265                  * Workaround: When a process had die and it has only
266                  * written to stderr, select() doesn't indicate that
267                  * there might be something to read in stderr fd. To
268                  * work around this issue, we try to read stderr just
269                  * in case in order to ensure everything gets read.
270                  */
271                 if (bytes == 0) {
272                         bytes = read(efd, rbuf, BUF_SIZE);
273                         typestr = stderrstr;
274                         eof = 1;
275                 }
276
277                 sptr = eptr = rbuf;
278                 while(bytes--) {
279                         if (*eptr == '\n') {
280                                 *eptr = 0;
281                                 printf("%s[%5d %s] %s: %s%s\n", indent,
282                                        child, cmd, typestr, sptr, normal_color);
283                                 sptr = eptr;
284                         }
285                         eptr++;
286                 }
287         }
288
289         close(ofd);
290         close(efd);
291
292         harvest_zombies(child); 
293
294         exit(1);
295         return 0;
296 }