prompt.c revision 71657
1/*-
2 * Copyright (c) 1998 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: head/usr.sbin/ppp/prompt.c 71657 2001-01-26 01:41:34Z brian $
27 */
28
29#include <sys/param.h>
30#include <netinet/in.h>
31#include <netinet/in_systm.h>
32#include <netinet/ip.h>
33#include <sys/un.h>
34
35#include <errno.h>
36#include <stdarg.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/fcntl.h>
41#include <termios.h>
42#include <unistd.h>
43
44#include "layer.h"
45#include "defs.h"
46#include "timer.h"
47#include "command.h"
48#include "log.h"
49#include "descriptor.h"
50#include "prompt.h"
51#include "fsm.h"
52#include "auth.h"
53#include "iplist.h"
54#include "throughput.h"
55#include "slcompress.h"
56#include "mbuf.h"
57#include "lqr.h"
58#include "hdlc.h"
59#include "lcp.h"
60#include "ipcp.h"
61#include "filter.h"
62#include "async.h"
63#include "ccp.h"
64#include "link.h"
65#include "physical.h"
66#include "mp.h"
67#ifndef NORADIUS
68#include "radius.h"
69#endif
70#include "bundle.h"
71#include "chat.h"
72#include "chap.h"
73#include "cbcp.h"
74#include "datalink.h"
75#include "server.h"
76#include "main.h"
77
78static void
79prompt_Display(struct prompt *p)
80{
81  /* XXX: See Index2Nam() - should we only figure this out once ? */
82  static char shostname[MAXHOSTNAMELEN];
83  const char *pconnect, *pauth;
84
85  if (p->TermMode || !p->needprompt)
86    return;
87
88  p->needprompt = 0;
89
90  if (p->nonewline)
91    p->nonewline = 0;
92  else
93    fprintf(p->Term, "\n");
94
95  if (p->auth == LOCAL_AUTH)
96    pauth = " ON ";
97  else
98    pauth = " on ";
99
100  if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED)
101    pconnect = "PPP";
102  else if (bundle_Phase(p->bundle) == PHASE_NETWORK)
103    pconnect = "PPp";
104  else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE)
105    pconnect = "Ppp";
106  else
107    pconnect = "ppp";
108
109  if (*shostname == '\0') {
110    char *dot;
111
112    if (gethostname(shostname, sizeof shostname))
113      strcpy(shostname, "localhost");
114    else if ((dot = strchr(shostname, '.')))
115      *dot = '\0';
116  }
117
118  fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname);
119  fflush(p->Term);
120}
121
122static int
123prompt_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
124{
125  struct prompt *p = descriptor2prompt(d);
126  int sets;
127
128  sets = 0;
129
130  if (!p->active)
131    return sets;
132
133  if (p->fd_in >= 0) {
134    if (r) {
135      FD_SET(p->fd_in, r);
136      log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in);
137      sets++;
138    }
139    if (e) {
140      FD_SET(p->fd_in, e);
141      log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in);
142      sets++;
143    }
144    if (sets && *n < p->fd_in + 1)
145      *n = p->fd_in + 1;
146  }
147
148  prompt_Display(p);
149
150  return sets;
151}
152
153static int
154prompt_IsSet(struct fdescriptor *d, const fd_set *fdset)
155{
156  struct prompt *p = descriptor2prompt(d);
157  return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset);
158}
159
160
161static void
162prompt_ShowHelp(struct prompt *p)
163{
164  prompt_Printf(p, "The following commands are available:\n");
165  prompt_Printf(p, " ~p\tEnter Packet mode\n");
166  prompt_Printf(p, " ~t\tShow timers\n");
167  prompt_Printf(p, " ~m\tShow memory map\n");
168  prompt_Printf(p, " ~.\tTerminate program\n");
169  prompt_Printf(p, " ~?\tThis help\n");
170}
171
172static void
173prompt_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
174{
175  struct prompt *p = descriptor2prompt(d);
176  struct prompt *op;
177  int n;
178  char ch;
179  char linebuff[LINE_LEN];
180
181  if (p->TermMode == NULL) {
182    n = read(p->fd_in, linebuff, sizeof linebuff - 1);
183    if (n > 0) {
184      if (linebuff[n-1] == '\n')
185        linebuff[--n] = '\0';
186      else
187        linebuff[n] = '\0';
188      p->nonewline = 1;		/* Maybe command_Decode does a prompt */
189      prompt_Required(p);
190      if (n) {
191        if ((op = log_PromptContext) == NULL)
192          log_PromptContext = p;
193        if (!command_Decode(bundle, linebuff, n, p, p->src.from))
194          prompt_Printf(p, "Syntax error\n");
195        log_PromptContext = op;
196      }
197    } else if (n <= 0) {
198      log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from);
199      if (!p->owner)
200        Cleanup(EX_NORMAL);
201      prompt_Destroy(p, 0);
202    }
203    return;
204  }
205
206  switch (p->TermMode->state) {
207    case DATALINK_CLOSED:
208      prompt_Printf(p, "Link lost, terminal mode.\n");
209      prompt_TtyCommandMode(p);
210      p->nonewline = 0;
211      prompt_Required(p);
212      return;
213
214    case DATALINK_READY:
215      break;
216
217    case DATALINK_OPEN:
218      prompt_Printf(p, "\nPacket mode detected.\n");
219      prompt_TtyCommandMode(p);
220      p->nonewline = 0;
221      /* We'll get a prompt because of our status change */
222      /* Fall through */
223
224    default:
225      /* Wait 'till we're in a state we care about */
226      return;
227  }
228
229  /*
230   * We are in terminal mode, decode special sequences
231   */
232  n = read(p->fd_in, &ch, 1);
233  log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n);
234
235  if (n > 0) {
236    switch (p->readtilde) {
237    case 0:
238      if (ch == '~')
239        p->readtilde = 1;
240      else
241	if (physical_Write(p->TermMode->physical, &ch, n) < 0) {
242	  log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno));
243          prompt_TtyCommandMode(p);
244        }
245      break;
246    case 1:
247      switch (ch) {
248      case '?':
249	prompt_ShowHelp(p);
250	break;
251      case 'p':
252        datalink_Up(p->TermMode, 0, 1);
253        prompt_Printf(p, "\nPacket mode.\n");
254	prompt_TtyCommandMode(p);
255        break;
256      case '.':
257	prompt_TtyCommandMode(p);
258        p->nonewline = 0;
259        prompt_Required(p);
260	break;
261      case 't':
262	timer_Show(0, p);
263	break;
264      case 'm':
265        {
266          struct cmdargs arg;
267
268          arg.cmdtab = NULL;
269          arg.cmd = NULL;
270          arg.argc = 0;
271          arg.argn = 0;
272          arg.argv = NULL;
273          arg.bundle = bundle;
274          arg.cx = p->TermMode;
275          arg.prompt = p;
276
277	  mbuf_Show(&arg);
278        }
279	break;
280      default:
281	if (physical_Write(p->TermMode->physical, &ch, n) < 0) {
282	  log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno));
283          prompt_TtyCommandMode(p);
284        }
285	break;
286      }
287      p->readtilde = 0;
288      break;
289    }
290  }
291}
292
293static int
294prompt_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
295{
296  /* We never want to write here ! */
297  log_Printf(LogALERT, "prompt_Write: Internal error: Bad call !\n");
298  return 0;
299}
300
301struct prompt *
302prompt_Create(struct server *s, struct bundle *bundle, int fd)
303{
304  struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt));
305
306  if (p != NULL) {
307    p->desc.type = PROMPT_DESCRIPTOR;
308    p->desc.UpdateSet = prompt_UpdateSet;
309    p->desc.IsSet = prompt_IsSet;
310    p->desc.Read = prompt_Read;
311    p->desc.Write = prompt_Write;
312
313    if (fd == PROMPT_STD) {
314      char *tty = ttyname(STDIN_FILENO);
315
316      if (!tty) {
317        free(p);
318        return NULL;
319      }
320      p->fd_in = STDIN_FILENO;
321      p->fd_out = STDOUT_FILENO;
322      p->Term = stdout;
323      p->owner = NULL;
324      p->auth = LOCAL_AUTH;
325      p->src.type = "Controller";
326      strncpy(p->src.from, tty, sizeof p->src.from - 1);
327      p->src.from[sizeof p->src.from - 1] = '\0';
328      tcgetattr(p->fd_in, &p->oldtio);	/* Save original tty mode */
329    } else {
330      p->fd_in = p->fd_out = fd;
331      p->Term = fdopen(fd, "a+");
332      p->owner = s;
333      p->auth = *s->cfg.passwd ? LOCAL_NO_AUTH : LOCAL_AUTH;
334      p->src.type = "unknown";
335      *p->src.from = '\0';
336    }
337    p->TermMode = NULL;
338    p->nonewline = 1;
339    p->needprompt = 1;
340    p->readtilde = 0;
341    p->bundle = bundle;
342    log_RegisterPrompt(p);
343  }
344
345  return p;
346}
347
348void
349prompt_Destroy(struct prompt *p, int verbose)
350{
351  if (p) {
352    if (p->Term != stdout) {
353      fclose(p->Term);
354      close(p->fd_in);
355      if (p->fd_out != p->fd_in)
356        close(p->fd_out);
357      if (verbose)
358        log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from);
359    } else
360      prompt_TtyOldMode(p);
361
362    log_UnRegisterPrompt(p);
363    free(p);
364  }
365}
366
367void
368prompt_Printf(struct prompt *p, const char *fmt,...)
369{
370  if (p && p->active) {
371    va_list ap;
372
373    va_start(ap, fmt);
374    prompt_vPrintf(p, fmt, ap);
375    va_end(ap);
376  }
377}
378
379void
380prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap)
381{
382  if (p && p->active) {
383    char nfmt[LINE_LEN];
384    const char *pfmt;
385
386    if (p->TermMode) {
387      /* Stuff '\r' in front of '\n' 'cos we're in raw mode */
388      int len = strlen(fmt);
389
390      if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n' &&
391          (len == 1 || fmt[len-2] != '\r')) {
392        strcpy(nfmt, fmt);
393        strcpy(nfmt + len - 1, "\r\n");
394        pfmt = nfmt;
395      } else
396        pfmt = fmt;
397    } else
398      pfmt = fmt;
399    vfprintf(p->Term, pfmt, ap);
400    fflush(p->Term);
401    p->nonewline = 1;
402  }
403}
404
405void
406prompt_TtyInit(struct prompt *p)
407{
408  int stat, fd = p ? p->fd_in : STDIN_FILENO;
409  struct termios newtio;
410
411  stat = fcntl(fd, F_GETFL, 0);
412  if (stat > 0) {
413    stat |= O_NONBLOCK;
414    fcntl(fd, F_SETFL, stat);
415  }
416
417  if (p)
418    newtio = p->oldtio;
419  else
420    tcgetattr(fd, &newtio);
421
422  newtio.c_lflag &= ~(ECHO | ISIG | ICANON);
423  newtio.c_iflag = 0;
424  newtio.c_oflag &= ~OPOST;
425  if (!p)
426    newtio.c_cc[VINTR] = _POSIX_VDISABLE;
427  newtio.c_cc[VMIN] = 1;
428  newtio.c_cc[VTIME] = 0;
429  newtio.c_cflag |= CS8;
430  tcsetattr(fd, TCSANOW, &newtio);
431  if (p)
432    p->comtio = newtio;
433}
434
435/*
436 *  Set tty into command mode. We allow canonical input and echo processing.
437 */
438void
439prompt_TtyCommandMode(struct prompt *p)
440{
441  struct termios newtio;
442  int stat;
443
444  tcgetattr(p->fd_in, &newtio);
445  newtio.c_lflag |= (ECHO | ISIG | ICANON);
446  newtio.c_iflag = p->oldtio.c_iflag;
447  newtio.c_oflag |= OPOST;
448  tcsetattr(p->fd_in, TCSADRAIN, &newtio);
449
450  stat = fcntl(p->fd_in, F_GETFL, 0);
451  if (stat > 0) {
452    stat |= O_NONBLOCK;
453    fcntl(p->fd_in, F_SETFL, stat);
454  }
455
456  p->TermMode = NULL;
457}
458
459/*
460 * Set tty into terminal mode which is used while we invoke term command.
461 */
462void
463prompt_TtyTermMode(struct prompt *p, struct datalink *dl)
464{
465  int stat;
466
467  if (p->Term == stdout)
468    tcsetattr(p->fd_in, TCSADRAIN, &p->comtio);
469
470  stat = fcntl(p->fd_in, F_GETFL, 0);
471  if (stat > 0) {
472    stat &= ~O_NONBLOCK;
473    fcntl(p->fd_in, F_SETFL, stat);
474  }
475  p->TermMode = dl;
476}
477
478void
479prompt_TtyOldMode(struct prompt *p)
480{
481  int stat;
482
483  stat = fcntl(p->fd_in, F_GETFL, 0);
484  if (stat > 0) {
485    stat &= ~O_NONBLOCK;
486    fcntl(p->fd_in, F_SETFL, stat);
487  }
488
489  if (p->Term == stdout)
490    tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio);
491}
492
493pid_t
494prompt_pgrp(struct prompt *p)
495{
496  return tcgetpgrp(p->fd_in);
497}
498
499int
500PasswdCommand(struct cmdargs const *arg)
501{
502  const char *pass;
503
504  if (!arg->prompt) {
505    log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n");
506    return 0;
507  }
508
509  if (arg->prompt->owner == NULL) {
510    log_Printf(LogWARN, "passwd: Not required\n");
511    return 0;
512  }
513
514  if (arg->argc == arg->argn)
515    pass = "";
516  else if (arg->argc > arg->argn+1)
517    return -1;
518  else
519    pass = arg->argv[arg->argn];
520
521  if (!strcmp(arg->prompt->owner->cfg.passwd, pass))
522    arg->prompt->auth = LOCAL_AUTH;
523  else
524    arg->prompt->auth = LOCAL_NO_AUTH;
525
526  return 0;
527}
528
529static struct pppTimer bgtimer;
530
531static void
532prompt_TimedContinue(void *v)
533{
534  prompt_Continue((struct prompt *)v);
535}
536
537void
538prompt_Continue(struct prompt *p)
539{
540  timer_Stop(&bgtimer);
541  if (getpgrp() == prompt_pgrp(p)) {
542    prompt_TtyCommandMode(p);
543    p->nonewline = 1;
544    prompt_Required(p);
545    log_ActivatePrompt(p);
546  } else if (!p->owner) {
547    bgtimer.func = prompt_TimedContinue;
548    bgtimer.name = "prompt bg";
549    bgtimer.load = SECTICKS;
550    bgtimer.arg = p;
551    timer_Start(&bgtimer);
552  }
553}
554
555void
556prompt_Suspend(struct prompt *p)
557{
558  if (getpgrp() == prompt_pgrp(p)) {
559    prompt_TtyOldMode(p);
560    log_DeactivatePrompt(p);
561  }
562}
563