]> jfr.im git - solanum.git/blob - ircd/wsproc.c
Support more human friendly k/d/x-line duration format
[solanum.git] / ircd / wsproc.c
1 /*
2 * sslproc.c: An interface to wsockd
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
37 static void ws_read_ctl(rb_fde_t * F, void *data);
38 static int wsockd_count;
39
40 #define MAXPASSFD 4
41 #define READSIZE 1024
42 typedef 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
52 struct 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
65 static rb_dlink_list wsock_daemons;
66
67 static inline uint32_t
68 buf_to_uint32(char *buf)
69 {
70 uint32_t x;
71 memcpy(&x, buf, sizeof(x));
72 return x;
73 }
74
75 static inline void
76 uint32_to_buf(char *buf, uint32_t x)
77 {
78 memcpy(buf, &x, sizeof(x));
79 return;
80 }
81
82 static ws_ctl_t *
83 allocate_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
98 static void
99 free_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
132 static char *wsockd_path;
133
134 static int wsockd_spin_count = 0;
135 static time_t last_spin;
136 static int wsockd_wait = 0;
137
138 void
139 restart_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
163 #if 0
164 static void
165 ws_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 }
182 #endif
183
184 static void
185 ws_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");
197 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "wsockd helper died - attempting to restart");
198 start_wsockd(1);
199 }
200 }
201
202 static void
203 ws_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
216 static void
217 restart_wsockd_event(void *unused)
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");
226 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Attempting to restart wsockd processes");
227 start_wsockd(start);
228 }
229 }
230
231 int
232 start_wsockd(int count)
233 {
234 rb_fde_t *F1, *F2;
235 rb_fde_t *P1, *P2;
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 {
249 ilog(L_MAIN, "wsockd helper is spinning - will attempt to restart in 1 minute");
250 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
251 "wsockd helper is spinning - will attempt to restart in 1 minute");
252 rb_event_add("restart_wsockd_event", restart_wsockd_event, NULL, 60);
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 {
262 snprintf(fullpath, sizeof(fullpath), "%s/wsockd", ircd_paths[IRCD_PATH_LIBEXEC]);
263
264 if(access(fullpath, X_OK) == -1)
265 {
266 snprintf(fullpath, sizeof(fullpath), "%s/bin/wsockd", ConfigFileEntry.dpath);
267 if(access(fullpath, X_OK) == -1)
268 {
269 ilog(L_MAIN,
270 "Unable to execute wsockd in %s or %s/bin",
271 ircd_paths[IRCD_PATH_LIBEXEC], ConfigFileEntry.dpath);
272 return 0;
273 }
274 }
275 wsockd_path = rb_strdup(fullpath);
276 }
277 rb_strlcpy(buf, "-ircd wsockd daemon", sizeof(buf));
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
304 rb_clear_cloexec(F2);
305 rb_clear_cloexec(P1);
306
307 pid = rb_spawn_process(wsockd_path, (const char **) parv);
308 if(pid == -1)
309 {
310 ilog(L_MAIN, "Unable to create wsockd: %s\n", strerror(errno));
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
328 static void
329 ws_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
355 static void
356 ws_process_cmd_recv(ws_ctl_t * ctl)
357 {
358 rb_dlink_node *ptr, *next;
359 ws_ctl_buf_t *ctl_buf;
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:
373 ilog(L_MAIN, "Received invalid command from wsockd: %s", ctl_buf->buf);
374 sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Received invalid command from wsockd");
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
385 static void
386 ws_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
419 static ws_ctl_t *
420 which_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
444 static void
445 ws_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
481 static void
482 ws_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
505 ws_ctl_t *
506 start_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
524 void
525 wsockd_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
542 static void
543 cleanup_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
557 int
558 get_wsockd_count(void)
559 {
560 return wsockd_count;
561 }
562
563 void
564 wsockd_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
577 void
578 init_wsockd(void)
579 {
580 rb_event_addish("cleanup_dead_ws", cleanup_dead_ws, NULL, 60);
581 }