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