Added libXML memory handlers: xmlfree(), xmlmalloc(), xmlrealloc() and
[pwmd.git] / src / pwmd.c
blob27f3011c137ea2e15ec99968d41548001f088063
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>
39 #include <assert.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 #include "xml.h"
51 #include "common.h"
52 #include "commands.h"
53 #include "pwmd_error.h"
54 #include "pwmd.h"
56 void send_to_client(struct client_s *client, const gchar *fmt, ...)
58 va_list ap;
59 gint n = 0;
60 gchar **p;
62 va_start(ap, fmt);
64 if (client->outbuf)
65 for (p = client->outbuf, n = 0; *p; p++, n++);
67 if ((client->outbuf = g_realloc(client->outbuf, (n + 2) * sizeof(gchar *))) == NULL) {
68 warn("g_realloc()");
69 return;
72 client->outbuf[n++] = g_strdup_vprintf(fmt, ap);
73 client->outbuf[n] = NULL;
74 va_end(ap);
77 void send_error(struct client_s *client, int pwmd_errno)
79 send_to_client(client, "ERR %03i %s\n", pwmd_errno, pwmd_strerror(pwmd_errno));
82 void log_write(const gchar *fmt, ...)
84 gchar *args, *line;
85 va_list ap;
86 struct tm *tm;
87 time_t now;
88 gchar tbuf[21];
89 gint fd;
91 if (!logfile || !fmt)
92 return;
94 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
95 warn("logfile");
96 return;
99 va_start(ap, fmt);
100 g_vasprintf(&args, fmt, ap);
101 va_end(ap);
102 time(&now);
103 tm = localtime(&now);
104 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
105 tbuf[sizeof(tbuf) - 1] = 0;
106 line = g_strdup_printf("%s %i %s\n", tbuf, getpid(), args);
107 write(fd, line, strlen(line));
108 g_free(args);
109 g_free(line);
110 close(fd);
113 static void catchsig(gint sig)
115 gint status;
117 log_write("caught signal %i (%s)", sig, strsignal(sig));
119 switch (sig) {
120 case SIGCHLD:
121 waitpid(-1, &status, 0);
122 break;
123 case SIGHUP:
124 log_write("clearing file cache");
125 memset(shm_data, 0, cache_size);
126 break;
127 default:
128 quit = 1;
129 shutdown(sfd, SHUT_RDWR);
130 close(sfd);
131 break;
135 static void usage(gchar *pn)
137 g_printf(
138 "Usage: %s [-hv] [-f <rcfile>]\n"
139 " -f load the specified rcfile (~/.pwmdrc)\n"
140 " -v version\n"
141 " -h this help text\n",
142 pn);
143 exit(EXIT_SUCCESS);
146 gchar **split_input_line(gchar *str, gchar *delim, gint n)
148 if (!str || !*str)
149 return NULL;
151 return g_strsplit(str, delim, n);
154 static void setup_gcrypt()
156 gcry_check_version(NULL);
158 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
159 NULL) != 0)
160 errx(EXIT_FAILURE, "AES cipher not supported");
162 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
163 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
166 static gint input_parser(gchar *str)
168 gchar *p, *t;
169 gchar **req = NULL;
171 if (str)
172 str = g_strchug(str);
174 if (!*str)
175 return P_OK;
177 while ((p = strsep(&str, "\n")) != NULL) {
178 if (g_ascii_strcasecmp(p, "QUIT") == 0)
179 return P_QUIT;
180 else if (g_ascii_strcasecmp(p, "HELP") == 0)
181 help_command(cl, NULL);
182 else if (g_ascii_strncasecmp(p, "HELP ", 5) == 0) {
183 t = p + 5;
184 t = g_strchug(t);
185 help_command(cl, t);
187 else if (g_ascii_strcasecmp(p, "LIST") == 0 ||
188 g_ascii_strncasecmp(p, "LIST ", 5) == 0) {
189 if (cl->state != STATE_OPEN)
190 send_error(cl, EPWMD_NO_FILE);
191 else
192 list_command(cl, p);
194 else if (g_ascii_strncasecmp(p, "STORE ", 6) == 0) {
195 t = p + 6;
196 t = g_strchug(t);
198 if (cl->state != STATE_OPEN)
199 send_error(cl, EPWMD_NO_FILE);
200 else {
201 if ((req = split_input_line(t, "\t", 0)) != NULL) {
202 if (store_command(cl, req) == TRUE)
203 send_to_client(cl, "OK \n");
205 else
206 send_error(cl, EPWMD_COMMAND_SYNTAX);
209 else if (g_ascii_strncasecmp(p, "DELETE ", 7) == 0) {
210 t = p + 7;
212 if (cl->state != STATE_OPEN)
213 send_error(cl, EPWMD_NO_FILE);
214 else {
215 if ((req = split_input_line(t, "\t", 0)) != NULL) {
216 if (delete_command(cl, req) == TRUE)
217 send_to_client(cl, "OK \n");
219 else
220 send_error(cl, EPWMD_COMMAND_SYNTAX);
223 else if (g_ascii_strncasecmp(p, "GET ", 4) == 0) {
224 t = p + 4;
225 t = g_strchug(t);
227 if (cl->state != STATE_OPEN)
228 send_error(cl, EPWMD_NO_FILE);
229 else {
230 if ((req = split_input_line(t, "\t", 0)) != NULL)
231 get_command(cl, &cl->reader, req, 0);
232 else
233 send_error(cl, EPWMD_COMMAND_SYNTAX);
236 else if (g_ascii_strncasecmp(p, "ATTR ", 5) == 0) {
237 t = p + 5;
238 t = g_strchug(t);
240 if (cl->state != STATE_OPEN)
241 send_error(cl, EPWMD_NO_FILE);
242 else {
243 if ((req = split_input_line(t, " ", 4)) != NULL) {
244 if (attr_command(cl, req) == TRUE)
245 send_to_client(cl, "OK \n");
247 else
248 send_error(cl, EPWMD_COMMAND_SYNTAX);
251 else if (g_ascii_strncasecmp(p, "OPEN ", 5) == 0) {
252 t = p + 5;
253 t = g_strchug(t);
255 if (cl->state == STATE_OPEN)
256 send_error(cl, EPWMD_FILE_OPENED);
257 else {
258 if ((req = split_input_line(t, " ", 2)) != NULL) {
259 if (open_command(cl, req) == TRUE) {
260 send_to_client(cl, "OK \n");
261 cl->state = STATE_OPEN;
265 * The document has been parsed and is stored in cl->doc.
267 if (cl->xml) {
268 memset(cl->xml, 0, cl->len);
269 g_free(cl->xml);
270 cl->xml = NULL;
273 else
274 send_error(cl, EPWMD_COMMAND_SYNTAX);
277 else if (g_ascii_strncasecmp(p, "SAVE", 4) == 0) {
278 t = p + 4;
279 t = g_strchug(t);
281 if (cl->state != STATE_OPEN)
282 send_error(cl, EPWMD_NO_FILE);
283 else {
284 req = split_input_line(t, " ", 1);
286 if (save_command(cl, cl->filename, (req) ? req[0] : NULL) == TRUE)
287 send_to_client(cl, "OK \n");
290 else if (g_ascii_strncasecmp(p, "CACHE ", 6) == 0) {
291 t = p + 6;
292 t = g_strchug(t);
293 req = split_input_line(t, " ", 2);
295 if (cache_command(cl, req) == TRUE)
296 send_to_client(cl, "OK \n");
298 else if (g_ascii_strncasecmp(p, "DUMP", 4) == 0) {
299 if (cl->state != STATE_OPEN)
300 send_error(cl, EPWMD_NO_FILE);
301 else {
302 if (dump_command(cl) == TRUE)
303 send_to_client(cl, "OK \n");
306 else
307 send_error(cl, EPWMD_COMMAND_SYNTAX);
310 if (req) {
311 g_strfreev(req);
312 req = NULL;
315 return P_OK;
318 static gboolean source_prepare(GSource *src, gint *to)
320 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
321 return TRUE;
323 return FALSE;
326 static gboolean source_check(GSource *src)
328 if (cl->gfd.revents & (G_IO_IN|G_IO_PRI))
329 return TRUE;
331 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT)))
332 return TRUE;
334 return FALSE;
337 static gboolean source_dispatch(GSource *src, GSourceFunc cb, gpointer data)
339 return (*cb)(data);
342 static gboolean source_cb(gpointer data)
344 GIOStatus ret;
345 gsize len;
346 gchar *line = NULL;
347 GError *gerror = NULL;
348 gchar **p;
350 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
351 goto quit;
353 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT))) {
354 for (p = cl->outbuf; *p; p++) {
355 ret = g_io_channel_write_chars(cl->ioc, *p, -1, &len, &gerror);
357 if (ret == G_IO_STATUS_NORMAL)
358 g_io_channel_flush(cl->ioc, &gerror);
359 else
360 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gerror->message);
362 g_clear_error(&gerror);
364 if (quit)
365 goto quit;
368 g_strfreev(cl->outbuf);
369 cl->outbuf = NULL;
372 if (!cl->gfd.revents & (G_IO_IN))
373 return TRUE;
375 ret = g_io_channel_read_line(cl->ioc, &line, &len, NULL, &gerror);
377 if (ret != G_IO_STATUS_NORMAL) {
378 if (ret == G_IO_STATUS_EOF)
379 goto quit;
381 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gerror->message);
382 g_clear_error(&gerror);
383 return TRUE;
386 line[g_utf8_strlen(line, -1) - 1] = 0;
388 switch (input_parser(line)) {
389 case P_QUIT:
390 quit:
391 if (line) {
392 memset(line, 0, len);
393 g_free(line);
396 g_clear_error(&gerror);
398 if (cl->xml) {
399 memset(cl->xml, 0, cl->len);
400 xmlFree(cl->xml);
403 if (cl->gh)
404 gcry_cipher_close(cl->gh);
406 if (cl->doc)
407 xmlFreeDoc(cl->doc);
409 if (cl->reader)
410 xmlFreeTextReader(cl->reader);
412 if (cl->outbuf)
413 g_free(cl->outbuf);
415 if (cl->filename)
416 g_free(cl->filename);
418 if (memlist)
419 g_slist_free(memlist);
421 g_main_loop_unref(gloop);
422 g_main_loop_quit(gloop);
423 return FALSE;
424 default:
425 break;
428 memset(line, 0, len);
429 g_free(line);
430 return TRUE;
433 static void xmlfree(void *ptr)
435 gpointer p;
436 int n;
437 mem_t *data;
439 if (!ptr)
440 return;
442 for (n = 0; ; n++) {
443 if ((p = g_slist_nth_data(memlist, n)) == NULL)
444 break;
446 data = p;
448 if (data->data == ptr) {
449 memset(data->data, 0, data->size);
450 free(data->data);
451 memlist = g_slist_remove(memlist, p);
452 return;
456 #ifdef DEBUG
457 warnx("xmlfree(): %p not found", ptr);
458 assert(0);
459 #endif
462 static void *xmlmalloc(size_t size)
464 void *p;
465 mem_t *new;
467 if (size <= 0)
468 return NULL;
470 if ((new = malloc(sizeof(mem_t))) == NULL)
471 return NULL;
473 if ((p = malloc(size)) == NULL) {
474 free(new);
475 return NULL;
478 new->data = p;
479 new->size = size;
480 memlist = g_slist_append(memlist, new);
481 return new->data;
484 static void *xmlrealloc(void *ptr, size_t size)
486 void *p, *new;
487 int n;
488 mem_t *data;
490 if (!ptr)
491 return xmlmalloc(size);
493 if (size <= 0)
494 return ptr;
496 for (n = 0; ; n++) {
497 if ((p = g_slist_nth_data(memlist, n)) == NULL)
498 break;
500 data = p;
502 if (data->data == ptr) {
503 if ((new = malloc(size)) == NULL)
504 return NULL;
506 memcpy(new, data->data, (size < data->size) ? size : data->size);
507 memset(data->data, 0, data->size);
508 free(data->data);
509 data->data = new;
510 data->size = size;
511 return data->data;
515 #ifdef DEBUG
516 warnx("xmlrealloc(): %p not found", ptr);
517 assert(0);
518 #endif
519 return NULL;
522 static char *xmlstrdup(const char *str)
524 char *new;
525 size_t len;
526 const char *p;
527 char *np;
529 if (!str)
530 return NULL;
532 len = strlen(str) + 1;
534 if ((new = xmlmalloc(len * sizeof(char))) == NULL)
535 return NULL;
537 for (p = str, np = new; *p; p++)
538 *np++ = *p;
540 *np = 0;
541 return new;
545 * Called every time a connection is made.
547 static void doit(int fd)
549 static GSourceFuncs gsrcf = {
550 source_prepare, source_check, source_dispatch, NULL, 0, 0
552 GPollFD gfd = { fd, G_IO_IN|G_IO_OUT|G_IO_HUP|G_IO_ERR, 0 };
554 #ifdef HAVE_MLOCKALL
555 if (use_mlock && mlockall(MCL_FUTURE) == -1)
556 err(EXIT_FAILURE, "mlockall()");
557 #endif
559 gloop = g_main_loop_new(NULL, TRUE);
560 cl = g_malloc0(sizeof(struct client_s));
561 cl->src = g_source_new(&gsrcf, sizeof(GSource));
562 cl->gfd = gfd;
563 cl->state = STATE_CONNECTED;
564 cl->ioc = g_io_channel_unix_new(fd);
565 g_source_add_poll(cl->src, &cl->gfd);
566 g_source_set_callback(cl->src, source_cb, NULL, NULL);
567 g_source_attach(cl->src, NULL);
568 #ifndef DEBUG
569 close(0);
570 close(1);
571 close(2);
572 #endif
574 if ((gcryerrno = gcry_cipher_open(&cl->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
575 send_to_client(cl, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
576 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
577 quit = 1;
579 else
580 // FIXME 100% CPU if removed (poll()).
581 send_to_client(cl, "OK \n");
583 xmlMemSetup(xmlfree, xmlmalloc, xmlrealloc, xmlstrdup);
584 xmlInitMemory();
585 g_main_loop_run(gloop);
586 g_io_channel_unref(cl->ioc);
587 g_free(cl);
588 shutdown(fd, SHUT_RDWR);
589 log_write("exiting");
590 _exit(EXIT_SUCCESS);
593 static void set_rcfile_defaults(GKeyFile *kf)
595 gchar buf[PATH_MAX];
597 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
598 g_key_file_set_string(kf, "default", "socket_path", buf);
599 snprintf(buf, sizeof(buf), "~/.pwmd");
600 g_key_file_set_string(kf, "default", "data_directory", buf);
601 snprintf(buf, sizeof(buf), "~/.pwmd/.log");
602 g_key_file_set_string(kf, "default", "log_path", buf);
603 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
604 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
605 g_key_file_set_boolean(kf, "default", "disable_mlockall", FALSE);
608 static GKeyFile *parse_rcfile(const gchar *filename)
610 GKeyFile *kf = g_key_file_new();
611 GError *error = NULL;
613 if (g_key_file_load_from_file(kf, filename, G_KEY_FILE_NONE, &error) == FALSE) {
614 if (error->code == G_FILE_ERROR_NOENT) {
615 g_clear_error(&error);
616 set_rcfile_defaults(kf);
617 return kf;
619 else {
620 warnx("%s: %s", filename, error->message);
621 g_clear_error(&error);
622 return NULL;
626 return kf;
629 static gboolean try_xml_decrypt(gint fd, struct stat st, guchar *key, gchar **xml,
630 gsize *len)
632 guchar *iv;
633 gchar *inbuf;
634 gcry_cipher_hd_t gh;
636 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
637 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
638 return FALSE;
641 inbuf = gcry_malloc(st.st_size);
642 read(fd, inbuf, st.st_size);
643 close(fd);
644 iv = gcry_malloc(gcryblocksize);
645 memcpy(iv, inbuf, gcryblocksize);
647 if (decrypt_xml(gh, key, gcrykeysize, iv, inbuf + gcryblocksize,
648 st.st_size - gcryblocksize, NULL, 0) == FALSE) {
649 memset(inbuf, 0, st.st_size);
650 gcry_free(inbuf);
651 gcry_free(iv);
652 return FALSE;
655 gcry_free(iv);
656 memmove(inbuf, inbuf + gcryblocksize, st.st_size - gcryblocksize);
657 *xml = inbuf;
658 *len = st.st_size - gcryblocksize;
660 if (g_strncasecmp(*xml, "<?xml version=\"1.0\"?>", 21) != 0)
661 return FALSE;
663 return TRUE;
666 static gboolean get_input(const gchar *filename, guchar *key)
668 gchar buf[LINE_MAX], *p;
669 struct termios told, tnew;
670 gchar *xml = NULL;
671 gsize len = 0;
672 gint fd;
673 struct stat st;
674 int try = 0;
675 gint tty = isatty(STDIN_FILENO);
677 if ((fd = open_file(filename, &st)) == -1)
678 return FALSE;
680 if (st.st_size == 0) {
681 fprintf(stderr, "Skipping empty file '%s'.\n", filename);
682 close(fd);
683 return FALSE;
686 if (tty) {
687 if (tcgetattr(STDIN_FILENO, &told) == -1)
688 err(EXIT_FAILURE, "tcgetattr()");
690 memcpy(&tnew, &told, sizeof(struct termios));
691 tnew.c_lflag &= ~(ECHO);
692 tnew.c_lflag |= ICANON|ECHONL;
695 again:
696 if (tty) {
697 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
698 tcsetattr(STDIN_FILENO, TCSANOW, &told);
699 err(EXIT_FAILURE, "tcsetattr()");
703 printf("Password for '%s': ", filename);
705 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
706 if (tty)
707 tcsetattr(STDIN_FILENO, TCSANOW, &told);
709 close(fd);
710 return FALSE;
713 if (tty)
714 tcsetattr(STDIN_FILENO, TCSANOW, &told);
716 p[strlen(p) - 1] = 0;
718 if (!*p) {
719 fprintf(stderr, "Skipping.\n");
720 close(fd);
721 return FALSE;
724 gcry_md_hash_buffer(GCRY_MD_SHA256, key, p, strlen(p));
725 memset(buf, 0, sizeof(buf));
727 if (try_xml_decrypt(fd, st, key, &xml, &len) == FALSE) {
728 if (xml) {
729 memset(xml, 0, len);
730 g_free(xml);
733 if (try++ == 2) {
734 fprintf(stderr, "Invalid password. Skipping file.\n");
735 return FALSE;
737 else {
738 fprintf(stderr, "Invalid password.\n");
739 goto again;
743 memset(xml, 0, len);
744 g_free(xml);
745 return TRUE;
748 gint cache_file_count()
750 void *p;
751 gint n = 0;
752 glong len;
753 file_cache_t f;
755 for (p = shm_data, len = 0; len <= cache_size;) {
756 memcpy(&f, p, sizeof(file_cache_t));
758 if (f.used == TRUE)
759 n++;
761 p += sizeof(file_cache_t);
762 len += sizeof(file_cache_t);
764 if (len + sizeof(file_cache_t) > cache_size)
765 break;
768 return n;
771 gboolean cache_add_file(const guchar *md5file, const guchar *shakey)
773 void *p;
774 file_cache_t f;
775 gint nfiles = cache_file_count();
776 glong len;
779 * Make sure there is enough secure memory.
781 if (!md5file || (nfiles + 1) * sizeof(file_cache_t) > cache_size)
782 return FALSE;
785 * Find the first available "slot".
787 for (p = shm_data, len = 0; len <= cache_size;) {
788 memcpy(&f, p, sizeof(file_cache_t));
790 if (f.used == FALSE) {
791 memcpy(&f.filename, md5file, sizeof(f.filename));
793 if (shakey)
794 memcpy(&f.key, shakey, sizeof(f.key));
796 f.used = TRUE;
797 memcpy(p, &f, sizeof(file_cache_t));
798 return TRUE;
801 p += sizeof(file_cache_t);
802 len += sizeof(file_cache_t);
804 if (len + sizeof(file_cache_t) > cache_size)
805 break;
808 return FALSE;
811 int main(int argc, char *argv[])
813 gint opt;
814 struct sockaddr_un addr;
815 struct passwd *pw = getpwuid(getuid());
816 gchar buf[PATH_MAX];
817 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
818 gchar *socketarg = NULL;
819 gchar *datadir = NULL;
820 gint fd;
821 GKeyFile *kf;
822 gboolean n;
823 gchar *p;
824 gchar **cache_push = NULL;
825 gchar *rcfile;
826 #ifndef DEBUG
827 #ifdef HAVE_SETRLIMIT
828 struct rlimit rl;
830 rl.rlim_cur = rl.rlim_max = 0;
832 if (setrlimit(RLIMIT_CORE, &rl) != 0)
833 err(EXIT_FAILURE, "setrlimit()");
834 #endif
835 #endif
837 rcfile = g_strdup_printf("%s/.pwmdrc", pw->pw_dir);
839 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
840 err(EXIT_FAILURE, "sysconf()");
842 cache_size = page_size;
844 #ifdef HAVE_MLOCKALL
846 * Default to using mlockall().
848 use_mlock = 1;
849 #endif
851 while ((opt = getopt(argc, argv, "hvf:")) != EOF) {
852 switch (opt) {
853 case 'f':
854 g_free(rcfile);
855 rcfile = g_strdup(optarg);
856 break;
857 case 'v':
858 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
859 exit(EXIT_SUCCESS);
860 case 'h':
861 default:
862 usage(argv[0]);
866 if ((kf = parse_rcfile(rcfile)) == NULL)
867 exit(EXIT_FAILURE);
869 g_key_file_set_list_separator(kf, ',');
871 if ((p = g_key_file_get_string(kf, "default", "socket_path", NULL)) == NULL)
872 errx(EXIT_FAILURE, "%s: socket_path not defined", rcfile);
874 if (*p == '~') {
875 p++;
876 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
877 g_free(p);
878 socketarg = g_strdup(buf);
880 else
881 socketarg = p;
883 if ((p = g_key_file_get_string(kf, "default", "data_directory", NULL)) == NULL)
884 errx(EXIT_FAILURE, "%s: data_directory not defined", rcfile);
886 if (*p == '~') {
887 p++;
888 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
889 g_free(p);
890 datadir = g_strdup(buf);
892 else
893 datadir = p;
895 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == TRUE) {
896 cache_size = g_key_file_get_integer(kf, "default", "cache_size", NULL);
898 if (cache_size < page_size || cache_size % page_size)
899 errx(EXIT_FAILURE, "cache size must be in multiples of %li.", page_size);
902 #ifdef HAVE_MLOCKALL
903 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == TRUE)
904 use_mlock = g_key_file_get_integer(kf, "default", "disable_mlockall", NULL);
905 #endif
907 if (g_key_file_has_key(kf, "default", "log_path", NULL) == TRUE) {
908 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == TRUE) {
909 n = g_key_file_get_boolean(kf, "default", "enable_logging", NULL);
911 if (n == TRUE) {
912 p = g_key_file_get_string(kf, "default", "log_path", NULL);
914 if (*p == '~') {
915 p++;
916 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
917 g_free(p);
918 logfile = g_strdup(buf);
920 else
921 logfile = p;
926 if (g_key_file_has_key(kf, "default", "cache_push", NULL) == TRUE)
927 cache_push = g_key_file_get_string_list(kf, "default", "cache_push", NULL, NULL);
929 g_key_file_free(kf);
931 if (strchr(socketarg, '/') == NULL) {
932 socketdir = g_get_current_dir();
933 socketname = g_strdup(socketarg);
934 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
936 else {
937 socketname = g_strdup(strrchr(socketarg, '/'));
938 socketname++;
939 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
940 socketdir = g_strdup(socketarg);
941 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
944 snprintf(buf, sizeof(buf), "%s", datadir);
946 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
947 err(EXIT_FAILURE, "%s", buf);
949 #ifdef MMAP_ANONYMOUS_SHARED
950 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
951 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == NULL) {
952 err(EXIT_FAILURE, "mmap()");
954 #else
955 snprintf(buf, sizeof(buf), "pwmd.%i", pw->pw_uid);
957 if ((fd = shm_open(buf, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
958 err(EXIT_FAILURE, "shm_open(): %s", buf);
961 * Should be enough for the file cache.
963 if (ftruncate(fd, cache_size) == -1)
964 err(EXIT_FAILURE, "ftruncate()");
966 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
967 fd, 0)) == NULL) {
968 shm_unlink(buf);
969 err(EXIT_FAILURE, "mmap()");
972 close(fd);
973 #endif
975 if (mlock(shm_data, cache_size) == -1)
976 warn("mlock()");
979 * bind() doesn't like the full pathname of the socket or any non alphanum
980 * characters so change to the directory where the socket is wanted then
981 * create it then change to datadir.
983 if (chdir(socketdir))
984 err(EXIT_FAILURE, "%s", socketdir);
986 g_free(socketdir);
988 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
989 err(EXIT_FAILURE, "socket()");
991 addr.sun_family = AF_UNIX;
992 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
993 g_free(--socketname);
995 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1)
996 err(EXIT_FAILURE, "bind()");
998 if (chdir(datadir)) {
999 close(sfd);
1000 unlink(socketpath);
1001 err(EXIT_FAILURE, "%s", datadir);
1004 g_free(datadir);
1005 setup_gcrypt();
1008 * Set the cache entry for a file. Prompts for the password.
1010 if (cache_push) {
1011 guchar md5file[16];
1012 guchar key[16];
1014 for (opt = 0; cache_push[opt]; opt++) {
1015 p = cache_push[opt];
1017 while (isspace(*p))
1018 p++;
1020 if (!*p)
1021 continue;
1023 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
1025 if (access(p, R_OK|W_OK) != 0) {
1026 if (errno != ENOENT) {
1027 unlink(socketpath);
1028 err(EXIT_FAILURE, "%s", p);
1031 warn("%s", p);
1032 continue;
1035 if (get_input(p, key) == FALSE) {
1036 memset(key, 0, sizeof(key));
1037 continue;
1040 if (cache_add_file(md5file, key) == FALSE) {
1041 memset(key, 0, sizeof(key));
1042 errx(EXIT_FAILURE, "%s: couldn't add file (cache_size?)", p);
1045 fprintf(stderr, "Added.\n");
1046 memset(key, 0, sizeof(key));
1047 memset(md5file, 0, sizeof(md5file));
1050 g_strfreev(cache_push);
1051 fprintf(stderr, "Done! Daemonizing...\n");
1054 g_free(rcfile);
1056 if (listen(sfd, 0) == -1)
1057 err(EXIT_FAILURE, "listen()");
1059 signal(SIGCHLD, catchsig);
1060 signal(SIGTERM, catchsig);
1061 signal(SIGINT, catchsig);
1062 signal(SIGHUP, catchsig);
1063 log_write("%s starting: %li slots available", PACKAGE_STRING, cache_size / sizeof(file_cache_t));
1065 while (!quit) {
1066 socklen_t slen = sizeof(struct sockaddr_un);
1067 struct sockaddr_un raddr;
1068 pid_t pid;
1070 if ((fd = accept(sfd, (struct sockaddr_un *)&raddr, &slen)) == -1) {
1071 if (!quit)
1072 log_write("accept(): %s", strerror(errno));
1074 continue;
1077 switch ((pid = fork())) {
1078 case -1:
1079 log_write("fork(): %s", strerror(errno));
1080 break;
1081 case 0:
1082 doit(fd);
1083 break;
1084 default:
1085 break;
1088 log_write("new connection: fd=%i, pid=%i", fd, pid);
1091 unlink(socketpath);
1092 g_free(socketpath);
1094 if (munmap(shm_data, cache_size) == -1)
1095 log_write("munmap(): %s", strerror(errno));
1097 #ifndef MMAP_ANONYMOUS_SHARED
1098 if (shm_unlink(buf) == -1)
1099 log_write("shm_unlink(): %s: %s", buf, strerror(errno));
1100 #endif
1102 log_write("pwmd exiting normally");
1103 exit(EXIT_SUCCESS);