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"); \
34 rxpd_connection_cmd_CHECK (struct rxpd_connection
* self
)
36 RXPD_FILENAME_REQUIRED
;
39 while ((line
= rxpd_buffer_readline (&self
->in
)))
43 rxpd_buffer_printf (&self
->out
, "#OK:\n");
47 // TODO implement RXPD_YIELD_EVERY
48 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RD
, FALSE
, NULL
);
49 LLIST_FOREACH (&self
->file
->rules
, n
)
51 struct rxpd_rule
* rule
= (struct rxpd_rule
*)n
;
52 if (rule
->string
[0] != '#')
54 if (regexec (&rule
->rx
, line
, 0, NULL
, 0) == 0)
56 if (rule
->atime
!= (time_t) -1)
59 rxpd_buffer_printf (&self
->out
, "%s\n", rule
->string
);
64 pth_rwlock_release (&self
->file
->lock
);
68 if (rxpd_buffer_state (&self
->in
) == RXPD_ERROR
)
69 rxpd_buffer_printf (&self
->out
, "#ERROR:\n");
75 rxpd_connection_APPEND_PREPEND_helper (struct rxpd_connection
* self
)
79 while ((line
= rxpd_buffer_readline (&self
->in
)))
81 if (*line
&& !RXPD_PREFIXCMP (line
, "#EXIT:"))
83 struct rxpd_rule
* rule
;
84 rule
= rxpd_rule_new (line
);
85 llist_insert_tail (&self
->tmp_list
, &rule
->node
);
88 break; /* exit at empty line, error or whatever */
91 if (rxpd_buffer_state (&self
->in
) == RXPD_ERROR
)
92 rxpd_buffer_printf (&self
->out
, "#ERROR:\n");
94 rxpd_buffer_printf (&self
->out
, "#OK:\n");
98 rxpd_connection_cmd_APPEND (struct rxpd_connection
* self
)
100 RXPD_FILENAME_REQUIRED
;
102 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
104 rxpd_connection_APPEND_PREPEND_helper (self
);
105 llist_insertlist_prev (&self
->file
->rules
, &self
->tmp_list
);
107 pth_rwlock_release (&self
->file
->lock
);
111 rxpd_connection_cmd_PREPEND (struct rxpd_connection
* self
)
113 RXPD_FILENAME_REQUIRED
;
115 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
117 rxpd_connection_APPEND_PREPEND_helper (self
);
118 llist_insertlist_next (&self
->file
->rules
, &self
->tmp_list
);
120 pth_rwlock_release (&self
->file
->lock
);
124 rxpd_connection_cmd_REMOVE (struct rxpd_connection
* self
)
126 RXPD_FILENAME_REQUIRED
;
129 while ((line
= rxpd_buffer_readline (&self
->in
)))
131 if (*line
&& !RXPD_PREFIXCMP (line
, "#EXIT:"))
133 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
134 LLIST_FOREACH (&self
->file
->rules
, n
)
136 struct rxpd_rule
* rule
= (struct rxpd_rule
*)n
;
137 if (strcmp (rule
->string
, line
) == 0)
139 LList tmp
= llist_prev (n
);
140 rxpd_rule_delete (rule
);
142 rxpd_buffer_printf (&self
->out
, "#OK:\n");
146 rxpd_buffer_printf (&self
->out
, "#ERROR: line not found\n");
148 pth_rwlock_release (&self
->file
->lock
);
151 break; /* exit at empty line, error or whatever */
154 if (rxpd_buffer_state (&self
->in
) == RXPD_ERROR
)
155 rxpd_buffer_printf (&self
->out
, "#ERROR:\n");
161 rxpd_connection_do_REPLACE (struct rxpd_connection
* self
)
163 struct rxpd_rule
* rule
;
165 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
167 LLIST_FOREACH (&self
->file
->rules
, n
)
169 rule
= (struct rxpd_rule
*)n
;
170 if (strcmp (rule
->string
, self
->tmp_str
) == 0)
173 pth_rwlock_release (&self
->file
->lock
);
177 llist_insertlist_next (&rule
->node
, &self
->tmp_list
);
178 rxpd_rule_delete (rule
);
179 pth_rwlock_release (&self
->file
->lock
);
181 free (self
->tmp_str
);
182 self
->tmp_str
= NULL
;
187 rxpd_connection_cmd_REPLACE (struct rxpd_connection
* self
)
189 RXPD_FILENAME_REQUIRED
;
191 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
194 while ((line
= rxpd_buffer_readline (&self
->in
)))
196 if (*line
&& !RXPD_PREFIXCMP (line
, "#EXIT:"))
200 struct rxpd_rule
* rule
;
201 rule
= rxpd_rule_new (line
);
203 llist_insert_tail (&self
->tmp_list
, &rule
->node
);
205 rxpd_buffer_printf (&self
->out
, "#ERROR: illegal rule '%s'\n", line
);
208 self
->tmp_str
= rxpd_strdup (line
);
211 break; /* exit at empty line, error or whatever */
214 pth_rwlock_release (&self
->file
->lock
);
216 if (rxpd_buffer_state (&self
->in
) == RXPD_ERROR
)
217 rxpd_buffer_printf (&self
->out
, "#ERROR:\n");
219 if (rxpd_connection_do_REPLACE (self
))
220 rxpd_buffer_printf (&self
->out
, "#OK:\n");
222 rxpd_buffer_printf (&self
->out
, "#ERROR: no rule matching '%s'\n", self
->tmp_str
);
226 rxpd_connection_cmd_LOAD (struct rxpd_connection
* self
)
228 RXPD_FILENAME_REQUIRED
;
230 if (rxpd_file_load (self
->file
))
231 rxpd_buffer_printf (&self
->out
, "#OK:\n");
233 rxpd_buffer_printf (&self
->out
, "#ERROR: loading file '%s'\n", (const char*)self
->file
->node
.key
);
237 rxpd_connection_cmd_SAVE (struct rxpd_connection
* self
)
239 RXPD_FILENAME_REQUIRED
;
241 if (rxpd_file_save (self
->file
))
242 rxpd_buffer_printf (&self
->out
, "#OK:\n");
244 rxpd_buffer_printf (&self
->out
, "#ERROR: saving file '%s'\n", (const char*)self
->file
->node
.key
);
249 rxpd_connection_cmd_DELETE (struct rxpd_connection
* self
)
251 RXPD_FILENAME_REQUIRED
;
253 rxpd_file_delete (self
->file
);
254 rxpd_buffer_printf (&self
->out
, "#OK:\n");
258 rxpd_connection_cmd_FETCH (struct rxpd_connection
* self
)
260 RXPD_FILENAME_REQUIRED
;
262 struct rxpd_base
* base
= self
->socket
->base
;
265 line
= rxpd_buffer_readline (&self
->in
);
269 char* list
= strrchr (line
, ':');
276 char* port
= strrchr (line
, ':');
283 struct addrinfo
* addrs
= NULL
;
287 aierr
= getaddrinfo (line
, port
, NULL
, &addrs
);
290 rxpd_buffer_printf (&self
->out
, "#ERROR: resolving '%s': %s\n", line
, gai_strerror (aierr
));
294 rxpd_log (base
, LOG_INFO
, "fetching list '%s' from '%s(%s)' at port '%s' to '%s'\n",
297 inet_ntoa (((struct sockaddr_in
*)addrs
->ai_addr
)->sin_addr
),
299 self
->file
->node
.key
);
301 /* establish connection */
302 int fd
= socket (addrs
->ai_family
, addrs
->ai_socktype
, addrs
->ai_protocol
);
303 if (fd
== -1 || connect (fd
, addrs
->ai_addr
, addrs
->ai_addrlen
))
305 freeaddrinfo (addrs
);
306 rxpd_buffer_printf (&self
->out
, "#ERROR: error connecting '%s': %s\n", line
, strerror (errno
));
310 freeaddrinfo (addrs
);
312 /* send DUMP command and fetch data */
313 struct rxpd_buffer in
;
314 struct rxpd_buffer out
;
315 rxpd_buffer_init (&in
, fd
);
316 rxpd_buffer_init (&out
, fd
);
318 rxpd_buffer_printf (&out
, "DUMP:%s\n", list
);
321 while ((line
= rxpd_buffer_readline (&in
)))
323 if (*line
&& !RXPD_PREFIXCMP (line
, "#EXIT:"))
325 struct rxpd_rule
* rule
;
326 rule
= rxpd_rule_new (line
);
328 rxpd_die ("rule creation failed on '%s'\n", line
);
330 llist_insert_tail (&self
->tmp_list
, &rule
->node
);
333 rxpd_log (base
, LOG_INFO
, "purge remote side error '%s'\n", line
);
335 rxpd_log (base
, LOG_DEBUG
, "finished dump\n");
339 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
340 rxpd_file_rules_delete (self
->file
);
341 llist_insertlist_next (&self
->file
->rules
, &self
->tmp_list
);
342 pth_rwlock_release (&self
->file
->lock
);
344 rxpd_buffer_printf (&self
->out
, "#OK:\n");
349 rxpd_buffer_printf (&self
->out
, "#ERROR: syntax error\n");
354 rxpd_connection_cmd_DUMP (struct rxpd_connection
* self
)
356 RXPD_FILENAME_REQUIRED
;
357 rxpd_file_dump (self
->file
, &self
->out
);
361 static psplay_delete_t
362 walk_LIST (PSplay node
, const enum psplay_order_e which
, int level
, void* data
)
365 struct rxpd_file
* file
= (struct rxpd_file
*) node
;
366 struct rxpd_connection
* conn
= (struct rxpd_connection
*) data
;
368 if (which
== PSPLAY_INORDER
)
369 rxpd_buffer_printf (&conn
->out
, "%s\n", file
->node
.key
);
376 rxpd_connection_cmd_LIST (struct rxpd_connection
* self
)
378 struct rxpd_base
* base
= self
->socket
->base
;
380 if (psplay_isempty_root (&base
->files
))
381 rxpd_buffer_printf (&self
->out
, "#OK:\n");
383 psplay_walk (&base
->files
, NULL
, walk_LIST
, 0, self
);
387 rxpd_connection_cmd_SHUTDOWN (struct rxpd_connection
* self
)
389 struct rxpd_base
* base
= self
->socket
->base
;
391 LLIST_FOREACH (&base
->sockets
, n
)
393 struct rxpd_socket
* socket
= (struct rxpd_socket
*)n
;
394 rxpd_socket_cancel (socket
);
397 rxpd_buffer_printf (&self
->out
, "#OK:\n");
402 rxpd_connection_cmd_VERSION (struct rxpd_connection
* self
)
404 rxpd_buffer_printf (&self
->out
, PACKAGE_STRING
"\n#\n"
406 "# 2007, Christian Thaeter <ct@pipapo.org>\n#\n"
407 "# This is free software. You may redistribute copies of it under the terms of\n"
408 "# the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
409 "# There is NO WARRANTY, to the extent permitted by law.\n#\n"
410 "# http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
414 rxpd_connection_cmd_HELP (struct rxpd_connection
* self
)
416 rxpd_buffer_printf (&self
->out
, "# Available commands:\n#\n");
417 #define RXPD_CMD(cmd, help) rxpd_buffer_printf (&self->out, "# %s %s.\n", #cmd, help);
420 rxpd_buffer_printf (&self
->out
,
422 "# general syntax is: 'COMMAND:listname\\n..data..'\n"
423 "# see http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
427 rxpd_connection_cmd_EXPIRE (struct rxpd_connection
* self
)
429 RXPD_FILENAME_REQUIRED
;
431 struct rxpd_base
* base
= self
->socket
->base
;
433 char* line
= rxpd_buffer_readline (&self
->in
);
436 // TODO strtol, error handling
437 time_t since
= time (NULL
) - atoi (line
);
438 rxpd_log (base
, LOG_INFO
, "expire all entries in '%s' since %ld\n",
439 (const char*) self
->file
->node
.key
, since
);
441 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
443 LLIST_FOREACH (&self
->file
->rules
, n
)
445 struct rxpd_rule
* rule
= (struct rxpd_rule
*)n
;
446 if (rule
->atime
!= -1 && rule
->atime
< since
)
448 rxpd_log (base
, LOG_DEBUG
, "expiring %ld:%s\n", rule
->atime
, rule
->string
);
449 rxpd_buffer_printf (&self
->out
, "#OK: expiring '%s'\n", rule
->string
);
450 if (!rxpd_rule_comment (rule
, "EXPIRED"))
451 rxpd_die ("couldn't comment rule out");
455 pth_rwlock_release (&self
->file
->lock
);
458 rxpd_buffer_printf (&self
->out
, "#ERROR: no age given\n");
462 rxpd_connection_cmd_UPDATE (struct rxpd_connection
* self
)
464 RXPD_FILENAME_REQUIRED
;
466 struct rxpd_base
* base
= self
->socket
->base
;
468 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
471 while ((line
= rxpd_buffer_readline (&self
->in
)))
473 /* for each line which is a listname */
474 struct rxpd_file
* source
;
475 source
= (struct rxpd_file
*) psplay_find (&base
->files
, line
);
478 pth_rwlock_acquire (&source
->lock
, PTH_RWLOCK_RD
, FALSE
, NULL
);
480 /* find first line which is in source and self->file, this is our start */
481 LList source_itr
= NULL
;
482 LList file_itr
= NULL
;
483 LLIST_FOREACH (&source
->rules
, n
)
485 struct rxpd_rule
* rule_n
= (struct rxpd_rule
*)n
;
486 LLIST_FOREACH (&self
->file
->rules
, m
)
488 struct rxpd_rule
* rule_m
= (struct rxpd_rule
*)m
;
489 if (strcmp (rule_n
->string
, rule_m
->string
) == 0)
491 source_itr
= &rule_n
->node
;
492 file_itr
= &rule_m
->node
;
493 rxpd_log (base
, LOG_DEBUG
, "starting at '%s'\n", rule_n
->string
);
501 rxpd_log (base
, LOG_DEBUG
, "no common line found between '%s' and '%s'\n",
502 (const char*)self
->file
->node
.key
, line
);
503 pth_rwlock_release (&source
->lock
);
507 /* Now we go for each rule in source which has a atime find the matching rule in self->file and update its atime */
508 /* This algorihm assumes that lists stay ordered (insertions/deletions are supported, but not reordering) */
509 /* advantage is that the algorithm has far less complexity */
510 LLIST_FORRANGE (source_itr
, &source
->rules
, n
)
512 struct rxpd_rule
* rule_n
= (struct rxpd_rule
*)n
;
513 if (rule_n
->atime
!= (time_t)-1)
515 LLIST_FORRANGE (file_itr
, &self
->file
->rules
, m
)
517 struct rxpd_rule
* rule_m
= (struct rxpd_rule
*)m
;
518 if (strcmp (rule_n
->string
, rule_m
->string
) == 0)
521 if (rule_m
->atime
!= (time_t)-1 && rule_m
->atime
< rule_n
->atime
)
523 rxpd_log (base
, LOG_DEBUG
,
524 "updating atime of rule '%s' from %ld to %ld\n",
529 rule_m
->atime
= rule_n
->atime
;
532 file_itr
= llist_next (m
);
539 pth_rwlock_release (&source
->lock
);
542 rxpd_buffer_printf (&self
->out
, "#ERROR: unknown file '%s'\n", line
);
545 pth_rwlock_release (&self
->file
->lock
);
547 rxpd_buffer_printf (&self
->out
, "#OK:\n");
552 rxpd_connection_cmd_MERGE (struct rxpd_connection
* self
)
554 RXPD_FILENAME_REQUIRED
;
556 struct rxpd_base
* base
= self
->socket
->base
;
558 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
561 while ((line
= rxpd_buffer_readline (&self
->in
)))
563 /* for each line which is a listname */
564 struct rxpd_file
* source
;
565 source
= (struct rxpd_file
*) psplay_find (&base
->files
, line
);
568 pth_rwlock_acquire (&source
->lock
, PTH_RWLOCK_RD
, FALSE
, NULL
);
570 LList source_start
= llist_head (&source
->rules
);
571 LList file_start
= llist_head (&self
->file
->rules
);
572 LList source_itr
= NULL
;
573 LList file_itr
= NULL
;
575 /* find next line which is in source and self->file */
576 while (source_itr
!= &source
->rules
)
578 LLIST_FORRANGE (source_start
, &source
->rules
, n
)
580 LLIST_FORRANGE (file_start
, &self
->file
->rules
, m
)
582 struct rxpd_rule
* rule_n
= (struct rxpd_rule
*)n
;
583 struct rxpd_rule
* rule_m
= (struct rxpd_rule
*)m
;
584 if (strcmp (rule_n
->string
, rule_m
->string
) == 0)
586 source_itr
= &rule_n
->node
;
587 file_itr
= &rule_m
->node
;
592 /* not found, place the itr's at the end of their lists */
593 source_itr
= &source
->rules
;
594 file_itr
= &self
->file
->rules
;
596 /* copy all rules from source_start to source_itr before file_itr */
598 LLIST_FORRANGE (source_start
, source_itr
, n
)
600 struct rxpd_rule
* rule
= rxpd_rule_copy ((struct rxpd_rule
*)n
);
602 rxpd_die ("rule copy failed\n");
604 llist_insert_prev (file_itr
, &rule
->node
);
607 /*iterate for mismatch*/
608 file_start
= llist_next (file_itr
);
609 source_start
= llist_next (source_itr
);
612 pth_rwlock_release (&source
->lock
);
615 rxpd_buffer_printf (&self
->out
, "#ERROR: unknown file '%s'\n", line
);
618 pth_rwlock_release (&self
->file
->lock
);
620 rxpd_buffer_printf (&self
->out
, "#OK:\n");
624 rxpd_connection_cmd_FILTER (struct rxpd_connection
* self
)
626 RXPD_FILENAME_REQUIRED
;
628 /* Filter processes the target list by checking its rules against other lists */
629 /* The outcome of this check is used to decide how to process with a rule */
630 /* DELETE: delete rule from list */
631 /* ACTIVATE: try to reactivate a rule which was commented out */
632 /* any other: comment the rule out by using 'any other' as hint*/
634 struct rxpd_base
* base
= self
->socket
->base
;
636 pth_rwlock_acquire (&self
->file
->lock
, PTH_RWLOCK_RW
, FALSE
, NULL
);
639 while ((line
= rxpd_buffer_readline (&self
->in
)))
641 if (*line
&& !RXPD_PREFIXCMP (line
, "#EXIT:"))
644 /* for each line which is a listname */
645 struct rxpd_file
* source
;
646 source
= (struct rxpd_file
*) psplay_find (&base
->files
, line
);
649 pth_rwlock_acquire (&source
->lock
, PTH_RWLOCK_RD
, FALSE
, NULL
);
651 LLIST_FOREACH (&self
->file
->rules
, n
)
653 LLIST_FOREACH (&source
->rules
, m
)
655 struct rxpd_rule
* rule_n
= (struct rxpd_rule
*)n
;
656 struct rxpd_rule
* rule_m
= (struct rxpd_rule
*)m
;
657 if (rule_m
->string
[0] != '#')
659 if (regexec (&rule_m
->rx
, rule_n
->string
, 0, NULL
, 0) == 0)
661 if (rule_m
->atime
!= (time_t) -1)
662 time (&rule_m
->atime
);
664 if (RXPD_PREFIXCMP (rule_m
->string
, "DELETE:"))
667 rxpd_log (base
, LOG_DEBUG
, "deleting rule '%s' from list '%s'\n",
668 rule_n
->string
, (const char*) self
->file
->node
.key
);
669 rxpd_rule_delete (rule_n
);
671 else if (RXPD_PREFIXCMP (rule_m
->string
, "ACTIVATE:"))
673 if (!rxpd_rule_activate (rule_n
))
674 rxpd_buffer_printf (&self
->out
, "#ERROR: cant activate rule '%s'\n", rule_n
->string
);
676 rxpd_log (base
, LOG_DEBUG
, "activated rule '%s' in list '%s'\n",
677 rule_n
->string
, (const char*) self
->file
->node
.key
);
681 char* c
= strndupa (rule_m
->string
, strchr (rule_m
->string
, ':') - rule_m
->string
);
682 rxpd_log (base
, LOG_DEBUG
, "commenting rule '%s' as '%s', in list '%s'\n",
683 rule_n
->string
, c
, (const char*) self
->file
->node
.key
);
685 rxpd_rule_comment (rule_n
, c
);
691 pth_rwlock_release (&source
->lock
);
694 rxpd_buffer_printf (&self
->out
, "#ERROR: unknown file '%s'\n", line
);
697 break; /* exit at empty line, error or whatever */
700 pth_rwlock_release (&self
->file
->lock
);
702 rxpd_buffer_printf (&self
->out
, "#OK:\n");
707 rxpd_connection_cmd_ (struct rxpd_connection* self)
709 RXPD_FILENAME_REQUIRED;
710 //struct rxpd_base* base = self->socket->base;
711 rxpd_buffer_printf (&self->out, "#ERROR: Unimplemented\n");