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