1/*-
2 * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <sys/types.h>
30
31#include <ctype.h>
32#include <stdarg.h>
33#include <stdio.h>
34#include <string.h>
35#include <syslog.h>
36#include <termios.h>
37
38#include "defs.h"
39#include "command.h"
40#include "mbuf.h"
41#include "log.h"
42#include "descriptor.h"
43#include "prompt.h"
44
45static const char *const LogNames[] = {
46  "Async",
47  "CBCP",
48  "CCP",
49  "Chat",
50  "Command",
51  "Connect",
52  "Debug",
53  "DNS",
54  "Filter",			/* Log discarded packets */
55  "HDLC",
56  "ID0",
57  "IPCP",
58  "IPV6CP",
59  "LCP",
60  "LQM",
61  "Phase",
62  "Physical",
63  "Radius",
64  "Sync",
65  "TCP/IP",
66  "Timer",
67  "Tun",
68  "Warning",
69  "Error",
70  "Alert"
71};
72
73#define MSK(n) (1<<((n)-1))
74
75static u_long LogMask = MSK(LogPHASE);
76static u_long LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
77static int LogTunno = -1;
78static const char *LogIfaceName;
79static struct prompt *promptlist;	/* Where to log local stuff */
80struct prompt *log_PromptContext;
81int log_PromptListChanged;
82
83struct prompt *
84log_PromptList()
85{
86  return promptlist;
87}
88
89void
90log_RegisterPrompt(struct prompt *prompt)
91{
92  prompt->next = promptlist;
93  promptlist = prompt;
94  prompt->active = 1;
95  log_DiscardAllLocal(&prompt->logmask);
96}
97
98void
99log_ActivatePrompt(struct prompt *prompt)
100{
101  prompt->active = 1;
102  LogMaskLocal |= prompt->logmask;
103}
104
105static void
106LogSetMaskLocal(void)
107{
108  struct prompt *p;
109
110  LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
111  for (p = promptlist; p; p = p->next)
112    LogMaskLocal |= p->logmask;
113}
114
115void
116log_DeactivatePrompt(struct prompt *prompt)
117{
118  if (prompt->active) {
119    prompt->active = 0;
120    LogSetMaskLocal();
121  }
122}
123
124void
125log_UnRegisterPrompt(struct prompt *prompt)
126{
127  if (prompt) {
128    struct prompt **p;
129
130    for (p = &promptlist; *p; p = &(*p)->next)
131      if (*p == prompt) {
132        *p = prompt->next;
133        prompt->next = NULL;
134        break;
135      }
136    LogSetMaskLocal();
137    log_PromptListChanged++;
138  }
139}
140
141void
142log_DestroyPrompts(struct server *s)
143{
144  struct prompt *p, *pn, *pl;
145
146  p = promptlist;
147  pl = NULL;
148  while (p) {
149    pn = p->next;
150    if (s && p->owner == s) {
151      if (pl)
152        pl->next = p->next;
153      else
154        promptlist = p->next;
155      p->next = NULL;
156      prompt_Destroy(p, 1);
157    } else
158      pl = p;
159    p = pn;
160  }
161}
162
163void
164log_DisplayPrompts()
165{
166  struct prompt *p;
167
168  for (p = promptlist; p; p = p->next)
169    prompt_Required(p);
170}
171
172void
173log_WritePrompts(struct datalink *dl, const char *fmt,...)
174{
175  va_list ap;
176  struct prompt *p;
177
178  va_start(ap, fmt);
179  for (p = promptlist; p; p = p->next)
180    if (prompt_IsTermMode(p, dl))
181      prompt_vPrintf(p, fmt, ap);
182  va_end(ap);
183}
184
185void
186log_SetTtyCommandMode(struct datalink *dl)
187{
188  struct prompt *p;
189
190  for (p = promptlist; p; p = p->next)
191    if (prompt_IsTermMode(p, dl))
192      prompt_TtyCommandMode(p);
193}
194
195static int
196syslogLevel(int lev)
197{
198  switch (lev) {
199  case LogLOG:
200    return LOG_INFO;
201  case LogDEBUG:
202  case LogTIMER:
203    return LOG_DEBUG;
204  case LogWARN:
205    return LOG_WARNING;
206  case LogERROR:
207    return LOG_ERR;
208  case LogALERT:
209    return LOG_ALERT;
210  }
211  return lev >= LogMIN && lev <= LogMAX ? LOG_INFO : 0;
212}
213
214const char *
215log_Name(int id)
216{
217  if (id == LogLOG)
218    return "LOG";
219  return id < LogMIN || id > LogMAX ? "Unknown" : LogNames[id - 1];
220}
221
222void
223log_Keep(int id)
224{
225  if (id >= LogMIN && id <= LogMAXCONF)
226    LogMask |= MSK(id);
227}
228
229void
230log_KeepLocal(int id, u_long *mask)
231{
232  if (id >= LogMIN && id <= LogMAXCONF) {
233    LogMaskLocal |= MSK(id);
234    *mask |= MSK(id);
235  }
236}
237
238void
239log_Discard(int id)
240{
241  if (id >= LogMIN && id <= LogMAXCONF)
242    LogMask &= ~MSK(id);
243}
244
245void
246log_DiscardLocal(int id, u_long *mask)
247{
248  if (id >= LogMIN && id <= LogMAXCONF) {
249    *mask &= ~MSK(id);
250    LogSetMaskLocal();
251  }
252}
253
254void
255log_DiscardAll()
256{
257  LogMask = 0;
258}
259
260void
261log_DiscardAllLocal(u_long *mask)
262{
263  *mask = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
264  LogSetMaskLocal();
265}
266
267int
268log_IsKept(int id)
269{
270  if (id == LogLOG)
271    return LOG_KEPT_SYSLOG;
272  if (id < LogMIN || id > LogMAX)
273    return 0;
274  if (id > LogMAXCONF)
275    return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
276
277  return ((LogMaskLocal & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
278    ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
279}
280
281int
282log_IsKeptLocal(int id, u_long mask)
283{
284  if (id < LogMIN || id > LogMAX)
285    return 0;
286  if (id > LogMAXCONF)
287    return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
288
289  return ((mask & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
290    ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
291}
292
293void
294log_Open(const char *Name)
295{
296  openlog(Name, LOG_PID, LOG_DAEMON);
297}
298
299void
300log_SetTun(int tunno, const char *ifaceName)
301{
302  LogTunno = tunno;
303  LogIfaceName = ifaceName;
304}
305
306void
307log_Close()
308{
309  closelog();
310  LogTunno = -1;
311  LogIfaceName = NULL;
312}
313
314void
315log_Printf(int lev, const char *fmt,...)
316{
317  va_list ap;
318  struct prompt *prompt;
319
320  if (log_IsKept(lev)) {
321    char nfmt[200];
322
323    va_start(ap, fmt);
324    if (promptlist && (log_IsKept(lev) & LOG_KEPT_LOCAL)) {
325      if ((log_IsKept(LogTUN) & LOG_KEPT_LOCAL) && LogTunno != -1) {
326        if (LogIfaceName)
327          snprintf(nfmt, sizeof nfmt, "%s%d(%s): %s: %s", TUN_NAME,
328	         LogTunno, LogIfaceName, log_Name(lev), fmt);
329        else
330          snprintf(nfmt, sizeof nfmt, "%s%d: %s: %s", TUN_NAME,
331	         LogTunno, log_Name(lev), fmt);
332      } else
333        snprintf(nfmt, sizeof nfmt, "%s: %s", log_Name(lev), fmt);
334
335      if (log_PromptContext && lev == LogWARN)
336        /* Warnings just go to the current prompt */
337        prompt_vPrintf(log_PromptContext, nfmt, ap);
338      else for (prompt = promptlist; prompt; prompt = prompt->next)
339        if (lev > LogMAXCONF || (prompt->logmask & MSK(lev)))
340          prompt_vPrintf(prompt, nfmt, ap);
341    }
342    va_end(ap);
343
344    va_start(ap, fmt);
345    if ((log_IsKept(lev) & LOG_KEPT_SYSLOG) &&
346        (lev != LogWARN || !log_PromptContext)) {
347      if ((log_IsKept(LogTUN) & LOG_KEPT_SYSLOG) && LogTunno != -1) {
348        if (LogIfaceName)
349          snprintf(nfmt, sizeof nfmt, "%s%d(%s): %s: %s", TUN_NAME,
350	         LogTunno, LogIfaceName, log_Name(lev), fmt);
351        else
352          snprintf(nfmt, sizeof nfmt, "%s%d: %s: %s", TUN_NAME,
353	         LogTunno, log_Name(lev), fmt);
354      } else
355        snprintf(nfmt, sizeof nfmt, "%s: %s", log_Name(lev), fmt);
356      vsyslog(syslogLevel(lev), nfmt, ap);
357    }
358    va_end(ap);
359  }
360}
361
362void
363log_DumpBp(int lev, const char *hdr, const struct mbuf *bp)
364{
365  if (log_IsKept(lev)) {
366    char buf[68];
367    char *b, *c;
368    const u_char *ptr;
369    int f;
370
371    if (hdr && *hdr)
372      log_Printf(lev, "%s\n", hdr);
373
374    b = buf;
375    c = b + 50;
376    do {
377      f = bp->m_len;
378      ptr = CONST_MBUF_CTOP(bp);
379      while (f--) {
380	sprintf(b, " %02x", (int) *ptr);
381        *c++ = isprint(*ptr) ? *ptr : '.';
382        ptr++;
383        b += 3;
384        if (b == buf + 48) {
385          memset(b, ' ', 2);
386          *c = '\0';
387          log_Printf(lev, "%s\n", buf);
388          b = buf;
389          c = b + 50;
390        }
391      }
392    } while ((bp = bp->m_next) != NULL);
393
394    if (b > buf) {
395      memset(b, ' ', 50 - (b - buf));
396      *c = '\0';
397      log_Printf(lev, "%s\n", buf);
398    }
399  }
400}
401
402void
403log_DumpBuff(int lev, const char *hdr, const u_char *ptr, int n)
404{
405  if (log_IsKept(lev)) {
406    char buf[68];
407    char *b, *c;
408
409    if (hdr && *hdr)
410      log_Printf(lev, "%s\n", hdr);
411    while (n > 0) {
412      b = buf;
413      c = b + 50;
414      for (b = buf; b != buf + 48 && n--; b += 3, ptr++) {
415	sprintf(b, " %02x", (int) *ptr);
416        *c++ = isprint(*ptr) ? *ptr : '.';
417      }
418      memset(b, ' ', 50 - (b - buf));
419      *c = '\0';
420      log_Printf(lev, "%s\n", buf);
421    }
422  }
423}
424
425int
426log_ShowLevel(struct cmdargs const *arg)
427{
428  int i;
429
430  prompt_Printf(arg->prompt, "Log:  ");
431  for (i = LogMIN; i <= LogMAX; i++)
432    if (log_IsKept(i) & LOG_KEPT_SYSLOG)
433      prompt_Printf(arg->prompt, " %s", log_Name(i));
434
435  prompt_Printf(arg->prompt, "\nLocal:");
436  for (i = LogMIN; i <= LogMAX; i++)
437    if (log_IsKeptLocal(i, arg->prompt->logmask) & LOG_KEPT_LOCAL)
438      prompt_Printf(arg->prompt, " %s", log_Name(i));
439
440  prompt_Printf(arg->prompt, "\n");
441
442  return 0;
443}
444
445int
446log_SetLevel(struct cmdargs const *arg)
447{
448  int i, res, argc, local;
449  char const *const *argv, *argp;
450
451  argc = arg->argc - arg->argn;
452  argv = arg->argv + arg->argn;
453  res = 0;
454
455  if (argc == 0 || strcasecmp(argv[0], "local"))
456    local = 0;
457  else {
458    if (arg->prompt == NULL) {
459      log_Printf(LogWARN, "set log local: Only available on the"
460                 " command line\n");
461      return 1;
462    }
463    argc--;
464    argv++;
465    local = 1;
466  }
467
468  if (argc == 0 || (argv[0][0] != '+' && argv[0][0] != '-')) {
469    if (local)
470      log_DiscardAllLocal(&arg->prompt->logmask);
471    else
472      log_DiscardAll();
473  }
474
475  while (argc--) {
476    argp = **argv == '+' || **argv == '-' ? *argv + 1 : *argv;
477    /* Special case 'all' */
478    if (strcasecmp(argp, "all") == 0) {
479        if (**argv == '-') {
480          if (local)
481            for (i = LogMIN; i <= LogMAX; i++)
482              log_DiscardLocal(i, &arg->prompt->logmask);
483          else
484            for (i = LogMIN; i <= LogMAX; i++)
485              log_Discard(i);
486        } else if (local)
487          for (i = LogMIN; i <= LogMAX; i++)
488            log_KeepLocal(i, &arg->prompt->logmask);
489        else
490          for (i = LogMIN; i <= LogMAX; i++)
491            log_Keep(i);
492        argv++;
493        continue;
494    }
495    for (i = LogMIN; i <= LogMAX; i++)
496      if (strcasecmp(argp, log_Name(i)) == 0) {
497	if (**argv == '-') {
498          if (local)
499            log_DiscardLocal(i, &arg->prompt->logmask);
500          else
501	    log_Discard(i);
502	} else if (local)
503          log_KeepLocal(i, &arg->prompt->logmask);
504        else
505          log_Keep(i);
506	break;
507      }
508    if (i > LogMAX) {
509      log_Printf(LogWARN, "%s: Invalid log value\n", argp);
510      res = -1;
511    }
512    argv++;
513  }
514  return res;
515}
516
517int
518log_ShowWho(struct cmdargs const *arg)
519{
520  struct prompt *p;
521
522  for (p = promptlist; p; p = p->next) {
523    prompt_Printf(arg->prompt, "%s (%s)", p->src.type, p->src.from);
524    if (p == arg->prompt)
525      prompt_Printf(arg->prompt, " *");
526    if (!p->active)
527      prompt_Printf(arg->prompt, " ^Z");
528    prompt_Printf(arg->prompt, "\n");
529  }
530
531  return 0;
532}
533