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