Check and call arc4random_stir() if present
[ngircd-alex.git] / src / ngircd / proc.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Process management
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <errno.h>
22 #include <signal.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26
27 #include "log.h"
28 #include "io.h"
29 #include "conn.h"
30
31 #include "exp.h"
32 #include "sighandlers.h"
33 #include "proc.h"
34
35 /**
36  * Initialize process structure.
37  */
38 GLOBAL void
39 Proc_InitStruct (PROC_STAT *proc)
40 {
41         assert(proc != NULL);
42         proc->pid = 0;
43         proc->pipe_fd = -1;
44 }
45
46 /**
47  * Fork a child process.
48  */
49 GLOBAL pid_t
50 Proc_Fork(PROC_STAT *proc, int *pipefds, void (*cbfunc)(int, short), int timeout)
51 {
52         pid_t pid;
53 #ifndef HAVE_ARC4RANDOM
54         unsigned int seed;
55 #endif
56
57         assert(proc != NULL);
58         assert(pipefds != NULL);
59         assert(cbfunc != NULL);
60
61         if (pipe(pipefds) != 0) {
62                 Log(LOG_ALERT, "Can't create output pipe for child process: %s!",
63                     strerror(errno));
64                 return -1;
65         }
66
67 #ifndef HAVE_ARC4RANDOM
68         seed = (unsigned int)rand();
69 #endif
70         pid = fork();
71         switch (pid) {
72         case -1:
73                 /* Error on fork: */
74                 Log(LOG_CRIT, "Can't fork child process: %s!", strerror(errno));
75                 close(pipefds[0]);
76                 close(pipefds[1]);
77                 return -1;
78         case 0:
79                 /* New child process: */
80 #ifdef HAVE_ARC4RANDOM_STIR
81                 arc4random_stir();
82 #endif
83 #ifndef HAVE_ARC4RANDOM
84                 srand(seed ^ (unsigned int)time(NULL) ^ getpid());
85 #endif
86                 Signals_Exit();
87                 signal(SIGTERM, Proc_GenericSignalHandler);
88                 signal(SIGALRM, Proc_GenericSignalHandler);
89                 close(pipefds[0]);
90                 alarm(timeout);
91                 return 0;
92         }
93
94         /* Old parent process: */
95         close(pipefds[1]);
96
97         if (!io_setnonblock(pipefds[0])
98          || !io_event_create(pipefds[0], IO_WANTREAD, cbfunc)) {
99                 Log(LOG_CRIT, "Can't register callback for child process: %s!",
100                     strerror(errno));
101                 close(pipefds[0]);
102                 return -1;
103         }
104
105         proc->pid = pid;
106         proc->pipe_fd = pipefds[0];
107         return pid;
108 }
109
110 /**
111  * Generic signal handler for forked child processes.
112  */
113 GLOBAL void
114 Proc_GenericSignalHandler(int Signal)
115 {
116         switch(Signal) {
117         case SIGTERM:
118 #ifdef DEBUG
119                 Log_Subprocess(LOG_DEBUG, "Child got TERM signal, exiting.");
120 #endif
121                 exit(1);
122         case SIGALRM:
123 #ifdef DEBUG
124                 Log_Subprocess(LOG_DEBUG, "Child got ALARM signal, exiting.");
125 #endif
126                 exit(1);
127         }
128 }
129
130 /**
131  * Read bytes from a pipe of a forked child process.
132  * In addition, this function makes sure that the child process is ignored
133  * after all data has been read or a fatal error occurred.
134  */
135 GLOBAL size_t
136 Proc_Read(PROC_STAT *proc, void *buffer, size_t buflen)
137 {
138         ssize_t bytes_read = 0;
139
140         assert(buffer != NULL);
141         assert(buflen > 0);
142
143         bytes_read = read(proc->pipe_fd, buffer, buflen);
144         if (bytes_read < 0) {
145                 if (errno == EAGAIN)
146                         return 0;
147                 Log(LOG_CRIT, "Can't read from child process %ld: %s",
148                     proc->pid, strerror(errno));
149                 Proc_Close(proc);
150                 bytes_read = 0;
151         } else if (bytes_read == 0) {
152                 /* EOF: clean up */
153                 LogDebug("Child process %ld: EOF reached, closing pipe.",
154                          proc->pid);
155                 Proc_Close(proc);
156         }
157         return (size_t)bytes_read;
158 }
159
160 /**
161  * Close pipe to a forked child process.
162  */
163 GLOBAL void
164 Proc_Close(PROC_STAT *proc)
165 {
166         /* Close socket, if it exists */
167         if (proc->pipe_fd >= 0)
168                 io_close(proc->pipe_fd);
169
170         Proc_InitStruct(proc);
171 }
172
173 /* -eof- */