Updates.
[pwmd.git] / src / pwmd.c
blob486731f791929a70e857c7a3b613c7445a2eb209
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006 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>
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
40 #include "xml.h"
41 #include "pwmd.h"
43 void catchsig(gint sig)
45 gint status;
47 switch (sig) {
48 case SIGCHLD:
49 waitpid(-1, &status, 0);
50 break;
51 default:
52 shutdown(sfd, SHUT_RDWR);
53 close(sfd);
54 quit = 1;
55 break;
59 void usage(gchar *pn)
61 g_printf(
62 "Usage: %s [-hv] [-r <directory>]\n"
63 " -r root directory where the socket and files are stored\n"
64 " -v version\n"
65 " -h this help text\n",
66 pn);
67 exit(EXIT_SUCCESS);
70 void send_to_client(gchar **dst, const gchar *fmt, ...)
72 va_list ap;
74 va_start(ap, fmt);
75 *dst = g_strdup_vprintf(fmt, ap);
76 va_end(ap);
79 gboolean split_input_line(gchar *str, gchar *delim)
81 if (!*str)
82 return FALSE;
84 g_strfreev(cl->req);
85 cl->req = g_strsplit(str, delim, 0);
86 return TRUE;
89 void client_help(const gchar *what)
91 gchar *line;
93 if (!what || !*what)
94 line =
95 "NFO Try 'help topic' for details\n"
96 "NFO open list get store delete set save quit\n";
97 else if (g_ascii_strcasecmp(what, "get") == 0)
98 line =
99 "NFO syntax: get account <TAB> element [<TAB> element ...]\n"
100 "NFO <account> is the account to work on and <element>\n"
101 "NFO is the element wanted.\n"
102 "NFO -\n"
103 "NFO Example: get isp <TAB> imap <TAB> port\n"
104 "NFO get isp <TAB> username\n";
105 else if (g_ascii_strcasecmp(what, "quit") == 0)
106 line =
107 "NFO syntax: quit\n"
108 "NFO close the connection\n";
109 else if (g_ascii_strcasecmp(what, "delete") == 0)
110 line =
111 "NFO syntax: delete account <TAB> element [<TAB> element ...]\n";
112 else if (g_ascii_strcasecmp(what, "store") == 0)
113 line =
114 "NFO syntax: store account <TAB> element [<TAB> element ...] <TAB> value\n"
115 "NFO <account> is the account to work on and <element>\n"
116 "NFO is the element to create or modify\n"
117 "NFO -\n"
118 "NFO Example: store isp <TAB> imap <TAB> port <TAB> 993\n"
119 "NFO store isp <TAB> username <TAB> someuser\n";
120 else if (g_ascii_strcasecmp(what, "open") == 0)
121 line =
122 "NFO syntax: open filename\n";
123 else if (g_ascii_strcasecmp(what, "list") == 0)
124 line =
125 "NFO syntax: list [account]\n"
126 "NFO shows available accounts or account elements\n";
127 else if (g_ascii_strcasecmp(what, "set") == 0)
128 line =
129 "NFO syntax: set account option value\n"
130 "NFO -----------------------------------------\n"
131 "NFO name account name\n";
132 else if (g_ascii_strcasecmp(what, "set") == 0)
133 line =
134 "NFO syntax: save\n"
135 "NFO save any changes to the opened file\n";
136 else
137 line = "NFO unknown command\n";
139 send_to_client(&cl->outbuf, "%sOK \n", line);
142 static gboolean parse_xml()
144 switch (open_xml(cl->xml, cl->len, &cl->doc, &cl->root, &cl->reader)) {
145 case 1:
146 send_to_client(&cl->outbuf, "%s", "ERR XML parse error\n");
147 return FALSE;
148 case 2:
149 send_to_client(&cl->outbuf, "%s", "ERR XMLReaderWalker() failed\n");
150 return FALSE;
151 default:
152 break;
155 cl->state = STATE_OPEN;
156 return TRUE;
159 static gboolean valid_filename(gchar *filename)
161 gchar *p;
163 for (p = filename; *p; p++) {
164 if (g_ascii_isalnum(*p) == FALSE)
165 return FALSE;
168 return TRUE;
172 * The filename will be ~/.pwmd/filename.xml.gpg.
174 static gboolean authenticate_client(gchar *filename)
176 gchar buf[LINE_MAX];
177 FILE *fp;
178 xmlChar *xml = NULL;
179 gint t = 0;
181 if (valid_filename(filename) == FALSE) {
182 send_to_client(&cl->outbuf, "%s", "ERR invalid characters in filename\n");
183 return FALSE;
186 g_snprintf(buf, sizeof(buf), "%s/%s.xml.gpg", root_dir, filename);
188 if (access(buf, R_OK|W_OK) != 0) {
189 if (errno != ENOENT) {
190 send_to_client(&cl->outbuf, "ERR %s\n", strerror(errno));
191 return FALSE;
193 else {
194 cl->filename = g_strdup(buf);
196 if ((cl->xml = new_document()) == NULL) {
197 send_to_client(&cl->outbuf, "ERR malloc()\n");
198 return FALSE;
201 cl->len = xmlStrlen(cl->xml);
202 return parse_xml();
206 if (!cl->filename)
207 cl->filename = g_strdup(buf);
209 g_snprintf(buf, sizeof(buf), "%s -o - -- %s 2>/dev/null", gpg_path,
210 cl->filename);
212 if ((fp = popen(buf, "r")) == NULL) {
213 send_to_client(&cl->outbuf, "ERR popen(): %s\n", strerror(errno));
214 return FALSE;
217 while (1) {
218 gsize len = fread(buf, 1, sizeof(buf), fp);
220 if (len == 0)
221 break;
223 xml = realloc(xml, t + len + 1);
224 memcpy(&xml[t], buf, len);
225 t += len;
226 xml[t] = 0;
229 cl->xml = xml;
230 cl->len = t;
231 pclose(fp);
234 * Not needed anymore.
236 #if 0
237 close(0);
238 close(1); // needed for save_xml() and gpg
239 close(2);
240 #endif
242 return parse_xml();
245 static gboolean get_elements()
247 gint i;
249 if (!cl->req || !cl->req[1]) {
250 send_to_client(&cl->outbuf, "ERR need an element\n");
251 return FALSE;
254 cl->doc = xmlTextReaderCurrentDoc(cl->reader);
257 * We are at the position in the document where the account was found.
258 * Search through the account for the wanted elements.
260 for (i = 1; cl->req[i]; i++) {
261 xmlNodePtr n;
263 switch (find_element(cl->reader, cl->req[i], cl->req[i+1] != NULL)) {
264 case 0:
265 break;
266 case 1:
267 send_to_client(&cl->outbuf, "ERR could not find element\n");
268 return FALSE;
269 case 2:
270 send_to_client(&cl->outbuf, "ERR trailing elements in tree\n");
271 return FALSE;
272 default:
273 break;
277 * We are at the end of the element list. Save the result.
279 if (!cl->req[i+1]) {
280 n = xmlTextReaderCurrentNode(cl->reader);
281 send_to_client(&cl->outbuf, "BEGIN %li\n%s\nOK \n",
282 xmlStrlen(n->content), n->content);
286 return TRUE;
289 static gboolean save_xml()
291 FILE *fp;
292 gchar buf[FILENAME_MAX];
293 gboolean ret = TRUE;
295 g_snprintf(buf, sizeof(buf), "gpg --use-agent --yes -c -o %s --", cl->filename);
297 if ((fp = popen(buf, "w")) == NULL)
298 return FALSE;
300 if (xmlDocDump(fp, cl->doc) == -1)
301 ret = FALSE;
303 pclose(fp);
304 return ret;
308 * Create a new or modify an existing element.
310 static gboolean create_elements()
312 gint i;
313 gboolean ret = TRUE;
314 xmlNodePtr r;
316 r = xmlTextReaderCurrentNode(cl->reader);
318 if (xmlTextReaderDepth(cl->reader) > 1)
319 r = r->parent;
321 for (i = 1; cl->req[i]; i++) {
322 xmlNodePtr n;
323 gchar *b64 = NULL;
325 if (!cl->req[i+1]) {
327 * Prevent creating 'text' elements in the root of the document.
329 if (i < 2)
330 return FALSE;
332 b64 = g_base64_encode((guchar *)cl->req[i],
333 g_utf8_strlen(cl->req[i], -1));
334 xmlNodeSetContent(r, (xmlChar *)b64);
335 break;
338 if ((n = find_node(r, (xmlChar *)cl->req[i])) == NULL) {
339 n = xmlNewNode(NULL, (xmlChar *)cl->req[i]);
340 r = xmlAddChild(r, n);
342 else
343 r = n;
346 return ret;
349 static void delete_node(xmlNodePtr n)
351 xmlUnlinkNode(n);
352 xmlFreeNode(n);
355 static gboolean delete_command()
357 int i = 1;
358 xmlNodePtr n;
360 xmlFreeTextReader(cl->reader);
361 cl->reader = NULL;
363 if ((cl->reader = xmlReaderWalker(cl->doc)) == NULL) {
364 send_to_client(&cl->outbuf, "ERR xmlReaderWalker() failed\n");
365 return FALSE;
368 if (find_account(cl->reader, cl->req[0], &cl->root) == FALSE)
369 return FALSE;
371 n = xmlTextReaderCurrentNode(cl->reader);
374 * No sub-node defined. Remove the entire node (account).
376 if (!cl->req[i]) {
377 if (n)
378 delete_node(n);
379 return TRUE;
383 * Remove matching sub-nodes starting from the root of the account.
385 while (cl->req[i] && find_element(cl->reader, cl->req[i++], 1) == 0)
386 n = xmlTextReaderCurrentNode(cl->reader);
388 if (n)
389 delete_node(n);
391 return TRUE;
394 static gboolean store_command()
396 again:
397 xmlFreeTextReader(cl->reader);
398 cl->reader = NULL;
400 if ((cl->reader = xmlReaderWalker(cl->doc)) == NULL) {
401 send_to_client(&cl->outbuf, "ERR xmlReaderWalker() failed\n");
402 return FALSE;
405 if (find_account(cl->reader, cl->req[0], &cl->root) == FALSE) {
406 if (new_account(cl->doc, cl->req[0]) == FALSE) {
407 send_to_client(&cl->outbuf, "ERR failed to create account\n");
408 return FALSE;
411 goto again;
414 xmlTextReaderNext(cl->reader);
415 return create_elements();
418 static gboolean get_command()
420 xmlFreeTextReader(cl->reader);
421 cl->reader = NULL;
423 if ((cl->reader = xmlReaderWalker(cl->doc)) == NULL) {
424 send_to_client(&cl->outbuf, "ERR xmlReaderWalker() failed\n");
425 return FALSE;
428 if (find_account(cl->reader, cl->req[0], &cl->root) == FALSE) {
429 send_to_client(&cl->outbuf, "ERR account not found\n");
430 return FALSE;
433 return get_elements();
436 static gboolean list_command(gchar *str)
438 gchar *dst = NULL;
439 gchar *p = str;
440 xmlNodePtr n;
441 gint depth = 0;
442 gchar **elements = NULL;
443 gint i = 0;
444 gchar *line;
446 xmlFreeTextReader(cl->reader);
447 cl->reader = NULL;
449 if ((cl->reader = xmlReaderWalker(cl->doc)) == NULL) {
450 send_to_client(&cl->outbuf, "ERR xmlReaderWalker() failed\n");
451 return FALSE;
454 if (strchr(p, ' ') == NULL) {
455 list_only:
456 if (list_accounts(cl->reader, &dst) == FALSE)
457 return FALSE;
458 else {
459 send_to_client(&cl->outbuf, "BEGIN %i\n%s\nOK \n",
460 g_utf8_strlen(dst, -1), dst);
461 memset(dst, 0, strlen(dst));
462 g_free(dst);
465 else {
466 p = str + 5;
468 while (*p && isspace(*p))
469 p++;
471 if (!*p)
472 goto list_only;
474 if (find_account(cl->reader, p, &n) == FALSE)
475 return FALSE;
477 depth = xmlTextReaderDepth(cl->reader);
479 while (xmlTextReaderNext(cl->reader) == 1) {
480 gchar *buf;
481 xmlChar *t;
482 gint x = 0;
484 if (xmlTextReaderDepth(cl->reader) == depth)
485 break;
487 n = xmlTextReaderCurrentNode(cl->reader);
489 if (xmlTextReaderNodeType(cl->reader) == XML_READER_TYPE_TEXT) {
490 t = xmlGetNodePath(n);
492 for (x = 0; *t && x < 3; t++) {
493 if (*t == '/')
494 x++;
497 t[xmlStrlen(t) - 7] = 0;
499 for (x = 0; t[x]; x++) {
500 if (t[x] == '/')
501 t[x] = '\t';
504 buf = g_malloc(xmlStrlen(t)+xmlStrlen(n->content)+strlen(p)+3);
505 g_sprintf(buf, "%s\t%s\t%s", p, t, n->content);
506 elements = g_realloc(elements, (i + 2) * sizeof(gchar *));
507 elements[i++] = buf;
508 elements[i] = NULL;
512 if (!elements)
513 return FALSE;
515 line = g_strjoinv("\n", elements);
516 send_to_client(&cl->outbuf, "BEGIN %li\n%s\nOK \n",
517 g_utf8_strlen(line, -1), line);
518 g_strfreev(elements);
519 g_free(line);
522 return TRUE;
525 static gchar *build_line_from_array(gchar **src)
527 if (!src)
528 return NULL;
530 return g_strjoinv(NULL, src);
533 static gboolean set_account_attribute()
535 xmlAttrPtr a;
536 gchar *name = cl->req[1];
537 xmlNodePtr n;
538 gchar *line = NULL;
539 gchar *p;
540 gboolean ret = TRUE;
542 if (g_strcasecmp(name, "name") != 0) {
543 send_to_client(&cl->outbuf, "ERR invalid attribute\n");
544 return FALSE;
547 xmlFreeTextReader(cl->reader);
548 cl->reader = NULL;
550 if ((cl->reader = xmlReaderWalker(cl->doc)) == NULL) {
551 send_to_client(&cl->outbuf, "ERR xmlReaderWalker() failed\n");
552 return FALSE;
555 if (find_account(cl->reader, cl->req[0], &cl->root) == FALSE)
556 return FALSE;
558 if ((line = build_line_from_array(cl->req + 2)) == NULL)
559 return FALSE;
562 * Don't want spaces in the account name.
564 for (p = line; *p; p++) {
565 if (g_ascii_isspace(*p) == TRUE) {
566 if (g_strcasecmp(name, "name") == 0) {
567 ret = FALSE;
568 goto done;
573 n = xmlTextReaderCurrentNode(cl->reader);
574 a = xmlHasProp(n, (xmlChar *)name);
576 if (!a)
577 a = xmlNewProp(n, (xmlChar *)name, (xmlChar *)line);
578 else
579 xmlNodeSetContent(a->children, (xmlChar *)line);
581 done:
582 g_free(line);
583 return ret;
586 gint input_parser(gchar *str)
588 gchar *p, *t;
590 str = g_strchug(str);
592 if (!*str)
593 return P_OK;
595 while ((p = strsep(&str, "\n")) != NULL) {
596 if (g_ascii_strcasecmp(p, "quit") == 0)
597 return P_QUIT;
598 else if (g_ascii_strcasecmp(p, "help") == 0)
599 client_help(NULL);
600 else if (g_ascii_strncasecmp(p, "help ", 5) == 0) {
601 t = p + 5;
602 t = g_strchug(t);
603 client_help(t);
605 else if (g_ascii_strcasecmp(p, "list") == 0 ||
606 g_ascii_strncasecmp(p, "list ", 5) == 0) {
607 if (cl->state != STATE_OPEN)
608 send_to_client(&cl->outbuf, "ERR no file opened\n");
609 else {
610 if (list_command(p) == FALSE)
611 send_to_client(&cl->outbuf, "ERR \n");
614 else if (g_ascii_strncasecmp(p, "store ", 6) == 0) {
615 t = p + 6;
616 t = g_strchug(t);
618 if (cl->state != STATE_OPEN)
619 send_to_client(&cl->outbuf, "ERR no file opened\n");
620 else {
621 if (split_input_line(t, "\t") == TRUE) {
622 if (store_command() == TRUE)
623 send_to_client(&cl->outbuf, "OK \n");
624 else
625 send_to_client(&cl->outbuf, "ERR \n");
629 else if (g_ascii_strncasecmp(p, "delete ", 7) == 0) {
630 t = p + 7;
632 if (cl->state != STATE_OPEN)
633 send_to_client(&cl->outbuf, "ERR no file opened\n");
634 else {
635 if (split_input_line(t, "\t") == TRUE) {
636 if (delete_command() == TRUE)
637 send_to_client(&cl->outbuf, "OK \n");
638 else
639 send_to_client(&cl->outbuf, "\nERR \n");
643 else if (g_ascii_strncasecmp(p, "get ", 4) == 0) {
644 t = p + 4;
645 t = g_strchug(t);
647 if (cl->state != STATE_OPEN)
648 send_to_client(&cl->outbuf, "ERR no file opened\n");
649 else {
650 if (split_input_line(t, "\t") == TRUE)
651 get_command();
654 else if (g_ascii_strncasecmp(p, "set ", 4) == 0) {
655 t = p + 4;
656 t = g_strchug(t);
658 if (cl->state != STATE_OPEN)
659 send_to_client(&cl->outbuf, "ERR no file opened\n");
660 else {
661 if (split_input_line(t, " ") == TRUE) {
662 if (set_account_attribute() == FALSE)
663 send_to_client(&cl->outbuf, "ERR \n");
664 else
665 send_to_client(&cl->outbuf, "OK \n");
669 else if (g_ascii_strncasecmp(p, "open ", 5) == 0) {
670 t = p + 5;
671 t = g_strchug(t);
673 if (cl->state == STATE_OPEN)
674 send_to_client(&cl->outbuf, "ERR a file is already opened\n");
675 else {
676 if (authenticate_client(t) == TRUE)
677 send_to_client(&cl->outbuf, "OK \n");
680 else if (g_ascii_strcasecmp(p, "save") == 0) {
681 if (cl->state != STATE_OPEN)
682 send_to_client(&cl->outbuf, "ERR no file opened\n");
683 else {
684 if (save_xml() == FALSE)
685 send_to_client(&cl->outbuf, "ERR \n");
686 else
687 send_to_client(&cl->outbuf, "OK \n");
690 else
691 send_to_client(&cl->outbuf, "ERR invalid command or command syntax\n");
694 return P_OK;
697 gboolean source_prepare(GSource *src, gint *to)
699 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
700 return TRUE;
702 return FALSE;
705 gboolean source_check(GSource *src)
707 if (cl->gfd.revents & (G_IO_IN|G_IO_PRI))
708 return TRUE;
710 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT)))
711 return TRUE;
713 return FALSE;
716 gboolean source_dispatch(GSource *src, GSourceFunc cb, gpointer data)
718 return (*cb)(data);
721 gboolean source_cb(gpointer data)
723 GIOStatus ret;
724 gsize len;
725 gchar *line;
726 GError *gerror = NULL;
728 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
729 goto quit;
731 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT))) {
732 ret = g_io_channel_write_chars(cl->ioc, cl->outbuf, -1, &len,
733 &gerror);
735 if (ret == G_IO_STATUS_NORMAL)
736 g_io_channel_flush(cl->ioc, &gerror);
737 else
738 fprintf(stderr, "%s\n", gerror->message);
740 g_clear_error(&gerror);
741 g_free(cl->outbuf);
742 cl->outbuf = NULL;
745 if (!cl->gfd.revents & (G_IO_IN))
746 return TRUE;
748 ret = g_io_channel_read_line(cl->ioc, &line, &len, NULL, &gerror);
750 if (ret != G_IO_STATUS_NORMAL) {
751 fprintf(stderr, "%s\n", gerror->message);
752 g_clear_error(&gerror);
753 return TRUE;
756 if (ret == G_IO_STATUS_EOF)
757 goto quit;
759 line[g_utf8_strlen(line, -1) - 1] = 0;
761 switch (input_parser(line)) {
762 case P_QUIT:
763 quit:
764 ret = g_io_channel_shutdown(cl->ioc, FALSE, &gerror);
766 if (ret != G_IO_STATUS_NORMAL)
767 fprintf(stderr, "%s\n", gerror->message);
769 g_io_channel_unref(cl->ioc);
770 g_clear_error(&gerror);
772 if (cl->xml) {
773 memset(cl->xml, 0, cl->len);
774 free(cl->xml);
777 if (cl->doc)
778 xmlFreeDoc(cl->doc);
780 if (cl->reader)
781 xmlFreeTextReader(cl->reader);
783 if (cl->filename)
784 free(cl->filename);
786 g_strfreev(cl->req);
787 free(cl);
788 g_main_loop_unref(gloop);
789 g_main_loop_quit(gloop);
790 return FALSE;
791 default:
792 break;
795 return TRUE;
799 * Called every time a connection is made.
801 void doit(int rfd)
803 static GSourceFuncs gsrcf = {
804 source_prepare, source_check, source_dispatch, NULL, 0, 0
806 GPollFD gfd = { rfd, G_IO_IN|G_IO_OUT|G_IO_HUP|G_IO_ERR, 0 };
808 gloop = g_main_loop_new(NULL, TRUE);
809 cl = calloc(1, sizeof(struct client_s));
810 cl->src = g_source_new(&gsrcf, sizeof(GSource));
811 cl->gfd = gfd;
812 cl->state = STATE_CONNECTED;
813 cl->ioc = g_io_channel_unix_new(rfd);
814 g_source_add_poll(cl->src, &cl->gfd);
815 g_source_set_callback(cl->src, source_cb, NULL, NULL);
816 g_source_attach(cl->src, NULL);
817 send_to_client(&cl->outbuf, "NFO Type help for available commands\nOK \n");
818 g_main_loop_run(gloop);
819 _exit(EXIT_SUCCESS);
822 int main(int argc, char *argv[])
824 int opt;
825 int rfd;
826 struct sockaddr_un addr;
827 struct passwd *pw = getpwuid(getuid());
828 char buf[PATH_MAX];
830 if ((gpg_path = g_find_program_in_path("gpg")) == NULL)
831 warnx("Could not find gpg in your path");
832 else if (getenv("GPG_AGENT_INFO") == NULL)
833 warnx("GPG_AGENT_INFO is not set");
835 while ((opt = getopt(argc, argv, "hr:v")) != EOF) {
836 switch (opt) {
837 case 'r':
838 root_dir = strdup(optarg);
839 break;
840 case 'v':
841 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
842 exit(EXIT_SUCCESS);
843 case 'h':
844 default:
845 usage(argv[0]);
849 if (!root_dir) {
850 snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
851 root_dir = strdup(buf);
853 else
854 strncpy(buf, root_dir, sizeof(buf));
856 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
857 err(EXIT_FAILURE, "%s", buf);
860 * bind() doesn't like the full pathname of the socket or any non alphanum
861 * characters.
863 if (chdir(buf))
864 err(EXIT_FAILURE, "%s", buf);
866 strncpy(buf, "socket", sizeof(buf));
868 if (access(buf, R_OK | W_OK) == 0)
869 unlink(buf);
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", buf);
877 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1)
878 err(EXIT_FAILURE, "bind()");
880 if (listen(sfd, 10) == -1)
881 err(EXIT_FAILURE, "listen()");
883 signal(SIGCHLD, catchsig);
884 signal(SIGTERM, catchsig);
885 signal(SIGINT, catchsig);
887 while (1) {
888 socklen_t slen = sizeof(struct sockaddr_un);
889 struct sockaddr_un raddr;
891 if (quit)
892 break;
894 if ((rfd = accept(sfd, (struct sockaddr_un *)&raddr, &slen)) == -1) {
895 if (!quit)
896 warn("accept");
898 continue;
901 switch (fork()) {
902 case -1:
903 warn("fork()");
904 break;
905 case 0:
906 doit(rfd);
907 break;
908 default:
909 break;
913 unlink(addr.sun_path);
914 exit(EXIT_SUCCESS);