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 (20 years, 1 month ago) by michael
Content type: text/x-csrc
File size: 15197 byte(s)
Log Message:
- Imported contrib/

File Contents

# User Rev Content
1 michael 812 /************************************************************************
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"