source: ipc.c @ ef9ee0e

Last change on this file since ef9ee0e was 05816dd, checked in by dequis <dx@…>, at 2015-02-22T22:44:40Z

coverity: Fix some (harmless?) use-after-free with g_slist_remove()

These were passing a pointer to a variable right after it was g_free()'d

They are most likely harmless as g_slist_remove() probably just needs
the pointer location, but fixing it anyway.

  • Property mode set to 100644
File size: 23.8 KB
RevLine 
[5ebff60]1/********************************************************************\
[0431ea1]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[0431ea1]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;
[6f10697]22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
[0431ea1]24*/
25
26#define BITLBEE_CORE
27#include "bitlbee.h"
28#include "ipc.h"
29#include "commands.h"
[3709301]30#include <sys/uio.h>
[6dff9d4]31#include <sys/un.h>
[0431ea1]32
33GSList *child_list = NULL;
[0b09da0]34static int ipc_child_recv_fd = -1;
[74c119d]35
[5ebff60]36static void ipc_master_takeover_fail(struct bitlbee_child *child, gboolean both);
37static gboolean ipc_send_fd(int fd, int send_fd);
[c5bff81]38
[70d7795]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
[5ebff60]46static void ipc_master_cmd_client(irc_t *data, char **cmd)
[2face62]47{
[5e713f6]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. */
[5ebff60]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
[debe871]60        /* CLIENT == On initial connects, HELLO is after /RESTARTs. */
[5ebff60]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        }
[2face62]65}
66
[5ebff60]67static void ipc_master_cmd_nick(irc_t *data, char **cmd)
[debe871]68{
[5ebff60]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]);
[debe871]74        }
75}
76
[5ebff60]77static void ipc_master_cmd_die(irc_t *data, char **cmd)
[0431ea1]78{
[5ebff60]79        if (global.conf->runmode == RUNMODE_FORKDAEMON) {
80                ipc_to_children_str("DIE\r\n");
81        }
82
83        bitlbee_shutdown(NULL, -1, 0);
[0431ea1]84}
85
[5ebff60]86static void ipc_master_cmd_deaf(irc_t *data, char **cmd)
[565a1ea]87{
[5ebff60]88        if (global.conf->runmode == RUNMODE_DAEMON) {
89                b_event_remove(global.listen_watch_source_id);
90                close(global.listen_socket);
91
[565a1ea]92                global.listen_socket = global.listen_watch_source_id = -1;
[5ebff60]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.");
[565a1ea]99        }
100}
101
[5ebff60]102void ipc_master_cmd_rehash(irc_t *data, char **cmd)
[0431ea1]103{
[f4a5940]104        runmode_t oldmode;
[5ebff60]105
[f4a5940]106        oldmode = global.conf->runmode;
[5ebff60]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");
[f4a5940]113                global.conf->runmode = oldmode;
114        }
[5ebff60]115
116        if (global.conf->runmode == RUNMODE_FORKDAEMON) {
117                ipc_to_children(cmd);
118        }
[0431ea1]119}
120
[5ebff60]121void ipc_master_cmd_restart(irc_t *data, char **cmd)
[54879ab]122{
[5ebff60]123        if (global.conf->runmode != RUNMODE_FORKDAEMON) {
[54879ab]124                /* Tell child that this is unsupported. */
125                return;
126        }
[5ebff60]127
[54879ab]128        global.restart = -1;
[5ebff60]129        bitlbee_shutdown(NULL, -1, 0);
[54879ab]130}
131
[5ebff60]132void ipc_master_cmd_identify(irc_t *data, char **cmd)
[6c2404e]133{
[5ebff60]134        struct bitlbee_child *child = (void *) data, *old = NULL;
[0b09da0]135        char *resp;
[6c2404e]136        GSList *l;
[5ebff60]137
138        if (!child || !child->nick || strcmp(child->nick, cmd[1]) != 0) {
[6c2404e]139                return;
[5ebff60]140        }
141
142        g_free(child->password);
143        child->password = g_strdup(cmd[2]);
144
145        for (l = child_list; l; l = l->next) {
[6c2404e]146                old = l->data;
[5ebff60]147                if (child != old &&
148                    old->nick && nick_cmp(NULL, old->nick, child->nick) == 0 &&
149                    old->password && strcmp(old->password, child->password) == 0) {
[6c2404e]150                        break;
[5ebff60]151                }
[6c2404e]152        }
[5ebff60]153
154        if (l && !child->to_child && !old->to_child) {
[0b09da0]155                resp = "TAKEOVER INIT\r\n";
[f545372]156                child->to_child = old;
157                old->to_child = child;
[5ebff60]158        } else {
[0b09da0]159                /* Won't need the fd since we can't send it anywhere. */
[5ebff60]160                closesocket(child->to_fd);
[0b09da0]161                child->to_fd = -1;
162                resp = "TAKEOVER NO\r\n";
163        }
[5ebff60]164
165        if (write(child->ipc_fd, resp, strlen(resp)) != strlen(resp)) {
166                ipc_master_free_one(child);
167        }
[0b09da0]168}
169
170
[5ebff60]171void ipc_master_cmd_takeover(irc_t *data, char **cmd)
[0b09da0]172{
[5ebff60]173        struct bitlbee_child *child = (void *) data;
[f545372]174        char *fwd = NULL;
[5ebff60]175
[e92c4f4]176        /* Normal daemon mode doesn't keep these and has simplified code for
177           takeovers. */
[5ebff60]178        if (child == NULL) {
[e92c4f4]179                return;
[5ebff60]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) {
[c5bff81]188                /* New connection -> Master */
[5ebff60]189                if (child->to_child &&
[0b09da0]190                    child->nick && child->to_child->nick && cmd[2] &&
191                    child->password && child->to_child->password && cmd[3] &&
[5ebff60]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);
[0b09da0]205                }
[5ebff60]206        } else if (strcmp(cmd[1], "DONE") == 0 || strcmp(cmd[1], "FAIL") == 0) {
[c5bff81]207                /* Old connection -> Master */
[f545372]208                int fd;
[5ebff60]209
[f545372]210                /* The copy was successful (or not), we don't need it anymore. */
[5ebff60]211                closesocket(child->to_fd);
[f545372]212                child->to_fd = -1;
[5ebff60]213
[f545372]214                /* Pass it through to the other party, and flush all state. */
[5ebff60]215                fwd = irc_build_line(cmd);
[f545372]216                fd = child->to_child->ipc_fd;
217                child->to_child->to_child = NULL;
218                child->to_child = NULL;
[5ebff60]219                if (write(fd, fwd, strlen(fwd)) != strlen(fwd)) {
220                        ipc_master_free_one(child);
221                }
222                g_free(fwd);
[f545372]223        }
[6c2404e]224}
225
[0431ea1]226static const command_t ipc_master_commands[] = {
[2face62]227        { "client",     3, ipc_master_cmd_client,     0 },
[54879ab]228        { "hello",      0, ipc_master_cmd_client,     0 },
[debe871]229        { "nick",       1, ipc_master_cmd_nick,       0 },
[0431ea1]230        { "die",        0, ipc_master_cmd_die,        0 },
[565a1ea]231        { "deaf",       0, ipc_master_cmd_deaf,       0 },
[f4a5940]232        { "wallops",    1, NULL,                      IPC_CMD_TO_CHILDREN },
[dfc8a46]233        { "wall",       1, NULL,                      IPC_CMD_TO_CHILDREN },
[2face62]234        { "opermsg",    1, NULL,                      IPC_CMD_TO_CHILDREN },
[f4a5940]235        { "rehash",     0, ipc_master_cmd_rehash,     0 },
[48721c3]236        { "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
[54879ab]237        { "restart",    0, ipc_master_cmd_restart,    0 },
[6c2404e]238        { "identify",   2, ipc_master_cmd_identify,   0 },
[0b09da0]239        { "takeover",   1, ipc_master_cmd_takeover,   0 },
[0431ea1]240        { NULL }
241};
242
[74c119d]243
[5ebff60]244static void ipc_child_cmd_die(irc_t *irc, char **cmd)
[74c119d]245{
[5ebff60]246        irc_abort(irc, 0, "Shutdown requested by operator");
[74c119d]247}
248
[5ebff60]249static void ipc_child_cmd_wallops(irc_t *irc, char **cmd)
[0431ea1]250{
[5ebff60]251        if (!(irc->status & USTATUS_LOGGED_IN)) {
[f73b969]252                return;
[5ebff60]253        }
254
255        if (strchr(irc->umode, 'w')) {
256                irc_write(irc, ":%s WALLOPS :%s", irc->root->host, cmd[1]);
257        }
[e0ca412]258}
259
[5ebff60]260static void ipc_child_cmd_wall(irc_t *irc, char **cmd)
[e0ca412]261{
[5ebff60]262        if (!(irc->status & USTATUS_LOGGED_IN)) {
[f73b969]263                return;
[5ebff60]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        }
[0431ea1]269}
270
[5ebff60]271static void ipc_child_cmd_opermsg(irc_t *irc, char **cmd)
[2face62]272{
[5ebff60]273        if (!(irc->status & USTATUS_LOGGED_IN)) {
[f73b969]274                return;
[5ebff60]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        }
[2face62]280}
281
[5ebff60]282static void ipc_child_cmd_rehash(irc_t *irc, char **cmd)
[f4a5940]283{
284        runmode_t oldmode;
[5ebff60]285
[f4a5940]286        oldmode = global.conf->runmode;
[5ebff60]287
288        g_free(global.conf);
289        global.conf = conf_load(0, NULL);
290
[f4a5940]291        global.conf->runmode = oldmode;
292}
293
[5ebff60]294static void ipc_child_cmd_kill(irc_t *irc, char **cmd)
[48721c3]295{
[5ebff60]296        if (!(irc->status & USTATUS_LOGGED_IN)) {
[f73b969]297                return;
[5ebff60]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]);
[48721c3]307}
308
[5ebff60]309static void ipc_child_cmd_hello(irc_t *irc, char **cmd)
[54879ab]310{
[5ebff60]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        }
[54879ab]316}
317
[5ebff60]318static void ipc_child_cmd_takeover_yes(void *data);
319static void ipc_child_cmd_takeover_no(void *data);
[f545372]320
[5ebff60]321static void ipc_child_cmd_takeover(irc_t *irc, char **cmd)
[0b09da0]322{
[5ebff60]323        if (strcmp(cmd[1], "NO") == 0) {
[f545372]324                /* Master->New connection */
[0b09da0]325                /* No takeover, finish the login. */
[5ebff60]326        } else if (strcmp(cmd[1], "INIT") == 0) {
[f545372]327                /* Master->New connection */
[5ebff60]328                if (!set_getbool(&irc->b->set, "allow_takeover")) {
329                        ipc_child_cmd_takeover_no(irc);
[af9f2ca]330                        return;
331                }
[5ebff60]332
[f545372]333                /* Offer to take over the old session, unless for some reason
334                   we're already logging into IM connections. */
[5ebff60]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
[f545372]343                /* This one's going to connect to accounts, avoid that. */
[5ebff60]344                b_event_remove(irc->login_source_id);
[f545372]345                irc->login_source_id = -1;
[5ebff60]346        } else if (strcmp(cmd[1], "AUTH") == 0) {
[f545372]347                /* Master->Old connection */
[5ebff60]348                if (irc->password && cmd[2] && cmd[3] &&
[0b09da0]349                    ipc_child_recv_fd != -1 &&
[5ebff60]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");
[f1c2b21]356                        ipc_child_recv_fd = -1;
[5ebff60]357
358                        ipc_to_master_str("TAKEOVER DONE\r\n");
359                } else {
360                        ipc_to_master_str("TAKEOVER FAIL\r\n");
[f545372]361                }
[5ebff60]362        } else if (strcmp(cmd[1], "DONE") == 0) {
[f545372]363                /* Master->New connection (now taken over by old process) */
[5ebff60]364                irc_free(irc);
365        } else if (strcmp(cmd[1], "FAIL") == 0) {
[f545372]366                /* Master->New connection */
[5ebff60]367                irc_rootmsg(irc, "Could not take over old session");
[0b09da0]368        }
369}
370
[5ebff60]371static void ipc_child_cmd_takeover_yes(void *data)
[f545372]372{
[e92c4f4]373        irc_t *irc = data, *old = NULL;
374        char *to_auth[] = { "TAKEOVER", "AUTH", irc->user->nick, irc->password, NULL };
[5ebff60]375
[f545372]376        /* Master->New connection */
[5ebff60]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) {
[e92c4f4]381                GSList *l;
[5ebff60]382
383                for (l = irc_connection_list; l; l = l->next) {
[e92c4f4]384                        old = l->data;
[5ebff60]385
386                        if (irc != old &&
[e92c4f4]387                            irc->user->nick && old->user->nick &&
388                            irc->password && old->password &&
[5ebff60]389                            strcmp(irc->user->nick, old->user->nick) == 0 &&
390                            strcmp(irc->password, old->password) == 0) {
[e92c4f4]391                                break;
[5ebff60]392                        }
[e92c4f4]393                }
[5ebff60]394                if (l == NULL) {
[e92c4f4]395                        to_auth[1] = "FAIL";
[5ebff60]396                        ipc_child_cmd_takeover(irc, to_auth);
[e92c4f4]397                        return;
398                }
399        }
[5ebff60]400
[f545372]401        /* Drop credentials, we'll shut down soon and shouldn't overwrite
402           any settings. */
[5ebff60]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
[f545372]412        /* TODO: irc_setpass() should do all of this. */
[5ebff60]413        irc_setpass(irc, NULL);
[f545372]414        irc->status &= ~USTATUS_IDENTIFIED;
[5ebff60]415        irc_umode_set(irc, "-R", 1);
416
417        if (old) {
418                irc_abort(irc, FALSE, NULL);
419        }
[f545372]420}
421
[5ebff60]422static void ipc_child_cmd_takeover_no(void *data)
[f545372]423{
[5ebff60]424        ipc_to_master_str("TAKEOVER NO\r\n");
425        cmd_identify_finish(data, 0, 0);
[f545372]426}
427
[0431ea1]428static const command_t ipc_child_commands[] = {
[74c119d]429        { "die",        0, ipc_child_cmd_die,         0 },
430        { "wallops",    1, ipc_child_cmd_wallops,     0 },
[dfc8a46]431        { "wall",       1, ipc_child_cmd_wall,        0 },
[2face62]432        { "opermsg",    1, ipc_child_cmd_opermsg,     0 },
[48721c3]433        { "rehash",     0, ipc_child_cmd_rehash,      0 },
434        { "kill",       2, ipc_child_cmd_kill,        0 },
[54879ab]435        { "hello",      0, ipc_child_cmd_hello,       0 },
[0b09da0]436        { "takeover",   1, ipc_child_cmd_takeover,    0 },
[0431ea1]437        { NULL }
438};
439
[5ebff60]440gboolean ipc_child_identify(irc_t *irc)
[6c2404e]441{
[5ebff60]442        if (global.conf->runmode == RUNMODE_FORKDAEMON) {
[70d7795]443#ifndef NO_FD_PASSING
[5ebff60]444                if (!ipc_send_fd(global.listen_socket, irc->fd)) {
[6c2404e]445                        ipc_child_disable();
[5ebff60]446                }
447
448                ipc_to_master_str("IDENTIFY %s :%s\r\n", irc->user->nick, irc->password);
[70d7795]449#endif
[5ebff60]450
[6c2404e]451                return TRUE;
[5ebff60]452        } else if (global.conf->runmode == RUNMODE_DAEMON) {
[e92c4f4]453                GSList *l;
[a6cd799]454                irc_t *old = NULL;
[e92c4f4]455                char *to_init[] = { "TAKEOVER", "INIT", NULL };
[5ebff60]456
457                for (l = irc_connection_list; l; l = l->next) {
[e92c4f4]458                        old = l->data;
[5ebff60]459
460                        if (irc != old &&
[e92c4f4]461                            irc->user->nick && old->user->nick &&
462                            irc->password && old->password &&
[5ebff60]463                            strcmp(irc->user->nick, old->user->nick) == 0 &&
464                            strcmp(irc->password, old->password) == 0) {
[e92c4f4]465                                break;
[5ebff60]466                        }
[e92c4f4]467                }
[5ebff60]468                if (l == NULL || old == NULL ||
469                    !set_getbool(&irc->b->set, "allow_takeover") ||
470                    !set_getbool(&old->b->set, "allow_takeover")) {
[e92c4f4]471                        return FALSE;
[5ebff60]472                }
473
474                ipc_child_cmd_takeover(irc, to_init);
475
[e92c4f4]476                return TRUE;
[5ebff60]477        } else {
[6c2404e]478                return FALSE;
[5ebff60]479        }
[6c2404e]480}
[74c119d]481
[5ebff60]482static void ipc_master_takeover_fail(struct bitlbee_child *child, gboolean both)
[c5bff81]483{
[5ebff60]484        if (child == NULL || g_slist_find(child_list, child) == NULL) {
[c5bff81]485                return;
[5ebff60]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) {
[c5bff81]493                /* Send this error only to the new connection, which can be
494                   recognised by to_fd being set. */
[5ebff60]495                if (write(child->ipc_fd, "TAKEOVER FAIL\r\n", 15) != 15) {
496                        ipc_master_free_one(child);
[c5bff81]497                        return;
498                }
[5ebff60]499                close(child->to_fd);
[c5bff81]500                child->to_fd = -1;
501        }
502        child->to_child = NULL;
503}
504
[5ebff60]505static void ipc_command_exec(void *data, char **cmd, const command_t *commands)
[0431ea1]506{
[f1d38f2]507        int i, j;
[5ebff60]508
509        if (!cmd[0]) {
[0431ea1]510                return;
[5ebff60]511        }
512
513        for (i = 0; commands[i].command; i++) {
514                if (g_strcasecmp(commands[i].command, cmd[0]) == 0) {
[f1d38f2]515                        /* There is no typo in this line: */
[5ebff60]516                        for (j = 1; cmd[j]; j++) {
[25c4c78]517                                ;
[5ebff60]518                        }
[25c4c78]519                        j--;
[5ebff60]520
521                        if (j < commands[i].required_parameters) {
[f1d38f2]522                                break;
[5ebff60]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
[f1d38f2]531                        break;
[0431ea1]532                }
[5ebff60]533        }
[0431ea1]534}
535
[da7b484]536/* Return just one line. Returns NULL if something broke, an empty string
537   on temporary "errors" (EAGAIN and friends). */
[5ebff60]538static char *ipc_readline(int fd, int *recv_fd)
[0431ea1]539{
[6c2404e]540        struct msghdr msg;
541        struct iovec iov;
542        char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
543        struct cmsghdr *cmsg;
[da7b484]544        char buf[513], *eol;
[0431ea1]545        int size;
[5ebff60]546
[0431ea1]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. */
[5ebff60]552        size = recv(fd, buf, sizeof(buf) - 1, MSG_PEEK);
553        if (size == 0 || (size < 0 && !sockerr_again())) {
[0431ea1]554                return NULL;
[5ebff60]555        } else if (size < 0) { /* && sockerr_again() */
556                return(g_strdup(""));
557        } else {
[0431ea1]558                buf[size] = 0;
[5ebff60]559        }
560
561        if ((eol = strstr(buf, "\r\n")) == NULL) {
[0431ea1]562                return NULL;
[5ebff60]563        } else {
[0431ea1]564                size = eol - buf + 2;
[5ebff60]565        }
566
[6c2404e]567        iov.iov_base = buf;
568        iov.iov_len = size;
[5ebff60]569
570        memset(&msg, 0, sizeof(msg));
[6c2404e]571        msg.msg_iov = &iov;
572        msg.msg_iovlen = 1;
[70d7795]573#ifndef NO_FD_PASSING
[6c2404e]574        msg.msg_control = ccmsg;
[5ebff60]575        msg.msg_controllen = sizeof(ccmsg);
[70d7795]576#endif
[5ebff60]577
578        if (recvmsg(fd, &msg, 0) != size) {
[0431ea1]579                return NULL;
[5ebff60]580        }
581
[70d7795]582#ifndef NO_FD_PASSING
[5ebff60]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) {
[6c2404e]586                                /* Getting more than one shouldn't happen but if it does,
587                                   make sure we don't leave them around. */
[5ebff60]588                                if (*recv_fd != -1) {
589                                        close(*recv_fd);
590                                }
591
592                                *recv_fd = *(int *) CMSG_DATA(cmsg);
[e92c4f4]593                                /*
[0b09da0]594                                fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
[e92c4f4]595                                */
[6c2404e]596                        }
[5ebff60]597                }
598        }
[70d7795]599#endif
[5ebff60]600
[e92c4f4]601        /*
[0b09da0]602        fprintf( stderr, "pid %d received: %s", (int) getpid(), buf );
[e92c4f4]603        */
[5ebff60]604        return g_strndup(buf, size - 2);
[0431ea1]605}
606
[5ebff60]607gboolean ipc_master_read(gpointer data, gint source, b_input_condition cond)
[0431ea1]608{
[6c2404e]609        struct bitlbee_child *child = data;
[0431ea1]610        char *buf, **cmd;
[5ebff60]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);
[edc767b]617                }
[5ebff60]618                g_free(buf);
619        } else {
620                ipc_master_free_fd(source);
[0431ea1]621        }
[5ebff60]622
[ba9edaa]623        return TRUE;
[0431ea1]624}
625
[5ebff60]626gboolean ipc_child_read(gpointer data, gint source, b_input_condition cond)
[0431ea1]627{
628        char *buf, **cmd;
[5ebff60]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);
[edc767b]635                }
[5ebff60]636                g_free(buf);
637        } else {
[171ef85]638                ipc_child_disable();
[0431ea1]639        }
[5ebff60]640
[ba9edaa]641        return TRUE;
[0431ea1]642}
643
[5ebff60]644void ipc_to_master(char **cmd)
[0431ea1]645{
[5ebff60]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);
[f4a5940]652        }
653}
654
[5ebff60]655void ipc_to_master_str(char *format, ...)
[f4a5940]656{
[2face62]657        char *msg_buf;
658        va_list params;
659
[5ebff60]660        va_start(params, format);
661        msg_buf = g_strdup_vprintf(format, params);
662        va_end(params);
663
664        if (strlen(msg_buf) > 512) {
[2face62]665                /* Don't send it, it's too long... */
[5ebff60]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) {
[171ef85]669                                ipc_child_disable();
[5ebff60]670                        }
671                }
672        } else if (global.conf->runmode == RUNMODE_DAEMON) {
[bd9b00f]673                char **cmd, *s;
[5ebff60]674
675                if ((s = strchr(msg_buf, '\r'))) {
[bd9b00f]676                        *s = 0;
[5ebff60]677                }
678
679                cmd = irc_parse_line(msg_buf);
680                ipc_command_exec(NULL, cmd, ipc_master_commands);
681                g_free(cmd);
[74c119d]682        }
[5ebff60]683
684        g_free(msg_buf);
[74c119d]685}
686
[5ebff60]687void ipc_to_children(char **cmd)
[74c119d]688{
[5ebff60]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) {
[f4a5940]694                GSList *l;
[5ebff60]695
696                for (l = irc_connection_list; l; l = l->next) {
697                        ipc_command_exec(l->data, cmd, ipc_child_commands);
698                }
[f4a5940]699        }
[74c119d]700}
701
[5ebff60]702void ipc_to_children_str(char *format, ...)
[74c119d]703{
[2face62]704        char *msg_buf;
705        va_list params;
706
[5ebff60]707        va_start(params, format);
708        msg_buf = g_strdup_vprintf(format, params);
709        va_end(params);
710
711        if (strlen(msg_buf) > 512) {
[2face62]712                /* Don't send it, it's too long... */
[5ebff60]713        } else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
714                int msg_len = strlen(msg_buf);
[171ef85]715                GSList *l, *next;
[5ebff60]716
717                for (l = child_list; l; l = next) {
[74c119d]718                        struct bitlbee_child *c = l->data;
[5ebff60]719
[171ef85]720                        next = l->next;
[5ebff60]721                        if (write(c->ipc_fd, msg_buf, msg_len) <= 0) {
722                                ipc_master_free_one(c);
723                        }
[0431ea1]724                }
[5ebff60]725        } else if (global.conf->runmode == RUNMODE_DAEMON) {
[bd9b00f]726                char **cmd, *s;
[5ebff60]727
728                if ((s = strchr(msg_buf, '\r'))) {
[bd9b00f]729                        *s = 0;
[5ebff60]730                }
731
732                cmd = irc_parse_line(msg_buf);
733                ipc_to_children(cmd);
734                g_free(cmd);
[f4a5940]735        }
[5ebff60]736
737        g_free(msg_buf);
[2face62]738}
739
[5ebff60]740static gboolean ipc_send_fd(int fd, int send_fd)
[6c2404e]741{
742        struct msghdr msg;
743        struct iovec iov;
744        char ccmsg[CMSG_SPACE(sizeof(fd))];
745        struct cmsghdr *cmsg;
[5ebff60]746
747        memset(&msg, 0, sizeof(msg));
[c5bff81]748        iov.iov_base = "0x90\r\n";         /* Ja, noppes */
[6c2404e]749        iov.iov_len = 6;
750        msg.msg_iov = &iov;
751        msg.msg_iovlen = 1;
[5ebff60]752
[70d7795]753#ifndef NO_FD_PASSING
[6c2404e]754        msg.msg_control = ccmsg;
[5ebff60]755        msg.msg_controllen = sizeof(ccmsg);
756        cmsg = CMSG_FIRSTHDR(&msg);
[6c2404e]757        cmsg->cmsg_level = SOL_SOCKET;
758        cmsg->cmsg_type = SCM_RIGHTS;
[5ebff60]759        cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
760        *(int *) CMSG_DATA(cmsg) = send_fd;
[6c2404e]761        msg.msg_controllen = cmsg->cmsg_len;
[70d7795]762#endif
[5ebff60]763
764        return sendmsg(fd, &msg, 0) == 6;
[6c2404e]765}
766
[5ebff60]767void ipc_master_free_one(struct bitlbee_child *c)
[2face62]768{
[c5bff81]769        GSList *l;
[5ebff60]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
[05816dd]778        child_list = g_slist_remove(child_list, c);
779
[5ebff60]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
[c5bff81]786        /* Also, if any child has a reference to this one, remove it. */
[5ebff60]787        for (l = child_list; l; l = l->next) {
[c5bff81]788                struct bitlbee_child *oc = l->data;
[5ebff60]789
790                if (oc->to_child == c) {
791                        ipc_master_takeover_fail(oc, FALSE);
792                }
[c5bff81]793        }
[2face62]794}
795
[5ebff60]796void ipc_master_free_fd(int fd)
[171ef85]797{
798        GSList *l;
799        struct bitlbee_child *c;
[5ebff60]800
801        for (l = child_list; l; l = l->next) {
[171ef85]802                c = l->data;
[5ebff60]803                if (c->ipc_fd == fd) {
804                        ipc_master_free_one(c);
[171ef85]805                        break;
806                }
807        }
808}
809
[2face62]810void ipc_master_free_all()
811{
[5ebff60]812        while (child_list) {
813                ipc_master_free_one(child_list->data);
814        }
[0431ea1]815}
[54879ab]816
[171ef85]817void ipc_child_disable()
818{
[5ebff60]819        b_event_remove(global.listen_watch_source_id);
820        close(global.listen_socket);
821
[171ef85]822        global.listen_socket = -1;
823}
824
[54879ab]825char *ipc_master_save_state()
826{
[5ebff60]827        char *fn = g_strdup("/tmp/bee-restart.XXXXXX");
828        int fd = mkstemp(fn);
[54879ab]829        GSList *l;
830        FILE *fp;
831        int i;
[5ebff60]832
833        if (fd == -1) {
834                log_message(LOGLVL_ERROR, "Could not create temporary file: %s", strerror(errno));
835                g_free(fn);
[54879ab]836                return NULL;
837        }
[5ebff60]838
[54879ab]839        /* This is more convenient now. */
[5ebff60]840        fp = fdopen(fd, "w");
841
842        for (l = child_list, i = 0; l; l = l->next) {
843                i++;
844        }
845
[54879ab]846        /* Number of client processes. */
[5ebff60]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);
[54879ab]852        }
[5ebff60]853
854        if (fclose(fp) == 0) {
855                return fn;
856        } else {
857                unlink(fn);
858                g_free(fn);
[54879ab]859                return NULL;
860        }
861}
862
[6dff9d4]863
[5ebff60]864static gboolean new_ipc_client(gpointer data, gint serversock, b_input_condition cond)
[6dff9d4]865{
[5ebff60]866        struct bitlbee_child *child = g_new0(struct bitlbee_child, 1);
867
[6c2404e]868        child->to_fd = -1;
[5ebff60]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));
[8a56e52]872                return TRUE;
873        }
[5ebff60]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
[6dff9d4]879        return TRUE;
880}
881
882int ipc_master_listen_socket()
883{
884        struct sockaddr_un un_addr;
885        int serversock;
886
[5ebff60]887        if (!IPCSOCKET || !*IPCSOCKET) {
[4c225f0]888                return 1;
[5ebff60]889        }
[4c225f0]890
[6dff9d4]891        /* Clean up old socket files that were hanging around.. */
[8a56e52]892        if (unlink(IPCSOCKET) == -1 && errno != ENOENT) {
[5ebff60]893                log_message(LOGLVL_ERROR, "Could not remove old IPC socket at %s: %s", IPCSOCKET, strerror(errno));
[8a56e52]894                return 0;
895        }
[6dff9d4]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
[8a56e52]902        if (serversock == -1) {
[5ebff60]903                log_message(LOGLVL_WARNING, "Unable to create UNIX socket: %s", strerror(errno));
[8a56e52]904                return 0;
905        }
[6dff9d4]906
[5ebff60]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));
[8a56e52]909                return 0;
910        }
911
912        if (listen(serversock, 5) == -1) {
[5ebff60]913                log_message(LOGLVL_WARNING, "Unable to listen on UNIX socket: %s", strerror(errno));
[8a56e52]914                return 0;
915        }
[5ebff60]916
917        b_input_add(serversock, B_EV_IO_READ, new_ipc_client, NULL);
918
[6dff9d4]919        return 1;
920}
921
[5ebff60]922int ipc_master_load_state(char *statefile)
[54879ab]923{
924        struct bitlbee_child *child;
925        FILE *fp;
926        int i, n;
[5ebff60]927
928        if (statefile == NULL) {
[54879ab]929                return 0;
[5ebff60]930        }
931
932        fp = fopen(statefile, "r");
933        unlink(statefile);      /* Why do it later? :-) */
934        if (fp == NULL) {
[54879ab]935                return 0;
[5ebff60]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);
[54879ab]941                return 0;
942        }
[5ebff60]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);
[54879ab]952                        return 0;
953                }
[5ebff60]954                child->ipc_inpa = b_input_add(child->ipc_fd, B_EV_IO_READ, ipc_master_read, child);
[6c2404e]955                child->to_fd = -1;
[5ebff60]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);
[54879ab]964        return 1;
965}
Note: See TracBrowser for help on using the repository browser.