Fix format mismatch.
[doldaconnect.git] / daemon / main.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
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 2 of the License, or
8 * (at your option) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19#include <stdio.h>
20#include <unistd.h>
21#include <stdlib.h>
22#include <string.h>
23#include <errno.h>
24#include <locale.h>
25#include <signal.h>
26#include <getopt.h>
27#include <time.h>
28#include <pwd.h>
29#include <grp.h>
30#include <sys/wait.h>
31#include <stdarg.h>
32#include <fcntl.h>
33
34#ifdef HAVE_CONFIG_H
35#include <config.h>
36#endif
37#include "utils.h"
38#include "log.h"
39#include "conf.h"
40#include "module.h"
41#include "net.h"
42#include "client.h"
43#include "sysevents.h"
44#include "auth.h"
45
46struct module *modchain = NULL;
47static struct timer *timers = NULL;
48static struct child *children = NULL;
49volatile int running;
50volatile int reinit;
51static volatile int childrendone = 0;
52
53struct timer *timercallback(double at, void (*func)(int, void *), void *data)
54{
55 struct timer *new;
56
57 new = smalloc(sizeof(*new));
58 new->at = at;
59 new->func = func;
60 new->data = data;
61 new->next = timers;
62 new->prev = NULL;
63 if(timers != NULL)
64 timers->prev = new;
65 timers = new;
66 return(new);
67}
68
69void canceltimer(struct timer *timer)
70{
71 if(timer->next != NULL)
72 timer->next->prev = timer->prev;
73 if(timer->prev != NULL)
74 timer->prev->next = timer->next;
75 if(timer == timers)
76 timers = timer->next;
77 timer->func(1, timer->data);
78 free(timer);
79}
80
81void childcallback(pid_t pid, void (*func)(pid_t, int, void *), void *data)
82{
83 struct child *new;
84
85 new = smalloc(sizeof(*new));
86 new->pid = pid;
87 new->callback = func;
88 new->data = data;
89 new->finished = 0;
90 new->prev = NULL;
91 new->next = children;
92 if(children != NULL)
93 children->prev = new;
94 children = new;
95}
96
97static void preinit(int hup)
98{
99 struct module *mod;
100
101 for(mod = modchain; mod != NULL; mod = mod->next)
102 {
103 if(mod->preinit)
104 mod->preinit(hup);
105 if(!hup && ((mod->conf.vars != NULL) || (mod->conf.cmds != NULL)))
106 confregmod(&mod->conf);
107 }
108}
109
110static void init(int hup)
111{
112 struct module *mod;
113
114 for(mod = modchain; mod != NULL; mod = mod->next)
115 {
116 if(mod->init && mod->init(hup))
117 {
118 flog(LOG_CRIT, "initialization of \"%s\" failed", mod->name);
119 exit(1);
120 }
121 }
122}
123
124static void terminate(void)
125{
126 struct module *mod;
127
128 for(mod = modchain; mod != NULL; mod = mod->next)
129 {
130 if(mod->terminate)
131 mod->terminate();
132 }
133}
134
135static void handler(int signum)
136{
137 pid_t pid;
138 int status;
139 struct child *child;
140 FILE *dumpfile;
141 extern int numfnetnodes, numtransfers, numdcpeers;
142
143 switch(signum)
144 {
145 case SIGHUP:
146 reinit = 1;
147 break;
148 case SIGINT:
149 case SIGTERM:
150 running = 0;
151 break;
152 case SIGCHLD:
153 while((pid = waitpid(-1, &status, WNOHANG)) > 0)
154 {
155 for(child = children; child != NULL; child = child->next)
156 {
157 if(child->pid == pid)
158 {
159 child->finished = 1;
160 child->status = status;
161 }
162 }
163 childrendone = 1;
164 }
165 break;
166 case SIGUSR1:
167 flog(LOG_NOTICE, "forking and dumping core upon SIGUSR1");
168 if(fork() == 0)
169 abort();
170 break;
171 case SIGUSR2:
172 flog(LOG_NOTICE, "dumping memstats to /tmp/dc-mem upon SIGUSR2");
173 if((dumpfile = fopen("/tmp/dc-mem", "w")) == NULL) {
174 flog(LOG_ERR, "could not dump stats: %s", strerror(errno));
175 break;
176 }
177 fprintf(dumpfile, "%i %i %i\n", numfnetnodes, numtransfers, numdcpeers);
178 fclose(dumpfile);
179 break;
180 }
181}
182
183pid_t forksess(uid_t user, struct authhandle *auth, void (*ccbfunc)(pid_t, int, void *), void *data, ...)
184{
185 int i, o;
186 int cpipe[2];
187 struct
188 {
189 int tfd;
190 int fd;
191 } *files;
192 int maxfd, type, numfiles;
193 int acc;
194 int *ibuf;
195 struct passwd *pwent;
196 pid_t pid;
197 char *buf;
198 va_list args;
199 sigset_t sigset;
200 int ret, status;
201
202 if((pwent = getpwuid(user)) == NULL)
203 {
204 flog(LOG_WARNING, "no passwd entry for uid %i, cannot fork session", user);
205 errno = EACCES;
206 return(-1);
207 }
208 if((geteuid() != 0) && (user != geteuid()))
209 {
210 flog(LOG_WARNING, "cannot fork non-owning session when not running as root (EUID is %i, target UID is %i)", geteuid(), user);
211 errno = EPERM;
212 return(-1);
213 }
214 va_start(args, data);
215 numfiles = 0;
216 files = NULL;
217 maxfd = 0;
218 while((type = va_arg(args, int)) != FD_END)
219 {
220 files = srealloc(files, sizeof(*files) * (numfiles + 1));
221 files[numfiles].fd = va_arg(args, int);
222 if(files[numfiles].fd > maxfd)
223 maxfd = files[numfiles].fd;
224 acc = va_arg(args, int);
225 if(type == FD_PIPE)
226 {
227 if(pipe(cpipe) < 0)
228 {
229 flog(LOG_CRIT, "could not create pipe(!): %s", strerror(errno));
230 for(i = 0; i < numfiles; i++)
231 close(files[i].tfd);
232 return(-1);
233 }
234 ibuf = va_arg(args, int *);
235 if(acc == O_WRONLY)
236 {
237 *ibuf = cpipe[1];
238 files[numfiles].tfd = cpipe[0];
239 } else {
240 *ibuf = cpipe[0];
241 files[numfiles].tfd = cpipe[1];
242 }
243 } else if(type == FD_FILE) {
244 buf = va_arg(args, char *);
245 if((files[numfiles].tfd = open(buf, acc)) < 0)
246 {
247 flog(LOG_CRIT, "could not open file \"%s\": %s", buf, strerror(errno));
248 for(i = 0; i < numfiles; i++)
249 close(files[i].tfd);
250 return(-1);
251 }
252 }
253 if(files[numfiles].tfd > maxfd)
254 maxfd = files[numfiles].tfd;
255 numfiles++;
256 }
257 va_end(args);
258 sigemptyset(&sigset);
259 sigaddset(&sigset, SIGCHLD);
260 sigprocmask(SIG_BLOCK, &sigset, NULL);
261 if((pid = fork()) < 0)
262 {
263 flog(LOG_WARNING, "could not fork(!) in forksess(): %s", strerror(errno));
264 for(i = 0; i < numfiles; i++)
265 close(files[i].tfd);
266 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
267 }
268 if(pid == 0)
269 {
270 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
271 signal(SIGPIPE, SIG_DFL);
272 signal(SIGCHLD, SIG_DFL);
273 signal(SIGINT, SIG_DFL);
274 signal(SIGTERM, SIG_DFL);
275 signal(SIGHUP, SIG_DFL);
276 for(i = 0; i < numfiles; i++)
277 {
278 if(dup2(files[i].tfd, maxfd + i + 1) < 0)
279 exit(127);
280 files[i].tfd = maxfd + i + 1;
281 }
282 for(i = 0; i < numfiles; i++)
283 {
284 if(dup2(files[i].tfd, files[i].fd) < 0)
285 exit(127);
286 }
287 initlog();
288 for(i = 0; i < FD_SETSIZE; i++)
289 {
290 if(i <= maxfd)
291 {
292 for(o = 0; o < numfiles; o++)
293 {
294 if(i == files[o].fd)
295 break;
296 }
297 if(o == numfiles)
298 close(i);
299 } else {
300 close(i);
301 }
302 }
303 setpgrp();
304 signal(SIGHUP, SIG_IGN);
305 errno = 0;
306 if((authopensess(auth)) != AUTH_SUCCESS)
307 {
308 flog(LOG_WARNING, "could not open session for user %s: %s", pwent->pw_name, (errno == 0)?"Unknown error - should be logged above":strerror(errno));
309 exit(127);
310 }
311 if((pid = fork()) < 0)
312 {
313 authclosesess(auth);
314 exit(127);
315 }
316 if(pid == 0)
317 {
318 if(geteuid() == 0)
319 {
320 if(initgroups(pwent->pw_name, pwent->pw_gid))
321 {
322 flog(LOG_WARNING, "could not initgroups: %s", strerror(errno));
323 exit(127);
324 }
325 if(setgid(pwent->pw_gid))
326 {
327 flog(LOG_WARNING, "could not setgid: %s", strerror(errno));
328 exit(127);
329 }
330 if(setuid(pwent->pw_uid))
331 {
332 flog(LOG_WARNING, "could not setuid: %s", strerror(errno));
333 exit(127);
334 }
335 }
336 putenv(sprintf2("HOME=%s", pwent->pw_dir));
337 putenv(sprintf2("SHELL=%s", pwent->pw_shell));
338 putenv(sprintf2("USER=%s", pwent->pw_name));
339 putenv(sprintf2("LOGNAME=%s", pwent->pw_name));
340 putenv(sprintf2("PATH=%s/bin:/usr/local/bin:/bin:/usr/bin", pwent->pw_dir));
341 chdir(pwent->pw_dir);
342 return(0);
343 }
344 for(i = 0; i < numfiles; i++)
345 close(files[i].fd);
346 while(((ret = waitpid(pid, &status, 0)) != pid) && (ret >= 0));
347 authclosesess(auth);
348 if(ret < 0)
349 {
350 flog(LOG_WARNING, "waitpid(%i) said \"%s\"", pid, strerror(errno));
351 exit(127);
352 }
353 if(!WIFEXITED(status))
354 exit(127);
355 exit(WEXITSTATUS(status));
356 }
357 for(i = 0; i < numfiles; i++)
358 close(files[i].tfd);
359 if(files != NULL)
360 free(files);
361 if(ccbfunc != NULL)
362 childcallback(pid, ccbfunc, data);
363 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
364 return(pid);
365}
366
367int main(int argc, char **argv)
368{
369 int c;
370 int nofork;
371 char *configfile;
372 char *pidfile;
373 FILE *pfstream, *confstream;
5cccc2d5 374 int delay, immsyslog;
d3372da9 375 struct module *mod;
376 struct timer *timer, *ntimer;
377 struct child *child;
378 double now;
379
5cccc2d5 380 immsyslog = nofork = 0;
d3372da9 381 syslogfac = LOG_DAEMON;
382 configfile = NULL;
383 pidfile = NULL;
9a0b3ef8 384 while((c = getopt(argc, argv, "p:C:f:hns")) != -1)
d3372da9 385 {
386 switch(c)
387 {
388 case 'p':
389 pidfile = optarg;
390 break;
391 case 'C':
392 configfile = optarg;
393 break;
394 case 'f':
395 if(!strcmp(optarg, "auth"))
396 syslogfac = LOG_AUTH;
397 else if(!strcmp(optarg, "authpriv"))
398 syslogfac = LOG_AUTHPRIV;
399 else if(!strcmp(optarg, "cron"))
400 syslogfac = LOG_CRON;
401 else if(!strcmp(optarg, "daemon"))
402 syslogfac = LOG_DAEMON;
403 else if(!strcmp(optarg, "ftp"))
404 syslogfac = LOG_FTP;
405 else if(!strcmp(optarg, "kern"))
406 syslogfac = LOG_KERN;
407 else if(!strcmp(optarg, "lpr"))
408 syslogfac = LOG_LPR;
409 else if(!strcmp(optarg, "mail"))
410 syslogfac = LOG_MAIL;
411 else if(!strcmp(optarg, "news"))
412 syslogfac = LOG_NEWS;
413 else if(!strcmp(optarg, "syslog"))
414 syslogfac = LOG_SYSLOG;
415 else if(!strcmp(optarg, "user"))
416 syslogfac = LOG_USER;
417 else if(!strcmp(optarg, "uucp"))
418 syslogfac = LOG_UUCP;
419 else if(!strncmp(optarg, "local", 5) && (strlen(optarg) == 6))
420 syslogfac = LOG_LOCAL0 + (optarg[5] - '0');
421 else
422 fprintf(stderr, "unknown syslog facility %s, using daemon\n", optarg);
423 break;
424 case 'n':
425 nofork = 1;
426 break;
9a0b3ef8 427 case 's':
5cccc2d5 428 immsyslog = 1;
58b3dc7b 429 break;
d3372da9 430 case 'h':
431 case ':':
432 case '?':
433 default:
f1c63392 434 printf("usage: doldacond [-hns] [-C configfile] [-p pidfile] [-f facility]\n");
d3372da9 435 exit(c != 'h');
436 }
437 }
438 setlocale(LC_ALL, "");
439 initlog();
5cccc2d5 440 if(immsyslog)
441 {
442 logtosyslog = 1;
443 logtostderr = 0;
444 }
d3372da9 445 signal(SIGPIPE, SIG_IGN);
446 signal(SIGHUP, handler);
447 signal(SIGINT, handler);
448 signal(SIGTERM, handler);
449 signal(SIGCHLD, handler);
450 signal(SIGUSR1, handler);
451 signal(SIGUSR2, handler);
452 preinit(0);
453 if(configfile == NULL)
454 {
455 if((configfile = findconfigfile()) == NULL)
456 {
457 flog(LOG_CRIT, "could not find a configuration file");
458 exit(1);
459 }
460 }
461 pfstream = NULL;
462 if(pidfile != NULL)
463 {
464 if((pfstream = fopen(pidfile, "w")) == NULL)
465 {
466 flog(LOG_CRIT, "could not open specified PID file %s: %s", pidfile, strerror(errno));
467 exit(1);
468 }
469 }
470 if((confstream = fopen(configfile, "r")) == NULL)
471 {
472 flog(LOG_CRIT, "could not open configuration file %s: %s", configfile, strerror(errno));
473 exit(1);
474 }
475 readconfig(confstream);
476 fclose(confstream);
477 init(0);
478 if(!nofork)
479 {
480 logtosyslog = 1;
481 daemon(0, 0);
482 flog(LOG_INFO, "daemonized");
483 logtostderr = 0;
484 }
485 if(pfstream != NULL) {
486 fprintf(pfstream, "%i\n", getpid());
487 fclose(pfstream);
488 }
489 running = 1;
490 reinit = 0;
491 while(running)
492 {
493 if(reinit)
494 {
495 if((confstream = fopen(configfile, "r")) == NULL)
496 {
497 flog(LOG_ERR, "could not open configuration file %s: %s (ignoring HUP)", configfile, strerror(errno));
498 } else {
499 preinit(1);
500 readconfig(confstream);
501 fclose(confstream);
502 init(1);
503 }
504 reinit = 0;
505 }
506 delay = 1000; /* -1; */
507 for(mod = modchain; mod != NULL; mod = mod->next)
508 {
509 if(mod->run && mod->run())
510 delay = 0;
511 }
512 if(!running)
513 delay = 0;
514 if(delay != 0)
515 {
516 now = ntime();
517 for(timer = timers; timer != NULL; timer = timer->next)
518 {
519 if((delay == -1) || ((int)((timer->at - now) * 1000.0) < delay))
520 delay = (int)((timer->at - now) * 1000.0);
521 }
522 }
523 if(childrendone)
524 {
525 delay = 0;
526 childrendone = 0;
527 }
528 pollsocks(delay);
529 now = ntime();
530 for(timer = timers; timer != NULL; timer = ntimer)
531 {
532 ntimer = timer->next;
533 if(now < timer->at)
534 continue;
535 if(timer->prev != NULL)
536 timer->prev->next = timer->next;
537 if(timer->next != NULL)
538 timer->next->prev = timer->prev;
539 if(timer == timers)
540 timers = timer->next;
541 timer->func(0, timer->data);
542 free(timer);
543 }
544 do
545 {
546 for(child = children; child != NULL; child = child->next)
547 {
548 if(child->finished)
549 {
550 child->callback(child->pid, child->status, child->data);
551 if(child == children)
552 children = child->next;
553 if(child->prev != NULL)
554 child->prev->next = child->next;
555 if(child->next != NULL)
556 child->next->prev = child->prev;
557 free(child);
558 break;
559 }
560 }
561 } while(child != NULL);
562 }
563 flog(LOG_INFO, "terminating...");
564 terminate();
267c4a93 565 if(pidfile != NULL)
566 unlink(pidfile);
d3372da9 567 return(0);
568}