source: ipc.c @ 57f81ec

Last change on this file since 57f81ec was 129e282, checked in by dequis <dx@…>, at 2015-10-30T10:31:09Z

ipc: Fix strict aliasing warnings

Turned out to be as simple as just using memcpy.

As with all strict aliasing warnings, these only appeared when
compiling with optimization, and might have caused Bad Things™

This page is cool:

http://dbp-consulting.com/tutorials/StrictAliasing.html

  • Property mode set to 100644
File size: 23.8 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/* IPC - communication between BitlBee processes                        */
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#define BITLBEE_CORE
27#include "bitlbee.h"
28#include "ipc.h"
29#include "commands.h"
30#include <sys/uio.h>
31#include <sys/un.h>
32
33GSList *child_list = NULL;
34static int ipc_child_recv_fd = -1;
35
36static void ipc_master_takeover_fail(struct bitlbee_child *child, gboolean both);
37static gboolean ipc_send_fd(int fd, int send_fd);
38
39/* On Solaris and possibly other systems passing FDs between processes is
40 * not possible (or at least not using the method used in this file.
41 * Just disable that code, the functionality is not that important. */
42#if defined(NO_FD_PASSING) && !defined(CMSG_SPACE)
43#define CMSG_SPACE(len) 1
44#endif
45
46static void ipc_master_cmd_client(irc_t *data, char **cmd)
47{
48        /* Normally data points at an irc_t block, but for the IPC master
49           this is different. We think this scary cast is better than
50           creating a new command_t structure, just to make the compiler
51           happy. */
52        struct bitlbee_child *child = (void *) data;
53
54        if (child && cmd[1]) {
55                child->host = g_strdup(cmd[1]);
56                child->nick = g_strdup(cmd[2]);
57                child->realname = g_strdup(cmd[3]);
58        }
59
60        /* CLIENT == On initial connects, HELLO is after /RESTARTs. */
61        if (g_strcasecmp(cmd[0], "CLIENT") == 0) {
62                ipc_to_children_str("OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
63                                    (int) (child ? child->pid : -1), cmd[2], cmd[1], cmd[3]);
64        }
65}
66
67static void ipc_master_cmd_nick(irc_t *data, char **cmd)
68{
69        struct bitlbee_child *child = (void *) data;
70
71        if (child && cmd[1]) {
72                g_free(child->nick);
73                child->nick = g_strdup(cmd[1]);
74        }
75}
76
77static void ipc_master_cmd_die(irc_t *data, char **cmd)
78{
79        if (global.conf->runmode == RUNMODE_FORKDAEMON) {
80                ipc_to_children_str("DIE\r\n");
81        }
82
83        bitlbee_shutdown(NULL, -1, 0);
84}
85
86static void ipc_master_cmd_deaf(irc_t *data, char **cmd)
87{
88        if (global.conf->runmode == RUNMODE_DAEMON) {
89                b_event_remove(global.listen_watch_source_id);
90                close(global.listen_socket);
91
92                global.listen_socket = global.listen_watch_source_id = -1;
93
94                ipc_to_children_str("OPERMSG :Closed listening socket, waiting "
95                                    "for all users to disconnect.");
96        } else {
97                ipc_to_children_str("OPERMSG :The DEAF command only works in "
98                                    "normal daemon mode. Try DIE instead.");
99        }
100}
101
102void ipc_master_cmd_rehash(irc_t *data, char **cmd)
103{
104        runmode_t oldmode;
105
106        oldmode = global.conf->runmode;
107
108        g_free(global.conf);
109        global.conf = conf_load(0, NULL);
110
111        if (global.conf->runmode != oldmode) {
112                log_message(LOGLVL_WARNING, "Can't change RunMode setting at runtime, restoring original setting");
113                global.conf->runmode = oldmode;
114        }
115
116        if (global.conf->runmode == RUNMODE_FORKDAEMON) {
117                ipc_to_children(cmd);
118        }
119}
120
121void ipc_master_cmd_restart(irc_t *data, char **cmd)
122{
123        if (global.conf->runmode != RUNMODE_FORKDAEMON) {
124                /* Tell child that this is unsupported. */
125                return;
126        }
127
128        global.restart = -1;
129        bitlbee_shutdown(NULL, -1, 0);
130}
131
132void ipc_master_cmd_identify(irc_t *data, char **cmd)
133{
134        struct bitlbee_child *child = (void *) data, *old = NULL;
135        char *resp;
136        GSList *l;
137
138        if (!child || !child->nick || strcmp(child->nick, cmd[1]) != 0) {
139                return;
140        }
141
142        g_free(child->password);
143        child->password = g_strdup(cmd[2]);
144
145        for (l = child_list; l; l = l->next) {
146                old = l->data;
147                if (child != old &&
148                    old->nick && nick_cmp(NULL, old->nick, child->nick) == 0 &&
149                    old->password && strcmp(old->password, child->password) == 0) {
150                        break;
151                }
152        }
153
154        if (l && !child->to_child && !old->to_child) {
155                resp = "TAKEOVER INIT\r\n";
156                child->to_child = old;
157                old->to_child = child;
158        } else {
159                /* Won't need the fd since we can't send it anywhere. */
160                closesocket(child->to_fd);
161                child->to_fd = -1;
162                resp = "TAKEOVER NO\r\n";
163        }
164
165        if (write(child->ipc_fd, resp, strlen(resp)) != strlen(resp)) {
166                ipc_master_free_one(child);
167        }
168}
169
170
171void ipc_master_cmd_takeover(irc_t *data, char **cmd)
172{
173        struct bitlbee_child *child = (void *) data;
174        char *fwd = NULL;
175
176        /* Normal daemon mode doesn't keep these and has simplified code for
177           takeovers. */
178        if (child == NULL) {
179                return;
180        }
181
182        if (child->to_child == NULL ||
183            g_slist_find(child_list, child->to_child) == NULL) {
184                return ipc_master_takeover_fail(child, FALSE);
185        }
186
187        if (strcmp(cmd[1], "AUTH") == 0) {
188                /* New connection -> Master */
189                if (child->to_child &&
190                    child->nick && child->to_child->nick && cmd[2] &&
191                    child->password && child->to_child->password && cmd[3] &&
192                    strcmp(child->nick, child->to_child->nick) == 0 &&
193                    strcmp(child->nick, cmd[2]) == 0 &&
194                    strcmp(child->password, child->to_child->password) == 0 &&
195                    strcmp(child->password, cmd[3]) == 0) {
196                        ipc_send_fd(child->to_child->ipc_fd, child->to_fd);
197
198                        fwd = irc_build_line(cmd);
199                        if (write(child->to_child->ipc_fd, fwd, strlen(fwd)) != strlen(fwd)) {
200                                ipc_master_free_one(child);
201                        }
202                        g_free(fwd);
203                } else {
204                        return ipc_master_takeover_fail(child, TRUE);
205                }
206        } else if (strcmp(cmd[1], "DONE") == 0 || strcmp(cmd[1], "FAIL") == 0) {
207                /* Old connection -> Master */
208                int fd;
209
210                /* The copy was successful (or not), we don't need it anymore. */
211                closesocket(child->to_fd);
212                child->to_fd = -1;
213
214                /* Pass it through to the other party, and flush all state. */
215                fwd = irc_build_line(cmd);
216                fd = child->to_child->ipc_fd;
217                child->to_child->to_child = NULL;
218                child->to_child = NULL;
219                if (write(fd, fwd, strlen(fwd)) != strlen(fwd)) {
220                        ipc_master_free_one(child);
221                }
222                g_free(fwd);
223        }
224}
225
226static const command_t ipc_master_commands[] = {
227        { "client",     3, ipc_master_cmd_client,     0 },
228        { "hello",      0, ipc_master_cmd_client,     0 },
229        { "nick",       1, ipc_master_cmd_nick,       0 },
230        { "die",        0, ipc_master_cmd_die,        0 },
231        { "deaf",       0, ipc_master_cmd_deaf,       0 },
232        { "wallops",    1, NULL,                      IPC_CMD_TO_CHILDREN },
233        { "wall",       1, NULL,                      IPC_CMD_TO_CHILDREN },
234        { "opermsg",    1, NULL,                      IPC_CMD_TO_CHILDREN },
235        { "rehash",     0, ipc_master_cmd_rehash,     0 },
236        { "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
237        { "restart",    0, ipc_master_cmd_restart,    0 },
238        { "identify",   2, ipc_master_cmd_identify,   0 },
239        { "takeover",   1, ipc_master_cmd_takeover,   0 },
240        { NULL }
241};
242
243
244static void ipc_child_cmd_die(irc_t *irc, char **cmd)
245{
246        irc_abort(irc, 0, "Shutdown requested by operator");
247}
248
249static void ipc_child_cmd_wallops(irc_t *irc, char **cmd)
250{
251        if (!(irc->status & USTATUS_LOGGED_IN)) {
252                return;
253        }
254
255        if (strchr(irc->umode, 'w')) {
256                irc_write(irc, ":%s WALLOPS :%s", irc->root->host, cmd[1]);
257        }
258}
259
260static void ipc_child_cmd_wall(irc_t *irc, char **cmd)
261{
262        if (!(irc->status & USTATUS_LOGGED_IN)) {
263                return;
264        }
265
266        if (strchr(irc->umode, 's')) {
267                irc_write(irc, ":%s NOTICE %s :%s", irc->root->host, irc->user->nick, cmd[1]);
268        }
269}
270
271static void ipc_child_cmd_opermsg(irc_t *irc, char **cmd)
272{
273        if (!(irc->status & USTATUS_LOGGED_IN)) {
274                return;
275        }
276
277        if (strchr(irc->umode, 'o')) {
278                irc_write(irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->root->host, irc->user->nick, cmd[1]);
279        }
280}
281
282static void ipc_child_cmd_rehash(irc_t *irc, char **cmd)
283{
284        runmode_t oldmode;
285
286        oldmode = global.conf->runmode;
287
288        g_free(global.conf);
289        global.conf = conf_load(0, NULL);
290
291        global.conf->runmode = oldmode;
292}
293
294static void ipc_child_cmd_kill(irc_t *irc, char **cmd)
295{
296        if (!(irc->status & USTATUS_LOGGED_IN)) {
297                return;
298        }
299
300        if (nick_cmp(NULL, cmd[1], irc->user->nick) != 0) {
301                return;         /* It's not for us. */
302
303        }
304        irc_write(irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick,
305                  cmd[2]);
306        irc_abort(irc, 0, "Killed by operator: %s", cmd[2]);
307}
308
309static void ipc_child_cmd_hello(irc_t *irc, char **cmd)
310{
311        if (!(irc->status & USTATUS_LOGGED_IN)) {
312                ipc_to_master_str("HELLO\r\n");
313        } else {
314                ipc_to_master_str("HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname);
315        }
316}
317
318static void ipc_child_cmd_takeover_yes(void *data);
319static void ipc_child_cmd_takeover_no(void *data);
320
321static void ipc_child_cmd_takeover(irc_t *irc, char **cmd)
322{
323        if (strcmp(cmd[1], "NO") == 0) {
324                /* Master->New connection */
325                /* No takeover, finish the login. */
326        } else if (strcmp(cmd[1], "INIT") == 0) {
327                /* Master->New connection */
328                if (!set_getbool(&irc->b->set, "allow_takeover")) {
329                        ipc_child_cmd_takeover_no(irc);
330                        return;
331                }
332
333                /* Offer to take over the old session, unless for some reason
334                   we're already logging into IM connections. */
335                if (irc->login_source_id != -1) {
336                        query_add(irc, NULL,
337                                  "You're already connected to this server. "
338                                  "Would you like to take over this session?",
339                                  ipc_child_cmd_takeover_yes,
340                                  ipc_child_cmd_takeover_no, NULL, irc);
341                }
342
343                /* This one's going to connect to accounts, avoid that. */
344                b_event_remove(irc->login_source_id);
345                irc->login_source_id = -1;
346        } else if (strcmp(cmd[1], "AUTH") == 0) {
347                /* Master->Old connection */
348                if (irc->password && cmd[2] && cmd[3] &&
349                    ipc_child_recv_fd != -1 &&
350                    strcmp(irc->user->nick, cmd[2]) == 0 &&
351                    strcmp(irc->password, cmd[3]) == 0 &&
352                    set_getbool(&irc->b->set, "allow_takeover")) {
353                        irc_switch_fd(irc, ipc_child_recv_fd);
354                        irc_sync(irc);
355                        irc_rootmsg(irc, "You've successfully taken over your old session");
356                        ipc_child_recv_fd = -1;
357
358                        ipc_to_master_str("TAKEOVER DONE\r\n");
359                } else {
360                        ipc_to_master_str("TAKEOVER FAIL\r\n");
361                }
362        } else if (strcmp(cmd[1], "DONE") == 0) {
363                /* Master->New connection (now taken over by old process) */
364                irc_free(irc);
365        } else if (strcmp(cmd[1], "FAIL") == 0) {
366                /* Master->New connection */
367                irc_rootmsg(irc, "Could not take over old session");
368        }
369}
370
371static void ipc_child_cmd_takeover_yes(void *data)
372{
373        irc_t *irc = data, *old = NULL;
374        char *to_auth[] = { "TAKEOVER", "AUTH", irc->user->nick, irc->password, NULL };
375
376        /* Master->New connection */
377        ipc_to_master_str("TAKEOVER AUTH %s :%s\r\n",
378                          irc->user->nick, irc->password);
379
380        if (global.conf->runmode == RUNMODE_DAEMON) {
381                GSList *l;
382
383                for (l = irc_connection_list; l; l = l->next) {
384                        old = l->data;
385
386                        if (irc != old &&
387                            irc->user->nick && old->user->nick &&
388                            irc->password && old->password &&
389                            strcmp(irc->user->nick, old->user->nick) == 0 &&
390                            strcmp(irc->password, old->password) == 0) {
391                                break;
392                        }
393                }
394                if (l == NULL) {
395                        to_auth[1] = "FAIL";
396                        ipc_child_cmd_takeover(irc, to_auth);
397                        return;
398                }
399        }
400
401        /* Drop credentials, we'll shut down soon and shouldn't overwrite
402           any settings. */
403        irc_rootmsg(irc, "Trying to take over existing session");
404
405        irc_desync(irc);
406
407        if (old) {
408                ipc_child_recv_fd = dup(irc->fd);
409                ipc_child_cmd_takeover(old, to_auth);
410        }
411
412        /* TODO: irc_setpass() should do all of this. */
413        irc_setpass(irc, NULL);
414        irc->status &= ~USTATUS_IDENTIFIED;
415        irc_umode_set(irc, "-R", 1);
416
417        if (old) {
418                irc_abort(irc, FALSE, NULL);
419        }
420}
421
422static void ipc_child_cmd_takeover_no(void *data)
423{
424        ipc_to_master_str("TAKEOVER NO\r\n");
425        cmd_identify_finish(data, 0, 0);
426}
427
428static const command_t ipc_child_commands[] = {
429        { "die",        0, ipc_child_cmd_die,         0 },
430        { "wallops",    1, ipc_child_cmd_wallops,     0 },
431        { "wall",       1, ipc_child_cmd_wall,        0 },
432        { "opermsg",    1, ipc_child_cmd_opermsg,     0 },
433        { "rehash",     0, ipc_child_cmd_rehash,      0 },
434        { "kill",       2, ipc_child_cmd_kill,        0 },
435        { "hello",      0, ipc_child_cmd_hello,       0 },
436        { "takeover",   1, ipc_child_cmd_takeover,    0 },
437        { NULL }
438};
439
440gboolean ipc_child_identify(irc_t *irc)
441{
442        if (global.conf->runmode == RUNMODE_FORKDAEMON) {
443#ifndef NO_FD_PASSING
444                if (!ipc_send_fd(global.listen_socket, irc->fd)) {
445                        ipc_child_disable();
446                }
447
448                ipc_to_master_str("IDENTIFY %s :%s\r\n", irc->user->nick, irc->password);
449#endif
450
451                return TRUE;
452        } else if (global.conf->runmode == RUNMODE_DAEMON) {
453                GSList *l;
454                irc_t *old = NULL;
455                char *to_init[] = { "TAKEOVER", "INIT", NULL };
456
457                for (l = irc_connection_list; l; l = l->next) {
458                        old = l->data;
459
460                        if (irc != old &&
461                            irc->user->nick && old->user->nick &&
462                            irc->password && old->password &&
463                            strcmp(irc->user->nick, old->user->nick) == 0 &&
464                            strcmp(irc->password, old->password) == 0) {
465                                break;
466                        }
467                }
468                if (l == NULL || old == NULL ||
469                    !set_getbool(&irc->b->set, "allow_takeover") ||
470                    !set_getbool(&old->b->set, "allow_takeover")) {
471                        return FALSE;
472                }
473
474                ipc_child_cmd_takeover(irc, to_init);
475
476                return TRUE;
477        } else {
478                return FALSE;
479        }
480}
481
482static void ipc_master_takeover_fail(struct bitlbee_child *child, gboolean both)
483{
484        if (child == NULL || g_slist_find(child_list, child) == NULL) {
485                return;
486        }
487
488        if (both && child->to_child != NULL) {
489                ipc_master_takeover_fail(child->to_child, FALSE);
490        }
491
492        if (child->to_fd > -1) {
493                /* Send this error only to the new connection, which can be
494                   recognised by to_fd being set. */
495                if (write(child->ipc_fd, "TAKEOVER FAIL\r\n", 15) != 15) {
496                        ipc_master_free_one(child);
497                        return;
498                }
499                close(child->to_fd);
500                child->to_fd = -1;
501        }
502        child->to_child = NULL;
503}
504
505static void ipc_command_exec(void *data, char **cmd, const command_t *commands)
506{
507        int i, j;
508
509        if (!cmd[0]) {
510                return;
511        }
512
513        for (i = 0; commands[i].command; i++) {
514                if (g_strcasecmp(commands[i].command, cmd[0]) == 0) {
515                        /* There is no typo in this line: */
516                        for (j = 1; cmd[j]; j++) {
517                                ;
518                        }
519                        j--;
520
521                        if (j < commands[i].required_parameters) {
522                                break;
523                        }
524
525                        if (commands[i].flags & IPC_CMD_TO_CHILDREN) {
526                                ipc_to_children(cmd);
527                        } else {
528                                commands[i].execute(data, cmd);
529                        }
530
531                        break;
532                }
533        }
534}
535
536/* Return just one line. Returns NULL if something broke, an empty string
537   on temporary "errors" (EAGAIN and friends). */
538static char *ipc_readline(int fd, int *recv_fd)
539{
540        struct msghdr msg;
541        struct iovec iov;
542        char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
543        struct cmsghdr *cmsg;
544        char buf[513], *eol;
545        int size;
546
547        /* Because this is internal communication, it should be pretty safe
548           to just peek at the message, find its length (by searching for the
549           end-of-line) and then just read that message. With internal
550           sockets and limites message length, messages should always be
551           complete. Saves us quite a lot of code and buffering. */
552        size = recv(fd, buf, sizeof(buf) - 1, MSG_PEEK);
553        if (size == 0 || (size < 0 && !sockerr_again())) {
554                return NULL;
555        } else if (size < 0) { /* && sockerr_again() */
556                return(g_strdup(""));
557        } else {
558                buf[size] = 0;
559        }
560
561        if ((eol = strstr(buf, "\r\n")) == NULL) {
562                return NULL;
563        } else {
564                size = eol - buf + 2;
565        }
566
567        iov.iov_base = buf;
568        iov.iov_len = size;
569
570        memset(&msg, 0, sizeof(msg));
571        msg.msg_iov = &iov;
572        msg.msg_iovlen = 1;
573#ifndef NO_FD_PASSING
574        msg.msg_control = ccmsg;
575        msg.msg_controllen = sizeof(ccmsg);
576#endif
577
578        if (recvmsg(fd, &msg, 0) != size) {
579                return NULL;
580        }
581
582#ifndef NO_FD_PASSING
583        if (recv_fd) {
584                for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
585                        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
586                                /* Getting more than one shouldn't happen but if it does,
587                                   make sure we don't leave them around. */
588                                if (*recv_fd != -1) {
589                                        close(*recv_fd);
590                                }
591
592                                memcpy(recv_fd, CMSG_DATA(cmsg), sizeof(int));
593                                /*
594                                fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
595                                */
596                        }
597                }
598        }
599#endif
600
601        /*
602        fprintf( stderr, "pid %d received: %s", (int) getpid(), buf );
603        */
604        return g_strndup(buf, size - 2);
605}
606
607gboolean ipc_master_read(gpointer data, gint source, b_input_condition cond)
608{
609        struct bitlbee_child *child = data;
610        char *buf, **cmd;
611
612        if ((buf = ipc_readline(source, &child->to_fd))) {
613                cmd = irc_parse_line(buf);
614                if (cmd) {
615                        ipc_command_exec(child, cmd, ipc_master_commands);
616                        g_free(cmd);
617                }
618                g_free(buf);
619        } else {
620                ipc_master_free_fd(source);
621        }
622
623        return TRUE;
624}
625
626gboolean ipc_child_read(gpointer data, gint source, b_input_condition cond)
627{
628        char *buf, **cmd;
629
630        if ((buf = ipc_readline(source, &ipc_child_recv_fd))) {
631                cmd = irc_parse_line(buf);
632                if (cmd) {
633                        ipc_command_exec(data, cmd, ipc_child_commands);
634                        g_free(cmd);
635                }
636                g_free(buf);
637        } else {
638                ipc_child_disable();
639        }
640
641        return TRUE;
642}
643
644void ipc_to_master(char **cmd)
645{
646        if (global.conf->runmode == RUNMODE_FORKDAEMON) {
647                char *s = irc_build_line(cmd);
648                ipc_to_master_str("%s", s);
649                g_free(s);
650        } else if (global.conf->runmode == RUNMODE_DAEMON) {
651                ipc_command_exec(NULL, cmd, ipc_master_commands);
652        }
653}
654
655void ipc_to_master_str(char *format, ...)
656{
657        char *msg_buf;
658        va_list params;
659
660        va_start(params, format);
661        msg_buf = g_strdup_vprintf(format, params);
662        va_end(params);
663
664        if (strlen(msg_buf) > 512) {
665                /* Don't send it, it's too long... */
666        } else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
667                if (global.listen_socket >= 0) {
668                        if (write(global.listen_socket, msg_buf, strlen(msg_buf)) <= 0) {
669                                ipc_child_disable();
670                        }
671                }
672        } else if (global.conf->runmode == RUNMODE_DAEMON) {
673                char **cmd, *s;
674
675                if ((s = strchr(msg_buf, '\r'))) {
676                        *s = 0;
677                }
678
679                cmd = irc_parse_line(msg_buf);
680                ipc_command_exec(NULL, cmd, ipc_master_commands);
681                g_free(cmd);
682        }
683
684        g_free(msg_buf);
685}
686
687void ipc_to_children(char **cmd)
688{
689        if (global.conf->runmode == RUNMODE_FORKDAEMON) {
690                char *msg_buf = irc_build_line(cmd);
691                ipc_to_children_str("%s", msg_buf);
692                g_free(msg_buf);
693        } else if (global.conf->runmode == RUNMODE_DAEMON) {
694                GSList *l;
695
696                for (l = irc_connection_list; l; l = l->next) {
697                        ipc_command_exec(l->data, cmd, ipc_child_commands);
698                }
699        }
700}
701
702void ipc_to_children_str(char *format, ...)
703{
704        char *msg_buf;
705        va_list params;
706
707        va_start(params, format);
708        msg_buf = g_strdup_vprintf(format, params);
709        va_end(params);
710
711        if (strlen(msg_buf) > 512) {
712                /* Don't send it, it's too long... */
713        } else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
714                int msg_len = strlen(msg_buf);
715                GSList *l, *next;
716
717                for (l = child_list; l; l = next) {
718                        struct bitlbee_child *c = l->data;
719
720                        next = l->next;
721                        if (write(c->ipc_fd, msg_buf, msg_len) <= 0) {
722                                ipc_master_free_one(c);
723                        }
724                }
725        } else if (global.conf->runmode == RUNMODE_DAEMON) {
726                char **cmd, *s;
727
728                if ((s = strchr(msg_buf, '\r'))) {
729                        *s = 0;
730                }
731
732                cmd = irc_parse_line(msg_buf);
733                ipc_to_children(cmd);
734                g_free(cmd);
735        }
736
737        g_free(msg_buf);
738}
739
740static gboolean ipc_send_fd(int fd, int send_fd)
741{
742        struct msghdr msg;
743        struct iovec iov;
744        char ccmsg[CMSG_SPACE(sizeof(fd))];
745        struct cmsghdr *cmsg;
746
747        memset(&msg, 0, sizeof(msg));
748        iov.iov_base = "0x90\r\n";         /* Ja, noppes */
749        iov.iov_len = 6;
750        msg.msg_iov = &iov;
751        msg.msg_iovlen = 1;
752
753#ifndef NO_FD_PASSING
754        msg.msg_control = ccmsg;
755        msg.msg_controllen = sizeof(ccmsg);
756        cmsg = CMSG_FIRSTHDR(&msg);
757        cmsg->cmsg_level = SOL_SOCKET;
758        cmsg->cmsg_type = SCM_RIGHTS;
759        cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
760        memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(int));
761        msg.msg_controllen = cmsg->cmsg_len;
762#endif
763
764        return sendmsg(fd, &msg, 0) == 6;
765}
766
767void ipc_master_free_one(struct bitlbee_child *c)
768{
769        GSList *l;
770
771        b_event_remove(c->ipc_inpa);
772        closesocket(c->ipc_fd);
773
774        if (c->to_fd != -1) {
775                close(c->to_fd);
776        }
777
778        child_list = g_slist_remove(child_list, c);
779
780        g_free(c->host);
781        g_free(c->nick);
782        g_free(c->realname);
783        g_free(c->password);
784        g_free(c);
785
786        /* Also, if any child has a reference to this one, remove it. */
787        for (l = child_list; l; l = l->next) {
788                struct bitlbee_child *oc = l->data;
789
790                if (oc->to_child == c) {
791                        ipc_master_takeover_fail(oc, FALSE);
792                }
793        }
794}
795
796void ipc_master_free_fd(int fd)
797{
798        GSList *l;
799        struct bitlbee_child *c;
800
801        for (l = child_list; l; l = l->next) {
802                c = l->data;
803                if (c->ipc_fd == fd) {
804                        ipc_master_free_one(c);
805                        break;
806                }
807        }
808}
809
810void ipc_master_free_all()
811{
812        while (child_list) {
813                ipc_master_free_one(child_list->data);
814        }
815}
816
817void ipc_child_disable()
818{
819        b_event_remove(global.listen_watch_source_id);
820        close(global.listen_socket);
821
822        global.listen_socket = -1;
823}
824
825char *ipc_master_save_state()
826{
827        char *fn = g_strdup("/tmp/bee-restart.XXXXXX");
828        int fd = mkstemp(fn);
829        GSList *l;
830        FILE *fp;
831        int i;
832
833        if (fd == -1) {
834                log_message(LOGLVL_ERROR, "Could not create temporary file: %s", strerror(errno));
835                g_free(fn);
836                return NULL;
837        }
838
839        /* This is more convenient now. */
840        fp = fdopen(fd, "w");
841
842        for (l = child_list, i = 0; l; l = l->next) {
843                i++;
844        }
845
846        /* Number of client processes. */
847        fprintf(fp, "%d\n", i);
848
849        for (l = child_list; l; l = l->next) {
850                fprintf(fp, "%d %d\n", (int) ((struct bitlbee_child*) l->data)->pid,
851                        ((struct bitlbee_child*) l->data)->ipc_fd);
852        }
853
854        if (fclose(fp) == 0) {
855                return fn;
856        } else {
857                unlink(fn);
858                g_free(fn);
859                return NULL;
860        }
861}
862
863
864static gboolean new_ipc_client(gpointer data, gint serversock, b_input_condition cond)
865{
866        struct bitlbee_child *child = g_new0(struct bitlbee_child, 1);
867
868        child->to_fd = -1;
869        child->ipc_fd = accept(serversock, NULL, 0);
870        if (child->ipc_fd == -1) {
871                log_message(LOGLVL_WARNING, "Unable to accept connection on UNIX domain socket: %s", strerror(errno));
872                return TRUE;
873        }
874
875        child->ipc_inpa = b_input_add(child->ipc_fd, B_EV_IO_READ, ipc_master_read, child);
876
877        child_list = g_slist_prepend(child_list, child);
878
879        return TRUE;
880}
881
882int ipc_master_listen_socket()
883{
884        struct sockaddr_un un_addr;
885        int serversock;
886
887        if (!IPCSOCKET || !*IPCSOCKET) {
888                return 1;
889        }
890
891        /* Clean up old socket files that were hanging around.. */
892        if (unlink(IPCSOCKET) == -1 && errno != ENOENT) {
893                log_message(LOGLVL_ERROR, "Could not remove old IPC socket at %s: %s", IPCSOCKET, strerror(errno));
894                return 0;
895        }
896
897        un_addr.sun_family = AF_UNIX;
898        strcpy(un_addr.sun_path, IPCSOCKET);
899
900        serversock = socket(AF_UNIX, SOCK_STREAM, PF_UNIX);
901
902        if (serversock == -1) {
903                log_message(LOGLVL_WARNING, "Unable to create UNIX socket: %s", strerror(errno));
904                return 0;
905        }
906
907        if (bind(serversock, (struct sockaddr *) &un_addr, sizeof(un_addr)) == -1) {
908                log_message(LOGLVL_WARNING, "Unable to bind UNIX socket to %s: %s", IPCSOCKET, strerror(errno));
909                return 0;
910        }
911
912        if (listen(serversock, 5) == -1) {
913                log_message(LOGLVL_WARNING, "Unable to listen on UNIX socket: %s", strerror(errno));
914                return 0;
915        }
916
917        b_input_add(serversock, B_EV_IO_READ, new_ipc_client, NULL);
918
919        return 1;
920}
921
922int ipc_master_load_state(char *statefile)
923{
924        struct bitlbee_child *child;
925        FILE *fp;
926        int i, n;
927
928        if (statefile == NULL) {
929                return 0;
930        }
931
932        fp = fopen(statefile, "r");
933        unlink(statefile);      /* Why do it later? :-) */
934        if (fp == NULL) {
935                return 0;
936        }
937
938        if (fscanf(fp, "%d", &n) != 1) {
939                log_message(LOGLVL_WARNING, "Could not import state information for child processes.");
940                fclose(fp);
941                return 0;
942        }
943
944        log_message(LOGLVL_INFO, "Importing information for %d child processes.", n);
945        for (i = 0; i < n; i++) {
946                child = g_new0(struct bitlbee_child, 1);
947
948                if (fscanf(fp, "%d %d", (int *) &child->pid, &child->ipc_fd) != 2) {
949                        log_message(LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i);
950                        g_free(child);
951                        fclose(fp);
952                        return 0;
953                }
954                child->ipc_inpa = b_input_add(child->ipc_fd, B_EV_IO_READ, ipc_master_read, child);
955                child->to_fd = -1;
956
957                child_list = g_slist_prepend(child_list, child);
958        }
959
960        ipc_to_children_str("HELLO\r\n");
961        ipc_to_children_str("OPERMSG :New %s master process started (version %s)\r\n", PACKAGE, BITLBEE_VERSION);
962
963        fclose(fp);
964        return 1;
965}
Note: See TracBrowser for help on using the repository browser.