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