additionally log status messages to syslog
[pve-firewall.git] / src / pvefw-logger.c
CommitLineData
ba0b3a0a
DM
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>
9da3465c 47#include <syslog.h>
ba0b3a0a
DM
48
49#include <glib.h>
50#include <glib-unix.h>
51
52static struct nflog_handle *logh = NULL;
53static struct nlif_handle *nlifh = NULL;
54
782c4cde
DM
55/*
56
57LOG FORMAT:
58
59Special care was taken to allow fast parsing (and filer messages for a singl VM).
60
61<VMID> <LOGLEVEL> <CHAIN> <TIME> <TIMEZONE> <MSG>
62
63Example:
64
65117 6 tap117i0-IN 14/Mar/2014:12:47:07 +0100 policy REJECT: IN=vmbr1 ...
66
67*/
68
ba0b3a0a
DM
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
782c4cde
DM
77#define MAX_CHAIN_LEN 28
78
ba0b3a0a
DM
79struct log_entry {
80 guint32 len; // max LE_MAX chars
81 char buf[LE_MAX];
82};
83
84int outfd = -1;
85
86gboolean terminate_threads = FALSE;
87
88static 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
99static GAsyncQueue *queue;
100
101ssize_t
102safe_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
113static gpointer
114log_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 }
782c4cde 124
ba0b3a0a 125 int res = safe_write(outfd, le->buf, le->len);
9da3465c 126
ba0b3a0a
DM
127 g_free(le);
128
129 if (res < 0) {
9da3465c
DM
130 syslog(3, "writing log failed, stopping daemon - %s", strerror (errno));
131 return NULL;
ba0b3a0a
DM
132 }
133 }
134
135 return NULL;
136}
137
138static int skipped_logs = 0;
139
782c4cde 140static void log_status_message(guint loglevel, const char *fmt, ...);
ba0b3a0a
DM
141
142static void
143queue_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()
782c4cde 153 log_status_message(3, "skipped %d log entries (queue full)", skip_tmp);
ba0b3a0a
DM
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
169static void
782c4cde 170log_status_message(guint loglevel, const char *fmt, ...)
ba0b3a0a
DM
171{
172 va_list ap;
173 va_start(ap, fmt);
174
782c4cde
DM
175 if (loglevel > 7 ) loglevel = 7; // syslog defines level 0-7
176
ba0b3a0a
DM
177 struct log_entry *le = g_new0(struct log_entry, 1);
178
782c4cde
DM
179 LEPRINTF("0 %d - ", loglevel);
180
ba0b3a0a
DM
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);
9da3465c
DM
188
189 // also log to syslog
190
191 vsyslog(loglevel, fmt, ap);
ba0b3a0a
DM
192}
193
194static int
195print_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
221static int
222print_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
237static int
238print_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 */
277typedef struct sctphdr {
278 __be16 source;
279 __be16 dest;
280 __be32 vtag;
281 __be32 checksum;
282} __attribute__((packed)) sctp_sctphdr_t;
283
284static int
285print_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
300static int
301print_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
366static int
367print_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
375static int
376print_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
418static 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
782c4cde
DM
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
ba0b3a0a
DM
473 struct timeval ts;
474 nflog_get_timestamp(ldata, &ts);
475
476 LEPRINTTIME(ts.tv_sec);
477
ba0b3a0a
DM
478 if (prefix != NULL) {
479 LEPRINTF("%s", prefix);
480 }
481
e7ceb650
DM
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 }
ba0b3a0a
DM
488 }
489
e7ceb650
DM
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 }
ba0b3a0a
DM
496 }
497
e7ceb650
DM
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 }
ba0b3a0a
DM
504 }
505
e7ceb650
DM
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 }
ba0b3a0a
DM
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
565static int
566nflog_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
580static gboolean
581nflog_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
597static gboolean
598nlif_read_cb(GIOChannel *source,
599 GIOCondition condition,
600 gpointer data)
601{
9da3465c
DM
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
ba0b3a0a
DM
614 return TRUE;
615}
616
617GMainLoop *main_loop;
618
619static gboolean
620terminate_request(gpointer data)
621{
622 terminate_threads = TRUE;
623
782c4cde 624 log_status_message(5, "received terminate request (signal)");
ba0b3a0a
DM
625
626 g_main_loop_quit(main_loop);
627
628 return TRUE;
629}
630
631
632int
633main(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
9da3465c
DM
641 openlog("pvepw-logger", LOG_CONS|LOG_PID, LOG_DAEMON);
642
ba0b3a0a 643 if ((lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_APPEND, 0644)) == -1) {
9da3465c 644 fprintf(stderr, "unable to create lock '%s': %s", LOCKFILE, strerror (errno) );
ba0b3a0a
DM
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
782c4cde 738 log_status_message(5, "starting pvefw logger");
ba0b3a0a
DM
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
782c4cde 760 log_status_message(5, "stopping pvefw logger");
ba0b3a0a
DM
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}