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