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