Only set HOME, SHELL and PATH in multiuser sessions.
[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;
9a0b3ef8 393 while((c = getopt(argc, argv, "p:C:f:hns")) != -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;
d3372da9 439 case 'h':
440 case ':':
441 case '?':
442 default:
f1c63392 443 printf("usage: doldacond [-hns] [-C configfile] [-p pidfile] [-f facility]\n");
d3372da9 444 exit(c != 'h');
445 }
446 }
447 setlocale(LC_ALL, "");
448 initlog();
5cccc2d5 449 if(immsyslog)
450 {
451 logtosyslog = 1;
452 logtostderr = 0;
453 }
d3372da9 454 signal(SIGPIPE, SIG_IGN);
455 signal(SIGHUP, handler);
456 signal(SIGINT, handler);
457 signal(SIGTERM, handler);
458 signal(SIGCHLD, handler);
459 signal(SIGUSR1, handler);
460 signal(SIGUSR2, handler);
461 preinit(0);
462 if(configfile == NULL)
463 {
c5d23632 464 if((configfile = findfile("doldacond.conf", NULL, 0)) == NULL)
d3372da9 465 {
466 flog(LOG_CRIT, "could not find a configuration file");
467 exit(1);
468 }
469 }
470 pfstream = NULL;
471 if(pidfile != NULL)
472 {
473 if((pfstream = fopen(pidfile, "w")) == NULL)
474 {
475 flog(LOG_CRIT, "could not open specified PID file %s: %s", pidfile, strerror(errno));
476 exit(1);
477 }
478 }
479 if((confstream = fopen(configfile, "r")) == NULL)
480 {
481 flog(LOG_CRIT, "could not open configuration file %s: %s", configfile, strerror(errno));
482 exit(1);
483 }
484 readconfig(confstream);
485 fclose(confstream);
486 init(0);
487 if(!nofork)
488 {
489 logtosyslog = 1;
490 daemon(0, 0);
491 flog(LOG_INFO, "daemonized");
492 logtostderr = 0;
493 }
494 if(pfstream != NULL) {
495 fprintf(pfstream, "%i\n", getpid());
496 fclose(pfstream);
497 }
2559e077 498 flog(LOG_INFO, "startup took %f seconds", ntime() - now);
d3372da9 499 running = 1;
500 reinit = 0;
501 while(running)
502 {
503 if(reinit)
504 {
505 if((confstream = fopen(configfile, "r")) == NULL)
506 {
507 flog(LOG_ERR, "could not open configuration file %s: %s (ignoring HUP)", configfile, strerror(errno));
508 } else {
509 preinit(1);
510 readconfig(confstream);
511 fclose(confstream);
512 init(1);
513 }
514 reinit = 0;
515 }
516 delay = 1000; /* -1; */
517 for(mod = modchain; mod != NULL; mod = mod->next)
518 {
519 if(mod->run && mod->run())
520 delay = 0;
521 }
522 if(!running)
523 delay = 0;
524 if(delay != 0)
525 {
526 now = ntime();
527 for(timer = timers; timer != NULL; timer = timer->next)
528 {
529 if((delay == -1) || ((int)((timer->at - now) * 1000.0) < delay))
530 delay = (int)((timer->at - now) * 1000.0);
531 }
532 }
533 if(childrendone)
534 {
535 delay = 0;
536 childrendone = 0;
537 }
538 pollsocks(delay);
539 now = ntime();
dae3c8b4 540 do
d3372da9 541 {
dae3c8b4 542 for(timer = timers; timer != NULL; timer = timer->next)
543 {
544 if(now < timer->at)
545 continue;
546 if(timer->prev != NULL)
547 timer->prev->next = timer->next;
548 if(timer->next != NULL)
549 timer->next->prev = timer->prev;
550 if(timer == timers)
551 timers = timer->next;
552 timer->func(0, timer->data);
553 free(timer);
554 break;
555 }
556 } while(timer != NULL);
d3372da9 557 do
558 {
559 for(child = children; child != NULL; child = child->next)
560 {
561 if(child->finished)
562 {
563 child->callback(child->pid, child->status, child->data);
564 if(child == children)
565 children = child->next;
566 if(child->prev != NULL)
567 child->prev->next = child->next;
568 if(child->next != NULL)
569 child->next->prev = child->prev;
570 free(child);
571 break;
572 }
573 }
574 } while(child != NULL);
575 }
576 flog(LOG_INFO, "terminating...");
577 terminate();
267c4a93 578 if(pidfile != NULL)
579 unlink(pidfile);
d3372da9 580 return(0);
581}