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