The memory allocators have been redone to use a linked list like
[pwmd.git] / src / pwmd.c
blobea9b0be36ccfb40beffdbe14dff854afba8af49f
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 int gcry_SecureCheck(const void *ptr)
156 return 1;
159 static void setup_gcrypt()
161 gcry_check_version(NULL);
163 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
164 xfree);
166 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
167 NULL) != 0)
168 errx(EXIT_FAILURE, "AES cipher not supported");
170 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
171 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
174 static gint input_parser(gchar *str)
176 gchar *p, *t;
177 gchar **req = NULL;
179 if (str)
180 str = g_strchug(str);
182 if (!*str)
183 return P_OK;
185 while ((p = strsep(&str, "\n")) != NULL) {
186 if (g_ascii_strcasecmp(p, "QUIT") == 0)
187 return P_QUIT;
188 else if (g_ascii_strcasecmp(p, "HELP") == 0)
189 help_command(cl, NULL);
190 else if (g_ascii_strncasecmp(p, "HELP ", 5) == 0) {
191 t = p + 5;
192 t = g_strchug(t);
193 help_command(cl, t);
195 else if (g_ascii_strcasecmp(p, "LIST") == 0 ||
196 g_ascii_strncasecmp(p, "LIST ", 5) == 0) {
197 if (cl->state != STATE_OPEN)
198 send_error(cl, EPWMD_NO_FILE);
199 else
200 list_command(cl, p);
202 else if (g_ascii_strncasecmp(p, "STORE ", 6) == 0) {
203 t = p + 6;
204 t = g_strchug(t);
206 if (cl->state != STATE_OPEN)
207 send_error(cl, EPWMD_NO_FILE);
208 else {
209 if ((req = split_input_line(t, "\t", 0)) != NULL) {
210 if (store_command(cl, req) == TRUE)
211 send_to_client(cl, "OK \n");
213 else
214 send_error(cl, EPWMD_COMMAND_SYNTAX);
217 else if (g_ascii_strncasecmp(p, "DELETE ", 7) == 0) {
218 t = p + 7;
220 if (cl->state != STATE_OPEN)
221 send_error(cl, EPWMD_NO_FILE);
222 else {
223 if ((req = split_input_line(t, "\t", 0)) != NULL) {
224 if (delete_command(cl, req) == TRUE)
225 send_to_client(cl, "OK \n");
227 else
228 send_error(cl, EPWMD_COMMAND_SYNTAX);
231 else if (g_ascii_strncasecmp(p, "GET ", 4) == 0) {
232 t = p + 4;
233 t = g_strchug(t);
235 if (cl->state != STATE_OPEN)
236 send_error(cl, EPWMD_NO_FILE);
237 else {
238 if ((req = split_input_line(t, "\t", 0)) != NULL)
239 get_command(cl, &cl->reader, req, 0);
240 else
241 send_error(cl, EPWMD_COMMAND_SYNTAX);
244 else if (g_ascii_strncasecmp(p, "ATTR ", 5) == 0) {
245 t = p + 5;
246 t = g_strchug(t);
248 if (cl->state != STATE_OPEN)
249 send_error(cl, EPWMD_NO_FILE);
250 else {
251 if ((req = split_input_line(t, " ", 4)) != NULL) {
252 if (attr_command(cl, req) == TRUE)
253 send_to_client(cl, "OK \n");
255 else
256 send_error(cl, EPWMD_COMMAND_SYNTAX);
259 else if (g_ascii_strncasecmp(p, "OPEN ", 5) == 0) {
260 t = p + 5;
261 t = g_strchug(t);
263 if (cl->state == STATE_OPEN)
264 send_error(cl, EPWMD_FILE_OPENED);
265 else {
266 if ((req = split_input_line(t, " ", 2)) != NULL) {
267 if (open_command(cl, req) == TRUE) {
268 send_to_client(cl, "OK \n");
269 cl->state = STATE_OPEN;
273 * The document has been parsed and is stored in cl->doc.
275 if (cl->xml) {
276 gcry_free(cl->xml);
277 cl->xml = NULL;
280 else
281 send_error(cl, EPWMD_COMMAND_SYNTAX);
284 else if (g_ascii_strncasecmp(p, "SAVE", 4) == 0) {
285 t = p + 4;
286 t = g_strchug(t);
288 if (cl->state != STATE_OPEN)
289 send_error(cl, EPWMD_NO_FILE);
290 else {
291 req = split_input_line(t, " ", 1);
293 if (save_command(cl, cl->filename, (req) ? req[0] : NULL) == TRUE)
294 send_to_client(cl, "OK \n");
297 else if (g_ascii_strncasecmp(p, "CACHE ", 6) == 0) {
298 t = p + 6;
299 t = g_strchug(t);
300 req = split_input_line(t, " ", 2);
302 if (cache_command(cl, req) == TRUE)
303 send_to_client(cl, "OK \n");
305 else if (g_ascii_strncasecmp(p, "DUMP", 4) == 0) {
306 if (cl->state != STATE_OPEN)
307 send_error(cl, EPWMD_NO_FILE);
308 else {
309 if (dump_command(cl) == TRUE)
310 send_to_client(cl, "OK \n");
313 else
314 send_error(cl, EPWMD_COMMAND_SYNTAX);
317 if (req) {
318 g_strfreev(req);
319 req = NULL;
322 return P_OK;
325 static gboolean source_prepare(GSource *src, gint *to)
327 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
328 return TRUE;
330 return FALSE;
333 static gboolean source_check(GSource *src)
335 if (cl->gfd.revents & (G_IO_IN|G_IO_PRI))
336 return TRUE;
338 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT)))
339 return TRUE;
341 return FALSE;
344 static gboolean source_dispatch(GSource *src, GSourceFunc cb, gpointer data)
346 return (*cb)(data);
349 static gboolean source_cb(gpointer data)
351 GIOStatus ret;
352 gsize len;
353 gchar *line = NULL;
354 GError *gerror = NULL;
355 gchar **p;
357 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
358 goto quit;
360 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT))) {
361 for (p = cl->outbuf; *p; p++) {
362 ret = g_io_channel_write_chars(cl->ioc, *p, -1, &len, &gerror);
364 if (ret == G_IO_STATUS_NORMAL)
365 g_io_channel_flush(cl->ioc, &gerror);
366 else
367 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gerror->message);
369 g_clear_error(&gerror);
371 if (quit)
372 goto quit;
375 g_strfreev(cl->outbuf);
376 cl->outbuf = NULL;
379 if (!cl->gfd.revents & (G_IO_IN))
380 return TRUE;
382 ret = g_io_channel_read_line(cl->ioc, &line, &len, NULL, &gerror);
384 if (ret != G_IO_STATUS_NORMAL) {
385 if (ret == G_IO_STATUS_EOF)
386 goto quit;
388 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gerror->message);
389 g_clear_error(&gerror);
390 return TRUE;
393 line[g_utf8_strlen(line, -1) - 1] = 0;
395 switch (input_parser(line)) {
396 case P_QUIT:
397 quit:
398 if (line) {
399 memset(line, 0, len);
400 g_free(line);
403 g_clear_error(&gerror);
405 if (cl->xml)
406 gcry_free(cl->xml);
408 if (cl->gh)
409 gcry_cipher_close(cl->gh);
411 if (cl->doc)
412 xmlFreeDoc(cl->doc);
414 if (cl->reader)
415 xmlFreeTextReader(cl->reader);
417 if (cl->outbuf)
418 g_free(cl->outbuf);
420 if (cl->filename)
421 g_free(cl->filename);
423 #if 0
424 if (memlist)
425 g_slist_free(memlist);
426 #endif
428 g_main_loop_unref(gloop);
429 g_main_loop_quit(gloop);
430 return FALSE;
431 default:
432 break;
435 memset(line, 0, len);
436 g_free(line);
437 return TRUE;
440 static struct memlist_s *memlist_remove(struct memlist_s *list, struct memlist_s *r)
442 struct memlist_s *m, *last = NULL;
444 for (m = list; m; m = m->next) {
445 if (m->ptr == r->ptr) {
446 memset(m->ptr, 0, m->size);
447 free(m->ptr);
449 if (last) {
450 if (m->next)
451 last->next = m->next;
452 else
453 last->next = NULL;
455 else if (m->next)
456 m = m->next;
457 else
458 list = NULL;
460 break;
463 last = m;
466 return list;
469 static struct memlist_s *memlist_append(struct memlist_s *list, struct memlist_s *new)
471 struct memlist_s *m;
473 if (!list) {
474 list = new;
475 list->next = NULL;
476 return list;
479 for (m = list; m; m = m->next) {
480 if (!m->next) {
481 m->next = new;
482 m = m->next;
483 m->next = NULL;
484 break;
488 return list;
491 void xfree(void *ptr)
493 struct memlist_s *m;
495 if (!ptr)
496 return;
498 for (m = memlist; m; m = m->next) {
499 if (m->ptr == ptr) {
500 #ifdef DEBUG
501 fprintf(stderr, "xfree(): %p %i\n", ptr, m->size);
502 #endif
503 memlist = memlist_remove(memlist, m);
504 return;
508 warnx("xfree(): %p not found", ptr);
509 assert(0);
512 void *xmalloc(size_t size)
514 void *p;
515 struct memlist_s *new;
517 if (size <= 0)
518 return NULL;
520 if ((new = malloc(sizeof(struct memlist_s))) == NULL)
521 return NULL;
523 if ((p = malloc(size)) == NULL) {
524 free(new);
525 return NULL;
528 new->ptr = p;
529 new->size = size;
530 memlist = memlist_append(memlist, new);
531 #ifdef DEBUG
532 fprintf(stderr, "xmalloc(): %p %i\n", p, size);
533 #endif
534 return new->ptr;
537 void *xcalloc(size_t nmemb, size_t size)
539 void *p;
540 struct memlist_s *new;
542 if (size <= 0)
543 return NULL;
545 if ((new = malloc(sizeof(struct memlist_s))) == NULL)
546 return NULL;
548 if ((p = calloc(nmemb, size)) == NULL) {
549 free(new);
550 return NULL;
553 new->ptr = p;
554 new->size = size;
555 memlist = memlist_append(memlist, new);
556 #ifdef DEBUG
557 fprintf(stderr, "xcalloc(): %p %i\n", p, size);
558 #endif
559 return new->ptr;
562 void *xrealloc(void *ptr, size_t size)
564 void *new;
565 struct memlist_s *m;
567 if (!ptr)
568 return xmalloc(size);
570 if (size <= 0)
571 return ptr;
573 for (m = memlist; m; m = m->next) {
574 if (m->ptr == ptr) {
575 if ((new = malloc(size)) == NULL)
576 return NULL;
578 memcpy(new, m->ptr, (size < m->size) ? size : m->size);
579 memset(m->ptr, 0, m->size);
580 free(m->ptr);
581 m->ptr = new;
582 m->size = size;
583 #ifdef DEBUG
584 fprintf(stderr, "xrealloc(): %p %i\n", new, size);
585 #endif
586 return m->ptr;
590 warnx("xrealloc(): %p not found", ptr);
591 assert(0);
592 return NULL;
595 char *xstrdup(const char *str)
597 char *new;
598 size_t len;
599 const char *p;
600 char *np;
602 if (!str)
603 return NULL;
605 len = strlen(str) + 1;
607 if ((new = xmalloc(len * sizeof(char))) == NULL)
608 return NULL;
610 for (p = str, np = new; *p; p++)
611 *np++ = *p;
613 *np = 0;
614 #ifdef DEBUG
615 fprintf(stderr, "xstrdup(): %p\n", new);
616 #endif
617 return new;
621 * Called every time a connection is made.
623 static void doit(int fd, gint iter)
625 static GSourceFuncs gsrcf = {
626 source_prepare, source_check, source_dispatch, NULL, 0, 0
628 GPollFD gfd = { fd, G_IO_IN|G_IO_OUT|G_IO_HUP|G_IO_ERR, 0 };
629 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
631 g_mem_set_vtable(&mtable);
633 #ifdef HAVE_MLOCKALL
634 if (use_mlock && mlockall(MCL_FUTURE) == -1)
635 err(EXIT_FAILURE, "mlockall()");
636 #endif
638 gloop = g_main_loop_new(NULL, TRUE);
639 cl = g_malloc0(sizeof(struct client_s));
640 cl->src = g_source_new(&gsrcf, sizeof(GSource));
641 cl->gfd = gfd;
642 cl->iter = iter;
643 cl->state = STATE_CONNECTED;
644 cl->ioc = g_io_channel_unix_new(fd);
645 g_source_add_poll(cl->src, &cl->gfd);
646 g_source_set_callback(cl->src, source_cb, NULL, NULL);
647 g_source_attach(cl->src, NULL);
648 #ifndef DEBUG
649 close(0);
650 close(1);
651 close(2);
652 #endif
654 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
655 xmlInitMemory();
657 if ((gcryerrno = gcry_cipher_open(&cl->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
658 send_to_client(cl, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
659 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
660 quit = 1;
662 else
663 // FIXME 100% CPU if removed (poll()).
664 send_to_client(cl, "OK \n");
666 g_main_loop_run(gloop);
667 g_io_channel_unref(cl->ioc);
668 g_free(cl);
669 shutdown(fd, SHUT_RDWR);
670 log_write("exiting");
671 _exit(EXIT_SUCCESS);
674 static void set_rcfile_defaults(GKeyFile *kf)
676 gchar buf[PATH_MAX];
678 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
679 g_key_file_set_string(kf, "default", "socket_path", buf);
680 snprintf(buf, sizeof(buf), "~/.pwmd");
681 g_key_file_set_string(kf, "default", "data_directory", buf);
682 snprintf(buf, sizeof(buf), "~/.pwmd/.log");
683 g_key_file_set_string(kf, "default", "log_path", buf);
684 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
685 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
686 g_key_file_set_boolean(kf, "default", "disable_mlockall", FALSE);
689 static GKeyFile *parse_rcfile(const gchar *filename)
691 GKeyFile *kf = g_key_file_new();
692 GError *error = NULL;
694 if (g_key_file_load_from_file(kf, filename, G_KEY_FILE_NONE, &error) == FALSE) {
695 if (error->code == G_FILE_ERROR_NOENT) {
696 g_clear_error(&error);
697 set_rcfile_defaults(kf);
698 return kf;
700 else {
701 warnx("%s: %s", filename, error->message);
702 g_clear_error(&error);
703 return NULL;
707 return kf;
710 static gboolean try_xml_decrypt(gint fd, struct stat st, guchar *key, gint iter)
712 guchar *iv;
713 void *inbuf;
714 gcry_cipher_hd_t gh;
715 gsize insize;
716 guchar tkey[gcrykeysize];
718 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
719 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
720 return FALSE;
723 insize = st.st_size - gcryblocksize;
724 iv = gcry_malloc(gcryblocksize);
725 read(fd, iv, gcryblocksize);
726 inbuf = gcry_malloc(insize);
727 read(fd, inbuf, insize);
728 close(fd);
729 memcpy(tkey, key, sizeof(tkey));
730 tkey[0] ^= 1;
732 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
733 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
734 gcry_cipher_close(gh);
735 gcry_free(inbuf);
736 gcry_free(iv);
737 return FALSE;
740 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
741 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
742 gcry_cipher_close(gh);
743 gcry_free(inbuf);
744 gcry_free(iv);
745 return FALSE;
748 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
749 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
750 gcry_cipher_close(gh);
751 gcry_free(inbuf);
752 gcry_free(iv);
753 return FALSE;
756 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
757 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
758 gcry_cipher_close(gh);
759 gcry_free(inbuf);
760 gcry_free(iv);
761 return FALSE;
764 while (iter-- > 0) {
765 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
766 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
767 gcry_cipher_close(gh);
768 gcry_free(inbuf);
769 gcry_free(iv);
770 return FALSE;
773 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
774 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
775 gcry_cipher_close(gh);
776 gcry_free(inbuf);
777 gcry_free(iv);
778 return FALSE;
782 gcry_cipher_close(gh);
783 gcry_free(iv);
785 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
786 gcry_free(inbuf);
787 return FALSE;
790 gcry_free(inbuf);
791 return TRUE;
794 static gboolean get_input(const gchar *filename, guchar *key, gint iter)
796 gchar buf[LINE_MAX], *p;
797 struct termios told, tnew;
798 gint fd;
799 struct stat st;
800 int try = 0;
801 gint tty = isatty(STDIN_FILENO);
803 if ((fd = open_file(filename, &st)) == -1) {
804 warn("%s", filename);
805 return FALSE;
808 if (st.st_size == 0) {
809 fprintf(stderr, "Skipping empty file '%s'.\n", filename);
810 close(fd);
811 return FALSE;
814 if (tty) {
815 if (tcgetattr(STDIN_FILENO, &told) == -1)
816 err(EXIT_FAILURE, "tcgetattr()");
818 memcpy(&tnew, &told, sizeof(struct termios));
819 tnew.c_lflag &= ~(ECHO);
820 tnew.c_lflag |= ICANON|ECHONL;
823 again:
824 if (tty) {
825 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
826 tcsetattr(STDIN_FILENO, TCSANOW, &told);
827 err(EXIT_FAILURE, "tcsetattr()");
831 printf("Password for '%s': ", filename);
833 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
834 if (tty)
835 tcsetattr(STDIN_FILENO, TCSANOW, &told);
837 close(fd);
838 return FALSE;
841 if (tty)
842 tcsetattr(STDIN_FILENO, TCSANOW, &told);
844 p[strlen(p) - 1] = 0;
846 if (!*p) {
847 fprintf(stderr, "Skipping.\n");
848 close(fd);
849 return FALSE;
852 gcry_md_hash_buffer(GCRY_MD_SHA256, key, p, strlen(p));
853 memset(buf, 0, sizeof(buf));
855 if (try_xml_decrypt(fd, st, key, iter) == FALSE) {
856 if (try++ == 2) {
857 fprintf(stderr, "Invalid password. Skipping file.\n");
858 return FALSE;
860 else {
861 fprintf(stderr, "Invalid password.\n");
862 goto again;
866 return TRUE;
869 gint cache_file_count()
871 void *p;
872 gint n = 0;
873 glong len;
874 file_cache_t f;
876 for (p = shm_data, len = 0; len <= cache_size;) {
877 memcpy(&f, p, sizeof(file_cache_t));
879 if (f.used == TRUE)
880 n++;
882 p += sizeof(file_cache_t);
883 len += sizeof(file_cache_t);
885 if (len + sizeof(file_cache_t) > cache_size)
886 break;
889 return n;
892 gboolean cache_add_file(const guchar *md5file, const guchar *shakey)
894 void *p;
895 file_cache_t f;
896 gint nfiles = cache_file_count();
897 glong len;
900 * Make sure there is enough secure memory.
902 if (!md5file || (nfiles + 1) * sizeof(file_cache_t) > cache_size)
903 return FALSE;
906 * Find the first available "slot".
908 for (p = shm_data, len = 0; len <= cache_size;) {
909 memcpy(&f, p, sizeof(file_cache_t));
911 if (f.used == FALSE) {
912 memcpy(&f.filename, md5file, sizeof(f.filename));
914 if (shakey)
915 memcpy(&f.key, shakey, sizeof(f.key));
917 f.used = TRUE;
918 memcpy(p, &f, sizeof(file_cache_t));
919 return TRUE;
922 p += sizeof(file_cache_t);
923 len += sizeof(file_cache_t);
925 if (len + sizeof(file_cache_t) > cache_size)
926 break;
929 return FALSE;
932 int main(int argc, char *argv[])
934 gint opt;
935 struct sockaddr_un addr;
936 struct passwd *pw = getpwuid(getuid());
937 gchar buf[PATH_MAX];
938 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
939 gchar *socketarg = NULL;
940 gchar *datadir = NULL;
941 gint fd;
942 GKeyFile *kf;
943 gboolean n;
944 gchar *p;
945 gchar **cache_push = NULL;
946 gchar *rcfile;
947 gint iter = 0;
948 #ifndef DEBUG
949 #ifdef HAVE_SETRLIMIT
950 struct rlimit rl;
952 rl.rlim_cur = rl.rlim_max = 0;
954 if (setrlimit(RLIMIT_CORE, &rl) != 0)
955 err(EXIT_FAILURE, "setrlimit()");
956 #endif
957 #endif
959 rcfile = g_strdup_printf("%s/.pwmdrc", pw->pw_dir);
961 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
962 err(EXIT_FAILURE, "sysconf()");
964 cache_size = page_size;
966 #ifdef HAVE_MLOCKALL
968 * Default to using mlockall().
970 use_mlock = 1;
971 #endif
973 while ((opt = getopt(argc, argv, "hvf:")) != EOF) {
974 switch (opt) {
975 case 'f':
976 g_free(rcfile);
977 rcfile = g_strdup(optarg);
978 break;
979 case 'v':
980 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
981 exit(EXIT_SUCCESS);
982 case 'h':
983 default:
984 usage(argv[0]);
988 if ((kf = parse_rcfile(rcfile)) == NULL)
989 exit(EXIT_FAILURE);
991 g_key_file_set_list_separator(kf, ',');
993 if ((p = g_key_file_get_string(kf, "default", "socket_path", NULL)) == NULL)
994 errx(EXIT_FAILURE, "%s: socket_path not defined", rcfile);
996 if (*p == '~') {
997 p++;
998 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
999 g_free(p);
1000 socketarg = g_strdup(buf);
1002 else
1003 socketarg = p;
1005 if ((p = g_key_file_get_string(kf, "default", "data_directory", NULL)) == NULL)
1006 errx(EXIT_FAILURE, "%s: data_directory not defined", rcfile);
1008 if (*p == '~') {
1009 p++;
1010 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1011 g_free(p);
1012 datadir = g_strdup(buf);
1014 else
1015 datadir = p;
1017 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == TRUE) {
1018 cache_size = g_key_file_get_integer(kf, "default", "cache_size", NULL);
1020 if (cache_size < page_size || cache_size % page_size)
1021 errx(EXIT_FAILURE, "cache size must be in multiples of %li.", page_size);
1024 if (g_key_file_has_key(kf, "default", "iterations", NULL) == TRUE)
1025 iter = g_key_file_get_integer(kf, "default", "iterations", NULL);
1027 #ifdef HAVE_MLOCKALL
1028 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == TRUE)
1029 use_mlock = g_key_file_get_integer(kf, "default", "disable_mlockall", NULL);
1030 #endif
1032 if (g_key_file_has_key(kf, "default", "log_path", NULL) == TRUE) {
1033 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == TRUE) {
1034 n = g_key_file_get_boolean(kf, "default", "enable_logging", NULL);
1036 if (n == TRUE) {
1037 p = g_key_file_get_string(kf, "default", "log_path", NULL);
1039 if (*p == '~') {
1040 p++;
1041 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1042 g_free(p);
1043 logfile = g_strdup(buf);
1045 else
1046 logfile = p;
1051 if (g_key_file_has_key(kf, "default", "cache_push", NULL) == TRUE)
1052 cache_push = g_key_file_get_string_list(kf, "default", "cache_push", NULL, NULL);
1054 g_key_file_free(kf);
1056 if (strchr(socketarg, '/') == NULL) {
1057 socketdir = g_get_current_dir();
1058 socketname = g_strdup(socketarg);
1059 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1061 else {
1062 socketname = g_strdup(strrchr(socketarg, '/'));
1063 socketname++;
1064 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1065 socketdir = g_strdup(socketarg);
1066 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1069 snprintf(buf, sizeof(buf), "%s", datadir);
1071 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
1072 err(EXIT_FAILURE, "%s", buf);
1074 #ifdef MMAP_ANONYMOUS_SHARED
1075 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1076 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == NULL) {
1077 err(EXIT_FAILURE, "mmap()");
1079 #else
1080 snprintf(buf, sizeof(buf), "pwmd.%i", pw->pw_uid);
1082 if ((fd = shm_open(buf, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
1083 err(EXIT_FAILURE, "shm_open(): %s", buf);
1086 * Should be enough for the file cache.
1088 if (ftruncate(fd, cache_size) == -1)
1089 err(EXIT_FAILURE, "ftruncate()");
1091 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
1092 fd, 0)) == NULL) {
1093 shm_unlink(buf);
1094 err(EXIT_FAILURE, "mmap()");
1097 close(fd);
1098 #endif
1100 if (mlock(shm_data, cache_size) == -1)
1101 warn("mlock()");
1104 * bind() doesn't like the full pathname of the socket or any non alphanum
1105 * characters so change to the directory where the socket is wanted then
1106 * create it then change to datadir.
1108 if (chdir(socketdir))
1109 err(EXIT_FAILURE, "%s", socketdir);
1111 g_free(socketdir);
1113 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
1114 err(EXIT_FAILURE, "socket()");
1116 addr.sun_family = AF_UNIX;
1117 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1118 g_free(--socketname);
1120 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1)
1121 err(EXIT_FAILURE, "bind()");
1123 if (chdir(datadir)) {
1124 close(sfd);
1125 unlink(socketpath);
1126 err(EXIT_FAILURE, "%s", datadir);
1129 g_free(datadir);
1130 setup_gcrypt();
1132 // FIXME broken is the password is wrong.
1134 * Set the cache entry for a file. Prompts for the password.
1136 if (cache_push) {
1137 guchar *md5file;
1138 guchar *key;
1140 for (opt = 0; cache_push[opt]; opt++) {
1141 p = cache_push[opt];
1143 while (isspace(*p))
1144 p++;
1146 if (!*p)
1147 continue;
1149 md5file = gcry_malloc(16);
1150 key = gcry_malloc(gcrykeysize);
1151 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
1153 if (access(p, R_OK|W_OK) != 0) {
1154 if (errno != ENOENT) {
1155 unlink(socketpath);
1156 err(EXIT_FAILURE, "%s", p);
1159 warn("%s", p);
1160 continue;
1163 if (get_input(p, key, iter) == FALSE) {
1164 gcry_free(key);
1165 gcry_free(md5file);
1166 continue;
1169 if (cache_add_file(md5file, key) == FALSE) {
1170 gcry_free(key);
1171 errx(EXIT_FAILURE, "%s: couldn't add file (cache_size?)", p);
1174 fprintf(stderr, "Added.\n");
1175 gcry_free(key);
1176 gcry_free(md5file);
1179 g_strfreev(cache_push);
1180 fprintf(stderr, "Done! Daemonizing...\n");
1183 g_free(rcfile);
1185 if (listen(sfd, 0) == -1)
1186 err(EXIT_FAILURE, "listen()");
1188 signal(SIGCHLD, catchsig);
1189 signal(SIGTERM, catchsig);
1190 signal(SIGINT, catchsig);
1191 signal(SIGHUP, catchsig);
1192 log_write("%s starting: %li slots available", PACKAGE_STRING, cache_size / sizeof(file_cache_t));
1194 while (!quit) {
1195 socklen_t slen = sizeof(struct sockaddr_un);
1196 struct sockaddr_un raddr;
1197 pid_t pid;
1199 if ((fd = accept(sfd, (struct sockaddr_un *)&raddr, &slen)) == -1) {
1200 if (!quit)
1201 log_write("accept(): %s", strerror(errno));
1203 continue;
1206 switch ((pid = fork())) {
1207 case -1:
1208 log_write("fork(): %s", strerror(errno));
1209 break;
1210 case 0:
1211 doit(fd, iter);
1212 break;
1213 default:
1214 break;
1217 log_write("new connection: fd=%i, pid=%i", fd, pid);
1220 unlink(socketpath);
1221 g_free(socketpath);
1223 if (munmap(shm_data, cache_size) == -1)
1224 log_write("munmap(): %s", strerror(errno));
1226 #ifndef MMAP_ANONYMOUS_SHARED
1227 if (shm_unlink(buf) == -1)
1228 log_write("shm_unlink(): %s: %s", buf, strerror(errno));
1229 #endif
1231 log_write("pwmd exiting normally");
1232 exit(EXIT_SUCCESS);