additionally log status messages to syslog
[pve-firewall.git] / src / pvefw-logger.c
1 /*
2
3   Copyright (C) 2014 Proxmox Server Solutions GmbH
4
5   This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
6
7   This program is free software: you can redistribute it and/or modify
8   it under the terms of the GNU Affero General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (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 Affero General Public License for more details.
16
17   You should have received a copy of the GNU Affero General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20   Author: Dietmar Maurer <dietmar@proxmox.com>
21
22 */
23
24 #define _GNU_SOURCE
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <signal.h>
32 #include <sys/types.h> 
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <arpa/inet.h>
37 #include <sys/socket.h>
38 #include <sys/file.h>
39 #include <linux/netlink.h>
40 #include <libnfnetlink/libnfnetlink.h>
41 #include <libnetfilter_log/libnetfilter_log.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_icmp.h>
44 #include <netinet/udp.h>
45 #include <netinet/tcp.h>
46 #include <netinet/if_ether.h>
47 #include <syslog.h>
48
49 #include <glib.h>
50 #include <glib-unix.h>
51
52 static struct nflog_handle *logh = NULL;
53 static struct nlif_handle *nlifh = NULL;
54
55 /*
56   
57 LOG FORMAT:
58
59 Special care was taken to allow fast parsing (and filer messages for a singl VM).
60
61 <VMID> <LOGLEVEL> <CHAIN> <TIME> <TIMEZONE> <MSG> 
62
63 Example:
64
65 117 6 tap117i0-IN 14/Mar/2014:12:47:07 +0100 policy REJECT: IN=vmbr1 ...
66   
67 */
68
69 #define LOGFILE "/var/log/pve-firewall.log"
70
71 #define LOCKFILE "/var/lock/pvefw-logger.lck"
72 #define PIDFILE "/var/run/pvefw-logger.pid"
73
74 #define LQ_LEN 512
75 #define LE_MAX (512 - 16) // try to fit into 512 bytes
76
77 #define MAX_CHAIN_LEN 28
78
79 struct log_entry { 
80     guint32 len; // max LE_MAX chars
81     char buf[LE_MAX];
82 };
83
84 int outfd = -1;
85
86 gboolean terminate_threads = FALSE;
87
88 static gboolean write_pidfile(pid_t pid)
89 {
90     gboolean res;
91
92     char *strpid = g_strdup_printf("%d\n", pid);
93     res = g_file_set_contents(PIDFILE, strpid, strlen(strpid), NULL);
94     g_free(strpid);
95
96     return res;
97 }
98
99 static GAsyncQueue *queue;
100
101 ssize_t 
102 safe_write(int fd, char *buf, size_t count)
103 {
104   ssize_t n;
105
106   do {
107     n = write(fd, buf, count);
108   } while (n < 0 && errno == EINTR);
109
110   return n;
111 }
112
113 static gpointer
114 log_writer_thread(gpointer data) 
115 {
116     while (1) {
117         struct log_entry *le = (struct log_entry *)g_async_queue_timeout_pop(queue, 250000);
118         if (le == NULL) {
119             if (terminate_threads) {
120                 return NULL;
121             }
122             continue;
123         }
124        
125         int res = safe_write(outfd, le->buf, le->len);
126  
127         g_free(le);
128
129         if (res < 0) {
130             syslog(3, "writing log failed, stopping daemon - %s", strerror (errno));
131             return NULL;
132         }
133     }
134
135     return NULL;
136 }
137
138 static int skipped_logs = 0;
139
140 static void log_status_message(guint loglevel, const char *fmt, ...);
141  
142 static void
143 queue_log_entry(struct log_entry *le)
144 {
145     gint len = g_async_queue_length(queue);
146
147     if (skipped_logs > 0) {
148         if (len >= (LQ_LEN - 1)) {
149             skipped_logs++;
150         } else {
151             int skip_tmp = skipped_logs; 
152             skipped_logs = 0; // clear before calling log_status_message()
153             log_status_message(3, "skipped %d log entries (queue full)", skip_tmp);
154             g_async_queue_push(queue, le);
155         }
156     } else {
157         if (len >= LQ_LEN) {
158             skipped_logs++;
159         } else {
160             g_async_queue_push(queue, le);
161         }
162     }
163 }
164
165
166 #define LEPRINTF(format, ...) { if (le->len < LE_MAX) le->len += snprintf(le->buf + le->len, LE_MAX - le->len, format, ##__VA_ARGS__); }
167 #define LEPRINTTIME(sec) { time_t tmp_sec = sec; if (le->len < (LE_MAX - 30)) le->len += strftime(le->buf + le->len, LE_MAX - le->len, "%d/%b/%Y:%H:%M:%S %z ", localtime(&tmp_sec)); }
168
169 static void 
170 log_status_message(guint loglevel, const char *fmt, ...) 
171 {
172     va_list ap;
173     va_start(ap, fmt);
174     
175     if (loglevel > 7 ) loglevel = 7; // syslog defines level 0-7
176
177     struct log_entry *le = g_new0(struct log_entry, 1);
178
179     LEPRINTF("0 %d - ", loglevel);
180
181     LEPRINTTIME(time(NULL));
182
183     le->len += vsnprintf(le->buf + le->len, LE_MAX - le->len, fmt, ap);
184
185     LEPRINTF("\n");
186
187     queue_log_entry(le);
188
189     // also log to syslog
190
191     vsyslog(loglevel, fmt, ap);
192 }
193
194 static int 
195 print_tcp(struct log_entry *le, struct tcphdr *h, int payload_len)
196 {
197     LEPRINTF("PROTO=TCP ");
198
199     if (payload_len < sizeof(struct tcphdr)) {
200         LEPRINTF("LEN=%d ", payload_len);
201         LEPRINTF("INVALID=LEN ");
202         return -1;
203     }
204
205     LEPRINTF("SPT=%u DPT=%u ", ntohs(h->source), ntohs(h->dest));
206     LEPRINTF("SEQ=%u ACK=%u ", ntohl(h->seq), ntohl(h->ack_seq));
207     LEPRINTF("WINDOW=%u ", ntohs(h->window));
208
209     if (h->urg) LEPRINTF("URG ");
210     if (h->ack) LEPRINTF("ACK ");
211     if (h->psh) LEPRINTF("PSH ");
212     if (h->rst) LEPRINTF("RST ");
213     if (h->syn) LEPRINTF("SYN ");
214     if (h->fin) LEPRINTF("FIN ");
215
216     if (h->urg) LEPRINTF("URGP=%u ",ntohs(h->urg_ptr));
217
218     return 0;
219 }
220
221 static int 
222 print_udp(struct log_entry *le, struct udphdr *h, int payload_len)
223 {
224     LEPRINTF("PROTO=UDP ");
225
226     if (payload_len < sizeof(struct udphdr)) {
227         LEPRINTF("LEN=%d ", payload_len);
228         LEPRINTF("INVALID=LEN ");
229         return -1;
230     }
231
232     LEPRINTF("SPT=%u DPT=%u LEN=%u", ntohs(h->source), ntohs(h->dest), ntohs(h->len));
233
234     return 0;
235 }
236
237 static int 
238 print_icmp(struct log_entry *le, struct icmphdr *h, int payload_len)
239 {
240     char tmp[INET_ADDRSTRLEN];
241     u_int32_t gateway;
242
243     LEPRINTF("PROTO=ICMP ");
244
245     if (payload_len < sizeof(struct icmphdr)) {
246         LEPRINTF("LEN=%d ", payload_len);
247         LEPRINTF("INVALID=LEN ");
248         return -1;
249     }
250
251     LEPRINTF("TYPE=%u CODE=%u ", h->type, h->code);
252
253     switch (h->type) {
254     case ICMP_ECHO:
255     case ICMP_ECHOREPLY:
256         LEPRINTF("ID=%u SEQ=%u ", ntohs(h->un.echo.id), ntohs(h->un.echo.sequence));
257         break;
258     case ICMP_PARAMETERPROB:
259         LEPRINTF("PARAMETER=%u ", ntohl(h->un.gateway) >> 24);
260         break;
261     case ICMP_REDIRECT:
262         gateway = ntohl(h->un.gateway);
263         inet_ntop(AF_INET, &gateway, tmp, sizeof(tmp));                
264         LEPRINTF("GATEWAY=%s ", tmp);
265         break;
266     case ICMP_DEST_UNREACH:
267         if (h->code == ICMP_FRAG_NEEDED) {
268             LEPRINTF("MTU=%u ", ntohs(h->un.frag.mtu));
269         }
270         break;
271     }
272
273     return 0;
274 }
275
276 /* Section 3.1.  SCTP Common Header Format */
277 typedef struct sctphdr {
278         __be16 source;
279         __be16 dest;
280         __be32 vtag;
281         __be32 checksum;
282 } __attribute__((packed)) sctp_sctphdr_t;
283
284 static int 
285 print_sctp(struct log_entry *le, struct sctphdr *h, int payload_len)
286 {
287     LEPRINTF("PROTO=SCTP ");
288
289     if (payload_len < sizeof(struct sctphdr)) {
290         LEPRINTF("LEN=%d ", payload_len);
291         LEPRINTF("INVALID=LEN ");
292         return -1;
293     }
294
295     LEPRINTF("SPT=%u DPT=%u ", ntohs(h->source), ntohs(h->dest));
296
297     return 0;
298 }
299
300 static int 
301 print_iphdr(struct log_entry *le, char * payload, int payload_len)
302 {
303     if (payload_len < sizeof(struct iphdr)) {
304        LEPRINTF("LEN=%d ", payload_len);
305        LEPRINTF("INVALID=LEN ");
306        return -1;
307     }
308
309     struct iphdr *h = (struct iphdr *)payload;
310  
311     if (payload_len <= (u_int32_t)(h->ihl * 4)) {
312         LEPRINTF("INVALID=IHL ");
313         return -1;
314     }
315
316     char tmp[INET_ADDRSTRLEN];
317    
318     inet_ntop(AF_INET, &h->saddr, tmp, sizeof(tmp));                
319     LEPRINTF("SRC=%s ", tmp);
320     inet_ntop(AF_INET, &h->daddr, tmp, sizeof(tmp));                
321     LEPRINTF("DST=%s ", tmp);
322
323     LEPRINTF("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
324              ntohs(h->tot_len),  h->tos & IPTOS_TOS_MASK,
325              h->tos & IPTOS_PREC_MASK, h->ttl, ntohs(h->id));
326                 
327     short ip_off = ntohs(h->frag_off);
328     if (ip_off & IP_OFFMASK) 
329         LEPRINTF("FRAG=%u ", ip_off & IP_OFFMASK);
330
331     if (ip_off & IP_DF) LEPRINTF("DF ");
332     if (ip_off & IP_MF) LEPRINTF("MF ");
333
334     void *nexthdr = (u_int32_t *)h + h->ihl;
335     payload_len -= h->ihl * 4;
336
337     switch (h->protocol) {
338     case IPPROTO_TCP:
339         print_tcp(le, (struct tcphdr *)nexthdr, payload_len);
340         break;
341     case IPPROTO_UDP:
342         print_udp(le, (struct udphdr *)nexthdr, payload_len);
343         break;
344     case IPPROTO_ICMP:
345         print_icmp(le, (struct icmphdr *)nexthdr, payload_len);
346         break;
347     case IPPROTO_SCTP:
348         print_sctp(le, (struct sctphdr *)nexthdr, payload_len);
349         break;
350     case IPPROTO_AH:
351         LEPRINTF("PROTO=AH ");
352         break;
353     case IPPROTO_ESP:
354         LEPRINTF("PROTO=ESP ");
355         break;
356     case IPPROTO_IGMP:
357         LEPRINTF("PROTO=IGMP ");
358         break;
359      default:
360         LEPRINTF("PROTO=%u ", h->protocol);
361     }
362
363     return 0;
364 }
365
366 static int 
367 print_ip6hdr(struct log_entry *le, char * payload, int payload_len)
368 {
369     LEPRINTF("IPV6 logging not implemented ");
370
371     return 0;
372 }
373
374 // ebtables -I FORWARD --nflog --nflog-group 0
375 static int 
376 print_arp(struct log_entry *le, struct ether_arp *h, int payload_len)
377 {
378     if (payload_len < sizeof(struct ether_arp)) {
379         LEPRINTF("LEN=%d ", payload_len);
380         LEPRINTF("INVALID=LEN ");
381         return -1;
382     }
383
384     LEPRINTF("SRC=%u.%u.%u.%u ", h->arp_spa[0], h->arp_spa[1],
385              h->arp_spa[2], h->arp_spa[3]);
386
387     LEPRINTF("DST=%u.%u.%u.%u ", h->arp_tpa[0], h->arp_tpa[1],
388              h->arp_tpa[2], h->arp_tpa[3]);
389
390     LEPRINTF("PROTO=ARP ");
391
392     unsigned short code = ntohs(h->arp_op);
393     switch (code) {
394     case ARPOP_REQUEST:
395         LEPRINTF("REQUEST ");
396         break;
397     case ARPOP_REPLY:
398         LEPRINTF("REPLY MAC=%02x:%02x:%02x:%02x:%02x:%02x ", 
399                  h->arp_sha[0], h->arp_sha[1], h->arp_sha[2], 
400                  h->arp_sha[3], h->arp_sha[4], h->arp_sha[5]);
401         break;
402     case ARPOP_NAK:
403         LEPRINTF("NAK ");
404         break;
405     default:
406         LEPRINTF("CODE=%u ", code);
407     }
408
409
410     // LEPRINTF("HTYPE=%u ", ntohs(h->arp_hrd));
411
412     // LEPRINTF("PTYPE=%u ", ntohs(h->arp_pro));
413
414     return 0;
415 }
416
417
418 static int print_pkt(struct log_entry *le, struct nflog_data *ldata, u_int8_t family)
419 {
420     u_int32_t mark = nflog_get_nfmark(ldata);
421     u_int32_t indev = nflog_get_indev(ldata);
422     u_int32_t outdev = nflog_get_outdev(ldata);
423     u_int32_t physindev = nflog_get_physindev(ldata);
424     u_int32_t physoutdev = nflog_get_physoutdev(ldata);
425
426     char *prefix = nflog_get_prefix(ldata);
427     char *payload;
428     char devname[256];
429     
430     guint32 vmid = 0;
431
432     guint8 log_level = 6; // info
433
434     char *chain_name = "-";
435
436     if (prefix != NULL) {
437         // Note: parse ":$vmid:$loglevel:$chain: $msg"
438         if (prefix[0] == ':') {
439             char *p = prefix + 1;
440             guint32 tmpid = 0;
441             while(*p  >= '0' && *p <= '9') { tmpid *= 10; tmpid += *p - '0'; p++; }
442
443             if ((*p == ':') &&
444                 (p[1] >= '0' && p[1] <= '7') &&
445                 (p[2] == ':')) {
446
447                 guint8 tmp_level = p[1] - '0'; // store for later use
448                 char *chain_start = p + 3; // store for later use
449                 p = chain_start;
450                 while (*p && *p != ':' && *p != ' ') p++;
451                 int len = p - chain_start;
452
453                 if (*p == ':' && p[1] == ' ' && len && (len <= MAX_CHAIN_LEN)) {
454                     // parsing successful
455
456                     *p = 0; // terminate string
457
458                     vmid = tmpid;
459                     log_level = tmp_level;
460                     chain_name = chain_start;
461                     prefix = p + 2; // the rest
462                 } 
463             }
464         }
465     }
466
467     LEPRINTF("%d ", vmid);
468
469     LEPRINTF("%d ", log_level);
470
471     LEPRINTF("%s ", chain_name);
472
473     struct timeval ts;
474     nflog_get_timestamp(ldata, &ts);
475
476     LEPRINTTIME(ts.tv_sec);
477
478     if (prefix != NULL) {
479         LEPRINTF("%s", prefix);
480     }
481     
482     if (indev > 0) { 
483         if (nlif_index2name(nlifh, indev, devname) != -1) {
484             LEPRINTF("IN=%s ", devname); 
485         } else {
486             LEPRINTF("IN=%u ", indev); 
487         }
488     }
489
490     if (outdev > 0) {
491         if (nlif_index2name(nlifh, outdev, devname) != -1) {
492             LEPRINTF("OUT=%s ", devname);
493         } else {
494             LEPRINTF("OUT=%u ", outdev);
495         }
496     }
497
498     if (physindev > 0) { 
499         if (nlif_index2name(nlifh, physindev, devname) != -1) {
500             LEPRINTF("PHYSIN=%s ", devname);
501         } else {
502             LEPRINTF("PHYSIN=%u ", physindev);
503         }
504     }
505          
506     if (physoutdev > 0) {
507         if (nlif_index2name(nlifh, physoutdev, devname) != -1) {
508             LEPRINTF("PHYSOUT=%s ", devname);
509         } else {
510             LEPRINTF("PHYSOUT=%u ", physoutdev);
511         }
512     }
513
514     int payload_len = nflog_get_payload(ldata, &payload);
515
516     int hwhdrlen = nflog_get_msg_packet_hwhdrlen(ldata);
517     if (hwhdrlen > 0) {
518         unsigned char *hwhdr = (unsigned char *)nflog_get_msg_packet_hwhdr(ldata);
519         if (hwhdr != NULL) {
520             int i;
521             LEPRINTF("MAC=");
522             for (i = 0; i < hwhdrlen; i++) {
523                 LEPRINTF("%02x", hwhdr[i]);
524                 if (i < (hwhdrlen -1 )) LEPRINTF(":");
525             }
526             LEPRINTF(" ");
527         }
528     }
529
530     u_int16_t hw_protocol = 0;
531     struct nfulnl_msg_packet_hdr *ph = NULL;
532
533     switch (family) {
534     case AF_INET: 
535         print_iphdr(le, payload, payload_len);
536         break;
537     case AF_INET6:
538         print_ip6hdr(le, payload, payload_len);
539         break;
540     case AF_BRIDGE:
541         ph = nflog_get_msg_packet_hdr(ldata);
542         if (ph) hw_protocol = ntohs(ph->hw_protocol);
543
544         switch (hw_protocol) {
545         case ETH_P_IP:
546             print_iphdr(le, payload, payload_len);
547             break;
548         case ETH_P_IPV6:
549              print_ip6hdr(le, payload, payload_len);
550             break;
551         case ETH_P_ARP:
552             print_arp(le, (struct ether_arp *)payload, payload_len);
553             break;
554         }
555         break;
556     }
557
558     if (mark) LEPRINTF("mark=%u ", mark);
559
560
561     return 0;
562
563 }
564
565 static int 
566 nflog_cb(struct nflog_g_handle *gh, struct nfgenmsg *nfmsg,
567                struct nflog_data *nfa, void *data)
568 {
569     struct log_entry *le = g_new0(struct log_entry, 1);
570  
571     print_pkt(le, nfa, nfmsg->nfgen_family);
572
573     LEPRINTF("\n"); // add newline
574
575     queue_log_entry(le);
576
577     return 0;
578 }
579
580 static gboolean
581 nflog_read_cb(GIOChannel *source,
582               GIOCondition condition,
583               gpointer data)
584 {
585     int rv = 0;
586     gchar buf[8192];
587
588     int fd =  g_io_channel_unix_get_fd(source);
589    
590     if ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
591          nflog_handle_packet(logh, buf, rv);
592     }
593
594     return TRUE;
595 }
596
597 static gboolean
598 nlif_read_cb(GIOChannel *source,
599              GIOCondition condition,
600              gpointer data)
601 {
602     static int last_res = 0;
603     int res;
604
605     if ((res = nlif_catch(nlifh)) < 0) {
606         if (last_res == 0) { // only report once
607             log_status_message(3, "nlif_catch failed (res = %d)", res);
608         }
609         last_res = res;
610     } else {
611         last_res = 0;
612     }
613
614     return TRUE; 
615 }
616
617 GMainLoop *main_loop;
618
619 static gboolean
620 terminate_request(gpointer data) 
621 {
622     terminate_threads = TRUE;
623
624     log_status_message(5, "received terminate request (signal)");
625
626     g_main_loop_quit(main_loop);
627     
628     return TRUE;
629 }
630                 
631
632 int
633 main(int argc, char *argv[])
634 {
635     int lockfd = -1;
636     gboolean foreground = FALSE;
637     gboolean wrote_pidfile = FALSE;
638
639     g_thread_init(NULL);
640
641     openlog("pvepw-logger", LOG_CONS|LOG_PID, LOG_DAEMON);
642
643     if ((lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_APPEND, 0644)) == -1) {
644         fprintf(stderr, "unable to create lock '%s': %s", LOCKFILE, strerror (errno) );
645         exit(-1);
646     }
647
648     for (int i = 10; i >= 0; i--) {
649         if (flock(lockfd, LOCK_EX|LOCK_NB) != 0) {
650             if (!i) {
651                 fprintf(stderr, "unable to aquire lock '%s': %s", LOCKFILE, strerror (errno));
652                 exit(-1);
653             }
654             if (i == 10)
655                 fprintf(stderr, "unable to aquire lock '%s' - trying again.\n", LOCKFILE);
656             
657             sleep(1);
658         }
659     }
660
661     if ((outfd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, 0644)) == -1) {
662         fprintf(stderr, "unable to open file '%s': %s", LOGFILE, strerror (errno));
663         exit(-1);
664     }
665
666     if ((logh = nflog_open())  == NULL) {
667         fprintf(stderr, "unable to open nflog\n");
668         exit(-1);
669     }
670
671     if (!nflog_bind_pf(logh, AF_INET) <= 0) {
672         fprintf(stderr, "nflog_bind_pf AF_INET failed\n");
673         exit(-1);
674     }
675
676 #if 0
677     if (!nflog_bind_pf(logh, AF_INET6) <= 0) {
678         fprintf(stderr, "nflog_bind_pf AF_INET6 failed\n");
679         exit(-1);
680     }
681 #endif
682     
683     if (!nflog_bind_pf(logh, AF_BRIDGE) <= 0) {
684         fprintf(stderr, "nflog_bind_pf AF_BRIDGE failed\n");
685         exit(-1);
686     }
687
688     struct nflog_g_handle *qh = nflog_bind_group(logh, 0);
689     if (!qh) {
690         fprintf(stderr, "no handle for group 1\n");
691         exit(-1);
692     }
693  
694     if (nflog_set_mode(qh, NFULNL_COPY_PACKET, 0xffff) < 0) {
695         fprintf(stderr, "can't set packet copy mode\n");
696         exit(-1);
697     }
698
699     if ((nlifh = nlif_open()) == NULL) {
700         fprintf(stderr, "unable to open netlink interface handle\n");
701         exit(-1);
702     }
703
704     if (!foreground) {
705         pid_t cpid = fork();
706
707         if (cpid == -1) {
708             fprintf(stderr, "failed to daemonize program - %s\n", strerror (errno));
709             exit(-1);
710         } else if (cpid) {
711             write_pidfile(cpid);
712             _exit(0);
713         } else {
714             int nullfd;
715
716             if (chroot("/") != 0) fprintf(stderr, "chroot '/' failed - %s\n", strerror (errno));
717
718             if ((nullfd = open("/dev/null", O_RDWR, 0)) != -1) {
719                 dup2(nullfd, 0);
720                 dup2(nullfd, 1);
721                 dup2(nullfd, 2);
722                 if (nullfd > 2)
723                     close (nullfd);
724             }
725
726             setsid();
727         }
728     } else {
729         write_pidfile(getpid());
730     }
731
732     wrote_pidfile = TRUE;
733
734     nflog_callback_register(qh, &nflog_cb, logh);
735
736     queue = g_async_queue_new_full(g_free);
737
738     log_status_message(5, "starting pvefw logger");
739
740     nlif_query(nlifh);
741
742     GIOChannel *nlif_ch = g_io_channel_unix_new(nlif_fd(nlifh));
743
744     g_io_add_watch(nlif_ch, G_IO_IN, nlif_read_cb, NULL);
745
746     int logfd = nflog_fd(logh);
747     GIOChannel *nflog_ch = g_io_channel_unix_new(logfd);
748
749     g_io_add_watch(nflog_ch, G_IO_IN, nflog_read_cb, NULL);
750
751     GThread *wthread = g_thread_new("log_writer_thread", log_writer_thread, NULL);
752     
753     main_loop = g_main_loop_new(NULL, TRUE);
754     
755     g_unix_signal_add(SIGINT, terminate_request, NULL);
756     g_unix_signal_add(SIGTERM, terminate_request, NULL);
757     
758     g_main_loop_run(main_loop);
759
760     log_status_message(5, "stopping pvefw logger");
761
762     g_thread_join(wthread);
763
764     close(outfd);
765
766     nflog_close(logh);
767
768     if (wrote_pidfile)
769         unlink(PIDFILE);
770
771     exit(0);
772 }