]> jfr.im git - solanum.git/blame - ircd/wsproc.c
support RSFNC indicating type of FNC (e.g. FORCE vs REGAIN) (#406)
[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 }
f085388a
U
325 ilog(L_MAIN, "wsockd helper started");
326 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "wsockd helper started");
c53ca1e0
AC
327 return started;
328}
329
330static void
331ws_process_dead_fd(ws_ctl_t * ctl, ws_ctl_buf_t * ctl_buf)
332{
333 struct Client *client_p;
334 char reason[256];
335 uint32_t fd;
336
337 if(ctl_buf->buflen < 6)
338 return; /* bogus message..drop it.. XXX should warn here */
339
340 fd = buf_to_uint32(&ctl_buf->buf[1]);
341 rb_strlcpy(reason, &ctl_buf->buf[5], sizeof(reason));
342 client_p = find_cli_connid_hash(fd);
343 if(client_p == NULL)
344 return;
345 if(IsAnyServer(client_p) || IsRegistered(client_p))
346 {
347 /* read any last moment ERROR, QUIT or the like -- jilles */
348 if (!strcmp(reason, "Remote host closed the connection"))
349 read_packet(client_p->localClient->F, client_p);
350 if (IsAnyDead(client_p))
351 return;
352 }
353 exit_client(client_p, client_p, &me, reason);
354}
355
356
357static void
358ws_process_cmd_recv(ws_ctl_t * ctl)
359{
c53ca1e0
AC
360 rb_dlink_node *ptr, *next;
361 ws_ctl_buf_t *ctl_buf;
c53ca1e0
AC
362
363 if(ctl->dead)
364 return;
365
366 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->readq.head)
367 {
368 ctl_buf = ptr->data;
369 switch (*ctl_buf->buf)
370 {
371 case 'D':
372 ws_process_dead_fd(ctl, ctl_buf);
373 break;
374 default:
bccb7ded 375 ilog(L_MAIN, "Received invalid command from wsockd: %s", ctl_buf->buf);
a9227555 376 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Received invalid command from wsockd");
c53ca1e0
AC
377 break;
378 }
379 rb_dlinkDelete(ptr, &ctl->readq);
380 rb_free(ctl_buf->buf);
381 rb_free(ctl_buf);
382 }
383
384}
385
386
387static void
388ws_read_ctl(rb_fde_t * F, void *data)
389{
390 ws_ctl_buf_t *ctl_buf;
391 ws_ctl_t *ctl = data;
392 int retlen;
393
394 if(ctl->dead)
395 return;
396 do
397 {
398 ctl_buf = rb_malloc(sizeof(ws_ctl_buf_t));
399 ctl_buf->buf = rb_malloc(READSIZE);
400 retlen = rb_recv_fd_buf(ctl->F, ctl_buf->buf, READSIZE, ctl_buf->F, 4);
401 ctl_buf->buflen = retlen;
402 if(retlen <= 0)
403 {
404 rb_free(ctl_buf->buf);
405 rb_free(ctl_buf);
406 }
407 else
408 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->readq);
409 }
410 while(retlen > 0);
411
412 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
413 {
414 ws_dead(ctl);
415 return;
416 }
417 ws_process_cmd_recv(ctl);
418 rb_setselect(ctl->F, RB_SELECT_READ, ws_read_ctl, ctl);
419}
420
421static ws_ctl_t *
422which_wsockd(void)
423{
424 ws_ctl_t *ctl, *lowest = NULL;
425 rb_dlink_node *ptr;
426
427 RB_DLINK_FOREACH(ptr, wsock_daemons.head)
428 {
429 ctl = ptr->data;
430 if(ctl->dead)
431 continue;
432 if(ctl->shutdown)
433 continue;
434 if(lowest == NULL)
435 {
436 lowest = ctl;
437 continue;
438 }
439 if(ctl->cli_count < lowest->cli_count)
440 lowest = ctl;
441 }
442
443 return (lowest);
444}
445
446static void
447ws_write_ctl(rb_fde_t * F, void *data)
448{
449 ws_ctl_t *ctl = data;
450 ws_ctl_buf_t *ctl_buf;
451 rb_dlink_node *ptr, *next;
452 int retlen, x;
453
454 if(ctl->dead)
455 return;
456
457 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->writeq.head)
458 {
459 ctl_buf = ptr->data;
460 /* in theory unix sock_dgram shouldn't ever short write this.. */
461 retlen = rb_send_fd_buf(ctl->F, ctl_buf->F, ctl_buf->nfds, ctl_buf->buf, ctl_buf->buflen, ctl->pid);
462 if(retlen > 0)
463 {
464 rb_dlinkDelete(ptr, &ctl->writeq);
465 for(x = 0; x < ctl_buf->nfds; x++)
466 rb_close(ctl_buf->F[x]);
467 rb_free(ctl_buf->buf);
468 rb_free(ctl_buf);
469
470 }
471 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
472 {
473 ws_dead(ctl);
474 return;
475 }
476 else
477 {
478 rb_setselect(ctl->F, RB_SELECT_WRITE, ws_write_ctl, ctl);
479 }
480 }
481}
482
483static void
484ws_cmd_write_queue(ws_ctl_t * ctl, rb_fde_t ** F, int count, const void *buf, size_t buflen)
485{
486 ws_ctl_buf_t *ctl_buf;
487 int x;
488
489 /* don't bother */
490 if(ctl->dead)
491 return;
492
493 ctl_buf = rb_malloc(sizeof(ws_ctl_buf_t));
494 ctl_buf->buf = rb_malloc(buflen);
495 memcpy(ctl_buf->buf, buf, buflen);
496 ctl_buf->buflen = buflen;
497
498 for(x = 0; x < count && x < MAXPASSFD; x++)
499 {
500 ctl_buf->F[x] = F[x];
501 }
502 ctl_buf->nfds = count;
503 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->writeq);
504 ws_write_ctl(ctl->F, ctl);
505}
506
507ws_ctl_t *
508start_wsockd_accept(rb_fde_t * sslF, rb_fde_t * plainF, uint32_t id)
509{
510 rb_fde_t *F[2];
511 ws_ctl_t *ctl;
512 char buf[5];
513 F[0] = sslF;
514 F[1] = plainF;
515
516 buf[0] = 'A';
517 uint32_to_buf(&buf[1], id);
518 ctl = which_wsockd();
519 if(!ctl)
520 return NULL;
521 ctl->cli_count++;
522 ws_cmd_write_queue(ctl, F, 2, buf, sizeof(buf));
523 return ctl;
524}
525
526void
527wsockd_decrement_clicount(ws_ctl_t * ctl)
528{
529 if(ctl == NULL)
530 return;
531
532 ctl->cli_count--;
533 if(ctl->shutdown && !ctl->cli_count)
534 {
535 ctl->dead = 1;
536 rb_kill(ctl->pid, SIGKILL);
537 }
538 if(ctl->dead && !ctl->cli_count)
539 {
540 free_ws_daemon(ctl);
541 }
542}
543
544static void
545cleanup_dead_ws(void *unused)
546{
547 rb_dlink_node *ptr, *next;
548 ws_ctl_t *ctl;
549 RB_DLINK_FOREACH_SAFE(ptr, next, wsock_daemons.head)
550 {
551 ctl = ptr->data;
552 if(ctl->dead && !ctl->cli_count)
553 {
554 free_ws_daemon(ctl);
555 }
556 }
557}
558
559int
560get_wsockd_count(void)
561{
562 return wsockd_count;
563}
564
565void
566wsockd_foreach_info(void (*func)(void *data, pid_t pid, int cli_count, enum wsockd_status status), void *data)
567{
568 rb_dlink_node *ptr, *next;
569 ws_ctl_t *ctl;
570 RB_DLINK_FOREACH_SAFE(ptr, next, wsock_daemons.head)
571 {
572 ctl = ptr->data;
573 func(data, ctl->pid, ctl->cli_count,
574 ctl->dead ? WSOCKD_DEAD :
575 (ctl->shutdown ? WSOCKD_SHUTDOWN : WSOCKD_ACTIVE));
576 }
577}
578
579void
580init_wsockd(void)
581{
582 rb_event_addish("cleanup_dead_ws", cleanup_dead_ws, NULL, 60);
583}