*/
#include <stdio.h>
+#include <stdlib.h>
#include <errno.h>
#include <string.h>
+#include <stdarg.h>
#include <signal.h>
#include <time.h>
#include <alloca.h>
#include <ulogd/conffile.h>
typedef unsigned char byte;
+typedef unsigned int uns;
/*** Configuration ***/
-static config_entry_t sip_cf_log = {
+static config_entry_t sip_cf_mail_to = {
.next = NULL,
+ .key = "mailto",
+ .type = CONFIG_TYPE_STRING,
+ .options = CONFIG_OPT_NONE,
+ .u.string = ""
+};
+
+static config_entry_t sip_cf_debug_log = {
+ .next = &sip_cf_mail_to,
+ .key = "debuglog",
+ .type = CONFIG_TYPE_STRING,
+ .options = CONFIG_OPT_NONE,
+ .u.string = ""
+};
+
+static config_entry_t sip_cf_log = {
+ .next = &sip_cf_debug_log,
.key = "file",
.type = CONFIG_TYPE_STRING,
.options = CONFIG_OPT_NONE,
/*** Unaligned access ***/
-static unsigned int get_u16(byte *p)
+static uns get_u16(byte *p)
{
return (p[0] << 8) | p[1];
}
-static unsigned int get_u32(byte *p)
+static uns get_u32(byte *p)
{
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}
static struct {
char *name;
- unsigned int id;
+ uns id;
} keys[] = {
{ "raw.pkt", 0 },
{ "raw.pktlen", 0 },
static int init_keys(void)
{
- for (unsigned int i=0; i < sizeof(keys)/sizeof(keys[0]); i++)
+ for (uns i=0; i < sizeof(keys)/sizeof(keys[0]); i++)
if (!(keys[i].id = keyh_getid(keys[i].name)))
{
ulogd_log(ULOGD_ERROR, "SIP: Unable to resolve key %s\n", keys[i].name);
#define KEY_TYPE(i) KEY_H(i).type
#define KEY_OK(i,t) ((KEY_FLAGS(i) & ULOGD_RETF_VALID) && KEY_TYPE(i) == ULOGD_RET_##t)
-/*** Logging ***/
-
-static FILE *siplog;
+/*** Representation of packets ***/
+
+struct pkt {
+ time_t time_sec; // Arrival time
+ uns time_usec;
+ char time[64];
+ byte *raw; // Raw packet
+ uns raw_len;
+ uns out_p;
+ byte *ip; // Inside IP
+ uns ip_len;
+ uns src_addr, dst_addr;
+ byte *udp; // Inside UDP
+ uns udp_len;
+ uns src_port, dst_port;
+ char *sip_rr; // Decoded SIP
+ struct hdr *sip_hdr;
+ char *sip_method;
+ char *sip_uri;
+ int sip_status;
+ char *sip_msg;
+ struct call *sip_call;
+ char *sip_seq;
+};
struct hdr {
struct hdr *next;
char *name, *value;
};
-static void sip_parse_obj(char *rr, struct hdr *headers)
+static char *find_hdr(struct pkt *pkt, char *name)
+{
+ for (struct hdr *h=pkt->sip_hdr; h; h=h->next)
+ if (!strcasecmp(h->name, name))
+ return h->value;
+ return NULL;
+}
+
+static char *find_hdr2(struct pkt *pkt, char *name, char *alt)
+{
+ return find_hdr(pkt, name) ? : find_hdr(pkt, alt);
+}
+
+/*** Logging ***/
+
+static FILE *siplog, *debuglog;
+
+static void debug(char *fmt, ...)
+{
+ if (!debuglog)
+ return;
+ va_list args;
+ va_start(args, fmt);
+ fputc('\t', debuglog);
+ vfprintf(debuglog, fmt, args);
+ fputc('\n', debuglog);
+ va_end(args);
+}
+
+static int error(struct pkt *pkt, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(siplog, "%s ERR: ", pkt->time);
+ vfprintf(siplog, fmt, args);
+ fputc('\n', siplog);
+ va_end(args);
+ return 0;
+}
+
+/*** Auxillary routines ***/
+
+static inline int is_white(int c)
+{
+ return (c == ' ' || c == '\t');
+}
+
+static char *skip_ws(char *c)
+{
+ while (is_white(*c))
+ c++;
+ return c;
+}
+
+static char *skip_nonws(char *c)
+{
+ while (*c && !is_white(*c))
+ c++;
+ return c;
+}
+
+/*** The parser ***/
+
+#define CALL_ID_LEN 63
+#define NUM_LEN 63
+#define CSEQ_LEN 63
+
+enum call_state {
+ CALL_IDLE,
+ CALL_DIALING,
+ CALL_CONNECTED,
+ CALL_BYE,
+};
+
+enum call_flags {
+ FLAG_RING = 1,
+ FLAG_ANSWERED = 2,
+ FLAG_REDIRECT = 4,
+};
+
+struct call {
+ struct call *next;
+ char call_id[CALL_ID_LEN+1];
+ enum call_state state;
+ uns out_p;
+ char active_seq[CSEQ_LEN+1];
+ char src_party[NUM_LEN+1];
+ char dst_party[NUM_LEN+1];
+ uns flags;
+};
+static struct call *call_list;
+
+static void log_call(struct pkt *pkt, struct call *c)
+{
+ fprintf(siplog, "%s: CALL %s <%s> -> <%s>", pkt->time, (c->out_p ? "OUT" : "IN"), c->src_party, c->dst_party);
+ if (c->flags & FLAG_ANSWERED)
+ fprintf(siplog, " ANS");
+ else if (c->flags & FLAG_RING)
+ fprintf(siplog, " RING");
+ if (c->flags & FLAG_REDIRECT)
+ fprintf(siplog, " REDIR");
+ if (pkt->sip_status >= 300)
+ fprintf(siplog, " ERR:%s", pkt->sip_msg);
+ else
+ fprintf(siplog, " OK");
+ fputc('\n', siplog);
+
+ if (sip_cf_mail_to.u.string[0])
+ {
+ char cmd[256];
+ sprintf(cmd, "mail -s 'SIP CALL' %s", sip_cf_mail_to.u.string);
+ debug("Running %s", cmd);
+ FILE *m = popen(cmd, "w");
+ if (m)
+ {
+ fprintf(m, "Finished-At: %s\n", pkt->time);
+ fprintf(m, "Direction: %s\n", (c->out_p ? "OUT" : "IN"));
+ fprintf(m, "From: %s\n", c->src_party);
+ fprintf(m, "To: %s\n", c->dst_party);
+ fprintf(m, "Flags:%s%s%s\n",
+ ((c->flags & FLAG_ANSWERED) ? " ANS" : ""),
+ ((c->flags & FLAG_RING) ? " RING" : ""),
+ ((c->flags & FLAG_REDIRECT) ? " REDIR" : ""));
+ fprintf(m, "Status: %s\n", pkt->sip_msg);
+ fclose(m);
+ }
+ else
+ error(pkt, "Cannot execute `%s': %m", cmd);
+ }
+}
+
+static void extract_number(char *buf, char *h)
+{
+ if (!h)
+ h = "???";
+ char *c;
+ // Multiple addresses => use the first one
+ if (c = strchr(h, ','))
+ *c = 0;
+ // Tagged parameters => drop them
+ if (c = strchr(h, ';'))
+ *c = 0;
+ // Quoted part => prefer it
+ if (c = strchr(h, '<'))
+ {
+ h = c+1;
+ if (c = strchr(h, '>'))
+ *c = 0;
+ }
+ // Skip URL scheme
+ if (c = strchr(h, ':'))
+ h = c+1;
+ // Use only numeric characters, * and #
+ char *d = buf;
+ while (d < buf + NUM_LEN)
+ {
+ if (*h >= '0' && *h <= '9' || *h == '*')
+ *d++ = *h++;
+ else if (*h == '%' && h[1] == '2' && h[2] == '3')
+ {
+ *d++ = '#';
+ h += 3;
+ }
+ else
+ break;
+ }
+ *d = 0;
+}
+
+static int parse_request(struct pkt *pkt)
{
+ struct call *c = pkt->sip_call;
+ if (!strcmp(pkt->sip_seq, c->active_seq))
+ {
+ debug("Retransmitted packet");
+ return 1;
+ }
+ if (!strcasecmp(pkt->sip_method, "INVITE"))
+ {
+ if (c->state != CALL_IDLE)
+ return error(pkt, "re-INVITE not supported yet");
+ c->state = CALL_DIALING;
+ c->out_p = pkt->out_p;
+ strcpy(c->active_seq, pkt->sip_seq);
+ extract_number(c->src_party, find_hdr2(pkt, "From", "f"));
+ extract_number(c->dst_party, find_hdr2(pkt, "To", "t"));
+ }
+ else if (!strcasecmp(pkt->sip_method, "BYE"))
+ {
+ if (c->state != CALL_CONNECTED)
+ {
+ c->state = CALL_IDLE;
+ return error(pkt, "Unexpected BYE");
+ }
+ c->state = CALL_BYE;
+ strcpy(c->active_seq, pkt->sip_seq);
+ }
+ return 1;
}
-static void sip_parse_header(byte *pkt, unsigned int len)
+static int parse_reply(struct pkt *pkt)
{
- char *p = (char*) pkt;
- char *end = p + len;
- struct hdr *headers = NULL;
+ struct call *c = pkt->sip_call;
+ if (strcmp(pkt->sip_seq, c->active_seq))
+ {
+ debug("Reply not associated to active request %s", c->active_seq);
+ return 1;
+ }
-#if 0
- for (unsigned int i=0; i<len; i++)
- fputc(p[i], siplog);
- fprintf(siplog, "----\n");
-#endif
+ uns stat = pkt->sip_status;
+ if (stat >= 200)
+ c->active_seq[0] = 0;
+
+ if (c->state == CALL_DIALING)
+ {
+ if (stat < 100)
+ ;
+ else if (stat < 200)
+ {
+ // Provisional responses
+ if (stat == 180)
+ c->flags |= FLAG_RING;
+ }
+ else if (stat < 300)
+ {
+ // Success
+ c->state = CALL_CONNECTED;
+ c->flags |= FLAG_ANSWERED;
+ }
+ else if (stat < 400)
+ {
+ // Redirect
+ c->flags |= FLAG_REDIRECT;
+ log_call(pkt, c);
+ c->state = CALL_IDLE;
+ }
+ else
+ {
+ // Failure of some sort
+ switch (stat)
+ {
+ case 408: // Timeout
+ case 404: // Not found
+ case 403: // Forbidden
+ case 480: // Tmp unavailable
+ case 486: // Busy here
+ case 487: // Cancelled
+ case 600: // Busy everywhere
+ case 603: // Decline
+ case 604: // Does not exist
+ case 606: // Unaccaptable
+ log_call(pkt, c);
+ }
+ c->state = CALL_IDLE;
+ // We do not reset flags on failures, because another INVITE may be sent for the same call and we wish to remember redirects etc.
+ }
+ }
+ else if (c->state == CALL_BYE)
+ {
+ if (stat >= 200)
+ {
+ log_call(pkt, c);
+ c->state = CALL_IDLE;
+ }
+ }
+ return 1;
+}
+
+static int parse_call(struct pkt *pkt)
+{
+ char *id = find_hdr2(pkt, "Call-ID", "i");
+ if (!id)
+ return error(pkt, "Missing Call-ID");
+ char *p = skip_nonws(id);
+ *p = 0;
+ if (!*id)
+ return error(pkt, "Empty Call-ID");
+ if (strlen(id) > CALL_ID_LEN)
+ return error(pkt, "Call-ID too long");
+
+ pkt->sip_seq = find_hdr(pkt, "CSeq");
+ if (!pkt->sip_seq)
+ return error(pkt, "Missing CSeq");
+ if (strlen(pkt->sip_seq) > CSEQ_LEN)
+ return error(pkt, "CSeq too long");
+
+ struct call *c;
+ for (c=call_list; c && strcasecmp(c->call_id, id); c=c->next)
+ ;
+ if (!c)
+ {
+ if (!pkt->sip_method)
+ return error(pkt, "Cannot match reply to an existing call");
+ debug("Creating new call <%s>", id);
+ c = malloc(sizeof(struct call));
+ if (!c)
+ return error(pkt, "Out of memory");
+ memset(c, 0, sizeof(*c));
+ c->next = call_list;
+ call_list = c;
+ strcpy(c->call_id, id);
+ c->state = CALL_IDLE;
+ }
+ else
+ debug("Continuing call <%s> in state %d", id, c->state);
+ pkt->sip_call = c;
+ // FIXME: We should time out the calls
+
+ if (pkt->sip_method)
+ return parse_request(pkt);
+ else
+ return parse_reply(pkt);
+}
+
+static int parse_rr(struct pkt *pkt)
+{
+ char *rr = pkt->sip_rr;
+ if (!strncmp(rr, "SIP/", 4))
+ {
+ // Reply
+ rr = skip_ws(skip_nonws(rr));
+ if (!*rr)
+ return error(pkt, "Truncated SIP response");
+ pkt->sip_msg = rr;
+ pkt->sip_status = atol(rr);
+ pkt->sip_method = NULL;
+ pkt->sip_uri = NULL;
+ debug("SIP status %d (%s)", pkt->sip_status, pkt->sip_msg);
+ }
+ else
+ {
+ // Request
+ pkt->sip_msg = NULL;
+ pkt->sip_status = -1;
+ pkt->sip_method = rr;
+ rr = skip_nonws(rr);
+ if (!*rr)
+ return error(pkt, "Missing URI in SIP request");
+ *rr++ = 0;
+ rr = skip_ws(rr);
+ if (!*rr)
+ return error(pkt, "Missing URI in SIP request");
+ pkt->sip_uri = rr;
+ rr = skip_nonws(rr);
+ *rr = 0;
+ debug("SIP request %s on %s", pkt->sip_method, pkt->sip_uri);
+ }
+ return parse_call(pkt);
+}
+
+static int parse_sip(struct pkt *pkt)
+{
+ char *p = (char*) pkt->udp;
+ char *end = p + pkt->udp_len;
+ pkt->sip_rr = NULL;
+ pkt->sip_hdr = NULL;
// Parse the request/reply line first
- char *rr = p;
+ pkt->sip_rr = p;
while (p < end && *p != '\r' && *p != '\n')
p++;
if (p >= end)
- {
- fprintf(siplog, "\tTruncated 1st line\n");
- return;
- }
+ return error(pkt, "Truncated 1st line");
if (*p == '\r')
*p++ = 0;
if (p < end && *p == '\n')
*p++ = 0;
- fprintf(siplog, "\t>>> %s\n", rr);
+ debug(">>> %s", pkt->sip_rr);
- // Slurp the other header lines
+ // Slurp the other header lines and sanitize them
for (;;)
{
char *start = p;
for (;;)
{
if (p >= end)
- {
- fprintf(siplog, "\tTruncated SIP headers\n");
- return;
- }
+ return error(pkt, "Truncated SIP headers");
if (*p == '\r')
p++;
else if (*p == '\n')
{
p++;
- if (p < end && (*p == ' ' || *p == '\t'))
+ if (p < end && is_white(*p) && pkt->sip_rr)
;
else
{
break;
}
}
- else
+ else if (*p > 0x20 && *p != 0x7f || *p < 0)
*w++ = *p++;
+ else if (is_white(*p))
+ *w++ = ' ', p++;
+ else
+ *w++ = '?', p++;
}
if (!start[0])
break;
- // fprintf(siplog, "\tH: %s\n", start);
// Got a line, parse it
+ if (!pkt->sip_rr)
+ {
+ pkt->sip_rr = start;
+ debug(">>> %s", pkt->sip_rr);
+ continue;
+ }
char *sep = strchr(start, ':');
if (!*sep)
- {
- fprintf(siplog, "\tMalformed header line\n");
+ {
+ error(pkt, "Malformed header line");
continue;
}
*sep = 0;
- for (char *p=sep; p>start && (p[-1] == ' ' || p[-1] == '\t');)
+ for (char *p=sep; p>start && is_white(p[-1]);)
*--p = 0;
- while (++sep < end && (*sep == ' ' || *sep == '\t'))
+ while (++sep < end && is_white(*sep))
*sep = 0;
- fprintf(siplog, "\t%s: %s\n", start, sep);
+ debug("\t%s: %s", start, sep);
struct hdr *h = alloca(sizeof(*h));
- h->next = headers;
- headers = h;
+ h->next = pkt->sip_hdr;
+ pkt->sip_hdr = h;
h->name = start;
h->value = sep;
}
+ if (!pkt->sip_rr)
+ return error(pkt, "Missing SIP header");
- sip_parse_obj(rr, headers);
+ return parse_rr(pkt);
}
-static void sip_parse_packet(byte *pkt, unsigned int len)
+static int parse_udp(struct pkt *pkt)
{
- /* Parse IP header */
- if (len < 20)
- {
- fprintf(siplog, "\tVery truncated IP header\n");
- return;
- }
- unsigned int ipver = pkt[0] >> 4;
- if (ipver != 4)
- {
- fprintf(siplog, "\tWe don't speak IPv%d\n", ipver);
- return;
- }
- unsigned int hlen = (pkt[0] & 0x0f)*4;
- if (len < hlen)
- {
- fprintf(siplog, "\tTruncated IP header (want %d)\n", hlen);
- return;
- }
- unsigned int ilen = get_u16(pkt+2);
- if (ilen > len)
- {
- fprintf(siplog, "\tTruncated (have %d, want %d)\n", len, ilen);
- return;
- }
- len = ilen;
- if (pkt[9] != 17)
- {
- fprintf(siplog, "\tNot UDP (protocol %02x)\n", pkt[9]);
- return;
- }
- if (get_u16(pkt+6) & 0x3fff)
- {
- fprintf(siplog, "\tFragmented\n");
- return;
- }
- unsigned int saddr = get_u32(pkt+12);
- unsigned int daddr = get_u32(pkt+16);
- len -= hlen;
- pkt += hlen;
-
- /* Parse UDP header */
- if (len < 8)
- {
- fprintf(siplog, "\tTruncated UDP header\n");
- return;
- }
- unsigned int sport = get_u16(pkt);
- unsigned int dport = get_u16(pkt+2);
- unsigned int ulen = get_u16(pkt+4);
- if (len < ulen)
- {
- fprintf(siplog, "\tTruncated UDP data (have %d, want %d)\n", len, ulen);
- return;
- }
- len = ulen-8;
- pkt += 8;
+ byte *p = pkt->ip;
+ if (pkt->ip_len < 8)
+ return error(pkt, "Truncated UDP header");
+ pkt->src_port = get_u16(p);
+ pkt->dst_port = get_u16(p+2);
+ uns ulen = get_u16(p+4);
+ if (ulen < 8)
+ return error(pkt, "Invalid UDP length (%d)", ulen);
+ if (ulen > pkt->ip_len)
+ return error(pkt, "Truncated UDP data (have %d, want %d)", pkt->ip_len, ulen);
+ pkt->udp = p + 8;
+ pkt->udp_len = ulen - 8;
+ debug("%08x:%d -> %08x:%d (%d bytes, out_p=%d)", pkt->src_addr, pkt->src_port, pkt->dst_addr, pkt->dst_port, pkt->udp_len, pkt->out_p);
+ return parse_sip(pkt);
+}
- fprintf(siplog, "\t%08x:%d -> %08x:%d (%d bytes)\n", saddr, sport, daddr, dport, len);
- sip_parse_header(pkt, len);
+static int parse_ip(struct pkt *pkt)
+{
+ byte *p = pkt->raw;
+ if (pkt->raw_len < 20)
+ return error(pkt, "Very truncated IP header");
+ uns ipver = p[0] >> 4;
+ if (ipver != 4)
+ return error(pkt, "We don't speak IPv%d", ipver);
+ uns hlen = (p[0] & 0x0f)*4;
+ if (pkt->raw_len < hlen)
+ return error(pkt, "Truncated IP header (want %d)", hlen);
+ uns ilen = get_u16(p+2);
+ if (ilen < hlen)
+ return error(pkt, "Invalid IP length (have %d, want at least %d for header)", ilen, hlen);
+ if (ilen > pkt->raw_len)
+ return error(pkt, "Truncated (have %d, want %d)", pkt->raw_len, ilen);
+ if (p[9] != 17)
+ return error(pkt, "Not UDP (protocol %02x)", p[9]);
+ if (get_u16(p+6) & 0x3fff)
+ return error(pkt, "Fragmented");
+ pkt->ip = p + hlen;
+ pkt->ip_len = ilen - hlen;
+ pkt->src_addr = get_u32(p+12);
+ pkt->dst_addr = get_u32(p+16);
+ return parse_udp(pkt);
}
+/*** Interface to ulogd ***/
+
static int sip_output(ulog_iret_t *res __attribute__((unused)))
{
+ struct pkt pkt;
+
+ if (!siplog)
+ return 0;
+
+ char *prefix = (KEY_OK(K_OOB_PREFIX, STRING) ? KEY_VAL(K_OOB_PREFIX).ptr : "?");
+ if (!strcasecmp(prefix, "phone-in"))
+ pkt.out_p = 0;
+ else if (!strcasecmp(prefix, "phone-out"))
+ pkt.out_p = 1;
+ else
+ {
+ // fprintf(debuglog, "(non-phone packet received, prefix=%s)\n", prefix);
+ return 0;
+ }
+
if (!KEY_OK(K_RAW_PKT, RAW) ||
!KEY_OK(K_RAW_PKTLEN, UINT32) ||
!KEY_OK(K_OOB_TIME_SEC, UINT32) ||
ulogd_log(ULOGD_ERROR, "SIP: Mandatory keys missing\n");
return 1;
}
-
- if (!KEY_OK(K_OOB_PREFIX, STRING) || strcasecmp(KEY_VAL(K_OOB_PREFIX).ptr, "phone"))
- {
- fprintf(siplog, "(non-phone packet received)\n");
- return 1;
- }
-
- byte *pkt = KEY_VAL(K_RAW_PKT).ptr;
- unsigned int len = KEY_VAL(K_RAW_PKTLEN).ui32;
- time_t t = KEY_VAL(K_OOB_TIME_SEC).ui32;
- struct tm *tm = localtime(&t);
+ pkt.raw = KEY_VAL(K_RAW_PKT).ptr;
+ pkt.raw_len = KEY_VAL(K_RAW_PKTLEN).ui32;
+ pkt.time_sec = KEY_VAL(K_OOB_TIME_SEC).ui32;
+ pkt.time_usec = KEY_VAL(K_OOB_TIME_USEC).ui32;
+ struct tm *tm = localtime(&pkt.time_sec);
if (!tm)
- fprintf(siplog, "<#%d> ", (int)t);
+ sprintf(pkt.time, "<#%d.%06d> ", (int)pkt.time_sec, pkt.time_usec);
else
- fprintf(siplog, "%04d-%02d-%02d %02d:%02d:%02d.%06d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec, KEY_VAL(K_OOB_TIME_USEC).ui32);
- fprintf(siplog, " [%d bytes]\n", len);
-
- sip_parse_packet(pkt, len);
+ sprintf(pkt.time, "%04d-%02d-%02d %02d:%02d:%02d.%06d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, pkt.time_usec);
+ if (debuglog)
+ fprintf(debuglog, "%s\n", pkt.time);
+ parse_ip(&pkt);
fflush(siplog);
+ if (debuglog)
+ fflush(debuglog);
return 0;
}
ulogd_log(ULOGD_ERROR, "SIP: Parsing of configuration file failed\n");
return 1;
}
- ulogd_log(ULOGD_INFO, "Initializing SIP plugin, logging to %s\n", sip_cf_log.u.string);
+ ulogd_log(ULOGD_INFO, "Initializing SIP plugin, logging to %s\n", sip_cf_debug_log.u.string);
siplog = fopen(sip_cf_log.u.string, "a");
if (!siplog)
{
ulogd_log(ULOGD_ERROR, "Cannot open %s: %s", sip_cf_log.u.string, strerror(errno));
return 1;
}
+ if (sip_cf_debug_log.u.string[0])
+ {
+ debuglog = fopen(sip_cf_debug_log.u.string, "a");
+ if (!debuglog)
+ {
+ ulogd_log(ULOGD_ERROR, "Cannot open %s: %s", sip_cf_debug_log.u.string, strerror(errno));
+ return 1;
+ }
+ }
return 0;
}
// Logging does not work here
// ulogd_log(ULOGD_INFO, "siplog fini\n");
fclose(siplog);
+ fclose(debuglog);
+ siplog = debuglog = NULL;
}
static void sip_signal(int sig)
{
if (sig == SIGHUP)
{
- // FIXME: Should reopen the log file here
+ // FIXME: Should reopen the log files here
}
}