ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/contrib/soft_reboot.c
Revision: 812
Committed: Thu Sep 7 09:41:54 2006 UTC (17 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 15197 byte(s)
Log Message:
- Imported contrib/

File Contents

# Content
1 /************************************************************************
2 * IRC - Internet Relay Chat, contrib/soft_reboot.c
3 * Copyright (C) 2006 Hybrid Development Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * $Id$
20 */
21
22 #include "stdinc.h"
23 #include "conf/conf.h"
24 #include "channel.h"
25 #include "channel_mode.h"
26 #include "client.h"
27 #include "hash.h"
28 #include "ircd.h"
29 #include "ircd_defs.h"
30 #include "listener.h"
31 #include "numeric.h"
32 #include "packet.h"
33 #include "parse.h"
34 #include "restart.h"
35 #include "server.h"
36 #include "user.h"
37 #include "send.h"
38 #include "userhost.h"
39
40 #ifdef HAVE_LIBCRYPTO
41 #define CanForward(x) (!IsDefunct(x) && !(x)->localClient->fd.ssl)
42 #else
43 #define CanForward(x) (!IsDefunct(x))
44 #endif
45
46 static dlink_node *h_shutdown, *h_verify;
47
48 struct SocketInfo
49 {
50 int fd;
51 int ctrlfd;
52 int namelen;
53 int pwdlen;
54 int caplen;
55 int recvqlen;
56 int sendqlen;
57 int slinkqofs;
58 int slinkqlen;
59 time_t first;
60 time_t last;
61 };
62
63 /*
64 * serverize()
65 *
66 * Turns a dummy client into a server.
67 *
68 * inputs: client pointer
69 * output: none
70 */
71 static void
72 serverize(struct Client *client_p)
73 {
74 struct ConnectConf *sconf = MyMalloc(sizeof(*sconf));
75 char *hub_mask;
76
77 DupString(sconf->name, client_p->name);
78 sconf->flags = LINK_BURSTAWAY | LINK_TOPICBURST;
79
80 DupString(hub_mask, "*");
81 dlinkAdd(hub_mask, make_dlink_node(), &sconf->hub_list);
82
83 client_p->serv->sconf = ref_link_by_ptr(sconf);
84
85 SetServer(client_p);
86 }
87
88 /*
89 * make_dummy()
90 *
91 * Prepares a dummy server-alike local client.
92 *
93 * inputs: file descriptor to use
94 * output: client pointer
95 */
96 static struct Client *
97 make_dummy(int transfd)
98 {
99 dlink_node *m;
100 struct Client *client_p = make_client(NULL);
101
102 fd_open(&client_p->localClient->fd, transfd, 1, "Softboot");
103 client_p->localClient->caps = -1;
104
105 strcpy(client_p->name, ".");
106 strcpy(client_p->id, "...");
107 hash_add_client(client_p);
108 hash_add_id(client_p);
109 dlinkAdd(client_p, &client_p->node, &global_client_list);
110
111 m = dlinkFind(&unknown_list, client_p);
112 dlinkDelete(m, &unknown_list);
113 dlinkAdd(client_p, m, &serv_list);
114 dlinkAdd(client_p, make_dlink_node(), &global_serv_list);
115
116 make_server(client_p);
117 serverize(client_p);
118
119 return client_p;
120 }
121
122 /*
123 * write_dbuf()
124 *
125 * Writes the contents of a dbuf to the given socket.
126 *
127 * inputs:
128 * transfd - where to write to
129 * dbuf - dbuf pointer
130 * output: none
131 */
132 static void
133 write_dbuf(int transfd, struct dbuf_queue *dbuf)
134 {
135 while (dbuf_length(dbuf) > 0)
136 {
137 struct dbuf_block *first = dbuf->blocks.head->data;
138
139 write(transfd, first->data, first->size);
140 dbuf_delete(dbuf, first->size);
141 }
142 }
143
144 /*
145 * introduce_socket()
146 *
147 * Encodes and writes socket information about the given client.
148 *
149 * inputs:
150 * transfd - where to write to
151 * client_p - local client to inform about
152 * output: none
153 */
154 static void
155 introduce_socket(int transfd, struct Client *client_p)
156 {
157 struct SocketInfo si;
158 const char *capabs = "";
159
160 if (!CanForward(client_p) || client_p->localClient->fd.fd == transfd)
161 return;
162
163 if (IsServer(client_p))
164 capabs = show_capabilities(client_p);
165
166 si.fd = client_p->localClient->fd.fd;
167 si.ctrlfd = client_p->localClient->ctrlfd.flags.open ?
168 client_p->localClient->ctrlfd.fd : -1;
169 si.namelen = strlen(client_p->name);
170 si.pwdlen = EmptyString(client_p->localClient->passwd) ? 0 :
171 strlen(client_p->localClient->passwd);
172 si.caplen = strlen(capabs);
173 si.recvqlen = dbuf_length(&client_p->localClient->buf_recvq);
174 si.sendqlen = dbuf_length(&client_p->localClient->buf_sendq);
175 si.slinkqofs = client_p->localClient->slinkq_ofs;
176 si.slinkqlen = client_p->localClient->slinkq_len;
177 si.first = client_p->firsttime;
178 si.last = client_p->localClient->last;
179
180 write(transfd, &si, sizeof(si));
181 write(transfd, client_p->name, si.namelen);
182 if (si.pwdlen > 0)
183 write(transfd, client_p->localClient->passwd, si.pwdlen);
184 if (si.caplen > 0)
185 write(transfd, capabs, si.caplen);
186
187 write_dbuf(transfd, &client_p->localClient->buf_recvq);
188 write_dbuf(transfd, &client_p->localClient->buf_sendq);
189 if (si.slinkqlen > 0)
190 write(transfd, client_p->localClient->slinkq, si.slinkqlen);
191 }
192
193 /*
194 * do_shutdown()
195 *
196 * Implements the /RESTART part of the soft reboot code.
197 *
198 * inputs:
199 * msg - shutdown message
200 * rboot - 1 if it's a restart, 0 if plain exit
201 * output: none
202 */
203 static void *
204 do_shutdown(va_list args)
205 {
206 const char *msg = va_arg(args, const char *);
207 int rboot = va_arg(args, int);
208 struct Client *client_p;
209 dlink_node *ptr;
210 int transfd[2];
211 char buf[24];
212
213 if (!rboot || socketpair(AF_UNIX, SOCK_STREAM, 0, transfd) < 0)
214 return pass_callback(h_shutdown, msg, rboot);
215
216 if (EmptyString(msg))
217 {
218 ilog(L_CRIT, "Server Soft-Rebooting");
219 sendto_realops_flags(UMODE_ALL, L_ALL, "Server Soft-Rebooting");
220 }
221 else
222 {
223 ilog(L_CRIT, "Server Soft-Rebooting: %s", msg);
224 sendto_realops_flags(UMODE_ALL, L_ALL, "Server Soft-Rebooting: %s", msg);
225 }
226
227 //
228 // Prevent all sockets which belong to registered users/servers from
229 // being closed on exec().
230 //
231 fcntl(transfd[0], F_SETFD, 0);
232
233 DLINK_FOREACH(ptr, local_client_list.head)
234 {
235 client_p = ptr->data;
236 if (CanForward(client_p))
237 {
238 fcntl(client_p->localClient->fd.fd, F_SETFD, 0);
239 if (client_p->localClient->list_task != NULL)
240 sendto_one(client_p, form_str(RPL_LISTEND), me.name, client_p->name);
241 }
242 }
243
244 DLINK_FOREACH(ptr, serv_list.head)
245 {
246 client_p = ptr->data;
247 if (CanForward(client_p))
248 fcntl(client_p->localClient->fd.fd, F_SETFD, 0);
249 }
250
251 close_listeners();
252 unlink(ServerState.pidfile);
253
254 //
255 // Start the new ircd.
256 //
257 switch (fork())
258 {
259 case -1:
260 ilog(L_CRIT, "Unable to fork(): %s", strerror(errno));
261 exit(1);
262
263 case 0:
264 {
265 int i;
266 char **argv;
267
268 close(transfd[1]);
269 snprintf(buf, sizeof(buf), "softboot_%d", transfd[0]);
270
271 for (i = 0; myargv[i] != NULL; i++);
272 argv = MyMalloc((i + 2) * sizeof(char *));
273
274 for (i = 0; myargv[i] != NULL; i++)
275 argv[i] = myargv[i];
276 argv[i++] = buf;
277 argv[i] = NULL;
278
279 execv(SPATH, argv);
280 ilog(L_CRIT, "Unable to exec(): %s", strerror(errno));
281 exit(1);
282 }
283 }
284
285 //
286 // Pass our data.
287 //
288 burst_all(make_dummy(transfd[1]));
289 send_queued_all();
290
291 snprintf(buf, sizeof(buf), "\001%ld\r\n", me.since);
292 write(transfd[1], buf, strlen(buf));
293
294 DLINK_FOREACH(ptr, local_client_list.head)
295 introduce_socket(transfd[1], ptr->data);
296
297 DLINK_FOREACH(ptr, serv_list.head)
298 introduce_socket(transfd[1], ptr->data);
299
300 exit(0);
301 }
302
303 /*
304 * restore_socket()
305 *
306 * Restores socket related fields in struct LocalUser.
307 *
308 * inputs:
309 * client_p - client pointer
310 * fd - file descriptor
311 * ctrlfd - servlink control fd
312 * first - when the client was created
313 * last - since when the client is idle
314 * output: none
315 */
316 static void
317 restore_socket(struct Client *client_p, int fd, int ctrlfd,
318 time_t first, time_t last)
319 {
320 char buf[HOSTLEN+16];
321 struct irc_ssaddr addr;
322 int family, port;
323
324 snprintf(buf, sizeof(buf), IsClient(client_p) ? "Nick: %s" :
325 (ctrlfd >= 0 ? "slink data: %s" : "Server: %s"),
326 client_p->name);
327
328 client_p->localClient = BlockHeapAlloc(lclient_heap);
329 attach_class(client_p, default_class);
330
331 fd_open(&client_p->localClient->fd, fd, 1, buf);
332 fcntl(fd, F_SETFD, FD_CLOEXEC);
333 if (ctrlfd >= 0)
334 {
335 snprintf(buf, sizeof(buf), "slink ctrl: %s", client_p->name);
336 fd_open(&client_p->localClient->ctrlfd, ctrlfd, 1, buf);
337 }
338
339 addr.ss_len = sizeof(addr);
340 getsockname(fd, (struct sockaddr *) &addr, &addr.ss_len);
341 family = addr.ss.sin_family;
342 port = ntohs(addr.ss.sin_port);
343 if (!(client_p->localClient->listener = find_listener(port, &addr)))
344 {
345 memset(&addr.ss, 0, sizeof(addr.ss));
346 addr.ss.sin_family = family;
347 addr.ss.sin_port = port;
348 client_p->localClient->listener = find_listener(port, &addr);
349 }
350
351 client_p->localClient->ip.ss_len = sizeof(client_p->localClient->ip.ss);
352 getpeername(fd, (struct sockaddr *) &client_p->localClient->ip,
353 &client_p->localClient->ip.ss_len);
354 client_p->localClient->aftype = client_p->localClient->ip.ss.sin_family;
355
356 irc_getnameinfo((struct sockaddr *) &client_p->localClient->ip,
357 client_p->localClient->ip.ss_len, client_p->sockhost,
358 sizeof(client_p->sockhost), NULL, 0, NI_NUMERICHOST);
359
360 client_p->servptr = &me;
361 client_p->since = client_p->lasttime = CurrentTime;
362 client_p->firsttime = first;
363 client_p->localClient->last = last;
364
365 client_p->localClient->allow_read = MAX_FLOOD;
366 comm_setflush(&client_p->localClient->fd, 1000, flood_recalc, client_p);
367
368 client_p->flags |= FLAGS_FINISHED_AUTH;
369 comm_setselect(&client_p->localClient->fd, COMM_SELECT_READ, read_packet,
370 client_p, 0);
371 }
372
373 /*
374 * restore_client()
375 * restore_server()
376 *
377 * Restores a local user/server entity.
378 *
379 * inputs:
380 * client_p - client pointer
381 * capabs - user/server capab string
382 * output: none
383 */
384 static void
385 restore_client(struct Client *client_p, char *capabs)
386 {
387 char userbuf[USERLEN+1];
388 struct Class *cptr;
389
390 if (client_p->username[0] != '~')
391 SetGotId(client_p);
392 strlcpy(userbuf, client_p->username + !IsGotId(client_p),
393 sizeof(userbuf));
394
395 if ((cptr = execute_callback(authorize_client, client_p, userbuf)))
396 attach_class(client_p, cptr);
397
398 Count.local++, Count.totalrestartcount++;
399 if (Count.local > Count.max_loc)
400 Count.max_loc = Count.local;
401
402 delete_user_host(client_p->username, client_p->host, 1);
403 add_user_host(client_p->username, client_p->host, 0);
404 SetUserHost(client_p);
405
406 dlinkAdd(client_p, &client_p->localClient->lclient_node, &local_client_list);
407 dlinkAdd(client_p, &client_p->lnode, &me.serv->client_list);
408 if (IsOper(client_p))
409 dlinkAdd(client_p, make_dlink_node(), &oper_list);
410 }
411
412 static void
413 restore_server(struct Client *client_p, char *capabs)
414 {
415 char *p = NULL, *s = NULL;
416 int cap;
417
418 SetGotId(client_p);
419
420 for (s = strtoken(&p, capabs, " "); s; s = strtoken(&p, NULL, " "))
421 if ((cap = find_capability(s)) != 0)
422 SetCapable(client_p, cap);
423
424 if (check_server(client_p->name, client_p, NO) != 0)
425 serverize(client_p);
426
427 set_chcap_usage_counts(client_p);
428 Count.myserver++;
429
430 dlinkAdd(client_p, &client_p->localClient->lclient_node, &serv_list);
431 dlinkAdd(client_p, &client_p->lnode, &me.serv->server_list);
432 }
433
434 /*
435 * discover_from()
436 *
437 * Returns correct value of a client's from field.
438 *
439 * inputs: client pointer
440 * output: from
441 */
442 static struct Client *
443 discover_from(struct Client *client_p)
444 {
445 return IsMe(client_p->servptr) ? client_p : discover_from(client_p->servptr);
446 }
447
448 /*
449 * restore_dbuf()
450 *
451 * Loads a dbuf contents from a file stream.
452 *
453 * inputs:
454 * f - file stream
455 * dbuf - dbuf to fill or NULL
456 * cnt - number of bytes to read
457 * output: none
458 */
459 static void
460 restore_dbuf(FILE *f, struct dbuf_queue *dbuf, int cnt)
461 {
462 while (cnt > 0)
463 {
464 int nread = fread(readBuf, 1, LIBIO_MIN(sizeof(readBuf), cnt), f);
465
466 if (dbuf != NULL)
467 dbuf_put(dbuf, readBuf, nread);
468 cnt -= nread;
469 }
470 }
471
472 /*
473 * load_state()
474 *
475 * Loads ircd state from a socket stream.
476 *
477 * inputs: socket fd
478 * output: none
479 */
480 static void
481 load_state(int transfd)
482 {
483 FILE *f = fdopen(transfd, "r");
484 char buf[IRCD_BUFSIZE+1], *p, *parv[4] = {NULL, NULL, "-o", NULL};
485 struct Client *client_p;
486 struct SocketInfo si;
487 dlink_node *ptr, *ptr_next;
488
489 //
490 // Read server burst from &me
491 //
492 fd_open(&me.localClient->fd, transfd, 1, "Softboot");
493 serverize(&me);
494
495 while (fgets(buf, sizeof(buf), f))
496 {
497 if ((p = strpbrk(buf, "\r\n")) != NULL)
498 *p = 0;
499 if (buf[0] == '\001')
500 {
501 me.since = atoi(buf + 1);
502 break;
503 }
504 parse(&me, buf, buf + strlen(buf));
505 }
506
507 SetMe(&me);
508 unref_link(me.serv->sconf);
509 me.serv->sconf = NULL;
510
511 fd_close(&me.localClient->fd);
512
513 //
514 // Read local client information
515 //
516 while (fread(&si, sizeof(si), 1, f) == 1)
517 {
518 if (si.fd == -1)
519 break;
520
521 assert(si.namelen < IRCD_BUFSIZE);
522 assert(si.caplen < IRCD_BUFSIZE);
523
524 fread(buf, 1, si.namelen, f);
525 buf[si.namelen] = 0;
526
527 if ((client_p = find_client(buf)) != NULL)
528 restore_socket(client_p, si.fd, si.ctrlfd, si.first, si.last);
529 else
530 close(si.fd);
531
532 fread(buf, 1, si.pwdlen, f);
533 if (client_p != NULL && si.pwdlen > 0)
534 {
535 buf[si.pwdlen] = 0;
536 DupString(client_p->localClient->passwd, buf);
537 }
538
539 fread(buf, 1, si.caplen, f);
540 buf[si.caplen] = 0;
541
542 if (client_p != NULL)
543 (IsServer(client_p) ? restore_server : restore_client) (client_p, buf);
544
545 restore_dbuf(f, client_p ? &client_p->localClient->buf_recvq : NULL,
546 si.recvqlen);
547 restore_dbuf(f, client_p ? &client_p->localClient->buf_sendq : NULL,
548 si.sendqlen);
549
550 if (si.slinkqlen > 0)
551 {
552 client_p->localClient->slinkq = MyMalloc(si.slinkqlen);
553 fread(client_p->localClient->slinkq, 1, si.slinkqlen, f);
554 client_p->localClient->slinkq_ofs = si.slinkqofs;
555 client_p->localClient->slinkq_len = si.slinkqlen;
556 }
557 }
558
559 //
560 // Finalization
561 //
562 DLINK_FOREACH_SAFE(ptr, ptr_next, global_client_list.head)
563 {
564 client_p = ptr->data;
565 client_p->from = discover_from(client_p);
566 if (client_p->from == client_p && !client_p->localClient)
567 {
568 SetDead(client_p);
569 client_p->localClient = BlockHeapAlloc(lclient_heap);
570 exit_client(client_p, &me, "Soft reboot");
571 }
572 }
573
574 while (oper_list.head != NULL)
575 {
576 client_p = oper_list.head->data;
577 parv[0] = parv[1] = client_p->name;
578 set_user_mode(client_p, client_p, 4, parv);
579 }
580
581 fclose(f);
582 send_queued_all();
583 }
584
585 /*
586 * do_verify_conf()
587 *
588 * Handles the conf verification callback.
589 *
590 * inputs: none
591 * output: none
592 */
593 static void *
594 do_verify_conf(va_list args)
595 {
596 pass_callback(h_verify);
597
598 if (conf_cold)
599 {
600 int i;
601
602 for (i = 0; myargv[i] != NULL; i++)
603 if (!ircncmp(myargv[i], "softboot_", 9))
604 {
605 load_state(atoi(myargv[i] + 9));
606 myargv[i] = NULL;
607 }
608 }
609
610 return NULL;
611 }
612
613 INIT_MODULE(soft_reboot, "$Revision$")
614 {
615 h_shutdown = install_hook(ircd_shutdown, do_shutdown);
616 h_verify = install_hook(verify_conf, do_verify_conf);
617 }
618
619 CLEANUP_MODULE
620 {
621 uninstall_hook(verify_conf, do_verify_conf);
622 uninstall_hook(ircd_shutdown, do_shutdown);
623 }

Properties

Name Value
svn:eol-style native
svn:keywords "Id Revision"