getopt handling and lots of smaller logging and other details
[rxpd] / src / rxpd.c
1 /*
2     rxpd.c - regex policy daemon
3
4   Copyright (C)
5     2007,               Christian Thaeter <ct@pipapo.org>
6
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "rxpd.h"
23
24 static struct rxpd_base global_base;
25
26 struct rxpd_base*
27 rxpd_init (struct event_base* eventbase)
28 {
29   if (global_base.basedir)
30     return NULL;
31
32   global_base.basedir = NULL;
33
34   global_base.verbosity = LOG_WARNING;
35   global_base.daemonize = 0;
36   global_base.regflags = 0;
37   global_base.policy = NULL;
38
39   if (!eventbase)
40     rxpd_die ("no eventbase provided");
41
42   global_base.eventbase = eventbase;
43
44   psplay_init_root (&global_base.files, rxpd_file_cmp, rxpd_file_delete);
45   llist_init (&global_base.sockets);
46
47   return &global_base;
48 }
49
50
51 void
52 rxpd_destroy (void)
53 {
54   if (global_base.basedir)
55     {
56       free (global_base.basedir);
57       psplay_destroy_root (&global_base.files);
58       LLIST_WHILE_HEAD (&global_base.sockets, n)
59         {
60           struct rxpd_socket* socket = (struct rxpd_socket*)n;
61           rxpd_socket_delete (socket);
62         }
63     }
64 }
65
66 void
67 rxpd_log (struct rxpd_base* self, int level, const char* fmt, ...)
68 {
69   va_list ap;
70   va_start (ap, fmt);
71   if (level <= (self?self->verbosity:LOG_DEBUG))
72     {
73       if (self->daemonize)
74         vsyslog (level, fmt, ap);
75       vfprintf (stderr, fmt, ap);
76     }
77   va_end (ap);
78 }
79
80 void
81 rxpd_die (const char* fmt, ...)
82 {
83   va_list ap;
84   va_start(ap, fmt);
85   vsyslog (LOG_EMERG, fmt, ap);
86   vfprintf (stderr, fmt, ap);
87   va_end (ap);
88   abort ();
89 }
90
91 void*
92 rxpd_malloc (size_t size)
93 {
94   void* r;
95   r = malloc (size);
96   if (!r)
97     rxpd_die ("Out of Memeory\n");
98   return r;
99 }
100
101 char*
102 rxpd_strdup (const char* str)
103 {
104   char* r;
105   r = strdup (str);
106   if (!r)
107     rxpd_die ("Out of Memeory\n");
108   return r;
109 }
110
111 //
112 struct rxpd_rule*
113 rxpd_rule_new (const char* buf)
114 {
115   struct rxpd_rule* self = rxpd_malloc (sizeof (struct rxpd_rule));
116
117   llist_init (&self->node);
118
119   if (*buf != '#')
120     {
121       int err;
122       char* rxstart = strchr (buf, ':');
123
124       if (!rxstart)
125         self->string = rxpd_strdup ("#ERROR: Syntax error, line was neither a comment nor a rule");
126       else
127         {
128           // TODO regflags from base
129           err = regcomp (&self->rx, rxstart+1, REG_EXTENDED|REG_ICASE|REG_NOSUB);
130
131           if (!err)
132             self->string = rxpd_strdup (buf);
133           else
134             {
135               regfree (&self->rx);
136               char ebuf[256];
137               size_t len = regerror (err, NULL, ebuf, 256);
138               self->string = rxpd_malloc (len + strlen(buf) + 14);
139               strcpy (self->string, "#ERROR: ");
140               strcat (self->string, ebuf);
141               strcat (self->string, " in '");
142               strcat (self->string, buf);
143               strcat (self->string, "'");
144             }
145         }
146     }
147   else
148     self->string = rxpd_strdup (buf);
149
150   return self;
151 }
152
153 void
154 rxpd_rule_delete (struct rxpd_rule* rule)
155 {
156   if (rule)
157     {
158       llist_unlink (&rule->node);
159       if (rule->string[0] != '#')
160         regfree (&rule->rx);
161       free (rule->string);
162       free(rule);
163     }
164 }
165
166 //
167
168
169 struct rxpd_file*
170 rxpd_file_new (struct rxpd_base* base, const char* filename)
171 {
172   char buf[4096];
173   struct rxpd_file* self = NULL;
174
175   if (!filename ||
176       strcspn(filename, RXPD_FILE_ILG_CHARS) != strlen (filename) ||
177       strlen (filename) + strlen (base->basedir) > 4095)
178     return NULL;
179
180   strcpy (buf, base->basedir);
181   strcat (buf, filename);
182   filename = rxpd_strdup (buf);
183
184   self = rxpd_malloc (sizeof (struct rxpd_file));
185   self->filename = filename;
186   self->base = base;
187   const char* basename = strrchr (filename, '/');
188   if (basename)
189     ++basename;
190   else
191     basename = filename;
192   psplay_init (&self->node, basename);
193   llist_init (&self->rules);
194
195   psplay_insert (&base->files, &self->node);
196   return self;
197 }
198
199 void
200 rxpd_file_delete (PSplay f)
201 {
202   if (f)
203     {
204       struct rxpd_file* file = (struct rxpd_file*)f;
205       LLIST_WHILE_HEAD (&file->rules, n)
206         {
207           struct rxpd_rule* node = (struct rxpd_rule*)n;
208           rxpd_rule_delete (node);
209         }
210       free ((void*)file->filename);
211       free (f);
212     }
213 }
214
215 int
216 rxpd_file_load (struct rxpd_file* self)
217 {
218   FILE* f = fopen (self->filename, "r");
219   // TODO error handling
220   if (f)
221     {
222       /* First purge old rules */
223       LLIST_WHILE_HEAD (&self->rules, n)
224         {
225           struct rxpd_rule* node = (struct rxpd_rule*)n;
226           rxpd_rule_delete (node);
227         }
228
229       // TODO test excess line length = error
230       char buf[4096];
231
232       rxpd_log (self->base, LOG_NOTICE, "loading '%s'\n", self->filename);
233
234       while (fgets (buf, 4096, f))
235         {
236           size_t last = strlen(buf);
237           if (buf[last-1] == '\n')
238             buf[last-1] = '\0';
239
240           struct rxpd_rule* rule;
241           rule = rxpd_rule_new (buf);
242           if (!rule)
243             abort();
244
245           rxpd_log (self->base, LOG_DEBUG, "new rule '%s'\n", rule->string);
246
247           llist_insert_tail (&self->rules, &rule->node);
248         }
249
250       fclose (f);
251       return 1;
252     }
253   else
254     {
255       rxpd_log (self->base, LOG_ERR, "failed loading '%s'\n", self->filename);
256       return 0;
257     }
258 }
259
260 int
261 rxpd_file_save (struct rxpd_file* self)
262 {
263   FILE* f = fopen (self->filename, "w");
264   // TODO error handling
265   if (f)
266     {
267       LLIST_FOREACH (&self->rules, n)
268         {
269           struct rxpd_rule* node = (struct rxpd_rule*)n;
270           fprintf (f, "%s\n", node->string);
271         }
272
273       fclose (f);
274       rxpd_log (self->base, LOG_NOTICE, "saved '%s'\n", self->filename);
275       return 1;
276     }
277   else
278     {
279       rxpd_log (self->base, LOG_ERR, "failed saving '%s'\n", self->filename);
280       return 0;
281     }
282 }
283
284 int
285 rxpd_file_cmp (const void* A, const void* B)
286 {
287   return strcmp (A, B);
288 }
289
290
291 //
292
293 struct rxpd_socket*
294 rxpd_socket_new_tcp4 (struct rxpd_base* base, const char* addr, unsigned short port)
295 {
296   struct rxpd_socket* self;
297   self = rxpd_malloc (sizeof (struct rxpd_socket));
298
299   self->base = base;
300
301   llist_init (&self->node);
302
303   self->fd = socket (PF_INET, SOCK_STREAM, 0);
304   if (self->fd == -1)
305     abort ();
306
307   struct sockaddr_in listen_addr;
308   memset (&listen_addr, 0, sizeof (listen_addr));
309
310   listen_addr.sin_family = AF_INET;
311   if (addr)
312     {
313       if (inet_aton (addr, &listen_addr.sin_addr) == 0)
314         abort();
315     }
316   else
317     listen_addr.sin_addr.s_addr = INADDR_ANY;
318
319   listen_addr.sin_port = htons(port);
320
321   if (bind (self->fd, (struct sockaddr*)&listen_addr, sizeof (listen_addr)) == -1)
322     abort();
323
324   static int yes = 1;
325   if (setsockopt (self->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
326     abort ();
327
328   if (listen (self->fd, 20) == -1)
329     abort ();
330
331   event_set (&self->ev, self->fd, EV_READ, rxpd_socket_accept, self);
332   llist_insert_tail (&base->sockets, &self->node);
333   return self;
334 }
335
336
337 void
338 rxpd_socket_delete (struct rxpd_socket* self)
339 {
340   if (self)
341     {
342       event_del (&self->ev);
343       llist_unlink (&self->node);
344       close (self->fd);
345     }
346   free (self);
347 }
348
349 struct rxpd_socket*
350 rxpd_socket_schedule (struct rxpd_socket* self)
351 {
352   if (self)
353     {
354       event_add (&self->ev, NULL);
355     }
356   return self;
357 }
358
359 struct rxpd_socket*
360 rxpd_socket_suspend (struct rxpd_socket* self)
361 {
362   if (self)
363     {
364       event_del (&self->ev);
365     }
366   return self;
367 }
368
369 void
370 rxpd_socket_accept (int fd, short event, void* ptr)
371 {
372   (void) event;
373   struct rxpd_socket* self = ptr;
374
375   struct rxpd_connection* conn =
376     rxpd_connection_new (self->base, fd);
377
378   rxpd_connection_schedule (conn);
379   rxpd_socket_schedule (self);
380 }
381
382 ///
383
384 struct rxpd_buffer*
385 rxpd_buffer_init (struct rxpd_buffer* self, struct rxpd_connection* conn)
386 {
387   self->conn = conn;
388   self->state = RXPD_OK;
389   self->eol = self->eob = self->buffer;
390   self->buffer [4095] = '\0';
391   return self;
392 }
393
394
395 char*
396 rxpd_buffer_readline (struct rxpd_buffer* self, int again)
397 {
398   int fd = self->conn->fd;
399
400   if (self->eol < self->eob)
401     {
402       //there was a line pending, shift buffer left
403       memmove (self->buffer, self->eol+1, self->eob - self->eol - 1);
404       self->eob = (char*)(self->eob - (self->eol - self->buffer + 1));
405       self->eol = self->buffer;
406       // TODO handle \r's
407     }
408
409
410   if (!again && self->state == RXPD_OK)   // we only read when again is 0, first iteration
411     {
412       ssize_t r = 0;
413       do
414         {
415           r = read(fd, self->eob, 4095 - (self->eob - self->buffer));
416         }
417       while (r == -1 && errno == EINTR);
418
419       if (r != -1)
420         {
421
422           if (r == 0)
423             {
424               shutdown (fd, SHUT_RD);
425               self->state = RXPD_EOF;
426             }
427
428           self->eob += r;
429         }
430       else
431         self->state = RXPD_ERROR;
432     }
433
434   // find next newline, terminate string there
435   for (char* i = self->buffer; i < self->eob; ++i)
436     {
437       if (*i == '\n')
438         {
439           *i = '\0';
440           self->eol = i;
441           break;
442         }
443     }
444
445   // TODO handle buffer overfulls
446
447   return (self->eob == self->buffer) ? NULL : self->buffer;
448 }
449
450 /*
451 void
452 rxpd_buffer_write(int fd, short event, void* ptr)
453 {
454   struct rxpd_buffer* self = (struct rxpd_buffer*) ptr;
455
456   ssize_t n = write(int fd, const void *buf, size_t count);
457
458 }
459 */
460
461 int
462 rxpd_buffer_printf (struct rxpd_buffer* self, const char* fmt, ...)
463 {
464   // for now we do a blocking write, needs to be fixed some day
465   // add string to buffer
466   va_list ap;
467   va_start(ap, fmt);
468   //int sz = self->buffer+4096 - self->eob;
469   int n = vsnprintf (self->buffer, 4096, fmt, ap);
470   va_end(ap);
471
472   write (self->conn->fd, self->buffer, n);
473
474   if (n>4095)
475     return 0;
476
477   return 1;
478 }
479
480
481
482
483 ///
484
485 struct rxpd_connection*
486 rxpd_connection_new (struct rxpd_base* base, int fd)
487 {
488   struct rxpd_connection* self;
489   self = rxpd_malloc (sizeof (struct rxpd_connection));
490
491   socklen_t addr_sz = sizeof (self->peer_addr); 
492   self->fd = accept (fd, (struct sockaddr*)&self->peer_addr, &addr_sz);
493   if (self->fd == -1)
494     abort ();
495
496   //static int yes = 1;
497   //if (setsockopt (self->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
498   //  abort ();
499
500   // TODO
501   rxpd_log (base, LOG_INFO, "incoming connection\n");
502
503
504   self->base = base;
505   self->file = NULL;
506   self->tmp_str = NULL;
507   llist_init (&self->tmp_list);
508
509   rxpd_buffer_init (&self->in, self);
510   rxpd_buffer_init (&self->out, self);
511   
512   event_set (&self->ev, self->fd, EV_READ, rxpd_connection_parse_cmd, self);
513   return self;
514 }
515
516 void
517 rxpd_connection_delete (struct rxpd_connection* self)
518 {
519   if (self)
520     {
521       event_del (&self->ev);
522       close (self->fd);
523       free (self->tmp_str);
524       LLIST_WHILE_HEAD (&self->tmp_list, n)
525         {
526           struct rxpd_rule* node = (struct rxpd_rule*)n;
527           rxpd_rule_delete (node);
528         }
529     }
530   free (self);
531 }
532
533 struct rxpd_connection*
534 rxpd_connection_schedule (struct rxpd_connection* self)
535 {
536   if (self)
537     {
538       event_add (&self->ev, NULL);
539     }
540   return self;
541 }
542
543 void
544 rxpd_connection_parse_cmd (int fd, short event, void* ptr)
545 {
546   (void) event;
547
548   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
549
550   char* line;
551   line = rxpd_buffer_readline (&self->in, 0);
552
553   if (!line)
554     {
555       rxpd_log (self->base, LOG_ERR, "no data\n");
556       rxpd_buffer_printf (&self->out, "#ERROR: no data\n");
557       close (fd);
558       return;
559     }
560
561   rxpd_log (self->base, LOG_DEBUG, "parse command '%s'\n", line);
562
563   static const struct cmd_table
564   {
565     enum rxpd_cmd_e nr;
566     const char* cmd;
567     size_t sz;
568   } cmds[] =
569     {
570 #define RXPD_CMD(cmd) {RXPD_CMD_##cmd, #cmd":", sizeof (#cmd)},
571       RXPD_COMMANDS
572 #undef RXPD_CMD
573       {0, NULL, 0}
574     };
575
576   const struct cmd_table* i;
577   for (i = cmds; i->cmd; ++i)
578     if (strncmp (line, i->cmd, i->sz) == 0)
579       break;
580   if (!i->cmd)
581     {
582       rxpd_log (self->base, LOG_ERR, "no command\n");
583       rxpd_buffer_printf (&self->out, "#ERROR: no command\n");
584       rxpd_connection_delete (self);
585       return;
586     }
587   // TODO policy check here
588
589   if (line[i->sz])
590     {
591       // rulename provided
592       self->file = (struct rxpd_file*) psplay_find (&self->base->files, &line[i->sz]);
593
594       if (!self->file)
595         {
596           self->file = rxpd_file_new (self->base, &line[i->sz]);
597           if (!self->file)
598             {
599               rxpd_log (self->base, LOG_ERR, "illeagal filename\n");
600               rxpd_buffer_printf (&self->out, "#ERROR: illegal filename\n");
601               rxpd_connection_delete (self);
602               return;
603             }
604         }
605     }
606
607   // dispatch
608   switch (i->nr)
609     {
610 #define RXPD_CMD(cmd)                                                           \
611 case RXPD_CMD_##cmd:                                                            \
612   event_set (&self->ev, self->fd, EV_READ, rxpd_connection_cmd_##cmd, self);    \
613   rxpd_connection_cmd_##cmd (fd, 0, ptr);                                       \
614   break;
615       RXPD_COMMANDS
616 #undef RXPD_CMD
617     }
618 }
619
620
621
622 void
623 rxpd_connection_cmd_CHECK (int fd, short event, void* ptr)
624 {
625   (void) fd;
626   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
627
628   if (event == EV_READ)
629     {
630       int again = -1;
631       char* line;
632       while ((line = rxpd_buffer_readline (&self->in, ++again)))
633         {
634           if (*line == '\0')
635             {
636               rxpd_buffer_printf (&self->out, "#OK:\n");
637             }
638           else
639             {
640               LLIST_FOREACH (&self->file->rules, n)
641                 {
642                   struct rxpd_rule* rule = (struct rxpd_rule*)n;
643                   if (rule->string[0] != '#')
644                     {
645                       if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
646                         {
647                           rxpd_buffer_printf (&self->out, "%s\n", rule->string);
648                           break;
649                         }
650                     }
651                 }
652             }
653         }
654     }
655   else if (!self->file)
656     {
657       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
658       rxpd_connection_delete (self);
659       return;
660     }
661
662   if (rxpd_buffer_state (&self->in) == RXPD_OK)
663     rxpd_connection_schedule (self);
664   else
665     {
666       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
667         rxpd_buffer_printf (&self->out, "#ERROR:\n");
668       rxpd_connection_delete (self);
669     }
670 }
671
672
673
674 static void
675 rxpd_connection_APPEND_PREPEND_helper (short event, void* ptr, int do_append)
676 {
677   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
678
679   if (event == EV_READ)
680     {
681       int again = -1;
682       char* line;
683
684       while ((line = rxpd_buffer_readline (&self->in, ++again)))
685         {
686           if (*line)
687             {
688               struct rxpd_rule* rule;
689               rule = rxpd_rule_new (line);
690               if (!rule)
691                 abort();
692
693               llist_insert_tail (&self->tmp_list, &rule->node);
694             }
695           else goto finish;     /* move along, look elsewhere! This goto is not harmful and saves some code. */
696         }
697     }
698   else if (!event && !self->file)
699     {
700       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
701       rxpd_connection_delete (self);
702       return;
703     }
704
705   if (rxpd_buffer_state (&self->in) == RXPD_OK)
706     rxpd_connection_schedule (self);
707   else
708     {
709       // TODO should also print error when any rule compilation failed, use tmp_str to save case?
710       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
711         rxpd_buffer_printf (&self->out, "#ERROR:\n");
712       else
713         {
714         finish:
715           rxpd_buffer_printf (&self->out, "#OK:\n");
716         }
717
718       if (do_append)
719         llist_insertlist_prev (&self->file->rules, &self->tmp_list);
720       else
721         llist_insertlist_next (&self->file->rules, &self->tmp_list);
722       rxpd_connection_delete (self);
723     }
724 }
725
726 void
727 rxpd_connection_cmd_APPEND (int fd, short event, void* ptr)
728 {
729   (void) fd;
730   rxpd_connection_APPEND_PREPEND_helper (event, ptr, 1);
731 }
732
733 void
734 rxpd_connection_cmd_PREPEND (int fd, short event, void* ptr)
735 {
736   (void) fd;
737   rxpd_connection_APPEND_PREPEND_helper (event, ptr, 0);
738 }
739
740
741 void
742 rxpd_connection_cmd_REMOVE (int fd, short event, void* ptr)
743 {
744   (void) fd;
745   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
746
747   if (event == EV_READ)
748     {
749       int again = -1;
750       char* line;
751       while ((line = rxpd_buffer_readline (&self->in, ++again)))
752         {
753           LLIST_FOREACH (&self->file->rules, n)
754             {
755               struct rxpd_rule* rule = (struct rxpd_rule*)n;
756               if (strcmp (rule->string, line) == 0)
757                 {
758                   LList tmp = llist_prev (n);
759                   rxpd_rule_delete (rule);
760                   n = tmp;
761                   rxpd_buffer_printf (&self->out, "#OK:\n");
762                   goto done;
763                 }
764             }
765           rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
766         done:
767           ;
768         }
769     }
770   else if (!self->file)
771     {
772       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
773       rxpd_connection_delete (self);
774       return;
775     }
776
777   if (rxpd_buffer_state (&self->in) == RXPD_OK)
778     rxpd_connection_schedule (self);
779   else
780     {
781       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
782         rxpd_buffer_printf (&self->out, "#ERROR:\n");
783       rxpd_connection_delete (self);
784     }
785 }
786
787
788 static int
789 rxpd_connection_do_REPLACE (struct rxpd_connection* self)
790 {
791   struct rxpd_rule* rule;
792
793   LLIST_FOREACH (&self->file->rules, n)
794     {
795       rule = (struct rxpd_rule*)n;
796       if (strcmp (rule->string, self->tmp_str) == 0)
797         goto found;
798     }
799   return 0;
800  found:
801   llist_insertlist_next (&rule->node, &self->tmp_list);
802   rxpd_rule_delete (rule);
803   free (self->tmp_str);
804   self->tmp_str = NULL;
805   return 1;
806 }
807
808 void
809 rxpd_connection_cmd_REPLACE (int fd, short event, void* ptr)
810 {
811   (void) fd;
812   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
813
814   if (event == EV_READ)
815     {
816       int again = -1;
817       char* line;
818       while ((line = rxpd_buffer_readline (&self->in, ++again)))
819         {
820           if (self->tmp_str)
821             {
822               if (*line)
823                 {
824                   struct rxpd_rule* rule;
825                   rule = rxpd_rule_new (line);
826                   if (!rule)
827                     abort();
828
829                   llist_insert_tail (&self->tmp_list, &rule->node);
830                 }
831               /* TODO handle empty lines? */
832             }
833           else
834             self->tmp_str = rxpd_strdup (line);
835         }
836     }
837   else if (!self->file)
838     {
839       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
840       rxpd_connection_delete (self);
841       return;
842     }
843
844   if (rxpd_buffer_state (&self->in) == RXPD_OK)
845     rxpd_connection_schedule (self);
846   else
847     {
848       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
849         rxpd_buffer_printf (&self->out, "#ERROR:\n");
850       else
851         {
852           if (rxpd_connection_do_REPLACE (self))
853             rxpd_buffer_printf (&self->out, "#OK:\n");
854           else
855             rxpd_buffer_printf (&self->out, "#ERROR: rule matching '%s'\n", self->tmp_str);
856         }
857
858       rxpd_connection_delete (self);
859     }
860 }
861
862 void
863 rxpd_connection_cmd_LOAD (int fd, short event, void* ptr)
864 {
865   (void) fd;
866   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
867
868   if (!event)
869     {
870       if (self->file)
871         {
872           if (rxpd_file_load (self->file))
873             {
874               rxpd_buffer_printf (&self->out, "#OK:\n");
875             }
876           else
877             {
878               rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
879             }
880         }
881       else
882         rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
883       rxpd_connection_delete (self);
884     }
885 }
886
887 void
888 rxpd_connection_cmd_SAVE (int fd, short event, void* ptr)
889 {
890   (void) fd;
891   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
892
893   if (!event)
894     {
895       if (self->file)
896         {
897           if (rxpd_file_save (self->file))
898             {
899               rxpd_buffer_printf (&self->out, "#OK:\n");
900             }
901           else
902             {
903               rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
904             }
905         }
906       else
907         rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
908       rxpd_connection_delete (self);
909     }
910 }
911
912 void
913 rxpd_connection_cmd_DUMP (int fd, short event, void* ptr)
914 {
915   (void) fd;
916   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
917
918   if (!event && !self->file)
919     {
920       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
921       rxpd_connection_delete (self);
922       return;
923     }
924
925   if (llist_is_empty (&self->file->rules))
926     rxpd_buffer_printf (&self->out, "#OK:\n");
927   else
928     {
929       LLIST_FOREACH (&self->file->rules, n)
930         {
931           struct rxpd_rule* rule = (struct rxpd_rule*)n;
932           rxpd_buffer_printf (&self->out, "%s\n", rule->string);
933         }
934     }
935
936   rxpd_connection_delete (self);
937 }
938
939
940 static psplay_delete_t
941 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
942 {
943   (void) level;
944   struct rxpd_file* file = (struct rxpd_file*) node;
945   struct rxpd_connection* conn = (struct rxpd_connection*) data;
946
947   if (which == PSPLAY_INORDER)
948     rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
949
950   return PSPLAY_CONT;
951 }
952
953
954 void
955 rxpd_connection_cmd_LIST (int fd, short event, void* ptr)
956 {
957   (void) fd;
958   (void) event;
959   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
960
961   if (psplay_isempty_root (&self->base->files))
962     rxpd_buffer_printf (&self->out, "#OK:\n");
963   else
964     psplay_walk (&self->base->files, NULL, walk_LIST, 0, ptr);
965
966   rxpd_connection_delete (self);
967 }
968
969 void
970 rxpd_connection_cmd_SHUTDOWN (int fd, short event, void* ptr)
971 {
972   (void) fd;
973   (void) event;
974   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
975   // destroy all sockets
976   LLIST_WHILE_HEAD (&self->base->sockets, n)
977     {
978       struct rxpd_socket* socket = (struct rxpd_socket*)n;
979       rxpd_socket_delete (socket);
980     }
981   rxpd_buffer_printf (&self->out, "#OK:\n");
982   rxpd_connection_delete (self);
983 }