dc-filtercmd needs bash, not sh.
[doldaconnect.git] / daemon / main.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
302a2600 3 * Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
d3372da9 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 }
1001e996 307 setpgid(0, 0);
d3372da9 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 }
5b50bfcd
FT
343 putenv(sprintf2("HOME=%s", pwent->pw_dir));
344 putenv(sprintf2("SHELL=%s", pwent->pw_shell));
345 putenv(sprintf2("PATH=%s/bin:/usr/local/bin:/bin:/usr/bin", pwent->pw_dir));
d3372da9 346 }
d3372da9 347 putenv(sprintf2("USER=%s", pwent->pw_name));
348 putenv(sprintf2("LOGNAME=%s", pwent->pw_name));
d3372da9 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
2559e077 388 now = ntime();
5cccc2d5 389 immsyslog = nofork = 0;
d3372da9 390 syslogfac = LOG_DAEMON;
391 configfile = NULL;
392 pidfile = NULL;
8af979f3 393 while((c = getopt(argc, argv, "p:C:f:hnsV")) != -1)
d3372da9 394 {
395 switch(c)
396 {
397 case 'p':
398 pidfile = optarg;
399 break;
400 case 'C':
401 configfile = optarg;
402 break;
403 case 'f':
404 if(!strcmp(optarg, "auth"))
405 syslogfac = LOG_AUTH;
406 else if(!strcmp(optarg, "authpriv"))
407 syslogfac = LOG_AUTHPRIV;
408 else if(!strcmp(optarg, "cron"))
409 syslogfac = LOG_CRON;
410 else if(!strcmp(optarg, "daemon"))
411 syslogfac = LOG_DAEMON;
412 else if(!strcmp(optarg, "ftp"))
413 syslogfac = LOG_FTP;
414 else if(!strcmp(optarg, "kern"))
415 syslogfac = LOG_KERN;
416 else if(!strcmp(optarg, "lpr"))
417 syslogfac = LOG_LPR;
418 else if(!strcmp(optarg, "mail"))
419 syslogfac = LOG_MAIL;
420 else if(!strcmp(optarg, "news"))
421 syslogfac = LOG_NEWS;
422 else if(!strcmp(optarg, "syslog"))
423 syslogfac = LOG_SYSLOG;
424 else if(!strcmp(optarg, "user"))
425 syslogfac = LOG_USER;
426 else if(!strcmp(optarg, "uucp"))
427 syslogfac = LOG_UUCP;
428 else if(!strncmp(optarg, "local", 5) && (strlen(optarg) == 6))
429 syslogfac = LOG_LOCAL0 + (optarg[5] - '0');
430 else
431 fprintf(stderr, "unknown syslog facility %s, using daemon\n", optarg);
432 break;
433 case 'n':
434 nofork = 1;
435 break;
9a0b3ef8 436 case 's':
5cccc2d5 437 immsyslog = 1;
58b3dc7b 438 break;
8af979f3
FT
439 case 'V':
440 printf("%s", RELEASEINFO);
441 exit(0);
d3372da9 442 case 'h':
443 case ':':
444 case '?':
445 default:
8af979f3 446 printf("usage: doldacond [-hnsV] [-C configfile] [-p pidfile] [-f facility]\n");
d3372da9 447 exit(c != 'h');
448 }
449 }
450 setlocale(LC_ALL, "");
451 initlog();
5cccc2d5 452 if(immsyslog)
453 {
454 logtosyslog = 1;
455 logtostderr = 0;
456 }
d3372da9 457 signal(SIGPIPE, SIG_IGN);
458 signal(SIGHUP, handler);
459 signal(SIGINT, handler);
460 signal(SIGTERM, handler);
461 signal(SIGCHLD, handler);
462 signal(SIGUSR1, handler);
463 signal(SIGUSR2, handler);
464 preinit(0);
465 if(configfile == NULL)
466 {
c5d23632 467 if((configfile = findfile("doldacond.conf", NULL, 0)) == NULL)
d3372da9 468 {
469 flog(LOG_CRIT, "could not find a configuration file");
470 exit(1);
471 }
472 }
473 pfstream = NULL;
474 if(pidfile != NULL)
475 {
476 if((pfstream = fopen(pidfile, "w")) == NULL)
477 {
478 flog(LOG_CRIT, "could not open specified PID file %s: %s", pidfile, strerror(errno));
479 exit(1);
480 }
481 }
482 if((confstream = fopen(configfile, "r")) == NULL)
483 {
484 flog(LOG_CRIT, "could not open configuration file %s: %s", configfile, strerror(errno));
485 exit(1);
486 }
487 readconfig(confstream);
488 fclose(confstream);
489 init(0);
490 if(!nofork)
491 {
492 logtosyslog = 1;
493 daemon(0, 0);
494 flog(LOG_INFO, "daemonized");
495 logtostderr = 0;
496 }
497 if(pfstream != NULL) {
498 fprintf(pfstream, "%i\n", getpid());
499 fclose(pfstream);
500 }
2559e077 501 flog(LOG_INFO, "startup took %f seconds", ntime() - now);
d3372da9 502 running = 1;
503 reinit = 0;
504 while(running)
505 {
506 if(reinit)
507 {
508 if((confstream = fopen(configfile, "r")) == NULL)
509 {
510 flog(LOG_ERR, "could not open configuration file %s: %s (ignoring HUP)", configfile, strerror(errno));
511 } else {
512 preinit(1);
513 readconfig(confstream);
514 fclose(confstream);
515 init(1);
516 }
517 reinit = 0;
518 }
519 delay = 1000; /* -1; */
520 for(mod = modchain; mod != NULL; mod = mod->next)
521 {
522 if(mod->run && mod->run())
523 delay = 0;
524 }
525 if(!running)
526 delay = 0;
527 if(delay != 0)
528 {
529 now = ntime();
530 for(timer = timers; timer != NULL; timer = timer->next)
531 {
532 if((delay == -1) || ((int)((timer->at - now) * 1000.0) < delay))
533 delay = (int)((timer->at - now) * 1000.0);
534 }
535 }
536 if(childrendone)
537 {
538 delay = 0;
539 childrendone = 0;
540 }
541 pollsocks(delay);
542 now = ntime();
dae3c8b4 543 do
d3372da9 544 {
dae3c8b4 545 for(timer = timers; timer != NULL; timer = timer->next)
546 {
547 if(now < timer->at)
548 continue;
549 if(timer->prev != NULL)
550 timer->prev->next = timer->next;
551 if(timer->next != NULL)
552 timer->next->prev = timer->prev;
553 if(timer == timers)
554 timers = timer->next;
555 timer->func(0, timer->data);
556 free(timer);
557 break;
558 }
559 } while(timer != NULL);
d3372da9 560 do
561 {
562 for(child = children; child != NULL; child = child->next)
563 {
564 if(child->finished)
565 {
566 child->callback(child->pid, child->status, child->data);
567 if(child == children)
568 children = child->next;
569 if(child->prev != NULL)
570 child->prev->next = child->next;
571 if(child->next != NULL)
572 child->next->prev = child->prev;
573 free(child);
574 break;
575 }
576 }
577 } while(child != NULL);
578 }
579 flog(LOG_INFO, "terminating...");
580 terminate();
267c4a93 581 if(pidfile != NULL)
582 unlink(pidfile);
d3372da9 583 return(0);
584}