Fixed keepalive_thread() to check the "keepalive" configuration
[pwmd.git] / src / pwmd.c
blob5503347067f8142acaf734383f4ab8b0dd556a90
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2007 Ben Kibbey <bjk@luxsci.net>
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.
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.
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
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <sys/un.h>
27 #include <signal.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <pwd.h>
33 #include <glib.h>
34 #include <glib/gprintf.h>
35 #include <gcrypt.h>
36 #include <sys/mman.h>
37 #include <termios.h>
38 #include <assert.h>
39 #include <syslog.h>
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
45 #ifdef HAVE_SETRLIMIT
46 #include <sys/time.h>
47 #include <sys/resource.h>
48 #endif
50 #ifdef HAVE_ZLIB_H
51 #include <zlib.h>
52 #endif
54 #ifndef MEM_DEBUG
55 #include "mem.h"
56 #endif
58 #include "xml.h"
59 #include "common.h"
60 #include "commands.h"
61 #include "pwmd_error.h"
62 #include "cache.h"
63 #include "pwmd.h"
65 static void clear_rcfile_key()
67 gsize n;
68 gchar **groups;
69 gchar **p;
71 groups = g_key_file_get_groups(keyfileh, &n);
73 for (p = groups; *p; p++) {
74 GError *error = NULL;
76 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE)
77 g_key_file_set_string(keyfileh, *p, "key", "");
80 g_strfreev(groups);
83 static void reload_rcfile()
85 log_write(N_("reloading configuration file '%s'"), rcfile);
86 g_key_file_free(keyfileh);
87 keyfileh = parse_rcfile(0);
88 clear_rcfile_key();
91 gpg_error_t send_syserror(assuan_context_t ctx, gint e)
93 gpg_error_t n = gpg_error_from_errno(e);
95 return assuan_process_done(ctx, assuan_set_error(ctx, n, gpg_strerror(n)));
98 gpg_error_t send_error(assuan_context_t ctx, gpg_error_t e)
100 gpg_err_code_t n = gpg_err_code(e);
101 gpg_error_t code = gpg_err_make(GPG_ERR_SOURCE_USER_1, n);
103 if (!e)
104 return assuan_process_done(ctx, 0);
106 if (!ctx) {
107 log_write("%s\n", pwmd_strerror(e));
108 return e;
111 if (n == EPWMD_LIBXML_ERROR) {
112 xmlErrorPtr xml_error = xmlGetLastError();
113 return assuan_process_done(ctx, assuan_set_error(ctx, code, xml_error->message));
116 return assuan_process_done(ctx, assuan_set_error(ctx, code, pwmd_strerror(e)));
119 void log_write(const gchar *fmt, ...)
121 gchar *args, *line;
122 va_list ap;
123 struct tm *tm;
124 time_t now;
125 gchar tbuf[21];
126 gint fd = -1;
128 if ((!logfile && !isatty(STDERR_FILENO) && log_syslog == FALSE) || !fmt)
129 return;
131 if (logfile) {
132 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
133 warn(N_("logfile"));
134 return;
138 va_start(ap, fmt);
140 if (g_vasprintf(&args, fmt, ap) == -1) {
141 va_end(ap);
142 return;
145 if (log_syslog == TRUE)
146 syslog(LOG_INFO, "%s", args);
148 va_end(ap);
149 time(&now);
150 tm = localtime(&now);
151 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
152 tbuf[sizeof(tbuf) - 1] = 0;
153 line = g_strdup_printf("%s %i %s\n", tbuf, getpid(), args);
155 if (!line) {
156 if (logfile)
157 close(fd);
159 g_free(args);
160 return;
163 if (logfile) {
164 write(fd, line, strlen(line));
165 fsync(fd);
166 close(fd);
169 if (isatty(STDERR_FILENO)) {
170 fprintf(stderr, "%s", line);
171 fflush(stderr);
174 g_free(line);
175 g_free(args);
178 static void usage(gchar *pn)
180 g_printf(N_(
181 "Usage: %s [-hvDb] [-f <rcfile>] [-I <filename>] [file1] [...]\n"
182 " -b run as a background process\n"
183 " -f load the specified rcfile (~/.pwmd/config)\n"
184 " -I import an XML file and write the encrypted data to stdout\n"
185 " -D disable use of the LIST and DUMP commands\n"
186 " -v version\n"
187 " -h this help text\n"
188 ), pn);
189 exit(EXIT_SUCCESS);
192 #ifndef MEM_DEBUG
193 static int gcry_SecureCheck(const void *ptr)
195 return 1;
197 #endif
199 static void setup_gcrypt()
201 gcry_check_version(NULL);
203 #ifndef MEM_DEBUG
204 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
205 xfree);
206 #endif
208 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
209 NULL) != 0)
210 errx(EXIT_FAILURE, N_("Required AES cipher not supported by libgcrypt."));
212 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
213 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
216 static assuan_context_t new_connection(gint fd)
218 gpg_error_t rc;
219 gchar ver[ASSUAN_LINELENGTH];
220 assuan_context_t ctx;
222 rc = assuan_init_socket_server_ext(&ctx, fd, 2);
224 if (rc)
225 goto fail;
227 g_snprintf(ver, sizeof(ver), "%s", PACKAGE_STRING);
228 assuan_set_hello_line(ctx, ver);
229 assuan_register_post_cmd_notify(ctx, command_finalize);
230 rc = register_commands(ctx);
232 if (rc)
233 goto fail;
235 rc = assuan_accept(ctx);
237 if (rc)
238 goto fail;
240 return ctx;
242 fail:
243 assuan_deinit_server(ctx);
244 log_write("%s", gpg_strerror(rc));
245 return NULL;
248 void send_cache_status_all()
250 guint i, t;
252 for (t = g_slist_length(cn_thread_list), i = 0; i < t; i++) {
253 struct client_thread_s *cn = g_slist_nth_data(cn_thread_list, i);
255 if (cn->tid == pth_self()) {
256 send_cache_status(cn->cl->ctx);
257 continue;
260 pth_raise(cn->tid, SIGUSR2);
264 static void *keepalive_thread(void *arg)
266 struct client_thread_s *thd = arg;
267 pth_event_t ev;
268 sigset_t set;
270 sigemptyset(&set);
271 sigaddset(&set, SIGALRM);
273 for (;;) {
274 gint sig = 0;
275 gpg_error_t error;
276 gint k = get_key_file_integer("default", "keepalive");
278 if (k <= 0)
279 break;
281 ev = pth_event(PTH_EVENT_TIME, pth_timeout(k, 0));
282 pth_sigwait_ev(&set, &sig, ev);
283 pth_event_free(ev, PTH_FREE_THIS);
285 if (sig)
286 break;
288 error = assuan_write_status(thd->cl->ctx, "KEEPALIVE", NULL);
290 if (error) {
291 pth_raise(thd->tid, SIGQUIT);
292 break;
296 return NULL;
300 * Called every time a connection is made via pth_spawn(). This is the thread
301 * entry point.
303 static void *client_thread(void *data)
305 struct client_thread_s *thd = data;
306 gint fd = thd->fd;
307 pth_event_t ev;
308 struct client_s *cl = g_malloc0(sizeof(struct client_s));
309 sigset_t set;
311 if (!cl) {
312 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
313 goto fail;
316 thd->cl = cl;
319 * This is a "child" thread. Don't catch any signals. Let the master
320 * thread take care of signals in server_loop().
322 sigfillset(&set);
323 pth_sigmask(SIG_BLOCK, &set, NULL);
324 sigemptyset(&set);
325 /* Sends a cache status message to the client. */
326 sigaddset(&set, SIGUSR2);
327 /* Terminates this thread. Raised from keepalive_thread(). */
328 sigaddset(&set, SIGQUIT);
330 cl->ctx = new_connection(fd);
331 cl->fd = fd;
333 if (!cl->ctx)
334 goto fail;
336 assuan_set_pointer(cl->ctx, cl);
338 #ifdef HAVE_MLOCKALL
339 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
340 log_write("mlockall(): %s", strerror(errno));
341 goto fail;
343 #endif
345 send_cache_status(cl->ctx);
346 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE, cl->fd);
347 thd->keepalive_tid = pth_spawn(NULL, keepalive_thread, thd);
349 for (;;) {
350 gpg_error_t rc;
351 gint sig = 0;
353 pth_sigwait_ev(&set, &sig, ev);
355 if (sig > 0) {
356 switch (sig) {
357 case SIGUSR2:
358 send_cache_status(cl->ctx);
359 continue;
360 case SIGQUIT:
361 goto done;
362 default:
363 break;
367 if (pth_event_occurred(ev)) {
368 rc = assuan_process_next(cl->ctx);
370 if (rc) {
371 cl->inquire = INQUIRE_NONE;
373 if (gpg_err_code(rc) == GPG_ERR_EOF)
374 goto done;
376 log_write("assuan_process_next(): %s", gpg_strerror(rc));
377 rc = assuan_process_done(cl->ctx, rc);
379 if (rc) {
380 log_write("assuan_process_done(): %s", gpg_strerror(rc));
381 goto done;
384 else {
385 /* Set from store_command_finalize(). */
386 if (cl->inquire == INQUIRE_OK || cl->inquire_error) {
387 cl->inquire = INQUIRE_NONE;
388 rc = assuan_process_done(cl->ctx, gpg_err_make(GPG_ERR_SOURCE_ANY, cl->inquire_error));
389 cl->inquire_error = 0;
391 if (rc) {
392 log_write("assuan_process_done(): %s", gpg_strerror(rc));
393 goto done;
401 * Client cleanup (including XML data) is done in remove_connection() from
402 * the cleanup thread.
404 done:
405 pth_event_free(ev, PTH_FREE_ALL);
407 fail:
408 pth_exit(NULL);
409 return NULL;
413 * Make sure all settings are set to either the specified setting or a
414 * default.
416 static void set_rcfile_defaults(GKeyFile *kf)
418 gchar buf[PATH_MAX];
420 if (g_key_file_has_key(kf, "default", "socket_path", NULL) == FALSE) {
421 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
422 g_key_file_set_string(kf, "default", "socket_path", buf);
425 if (g_key_file_has_key(kf, "default", "data_directory", NULL) == FALSE) {
426 snprintf(buf, sizeof(buf), "~/.pwmd/data");
427 g_key_file_set_string(kf, "default", "data_directory", buf);
430 if (g_key_file_has_key(kf, "default", "log_path", NULL) == FALSE) {
431 snprintf(buf, sizeof(buf), "~/.pwmd/log");
432 g_key_file_set_string(kf, "default", "log_path", buf);
435 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == FALSE)
436 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
438 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == FALSE)
439 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
441 #ifdef HAVE_MLOCKALL
442 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == FALSE)
443 g_key_file_set_boolean(kf, "default", "disable_mlockall", TRUE);
444 #endif
446 if (g_key_file_has_key(kf, "default", "cache_timeout", NULL) == FALSE)
447 g_key_file_set_integer(kf, "default", "cache_timeout", -1);
449 if (g_key_file_has_key(kf, "default", "iterations", NULL) == FALSE)
450 g_key_file_set_integer(kf, "default", "iterations", 0);
452 if (g_key_file_has_key(kf, "default", "disable_list_and_dump", NULL) == FALSE)
453 g_key_file_set_boolean(kf, "default", "disable_list_and_dump", FALSE);
455 if (g_key_file_has_key(kf, "default", "iteration_progress", NULL) == FALSE)
456 g_key_file_set_integer(kf, "default", "iteration_progress", 0);
458 if (g_key_file_has_key(kf, "default", "compression_level", NULL) == FALSE)
459 g_key_file_set_integer(kf, "default", "compression_level", 6);
461 if (g_key_file_has_key(kf, "default", "recursion_depth", NULL) == FALSE)
462 g_key_file_set_integer(kf, "default", "recursion_depth", DEFAULT_RECURSION_DEPTH);
464 if (g_key_file_has_key(kf, "default", "zlib_bufsize", NULL) == FALSE)
465 g_key_file_set_integer(kf, "default", "zlib_bufsize", DEFAULT_ZLIB_BUFSIZE);
467 zlib_bufsize = g_key_file_get_integer(kf, "default", "zlib_bufsize", NULL);
469 max_recursion_depth = g_key_file_get_integer(kf, "default", "recursion_depth", NULL);
470 disable_list_and_dump = g_key_file_get_boolean(kf, "default", "disable_list_and_dump", NULL);
472 #ifdef HAVE_MLOCKALL
473 disable_mlock = g_key_file_get_boolean(kf, "default", "disable_mlockall", NULL);
474 #endif
476 if (g_key_file_has_key(kf, "default", "syslog", NULL) == FALSE)
477 g_key_file_set_boolean(kf, "default", "syslog", FALSE);
479 if (g_key_file_has_key(kf, "default", "keepalive", NULL) == FALSE)
480 g_key_file_set_integer(kf, "default", "keepalive", 5);
483 static GKeyFile *parse_rcfile(int cmdline)
485 GKeyFile *kf = g_key_file_new();
486 GError *error = NULL;
488 if (g_key_file_load_from_file(kf, rcfile, G_KEY_FILE_NONE, &error) == FALSE) {
489 log_write("%s: %s", rcfile, error->message);
491 if (cmdline)
492 exit(EXIT_FAILURE);
494 if (error->code == G_FILE_ERROR_NOENT) {
495 g_clear_error(&error);
496 set_rcfile_defaults(kf);
497 return kf;
500 g_clear_error(&error);
501 return NULL;
503 else
504 set_rcfile_defaults(kf);
506 return kf;
509 static gchar *get_password(const gchar *prompt)
511 gchar buf[LINE_MAX], *p;
512 struct termios told, tnew;
513 gchar *key;
515 if (tcgetattr(STDIN_FILENO, &told) == -1)
516 err(EXIT_FAILURE, "tcgetattr()");
518 memcpy(&tnew, &told, sizeof(struct termios));
519 tnew.c_lflag &= ~(ECHO);
520 tnew.c_lflag |= ICANON|ECHONL;
522 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
523 tcsetattr(STDIN_FILENO, TCSANOW, &told);
524 err(EXIT_FAILURE, "tcsetattr()");
527 fprintf(stderr, "%s", prompt);
529 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
530 tcsetattr(STDIN_FILENO, TCSANOW, &told);
531 return NULL;
534 tcsetattr(STDIN_FILENO, TCSANOW, &told);
535 p[strlen(p) - 1] = 0;
537 if (!p || !*p)
538 return NULL;
540 key = gcry_malloc(strlen(p) + 1);
541 sprintf(key, "%s", p);
542 memset(&buf, 0, sizeof(buf));
543 return key;
546 static gboolean do_try_xml_decrypt(const gchar *filename, guchar *key)
548 int fd;
549 struct stat st;
550 gpg_error_t error;
552 if ((fd = open_file(filename, &st)) == -1) {
553 warn("%s", filename);
554 return FALSE;
557 if (st.st_size == 0) {
558 warnx(N_("%s: skipping empty file"), filename);
559 close(fd);
560 return FALSE;
563 error = try_xml_decrypt(NULL, fd, st, key);
564 close(fd);
565 return error ? FALSE : TRUE;
568 static gboolean get_input(const gchar *filename, guchar *key)
570 gint try = 0;
571 gchar *password;
572 gchar *prompt;
574 prompt = g_strdup_printf(N_("Password for '%s': "), filename);
576 again:
577 if ((password = get_password(prompt)) == NULL) {
578 warnx(N_("%s: skipping file"), filename);
579 g_free(prompt);
580 return FALSE;
583 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
584 gcry_free(password);
586 if (do_try_xml_decrypt(filename, key) == FALSE) {
587 if (try++ == 2) {
588 warnx(N_("%s: invalid password, skipping"), filename);
589 g_free(prompt);
590 return FALSE;
592 else {
593 warnx(N_("%s: invalid password"), filename);
594 goto again;
598 g_free(prompt);
599 return TRUE;
602 static gboolean xml_import(const gchar *filename, gint iter)
604 xmlDocPtr doc;
605 gint fd;
606 struct stat st;
607 gint len;
608 xmlChar *xmlbuf;
609 xmlChar *xml;
610 gchar *key = NULL;
611 gchar *key2 = NULL;
612 guchar shakey[gcrykeysize];
613 gcry_cipher_hd_t gh;
614 gpg_error_t error;
615 gint level;
616 glong outsize;
617 gpointer outbuf;
618 gint zerror;
620 if (stat(filename, &st) == -1) {
621 warn("%s", filename);
622 return FALSE;
625 if ((error = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
626 send_error(NULL, error);
627 gcry_cipher_close(gh);
628 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
629 return FALSE;
632 if ((key = get_password(N_("New password: "))) == NULL) {
633 fprintf(stderr, "%s\n", N_("Invalid password."));
634 gcry_cipher_close(gh);
635 return FALSE;
638 if ((key2 = get_password(N_("Verify password: "))) == NULL) {
639 fprintf(stderr, "%s\n", N_("Passwords do not match."));
640 gcry_free(key);
641 gcry_cipher_close(gh);
642 return FALSE;
645 if (g_utf8_collate(key, key2) != 0) {
646 fprintf(stderr, "%s\n", N_("Passwords do not match."));
647 gcry_free(key);
648 gcry_free(key2);
649 gcry_cipher_close(gh);
650 return FALSE;
653 gcry_free(key2);
655 if ((fd = open(filename, O_RDONLY)) == -1) {
656 gcry_free(key);
657 warn("%s", filename);
658 gcry_cipher_close(gh);
659 return FALSE;
662 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
663 gcry_free(key);
664 close(fd);
665 log_write("%s", strerror(ENOMEM));
666 gcry_cipher_close(gh);
667 return FALSE;
670 if (read(fd, xmlbuf, st.st_size) == -1) {
671 error = errno;
672 close(fd);
673 gcry_free(key);
674 gcry_cipher_close(gh);
675 errno = error;
676 err(EXIT_FAILURE, "read()");
679 close(fd);
680 xmlbuf[st.st_size] = 0;
683 * Make sure the document validates.
685 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
686 log_write("xmlReadDoc()");
687 close(fd);
688 gcry_free(key);
689 gcry_free(xmlbuf);
690 gcry_cipher_close(gh);
691 return FALSE;
694 gcry_free(xmlbuf);
695 xmlDocDumpMemory(doc, &xml, &len);
696 xmlFreeDoc(doc);
698 level = get_key_file_integer(filename, "compression_level");
700 if (level < 0)
701 level = 0;
703 if (do_compress(NULL, level, xml, len, &outbuf, &outsize, &zerror) == FALSE) {
704 memset(shakey, 0, sizeof(shakey));
705 gcry_free(xml);
707 if (zerror == Z_MEM_ERROR)
708 warnx("%s", strerror(ENOMEM));
709 else
710 warnx("do_compress() failed");
712 gcry_cipher_close(gh);
713 return FALSE;
715 else {
716 gcry_free(xml);
717 xml = outbuf;
718 len = outsize;
721 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
722 gcry_free(key);
723 error = do_xml_encrypt(NULL, gh, NULL, xml, len, shakey, iter);
724 gcry_cipher_close(gh);
726 if (error) {
727 memset(shakey, 0, sizeof(shakey));
728 warnx("%s", gpg_strerror(error));
729 return FALSE;
732 memset(shakey, 0, sizeof(shakey));
733 return TRUE;
736 gchar *get_key_file_string(const gchar *section, const gchar *what)
738 gchar *val = NULL;
739 GError *gerror = NULL;
741 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
742 val = g_key_file_get_string(keyfileh, section, what, &gerror);
744 if (gerror) {
745 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
746 g_clear_error(&gerror);
749 else {
750 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
751 val = g_key_file_get_string(keyfileh, "default", what, &gerror);
753 if (gerror) {
754 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
755 g_clear_error(&gerror);
760 return val;
763 gint get_key_file_integer(const gchar *section, const gchar *what)
765 gint val = -1;
766 GError *gerror = NULL;
768 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
769 val = g_key_file_get_integer(keyfileh, section, what, &gerror);
771 if (gerror) {
772 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
773 g_clear_error(&gerror);
776 else {
777 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
778 val = g_key_file_get_integer(keyfileh, "default", what, &gerror);
780 if (gerror) {
781 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
782 g_clear_error(&gerror);
787 return val;
790 gboolean get_key_file_boolean(const gchar *section, const gchar *what)
792 gboolean val = FALSE;
793 GError *gerror = NULL;
795 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
796 val = g_key_file_get_boolean(keyfileh, section, what, &gerror);
798 if (gerror) {
799 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
800 g_clear_error(&gerror);
803 else {
804 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
805 val = g_key_file_get_boolean(keyfileh, "default", what, &gerror);
807 if (gerror) {
808 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
809 g_clear_error(&gerror);
814 return val;
817 gchar *expand_homedir(gchar *str)
819 gchar *p = str;
821 if (*p++ == '~')
822 return g_strdup_printf("%s%s", g_get_home_dir(), p);
824 return g_strdup(str);
827 static gchar *_getline(const gchar *file)
829 FILE *fp;
830 gchar buf[LINE_MAX], *p;
831 gchar *str = NULL;
833 if ((fp = fopen(file, "r")) == NULL) {
834 warn("%s", file);
835 return NULL;
838 if ((p = fgets(buf, sizeof(buf), fp)) == NULL) {
839 warnx(N_("%s: empty file?"), file);
840 return NULL;
843 fclose(fp);
845 if (buf[strlen(buf) - 1] == '\n')
846 buf[strlen(buf) - 1] = 0;
848 str = gcry_malloc(strlen(p) + 1);
849 memcpy(str, p, strlen(p));
850 str[strlen(p)] = 0;
851 memset(&buf, 0, sizeof(buf));
852 return str;
855 static gboolean parse_keyfile_key()
857 gsize n;
858 gchar **groups;
859 gchar **p;
860 gchar *str;
862 groups = g_key_file_get_groups(keyfileh, &n);
864 for (p = groups; *p; p++) {
865 GError *error = NULL;
867 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE) {
868 str = g_key_file_get_string(keyfileh, *p, "key", &error);
870 if (!str) {
871 if (error) {
872 warnx("%s", error->message);
873 g_clear_error(&error);
876 continue;
879 do_cache_push(*p, str);
880 g_free(str);
881 continue;
884 if (error) {
885 warnx("%s", error->message);
886 g_clear_error(&error);
887 continue;
890 if (g_key_file_has_key(keyfileh, *p, "key_file", &error) == TRUE) {
891 gchar *t;
892 gchar *file = g_key_file_get_string(keyfileh, *p, "key_file", &error);
894 if (!file) {
895 if (error) {
896 warnx("%s", error->message);
897 g_clear_error(&error);
900 continue;
903 t = expand_homedir(file);
904 g_free(file);
905 file = t;
907 if ((str = _getline(file)) == NULL) {
908 g_free(file);
909 continue;
912 g_free(file);
913 do_cache_push(*p, str);
914 gcry_free(str);
915 continue;
918 if (error) {
919 warnx("%s", error->message);
920 g_clear_error(&error);
924 g_strfreev(groups);
925 return TRUE;
928 static gboolean do_cache_push(const gchar *filename, const gchar *password)
930 guchar *md5file;
931 guchar *key;
932 gint timeout;
933 const gchar *p = filename;
935 while (isspace(*p))
936 p++;
938 if (!*p)
939 return FALSE;
941 if (valid_filename(p) == FALSE) {
942 warnx(N_("%s: invalid characters in filename"), p);
943 return FALSE;
946 md5file = gcry_malloc(16);
947 key = gcry_malloc(gcrykeysize);
948 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
950 if (cache_iscached(md5file) == TRUE) {
951 warnx(N_("%s: file already cached, skipping"), p);
952 gcry_free(md5file);
953 gcry_free(key);
954 return FALSE;
957 if (access(p, R_OK|W_OK) != 0) {
958 gcry_free(md5file);
959 gcry_free(key);
961 if (errno != ENOENT) {
962 warn("%s", p);
963 return FALSE;
966 warn("%s", p);
967 return TRUE;
970 if (!password) {
971 if (get_input(p, key) == FALSE) {
972 gcry_free(key);
973 gcry_free(md5file);
974 return FALSE;
977 else {
978 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
980 if (do_try_xml_decrypt(filename, key) == FALSE) {
981 warnx(N_("%s: invalid password, skipping"), filename);
982 gcry_free(key);
983 gcry_free(md5file);
984 return FALSE;
988 if (cache_add_file(md5file, key) == FALSE) {
989 warnx("%s: %s", p, pwmd_strerror(EPWMD_MAX_SLOTS));
990 gcry_free(key);
991 gcry_free(md5file);
992 return FALSE;
995 timeout = get_key_file_integer(p, "cache_timeout");
996 cache_set_timeout(md5file, timeout);
997 warnx(N_("%s: file added to the cache"), filename);
998 gcry_free(key);
999 gcry_free(md5file);
1000 return TRUE;
1003 static GSList *remove_connection(GSList *list, struct client_thread_s *cn)
1005 gpointer value;
1006 struct client_s *cl = cn->cl;
1008 if (cn->keepalive_tid) {
1009 pth_raise(cn->keepalive_tid, SIGALRM);
1010 pth_join(cn->keepalive_tid, &value);
1013 pth_join(cn->tid, &value);
1014 close(cn->fd);
1016 if (cl->freed == FALSE)
1017 cleanup_assuan(cl->ctx);
1019 if (cl->ctx)
1020 assuan_deinit_server(cl->ctx);
1022 if (cl)
1023 g_free(cl);
1025 pth_event_isolate(cn->ev);
1026 pth_event_free(cn->ev, PTH_FREE_THIS);
1027 log_write(N_("client exited: fd=%i"), cn->fd);
1028 list = g_slist_remove(list, cn);
1029 g_free(cn);
1030 return list;
1034 * Can't pth_event_concat() to an empty event.
1036 static int event_ring_hack(void *data)
1038 return FALSE;
1042 * See if any thread has entered the DEAD queue and remove it.
1044 static GSList *cleanup_dead_queue(GSList *threads, pth_event_t tid_events)
1046 guint n, i;
1048 for (n = g_slist_length(threads), i = 0; i < n; i++) {
1049 struct client_thread_s *cn = g_slist_nth_data(threads, i);
1051 if (pth_event_occurred(cn->ev)) {
1052 threads = remove_connection(threads, cn);
1053 return cleanup_dead_queue(threads, tid_events);
1056 pth_event_concat(tid_events, cn->ev, NULL);
1059 return threads;
1062 static void *socket_thread(void *arg)
1064 gint sockfd = (gint)arg;
1065 pth_attr_t attr;
1068 * Thread priority is inherited from the calling thread. This thread is
1069 * PTH_PRIO_MAX. Keep the "child" threads at standard priority.
1071 attr = pth_attr_new();
1072 pth_attr_set(attr, PTH_ATTR_PRIO, PTH_PRIO_STD);
1074 for (;;) {
1075 socklen_t slen = sizeof(struct sockaddr_un);
1076 struct sockaddr_un raddr;
1077 gint fd = -1;
1079 if ((fd = pth_accept(sockfd, (struct sockaddr *)&raddr, &slen)) == -1) {
1080 if (errno != EAGAIN) {
1081 if (!quit) // probably EBADF
1082 log_write("accept(): %s", strerror(errno));
1084 break;
1088 if (fd >= 0) {
1089 pth_t tid;
1090 struct client_thread_s *new;
1092 new = g_malloc0(sizeof(struct client_thread_s));
1094 if (!new) {
1095 log_write("%s", strerror(ENOMEM));
1096 continue;
1099 log_write(N_("new connection: fd=%i"), fd);
1100 new->fd = fd;
1101 tid = pth_spawn(attr, client_thread, new);
1103 if (!tid) {
1104 g_free(new);
1105 log_write(N_("pth_spawn() failed"));
1106 continue;
1109 new->tid = tid;
1110 new->fd = fd;
1111 new->ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD, tid);
1112 pth_mutex_acquire(&cn_mutex, FALSE, NULL);
1113 pth_event_concat(cn_events, new->ev, NULL);
1114 cn_thread_list = g_slist_append(cn_thread_list, new);
1115 pth_mutex_release(&cn_mutex);
1119 pth_attr_destroy(attr);
1121 /* Just in case pth_accept() failed for some reason other than EBADF */
1122 quit = 1;
1123 pth_exit(PTH_CANCELED);
1124 return NULL;
1127 static void *cleanup_thread(void *arg)
1129 cn_events = pth_event(PTH_EVENT_TIME, pth_timeout(0, 500000));
1131 for (;;) {
1132 if (pth_wait(cn_events) && pth_event_occurred(cn_events)) {
1133 pth_mutex_acquire(&cn_mutex, FALSE, NULL);
1134 pth_event_isolate(cn_events);
1135 pth_event_free(cn_events, PTH_FREE_THIS);
1136 cn_events = pth_event(PTH_EVENT_TIME, pth_timeout(0, 500000));
1137 cn_thread_list = cleanup_dead_queue(cn_thread_list, cn_events);
1138 pth_mutex_release(&cn_mutex);
1142 pth_event_isolate(cn_events);
1143 pth_event_free(cn_events, PTH_FREE_THIS);
1144 return NULL;
1147 static void *adjust_cache_time_thread(void *arg)
1149 CACHE_LOCK(NULL, NULL);
1150 cache_adjust_timer();
1151 CACHE_UNLOCK;
1152 return NULL;
1155 static void server_loop(int sockfd)
1157 pth_t socket_tid, cleanup_tid;
1158 guint n, i;
1159 sigset_t set;
1160 gint n_clients = 0;
1161 pth_attr_t attr;
1162 pth_event_t timeout_event;
1163 gpointer value;
1165 pth_mutex_init(&cn_mutex);
1166 log_write(N_("%s started for user %s"), PACKAGE_STRING, g_get_user_name());
1168 sigemptyset(&set);
1169 sigaddset(&set, SIGTERM);
1170 sigaddset(&set, SIGINT);
1171 sigaddset(&set, SIGUSR1);
1172 sigaddset(&set, SIGHUP);
1173 sigaddset(&set, SIGABRT);
1175 attr = pth_attr_new();
1176 pth_attr_init(attr);
1177 pth_attr_set(attr, PTH_ATTR_PRIO, PTH_PRIO_MAX);
1178 socket_tid = pth_spawn(attr, socket_thread, (void *)sockfd);
1179 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
1180 pth_attr_set(attr, PTH_ATTR_CANCEL_STATE, PTH_CANCEL_ASYNCHRONOUS);
1181 cleanup_tid = pth_spawn(attr, cleanup_thread, NULL);
1184 * For the cache_timeout configuration parameter. This replaces the old
1185 * SIGALRM stuff and is safer.
1187 timeout_event = pth_event(PTH_EVENT_TIME, pth_timeout(1, 0));
1188 pth_attr_init(attr);
1189 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
1190 pth_attr_set(attr, PTH_ATTR_PRIO, PTH_PRIO_MAX);
1192 do {
1193 gint sig = 0;
1195 pth_sigwait_ev(&set, &sig, timeout_event);
1197 if (pth_event_occurred(timeout_event)) {
1199 * The timer event has expired. Update the file cache. When the
1200 * cache mutex is locked and the timer expires again, the threads
1201 * will stack.
1203 pth_spawn(attr, adjust_cache_time_thread, NULL);
1204 pth_event_free(timeout_event, PTH_FREE_THIS);
1205 timeout_event = pth_event(PTH_EVENT_TIME, pth_timeout(1, 0));
1208 if (sig > 0) {
1209 log_write(N_("caught signal %i (%s)"), sig, strsignal(sig));
1211 /* Caught a signal. */
1212 switch (sig) {
1213 case SIGUSR1:
1214 reload_rcfile();
1215 break;
1216 case SIGABRT:
1217 CACHE_LOCK(NULL, NULL);
1218 cache_clear(NULL, 2);
1219 CACHE_UNLOCK;
1220 #ifndef MEM_DEBUG
1221 xpanic();
1222 #endif
1223 exit(EXIT_FAILURE);
1224 case SIGHUP:
1225 CACHE_LOCK(NULL, NULL);
1226 log_write(N_("clearing file cache"));
1227 cache_clear(NULL, 2);
1228 CACHE_UNLOCK;
1229 break;
1230 default:
1231 quit = 1;
1232 shutdown(sockfd, SHUT_RDWR);
1233 close(sockfd);
1234 break;
1237 } while (!quit);
1240 * We're out of the main server loop. This happens when a signal was sent
1241 * to terminate the daemon. We'll wait for all clients to disconnect
1242 * before exiting and ignore any following signals.
1244 pth_join(socket_tid, &value);
1245 pth_attr_destroy(attr);
1246 pth_event_free(timeout_event, PTH_FREE_THIS);
1248 n = pth_ctrl(PTH_CTRL_GETTHREADS);
1250 /* 2 because the cleanup thread still exists, plus self. */
1251 if (n > 2)
1252 log_write(N_("waiting for all threads to terminate"));
1254 while (n > 2) {
1255 gint t;
1256 pth_event_t events;
1258 if (n != n_clients) {
1259 log_write(N_("%i threads remain"), n-2);
1260 n_clients = n;
1263 events = pth_event(PTH_EVENT_FUNC, event_ring_hack, NULL, pth_timeout(1, 0));
1264 pth_mutex_acquire(&cn_mutex, FALSE, NULL);
1266 for (t = g_slist_length(cn_thread_list), i = 0; i < t; i++) {
1267 struct client_thread_s *cn = g_slist_nth_data(cn_thread_list, i);
1269 pth_event_concat(events, cn->ev, NULL);
1272 if (!t)
1273 goto done;
1275 pth_mutex_release(&cn_mutex);
1276 pth_wait(events);
1277 pth_yield(cleanup_tid);
1278 done:
1279 pth_event_isolate(events);
1280 pth_event_free(events, PTH_FREE_THIS);
1281 n = pth_ctrl(PTH_CTRL_GETTHREADS);
1284 pth_cancel(cleanup_tid);
1287 int main(int argc, char *argv[])
1289 gint opt;
1290 struct sockaddr_un addr;
1291 struct passwd *pw = getpwuid(getuid());
1292 gchar buf[PATH_MAX];
1293 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
1294 gchar *socketarg = NULL;
1295 gchar *datadir = NULL;
1296 gboolean n;
1297 gchar *p;
1298 gchar **cache_push = NULL;
1299 gint iter = 0;
1300 gchar *import = NULL;
1301 gint default_timeout;
1302 gint rcfile_spec = 0;
1303 gint estatus = EXIT_FAILURE;
1304 gint sockfd;
1305 #ifndef MEM_DEBUG
1306 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
1307 #endif
1308 gint do_unlink = 1;
1309 gboolean secure = FALSE;
1310 guint ptotal = 0;
1311 gint background = 0;
1312 sigset_t set;
1313 #ifndef DEBUG
1314 #ifdef HAVE_SETRLIMIT
1315 struct rlimit rl;
1317 rl.rlim_cur = rl.rlim_max = 0;
1319 if (setrlimit(RLIMIT_CORE, &rl) != 0)
1320 err(EXIT_FAILURE, "setrlimit()");
1321 #endif
1322 #endif
1324 #ifdef ENABLE_NLS
1325 setlocale(LC_ALL, "");
1326 bindtextdomain("pwmd", LOCALEDIR);
1327 textdomain("pwmd");
1328 #endif
1330 gpg_err_init();
1331 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
1332 #ifndef MEM_DEBUG
1333 g_mem_set_vtable(&mtable);
1334 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
1335 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
1336 xmlInitMemory();
1337 #endif
1338 pth_init();
1339 snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
1341 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
1342 err(EXIT_FAILURE, "%s", buf);
1344 snprintf(buf, sizeof(buf), "%s/.pwmd/data", pw->pw_dir);
1346 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
1347 err(EXIT_FAILURE, "%s", buf);
1349 rcfile = g_strdup_printf("%s/.pwmd/config", pw->pw_dir);
1351 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
1352 err(EXIT_FAILURE, "sysconf()");
1354 cache_size = page_size;
1356 while ((opt = getopt(argc, argv, "bI:hvf:D")) != EOF) {
1357 switch (opt) {
1358 case 'b':
1359 background = 1;
1360 break;
1361 case 'D':
1362 secure = TRUE;
1363 break;
1364 case 'I':
1365 import = optarg;
1366 break;
1367 case 'f':
1368 g_free(rcfile);
1369 rcfile = g_strdup(optarg);
1370 rcfile_spec = 1;
1371 break;
1372 case 'v':
1373 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
1374 exit(EXIT_SUCCESS);
1375 case 'h':
1376 default:
1377 usage(argv[0]);
1381 if ((keyfileh = parse_rcfile(rcfile_spec)) == NULL)
1382 exit(EXIT_FAILURE);
1384 if (g_key_file_has_key(keyfileh, "default", "syslog", NULL) == TRUE)
1385 log_syslog = g_key_file_get_boolean(keyfileh, "default", "syslog", NULL);
1387 if (log_syslog == TRUE)
1388 openlog("pwmd", LOG_NDELAY|LOG_PID, LOG_DAEMON);
1390 if (g_key_file_has_key(keyfileh, "default", "iterations", NULL) == TRUE)
1391 iter = g_key_file_get_integer(keyfileh, "default", "iterations", NULL);
1393 #ifdef HAVE_MLOCKALL
1394 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
1395 warn("mlockall()");
1396 goto do_exit;
1398 #endif
1400 setup_gcrypt();
1402 if (import) {
1403 opt = xml_import(import, iter);
1404 g_key_file_free(keyfileh);
1405 g_free(rcfile);
1406 exit(opt == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
1409 g_key_file_set_list_separator(keyfileh, ',');
1411 if ((p = g_key_file_get_string(keyfileh, "default", "socket_path", NULL)) == NULL)
1412 errx(EXIT_FAILURE, N_("%s: socket_path not defined"), rcfile);
1414 if (*p == '~') {
1415 p++;
1416 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1417 g_free(p);
1418 socketarg = g_strdup(buf);
1420 else
1421 socketarg = p;
1423 if ((p = g_key_file_get_string(keyfileh, "default", "data_directory", NULL)) == NULL)
1424 errx(EXIT_FAILURE, N_("%s: data_directory not defined"), rcfile);
1426 datadir = expand_homedir(p);
1427 g_free(p);
1429 if (secure == FALSE && g_key_file_has_key(keyfileh, "default", "disable_list_and_dump", NULL) == TRUE) {
1430 n = g_key_file_get_boolean(keyfileh, "default", "disable_list_and_dump", NULL);
1431 disable_list_and_dump = n;
1433 else
1434 disable_list_and_dump = secure;
1436 if (g_key_file_has_key(keyfileh, "default", "cache_timeout", NULL) == TRUE)
1437 default_timeout = g_key_file_get_integer(keyfileh, "default", "cache_timeout", NULL);
1438 else
1439 default_timeout = -1;
1441 if (g_key_file_has_key(keyfileh, "default", "cache_size", NULL) == TRUE) {
1442 cache_size = g_key_file_get_integer(keyfileh, "default", "cache_size", NULL);
1444 if (cache_size < page_size || cache_size % page_size)
1445 errx(EXIT_FAILURE, N_("cache size must be in multiples of %li"), page_size);
1448 if (g_key_file_has_key(keyfileh, "default", "log_path", NULL) == TRUE) {
1449 if (g_key_file_has_key(keyfileh, "default", "enable_logging", NULL) == TRUE) {
1450 n = g_key_file_get_boolean(keyfileh, "default", "enable_logging", NULL);
1452 if (n == TRUE) {
1453 p = g_key_file_get_string(keyfileh, "default", "log_path", NULL);
1455 if (*p == '~') {
1456 p++;
1457 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1458 g_free(p);
1459 logfile = g_strdup(buf);
1461 else
1462 logfile = p;
1467 if (g_key_file_has_key(keyfileh, "default", "cache_push", NULL) == TRUE)
1468 cache_push = g_key_file_get_string_list(keyfileh, "default", "cache_push", NULL, NULL);
1470 if (argc != optind) {
1471 if (cache_push)
1472 ptotal = g_strv_length(cache_push);
1474 for (; optind < argc; optind++) {
1475 if (strv_printf(&cache_push, "%s", argv[optind]) == FALSE)
1476 errx(EXIT_FAILURE, "%s", strerror(ENOMEM));
1480 if (strchr(socketarg, '/') == NULL) {
1481 socketdir = g_get_current_dir();
1482 socketname = g_strdup(socketarg);
1483 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1485 else {
1486 socketname = g_strdup(strrchr(socketarg, '/'));
1487 socketname++;
1488 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1489 socketdir = g_strdup(socketarg);
1490 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1493 if ((key_cache = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1494 #ifdef MMAP_ANONYMOUS
1495 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) {
1496 #else
1497 MAP_PRIVATE|MAP_ANON, -1, 0)) == MAP_FAILED) {
1498 #endif
1499 err(EXIT_FAILURE, "mmap()");
1502 if (mlock(key_cache, cache_size) == -1)
1503 warn("mlock()");
1505 memset(key_cache, 0, cache_size);
1507 if (chdir(datadir)) {
1508 warn("%s", datadir);
1509 unlink(socketpath);
1510 goto do_exit;
1513 if (parse_keyfile_key() == FALSE)
1514 goto do_exit;
1516 clear_rcfile_key();
1519 * Set the cache entry for a file. Prompts for the password.
1521 if (cache_push) {
1522 for (opt = 0; cache_push[opt]; opt++)
1523 do_cache_push(cache_push[opt], NULL);
1525 g_strfreev(cache_push);
1526 warnx(background ? N_("Done. Daemonizing...") : N_("Done. Waiting for connections..."));
1530 * bind() doesn't like the full pathname of the socket or any non alphanum
1531 * characters so change to the directory where the socket is wanted then
1532 * create it then change to datadir.
1534 if (chdir(socketdir)) {
1535 warn("%s", socketdir);
1536 goto do_exit;
1539 g_free(socketdir);
1541 if ((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
1542 warn("socket()");
1543 goto do_exit;
1546 addr.sun_family = AF_UNIX;
1547 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1549 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
1550 warn("bind()");
1552 if (errno == EADDRINUSE)
1553 warnx(N_("Either there is another pwmd running or '%s' is a \n"
1554 "stale socket. Please remove it manually."), socketpath);
1556 do_unlink = 0;
1557 goto do_exit;
1560 if (g_key_file_has_key(keyfileh, "default", "socket_perms", NULL) == TRUE) {
1561 gchar *t = g_key_file_get_string(keyfileh, "default", "socket_perms", NULL);
1562 mode_t mode = strtol(t, NULL, 8);
1563 mode_t mask = umask(0);
1565 g_free(t);
1567 if (chmod(socketname, mode) == -1) {
1568 warn("%s", socketname);
1569 close(sockfd);
1570 unlink(socketpath);
1571 umask(mask);
1572 goto do_exit;
1575 umask(mask);
1578 g_free(--socketname);
1580 if (chdir(datadir)) {
1581 warn("%s", datadir);
1582 close(sockfd);
1583 unlink(socketpath);
1584 goto do_exit;
1587 g_free(datadir);
1588 pth_mutex_init(&cache_mutex);
1590 if (listen(sockfd, 0) == -1) {
1591 warn("listen()");
1592 goto do_exit;
1595 if (background) {
1596 switch (fork()) {
1597 case -1:
1598 warn("fork()");
1599 goto do_exit;
1600 case 0:
1601 close(0);
1602 close(1);
1603 close(2);
1604 setsid();
1605 break;
1606 default:
1607 exit(EXIT_SUCCESS);
1612 * These are the signals that we use in threads. libpth can catch signals
1613 * itself so ignore them everywhere else. Note that using
1614 * signal(N, SIG_IGN) doesn't work like you might think.
1616 sigemptyset(&set);
1618 /* Termination */
1619 sigaddset(&set, SIGTERM);
1620 sigaddset(&set, SIGINT);
1622 /* Configuration file reloading. */
1623 sigaddset(&set, SIGUSR1);
1625 /* Clears the file cache. */
1626 sigaddset(&set, SIGHUP);
1628 /* Caught in client_thread(). Sends a cache status message. */
1629 sigaddset(&set, SIGUSR2);
1631 /* Caught in client_thread(). When keepalive_thread() fails, this signal
1632 * is raised to terminate the client. More of a failsafe than anything. */
1633 sigaddset(&set, SIGQUIT);
1635 /* Ignored everywhere. When a client disconnects abnormally this signal
1636 * gets raised. It isn't needed though because client_thread() will check
1637 * for errors even after the client disconnects. */
1638 sigaddset(&set, SIGPIPE);
1639 pth_sigmask(SIG_BLOCK, &set, NULL);
1640 server_loop(sockfd);
1641 estatus = EXIT_SUCCESS;
1643 do_exit:
1644 if (socketpath && do_unlink) {
1645 unlink(socketpath);
1646 g_free(socketpath);
1649 g_key_file_free(keyfileh);
1650 g_free(rcfile);
1652 if (key_cache)
1653 memset(key_cache, 0, cache_size);
1655 if (key_cache && munmap(key_cache, cache_size) == -1)
1656 log_write("munmap(): %s", strerror(errno));
1658 if (estatus == EXIT_SUCCESS)
1659 log_write(N_("pwmd exiting normally"));
1661 pth_kill();
1662 #if defined(DEBUG) && !defined(MEM_DEBUG)
1663 xdump();
1664 #endif
1665 exit(estatus);