131921Sbrian/*-
231921Sbrian * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
331921Sbrian * All rights reserved.
431921Sbrian *
531921Sbrian * Redistribution and use in source and binary forms, with or without
631921Sbrian * modification, are permitted provided that the following conditions
731921Sbrian * are met:
831921Sbrian * 1. Redistributions of source code must retain the above copyright
931921Sbrian *    notice, this list of conditions and the following disclaimer.
1031921Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1131921Sbrian *    notice, this list of conditions and the following disclaimer in the
1231921Sbrian *    documentation and/or other materials provided with the distribution.
1331921Sbrian *
1431921Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1531921Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1631921Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1731921Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1831921Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1931921Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2031921Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2131921Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2231921Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2331921Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2431921Sbrian * SUCH DAMAGE.
2531921Sbrian *
2650479Speter * $FreeBSD: releng/11.0/usr.sbin/ppp/log.c 218397 2011-02-07 11:18:18Z brian $
2730715Sbrian */
2830715Sbrian
2936285Sbrian#include <sys/types.h>
3030715Sbrian
3147672Sbrian#include <ctype.h>
326735Samurai#include <stdarg.h>
336735Samurai#include <stdio.h>
3434537Sbrian#include <string.h>
3530715Sbrian#include <syslog.h>
3636285Sbrian#include <termios.h>
3730715Sbrian
3837009Sbrian#include "defs.h"
3931343Sbrian#include "command.h"
4026516Sbrian#include "mbuf.h"
4126516Sbrian#include "log.h"
4236285Sbrian#include "descriptor.h"
4336285Sbrian#include "prompt.h"
446059Samurai
4581634Sbrianstatic const char *const LogNames[] = {
4628679Sbrian  "Async",
4738174Sbrian  "CBCP",
4828679Sbrian  "CCP",
4928679Sbrian  "Chat",
5028679Sbrian  "Command",
5128679Sbrian  "Connect",
5228679Sbrian  "Debug",
5358033Sbrian  "DNS",
5465181Sbrian  "Filter",			/* Log discarded packets */
5528679Sbrian  "HDLC",
5631061Sbrian  "ID0",
5728679Sbrian  "IPCP",
5881634Sbrian  "IPV6CP",
5928679Sbrian  "LCP",
6028679Sbrian  "LQM",
6128679Sbrian  "Phase",
6247061Sbrian  "Physical",
63132273Sbrian  "Radius",
6447061Sbrian  "Sync",
6528679Sbrian  "TCP/IP",
6636285Sbrian  "Timer",
6728679Sbrian  "Tun",
6828679Sbrian  "Warning",
6928679Sbrian  "Error",
7028679Sbrian  "Alert"
7126516Sbrian};
726059Samurai
7326516Sbrian#define MSK(n) (1<<((n)-1))
746059Samurai
7536285Sbrianstatic u_long LogMask = MSK(LogPHASE);
7630913Sbrianstatic u_long LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
7726516Sbrianstatic int LogTunno = -1;
78218397Sbrianstatic const char *LogIfaceName;
7936314Sbrianstatic struct prompt *promptlist;	/* Where to log local stuff */
8051005Sbrianstruct prompt *log_PromptContext;
8138013Sbrianint log_PromptListChanged;
826059Samurai
8336314Sbrianstruct prompt *
8436314Sbrianlog_PromptList()
8536314Sbrian{
8636314Sbrian  return promptlist;
8736314Sbrian}
8836314Sbrian
8936285Sbrianvoid
9036285Sbrianlog_RegisterPrompt(struct prompt *prompt)
9136285Sbrian{
9236314Sbrian  prompt->next = promptlist;
9336314Sbrian  promptlist = prompt;
9436314Sbrian  prompt->active = 1;
9536314Sbrian  log_DiscardAllLocal(&prompt->logmask);
9636285Sbrian}
9736285Sbrian
9836314Sbrianvoid
9936314Sbrianlog_ActivatePrompt(struct prompt *prompt)
10036314Sbrian{
10136314Sbrian  prompt->active = 1;
10236314Sbrian  LogMaskLocal |= prompt->logmask;
10336314Sbrian}
10436314Sbrian
10536285Sbrianstatic void
10636285SbrianLogSetMaskLocal(void)
10736285Sbrian{
10836285Sbrian  struct prompt *p;
10936285Sbrian
11036285Sbrian  LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
11136314Sbrian  for (p = promptlist; p; p = p->next)
11236285Sbrian    LogMaskLocal |= p->logmask;
11336285Sbrian}
11436285Sbrian
11536285Sbrianvoid
11636314Sbrianlog_DeactivatePrompt(struct prompt *prompt)
11736314Sbrian{
11836314Sbrian  if (prompt->active) {
11936314Sbrian    prompt->active = 0;
12036314Sbrian    LogSetMaskLocal();
12136314Sbrian  }
12236314Sbrian}
12336314Sbrian
12436314Sbrianvoid
12536285Sbrianlog_UnRegisterPrompt(struct prompt *prompt)
12636285Sbrian{
12736285Sbrian  if (prompt) {
12836285Sbrian    struct prompt **p;
12936285Sbrian
13036314Sbrian    for (p = &promptlist; *p; p = &(*p)->next)
13136285Sbrian      if (*p == prompt) {
13236314Sbrian        *p = prompt->next;
13336314Sbrian        prompt->next = NULL;
13436285Sbrian        break;
13536285Sbrian      }
13636285Sbrian    LogSetMaskLocal();
13738013Sbrian    log_PromptListChanged++;
13836285Sbrian  }
13936285Sbrian}
14036285Sbrian
14136314Sbrianvoid
14236314Sbrianlog_DestroyPrompts(struct server *s)
14336314Sbrian{
14444544Sbrian  struct prompt *p, *pn, *pl;
14536314Sbrian
14636314Sbrian  p = promptlist;
14744544Sbrian  pl = NULL;
14836314Sbrian  while (p) {
14936314Sbrian    pn = p->next;
15044544Sbrian    if (s && p->owner == s) {
15144544Sbrian      if (pl)
15244544Sbrian        pl->next = p->next;
15344544Sbrian      else
15444544Sbrian        promptlist = p->next;
15536314Sbrian      p->next = NULL;
15636314Sbrian      prompt_Destroy(p, 1);
15744544Sbrian    } else
15844544Sbrian      pl = p;
15936314Sbrian    p = pn;
16036314Sbrian  }
16136314Sbrian}
16236314Sbrian
16336314Sbrianvoid
16436314Sbrianlog_DisplayPrompts()
16536314Sbrian{
16636314Sbrian  struct prompt *p;
16736314Sbrian
16836314Sbrian  for (p = promptlist; p; p = p->next)
16936314Sbrian    prompt_Required(p);
17036314Sbrian}
17136314Sbrian
17236314Sbrianvoid
17338200Sbrianlog_WritePrompts(struct datalink *dl, const char *fmt,...)
17436314Sbrian{
17538200Sbrian  va_list ap;
17636314Sbrian  struct prompt *p;
17736314Sbrian
17838200Sbrian  va_start(ap, fmt);
17936314Sbrian  for (p = promptlist; p; p = p->next)
18036314Sbrian    if (prompt_IsTermMode(p, dl))
18138200Sbrian      prompt_vPrintf(p, fmt, ap);
18238200Sbrian  va_end(ap);
18336314Sbrian}
18436314Sbrian
18536314Sbrianvoid
18636314Sbrianlog_SetTtyCommandMode(struct datalink *dl)
18736314Sbrian{
18836314Sbrian  struct prompt *p;
18936314Sbrian
19036314Sbrian  for (p = promptlist; p; p = p->next)
19136314Sbrian    if (prompt_IsTermMode(p, dl))
19236314Sbrian      prompt_TtyCommandMode(p);
19336314Sbrian}
19436314Sbrian
19526516Sbrianstatic int
19626516SbriansyslogLevel(int lev)
1976059Samurai{
19828679Sbrian  switch (lev) {
19985991Sbrian  case LogLOG:
20085991Sbrian    return LOG_INFO;
20136285Sbrian  case LogDEBUG:
20236285Sbrian  case LogTIMER:
20336285Sbrian    return LOG_DEBUG;
20428679Sbrian  case LogWARN:
20528679Sbrian    return LOG_WARNING;
20628679Sbrian  case LogERROR:
20728679Sbrian    return LOG_ERR;
20828679Sbrian  case LogALERT:
20928679Sbrian    return LOG_ALERT;
21028679Sbrian  }
21128679Sbrian  return lev >= LogMIN && lev <= LogMAX ? LOG_INFO : 0;
2126059Samurai}
2136059Samurai
21426516Sbrianconst char *
21536285Sbrianlog_Name(int id)
2166059Samurai{
21785991Sbrian  if (id == LogLOG)
21885991Sbrian    return "LOG";
21928679Sbrian  return id < LogMIN || id > LogMAX ? "Unknown" : LogNames[id - 1];
2206059Samurai}
2216059Samurai
2226059Samuraivoid
22336285Sbrianlog_Keep(int id)
2246059Samurai{
22528679Sbrian  if (id >= LogMIN && id <= LogMAXCONF)
22628679Sbrian    LogMask |= MSK(id);
2276059Samurai}
2286059Samurai
2296059Samuraivoid
23036285Sbrianlog_KeepLocal(int id, u_long *mask)
23130913Sbrian{
23236285Sbrian  if (id >= LogMIN && id <= LogMAXCONF) {
23330913Sbrian    LogMaskLocal |= MSK(id);
23436285Sbrian    *mask |= MSK(id);
23536285Sbrian  }
23630913Sbrian}
23730913Sbrian
23830913Sbrianvoid
23936285Sbrianlog_Discard(int id)
2406059Samurai{
24128679Sbrian  if (id >= LogMIN && id <= LogMAXCONF)
24228679Sbrian    LogMask &= ~MSK(id);
2436059Samurai}
2446059Samurai
2456059Samuraivoid
24636285Sbrianlog_DiscardLocal(int id, u_long *mask)
24730913Sbrian{
24836285Sbrian  if (id >= LogMIN && id <= LogMAXCONF) {
24936285Sbrian    *mask &= ~MSK(id);
25036285Sbrian    LogSetMaskLocal();
25136285Sbrian  }
25230913Sbrian}
25330913Sbrian
25430913Sbrianvoid
25536285Sbrianlog_DiscardAll()
2566059Samurai{
25728679Sbrian  LogMask = 0;
2586059Samurai}
2596059Samurai
26030913Sbrianvoid
26136285Sbrianlog_DiscardAllLocal(u_long *mask)
26230913Sbrian{
26336285Sbrian  *mask = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
26436285Sbrian  LogSetMaskLocal();
26530913Sbrian}
26630913Sbrian
26726516Sbrianint
26836285Sbrianlog_IsKept(int id)
2696735Samurai{
27085991Sbrian  if (id == LogLOG)
27185991Sbrian    return LOG_KEPT_SYSLOG;
27230913Sbrian  if (id < LogMIN || id > LogMAX)
27328679Sbrian    return 0;
27430913Sbrian  if (id > LogMAXCONF)
27530913Sbrian    return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
27630913Sbrian
27730913Sbrian  return ((LogMaskLocal & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
27830913Sbrian    ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
2796735Samurai}
2806735Samurai
28136285Sbrianint
28236285Sbrianlog_IsKeptLocal(int id, u_long mask)
28336285Sbrian{
28436285Sbrian  if (id < LogMIN || id > LogMAX)
28536285Sbrian    return 0;
28636285Sbrian  if (id > LogMAXCONF)
28736285Sbrian    return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
28836285Sbrian
28936285Sbrian  return ((mask & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
29036285Sbrian    ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
29136285Sbrian}
29236285Sbrian
29326516Sbrianvoid
29436285Sbrianlog_Open(const char *Name)
2956059Samurai{
29628679Sbrian  openlog(Name, LOG_PID, LOG_DAEMON);
2976059Samurai}
2986059Samurai
2996059Samuraivoid
300218397Sbrianlog_SetTun(int tunno, const char *ifaceName)
3016735Samurai{
30228679Sbrian  LogTunno = tunno;
303218397Sbrian  LogIfaceName = ifaceName;
3046735Samurai}
3056735Samurai
3066735Samuraivoid
30736285Sbrianlog_Close()
3086059Samurai{
30928679Sbrian  closelog();
31028679Sbrian  LogTunno = -1;
311218397Sbrian  LogIfaceName = NULL;
3126059Samurai}
3136059Samurai
3146059Samuraivoid
31536285Sbrianlog_Printf(int lev, const char *fmt,...)
3166059Samurai{
31728679Sbrian  va_list ap;
31836285Sbrian  struct prompt *prompt;
3196059Samurai
32036285Sbrian  if (log_IsKept(lev)) {
32137010Sbrian    char nfmt[200];
32228679Sbrian
32378444Sbrian    va_start(ap, fmt);
32451005Sbrian    if (promptlist && (log_IsKept(lev) & LOG_KEPT_LOCAL)) {
325218397Sbrian      if ((log_IsKept(LogTUN) & LOG_KEPT_LOCAL) && LogTunno != -1) {
326218397Sbrian        if (LogIfaceName)
327218397Sbrian          snprintf(nfmt, sizeof nfmt, "%s%d(%s): %s: %s", TUN_NAME,
328218397Sbrian	         LogTunno, LogIfaceName, log_Name(lev), fmt);
329218397Sbrian        else
330218397Sbrian          snprintf(nfmt, sizeof nfmt, "%s%d: %s: %s", TUN_NAME,
33136285Sbrian	         LogTunno, log_Name(lev), fmt);
332218397Sbrian      } else
33336285Sbrian        snprintf(nfmt, sizeof nfmt, "%s: %s", log_Name(lev), fmt);
33498243Sbrian
33551005Sbrian      if (log_PromptContext && lev == LogWARN)
33651005Sbrian        /* Warnings just go to the current prompt */
33751005Sbrian        prompt_vPrintf(log_PromptContext, nfmt, ap);
33851005Sbrian      else for (prompt = promptlist; prompt; prompt = prompt->next)
33936285Sbrian        if (lev > LogMAXCONF || (prompt->logmask & MSK(lev)))
34036285Sbrian          prompt_vPrintf(prompt, nfmt, ap);
34130913Sbrian    }
34278444Sbrian    va_end(ap);
34330913Sbrian
34478444Sbrian    va_start(ap, fmt);
34536314Sbrian    if ((log_IsKept(lev) & LOG_KEPT_SYSLOG) &&
34651005Sbrian        (lev != LogWARN || !log_PromptContext)) {
347218397Sbrian      if ((log_IsKept(LogTUN) & LOG_KEPT_SYSLOG) && LogTunno != -1) {
348218397Sbrian        if (LogIfaceName)
349218397Sbrian          snprintf(nfmt, sizeof nfmt, "%s%d(%s): %s: %s", TUN_NAME,
350218397Sbrian	         LogTunno, LogIfaceName, log_Name(lev), fmt);
351218397Sbrian        else
352218397Sbrian          snprintf(nfmt, sizeof nfmt, "%s%d: %s: %s", TUN_NAME,
35336285Sbrian	         LogTunno, log_Name(lev), fmt);
354218397Sbrian      } else
35536285Sbrian        snprintf(nfmt, sizeof nfmt, "%s: %s", log_Name(lev), fmt);
35628679Sbrian      vsyslog(syslogLevel(lev), nfmt, ap);
35730913Sbrian    }
35878444Sbrian    va_end(ap);
35928679Sbrian  }
3606059Samurai}
3616059Samurai
3626059Samuraivoid
36338472Sbrianlog_DumpBp(int lev, const char *hdr, const struct mbuf *bp)
3646059Samurai{
36536285Sbrian  if (log_IsKept(lev)) {
36647672Sbrian    char buf[68];
36747672Sbrian    char *b, *c;
36838472Sbrian    const u_char *ptr;
36930641Sbrian    int f;
37030641Sbrian
37130641Sbrian    if (hdr && *hdr)
37236285Sbrian      log_Printf(lev, "%s\n", hdr);
37330641Sbrian
37430641Sbrian    b = buf;
37547672Sbrian    c = b + 50;
37630641Sbrian    do {
37754912Sbrian      f = bp->m_len;
37838472Sbrian      ptr = CONST_MBUF_CTOP(bp);
37930641Sbrian      while (f--) {
38047672Sbrian	sprintf(b, " %02x", (int) *ptr);
38147672Sbrian        *c++ = isprint(*ptr) ? *ptr : '.';
38247672Sbrian        ptr++;
38330641Sbrian        b += 3;
38447672Sbrian        if (b == buf + 48) {
38547672Sbrian          memset(b, ' ', 2);
38649527Sbrian          *c = '\0';
38749527Sbrian          log_Printf(lev, "%s\n", buf);
38830641Sbrian          b = buf;
38947672Sbrian          c = b + 50;
39030641Sbrian        }
39130641Sbrian      }
39254912Sbrian    } while ((bp = bp->m_next) != NULL);
39330641Sbrian
39430913Sbrian    if (b > buf) {
39547672Sbrian      memset(b, ' ', 50 - (b - buf));
39649527Sbrian      *c = '\0';
39749527Sbrian      log_Printf(lev, "%s\n", buf);
39830913Sbrian    }
39930641Sbrian  }
4006059Samurai}
4016059Samurai
4026059Samuraivoid
40346828Sbrianlog_DumpBuff(int lev, const char *hdr, const u_char *ptr, int n)
4046059Samurai{
40536285Sbrian  if (log_IsKept(lev)) {
40647672Sbrian    char buf[68];
40747672Sbrian    char *b, *c;
4086735Samurai
40928679Sbrian    if (hdr && *hdr)
41036285Sbrian      log_Printf(lev, "%s\n", hdr);
41128679Sbrian    while (n > 0) {
41228679Sbrian      b = buf;
41347672Sbrian      c = b + 50;
41447672Sbrian      for (b = buf; b != buf + 48 && n--; b += 3, ptr++) {
41547672Sbrian	sprintf(b, " %02x", (int) *ptr);
41647672Sbrian        *c++ = isprint(*ptr) ? *ptr : '.';
41747672Sbrian      }
41847672Sbrian      memset(b, ' ', 50 - (b - buf));
41949527Sbrian      *c = '\0';
42049527Sbrian      log_Printf(lev, "%s\n", buf);
42126516Sbrian    }
42228679Sbrian  }
4236059Samurai}
42436285Sbrian
42536285Sbrianint
42636285Sbrianlog_ShowLevel(struct cmdargs const *arg)
42736285Sbrian{
42836285Sbrian  int i;
42936285Sbrian
43036285Sbrian  prompt_Printf(arg->prompt, "Log:  ");
43136285Sbrian  for (i = LogMIN; i <= LogMAX; i++)
43236285Sbrian    if (log_IsKept(i) & LOG_KEPT_SYSLOG)
43336285Sbrian      prompt_Printf(arg->prompt, " %s", log_Name(i));
43436285Sbrian
43536285Sbrian  prompt_Printf(arg->prompt, "\nLocal:");
43636285Sbrian  for (i = LogMIN; i <= LogMAX; i++)
43736285Sbrian    if (log_IsKeptLocal(i, arg->prompt->logmask) & LOG_KEPT_LOCAL)
43836285Sbrian      prompt_Printf(arg->prompt, " %s", log_Name(i));
43936285Sbrian
44036285Sbrian  prompt_Printf(arg->prompt, "\n");
44136285Sbrian
44236285Sbrian  return 0;
44336285Sbrian}
44436285Sbrian
44536285Sbrianint
44636285Sbrianlog_SetLevel(struct cmdargs const *arg)
44736285Sbrian{
44836285Sbrian  int i, res, argc, local;
44936285Sbrian  char const *const *argv, *argp;
45036285Sbrian
45136285Sbrian  argc = arg->argc - arg->argn;
45236285Sbrian  argv = arg->argv + arg->argn;
45336285Sbrian  res = 0;
45436285Sbrian
45536285Sbrian  if (argc == 0 || strcasecmp(argv[0], "local"))
45636285Sbrian    local = 0;
45736285Sbrian  else {
45836285Sbrian    if (arg->prompt == NULL) {
45967912Sbrian      log_Printf(LogWARN, "set log local: Only available on the"
46067912Sbrian                 " command line\n");
46136285Sbrian      return 1;
46236285Sbrian    }
46336285Sbrian    argc--;
46436285Sbrian    argv++;
46536285Sbrian    local = 1;
46636285Sbrian  }
46736285Sbrian
46836285Sbrian  if (argc == 0 || (argv[0][0] != '+' && argv[0][0] != '-')) {
46936285Sbrian    if (local)
47036285Sbrian      log_DiscardAllLocal(&arg->prompt->logmask);
47136285Sbrian    else
47236285Sbrian      log_DiscardAll();
47336285Sbrian  }
47436285Sbrian
47536285Sbrian  while (argc--) {
47636285Sbrian    argp = **argv == '+' || **argv == '-' ? *argv + 1 : *argv;
47767916Sbrian    /* Special case 'all' */
47867916Sbrian    if (strcasecmp(argp, "all") == 0) {
47967916Sbrian        if (**argv == '-') {
48067916Sbrian          if (local)
48167916Sbrian            for (i = LogMIN; i <= LogMAX; i++)
48267916Sbrian              log_DiscardLocal(i, &arg->prompt->logmask);
48367916Sbrian          else
48467916Sbrian            for (i = LogMIN; i <= LogMAX; i++)
48567916Sbrian              log_Discard(i);
48667916Sbrian        } else if (local)
48767916Sbrian          for (i = LogMIN; i <= LogMAX; i++)
48867916Sbrian            log_KeepLocal(i, &arg->prompt->logmask);
48967916Sbrian        else
49067916Sbrian          for (i = LogMIN; i <= LogMAX; i++)
49167916Sbrian            log_Keep(i);
49267916Sbrian        argv++;
49367916Sbrian        continue;
49467916Sbrian    }
49536285Sbrian    for (i = LogMIN; i <= LogMAX; i++)
49636285Sbrian      if (strcasecmp(argp, log_Name(i)) == 0) {
49736285Sbrian	if (**argv == '-') {
49836285Sbrian          if (local)
49936285Sbrian            log_DiscardLocal(i, &arg->prompt->logmask);
50036285Sbrian          else
50136285Sbrian	    log_Discard(i);
50236285Sbrian	} else if (local)
50336285Sbrian          log_KeepLocal(i, &arg->prompt->logmask);
50436285Sbrian        else
50536285Sbrian          log_Keep(i);
50636285Sbrian	break;
50736285Sbrian      }
50836285Sbrian    if (i > LogMAX) {
50936285Sbrian      log_Printf(LogWARN, "%s: Invalid log value\n", argp);
51036285Sbrian      res = -1;
51136285Sbrian    }
51236285Sbrian    argv++;
51336285Sbrian  }
51436285Sbrian  return res;
51536285Sbrian}
51636285Sbrian
51736285Sbrianint
51836285Sbrianlog_ShowWho(struct cmdargs const *arg)
51936285Sbrian{
52036285Sbrian  struct prompt *p;
52136285Sbrian
52236314Sbrian  for (p = promptlist; p; p = p->next) {
52336285Sbrian    prompt_Printf(arg->prompt, "%s (%s)", p->src.type, p->src.from);
52436285Sbrian    if (p == arg->prompt)
52536285Sbrian      prompt_Printf(arg->prompt, " *");
52636285Sbrian    if (!p->active)
52736285Sbrian      prompt_Printf(arg->prompt, " ^Z");
52836285Sbrian    prompt_Printf(arg->prompt, "\n");
52936285Sbrian  }
53036285Sbrian
53136285Sbrian  return 0;
53236285Sbrian}
533