]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/bsd_out.c
015283cdc45a125d8b1c3e50cfb4581ea6de54ec
[apple/syslog.git] / syslogd.tproj / bsd_out.c
1 /*
2 * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <sys/uio.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <pthread.h>
38 #include <notify.h>
39 #include "daemon.h"
40
41 #define MY_ID "bsd_out"
42
43 #define _PATH_WALL "/usr/bin/wall"
44 #define ASL_KEY_FACILITY "Facility"
45 #define FACILITY_KERNEL "kern"
46 #define _PATH_CONSOLE "/dev/console"
47
48 #define DST_TYPE_NONE 0
49 #define DST_TYPE_FILE 1
50 #define DST_TYPE_CONS 2
51 #define DST_TYPE_SOCK 3
52 #define DST_TYPE_WALL 4
53 #define DST_TYPE_NOTE 5
54
55 #define CLOSE_ON_IDLE_SEC 60
56
57 static asl_msg_t *query = NULL;
58 static int reset = RESET_NONE;
59 static pthread_mutex_t reset_lock = PTHREAD_MUTEX_INITIALIZER;
60
61 struct config_rule
62 {
63 uint32_t count;
64 char *dst;
65 int fd;
66 int type;
67 struct sockaddr *addr;
68 char **facility;
69 uint32_t *fac_prefix_len;
70 int *pri;
71 uint32_t last_hash;
72 uint32_t last_count;
73 time_t last_time;
74 char *last_msg;
75 TAILQ_ENTRY(config_rule) entries;
76 };
77
78 static TAILQ_HEAD(cr, config_rule) bsd_out_rule;
79
80 extern uint32_t asl_core_string_hash(const char *s, uint32_t inlen);
81
82 int bsd_out_close();
83 int bsd_out_network_reset(void);
84 static int _parse_config_file(const char *);
85
86 static void
87 _do_reset(void)
88 {
89 pthread_mutex_lock(&reset_lock);
90 if (reset == RESET_NONE)
91 {
92 pthread_mutex_unlock(&reset_lock);
93 return;
94 }
95
96 if (reset == RESET_CONFIG)
97 {
98 bsd_out_close();
99 _parse_config_file(_PATH_SYSLOG_CONF);
100 }
101 else if (reset == RESET_NETWORK)
102 {
103 bsd_out_network_reset();
104 }
105
106 reset = RESET_NONE;
107
108 pthread_mutex_unlock(&reset_lock);
109 }
110
111 static int
112 _level_for_name(const char *name)
113 {
114 if (name == NULL) return -1;
115
116 if (!strcasecmp(name, "emerg")) return ASL_LEVEL_EMERG;
117 if (!strcasecmp(name, "panic")) return ASL_LEVEL_EMERG;
118 if (!strcasecmp(name, "alert")) return ASL_LEVEL_ALERT;
119 if (!strcasecmp(name, "crit")) return ASL_LEVEL_CRIT;
120 if (!strcasecmp(name, "err")) return ASL_LEVEL_ERR;
121 if (!strcasecmp(name, "error")) return ASL_LEVEL_ERR;
122 if (!strcasecmp(name, "warn")) return ASL_LEVEL_WARNING;
123 if (!strcasecmp(name, "warning")) return ASL_LEVEL_WARNING;
124 if (!strcasecmp(name, "notice")) return ASL_LEVEL_NOTICE;
125 if (!strcasecmp(name, "info")) return ASL_LEVEL_INFO;
126 if (!strcasecmp(name, "debug")) return ASL_LEVEL_DEBUG;
127 if (!strcmp(name, "*")) return ASL_LEVEL_DEBUG;
128
129 /* special case */
130 if (!strcasecmp(name, "none")) return -2;
131
132 return -1;
133 }
134
135 static int
136 _syslog_dst_open(struct config_rule *r)
137 {
138 int i;
139 char *node, *serv;
140 struct addrinfo hints, *gai, *ai;
141
142 if (r == NULL) return -1;
143 if (r->fd != -1) return 0;
144
145 if (r->dst[0] == '/')
146 {
147 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
148 if (r->fd < 0)
149 {
150 asldebug("%s: open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
151 return -1;
152 }
153
154 r->type = DST_TYPE_FILE;
155 if (!strcmp(r->dst, _PATH_CONSOLE)) r->type = DST_TYPE_CONS;
156
157 return 0;
158 }
159
160 if (r->dst[0] == '!')
161 {
162 r->type = DST_TYPE_NOTE;
163 r->fd = -1;
164 return 0;
165 }
166
167 if (r->dst[0] == '@')
168 {
169 node = strdup(r->dst + 1);
170 if (node == NULL) return -1;
171
172 serv = NULL;
173 serv = strrchr(node, ':');
174 if (serv != NULL) *serv++ = '\0';
175 else serv = "syslog";
176
177 memset(&hints, 0, sizeof(hints));
178 hints.ai_family = PF_UNSPEC;
179 hints.ai_socktype = SOCK_DGRAM;
180 i = getaddrinfo(node, serv, &hints, &gai);
181 free(node);
182 if (i != 0)
183 {
184 asldebug("%s: getaddrinfo failed for node %s service %s: (%s)\n", MY_ID, node, serv, gai_strerror(i));
185 return -1;
186 }
187
188 for (ai = gai; ai != NULL; ai = ai->ai_next)
189 {
190 r->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
191 if (r->fd < 0) continue;
192
193 r->addr = (struct sockaddr *)malloc(ai->ai_addrlen);
194 if (r->addr == NULL) return -1;
195
196 memcpy(r->addr, ai->ai_addr, ai->ai_addrlen);
197
198 break;
199 }
200
201 freeaddrinfo(gai);
202
203 if (r->fd < 0)
204 {
205 asldebug("%s: connection failed for %s\n", MY_ID, (r->dst) + 1);
206 free(r->addr);
207 r->addr = NULL;
208 return -1;
209 }
210
211 if (fcntl(r->fd, F_SETFL, O_NONBLOCK) < 0)
212 {
213 close(r->fd);
214 r->fd = -1;
215 asldebug("%s: couldn't set O_NONBLOCK for fd %d: %s\n", MY_ID, r->fd, strerror(errno));
216 free(r->addr);
217 r->addr = NULL;
218 return -1;
219 }
220
221 r->type = DST_TYPE_SOCK;
222 return 0;
223
224 }
225
226 if (strcmp(r->dst, "*") == 0)
227 {
228 r->type = DST_TYPE_WALL;
229 r->fd = -1;
230 return 0;
231 }
232
233 /* Can't deal with dst! */
234 asldebug("%s: unsupported / unknown output name: %s\n", MY_ID, r->dst);
235 return -1;
236 }
237
238 static void
239 _syslog_dst_close(struct config_rule *r)
240 {
241 if (r == NULL) return;
242
243 if (r->addr != NULL)
244 {
245 free(r->addr);
246 r->addr = NULL;
247 }
248
249 switch (r->type)
250 {
251 case DST_TYPE_FILE:
252 case DST_TYPE_CONS:
253 {
254 if (r->fd >= 0) close(r->fd);
255 r->fd = -1;
256 break;
257 }
258
259 case DST_TYPE_SOCK:
260 {
261 if (r->fd >= 0) close(r->fd);
262 r->fd = -1;
263 break;
264 }
265
266 case DST_TYPE_NONE:
267 case DST_TYPE_WALL:
268 case DST_TYPE_NOTE:
269 default:
270 {
271 /* do nothing */
272 return;
273 }
274 }
275 }
276
277 static char *
278 _clean_facility_name(char *s)
279 {
280 uint32_t len;
281 char *p, *out;
282
283 if (s == NULL) return NULL;
284 len = strlen(s);
285 if (len == 0) return NULL;
286
287 p = s;
288
289 if ((*s == '\'') || (*s == '"'))
290 {
291 len--;
292 p++;
293 if (p[len - 1] == *s) len --;
294 }
295
296 out = calloc(1, len + 1);
297 if (out == NULL) return NULL;
298
299 memcpy(out, p, len);
300 return out;
301 }
302
303 static int
304 _parse_line(char *s)
305 {
306 char **semi, **comma, *star;
307 int i, j, n, lasts, lastc, pri;
308 struct config_rule *out;
309
310 if (s == NULL) return -1;
311 while ((*s == ' ') || (*s == '\t')) s++;
312 if (*s == '#') return -1;
313
314 semi = explode(s, "; \t");
315
316 if (semi == NULL) return -1;
317 out = (struct config_rule *)calloc(1, sizeof(struct config_rule));
318 if (out == NULL) return -1;
319 out->fd = -1;
320
321 n = 0;
322 lasts = -1;
323 for (i = 0; semi[i] != NULL; i++)
324 {
325 if (semi[i][0] == '\0') continue;
326 n++;
327 lasts = i;
328 }
329
330 out->dst = strdup(semi[lasts]);
331 if (out->dst == NULL) return -1;
332
333 for (i = 0; i < lasts; i++)
334 {
335 if (semi[i][0] == '\0') continue;
336 comma = explode(semi[i], ",.");
337 lastc = -1;
338 for (j = 0; comma[j] != NULL; j++)
339 {
340 if (comma[j][0] == '\0') continue;
341 lastc = j;
342 }
343
344 for (j = 0; j < lastc; j++)
345 {
346 if (comma[j][0] == '\0') continue;
347 pri = _level_for_name(comma[lastc]);
348 if (pri == -1) continue;
349
350 if (out->count == 0)
351 {
352 out->facility = (char **)calloc(1, sizeof(char *));
353 out->fac_prefix_len = (uint32_t *)calloc(1, sizeof(uint32_t));
354 out->pri = (int *)calloc(1, sizeof(int));
355 }
356 else
357 {
358 out->facility = (char **)reallocf(out->facility, (out->count + 1) * sizeof(char *));
359 out->fac_prefix_len = (uint32_t *)reallocf(out->fac_prefix_len, (out->count + 1) * sizeof(uint32_t));
360 out->pri = (int *)reallocf(out->pri, (out->count + 1) * sizeof(int));
361 }
362
363 if (out->facility == NULL) return -1;
364 if (out->fac_prefix_len == NULL) return -1;
365 if (out->pri == NULL) return -1;
366
367 out->facility[out->count] = _clean_facility_name(comma[j]);
368 if (out->facility[out->count] == NULL) return -1;
369
370 out->fac_prefix_len[out->count] = 0;
371 star = strchr(out->facility[out->count], '*');
372 if (star != NULL) out->fac_prefix_len[out->count] = (uint32_t)(star - out->facility[out->count]);
373
374 out->pri[out->count] = pri;
375 out->count++;
376 }
377
378 freeList(comma);
379 }
380
381 freeList(semi);
382
383 TAILQ_INSERT_TAIL(&bsd_out_rule, out, entries);
384
385 return 0;
386 }
387
388 static int
389 _syslog_send_repeat_msg(struct config_rule *r)
390 {
391 char vt[32], *msg;
392 time_t tick;
393 int len, status;
394
395 if (r == NULL) return -1;
396 if (r->type != DST_TYPE_FILE) return 0;
397 if (r->last_count == 0) return 0;
398
399 tick = time(NULL);
400 memset(vt, 0, sizeof(vt));
401 ctime_r(&tick, vt);
402 vt[19] = '\0';
403
404 msg = NULL;
405 asprintf(&msg, "%s: --- last message repeated %u time%s ---\n", vt + 4, r->last_count, (r->last_count == 1) ? "" : "s");
406 if (msg == NULL) return -1;
407
408 len = strlen(msg);
409 status = write(r->fd, msg, len);
410 if ((status < 0) || (status < len))
411 {
412 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst, strerror(errno));
413
414 /* Try re-opening the file (once) and write again */
415 close(r->fd);
416 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
417 if (r->fd < 0)
418 {
419 asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
420 free(msg);
421 return -1;
422 }
423
424 status = write(r->fd, msg, len);
425 if ((status < 0) || (status < len))
426 {
427 asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
428 free(msg);
429 return -1;
430 }
431 }
432
433 free(msg);
434 return 0;
435 }
436
437 static int
438 _syslog_send(aslmsg msg, struct config_rule *r, char **out, char **fwd, time_t now)
439 {
440 char *sf, *outmsg;
441 const char *vlevel, *vfacility;
442 size_t outlen;
443 int pf, fc, status, is_dup, do_write;
444 uint32_t msg_hash, n;
445
446 if (out == NULL) return -1;
447 if (fwd == NULL) return -1;
448 if (r == NULL) return -1;
449
450 _syslog_dst_open(r);
451
452 if (r->type == DST_TYPE_NOTE)
453 {
454 notify_post(r->dst+1);
455 return 0;
456 }
457
458 msg_hash = 0;
459 outmsg = NULL;
460
461 /* Build output string if it hasn't been built by a previous rule-match */
462 if (*out == NULL)
463 {
464 *out = asl_format_message((asl_msg_t *)msg, ASL_MSG_FMT_BSD, ASL_TIME_FMT_LCL, ASL_ENCODE_SAFE, &n);
465 if (*out == NULL) return -1;
466 }
467
468 /* check if message is a duplicate of the last message, and inside the dup time window */
469 is_dup = 0;
470 if ((global.bsd_max_dup_time > 0) && (*out != NULL) && (r->last_msg != NULL))
471 {
472 msg_hash = asl_core_string_hash(*out + 16, strlen(*out + 16));
473 if ((r->last_hash == msg_hash) && (!strcmp(r->last_msg, *out + 16)))
474 {
475 if ((now - r->last_time) < global.bsd_max_dup_time) is_dup = 1;
476 }
477 }
478
479 if ((*fwd == NULL) && (r->type == DST_TYPE_SOCK))
480 {
481 pf = 7;
482 vlevel = asl_get(msg, ASL_KEY_LEVEL);
483 if (vlevel != NULL) pf = atoi(vlevel);
484
485 fc = asl_syslog_faciliy_name_to_num(asl_get(msg, ASL_KEY_FACILITY));
486 if (fc > 0) pf |= fc;
487
488 sf = NULL;
489 asprintf(&sf, "<%d>%s", pf, *out);
490 if (sf == NULL) return -1;
491
492 *fwd = sf;
493 }
494
495 outlen = 0;
496 if (r->type == DST_TYPE_SOCK) outlen = strlen(*fwd);
497 else outlen = strlen(*out);
498
499 if ((r->type == DST_TYPE_FILE) || (r->type == DST_TYPE_CONS))
500 {
501 /*
502 * If current message is NOT a duplicate and r->last_count > 0
503 * we need to write a "last message was repeated N times" log entry
504 */
505 if ((r->type == DST_TYPE_FILE) && (is_dup == 0) && (r->last_count > 0)) _syslog_send_repeat_msg(r);
506
507 do_write = 1;
508
509 /*
510 * Special case for kernel messages.
511 * Don't write kernel messages to /dev/console.
512 * The kernel printf routine already sends them to /dev/console
513 * so writing them here would cause duplicates.
514 */
515 vfacility = asl_get(msg, ASL_KEY_FACILITY);
516 if ((vfacility != NULL) && (!strcmp(vfacility, FACILITY_KERNEL)) && (r->type == DST_TYPE_CONS)) do_write = 0;
517 if ((do_write == 1) && (r->type == DST_TYPE_FILE) && (is_dup == 1)) do_write = 0;
518
519 if (do_write == 0) status = outlen;
520 else status = write(r->fd, *out, outlen);
521
522 if ((status < 0) || (status < outlen))
523 {
524 asldebug("%s: error writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
525
526 /* Try re-opening the file (once) and write again */
527 close(r->fd);
528 r->fd = open(r->dst, O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0644);
529 if (r->fd < 0)
530 {
531 asldebug("%s: re-open failed for file: %s (%s)\n", MY_ID, r->dst, strerror(errno));
532 return -1;
533 }
534
535 status = write(r->fd, *out, outlen);
536 if ((status < 0) || (status < outlen))
537 {
538 asldebug("%s: error re-writing message (%s): %s\n", MY_ID, r->dst, strerror(errno));
539 }
540 }
541 }
542 else if ((r->type == DST_TYPE_SOCK) && (r->addr != NULL))
543 {
544 status = sendto(r->fd, *fwd, outlen, 0, r->addr, r->addr->sa_len);
545 if (status < 0) asldebug("%s: error sending message (%s): %s\n", MY_ID, r->dst, strerror(errno));
546 }
547 else if (r->type == DST_TYPE_WALL)
548 {
549 #ifndef CONFIG_IPHONE
550 FILE *pw = popen(_PATH_WALL, "w");
551 if (pw < 0)
552 {
553 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
554 return -1;
555 }
556
557 fprintf(pw, "%s", *out);
558 pclose(pw);
559 #endif
560 }
561
562 if (is_dup == 1)
563 {
564 r->last_count++;
565 }
566 else
567 {
568 free(r->last_msg);
569 r->last_msg = NULL;
570
571 if (*out != NULL) r->last_msg = strdup(*out + 16);
572
573 r->last_hash = msg_hash;
574 r->last_count = 0;
575 r->last_time = now;
576 }
577
578 return 0;
579 }
580
581 static int
582 _syslog_rule_match(aslmsg msg, struct config_rule *r)
583 {
584 uint32_t i, test, f, pri;
585 const char *val;
586
587 if (msg == NULL) return 0;
588 if (r == NULL) return 0;
589 if (r->count == 0) return 0;
590
591 test = 0;
592
593 for (i = 0; i < r->count; i++)
594 {
595 if (r->pri[i] == -1) continue;
596
597 if ((test == 1) && (r->pri[i] >= 0)) continue;
598 if ((test == 0) && (r->pri[i] == -2)) continue;
599
600 f = 0;
601 val = asl_get(msg, ASL_KEY_FACILITY);
602
603 if (strcmp(r->facility[i], "*") == 0)
604 {
605 f = 1;
606 }
607 else if ((r->fac_prefix_len[i] > 0) && (strncasecmp(r->facility[i], val, r->fac_prefix_len[i]) == 0))
608 {
609 f = 1;
610 }
611 else if ((val != NULL) && (strcasecmp(r->facility[i], val) == 0))
612 {
613 f = 1;
614 }
615
616 if (f == 0) continue;
617
618 /* Turn off matching facility with priority "none" */
619 if (r->pri[i] == -2)
620 {
621 test = 0;
622 continue;
623 }
624
625 val = asl_get(msg, ASL_KEY_LEVEL);
626 if (val == NULL) continue;
627
628 pri = atoi(val);
629 if (pri < 0) continue;
630
631 if (pri <= r->pri[i]) test = 1;
632 }
633
634 return test;
635 }
636
637 int
638 bsd_out_sendmsg(aslmsg msg, const char *outid)
639 {
640 struct config_rule *r;
641 char *out, *fwd;
642 time_t tick;
643 uint64_t delta;
644
645 if (reset != RESET_NONE) _do_reset();
646
647 if (msg == NULL) return -1;
648
649 out = NULL;
650 fwd = NULL;
651
652 tick = time(NULL);
653 global.bsd_flush_time = 0;
654
655 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
656 {
657 if (_syslog_rule_match(msg, r) == 1) _syslog_send(msg, r, &out, &fwd, tick);
658 if ((r->type == DST_TYPE_FILE) && (r->last_count > 0))
659 {
660 delta = tick - r->last_time;
661 if (delta < global.bsd_max_dup_time)
662 {
663 delta = global.bsd_max_dup_time - delta;
664 if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
665 else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
666 }
667 }
668 }
669
670 free(out);
671 free(fwd);
672
673 return 0;
674 }
675
676 void
677 bsd_close_idle_files(time_t now)
678 {
679 struct config_rule *r;
680 uint64_t delta;
681
682 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
683 {
684 /* only applies to files */
685 if (r->type != DST_TYPE_FILE) continue;
686
687 /*
688 * If the last message repeat count is non-zero, a bsd_flush_duplicates()
689 * call will occur within 30 seconds. Don't bother closing the file.
690 */
691 if (r->last_count > 0) continue;
692
693 delta = now - r->last_time;
694 if (delta > CLOSE_ON_IDLE_SEC) _syslog_dst_close(r);
695 }
696 }
697
698 void
699 bsd_flush_duplicates(time_t now)
700 {
701 struct config_rule *r;
702 uint64_t delta;
703
704 global.bsd_flush_time = 0;
705
706 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
707 {
708 if (r->type != DST_TYPE_FILE) continue;
709
710 if (r->last_count > 0)
711 {
712 delta = now - r->last_time;
713 if (delta < global.bsd_max_dup_time)
714 {
715 delta = global.bsd_max_dup_time - delta;
716 if (global.bsd_flush_time == 0) global.bsd_flush_time = delta;
717 else if (delta < global.bsd_flush_time) global.bsd_flush_time = delta;
718 }
719 else
720 {
721 _syslog_dst_open(r);
722 _syslog_send_repeat_msg(r);
723
724 r->last_count = 0;
725 }
726 }
727 }
728 }
729
730 static int
731 _parse_config_file(const char *confname)
732 {
733 FILE *cf;
734 char *line;
735
736 cf = fopen(confname, "r");
737 if (cf == NULL) return 1;
738
739 while (NULL != (line = get_line_from_file(cf)))
740 {
741 _parse_line(line);
742 free(line);
743 }
744
745 fclose(cf);
746
747 return 0;
748 }
749
750 int
751 bsd_out_init(void)
752 {
753 asldebug("%s: init\n", MY_ID);
754
755 TAILQ_INIT(&bsd_out_rule);
756
757 query = asl_msg_new(ASL_TYPE_QUERY);
758 aslevent_addmatch(query, MY_ID);
759 aslevent_addoutput(bsd_out_sendmsg, MY_ID);
760
761 _parse_config_file(_PATH_SYSLOG_CONF);
762 return 0;
763 }
764
765 int
766 bsd_out_reset(void)
767 {
768 reset = global.reset;
769 return 0;
770 }
771
772 int
773 bsd_out_close(void)
774 {
775 struct config_rule *r, *n;
776 int i;
777
778 n = NULL;
779 for (r = bsd_out_rule.tqh_first; r != NULL; r = n)
780 {
781 n = r->entries.tqe_next;
782
783 free(r->dst);
784 free(r->addr);
785 free(r->last_msg);
786 free(r->fac_prefix_len);
787 free(r->pri);
788
789 if (r->fd >= 0) close(r->fd);
790
791 if (r->facility != NULL)
792 {
793 for (i = 0; i < r->count; i++)
794 free(r->facility[i]);
795
796 free(r->facility);
797 }
798
799 TAILQ_REMOVE(&bsd_out_rule, r, entries);
800 free(r);
801 }
802
803 return 0;
804 }
805
806 int
807 bsd_out_network_reset(void)
808 {
809 struct config_rule *r;
810
811 for (r = bsd_out_rule.tqh_first; r != NULL; r = r->entries.tqe_next)
812 {
813 if (r->type == DST_TYPE_SOCK)
814 {
815 close(r->fd);
816 r->fd = -1;
817 if (r->addr != NULL)
818 {
819 free(r->addr);
820 r->addr = NULL;
821 }
822 }
823 }
824
825 return 0;
826 }