2 rxpd_connection_cmd.c - regex policy daemon
5 2007, Christian Thaeter <ct@pipapo.org>
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.
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.
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.
24 #define RXPD_FILENAME_REQUIRED \
28 rxpd_buffer_printf (&self->out, "#ERROR: missing filename\n"); \
35 rxpd_connection_do_CHECK (struct rxpd_connection
* self
, char* line
, struct rxpd_file
* file
)
39 // TODO implement RXPD_YIELD_EVERY
40 pth_rwlock_acquire (&file
->lock
, PTH_RWLOCK_RD
, FALSE
, NULL
);
41 LLIST_FOREACH (&file
->rules
, n
)
43 struct rxpd_rule
* rule
= (struct rxpd_rule
*)n
;
44 if (rule
->string
[0] != '#')
46 if (regexec (&rule
->rx
, line
, 0, NULL
, 0) == 0)
48 if (rule
->atime
!= (time_t) -1)
55 struct rxpd_base
* base
= self
->socket
->base
;
58 (size_t)(strchr (&rule
->string
[1], ':') - &rule
->string
[1]);
61 alloca (strlen (file
->node
.key
) + sublen
+ 1);
64 strcat (subname
, file
->node
.key
);
65 strncat (subname
, &rule
->string
[1], sublen
);
67 struct rxpd_file
* subfile
= (struct rxpd_file
*) psplay_find (&base
->files
, subname
);
70 rxpd_log (base
, LOG_DEBUG
, "going to sublist '%s'\n", subname
);
71 ret
= rxpd_connection_do_CHECK (self
, line
, subfile
);
74 rxpd_log (base
, LOG_ERR
, "sublist '%s' does not exist\n", subname
);
82 pth_rwlock_release (&file
->lock
);
88 rxpd_connection_cmd_CHECK (struct rxpd_connection
* self
)
90 RXPD_FILENAME_REQUIRED
;
93 while ((line
= rxpd_buffer_readline (&self
->in
)))
97 rxpd_buffer_printf (&self
->out
, "#OK:\n");
101 char* match
= rxpd_connection_do_CHECK (self
, line
, self
->file
);
104 rxpd_buffer_printf (&self
->out
, "%s\n", match
);
108 if (rxpd_buffer_state (&self
->in
) == RXPD_ERROR
)
109 rxpd_buffer_printf (&self
->out
, "#ERROR:\n");
114 rxpd_connection_APPEND_PREPEND_helper (struct rxpd_connection
* self
)
118 while ((line
= rxpd_buffer_readline (&self
->in
)))
120 if (*line
&& !RXPD_PREFIXCMP (line
, "!EXIT"))
122 struct rxpd_rule
* rule
;
124 rule
= rxpd_rule_new (line
, self
->socket
->base
);
125 llist_insert_tail (&self
->tmp_list
, &rule
->node
);
128 break; /* exit at empty line, error or whatever */
131 if (rxpd_buffer_state (&self
->in
) == RXPD_ERROR
)
132 rxpd_buffer_printf (&self
->out
, "#ERROR:\n");
134 rxpd_buffer_printf (&self
->out
, "#OK:\n");
138 rxpd_connection_cmd_APPEND (struct rxpd_connection
* self
)
140 RXPD_FILENAME_REQUIRED
;
142 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
144 rxpd_connection_APPEND_PREPEND_helper (self
);
145 llist_insertlist_prev (&self
->file
->rules
, &self
->tmp_list
);
147 pth_rwlock_release (&self
->file
->lock
);
151 rxpd_connection_cmd_PREPEND (struct rxpd_connection
* self
)
153 RXPD_FILENAME_REQUIRED
;
155 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
157 rxpd_connection_APPEND_PREPEND_helper (self
);
158 llist_insertlist_next (&self
->file
->rules
, &self
->tmp_list
);
160 pth_rwlock_release (&self
->file
->lock
);
164 rxpd_connection_cmd_REMOVE (struct rxpd_connection
* self
)
166 RXPD_FILENAME_REQUIRED
;
169 while ((line
= rxpd_buffer_readline (&self
->in
)))
171 if (*line
&& !RXPD_PREFIXCMP (line
, "!EXIT"))
173 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
174 LLIST_FOREACH (&self
->file
->rules
, n
)
176 struct rxpd_rule
* rule
= (struct rxpd_rule
*)n
;
177 if (strcmp (rule
->string
, line
) == 0)
179 LList tmp
= llist_prev (n
);
180 rxpd_rule_delete (rule
);
182 rxpd_buffer_printf (&self
->out
, "#OK:\n");
186 rxpd_buffer_printf (&self
->out
, "#ERROR: line not found\n");
188 pth_rwlock_release (&self
->file
->lock
);
191 break; /* exit at empty line, error or whatever */
194 if (rxpd_buffer_state (&self
->in
) == RXPD_ERROR
)
195 rxpd_buffer_printf (&self
->out
, "#ERROR:\n");
201 rxpd_connection_do_REPLACE (struct rxpd_connection
* self
)
203 struct rxpd_rule
* rule
;
205 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
207 LLIST_FOREACH (&self
->file
->rules
, n
)
209 rule
= (struct rxpd_rule
*)n
;
210 if (strcmp (rule
->string
, self
->tmp_str
) == 0)
213 pth_rwlock_release (&self
->file
->lock
);
217 llist_insertlist_next (&rule
->node
, &self
->tmp_list
);
218 rxpd_rule_delete (rule
);
219 pth_rwlock_release (&self
->file
->lock
);
221 free (self
->tmp_str
);
222 self
->tmp_str
= NULL
;
227 rxpd_connection_cmd_REPLACE (struct rxpd_connection
* self
)
229 RXPD_FILENAME_REQUIRED
;
231 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
234 while ((line
= rxpd_buffer_readline (&self
->in
)))
236 if (*line
&& !RXPD_PREFIXCMP (line
, "!EXIT"))
240 struct rxpd_rule
* rule
;
241 rule
= rxpd_rule_new (line
, self
->socket
->base
);
243 llist_insert_tail (&self
->tmp_list
, &rule
->node
);
245 rxpd_buffer_printf (&self
->out
, "#ERROR: illegal rule '%s'\n", line
);
248 self
->tmp_str
= rxpd_strdup (line
);
251 break; /* exit at empty line, error or whatever */
254 pth_rwlock_release (&self
->file
->lock
);
256 if (rxpd_buffer_state (&self
->in
) == RXPD_ERROR
)
257 rxpd_buffer_printf (&self
->out
, "#ERROR:\n");
259 if (rxpd_connection_do_REPLACE (self
))
260 rxpd_buffer_printf (&self
->out
, "#OK:\n");
262 rxpd_buffer_printf (&self
->out
, "#ERROR: no rule matching '%s'\n", self
->tmp_str
);
266 rxpd_connection_cmd_LOAD (struct rxpd_connection
* self
)
268 RXPD_FILENAME_REQUIRED
;
270 if (rxpd_file_load (self
->file
))
271 rxpd_buffer_printf (&self
->out
, "#OK:\n");
273 rxpd_buffer_printf (&self
->out
, "#ERROR: loading file '%s'\n", (const char*)self
->file
->node
.key
);
277 rxpd_connection_cmd_SAVE (struct rxpd_connection
* self
)
279 RXPD_FILENAME_REQUIRED
;
281 if (rxpd_file_save (self
->file
, 1))
282 rxpd_buffer_printf (&self
->out
, "#OK:\n");
284 rxpd_buffer_printf (&self
->out
, "#ERROR: saving file '%s'\n", (const char*)self
->file
->node
.key
);
289 rxpd_connection_cmd_CLEAR (struct rxpd_connection
* self
)
291 RXPD_FILENAME_REQUIRED
;
293 rxpd_file_rules_delete (self
->file
);
294 rxpd_buffer_printf (&self
->out
, "#OK:\n");
298 rxpd_connection_cmd_DELETE (struct rxpd_connection
* self
)
300 RXPD_FILENAME_REQUIRED
;
302 rxpd_file_delete (self
->file
);
303 rxpd_buffer_printf (&self
->out
, "#OK:\n");
307 rxpd_connection_cmd_FETCH (struct rxpd_connection
* self
)
309 RXPD_FILENAME_REQUIRED
;
311 struct rxpd_base
* base
= self
->socket
->base
;
314 line
= rxpd_buffer_readline (&self
->in
);
318 char* list
= strrchr (line
, ':');
325 char* port
= strrchr (line
, ':');
332 struct addrinfo
* addrs
= NULL
;
336 aierr
= getaddrinfo (line
, port
, NULL
, &addrs
);
339 rxpd_buffer_printf (&self
->out
, "#ERROR: resolving '%s': %s\n", line
, gai_strerror (aierr
));
343 rxpd_log (base
, LOG_INFO
, "fetching list '%s' from '%s(%s)' at port '%s' to '%s'\n",
346 inet_ntoa (((struct sockaddr_in
*)addrs
->ai_addr
)->sin_addr
),
348 self
->file
->node
.key
);
350 /* establish connection */
351 int fd
= socket (addrs
->ai_family
, addrs
->ai_socktype
, addrs
->ai_protocol
);
352 if (fd
== -1 || connect (fd
, addrs
->ai_addr
, addrs
->ai_addrlen
))
354 freeaddrinfo (addrs
);
355 rxpd_buffer_printf (&self
->out
, "#ERROR: error connecting '%s': %s\n", line
, strerror (errno
));
359 freeaddrinfo (addrs
);
361 /* send DUMP command and fetch data */
362 struct rxpd_buffer in
;
363 struct rxpd_buffer out
;
364 rxpd_buffer_init (&in
, fd
);
365 rxpd_buffer_init (&out
, fd
);
367 rxpd_buffer_printf (&out
, "DUMP:%s\n", list
);
370 while ((line
= rxpd_buffer_readline (&in
)))
372 if (*line
&& !RXPD_PREFIXCMP (line
, "!EXIT"))
374 struct rxpd_rule
* rule
;
375 rule
= rxpd_rule_new (line
, base
);
377 rxpd_die ("rule creation failed on '%s'\n", line
);
379 llist_insert_tail (&self
->tmp_list
, &rule
->node
);
382 rxpd_log (base
, LOG_INFO
, "purge remote side error '%s'\n", line
);
384 rxpd_log (base
, LOG_DEBUG
, "finished dump\n");
388 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
389 rxpd_file_rules_delete (self
->file
);
390 llist_insertlist_next (&self
->file
->rules
, &self
->tmp_list
);
391 pth_rwlock_release (&self
->file
->lock
);
393 rxpd_buffer_printf (&self
->out
, "#OK:\n");
398 rxpd_buffer_printf (&self
->out
, "#ERROR: syntax error\n");
403 rxpd_connection_cmd_DUMP (struct rxpd_connection
* self
)
405 RXPD_FILENAME_REQUIRED
;
406 rxpd_file_dump (self
->file
, &self
->out
);
410 static psplay_delete_t
411 walk_LIST (PSplay node
, const enum psplay_order_e which
, int level
, void* data
)
414 struct rxpd_file
* file
= (struct rxpd_file
*) node
;
415 struct rxpd_connection
* conn
= (struct rxpd_connection
*) data
;
417 if (which
== PSPLAY_INORDER
)
418 rxpd_buffer_printf (&conn
->out
, "%s\n", file
->node
.key
);
425 rxpd_connection_cmd_LIST (struct rxpd_connection
* self
)
427 struct rxpd_base
* base
= self
->socket
->base
;
429 if (psplay_isempty_root (&base
->files
))
430 rxpd_buffer_printf (&self
->out
, "#OK:\n");
432 psplay_walk (&base
->files
, NULL
, walk_LIST
, 0, self
);
436 rxpd_connection_cmd_SHUTDOWN (struct rxpd_connection
* self
)
438 /* not pth_raise, we don't want to schedule here! */
440 rxpd_buffer_printf (&self
->out
, "#OK:\n");
445 rxpd_connection_cmd_VERSION (struct rxpd_connection
* self
)
447 rxpd_buffer_printf (&self
->out
, PACKAGE_STRING
"\n#\n"
449 "# 2007, Christian Thaeter <ct@pipapo.org>\n#\n"
450 "# This is free software. You may redistribute copies of it under the terms of\n"
451 "# the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
452 "# There is NO WARRANTY, to the extent permitted by law.\n#\n"
453 "# http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
457 rxpd_connection_cmd_HELP (struct rxpd_connection
* self
)
459 rxpd_buffer_printf (&self
->out
, "# Available commands:\n#\n");
460 #define RXPD_CMD(cmd, help) rxpd_buffer_printf (&self->out, "# %s %s.\n", #cmd, help);
463 rxpd_buffer_printf (&self
->out
,
465 "# general syntax is: 'COMMAND:listname\\n..data..'\n"
466 "# see http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
470 rxpd_connection_cmd_EXPIRE (struct rxpd_connection
* self
)
472 RXPD_FILENAME_REQUIRED
;
474 struct rxpd_base
* base
= self
->socket
->base
;
476 char* line
= rxpd_buffer_readline (&self
->in
);
479 // TODO strtol, error handling
480 time_t since
= time (NULL
) - atoi (line
);
481 rxpd_log (base
, LOG_INFO
, "expire all entries in '%s' since %ld\n",
482 (const char*) self
->file
->node
.key
, since
);
484 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
486 LLIST_FOREACH (&self
->file
->rules
, n
)
488 struct rxpd_rule
* rule
= (struct rxpd_rule
*)n
;
489 if (rule
->atime
!= -1 && rule
->atime
< since
)
491 rxpd_log (base
, LOG_DEBUG
, "expiring %ld:%s\n", rule
->atime
, rule
->string
);
492 rxpd_buffer_printf (&self
->out
, "#OK: expiring '%s'\n", rule
->string
);
493 if (!rxpd_rule_comment (rule
, "EXPIRED"))
494 rxpd_die ("couldn't comment rule out");
498 pth_rwlock_release (&self
->file
->lock
);
501 rxpd_buffer_printf (&self
->out
, "#ERROR: no age given\n");
505 rxpd_connection_cmd_UPDATE (struct rxpd_connection
* self
)
507 RXPD_FILENAME_REQUIRED
;
509 struct rxpd_base
* base
= self
->socket
->base
;
511 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
514 while ((line
= rxpd_buffer_readline (&self
->in
)))
516 /* for each line which is a listname */
517 struct rxpd_file
* source
;
518 source
= (struct rxpd_file
*) psplay_find (&base
->files
, line
);
521 pth_rwlock_acquire (&source
->lock
, PTH_RWLOCK_RD
, FALSE
, NULL
);
523 /* find first line which is in source and self->file, this is our start */
524 LList source_itr
= NULL
;
525 LList file_itr
= NULL
;
526 LLIST_FOREACH (&source
->rules
, n
)
528 struct rxpd_rule
* rule_n
= (struct rxpd_rule
*)n
;
529 LLIST_FOREACH (&self
->file
->rules
, m
)
531 struct rxpd_rule
* rule_m
= (struct rxpd_rule
*)m
;
532 if (strcmp (rule_n
->string
, rule_m
->string
) == 0)
534 source_itr
= &rule_n
->node
;
535 file_itr
= &rule_m
->node
;
536 rxpd_log (base
, LOG_DEBUG
, "starting at '%s'\n", rule_n
->string
);
544 rxpd_log (base
, LOG_DEBUG
, "no common line found between '%s' and '%s'\n",
545 (const char*)self
->file
->node
.key
, line
);
546 pth_rwlock_release (&source
->lock
);
550 /* Now we go for each rule in source which has a atime find the matching rule in self->file and update its atime */
551 /* This algorihm assumes that lists stay ordered (insertions/deletions are supported, but not reordering) */
552 /* advantage is that the algorithm has far less complexity */
553 LLIST_FORRANGE (source_itr
, &source
->rules
, n
)
555 struct rxpd_rule
* rule_n
= (struct rxpd_rule
*)n
;
556 if (rule_n
->atime
!= (time_t)-1)
558 LLIST_FORRANGE (file_itr
, &self
->file
->rules
, m
)
560 struct rxpd_rule
* rule_m
= (struct rxpd_rule
*)m
;
561 if (strcmp (rule_n
->string
, rule_m
->string
) == 0)
564 if (rule_m
->atime
!= (time_t)-1 && rule_m
->atime
< rule_n
->atime
)
566 rxpd_log (base
, LOG_DEBUG
,
567 "updating atime of rule '%s' from %ld to %ld\n",
572 rule_m
->atime
= rule_n
->atime
;
575 file_itr
= llist_next (m
);
582 pth_rwlock_release (&source
->lock
);
585 rxpd_buffer_printf (&self
->out
, "#ERROR: unknown file '%s'\n", line
);
588 pth_rwlock_release (&self
->file
->lock
);
590 rxpd_buffer_printf (&self
->out
, "#OK:\n");
595 rxpd_connection_cmd_MERGE (struct rxpd_connection
* self
)
597 RXPD_FILENAME_REQUIRED
;
599 struct rxpd_base
* base
= self
->socket
->base
;
601 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
604 while ((line
= rxpd_buffer_readline (&self
->in
)))
606 /* for each line which is a listname */
607 struct rxpd_file
* source
;
608 source
= (struct rxpd_file
*) psplay_find (&base
->files
, line
);
611 pth_rwlock_acquire (&source
->lock
, PTH_RWLOCK_RD
, FALSE
, NULL
);
613 LList source_start
= llist_head (&source
->rules
);
614 LList file_start
= llist_head (&self
->file
->rules
);
615 LList source_itr
= NULL
;
616 LList file_itr
= NULL
;
618 /* find next line which is in source and self->file */
619 while (source_itr
!= &source
->rules
)
621 LLIST_FORRANGE (source_start
, &source
->rules
, n
)
623 LLIST_FORRANGE (file_start
, &self
->file
->rules
, m
)
625 struct rxpd_rule
* rule_n
= (struct rxpd_rule
*)n
;
626 struct rxpd_rule
* rule_m
= (struct rxpd_rule
*)m
;
627 if (strcmp (rule_n
->string
, rule_m
->string
) == 0)
629 source_itr
= &rule_n
->node
;
630 file_itr
= &rule_m
->node
;
635 /* not found, place the itr's at the end of their lists */
636 source_itr
= &source
->rules
;
637 file_itr
= &self
->file
->rules
;
639 /* copy all rules from source_start to source_itr before file_itr */
641 LLIST_FORRANGE (source_start
, source_itr
, n
)
643 struct rxpd_rule
* rule
= rxpd_rule_copy ((struct rxpd_rule
*)n
);
645 rxpd_die ("rule copy failed\n");
647 llist_insert_prev (file_itr
, &rule
->node
);
650 /*iterate for mismatch*/
651 file_start
= llist_next (file_itr
);
652 source_start
= llist_next (source_itr
);
655 pth_rwlock_release (&source
->lock
);
658 rxpd_buffer_printf (&self
->out
, "#ERROR: unknown file '%s'\n", line
);
661 pth_rwlock_release (&self
->file
->lock
);
663 rxpd_buffer_printf (&self
->out
, "#OK:\n");
667 rxpd_connection_cmd_FILTER (struct rxpd_connection
* self
)
669 RXPD_FILENAME_REQUIRED
;
671 /* Filter processes the target list by checking its rules against other lists */
672 /* The outcome of this check is used to decide how to process with a rule */
673 /* DELETE: delete rule from list */
674 /* ACTIVATE: try to reactivate a rule which was commented out */
675 /* any other: comment the rule out by using 'any other' as hint*/
677 struct rxpd_base
* base
= self
->socket
->base
;
679 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
682 while ((line
= rxpd_buffer_readline (&self
->in
)))
684 if (*line
&& !RXPD_PREFIXCMP (line
, "!EXIT"))
687 /* for each line which is a listname */
688 struct rxpd_file
* source
;
689 source
= (struct rxpd_file
*) psplay_find (&base
->files
, line
);
692 pth_rwlock_acquire (&source
->lock
, PTH_RWLOCK_RD
, FALSE
, NULL
);
694 LLIST_FOREACH (&self
->file
->rules
, n
)
696 LLIST_FOREACH (&source
->rules
, m
)
698 struct rxpd_rule
* rule_n
= (struct rxpd_rule
*)n
;
699 struct rxpd_rule
* rule_m
= (struct rxpd_rule
*)m
;
700 if (rule_m
->string
[0] != '#')
702 if (regexec (&rule_m
->rx
, rule_n
->string
, 0, NULL
, 0) == 0)
704 if (rule_m
->atime
!= (time_t) -1)
705 time (&rule_m
->atime
);
707 if (RXPD_PREFIXCMP (rule_m
->string
, "DELETE:"))
710 rxpd_log (base
, LOG_DEBUG
, "deleting rule '%s' from list '%s'\n",
711 rule_n
->string
, (const char*) self
->file
->node
.key
);
712 rxpd_rule_delete (rule_n
);
714 else if (RXPD_PREFIXCMP (rule_m
->string
, "ACTIVATE:"))
716 if (!rxpd_rule_activate (rule_n
))
717 rxpd_buffer_printf (&self
->out
, "#ERROR: cant activate rule '%s'\n", rule_n
->string
);
719 rxpd_log (base
, LOG_DEBUG
, "activated rule '%s' in list '%s'\n",
720 rule_n
->string
, (const char*) self
->file
->node
.key
);
724 char* c
= strndupa (rule_m
->string
, strchr (rule_m
->string
, ':') - rule_m
->string
);
725 rxpd_log (base
, LOG_DEBUG
, "commenting rule '%s' as '%s', in list '%s'\n",
726 rule_n
->string
, c
, (const char*) self
->file
->node
.key
);
728 rxpd_rule_comment (rule_n
, c
);
734 pth_rwlock_release (&source
->lock
);
737 rxpd_buffer_printf (&self
->out
, "#ERROR: unknown file '%s'\n", line
);
740 break; /* exit at empty line, error or whatever */
743 pth_rwlock_release (&self
->file
->lock
);
745 rxpd_buffer_printf (&self
->out
, "#OK:\n");
750 rxpd_connection_cmd_ (struct rxpd_connection* self)
752 RXPD_FILENAME_REQUIRED;
753 //struct rxpd_base* base = self->socket->base;
754 rxpd_buffer_printf (&self->out, "#ERROR: Unimplemented\n");