]> jfr.im git - solanum.git/blame - ircd/wsproc.c
modules/m_sasl.c: use IsSecure() instead of IsSSL()
[solanum.git] / ircd / wsproc.c
CommitLineData
c53ca1e0 1/*
bccb7ded 2 * sslproc.c: An interface to wsockd
c53ca1e0
AC
3 * Copyright (C) 2007 Aaron Sethman <androsyn@ratbox.org>
4 * Copyright (C) 2007 ircd-ratbox development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22#include <rb_lib.h>
23#include "stdinc.h"
24
25
26#include "s_conf.h"
27#include "logger.h"
28#include "listener.h"
29#include "wsproc.h"
30#include "s_serv.h"
31#include "ircd.h"
32#include "hash.h"
33#include "client.h"
34#include "send.h"
35#include "packet.h"
36
37static void ws_read_ctl(rb_fde_t * F, void *data);
38static int wsockd_count;
39
c53ca1e0
AC
40#define MAXPASSFD 4
41#define READSIZE 1024
42typedef struct _ws_ctl_buf
43{
44 rb_dlink_node node;
45 char *buf;
46 size_t buflen;
47 rb_fde_t *F[MAXPASSFD];
48 int nfds;
49} ws_ctl_buf_t;
50
51
52struct ws_ctl
53{
54 rb_dlink_node node;
55 int cli_count;
56 rb_fde_t *F;
57 rb_fde_t *P;
58 pid_t pid;
59 rb_dlink_list readq;
60 rb_dlink_list writeq;
61 uint8_t shutdown;
62 uint8_t dead;
63};
64
65static rb_dlink_list wsock_daemons;
66
67static inline uint32_t
68buf_to_uint32(char *buf)
69{
70 uint32_t x;
71 memcpy(&x, buf, sizeof(x));
72 return x;
73}
74
75static inline void
76uint32_to_buf(char *buf, uint32_t x)
77{
78 memcpy(buf, &x, sizeof(x));
79 return;
80}
81
82static ws_ctl_t *
83allocate_ws_daemon(rb_fde_t * F, rb_fde_t * P, int pid)
84{
85 ws_ctl_t *ctl;
86
87 if(F == NULL || pid < 0)
88 return NULL;
89 ctl = rb_malloc(sizeof(ws_ctl_t));
90 ctl->F = F;
91 ctl->P = P;
92 ctl->pid = pid;
93 wsockd_count++;
94 rb_dlinkAdd(ctl, &ctl->node, &wsock_daemons);
95 return ctl;
96}
97
98static void
99free_ws_daemon(ws_ctl_t * ctl)
100{
101 rb_dlink_node *ptr;
102 ws_ctl_buf_t *ctl_buf;
103 int x;
104 if(ctl->cli_count)
105 return;
106
107 RB_DLINK_FOREACH(ptr, ctl->readq.head)
108 {
109 ctl_buf = ptr->data;
110 for(x = 0; x < ctl_buf->nfds; x++)
111 rb_close(ctl_buf->F[x]);
112
113 rb_free(ctl_buf->buf);
114 rb_free(ctl_buf);
115 }
116
117 RB_DLINK_FOREACH(ptr, ctl->writeq.head)
118 {
119 ctl_buf = ptr->data;
120 for(x = 0; x < ctl_buf->nfds; x++)
121 rb_close(ctl_buf->F[x]);
122
123 rb_free(ctl_buf->buf);
124 rb_free(ctl_buf);
125 }
126 rb_close(ctl->F);
127 rb_close(ctl->P);
128 rb_dlinkDelete(&ctl->node, &wsock_daemons);
129 rb_free(ctl);
130}
131
132static char *wsockd_path;
133
134static int wsockd_spin_count = 0;
135static time_t last_spin;
136static int wsockd_wait = 0;
137
138void
139restart_wsockd(void)
140{
141 rb_dlink_node *ptr, *next;
142 ws_ctl_t *ctl;
143
144 RB_DLINK_FOREACH_SAFE(ptr, next, wsock_daemons.head)
145 {
146 ctl = ptr->data;
147 if(ctl->dead)
148 continue;
149 if(ctl->shutdown)
150 continue;
151 ctl->shutdown = 1;
152 wsockd_count--;
153 if(!ctl->cli_count)
154 {
155 rb_kill(ctl->pid, SIGKILL);
156 free_ws_daemon(ctl);
157 }
158 }
159
160 start_wsockd(ServerInfo.wsockd_count);
161}
162
9a9bc518 163#if 0
c53ca1e0
AC
164static void
165ws_killall(void)
166{
167 rb_dlink_node *ptr, *next;
168 ws_ctl_t *ctl;
169 RB_DLINK_FOREACH_SAFE(ptr, next, wsock_daemons.head)
170 {
171 ctl = ptr->data;
172 if(ctl->dead)
173 continue;
174 ctl->dead = 1;
175 if(!ctl->shutdown)
176 wsockd_count--;
177 rb_kill(ctl->pid, SIGKILL);
178 if(!ctl->cli_count)
179 free_ws_daemon(ctl);
180 }
181}
9a9bc518 182#endif
c53ca1e0
AC
183
184static void
185ws_dead(ws_ctl_t * ctl)
186{
187 if(ctl->dead)
188 return;
189
190 ctl->dead = 1;
191 rb_kill(ctl->pid, SIGKILL); /* make sure the process is really gone */
192
193 if(!ctl->shutdown)
194 {
195 wsockd_count--;
196 ilog(L_MAIN, "wsockd helper died - attempting to restart");
a9227555 197 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "wsockd helper died - attempting to restart");
c53ca1e0
AC
198 start_wsockd(1);
199 }
200}
201
202static void
203ws_do_pipe(rb_fde_t * F, void *data)
204{
205 int retlen;
206 ws_ctl_t *ctl = data;
207 retlen = rb_write(F, "0", 1);
208 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
209 {
210 ws_dead(ctl);
211 return;
212 }
213 rb_setselect(F, RB_SELECT_READ, ws_do_pipe, data);
214}
215
216static void
bccb7ded 217restart_wsockd_event(void *unused)
c53ca1e0
AC
218{
219 wsockd_spin_count = 0;
220 last_spin = 0;
221 wsockd_wait = 0;
222 if(ServerInfo.wsockd_count > get_wsockd_count())
223 {
224 int start = ServerInfo.wsockd_count - get_wsockd_count();
225 ilog(L_MAIN, "Attempting to restart wsockd processes");
a9227555 226 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Attempting to restart wsockd processes");
c53ca1e0
AC
227 start_wsockd(start);
228 }
229}
230
231int
232start_wsockd(int count)
233{
234 rb_fde_t *F1, *F2;
235 rb_fde_t *P1, *P2;
236#ifdef _WIN32
237 const char *suffix = ".exe";
238#else
239 const char *suffix = "";
240#endif
241
242 char fullpath[PATH_MAX + 1];
243 char fdarg[6];
244 const char *parv[2];
245 char buf[128];
246 char s_pid[10];
247 pid_t pid;
248 int started = 0, i;
249
250 if(wsockd_wait)
251 return 0;
252
253 if(wsockd_spin_count > 20 && (rb_current_time() - last_spin < 5))
254 {
bccb7ded 255 ilog(L_MAIN, "wsockd helper is spinning - will attempt to restart in 1 minute");
a9227555 256 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
bccb7ded
AC
257 "wsockd helper is spinning - will attempt to restart in 1 minute");
258 rb_event_add("restart_wsockd_event", restart_wsockd_event, NULL, 60);
c53ca1e0
AC
259 wsockd_wait = 1;
260 return 0;
261 }
262
263 wsockd_spin_count++;
264 last_spin = rb_current_time();
265
266 if(wsockd_path == NULL)
267 {
268 snprintf(fullpath, sizeof(fullpath), "%s%cwsockd%s", ircd_paths[IRCD_PATH_LIBEXEC], RB_PATH_SEPARATOR, suffix);
269
270 if(access(fullpath, X_OK) == -1)
271 {
272 snprintf(fullpath, sizeof(fullpath), "%s%cbin%cwsockd%s",
273 ConfigFileEntry.dpath, RB_PATH_SEPARATOR, RB_PATH_SEPARATOR, suffix);
274 if(access(fullpath, X_OK) == -1)
275 {
276 ilog(L_MAIN,
bccb7ded 277 "Unable to execute wsockd%s in %s or %s/bin",
c53ca1e0
AC
278 suffix, ircd_paths[IRCD_PATH_LIBEXEC], ConfigFileEntry.dpath);
279 return 0;
280 }
281 }
282 wsockd_path = rb_strdup(fullpath);
283 }
bccb7ded 284 rb_strlcpy(buf, "-ircd wsockd daemon", sizeof(buf));
c53ca1e0
AC
285 parv[0] = buf;
286 parv[1] = NULL;
287
288 for(i = 0; i < count; i++)
289 {
290 ws_ctl_t *ctl;
291 if(rb_socketpair(AF_UNIX, SOCK_DGRAM, 0, &F1, &F2, "wsockd handle passing socket") == -1)
292 {
293 ilog(L_MAIN, "Unable to create wsockd - rb_socketpair failed: %s", strerror(errno));
294 return started;
295 }
296
297 rb_set_buffers(F1, READBUF_SIZE);
298 rb_set_buffers(F2, READBUF_SIZE);
299 snprintf(fdarg, sizeof(fdarg), "%d", rb_get_fd(F2));
300 rb_setenv("CTL_FD", fdarg, 1);
301 if(rb_pipe(&P1, &P2, "wsockd pipe") == -1)
302 {
303 ilog(L_MAIN, "Unable to create wsockd - rb_pipe failed: %s", strerror(errno));
304 return started;
305 }
306 snprintf(fdarg, sizeof(fdarg), "%d", rb_get_fd(P1));
307 rb_setenv("CTL_PIPE", fdarg, 1);
308 snprintf(s_pid, sizeof(s_pid), "%d", (int)getpid());
309 rb_setenv("CTL_PPID", s_pid, 1);
310
12fd6e80
DF
311 rb_clear_cloexec(F2);
312 rb_clear_cloexec(P1);
c53ca1e0
AC
313
314 pid = rb_spawn_process(wsockd_path, (const char **) parv);
315 if(pid == -1)
316 {
bccb7ded 317 ilog(L_MAIN, "Unable to create wsockd: %s\n", strerror(errno));
c53ca1e0
AC
318 rb_close(F1);
319 rb_close(F2);
320 rb_close(P1);
321 rb_close(P2);
322 return started;
323 }
324 started++;
325 rb_close(F2);
326 rb_close(P1);
327 ctl = allocate_ws_daemon(F1, P2, pid);
328 ws_read_ctl(ctl->F, ctl);
329 ws_do_pipe(P2, ctl);
330
331 }
332 return started;
333}
334
335static void
336ws_process_dead_fd(ws_ctl_t * ctl, ws_ctl_buf_t * ctl_buf)
337{
338 struct Client *client_p;
339 char reason[256];
340 uint32_t fd;
341
342 if(ctl_buf->buflen < 6)
343 return; /* bogus message..drop it.. XXX should warn here */
344
345 fd = buf_to_uint32(&ctl_buf->buf[1]);
346 rb_strlcpy(reason, &ctl_buf->buf[5], sizeof(reason));
347 client_p = find_cli_connid_hash(fd);
348 if(client_p == NULL)
349 return;
350 if(IsAnyServer(client_p) || IsRegistered(client_p))
351 {
352 /* read any last moment ERROR, QUIT or the like -- jilles */
353 if (!strcmp(reason, "Remote host closed the connection"))
354 read_packet(client_p->localClient->F, client_p);
355 if (IsAnyDead(client_p))
356 return;
357 }
358 exit_client(client_p, client_p, &me, reason);
359}
360
361
362static void
363ws_process_cmd_recv(ws_ctl_t * ctl)
364{
c53ca1e0
AC
365 rb_dlink_node *ptr, *next;
366 ws_ctl_buf_t *ctl_buf;
c53ca1e0
AC
367
368 if(ctl->dead)
369 return;
370
371 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->readq.head)
372 {
373 ctl_buf = ptr->data;
374 switch (*ctl_buf->buf)
375 {
376 case 'D':
377 ws_process_dead_fd(ctl, ctl_buf);
378 break;
379 default:
bccb7ded 380 ilog(L_MAIN, "Received invalid command from wsockd: %s", ctl_buf->buf);
a9227555 381 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Received invalid command from wsockd");
c53ca1e0
AC
382 break;
383 }
384 rb_dlinkDelete(ptr, &ctl->readq);
385 rb_free(ctl_buf->buf);
386 rb_free(ctl_buf);
387 }
388
389}
390
391
392static void
393ws_read_ctl(rb_fde_t * F, void *data)
394{
395 ws_ctl_buf_t *ctl_buf;
396 ws_ctl_t *ctl = data;
397 int retlen;
398
399 if(ctl->dead)
400 return;
401 do
402 {
403 ctl_buf = rb_malloc(sizeof(ws_ctl_buf_t));
404 ctl_buf->buf = rb_malloc(READSIZE);
405 retlen = rb_recv_fd_buf(ctl->F, ctl_buf->buf, READSIZE, ctl_buf->F, 4);
406 ctl_buf->buflen = retlen;
407 if(retlen <= 0)
408 {
409 rb_free(ctl_buf->buf);
410 rb_free(ctl_buf);
411 }
412 else
413 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->readq);
414 }
415 while(retlen > 0);
416
417 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
418 {
419 ws_dead(ctl);
420 return;
421 }
422 ws_process_cmd_recv(ctl);
423 rb_setselect(ctl->F, RB_SELECT_READ, ws_read_ctl, ctl);
424}
425
426static ws_ctl_t *
427which_wsockd(void)
428{
429 ws_ctl_t *ctl, *lowest = NULL;
430 rb_dlink_node *ptr;
431
432 RB_DLINK_FOREACH(ptr, wsock_daemons.head)
433 {
434 ctl = ptr->data;
435 if(ctl->dead)
436 continue;
437 if(ctl->shutdown)
438 continue;
439 if(lowest == NULL)
440 {
441 lowest = ctl;
442 continue;
443 }
444 if(ctl->cli_count < lowest->cli_count)
445 lowest = ctl;
446 }
447
448 return (lowest);
449}
450
451static void
452ws_write_ctl(rb_fde_t * F, void *data)
453{
454 ws_ctl_t *ctl = data;
455 ws_ctl_buf_t *ctl_buf;
456 rb_dlink_node *ptr, *next;
457 int retlen, x;
458
459 if(ctl->dead)
460 return;
461
462 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->writeq.head)
463 {
464 ctl_buf = ptr->data;
465 /* in theory unix sock_dgram shouldn't ever short write this.. */
466 retlen = rb_send_fd_buf(ctl->F, ctl_buf->F, ctl_buf->nfds, ctl_buf->buf, ctl_buf->buflen, ctl->pid);
467 if(retlen > 0)
468 {
469 rb_dlinkDelete(ptr, &ctl->writeq);
470 for(x = 0; x < ctl_buf->nfds; x++)
471 rb_close(ctl_buf->F[x]);
472 rb_free(ctl_buf->buf);
473 rb_free(ctl_buf);
474
475 }
476 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
477 {
478 ws_dead(ctl);
479 return;
480 }
481 else
482 {
483 rb_setselect(ctl->F, RB_SELECT_WRITE, ws_write_ctl, ctl);
484 }
485 }
486}
487
488static void
489ws_cmd_write_queue(ws_ctl_t * ctl, rb_fde_t ** F, int count, const void *buf, size_t buflen)
490{
491 ws_ctl_buf_t *ctl_buf;
492 int x;
493
494 /* don't bother */
495 if(ctl->dead)
496 return;
497
498 ctl_buf = rb_malloc(sizeof(ws_ctl_buf_t));
499 ctl_buf->buf = rb_malloc(buflen);
500 memcpy(ctl_buf->buf, buf, buflen);
501 ctl_buf->buflen = buflen;
502
503 for(x = 0; x < count && x < MAXPASSFD; x++)
504 {
505 ctl_buf->F[x] = F[x];
506 }
507 ctl_buf->nfds = count;
508 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->writeq);
509 ws_write_ctl(ctl->F, ctl);
510}
511
512ws_ctl_t *
513start_wsockd_accept(rb_fde_t * sslF, rb_fde_t * plainF, uint32_t id)
514{
515 rb_fde_t *F[2];
516 ws_ctl_t *ctl;
517 char buf[5];
518 F[0] = sslF;
519 F[1] = plainF;
520
521 buf[0] = 'A';
522 uint32_to_buf(&buf[1], id);
523 ctl = which_wsockd();
524 if(!ctl)
525 return NULL;
526 ctl->cli_count++;
527 ws_cmd_write_queue(ctl, F, 2, buf, sizeof(buf));
528 return ctl;
529}
530
531void
532wsockd_decrement_clicount(ws_ctl_t * ctl)
533{
534 if(ctl == NULL)
535 return;
536
537 ctl->cli_count--;
538 if(ctl->shutdown && !ctl->cli_count)
539 {
540 ctl->dead = 1;
541 rb_kill(ctl->pid, SIGKILL);
542 }
543 if(ctl->dead && !ctl->cli_count)
544 {
545 free_ws_daemon(ctl);
546 }
547}
548
549static void
550cleanup_dead_ws(void *unused)
551{
552 rb_dlink_node *ptr, *next;
553 ws_ctl_t *ctl;
554 RB_DLINK_FOREACH_SAFE(ptr, next, wsock_daemons.head)
555 {
556 ctl = ptr->data;
557 if(ctl->dead && !ctl->cli_count)
558 {
559 free_ws_daemon(ctl);
560 }
561 }
562}
563
564int
565get_wsockd_count(void)
566{
567 return wsockd_count;
568}
569
570void
571wsockd_foreach_info(void (*func)(void *data, pid_t pid, int cli_count, enum wsockd_status status), void *data)
572{
573 rb_dlink_node *ptr, *next;
574 ws_ctl_t *ctl;
575 RB_DLINK_FOREACH_SAFE(ptr, next, wsock_daemons.head)
576 {
577 ctl = ptr->data;
578 func(data, ctl->pid, ctl->cli_count,
579 ctl->dead ? WSOCKD_DEAD :
580 (ctl->shutdown ? WSOCKD_SHUTDOWN : WSOCKD_ACTIVE));
581 }
582}
583
584void
585init_wsockd(void)
586{
587 rb_event_addish("cleanup_dead_ws", cleanup_dead_ws, NULL, 60);
588}