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 |
50 | struct module *modchain = NULL; |
51 | static struct timer *timers = NULL; |
52 | static struct child *children = NULL; |
53 | volatile int running; |
54 | volatile int reinit; |
55 | static volatile int childrendone = 0; |
56 | |
57 | struct 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 | |
73 | void 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 | |
85 | void 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 | |
101 | static 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 | |
114 | static 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 | |
128 | static 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 | |
139 | static 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 | |
187 | pid_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 | |
375 | int 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 | } |