ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/contrib/soft_reboot.c
Revision: 775
Committed: Wed Aug 23 07:46:06 2006 UTC (20 years, 1 month ago) by adx
Content type: text/x-csrc
File size: 11326 byte(s)
Error occurred while calculating annotation data.
Log Message:
.

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 "s_serv.h"
36 #include "s_user.h"
37 #include "send.h"
38
39 static dlink_node *h_shutdown, *h_verify;
40
41 struct SocketInfo
42 {
43 int fd;
44 int namelen;
45 int caplen;
46 int recvqlen;
47 int sendqlen;
48 };
49
50 /*
51 * make_dummy()
52 *
53 * Prepares a dummy server-alike local client.
54 *
55 * inputs: file descriptor to use
56 * output: client pointer
57 */
58 static struct Client *
59 make_dummy(int transfd)
60 {
61 dlink_node *m;
62 struct Client *client_p = make_client(NULL);
63
64 fd_open(&client_p->localClient->fd, transfd, 1, "Softboot");
65 client_p->localClient->caps = -1;
66
67 strcpy(client_p->name, ".");
68 strcpy(client_p->id, "...");
69 dlinkAdd(client_p, &client_p->node, &global_client_list);
70
71 m = dlinkFind(&unknown_list, client_p);
72 dlinkDelete(m, &unknown_list);
73 dlinkAdd(client_p, m, &serv_list);
74 dlinkAdd(client_p, make_dlink_node(), &global_serv_list);
75
76 make_server(client_p);
77 SetServer(client_p);
78
79 return client_p;
80 }
81
82 /*
83 * write_dbuf()
84 *
85 * Writes the contents of a dbuf to the given socket.
86 *
87 * inputs:
88 * transfd - where to write to
89 * dbuf - dbuf pointer
90 * output: none
91 */
92 static void
93 write_dbuf(int transfd, struct dbuf_queue *dbuf)
94 {
95 while (dbuf_length(dbuf) > 0)
96 {
97 struct dbuf_block *first = dbuf->blocks.head->data;
98
99 write(transfd, first->data, first->size);
100 dbuf_delete(dbuf, first->size);
101 }
102 }
103
104 /*
105 * introduce_socket()
106 *
107 * Encodes and writes socket information about the given client.
108 *
109 * inputs:
110 * transfd - where to write to
111 * client_p - local client to inform about
112 * output: none
113 */
114 static void
115 introduce_socket(int transfd, struct Client *client_p)
116 {
117 struct SocketInfo si;
118 const char *capabs = "";
119
120 if (IsDefunct(client_p))
121 return;
122
123 if (IsServer(client_p))
124 capabs = show_capabilities(client_p);
125
126 si.fd = client_p->localClient->fd.fd;
127 si.namelen = strlen(client_p->name);
128 si.caplen = strlen(capabs);
129 si.recvqlen = dbuf_length(&client_p->localClient->buf_recvq);
130 si.sendqlen = dbuf_length(&client_p->localClient->buf_sendq);
131
132 write(transfd, &si, sizeof(si));
133 write(transfd, client_p->name, si.namelen);
134 write(transfd, capabs, si.caplen);
135
136 write_dbuf(transfd, &client_p->localClient->buf_recvq);
137 write_dbuf(transfd, &client_p->localClient->buf_sendq);
138 }
139
140 /*
141 * do_shutdown()
142 *
143 * Implements the /RESTART part of the soft reboot code.
144 *
145 * inputs:
146 * msg - shutdown message
147 * rboot - 1 if it's a restart, 0 if plain exit
148 * output: none
149 */
150 static void *
151 do_shutdown(va_list args)
152 {
153 const char *msg = va_arg(args, const char *);
154 int rboot = va_arg(args, int);
155 struct Client *client_p;
156 dlink_node *ptr;
157 int transfd[2];
158
159 if (!rboot || socketpair(AF_UNIX, SOCK_STREAM, 0, transfd) < 0)
160 return pass_callback(h_shutdown, msg, rboot);
161
162 if (EmptyString(msg))
163 {
164 ilog(L_CRIT, "Server Soft-Rebooting");
165 sendto_realops_flags(UMODE_ALL, L_ALL, "Server Soft-Rebooting");
166 }
167 else
168 {
169 ilog(L_CRIT, "Server Soft-Rebooting: %s", msg);
170 sendto_realops_flags(UMODE_ALL, L_ALL, "Server Soft-Rebooting: %s", msg);
171 }
172
173 //
174 // Prevent all sockets which belong to registered users/servers from
175 // being closed on exec().
176 //
177 fcntl(transfd[0], F_SETFD, 0);
178
179 DLINK_FOREACH(ptr, local_client_list.head)
180 {
181 client_p = ptr->data;
182 if (!IsDefunct(client_p))
183 {
184 fcntl(client_p->localClient->fd.fd, F_SETFD, 0);
185 if (client_p->localClient->list_task != NULL)
186 sendto_one(client_p, form_str(RPL_LISTEND), me.name, client_p->name);
187 }
188 }
189
190 DLINK_FOREACH(ptr, serv_list.head)
191 {
192 client_p = ptr->data;
193 if (!IsDefunct(client_p))
194 fcntl(client_p->localClient->fd.fd, F_SETFD, 0);
195 }
196
197 //
198 // Start the new ircd.
199 //
200 switch (fork())
201 {
202 case -1:
203 ilog(L_CRIT, "Unable to fork(): %s", strerror(errno));
204 exit(1);
205
206 case 0:
207 {
208 int i;
209 char **argv, buf[24];
210
211 snprintf(buf, sizeof(buf), "softboot_%d", transfd[0]);
212
213 for (i = 0; myargv[i] != NULL; i++);
214 argv = MyMalloc((i + 2) * sizeof(char *));
215
216 for (i = 0; myargv[i] != NULL; i++)
217 argv[i] = myargv[i];
218 argv[i++] = buf;
219 argv[i] = NULL;
220
221 execv(SPATH, argv);
222 ilog(L_CRIT, "Unable to execve(): %s", strerror(errno));
223 exit(1);
224 }
225 }
226
227 //
228 // Pass our data.
229 //
230 burst_all(make_dummy(transfd[1]));
231
232 write(transfd[1], "\001\r\n", 3);
233
234 DLINK_FOREACH(ptr, local_client_list.head)
235 introduce_socket(transfd[1], ptr->data);
236
237 DLINK_FOREACH(ptr, serv_list.head)
238 introduce_socket(transfd[1], ptr->data);
239
240 exit(0);
241 }
242
243 /*
244 * restore_socket()
245 *
246 * Restores socket related fields in struct LocalUser.
247 *
248 * inputs:
249 * client_p - client pointer
250 * fd - file descriptor
251 * output: the same pointer or NULL if exited
252 */
253 static struct Client *
254 restore_socket(struct Client *client_p, int fd)
255 {
256 char buf[HOSTLEN+10];
257 struct irc_ssaddr addr;
258 int family, port;
259
260 snprintf(buf, sizeof(buf), IsServer(client_p) ? "Server: %s" : "Nick: %s",
261 client_p->name);
262
263 client_p->localClient = BlockHeapAlloc(lclient_heap);
264 fd_open(&client_p->localClient->fd, fd, 1, buf);
265 fcntl(fd, F_SETFD, FD_CLOEXEC);
266
267 addr.ss_len = sizeof(addr);
268 getsockname(fd, (struct sockaddr *) &addr, &addr.ss_len);
269 family = addr.ss.sin_family;
270 port = ntohs(addr.ss.sin_port);
271
272 if (!(client_p->localClient->listener = find_listener(port, &addr)))
273 {
274 memset(&addr.ss, 0, sizeof(addr.ss));
275 addr.ss.sin_family = family;
276 addr.ss.sin_port = htons(port);
277
278 if (!(client_p->localClient->listener = find_listener(port, &addr)))
279 {
280 exit_client(client_p, &me, "Listener closed");
281 return NULL;
282 }
283 }
284
285 client_p->localClient->ip.ss_len = sizeof(client_p->localClient->ip.ss);
286 getpeername(fd, (struct sockaddr *) &client_p->localClient->ip,
287 &client_p->localClient->ip.ss_len);
288 client_p->localClient->aftype = client_p->localClient->ip.ss.sin_family;
289
290 if (irc_getnameinfo((struct sockaddr *) &client_p->localClient->ip,
291 client_p->localClient->ip.ss_len, client_p->sockhost,
292 sizeof(client_p->sockhost), NULL, 0,
293 NI_NUMERICHOST) != 0)
294 {
295 exit_client(client_p, &me, "Protocol family no longer supported");
296 return NULL;
297 }
298
299 return client_p;
300 }
301
302 /*
303 * restore_client()
304 * restore_server()
305 *
306 * Restores a local user/server entity.
307 *
308 * inputs:
309 * client_p - client pointer
310 * capabs - user/server capab string
311 * output: the same pointer or NULL if exited
312 */
313 static struct Client *
314 restore_client(struct Client *client_p, char *capabs)
315 {
316 char userbuf[USERLEN+1];
317 struct Class *cptr;
318
319 if (client_p->username[0] != '~')
320 SetGotId(client_p);
321 strlcpy(userbuf, client_p->username + !IsGotId(client_p),
322 sizeof(userbuf));
323
324 if (!(cptr = execute_callback(authorize_client, client_p, userbuf)))
325 return NULL;
326 attach_class(client_p, cptr);
327
328 Count.local++, Count.totalrestartcount++;
329 if (Count.local > Count.max_loc)
330 Count.max_loc = Count.local;
331
332 dlinkAdd(client_p, &client_p->localClient->lclient_node, &local_client_list);
333 dlinkAdd(client_p, &client_p->lnode, &me.serv->client_list);
334 return client_p;
335 }
336
337 static struct Client *
338 restore_server(struct Client *client_p, char *capabs)
339 {
340 char *p = NULL, *s = NULL;
341 int cap;
342
343 SetGotId(client_p);
344
345 for (s = strtoken(&p, capabs, " "); s; s = strtoken(&p, NULL, " "))
346 if ((cap = find_capability(s)) != 0)
347 SetCapable(client_p, cap);
348
349 set_chcap_usage_counts(client_p);
350 Count.myserver++;
351
352 dlinkAdd(client_p, &client_p->localClient->lclient_node, &serv_list);
353 dlinkAdd(client_p, &client_p->lnode, &me.serv->server_list);
354 return client_p;
355 }
356
357 /*
358 * restore_dbuf()
359 *
360 * Loads a dbuf contents from a file stream.
361 *
362 * inputs:
363 * f - file stream
364 * dbuf - dbuf to fill or NULL
365 * cnt - number of bytes to read
366 * output: none
367 */
368 static void
369 restore_dbuf(FILE *f, struct dbuf_queue *dbuf, int cnt)
370 {
371 while (cnt > 0)
372 {
373 int nread = fread(readBuf, 1, sizeof(readBuf), f);
374
375 if (dbuf != NULL)
376 dbuf_put(dbuf, readBuf, nread);
377 cnt -= nread;
378 }
379 }
380
381 /*
382 * load_state()
383 *
384 * Loads ircd state from a socket stream.
385 *
386 * inputs: socket fd
387 * output: none
388 */
389 static void
390 load_state(int transfd)
391 {
392 FILE *f = fdopen(transfd, "r");
393 char buf[IRCD_BUFSIZE+1], *p;
394 struct Client *dummy = make_dummy(transfd);
395 struct Client *client_p;
396 struct SocketInfo si;
397
398 while (fgets(buf, sizeof(buf), f))
399 {
400 if ((p = strpbrk(buf, "\r\n")) != NULL)
401 *p = 0;
402 if (buf[0] == '\001')
403 break;
404 parse(dummy, buf, buf + strlen(buf));
405 }
406
407 SetDead(dummy);
408
409 while (fread(&si, sizeof(si), 1, f) == 1)
410 {
411 assert(si.namelen < IRCD_BUFSIZE);
412 assert(si.caplen < IRCD_BUFSIZE);
413
414 fread(buf, 1, si.namelen, f);
415 buf[si.namelen] = 0;
416
417 if ((client_p = find_client(buf)) != NULL)
418 {
419 client_p->servptr = &me;
420 client_p->from = client_p;
421 client_p = restore_socket(client_p, si.fd);
422 }
423 else
424 close(si.fd);
425
426 fread(buf, 1, si.caplen, f);
427 buf[si.caplen] = 0;
428
429 if (client_p != NULL)
430 client_p = (IsServer(client_p) ? restore_server : restore_client)
431 (client_p, buf);
432
433 restore_dbuf(f, client_p ? &client_p->localClient->buf_recvq : NULL,
434 si.recvqlen);
435 restore_dbuf(f, client_p ? &client_p->localClient->buf_sendq : NULL,
436 si.sendqlen);
437 }
438
439 exit_one_client(dummy, "Soft reboot");
440 fclose(f);
441 }
442
443 /*
444 * do_verify_conf()
445 *
446 * Handles the conf verification callback.
447 *
448 * inputs: none
449 * output: none
450 */
451 static void *
452 do_verify_conf(va_list args)
453 {
454 pass_callback(h_verify);
455
456 if (conf_cold)
457 {
458 int i;
459
460 for (i = 0; myargv[i] != NULL; i++)
461 if (!ircncmp(myargv[i], "softboot_", 9))
462 {
463 load_state(atoi(myargv[i] + 9));
464 myargv[i] = NULL;
465 }
466 }
467
468 return NULL;
469 }
470
471 INIT_MODULE(soft_reboot, "$Revision$")
472 {
473 h_shutdown = install_hook(ircd_shutdown, do_shutdown);
474 h_verify = install_hook(verify_conf, do_verify_conf);
475 }
476
477 CLEANUP_MODULE
478 {
479 uninstall_hook(verify_conf, do_verify_conf);
480 uninstall_hook(ircd_shutdown, do_shutdown);
481 }

Properties

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