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