1 /* This file is part of GNU Dico.
2 Copyright (C) 2008-2020 Sergey Poznyakoff
3
4 GNU Dico is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Dico is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <dicod.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22
23 struct dicod_server {
24 int fd; /* Socket descriptor */
25 struct sockaddr *addr;
26 socklen_t addrlen;
27 };
28
29 struct dicod_server *srvtab;
30 size_t srvcount;
31 int fdmax;
32
33 pid_t *childtab;
34 unsigned long num_children;
35 unsigned long total_forks;
36
37 struct sockaddr server_addr;
38 socklen_t server_addrlen;
39
40 static int
address_family_to_domain(int family)41 address_family_to_domain (int family)
42 {
43 switch (family) {
44 case AF_UNIX:
45 return PF_UNIX;
46
47 case AF_INET:
48 return PF_INET;
49
50 case AF_INET6:
51 return PF_INET6;
52
53 default:
54 abort();
55 }
56 }
57
58 void
open_sockets(void)59 open_sockets(void)
60 {
61 size_t i;
62 dico_iterator_t itr;
63 struct grecs_sockaddr *sp;
64 struct stat st;
65 int t;
66 char *p;
67
68 srvcount = dico_list_count(listen_addr);
69 if (srvcount == 0) {
70 /* Provide defaults */
71 struct sockaddr_in *s_in = xmalloc(sizeof(*s_in));
72
73 sp = xmalloc(sizeof(*sp));
74 sp->sa = (struct sockaddr*) s_in;
75 sp->len = sizeof(*s_in);
76
77 if (!listen_addr)
78 listen_addr = xdico_list_create();
79 s_in->sin_family = AF_INET;
80 s_in->sin_addr.s_addr = INADDR_ANY;
81 s_in->sin_port = htons(DICO_DICT_PORT);
82 xdico_list_append(listen_addr, sp);
83 srvcount = 1;
84 }
85 srvtab = xcalloc(srvcount, sizeof srvtab[0]);
86 fdmax = 0;
87 itr = xdico_list_iterator(listen_addr);
88 for (i = 0, sp = dico_iterator_first(itr); sp;
89 sp = dico_iterator_next(itr)) {
90 int fd = socket(address_family_to_domain(sp->sa->sa_family),
91 SOCK_STREAM, 0);
92 if (fd == -1) {
93 dico_log(L_ERR, errno, "socket");
94 continue;
95 }
96
97 switch (sp->sa->sa_family) {
98 case AF_UNIX: {
99 struct sockaddr_un *s_un = (struct sockaddr_un *)sp->sa;
100 if (stat(s_un->sun_path, &st)) {
101 if (errno != ENOENT) {
102 dico_log(L_ERR, errno,
103 _("file %s exists but cannot be stat'd"),
104 s_un->sun_path);
105 close(fd);
106 continue;
107 }
108 } else if (!S_ISSOCK(st.st_mode)) {
109 dico_log(L_ERR, 0,
110 _("file %s is not a socket"),
111 s_un->sun_path);
112 close(fd);
113 continue;
114 } else if (unlink(s_un->sun_path)) {
115 dico_log(L_ERR, errno,
116 _("cannot unlink file %s"),
117 s_un->sun_path);
118 close(fd);
119 continue;
120 }
121 break;
122 }
123
124 case AF_INET:
125 case AF_INET6:
126 t = 1;
127 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(t));
128 break;
129
130 default:
131 dico_log(L_ERR, 0, _("unsupported address family: %d"),
132 sp->sa->sa_family);
133 continue;
134 }
135
136 if (bind(fd, sp->sa, sp->len) == -1) {
137 p = sockaddr_to_astr(sp->sa, sp->len);
138 dico_log(L_ERR, errno, ("cannot bind to %s"), p);
139 free(p);
140 close(fd);
141 continue;
142 }
143
144 if (listen(fd, 8) == -1) {
145 p = sockaddr_to_astr(sp->sa, sp->len);
146 dico_log(L_ERR, errno, "cannot listen on %s", p);
147 free(p);
148 close(fd);
149 continue;
150 }
151
152 srvtab[i].addr = sp->sa;
153 srvtab[i].addrlen = sp->len;
154 srvtab[i].fd = fd;
155 i++;
156 if (fd > fdmax)
157 fdmax = fd;
158 }
159 dico_iterator_destroy(&itr);
160 srvcount = i;
161
162 if (srvcount == 0)
163 dico_die(EX_UNAVAILABLE, L_ERR, 0, _("No sockets opened"));
164 }
165
166 void
close_sockets(void)167 close_sockets(void)
168 {
169 size_t i;
170
171 for (i = 0; i < srvcount; i++)
172 close(srvtab[i].fd);
173 free(srvtab);
174 srvtab = NULL;
175 srvcount = 0;
176 fdmax = 0;
177 }
178
179
180 unsigned long
find_pid(pid_t pid)181 find_pid(pid_t pid)
182 {
183 unsigned long i;
184 for (i = 0; i < max_children; i++)
185 if (childtab[i] == pid)
186 return i;
187 return (unsigned long)-1;
188 }
189
190 void
register_child(pid_t pid)191 register_child(pid_t pid)
192 {
193 unsigned long i = find_pid(0);
194 if (i != (unsigned long)-1)
195 childtab[i] = pid;
196 ++num_children;
197 ++total_forks;
198 }
199
200 static void
print_status(pid_t pid,int status,int expect_term)201 print_status(pid_t pid, int status, int expect_term)
202 {
203 if (WIFEXITED(status)) {
204 switch (WEXITSTATUS(status)) {
205 case EX_OK:
206 dico_log(L_DEBUG, 0, _("%lu exited successfully"),
207 (unsigned long) pid);
208 break;
209
210 case EXIT_TIMEOUT:
211 dico_log(L_INFO, 0, _("%lu timed out"),
212 (unsigned long) pid);
213 break;
214
215 default:
216 dico_log(L_ERR, 0, _("%lu failed with status %d"),
217 (unsigned long) pid,
218 WEXITSTATUS(status));
219 }
220 } else if (WIFSIGNALED(status)) {
221 int prio;
222
223 if (expect_term && WTERMSIG(status) == SIGTERM)
224 prio = L_DEBUG;
225 else
226 prio = L_ERR;
227 dico_log(prio, 0, _("%lu terminated on signal %d"),
228 (unsigned long) pid, WTERMSIG(status));
229 } else if (WIFSTOPPED(status))
230 dico_log(L_ERR, 0, _("%lu stopped on signal %d"),
231 (unsigned long) pid, WSTOPSIG(status));
232 #ifdef WCOREDUMP
233 else if (WCOREDUMP(status))
234 dico_log(L_ERR, 0, _("%lu dumped core"), (unsigned long) pid);
235 #endif
236 else
237 dico_log(L_ERR, 0, _("%lu terminated with unrecognized status"),
238 (unsigned long) pid);
239 }
240
241
242 void
cleanup_children(int term)243 cleanup_children(int term)
244 {
245 pid_t pid;
246 int status;
247
248 while ((pid = waitpid ((pid_t)-1, &status, WNOHANG)) > 0) {
249 unsigned long i = find_pid(pid);
250 if (i == (unsigned long)-1) {
251 dico_log(L_INFO, 0, "subprocess %lu finished",
252 (unsigned long) pid);
253 } else {
254 print_status(pid, status, term);
255 }
256 childtab[i] = 0;
257 --num_children;
258 }
259
260
261 }
262
263 void
stop_all(int sig)264 stop_all(int sig)
265 {
266 unsigned long i;
267
268 if (!childtab)
269 return;
270 for (i = 0; i < max_children; i++)
271 if (childtab[i])
272 kill(sig, childtab[i]);
273 }
274
275 void
stop_children(void)276 stop_children(void)
277 {
278 int i;
279 if (!childtab)
280 return;
281 stop_all(SIGTERM);
282 for (i = 0; i < shutdown_timeout; i++) {
283 cleanup_children(1);
284 if (num_children == 0)
285 return;
286 sleep(1);
287 }
288 stop_all(SIGKILL);
289 }
290
291
292
293 /* Check whether pidfile NAME exists and if so, whether its PID is still
294 active. Exit if it is. */
295 void
check_pidfile(char * name)296 check_pidfile(char *name)
297 {
298 unsigned long pid;
299 FILE *fp = fopen(name, "r");
300
301 if (!fp) {
302 if (errno == ENOENT)
303 return;
304 dico_die(EX_NOINPUT, L_ERR, errno,
305 _("Cannot open pidfile `%s'"), name);
306 }
307 if (fscanf(fp, "%lu", &pid) != 1) {
308 dico_log(L_ERR, 0, _("Cannot get pid from pidfile `%s'"), name);
309 } else {
310 if (kill(pid, 0) == 0) {
311 dico_die(EX_UNAVAILABLE,
312 L_ERR,
313 0,
314 _("%s appears to run with pid %lu. "
315 "If it does not, remove `%s' and retry."),
316 dico_program_name,
317 pid,
318 name);
319 }
320 }
321 fclose(fp);
322 if (unlink(name))
323 dico_die(EX_OSERR, L_ERR, errno,
324 _("Cannot unlink pidfile `%s'"), name);
325 }
326
327 void
create_pidfile(char * name)328 create_pidfile(char *name)
329 {
330 FILE *fp = fopen(name, "w");
331
332 if (!fp)
333 dico_die(EX_CANTCREAT, L_ERR, errno,
334 _("Cannot create pidfile `%s'"), name);
335 fprintf(fp, "%lu", (unsigned long)getpid());
336 fclose(fp);
337 }
338
339 void
remove_pidfile(char * name)340 remove_pidfile(char *name)
341 {
342 if (unlink(name))
343 dico_log(L_ERR, errno, _("Cannot unlink pidfile `%s'"), name);
344 }
345
346
347 int restart;
348 int stop;
349 int need_cleanup;
350
351 static RETSIGTYPE
sig_stop(int sig)352 sig_stop(int sig)
353 {
354 stop = 1;
355 }
356
357 static RETSIGTYPE
sig_restart(int sig)358 sig_restart(int sig)
359 {
360 restart = 1;
361 }
362
363 static RETSIGTYPE
sig_child(int sig)364 sig_child(int sig)
365 {
366 need_cleanup = 1;
367 }
368
369 struct sockaddr_storage client_addr;
370 socklen_t client_addrlen;
371
372 #define ACCESS_DENIED_MSG "530 Access denied\n"
373 #define TEMP_FAIL_MSG "420 Server temporarily unavailable\n"
374 #define SWRITE(fd, s) write(fd, s, sizeof(s)-1)
375
376 int
handle_connection(int n)377 handle_connection(int n)
378 {
379 int status = 0;
380 int connfd;
381 int listenfd = srvtab[n].fd;
382
383 server_addr = *srvtab[n].addr;
384 server_addrlen = srvtab[n].addrlen;
385
386 client_addrlen = sizeof(client_addr);
387 connfd = accept(listenfd, (struct sockaddr*) &client_addr,
388 &client_addrlen);
389
390 if (connfd == -1) {
391 if (errno == EINTR)
392 return -1;
393 dico_log(L_ERR, errno, "accept");
394 return -1;
395 /*exit (EXIT_FAILURE);*/
396 }
397
398 if (dicod_acl_check(connect_acl, 1) == 0) {
399 char *p = sockaddr_to_astr((struct sockaddr *)&client_addr,
400 client_addrlen);
401 dico_log(L_NOTICE, 0,
402 _("connection from %s denied"),
403 p);
404 free(p);
405 SWRITE(connfd, ACCESS_DENIED_MSG);
406 close(connfd);
407 return 0;
408 }
409
410 if (single_process) {
411 dico_stream_t str = dicod_iostream(connfd, connfd);
412 if (str) {
413 status = dicod_loop(str);
414 dico_stream_close(str);
415 dico_stream_destroy(&str);
416 } else
417 status = -1;
418 } else {
419 pid_t pid = fork();
420 if (pid == -1) {
421 dico_log(L_ERR, errno, "fork");
422 SWRITE(connfd, TEMP_FAIL_MSG);
423 } else if (pid == 0) {
424 /* Child. */
425 dico_stream_t str;
426
427 close(listenfd);
428
429 signal(SIGTERM, SIG_DFL);
430 signal(SIGQUIT, SIG_DFL);
431 signal(SIGINT, SIG_DFL);
432 signal(SIGCHLD, SIG_DFL);
433 signal(SIGHUP, SIG_DFL);
434
435 str = dicod_iostream(connfd, connfd);
436 if (!str)
437 exit(EX_UNAVAILABLE);
438 status = dicod_loop(str);
439 dico_stream_close(str);
440 dico_stream_destroy(&str);
441 exit(status);
442 } else {
443 register_child(pid);
444 }
445 }
446 close(connfd);
447 return status;
448 }
449
450 static int
pre_restart_lint_internal(void)451 pre_restart_lint_internal(void)
452 {
453 pid_t pid;
454 time_t ts;
455 pid = fork();
456 if (pid == 0)
457 run_lint();
458 else if (pid == -1) {
459 dico_log(L_ERR, errno, _("fork failed"));
460 return 1;
461 }
462 /* Master process */
463 ts = time(NULL);
464 while (1) {
465 int status;
466 pid_t n = waitpid(pid, &status, WNOHANG);
467 if (n == pid) {
468 /* Child exited, examine status */
469 if (WIFEXITED(status)) {
470 if (WEXITSTATUS(status) == 0) {
471 /* OK to restart */
472 return 0;
473 } else {
474 dico_log(L_NOTICE, 0,
475 _("refusing to restart due to errors in "
476 "configuration file"));
477 return 1;
478 }
479 } else {
480 dico_log(L_NOTICE, 0,
481 _("refusing to restart due to unexpected "
482 "return status of configuration checker"));
483 print_status(pid, status, 0);
484 return 1;
485 }
486 } else if (pid == 0) {
487 if (time(NULL) - ts > 5) {
488 dico_log(L_NOTICE, 0,
489 _("refusing to restart: "
490 "configuration checker did not exit within "
491 "5 seconds"));
492 kill(pid, SIGKILL);
493 break;
494 }
495 } else if (errno == EINTR || errno == EAGAIN)
496 continue;
497 else {
498 dico_log(L_ERR, errno, _("waitpid failed"));
499 dico_log(L_NOTICE, 0, _("refusing to restart"));
500 kill(pid, SIGKILL);
501 break;
502 }
503 }
504 return 1;
505 }
506
507 static int
pre_restart_lint(void)508 pre_restart_lint(void)
509 {
510 int rc;
511 RETSIGTYPE (*sf)(int);
512
513 sf = signal(SIGCHLD, SIG_DFL);
514 rc = pre_restart_lint_internal();
515 signal(SIGCHLD, sf);
516 return rc;
517 }
518
519 int
server_loop(void)520 server_loop(void)
521 {
522 size_t i;
523 fd_set fdset;
524
525 FD_ZERO(&fdset);
526 for (i = 0; i < srvcount; i++)
527 FD_SET(srvtab[i].fd, &fdset);
528
529 for (;;) {
530 int rc;
531 fd_set rdset;
532
533 if (need_cleanup) {
534 cleanup_children(0);
535 need_cleanup = 1;
536 }
537
538 if (num_children > max_children) {
539 dico_log(L_ERR, 0, _("too many children (%lu)"), num_children);
540 pause();
541 continue;
542 }
543
544 rdset = fdset;
545 rc = select (fdmax + 1, &rdset, NULL, NULL, NULL);
546 if (rc == -1 && errno == EINTR) {
547 if (stop)
548 break;
549 else if (restart) {
550 if (pre_restart_lint() == 0)
551 break;
552 else
553 restart = 0;
554 }
555 continue;
556 } else if (rc < 0) {
557 dico_log(L_CRIT, errno, _("select error"));
558 return EX_OSERR;
559 }
560
561 for (i = 0; i < srvcount; i++)
562 if (FD_ISSET(srvtab[i].fd, &rdset))
563 handle_connection(i);
564 }
565 return 0;
566 }
567
568 /* Return the highest-numbered open file descriptor. */
569 static int
getmaxfd(void)570 getmaxfd(void)
571 {
572 int i = open("/dev/null", O_RDONLY);
573 if (i >= 0) {
574 close(i);
575 i--;
576 }
577 return i;
578 }
579
580 void
dicod_server(int argc,char ** argv)581 dicod_server(int argc, char **argv)
582 {
583 int rc;
584
585 dico_log(L_INFO, 0, _("%s started"), program_version);
586
587 if (user_id) {
588 if (getuid())
589 dico_log(L_NOTICE, 0,
590 _("not running as root: ignoring user/group settings"));
591 else if (switch_to_privs(user_id, group_id, group_list))
592 dico_die(EX_NOUSER, L_CRIT, 0, "exiting");
593 }
594
595 if (!foreground)
596 check_pidfile(pidfile_name);
597
598 signal(SIGTERM, sig_stop);
599 signal(SIGQUIT, sig_stop);
600 signal(SIGINT, sig_stop);
601 signal(SIGCHLD, sig_child);
602 if (argv[0][0] != '/') {
603 dico_log(L_WARN, 0, _("dicod started without full file name"));
604 dico_log(L_WARN, 0, _("restart (SIGHUP) will not work"));
605 signal(SIGHUP, sig_stop);
606 } else if (config_file[0] != '/') {
607 dico_log(L_WARN, 0, _("configuration file is not given with a full file name"));
608 dico_log(L_WARN, 0, _("restart (SIGHUP) will not work"));
609 signal(SIGHUP, sig_stop);
610 } else
611 signal(SIGHUP, sig_restart);
612
613 if (!foreground) {
614 if (daemon(0, 0) == -1)
615 dico_die(EX_OSERR, L_CRIT, errno, _("Cannot become a daemon"));
616
617 create_pidfile(pidfile_name);
618 }
619
620 open_sockets();
621 if (!single_process)
622 childtab = xcalloc(max_children, sizeof(childtab[0]));
623
624 rc = server_loop();
625
626 stop_children();
627 free(childtab);
628 dicod_server_cleanup();
629 close_sockets();
630
631 if (rc)
632 dico_log(L_NOTICE, errno, _("Exit code = %d, last error status"), rc);
633
634 if (!foreground)
635 remove_pidfile(pidfile_name);
636
637 if (restart) {
638 int i;
639
640 dico_log(L_INFO, 0, _("%s restarting"), program_version);
641 for (i = getmaxfd(); i > 2; i--)
642 close(i);
643 execv(argv[0], argv);
644 dico_die(EX_UNAVAILABLE, L_ERR|L_CONS, errno, _("Cannot restart"));
645 } else
646 dico_log(L_INFO, 0, _("%s terminating"), program_version);
647 exit(rc);
648 }
649
650
651
652
653
654
655
656
657
658