source: unix.c @ abf4717

Last change on this file since abf4717 was 2e99039, checked in by dequis <dx@…>, at 2015-10-30T10:31:09Z

Avoid propagating shutdown signal to all subprocesses

This was a sort-of-regression with 7233f68

While this behavior might seem desirable in some cases, multi-user
installs like public servers would rather not kill children while
upgrading.

Turns out that pipes are inherited by forks, and writing in one side
means there might be more than one listener that calls
bitlbee_shutdown(). If the parent gets it, the children will get it
too. If a child gets it, the parent and the other children get it too.

This adds a sighandler_shutdown_setup() function that closes any
previously existing pipes and disconnects the events from them, to
create a new one. This is called again after forking each child process.

While I'm sure this fixes the issue, I still don't understand why it
*didn't* kill the forked processes in some cases. Worrying.

  • Property mode set to 100644
File size: 8.9 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Main file (Unix specific part)                                       */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#include "bitlbee.h"
27
28#include "arc.h"
29#include "base64.h"
30#include "commands.h"
31#include "protocols/nogaim.h"
32#include "help.h"
33#include "ipc.h"
34#include "md5.h"
35#include "misc.h"
36#include <signal.h>
37#include <unistd.h>
38#include <sys/time.h>
39#include <sys/wait.h>
40#include <pwd.h>
41#include <locale.h>
42#include <grp.h>
43
44#if defined(OTR_BI) || defined(OTR_PI)
45#include "otr.h"
46#endif
47
48global_t global;        /* Against global namespace pollution */
49
50static struct {
51        int fd[2];
52        int tag;
53} shutdown_pipe = {{-1 , -1}, 0};
54
55static void sighandler_shutdown(int signal);
56static void sighandler_crash(int signal);
57
58static int crypt_main(int argc, char *argv[]);
59
60int main(int argc, char *argv[])
61{
62        int i = 0;
63        char *old_cwd = NULL;
64        struct sigaction sig, old;
65
66        /* Required to make iconv to ASCII//TRANSLIT work. This makes BitlBee
67           system-locale-sensitive. :-( */
68        setlocale(LC_CTYPE, "");
69
70        if (argc > 1 && strcmp(argv[1], "-x") == 0) {
71                return crypt_main(argc, argv);
72        }
73
74        log_init();
75
76        global.conf_file = g_strdup(CONF_FILE_DEF);
77        global.conf = conf_load(argc, argv);
78        if (global.conf == NULL) {
79                return(1);
80        }
81
82        b_main_init();
83
84        /* libpurple doesn't like fork()s after initializing itself, so if
85           we use it, do this init a little later (in case we're running in
86           ForkDaemon mode). */
87#ifndef WITH_PURPLE
88        nogaim_init();
89#endif
90
91#ifdef OTR_BI
92        otr_init();
93#endif
94
95        global.helpfile = g_strdup(HELP_FILE);
96        if (help_init(&global.help, global.helpfile) == NULL) {
97                log_message(LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE);
98        }
99
100        global.storage = storage_init(global.conf->primary_storage, global.conf->migrate_storage);
101        if (global.storage == NULL) {
102                log_message(LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage);
103                return(1);
104        }
105
106        if (global.conf->runmode == RUNMODE_INETD) {
107                log_link(LOGLVL_ERROR, LOGOUTPUT_IRC);
108                log_link(LOGLVL_WARNING, LOGOUTPUT_IRC);
109
110                i = bitlbee_inetd_init();
111                log_message(LOGLVL_INFO, "%s %s starting in inetd mode.", PACKAGE, BITLBEE_VERSION);
112
113        } else if (global.conf->runmode == RUNMODE_DAEMON) {
114                log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
115                log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
116
117                i = bitlbee_daemon_init();
118                log_message(LOGLVL_INFO, "%s %s starting in daemon mode.", PACKAGE, BITLBEE_VERSION);
119        } else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
120                log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
121                log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
122
123                /* In case the operator requests a restart, we need this. */
124                old_cwd = g_malloc(256);
125                if (getcwd(old_cwd, 255) == NULL) {
126                        log_message(LOGLVL_WARNING, "Could not save current directory: %s", strerror(errno));
127                        g_free(old_cwd);
128                        old_cwd = NULL;
129                }
130
131                i = bitlbee_daemon_init();
132                log_message(LOGLVL_INFO, "%s %s starting in forking daemon mode.", PACKAGE, BITLBEE_VERSION);
133        }
134        if (i != 0) {
135                return(i);
136        }
137
138        if ((global.conf->user && *global.conf->user) &&
139            (global.conf->runmode == RUNMODE_DAEMON ||
140             global.conf->runmode == RUNMODE_FORKDAEMON) &&
141            (!getuid() || !geteuid())) {
142                struct passwd *pw = NULL;
143                pw = getpwnam(global.conf->user);
144                if (pw) {
145                        initgroups(global.conf->user, pw->pw_gid);
146                        setgid(pw->pw_gid);
147                        setuid(pw->pw_uid);
148                } else {
149                        log_message(LOGLVL_WARNING, "Failed to look up user %s.", global.conf->user);
150                }
151        }
152
153        /* Catch some signals to tell the user what's happening before quitting */
154        memset(&sig, 0, sizeof(sig));
155        sig.sa_handler = SIG_IGN;
156        sigaction(SIGCHLD, &sig, &old);
157        sigaction(SIGPIPE, &sig, &old);
158        sig.sa_flags = SA_RESETHAND;
159        sig.sa_handler = sighandler_crash;
160        sigaction(SIGSEGV, &sig, &old);
161
162        sighandler_shutdown_setup();
163
164        sig.sa_handler = sighandler_shutdown;
165        sigaction(SIGINT, &sig, &old);
166        sigaction(SIGTERM, &sig, &old);
167
168        if (!getuid() || !geteuid()) {
169                log_message(LOGLVL_WARNING, "BitlBee is running with root privileges. Why?");
170        }
171
172        b_main_run();
173
174        /* Mainly good for restarting, to make sure we close the help.txt fd. */
175        help_free(&global.help);
176
177        if (global.restart) {
178                char *fn = ipc_master_save_state();
179                char *env;
180
181                env = g_strdup_printf("_BITLBEE_RESTART_STATE=%s", fn);
182                putenv(env);
183                g_free(fn);
184                /* Looks like env should *not* be freed here as putenv
185                   doesn't make a copy. Odd. */
186
187                i = chdir(old_cwd);
188                close(global.listen_socket);
189
190                if (execv(argv[0], argv) == -1) {
191                        /* Apparently the execve() failed, so let's just
192                           jump back into our own/current main(). */
193                        /* Need more cleanup code to make this work. */
194                        return 1; /* main( argc, argv ); */
195                }
196        }
197
198        return(0);
199}
200
201static int crypt_main(int argc, char *argv[])
202{
203        int pass_len;
204        unsigned char *pass_cr, *pass_cl;
205
206        if (argc < 4 || (strcmp(argv[2], "hash") != 0 &&
207                         strcmp(argv[2], "unhash") != 0 && argc < 5)) {
208                printf("Supported:\n"
209                       "  %s -x enc <key> <cleartext password>\n"
210                       "  %s -x dec <key> <encrypted password>\n"
211                       "  %s -x hash <cleartext password>\n"
212                       "  %s -x unhash <hashed password>\n"
213                       "  %s -x chkhash <hashed password> <cleartext password>\n",
214                       argv[0], argv[0], argv[0], argv[0], argv[0]);
215        } else if (strcmp(argv[2], "enc") == 0) {
216                char *encoded;
217
218                pass_len = arc_encode(argv[4], strlen(argv[4]), &pass_cr, argv[3], 12);
219
220                encoded = base64_encode(pass_cr, pass_len);
221                printf("%s\n", encoded);
222                g_free(encoded);
223                g_free(pass_cr);
224        } else if (strcmp(argv[2], "dec") == 0) {
225                pass_len = base64_decode(argv[4], &pass_cr);
226                arc_decode(pass_cr, pass_len, (char **) &pass_cl, argv[3]);
227                printf("%s\n", pass_cl);
228
229                g_free(pass_cr);
230                g_free(pass_cl);
231        } else if (strcmp(argv[2], "hash") == 0) {
232                md5_byte_t pass_md5[21];
233                md5_state_t md5_state;
234                char *encoded;
235
236                random_bytes(pass_md5 + 16, 5);
237                md5_init(&md5_state);
238                md5_append(&md5_state, (md5_byte_t *) argv[3], strlen(argv[3]));
239                md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
240                md5_finish(&md5_state, pass_md5);
241
242                encoded = base64_encode(pass_md5, 21);
243                printf("%s\n", encoded);
244                g_free(encoded);
245        } else if (strcmp(argv[2], "unhash") == 0) {
246                printf("Hash %s submitted to a massive Beowulf cluster of\n"
247                       "overclocked 486s. Expect your answer next year somewhere around this time. :-)\n", argv[3]);
248        } else if (strcmp(argv[2], "chkhash") == 0) {
249                char *hash = strncmp(argv[3], "md5:", 4) == 0 ? argv[3] + 4 : argv[3];
250                int st = md5_verify_password(argv[4], hash);
251
252                printf("Hash %s given password.\n", st == 0 ? "matches" : "does not match");
253
254                return st;
255        }
256
257        return 0;
258}
259
260/* Set up a pipe for SIGTERM/SIGINT so the actual signal handler doesn't do anything unsafe */
261void sighandler_shutdown_setup()
262{
263        if (shutdown_pipe.fd[0] != -1) {
264                /* called again from a forked process, clean up to avoid propagating the signal */
265                b_event_remove(shutdown_pipe.tag);
266                close(shutdown_pipe.fd[0]);
267                close(shutdown_pipe.fd[1]);
268        }
269
270        if (pipe(shutdown_pipe.fd) == 0) {
271                shutdown_pipe.tag = b_input_add(shutdown_pipe.fd[0], B_EV_IO_READ, bitlbee_shutdown, NULL);
272        }
273}
274
275/* Signal handler for SIGTERM and SIGINT */
276static void sighandler_shutdown(int signal)
277{
278        /* Write a single null byte to the pipe, just to send a message to the main loop.
279         * This gets handled by bitlbee_shutdown (the b_input_add callback for this pipe) */
280        write(shutdown_pipe.fd[1], "", 1);
281}
282
283/* Signal handler for SIGSEGV
284 * A desperate attempt to tell the user that everything is wrong in the world.
285 * Avoids using irc_abort() because it has several unsafe calls to malloc */
286static void sighandler_crash(int signal)
287{
288        GSList *l;
289        const char *message = "ERROR :BitlBee crashed! (SIGSEGV received)\r\n";
290        int len = strlen(message);
291
292        for (l = irc_connection_list; l; l = l->next) {
293                irc_t *irc = l->data;
294                write(irc->fd, message, len);
295        }
296
297        raise(signal);
298}
299
300double gettime()
301{
302        struct timeval time[1];
303
304        gettimeofday(time, 0);
305        return((double) time->tv_sec + (double) time->tv_usec / 1000000);
306}
Note: See TracBrowser for help on using the repository browser.