]> jfr.im git - solanum.git/blame - ircd/wsproc.c
whowas.c: store account name in whowas (#323)
[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;
c53ca1e0
AC
236 char fullpath[PATH_MAX + 1];
237 char fdarg[6];
238 const char *parv[2];
239 char buf[128];
240 char s_pid[10];
241 pid_t pid;
242 int started = 0, i;
243
244 if(wsockd_wait)
245 return 0;
246
247 if(wsockd_spin_count > 20 && (rb_current_time() - last_spin < 5))
248 {
bccb7ded 249 ilog(L_MAIN, "wsockd helper is spinning - will attempt to restart in 1 minute");
a9227555 250 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
bccb7ded
AC
251 "wsockd helper is spinning - will attempt to restart in 1 minute");
252 rb_event_add("restart_wsockd_event", restart_wsockd_event, NULL, 60);
c53ca1e0
AC
253 wsockd_wait = 1;
254 return 0;
255 }
256
257 wsockd_spin_count++;
258 last_spin = rb_current_time();
259
260 if(wsockd_path == NULL)
261 {
8f0c3422 262 snprintf(fullpath, sizeof(fullpath), "%s/wsockd", ircd_paths[IRCD_PATH_LIBEXEC]);
c53ca1e0
AC
263
264 if(access(fullpath, X_OK) == -1)
265 {
8f0c3422 266 snprintf(fullpath, sizeof(fullpath), "%s/bin/wsockd", ConfigFileEntry.dpath);
c53ca1e0
AC
267 if(access(fullpath, X_OK) == -1)
268 {
269 ilog(L_MAIN,
8f0c3422 270 "Unable to execute wsockd in %s or %s/bin",
271 ircd_paths[IRCD_PATH_LIBEXEC], ConfigFileEntry.dpath);
c53ca1e0
AC
272 return 0;
273 }
274 }
275 wsockd_path = rb_strdup(fullpath);
276 }
bccb7ded 277 rb_strlcpy(buf, "-ircd wsockd daemon", sizeof(buf));
c53ca1e0
AC
278 parv[0] = buf;
279 parv[1] = NULL;
280
281 for(i = 0; i < count; i++)
282 {
283 ws_ctl_t *ctl;
284 if(rb_socketpair(AF_UNIX, SOCK_DGRAM, 0, &F1, &F2, "wsockd handle passing socket") == -1)
285 {
286 ilog(L_MAIN, "Unable to create wsockd - rb_socketpair failed: %s", strerror(errno));
287 return started;
288 }
289
290 rb_set_buffers(F1, READBUF_SIZE);
291 rb_set_buffers(F2, READBUF_SIZE);
292 snprintf(fdarg, sizeof(fdarg), "%d", rb_get_fd(F2));
293 rb_setenv("CTL_FD", fdarg, 1);
294 if(rb_pipe(&P1, &P2, "wsockd pipe") == -1)
295 {
296 ilog(L_MAIN, "Unable to create wsockd - rb_pipe failed: %s", strerror(errno));
297 return started;
298 }
299 snprintf(fdarg, sizeof(fdarg), "%d", rb_get_fd(P1));
300 rb_setenv("CTL_PIPE", fdarg, 1);
301 snprintf(s_pid, sizeof(s_pid), "%d", (int)getpid());
302 rb_setenv("CTL_PPID", s_pid, 1);
303
12fd6e80
DF
304 rb_clear_cloexec(F2);
305 rb_clear_cloexec(P1);
c53ca1e0
AC
306
307 pid = rb_spawn_process(wsockd_path, (const char **) parv);
308 if(pid == -1)
309 {
bccb7ded 310 ilog(L_MAIN, "Unable to create wsockd: %s\n", strerror(errno));
c53ca1e0
AC
311 rb_close(F1);
312 rb_close(F2);
313 rb_close(P1);
314 rb_close(P2);
315 return started;
316 }
317 started++;
318 rb_close(F2);
319 rb_close(P1);
320 ctl = allocate_ws_daemon(F1, P2, pid);
321 ws_read_ctl(ctl->F, ctl);
322 ws_do_pipe(P2, ctl);
323
324 }
325 return started;
326}
327
328static void
329ws_process_dead_fd(ws_ctl_t * ctl, ws_ctl_buf_t * ctl_buf)
330{
331 struct Client *client_p;
332 char reason[256];
333 uint32_t fd;
334
335 if(ctl_buf->buflen < 6)
336 return; /* bogus message..drop it.. XXX should warn here */
337
338 fd = buf_to_uint32(&ctl_buf->buf[1]);
339 rb_strlcpy(reason, &ctl_buf->buf[5], sizeof(reason));
340 client_p = find_cli_connid_hash(fd);
341 if(client_p == NULL)
342 return;
343 if(IsAnyServer(client_p) || IsRegistered(client_p))
344 {
345 /* read any last moment ERROR, QUIT or the like -- jilles */
346 if (!strcmp(reason, "Remote host closed the connection"))
347 read_packet(client_p->localClient->F, client_p);
348 if (IsAnyDead(client_p))
349 return;
350 }
351 exit_client(client_p, client_p, &me, reason);
352}
353
354
355static void
356ws_process_cmd_recv(ws_ctl_t * ctl)
357{
c53ca1e0
AC
358 rb_dlink_node *ptr, *next;
359 ws_ctl_buf_t *ctl_buf;
c53ca1e0
AC
360
361 if(ctl->dead)
362 return;
363
364 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->readq.head)
365 {
366 ctl_buf = ptr->data;
367 switch (*ctl_buf->buf)
368 {
369 case 'D':
370 ws_process_dead_fd(ctl, ctl_buf);
371 break;
372 default:
bccb7ded 373 ilog(L_MAIN, "Received invalid command from wsockd: %s", ctl_buf->buf);
a9227555 374 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Received invalid command from wsockd");
c53ca1e0
AC
375 break;
376 }
377 rb_dlinkDelete(ptr, &ctl->readq);
378 rb_free(ctl_buf->buf);
379 rb_free(ctl_buf);
380 }
381
382}
383
384
385static void
386ws_read_ctl(rb_fde_t * F, void *data)
387{
388 ws_ctl_buf_t *ctl_buf;
389 ws_ctl_t *ctl = data;
390 int retlen;
391
392 if(ctl->dead)
393 return;
394 do
395 {
396 ctl_buf = rb_malloc(sizeof(ws_ctl_buf_t));
397 ctl_buf->buf = rb_malloc(READSIZE);
398 retlen = rb_recv_fd_buf(ctl->F, ctl_buf->buf, READSIZE, ctl_buf->F, 4);
399 ctl_buf->buflen = retlen;
400 if(retlen <= 0)
401 {
402 rb_free(ctl_buf->buf);
403 rb_free(ctl_buf);
404 }
405 else
406 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->readq);
407 }
408 while(retlen > 0);
409
410 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
411 {
412 ws_dead(ctl);
413 return;
414 }
415 ws_process_cmd_recv(ctl);
416 rb_setselect(ctl->F, RB_SELECT_READ, ws_read_ctl, ctl);
417}
418
419static ws_ctl_t *
420which_wsockd(void)
421{
422 ws_ctl_t *ctl, *lowest = NULL;
423 rb_dlink_node *ptr;
424
425 RB_DLINK_FOREACH(ptr, wsock_daemons.head)
426 {
427 ctl = ptr->data;
428 if(ctl->dead)
429 continue;
430 if(ctl->shutdown)
431 continue;
432 if(lowest == NULL)
433 {
434 lowest = ctl;
435 continue;
436 }
437 if(ctl->cli_count < lowest->cli_count)
438 lowest = ctl;
439 }
440
441 return (lowest);
442}
443
444static void
445ws_write_ctl(rb_fde_t * F, void *data)
446{
447 ws_ctl_t *ctl = data;
448 ws_ctl_buf_t *ctl_buf;
449 rb_dlink_node *ptr, *next;
450 int retlen, x;
451
452 if(ctl->dead)
453 return;
454
455 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->writeq.head)
456 {
457 ctl_buf = ptr->data;
458 /* in theory unix sock_dgram shouldn't ever short write this.. */
459 retlen = rb_send_fd_buf(ctl->F, ctl_buf->F, ctl_buf->nfds, ctl_buf->buf, ctl_buf->buflen, ctl->pid);
460 if(retlen > 0)
461 {
462 rb_dlinkDelete(ptr, &ctl->writeq);
463 for(x = 0; x < ctl_buf->nfds; x++)
464 rb_close(ctl_buf->F[x]);
465 rb_free(ctl_buf->buf);
466 rb_free(ctl_buf);
467
468 }
469 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
470 {
471 ws_dead(ctl);
472 return;
473 }
474 else
475 {
476 rb_setselect(ctl->F, RB_SELECT_WRITE, ws_write_ctl, ctl);
477 }
478 }
479}
480
481static void
482ws_cmd_write_queue(ws_ctl_t * ctl, rb_fde_t ** F, int count, const void *buf, size_t buflen)
483{
484 ws_ctl_buf_t *ctl_buf;
485 int x;
486
487 /* don't bother */
488 if(ctl->dead)
489 return;
490
491 ctl_buf = rb_malloc(sizeof(ws_ctl_buf_t));
492 ctl_buf->buf = rb_malloc(buflen);
493 memcpy(ctl_buf->buf, buf, buflen);
494 ctl_buf->buflen = buflen;
495
496 for(x = 0; x < count && x < MAXPASSFD; x++)
497 {
498 ctl_buf->F[x] = F[x];
499 }
500 ctl_buf->nfds = count;
501 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->writeq);
502 ws_write_ctl(ctl->F, ctl);
503}
504
505ws_ctl_t *
506start_wsockd_accept(rb_fde_t * sslF, rb_fde_t * plainF, uint32_t id)
507{
508 rb_fde_t *F[2];
509 ws_ctl_t *ctl;
510 char buf[5];
511 F[0] = sslF;
512 F[1] = plainF;
513
514 buf[0] = 'A';
515 uint32_to_buf(&buf[1], id);
516 ctl = which_wsockd();
517 if(!ctl)
518 return NULL;
519 ctl->cli_count++;
520 ws_cmd_write_queue(ctl, F, 2, buf, sizeof(buf));
521 return ctl;
522}
523
524void
525wsockd_decrement_clicount(ws_ctl_t * ctl)
526{
527 if(ctl == NULL)
528 return;
529
530 ctl->cli_count--;
531 if(ctl->shutdown && !ctl->cli_count)
532 {
533 ctl->dead = 1;
534 rb_kill(ctl->pid, SIGKILL);
535 }
536 if(ctl->dead && !ctl->cli_count)
537 {
538 free_ws_daemon(ctl);
539 }
540}
541
542static void
543cleanup_dead_ws(void *unused)
544{
545 rb_dlink_node *ptr, *next;
546 ws_ctl_t *ctl;
547 RB_DLINK_FOREACH_SAFE(ptr, next, wsock_daemons.head)
548 {
549 ctl = ptr->data;
550 if(ctl->dead && !ctl->cli_count)
551 {
552 free_ws_daemon(ctl);
553 }
554 }
555}
556
557int
558get_wsockd_count(void)
559{
560 return wsockd_count;
561}
562
563void
564wsockd_foreach_info(void (*func)(void *data, pid_t pid, int cli_count, enum wsockd_status status), void *data)
565{
566 rb_dlink_node *ptr, *next;
567 ws_ctl_t *ctl;
568 RB_DLINK_FOREACH_SAFE(ptr, next, wsock_daemons.head)
569 {
570 ctl = ptr->data;
571 func(data, ctl->pid, ctl->cli_count,
572 ctl->dead ? WSOCKD_DEAD :
573 (ctl->shutdown ? WSOCKD_SHUTDOWN : WSOCKD_ACTIVE));
574 }
575}
576
577void
578init_wsockd(void)
579{
580 rb_event_addish("cleanup_dead_ws", cleanup_dead_ws, NULL, 60);
581}