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