Fix for compiling the library with g++.
[pwmd.git] / src / pwmd.c
blob6dcc4c8aa41b68e771a98bd85199080d9b033d5e
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/socket.h>
27 #include <sys/un.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <sys/wait.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <glib.h>
35 #include <glib/gprintf.h>
36 #include <gcrypt.h>
37 #include <sys/mman.h>
38 #include <termios.h>
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
44 #ifdef HAVE_SETRLIMIT
45 #include <sys/time.h>
46 #include <sys/resource.h>
47 #endif
49 #include "xml.h"
50 #include "common.h"
51 #include "commands.h"
52 #include "pwmd_error.h"
53 #include "pwmd.h"
55 void send_to_client(struct client_s *client, const gchar *fmt, ...)
57 va_list ap;
58 gint n = 0;
59 gchar **p;
61 va_start(ap, fmt);
63 if (client->outbuf)
64 for (p = client->outbuf, n = 0; *p; p++, n++);
66 if ((client->outbuf = g_realloc(client->outbuf, (n + 2) * sizeof(gchar *))) == NULL) {
67 warn("g_realloc()");
68 return;
71 client->outbuf[n++] = g_strdup_vprintf(fmt, ap);
72 client->outbuf[n] = NULL;
73 va_end(ap);
76 void send_error(struct client_s *client, int pwmd_errno)
78 send_to_client(client, "ERR %03i %s\n", pwmd_errno, pwmd_strerror(pwmd_errno));
81 void log_write(const gchar *fmt, ...)
83 gchar *args, *line;
84 va_list ap;
85 struct tm *tm;
86 time_t now;
87 gchar tbuf[21];
88 gint fd;
90 if (!logfile || !fmt)
91 return;
93 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
94 warn("logfile");
95 return;
98 va_start(ap, fmt);
99 g_vasprintf(&args, fmt, ap);
100 va_end(ap);
101 time(&now);
102 tm = localtime(&now);
103 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
104 tbuf[sizeof(tbuf) - 1] = 0;
105 line = g_strdup_printf("%s %i %s\n", tbuf, getpid(), args);
106 write(fd, line, strlen(line));
107 g_free(args);
108 g_free(line);
109 close(fd);
112 static void catchsig(gint sig)
114 gint status;
116 log_write("caught signal %i (%s)", sig, strsignal(sig));
118 switch (sig) {
119 case SIGCHLD:
120 waitpid(-1, &status, 0);
121 break;
122 case SIGHUP:
123 log_write("clearing file cache");
124 memset(shm_data, 0, cache_size);
125 break;
126 default:
127 quit = 1;
128 shutdown(sfd, SHUT_RDWR);
129 close(sfd);
130 break;
134 static void usage(gchar *pn)
136 g_printf(
137 "Usage: %s [-hv] [-f <rcfile>]\n"
138 " -f load the specified rcfile (~/.pwmdrc)\n"
139 " -v version\n"
140 " -h this help text\n",
141 pn);
142 exit(EXIT_SUCCESS);
145 gchar **split_input_line(gchar *str, gchar *delim, gint n)
147 if (!str || !*str)
148 return NULL;
150 return g_strsplit(str, delim, n);
153 static void setup_gcrypt()
155 gcry_check_version(NULL);
157 if (gcry_cipher_algo_info(GCRY_CIPHER_AES, GCRYCTL_TEST_ALGO, NULL,
158 NULL) != 0)
159 errx(EXIT_FAILURE, "AES cipher not supported");
161 gcry_cipher_algo_info(GCRY_CIPHER_AES, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
162 gcry_cipher_algo_info(GCRY_CIPHER_AES, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
165 static gint input_parser(gchar *str)
167 gchar *p, *t;
168 gchar **req = NULL;
170 if (str)
171 str = g_strchug(str);
173 if (!*str)
174 return P_OK;
176 while ((p = strsep(&str, "\n")) != NULL) {
177 if (g_ascii_strcasecmp(p, "QUIT") == 0)
178 return P_QUIT;
179 else if (g_ascii_strcasecmp(p, "HELP") == 0)
180 help_command(cl, NULL);
181 else if (g_ascii_strncasecmp(p, "HELP ", 5) == 0) {
182 t = p + 5;
183 t = g_strchug(t);
184 help_command(cl, t);
186 else if (g_ascii_strcasecmp(p, "LIST") == 0 ||
187 g_ascii_strncasecmp(p, "LIST ", 5) == 0) {
188 if (cl->state != STATE_OPEN)
189 send_error(cl, EPWMD_NO_FILE);
190 else
191 list_command(cl, p);
193 else if (g_ascii_strncasecmp(p, "STORE ", 6) == 0) {
194 t = p + 6;
195 t = g_strchug(t);
197 if (cl->state != STATE_OPEN)
198 send_error(cl, EPWMD_NO_FILE);
199 else {
200 if ((req = split_input_line(t, "\t", 0)) != NULL) {
201 if (store_command(cl, req) == TRUE)
202 send_to_client(cl, "OK \n");
204 else
205 send_error(cl, EPWMD_COMMAND_SYNTAX);
208 else if (g_ascii_strncasecmp(p, "DELETE ", 7) == 0) {
209 t = p + 7;
211 if (cl->state != STATE_OPEN)
212 send_error(cl, EPWMD_NO_FILE);
213 else {
214 if ((req = split_input_line(t, "\t", 0)) != NULL) {
215 if (delete_command(cl, req) == TRUE)
216 send_to_client(cl, "OK \n");
218 else
219 send_error(cl, EPWMD_COMMAND_SYNTAX);
222 else if (g_ascii_strncasecmp(p, "GET ", 4) == 0) {
223 t = p + 4;
224 t = g_strchug(t);
226 if (cl->state != STATE_OPEN)
227 send_error(cl, EPWMD_NO_FILE);
228 else {
229 if ((req = split_input_line(t, "\t", 0)) != NULL)
230 get_command(cl, &cl->reader, req, 0);
231 else
232 send_error(cl, EPWMD_COMMAND_SYNTAX);
235 else if (g_ascii_strncasecmp(p, "ATTR ", 5) == 0) {
236 t = p + 5;
237 t = g_strchug(t);
239 if (cl->state != STATE_OPEN)
240 send_error(cl, EPWMD_NO_FILE);
241 else {
242 if ((req = split_input_line(t, " ", 4)) != NULL) {
243 if (attr_command(cl, req) == TRUE)
244 send_to_client(cl, "OK \n");
246 else
247 send_error(cl, EPWMD_COMMAND_SYNTAX);
250 else if (g_ascii_strncasecmp(p, "OPEN ", 5) == 0) {
251 t = p + 5;
252 t = g_strchug(t);
254 if (cl->state == STATE_OPEN)
255 send_error(cl, EPWMD_FILE_OPENED);
256 else {
257 if ((req = split_input_line(t, " ", 2)) != NULL) {
258 if (open_command(cl, req) == TRUE) {
259 send_to_client(cl, "OK \n");
260 cl->state = STATE_OPEN;
264 * The document has been parsed and is stored in cl->doc.
266 if (cl->xml) {
267 memset(cl->xml, 0, cl->len);
268 g_free(cl->xml);
269 cl->xml = NULL;
272 else
273 send_error(cl, EPWMD_COMMAND_SYNTAX);
276 else if (g_ascii_strncasecmp(p, "SAVE", 4) == 0) {
277 t = p + 4;
278 t = g_strchug(t);
280 if (cl->state != STATE_OPEN)
281 send_error(cl, EPWMD_NO_FILE);
282 else {
283 req = split_input_line(t, " ", 1);
285 if (save_command(cl, cl->filename, (req) ? req[0] : NULL) == TRUE)
286 send_to_client(cl, "OK \n");
289 else if (g_ascii_strncasecmp(p, "CACHE ", 6) == 0) {
290 t = p + 6;
291 t = g_strchug(t);
292 req = split_input_line(t, " ", 2);
294 if (cache_command(cl, req) == TRUE)
295 send_to_client(cl, "OK \n");
297 else if (g_ascii_strncasecmp(p, "DUMP", 4) == 0) {
298 if (cl->state != STATE_OPEN)
299 send_error(cl, EPWMD_NO_FILE);
300 else {
301 if (dump_command(cl) == TRUE)
302 send_to_client(cl, "OK \n");
305 else
306 send_error(cl, EPWMD_COMMAND_SYNTAX);
309 if (req) {
310 g_strfreev(req);
311 req = NULL;
314 return P_OK;
317 static gboolean source_prepare(GSource *src, gint *to)
319 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
320 return TRUE;
322 return FALSE;
325 static gboolean source_check(GSource *src)
327 if (cl->gfd.revents & (G_IO_IN|G_IO_PRI))
328 return TRUE;
330 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT)))
331 return TRUE;
333 return FALSE;
336 static gboolean source_dispatch(GSource *src, GSourceFunc cb, gpointer data)
338 return (*cb)(data);
341 static gboolean source_cb(gpointer data)
343 GIOStatus ret;
344 gsize len;
345 gchar *line = NULL;
346 GError *gerror = NULL;
347 gchar **p;
349 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
350 goto quit;
352 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT))) {
353 for (p = cl->outbuf; *p; p++) {
354 ret = g_io_channel_write_chars(cl->ioc, *p, -1, &len, &gerror);
356 if (ret == G_IO_STATUS_NORMAL)
357 g_io_channel_flush(cl->ioc, &gerror);
358 else
359 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gerror->message);
361 g_clear_error(&gerror);
363 if (quit)
364 goto quit;
367 g_strfreev(cl->outbuf);
368 cl->outbuf = NULL;
371 if (!cl->gfd.revents & (G_IO_IN))
372 return TRUE;
374 ret = g_io_channel_read_line(cl->ioc, &line, &len, NULL, &gerror);
376 if (ret != G_IO_STATUS_NORMAL) {
377 if (ret == G_IO_STATUS_EOF)
378 goto quit;
380 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gerror->message);
381 g_clear_error(&gerror);
382 return TRUE;
385 line[g_utf8_strlen(line, -1) - 1] = 0;
387 switch (input_parser(line)) {
388 case P_QUIT:
389 quit:
390 if (line) {
391 memset(line, 0, len);
392 g_free(line);
395 g_clear_error(&gerror);
397 if (cl->xml) {
398 memset(cl->xml, 0, cl->len);
399 xmlFree(cl->xml);
402 if (cl->gh)
403 gcry_cipher_close(cl->gh);
405 if (cl->doc)
406 xmlFreeDoc(cl->doc);
408 if (cl->reader)
409 xmlFreeTextReader(cl->reader);
411 if (cl->outbuf)
412 g_free(cl->outbuf);
414 if (cl->filename)
415 g_free(cl->filename);
417 g_main_loop_unref(gloop);
418 g_main_loop_quit(gloop);
419 return FALSE;
420 default:
421 break;
424 memset(line, 0, len);
425 g_free(line);
426 return TRUE;
430 * Called every time a connection is made.
432 static void doit(int fd)
434 static GSourceFuncs gsrcf = {
435 source_prepare, source_check, source_dispatch, NULL, 0, 0
437 GPollFD gfd = { fd, G_IO_IN|G_IO_OUT|G_IO_HUP|G_IO_ERR, 0 };
439 #ifdef HAVE_MLOCKALL
440 if (use_mlock && mlockall(MCL_FUTURE) == -1)
441 err(EXIT_FAILURE, "mlockall()");
442 #endif
444 gloop = g_main_loop_new(NULL, TRUE);
445 cl = g_malloc0(sizeof(struct client_s));
446 cl->src = g_source_new(&gsrcf, sizeof(GSource));
447 cl->gfd = gfd;
448 cl->state = STATE_CONNECTED;
449 cl->ioc = g_io_channel_unix_new(fd);
450 g_source_add_poll(cl->src, &cl->gfd);
451 g_source_set_callback(cl->src, source_cb, NULL, NULL);
452 g_source_attach(cl->src, NULL);
453 #ifndef DEBUG
454 close(0);
455 close(1);
456 close(2);
457 #endif
459 if ((gcryerrno = gcry_cipher_open(&cl->gh, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0))) {
460 send_to_client(cl, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
461 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
462 quit = 1;
464 else
465 // FIXME 100% CPU if removed (poll()).
466 send_to_client(cl, "OK \n");
468 g_main_loop_run(gloop);
469 g_io_channel_unref(cl->ioc);
470 g_free(cl);
471 shutdown(fd, SHUT_RDWR);
472 log_write("exiting");
473 _exit(EXIT_SUCCESS);
476 static void set_rcfile_defaults(GKeyFile *kf)
478 gchar buf[PATH_MAX];
480 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
481 g_key_file_set_string(kf, "default", "socket_path", buf);
482 snprintf(buf, sizeof(buf), "~/.pwmd");
483 g_key_file_set_string(kf, "default", "data_directory", buf);
484 snprintf(buf, sizeof(buf), "~/.pwmd/.log");
485 g_key_file_set_string(kf, "default", "log_path", buf);
486 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
487 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
488 g_key_file_set_boolean(kf, "default", "disable_mlockall", FALSE);
491 static GKeyFile *parse_rcfile(const gchar *filename)
493 GKeyFile *kf = g_key_file_new();
494 GError *error = NULL;
496 if (g_key_file_load_from_file(kf, filename, G_KEY_FILE_NONE, &error) == FALSE) {
497 if (error->code == G_FILE_ERROR_NOENT) {
498 g_clear_error(&error);
499 set_rcfile_defaults(kf);
500 return kf;
502 else {
503 warnx("%s: %s", filename, error->message);
504 g_clear_error(&error);
505 return NULL;
509 return kf;
512 static gboolean try_xml_decrypt(gint fd, struct stat st, guchar *key, gchar **xml,
513 gsize *len)
515 guchar *iv;
516 gchar *inbuf;
517 gcry_cipher_hd_t gh;
519 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0))) {
520 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
521 return FALSE;
524 inbuf = gcry_malloc(st.st_size);
525 read(fd, inbuf, st.st_size);
526 close(fd);
527 iv = gcry_malloc(gcryblocksize);
528 memcpy(iv, inbuf, gcryblocksize);
530 if (decrypt_xml(gh, key, gcrykeysize, iv, inbuf + gcryblocksize,
531 st.st_size - gcryblocksize, NULL, 0) == FALSE) {
532 memset(inbuf, 0, st.st_size);
533 gcry_free(inbuf);
534 gcry_free(iv);
535 return FALSE;
538 gcry_free(iv);
539 memmove(inbuf, inbuf + gcryblocksize, st.st_size - gcryblocksize);
540 *xml = inbuf;
541 *len = st.st_size - gcryblocksize;
543 if (g_strncasecmp(*xml, "<?xml version=\"1.0\"?>", 21) != 0)
544 return FALSE;
546 return TRUE;
549 static gboolean get_input(const gchar *filename, guchar *key)
551 gchar buf[LINE_MAX], *p;
552 struct termios told, tnew;
553 gchar *xml = NULL;
554 gsize len = 0;
555 gint fd;
556 struct stat st;
557 int try = 0;
558 gint tty = isatty(STDIN_FILENO);
560 if ((fd = open_file(filename, &st)) == -1)
561 return FALSE;
563 if (st.st_size == 0) {
564 fprintf(stderr, "Skipping empty file '%s'.\n", filename);
565 close(fd);
566 return FALSE;
569 if (tty) {
570 if (tcgetattr(STDIN_FILENO, &told) == -1)
571 err(EXIT_FAILURE, "tcgetattr()");
573 memcpy(&tnew, &told, sizeof(struct termios));
574 tnew.c_lflag &= ~(ECHO);
575 tnew.c_lflag |= ICANON|ECHONL;
578 again:
579 if (tty) {
580 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
581 tcsetattr(STDIN_FILENO, TCSANOW, &told);
582 err(EXIT_FAILURE, "tcsetattr()");
586 printf("Password for '%s': ", filename);
588 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
589 if (tty)
590 tcsetattr(STDIN_FILENO, TCSANOW, &told);
592 close(fd);
593 return FALSE;
596 if (tty)
597 tcsetattr(STDIN_FILENO, TCSANOW, &told);
599 p[strlen(p) - 1] = 0;
601 if (!*p) {
602 fprintf(stderr, "Skipping.\n");
603 close(fd);
604 return FALSE;
607 gcry_md_hash_buffer(GCRY_MD_MD5, key, p, strlen(p));
608 memset(buf, 0, sizeof(buf));
610 if (try_xml_decrypt(fd, st, key, &xml, &len) == FALSE) {
611 if (xml) {
612 memset(xml, 0, len);
613 g_free(xml);
616 if (try++ == 2) {
617 fprintf(stderr, "Invalid password. Skipping file.\n");
618 return FALSE;
620 else {
621 fprintf(stderr, "Invalid password.\n");
622 goto again;
626 memset(xml, 0, len);
627 g_free(xml);
628 return TRUE;
631 gint cache_file_count()
633 void *p;
634 gint n = 0;
635 glong len;
636 file_cache_t f;
638 for (p = shm_data, len = 0; len <= cache_size;) {
639 memcpy(&f, p, sizeof(file_cache_t));
641 if (f.used == TRUE)
642 n++;
644 p += sizeof(file_cache_t);
645 len += sizeof(file_cache_t);
647 if (len + sizeof(file_cache_t) > cache_size)
648 break;
651 return n;
654 gboolean cache_add_file(const guchar *md5file, const guchar *md5key)
656 void *p;
657 file_cache_t f;
658 gint nfiles = cache_file_count();
659 glong len;
662 * Make sure there is enough secure memory.
664 if (!md5file || (nfiles + 1) * sizeof(file_cache_t) > cache_size)
665 return FALSE;
668 * Find the first available "slot".
670 for (p = shm_data, len = 0; len <= cache_size;) {
671 memcpy(&f, p, sizeof(file_cache_t));
673 if (f.used == FALSE) {
674 memcpy(&f.filename, md5file, sizeof(f.filename));
676 if (md5key)
677 memcpy(&f.key, md5key, sizeof(f.key));
679 f.used = TRUE;
680 memcpy(p, &f, sizeof(file_cache_t));
681 return TRUE;
684 p += sizeof(file_cache_t);
685 len += sizeof(file_cache_t);
687 if (len + sizeof(file_cache_t) > cache_size)
688 break;
691 return FALSE;
694 int main(int argc, char *argv[])
696 gint opt;
697 struct sockaddr_un addr;
698 struct passwd *pw = getpwuid(getuid());
699 gchar buf[PATH_MAX];
700 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
701 gchar *socketarg = NULL;
702 gchar *datadir = NULL;
703 gint fd;
704 GKeyFile *kf;
705 gboolean n;
706 gchar *p;
707 gchar **cache_push = NULL;
708 gchar *rcfile;
709 #ifndef DEBUG
710 #ifdef HAVE_SETRLIMIT
711 struct rlimit rl;
713 rl.rlim_cur = rl.rlim_max = 0;
715 if (setrlimit(RLIMIT_CORE, &rl) != 0)
716 err(EXIT_FAILURE, "setrlimit()");
717 #endif
718 #endif
720 rcfile = g_strdup_printf("%s/.pwmdrc", pw->pw_dir);
722 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
723 err(EXIT_FAILURE, "sysconf()");
725 cache_size = page_size;
727 #ifdef HAVE_MLOCKALL
729 * Default to using mlockall().
731 use_mlock = 1;
732 #endif
734 while ((opt = getopt(argc, argv, "hvf:")) != EOF) {
735 switch (opt) {
736 case 'f':
737 g_free(rcfile);
738 rcfile = g_strdup(optarg);
739 break;
740 case 'v':
741 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
742 exit(EXIT_SUCCESS);
743 case 'h':
744 default:
745 usage(argv[0]);
749 if ((kf = parse_rcfile(rcfile)) == NULL)
750 exit(EXIT_FAILURE);
752 g_key_file_set_list_separator(kf, ',');
754 if ((p = g_key_file_get_string(kf, "default", "socket_path", NULL)) == NULL)
755 errx(EXIT_FAILURE, "%s: socket_path not defined", rcfile);
757 if (*p == '~') {
758 p++;
759 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
760 g_free(p);
761 socketarg = g_strdup(buf);
763 else
764 socketarg = p;
766 if ((p = g_key_file_get_string(kf, "default", "data_directory", NULL)) == NULL)
767 errx(EXIT_FAILURE, "%s: data_directory not defined", rcfile);
769 if (*p == '~') {
770 p++;
771 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
772 g_free(p);
773 datadir = g_strdup(buf);
775 else
776 datadir = p;
778 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == TRUE) {
779 cache_size = g_key_file_get_integer(kf, "default", "cache_size", NULL);
781 if (cache_size < page_size || cache_size % page_size)
782 errx(EXIT_FAILURE, "cache size must be in multiples of %li.", page_size);
785 #ifdef HAVE_MLOCKALL
786 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == TRUE)
787 use_mlock = g_key_file_get_integer(kf, "default", "disable_mlockall", NULL);
788 #endif
790 if (g_key_file_has_key(kf, "default", "log_path", NULL) == TRUE) {
791 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == TRUE) {
792 n = g_key_file_get_boolean(kf, "default", "enable_logging", NULL);
794 if (n == TRUE) {
795 p = g_key_file_get_string(kf, "default", "log_path", NULL);
797 if (*p == '~') {
798 p++;
799 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
800 g_free(p);
801 logfile = g_strdup(buf);
803 else
804 logfile = p;
809 if (g_key_file_has_key(kf, "default", "cache_push", NULL) == TRUE)
810 cache_push = g_key_file_get_string_list(kf, "default", "cache_push", NULL, NULL);
812 g_key_file_free(kf);
814 if (strchr(socketarg, '/') == NULL) {
815 socketdir = g_get_current_dir();
816 socketname = g_strdup(socketarg);
817 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
819 else {
820 socketname = g_strdup(strrchr(socketarg, '/'));
821 socketname++;
822 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
823 socketdir = g_strdup(socketarg);
824 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
827 snprintf(buf, sizeof(buf), "%s", datadir);
829 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
830 err(EXIT_FAILURE, "%s", buf);
832 #ifdef MMAP_ANONYMOUS_SHARED
833 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
834 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == NULL) {
835 err(EXIT_FAILURE, "mmap()");
837 #else
838 snprintf(buf, sizeof(buf), "pwmd.%i", pw->pw_uid);
840 if ((fd = shm_open(buf, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
841 err(EXIT_FAILURE, "shm_open(): %s", buf);
844 * Should be enough for the file cache.
846 if (ftruncate(fd, cache_size) == -1)
847 err(EXIT_FAILURE, "ftruncate()");
849 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
850 fd, 0)) == NULL) {
851 shm_unlink(buf);
852 err(EXIT_FAILURE, "mmap()");
855 close(fd);
856 #endif
858 if (mlock(shm_data, cache_size) == -1)
859 warn("mlock()");
862 * bind() doesn't like the full pathname of the socket or any non alphanum
863 * characters so change to the directory where the socket is wanted then
864 * create it then change to datadir.
866 if (chdir(socketdir))
867 err(EXIT_FAILURE, "%s", socketdir);
869 g_free(socketdir);
871 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
872 err(EXIT_FAILURE, "socket()");
874 addr.sun_family = AF_UNIX;
875 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
876 g_free(--socketname);
878 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1)
879 err(EXIT_FAILURE, "bind()");
881 if (chdir(datadir)) {
882 close(sfd);
883 unlink(socketpath);
884 err(EXIT_FAILURE, "%s", datadir);
887 g_free(datadir);
888 setup_gcrypt();
891 * Set the cache entry for a file. Prompts for the password.
893 if (cache_push) {
894 guchar md5file[16];
895 guchar key[16];
897 for (opt = 0; cache_push[opt]; opt++) {
898 p = cache_push[opt];
900 while (isspace(*p))
901 p++;
903 if (!*p)
904 continue;
906 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
908 if (access(p, R_OK|W_OK) != 0) {
909 if (errno != ENOENT) {
910 unlink(socketpath);
911 err(EXIT_FAILURE, "%s", p);
914 warn("%s", p);
915 continue;
918 if (get_input(p, key) == FALSE) {
919 memset(key, 0, sizeof(key));
920 continue;
923 if (cache_add_file(md5file, key) == FALSE) {
924 memset(key, 0, sizeof(key));
925 errx(EXIT_FAILURE, "%s: couldn't add file (cache_size?)", p);
928 fprintf(stderr, "Added.\n");
929 memset(key, 0, sizeof(key));
930 memset(md5file, 0, sizeof(md5file));
933 g_strfreev(cache_push);
934 fprintf(stderr, "Done! Daemonizing...\n");
937 g_free(rcfile);
939 if (listen(sfd, 0) == -1)
940 err(EXIT_FAILURE, "listen()");
942 signal(SIGCHLD, catchsig);
943 signal(SIGTERM, catchsig);
944 signal(SIGINT, catchsig);
945 signal(SIGHUP, catchsig);
946 log_write("%s starting: %li slots available", PACKAGE_STRING, cache_size / sizeof(file_cache_t));
948 while (!quit) {
949 socklen_t slen = sizeof(struct sockaddr_un);
950 struct sockaddr_un raddr;
951 pid_t pid;
953 if ((fd = accept(sfd, (struct sockaddr_un *)&raddr, &slen)) == -1) {
954 if (!quit)
955 log_write("accept(): %s", strerror(errno));
957 continue;
960 switch ((pid = fork())) {
961 case -1:
962 log_write("fork(): %s", strerror(errno));
963 break;
964 case 0:
965 doit(fd);
966 break;
967 default:
968 break;
971 log_write("new connection: fd=%i, pid=%i", fd, pid);
974 unlink(socketpath);
975 g_free(socketpath);
977 if (munmap(shm_data, cache_size) == -1)
978 log_write("munmap(): %s", strerror(errno));
980 #ifndef MMAP_ANONYMOUS_SHARED
981 if (shm_unlink(buf) == -1)
982 log_write("shm_unlink(): %s: %s", buf, strerror(errno));
983 #endif
985 log_write("pwmd exiting normally");
986 exit(EXIT_SUCCESS);