File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / console.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:39:23 2021 UTC (3 years, 3 months ago) by misho
Branches: mpd, MAIN
CVS tags: v5_9p16, v5_9, HEAD
mpd 5.9

    1: 
    2: /*
    3:  * console.c
    4:  *
    5:  * Written by Archie Cobbs <archie@freebsd.org>
    6:  * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
    7:  * See ``COPYRIGHT.whistle''
    8:  */
    9: 
   10: #include "ppp.h"
   11: #include "console.h"
   12: #include "util.h"
   13: #include <termios.h>
   14: 
   15: /*
   16:  * DEFINITIONS
   17:  */
   18: 
   19:   #ifndef CTRL
   20:   #define CTRL(c) (c - '@')
   21:   #endif
   22: 
   23:   #define EchoOff()	cs->write(cs, "\xFF\xFB\x01\xFF\xFD\x01");
   24:   #define EchoOn()	cs->write(cs, "\xFF\xFC\x01\xFF\xFE\x01");
   25: 						  
   26: 
   27:   enum {
   28:     STATE_USERNAME = 0,
   29:     STATE_PASSWORD,
   30:     STATE_AUTHENTIC
   31:   };
   32: 
   33:   /* Set menu options */
   34:   enum {
   35:     SET_OPEN,
   36:     SET_CLOSE,
   37:     SET_SELF,
   38:     SET_DISABLE,
   39:     SET_ENABLE
   40:   };
   41: 
   42: 
   43: /*
   44:  * INTERNAL FUNCTIONS
   45:  */
   46: 
   47:   static void	ConsoleConnect(int type, void *cookie);
   48:   static void	ConsoleSessionClose(ConsoleSession cs);
   49:   static void	ConsoleSessionReadEvent(int type, void *cookie);
   50:   static void	ConsoleSessionWrite(ConsoleSession cs, const char *fmt, ...);
   51:   static void	ConsoleSessionWriteV(ConsoleSession cs, const char *fmt, va_list vl);
   52:   static void	ConsoleSessionShowPrompt(ConsoleSession cs);
   53: 
   54:   static void	StdConsoleSessionClose(ConsoleSession cs);
   55:   static void	StdConsoleSessionWrite(ConsoleSession cs, const char *fmt, ...);
   56:   static void	StdConsoleSessionWriteV(ConsoleSession cs, const char *fmt, va_list vl);
   57: 
   58:   static int		ConsoleUserHashEqual(struct ghash *g, const void *item1, 
   59: 		  		const void *item2);
   60:   static u_int32_t	ConsoleUserHash(struct ghash *g, const void *item);
   61:   static int	ConsoleSetCommand(Context ctx, int ac, const char *const av[], const void *arg);
   62: 
   63: 
   64: /*
   65:  * GLOBAL VARIABLES
   66:  */
   67: 
   68:   const struct cmdtab ConsoleSetCmds[] = {
   69:     { "open",			"Open the console" ,
   70:   	ConsoleSetCommand, NULL, 2, (void *) SET_OPEN },
   71:     { "close",			"Close the console" ,
   72:   	ConsoleSetCommand, NULL, 2, (void *) SET_CLOSE },
   73:     { "self {ip} [{port}]",	"Set console ip and port" ,
   74:   	ConsoleSetCommand, NULL, 2, (void *) SET_SELF },
   75:     { "enable [opt ...]",	"Enable this console option" ,
   76:   	ConsoleSetCommand, NULL, 0, (void *) SET_ENABLE },
   77:     { "disable [opt ...]",	"Disable this console option" ,
   78:   	ConsoleSetCommand, NULL, 0, (void *) SET_DISABLE },
   79:     { NULL, NULL, NULL, NULL, 0, NULL },
   80:   };
   81: 
   82:   struct ghash		*gUsers;		/* allowed users */
   83:   pthread_rwlock_t	gUsersLock;
   84: 
   85: /*
   86:  * INTERNAL VARIABLES
   87:  */
   88: 
   89:   static const struct confinfo	gConfList[] = {
   90:     { 0,	CONSOLE_AUTH,		"auth"		},
   91:     { 0,	0,			NULL		},
   92:   };
   93: 
   94:   static const struct confinfo	sConfList[] = {
   95:     { 0,	CONSOLE_LOGGING,	"logging"	},
   96:     { 0,	0,			NULL		},
   97:   };
   98: 
   99: static struct termios	gOrigTermiosAttrs;
  100: static int		gOrigFlags;
  101: 
  102: /*
  103:  * ConsoleInit()
  104:  */
  105: 
  106: int
  107: ConsoleInit(Console c)
  108: {
  109:     int ret;
  110: 
  111:   /* setup console-defaults */
  112:   memset(&gConsole, 0, sizeof(gConsole));
  113:   ParseAddr(DEFAULT_CONSOLE_IP, &c->addr, ALLOW_IPV4|ALLOW_IPV6);
  114:   c->port = DEFAULT_CONSOLE_PORT;
  115:   Enable(&c->options, CONSOLE_AUTH);
  116: 
  117:   SLIST_INIT(&c->sessions);
  118:   
  119:   if ((ret = pthread_rwlock_init (&c->lock, NULL)) != 0) {
  120:     Log(LG_ERR, ("Could not create rwlock %d", ret));
  121:     return (-1);
  122:   }
  123: 
  124:   gUsers = ghash_create(c, 0, 0, MB_CONS, ConsoleUserHash, ConsoleUserHashEqual, NULL, NULL);
  125:   if ((ret = pthread_rwlock_init (&gUsersLock, NULL)) != 0) {
  126:     Log(LG_ERR, ("Could not create rwlock %d", ret));
  127:     return (-1);
  128:   }
  129: 
  130:   return (0);
  131: }
  132: 
  133: /*
  134:  * ConsoleOpen()
  135:  */
  136: 
  137: int
  138: ConsoleOpen(Console c)
  139: {
  140:   char		addrstr[INET6_ADDRSTRLEN];
  141: 
  142:   if (c->fd) {
  143:     Log(LG_ERR, ("CONSOLE: Console already running"));
  144:     return -1;
  145:   }
  146:   
  147:   u_addrtoa(&c->addr, addrstr, sizeof(addrstr));
  148:   if ((c->fd = TcpGetListenPort(&c->addr, c->port, FALSE)) < 0) {
  149:     Log(LG_ERR, ("CONSOLE: Can't listen for connections on %s %d", 
  150: 	addrstr, c->port));
  151:     return -1;
  152:   }
  153: 
  154:   if (EventRegister(&c->event, EVENT_READ, c->fd, 
  155: 	EVENT_RECURRING, ConsoleConnect, c) < 0)
  156:     return -1;
  157: 
  158:   Log(LG_CONSOLE, ("CONSOLE: listening on %s %d", 
  159: 	addrstr, c->port));
  160:   return 0;
  161: }
  162: 
  163: /*
  164:  * ConsoleClose()
  165:  */
  166: 
  167: int
  168: ConsoleClose(Console c)
  169: {
  170:   if (!c->fd)
  171:     return 0;
  172:   EventUnRegister(&c->event);
  173:   close(c->fd);
  174:   c->fd = 0;
  175:   return 0;
  176: }
  177: 
  178: void
  179: ConsoleCancelCleanup(void *rwlock) NO_THREAD_SAFETY_ANALYSIS
  180: {
  181:   pthread_rwlock_t p = (pthread_rwlock_t)rwlock;
  182: 
  183:   RWLOCK_UNLOCK(p);
  184: }
  185: 
  186: /*
  187:  * ConsoleStat()
  188:  */
  189: 
  190: int
  191: ConsoleStat(Context ctx, int ac, const char *const av[], const void *arg)
  192:     NO_THREAD_SAFETY_ANALYSIS
  193: {
  194:   Console		c = &gConsole;
  195:   ConsoleSession	s;
  196:   ConsoleSession	cs = ctx->cs;
  197:   char       		addrstr[INET6_ADDRSTRLEN];
  198: 
  199:   (void)ac;
  200:   (void)av;
  201:   (void)arg;
  202: 
  203:   Printf("Configuration:\r\n");
  204:   Printf("\tState         : %s\r\n", c->fd ? "OPENED" : "CLOSED");
  205:   Printf("\tIP-Address    : %s\r\n", u_addrtoa(&c->addr,addrstr,sizeof(addrstr)));
  206:   Printf("\tPort          : %d\r\n", c->port);
  207: 
  208:   pthread_cleanup_push(ConsoleCancelCleanup, c->lock);
  209:   RWLOCK_RDLOCK(c->lock);
  210:   Printf("Active sessions:\r\n");
  211:   SLIST_FOREACH(s, &c->sessions, next) {
  212:     Printf("\tUsername: %s\tFrom: %s\r\n",
  213: 	s->user.username, u_addrtoa(&s->peer_addr,addrstr,sizeof(addrstr)));
  214:   }
  215:   pthread_cleanup_pop(1);
  216: 
  217:   Printf("Global options:\r\n");
  218:   OptStat(ctx, &c->options, gConfList);
  219:   if (cs) {
  220:     Printf("This session options:\r\n");
  221:     OptStat(ctx, &cs->options, sConfList);
  222:   }
  223:   return 0;
  224: }
  225: 
  226: /*
  227:  * ConsoleConnect()
  228:  */
  229: 
  230: static void
  231: ConsoleConnect(int type, void *cookie)
  232: {
  233:   Console		c = cookie;
  234:   ConsoleSession	cs;
  235:   const char		*options = 
  236: 	  "\xFF\xFB\x03"	/* WILL Suppress Go-Ahead */
  237: 	  "\xFF\xFD\x03"	/* DO Suppress Go-Ahead */
  238: 	  "\xFF\xFB\x01"	/* WILL echo */
  239: 	  "\xFF\xFD\x01";	/* DO echo */
  240:   char                  addrstr[INET6_ADDRSTRLEN];
  241:   struct sockaddr_storage ss;
  242: 
  243:   (void)type;  
  244:   Log(LG_CONSOLE, ("CONSOLE: Connect"));
  245:   cs = Malloc(MB_CONS, sizeof(*cs));
  246:   if ((cs->fd = TcpAcceptConnection(c->fd, &ss, FALSE)) < 0) 
  247:     goto cleanup;
  248:   sockaddrtou_addr(&ss, &cs->peer_addr, &cs->peer_port);
  249: 
  250:   if (EventRegister(&cs->readEvent, EVENT_READ, cs->fd, 
  251: 	EVENT_RECURRING, ConsoleSessionReadEvent, cs) < 0)
  252:     goto fail;
  253: 
  254:   cs->console = c;
  255:   cs->close = ConsoleSessionClose;
  256:   cs->write = ConsoleSessionWrite;
  257:   cs->writev = ConsoleSessionWriteV;
  258:   cs->prompt = ConsoleSessionShowPrompt;
  259:   if (!Enabled(&c->options, CONSOLE_AUTH)) {
  260: 	cs->state = STATE_AUTHENTIC;
  261: 	strcpy(cs->user.username, "root");
  262: 	cs->context.priv = 2;
  263:   } else {
  264: 	cs->state = STATE_USERNAME;
  265:   }
  266:   cs->context.cs = cs;
  267:   RWLOCK_WRLOCK(c->lock);
  268:   SLIST_INSERT_HEAD(&c->sessions, cs, next);
  269:   RWLOCK_UNLOCK(c->lock);
  270:   Log(LG_CONSOLE, ("CONSOLE: Allocated new console session %p from %s", 
  271:     cs, u_addrtoa(&cs->peer_addr,addrstr,sizeof(addrstr))));
  272:   cs->write(cs, "Multi-link PPP daemon for FreeBSD\r\n\r\n");
  273:   cs->write(cs, options);
  274:   cs->prompt(cs);
  275:   return;
  276: 
  277: fail:
  278:   close(cs->fd);
  279: 
  280: cleanup:
  281:   Freee(cs);
  282:   return;
  283: 
  284: }
  285: 
  286: /*
  287:  * StdConsoleConnect()
  288:  */
  289: 
  290: Context
  291: StdConsoleConnect(Console c)
  292: {
  293:     ConsoleSession	cs;
  294:     struct termios settings;
  295:   
  296:     cs = Malloc(MB_CONS, sizeof(*cs));
  297:   
  298:     /* Init stdin */
  299:     cs->fd = 0;
  300: 
  301:     /* Save original STDxxx flags */
  302:     gOrigFlags = fcntl(0, F_GETFL, 0);
  303: 
  304:     tcgetattr(cs->fd, &settings);
  305:     gOrigTermiosAttrs = settings;
  306:     settings.c_lflag &= (~ICANON);
  307:     settings.c_lflag &= (~ECHO); // don't echo the character
  308:     settings.c_cc[VTIME] = 0; // timeout (tenths of a second)
  309:     settings.c_cc[VMIN] = 0; // minimum number of characters
  310:     tcsetattr(cs->fd, TCSANOW, &settings);
  311: 
  312:     if (fcntl(cs->fd, F_SETFL, O_NONBLOCK) < 0) {
  313:       Perror("%s: fcntl", __FUNCTION__);
  314:       Freee(cs);
  315:       return(NULL);
  316:     }
  317: 
  318:     /* Init stdout */
  319:     if (fcntl(1, F_SETFL, O_NONBLOCK) < 0) {
  320:       Perror("%s: fcntl", __FUNCTION__);
  321:       Freee(cs);
  322:       return(NULL);
  323:     }
  324: 
  325:     Enable(&cs->options, CONSOLE_LOGGING);
  326:     cs->console = c;
  327:     cs->close = StdConsoleSessionClose;
  328:     cs->write = StdConsoleSessionWrite;
  329:     cs->writev = StdConsoleSessionWriteV;
  330:     cs->prompt = ConsoleSessionShowPrompt;
  331:     cs->state = STATE_AUTHENTIC;
  332:     cs->context.cs = cs;
  333:     strcpy(cs->user.username, "root");
  334:     cs->context.priv = 2;
  335:     RWLOCK_WRLOCK(c->lock);
  336:     SLIST_INSERT_HEAD(&c->sessions, cs, next);
  337:     RWLOCK_UNLOCK(c->lock);
  338: 
  339:     EventRegister(&cs->readEvent, EVENT_READ, cs->fd, 
  340: 	EVENT_RECURRING, ConsoleSessionReadEvent, cs);
  341: 
  342:     return (&cs->context);
  343: }
  344: 
  345: 
  346: /*
  347:  * ConsoleSessionClose()
  348:  * This function is potentially thread unsafe.
  349:  * To avois this locks should be used.
  350:  * The only unlocked place is ->readEvent, 
  351:  * but it is actually the only place where ->close called.
  352:  */
  353: 
  354: static void
  355: ConsoleSessionClose(ConsoleSession cs)
  356: {
  357:     cs->write(cs, "Console closed.\r\n");
  358:     RWLOCK_WRLOCK(cs->console->lock);
  359:     SLIST_REMOVE(&cs->console->sessions, cs, console_session, next);
  360:     RWLOCK_UNLOCK(cs->console->lock);
  361:     EventUnRegister(&cs->readEvent);
  362:     close(cs->fd);
  363:     Freee(cs);
  364:     return;
  365: }
  366: 
  367: /*
  368:  * StdConsoleSessionClose()
  369:  */
  370: 
  371: static void
  372: StdConsoleSessionClose(ConsoleSession cs)
  373: {
  374:     cs->write(cs, "Console closed.\r\n");
  375:     EventUnRegister(&cs->readEvent);
  376:     /* Restore original attrs */
  377:     tcsetattr(cs->fd, TCSANOW, &gOrigTermiosAttrs);
  378:     /* Restore original STDxxx flags. */
  379:     if (gOrigFlags>=0)
  380: 	fcntl(cs->fd, F_SETFL, gOrigFlags);
  381:     cs->close = NULL;
  382:     return;
  383: }
  384: 
  385: /*
  386:  * ConsoleSessionReadEvent()
  387:  */
  388: 
  389: static void
  390: ConsoleSessionReadEvent(int type, void *cookie)
  391: {
  392:   ConsoleSession	cs = cookie;
  393:   CmdTab		cmd, tab;
  394:   int			n, ac, ac2, i;
  395:   u_char		c;
  396:   char			compl[MAX_CONSOLE_LINE], line[MAX_CONSOLE_LINE];
  397:   char			*av[MAX_CONSOLE_ARGS], *av2[MAX_CONSOLE_ARGS];
  398:   char			*av_copy[MAX_CONSOLE_ARGS];
  399:   char                  addrstr[INET6_ADDRSTRLEN];
  400: 
  401:   (void)type;
  402:   while(1) {
  403:     if ((n = read(cs->fd, &c, 1)) <= 0) {
  404:       if (n < 0) {
  405: 	if (errno == EAGAIN)
  406: 	  goto out;
  407: 	Perror("CONSOLE: Error while reading");
  408:       } else {
  409:         if (cs->fd == 0 && isatty(cs->fd))
  410: 	  goto out;
  411: 	Log(LG_ERR, ("CONSOLE: Connection closed by peer"));
  412:       }
  413:       goto abort;
  414:     }
  415:     
  416:     if (cs->context.lnk && cs->context.lnk->dead) {
  417: 	UNREF(cs->context.lnk);
  418: 	cs->context.lnk = NULL;
  419:     }
  420:     if (cs->context.bund && cs->context.bund->dead) {
  421: 	UNREF(cs->context.bund);
  422: 	cs->context.bund = NULL;
  423:     }
  424:     if (cs->context.rep && cs->context.rep->dead) {
  425: 	UNREF(cs->context.rep);
  426: 	cs->context.rep = NULL;
  427:     }
  428: 
  429:     /* deal with escapes, map cursors */
  430:     if (cs->escaped) {
  431:       if (cs->escaped == '[') {
  432: 	switch(c) {
  433: 	  case 'A':	/* up */
  434: 	    c = CTRL('P');
  435: 	    break;
  436: 	  case 'B':	/* down */
  437: 	    c = CTRL('N');
  438: 	    break;
  439: 	  case 'C':	/* right */
  440: 	    c = CTRL('F');
  441: 	    break;
  442: 	  case 'D':	/* left */
  443: 	    c = CTRL('B');
  444: 	    break;
  445: 	  default:
  446: 	    c = 0;
  447: 	}
  448: 	cs->escaped = FALSE;
  449:       } else {
  450: 	cs->escaped = c == '[' ? c : 0;
  451: 	continue;
  452:       }
  453:     }
  454: 
  455:     switch(c) {
  456:     case 0:
  457:       break;
  458:     case 27:	/* escape */
  459:       cs->escaped = TRUE;
  460:       break;
  461:     case 255:	/* telnet option follows */
  462:       cs->telnet = TRUE;
  463:       break;
  464:     case 253:	/* telnet option-code DO */
  465:       break;
  466:     case 9:	/* TAB */
  467:       if (cs->state != STATE_AUTHENTIC)
  468:         break;
  469:       strcpy(line, cs->cmd);
  470:       ac = ParseLine(line, av, sizeof(av) / sizeof(*av), 1);
  471:       memset(compl, 0, sizeof(compl));
  472:       tab = gCommands;
  473:       for (i = 0; i < ac; i++) {
  474: 
  475:         if (FindCommand(&cs->context, tab, av[i], &cmd)) {
  476: 	    cs->write(cs, "\a");
  477: 	    goto notfound;
  478: 	}
  479: 	strcpy(line, cmd->name);
  480:         ac2 = ParseLine(line, av2, sizeof(av2) / sizeof(*av2), 1);
  481: 	snprintf(&compl[strlen(compl)], sizeof(compl) - strlen(compl), "%s ", av2[0]);
  482: 	FreeArgs(ac2, av2);
  483: 	if (cmd->func != CMD_SUBMENU && 
  484: 	  (i != 0 || strcmp(compl, "help ") !=0)) {
  485: 	    i++;
  486: 	    if (i < ac) {
  487: 		cs->write(cs, "\a");
  488: 		for (; i < ac; i++) {
  489: 		    strcat(compl, av[i]);
  490: 		    if ((i+1) < ac)
  491: 			strcat(compl, " ");
  492: 		}
  493: 	    }
  494: 	    break;
  495: 	} else if (cmd->func == CMD_SUBMENU) {
  496: 	    tab = cmd->arg;
  497: 	}
  498:       }
  499:       for (i = 0; i < cs->cmd_len; i++)
  500: 	cs->write(cs, "\b \b");
  501:       strcpy(cs->cmd, compl);
  502:       cs->cmd_len = strlen(cs->cmd);
  503:       cs->write(cs, cs->cmd);
  504: notfound:
  505:       FreeArgs(ac, av);
  506:       break;
  507:     case CTRL('C'):
  508:       if (cs->telnet)
  509: 	break;
  510:       cs->write(cs, "\r\n");
  511:       memset(cs->cmd, 0, MAX_CONSOLE_LINE);
  512:       cs->cmd_len = 0;
  513:       cs->prompt(cs);
  514:       cs->telnet = FALSE;
  515:       break;
  516:     case CTRL('P'):	/* page up */
  517:       if ((cs->currhist < MAX_CONSOLE_HIST) && 
  518:         (cs->history[cs->currhist][0])) {
  519:     	for (i = 0; i < cs->cmd_len; i++)
  520: 	    cs->write(cs, "\b \b");    
  521: 	memcpy(cs->cmd, cs->history[cs->currhist], MAX_CONSOLE_LINE);
  522: 	cs->currhist++;
  523: 	cs->cmd_len = strlen(cs->cmd);
  524: 	cs->write(cs, cs->cmd);
  525:       }
  526:       break;
  527:     case CTRL('N'):	/* page down */
  528:       if (cs->currhist > 1) {
  529:     	for (i = 0; i < cs->cmd_len; i++)
  530: 	    cs->write(cs, "\b \b");    
  531: 	cs->currhist--;
  532: 	memcpy(cs->cmd, cs->history[cs->currhist-1], MAX_CONSOLE_LINE);
  533: 	cs->cmd_len = strlen(cs->cmd);
  534: 	cs->write(cs, cs->cmd);
  535:       } else {
  536:         if (cs->cmd_len>0) {
  537: 	    if (strncmp(cs->cmd, cs->history[0], MAX_CONSOLE_LINE) != 0) {
  538: 		for (i = MAX_CONSOLE_HIST - 1; i > 0; i--)
  539: 		    memcpy(cs->history[i], cs->history[i - 1], MAX_CONSOLE_LINE);
  540: 		memcpy(cs->history[0], cs->cmd, MAX_CONSOLE_LINE);
  541:     		cs->write(cs, "\r\n");
  542: 		cs->prompt(cs);
  543: 	    } else {
  544:     		for (i = 0; i < cs->cmd_len; i++)
  545: 		    cs->write(cs, "\b \b");    
  546: 	    }
  547: 	}
  548:         cs->currhist = 0;
  549: 	memset(cs->cmd, 0, MAX_CONSOLE_LINE);
  550: 	cs->cmd_len = 0;
  551:       }
  552:       break;
  553:     case CTRL('F'):
  554:     case CTRL('B'):
  555:       break;
  556:     case CTRL('H'):
  557:     case 127:	/* BS */
  558:       if (cs->cmd_len > 0) {
  559: 	cs->cmd_len -= 1;
  560: 	cs->cmd[cs->cmd_len] = 0;
  561:         if (cs->state != STATE_PASSWORD)
  562: 	    cs->write(cs, "\b \b");
  563:       }
  564:       break;
  565:     case '\r':
  566:     case '\n':
  567:     case '?':
  568:     { 
  569:       if (c == '?')
  570:         cs->write(cs, "?");
  571:       cs->write(cs, "\r\n");
  572:       
  573:       cs->telnet = FALSE;
  574:       if (cs->state == STATE_USERNAME) {
  575: 	strlcpy(cs->user.username, cs->cmd, sizeof(cs->user.username));
  576:         memset(cs->cmd, 0, MAX_CONSOLE_LINE);
  577:         cs->cmd_len = 0;
  578: 	cs->state = STATE_PASSWORD;
  579: 	cs->prompt(cs);
  580: 	break;
  581:       } else if (cs->state == STATE_PASSWORD) {
  582: 	ConsoleUser u;
  583: 
  584: 	RWLOCK_RDLOCK(gUsersLock);
  585: 	u = ghash_get(gUsers, &cs->user);
  586: 	RWLOCK_UNLOCK(gUsersLock);
  587: 
  588: 	if (!u) 
  589: 	  goto failed;
  590: 
  591: 	if (strcmp(u->password, cs->cmd)) 
  592: 	  goto failed;
  593: 
  594: 	cs->state = STATE_AUTHENTIC;
  595: 	cs->user.priv = u->priv;
  596: 	cs->context.priv = u->priv;
  597: 	cs->write(cs, "\r\nWelcome!\r\nMpd pid %lu, version %s\r\n", 
  598: 		(u_long) gPid, gVersion);
  599: 	goto success;
  600: 
  601: failed:
  602: 	cs->write(cs, "Login failed\r\n");
  603: 	Log(LG_CONSOLE, ("CONSOLE: Failed login attempt from %s", 
  604: 		u_addrtoa(&cs->peer_addr,addrstr,sizeof(addrstr))));
  605: 	cs->user.username[0] = 0;
  606: 	cs->state = STATE_USERNAME;
  607: success:
  608: 	memset(cs->cmd, 0, MAX_CONSOLE_LINE);
  609: 	cs->cmd_len = 0;
  610: 	cs->prompt(cs);
  611: 	break;
  612:       }
  613: 
  614:       memcpy(line, cs->cmd, sizeof(line));
  615:       ac = ParseLine(line, av, sizeof(av) / sizeof(*av), 1);
  616:       memcpy(av_copy, av, sizeof(av));
  617:       if (c != '?') {
  618:         Log2(LG_CONSOLE, ("[%s] CONSOLE: %s: %s", 
  619: 	    cs->context.lnk ? cs->context.lnk->name :
  620: 		(cs->context.bund? cs->context.bund->name : ""), 
  621: 	    cs->user.username, cs->cmd));
  622:         DoCommand(&cs->context, ac, (const char *const *)av, NULL, 0);
  623:       } else {
  624:         HelpCommand(&cs->context, ac, (const char *const *)av, NULL);
  625:       }
  626:       FreeArgs(ac, av_copy);
  627:       if (cs->exit)
  628: 	goto abort;
  629:       cs->prompt(cs);
  630:       if (c != '?') {
  631:         if (cs->cmd_len > 0 &&
  632: 	    strncmp(cs->cmd, cs->history[0], MAX_CONSOLE_LINE) != 0) {
  633: 	    for (i = MAX_CONSOLE_HIST - 1; i > 0; i--)
  634: 		memcpy(cs->history[i], cs->history[i - 1], MAX_CONSOLE_LINE);
  635: 	    memcpy(cs->history[0], cs->cmd, MAX_CONSOLE_LINE);
  636: 	}
  637:         cs->currhist = 0;
  638:         memset(cs->cmd, 0, MAX_CONSOLE_LINE);
  639: 	cs->cmd_len = 0;
  640:       } else {
  641: 	cs->write(cs, cs->cmd);
  642:       }
  643:       break;
  644:     }
  645:     /* "normal" char entered */
  646:     default:
  647:       if (cs->telnet) {
  648: 	if (c < 251)
  649: 	  cs->telnet = FALSE;
  650: 	break;
  651:       }
  652: 
  653:       if ((cs->cmd_len + 2) >= MAX_CONSOLE_LINE) {
  654:         cs->write(cs, "\a");
  655:         break;
  656:       }
  657:       
  658:       if (c < 32)
  659:         break;
  660: 
  661:       if (cs->state != STATE_PASSWORD)
  662:  	cs->write(cs, "%c", c);
  663: 
  664:       cs->cmd[cs->cmd_len++] = c;
  665:     }
  666:   }
  667: 
  668: abort:
  669:     if (cs->close) {
  670: 	RESETREF(cs->context.lnk, NULL);
  671:         RESETREF(cs->context.bund, NULL);
  672:         RESETREF(cs->context.rep, NULL);
  673: 	cs->close(cs);
  674:     }
  675: out:
  676:   return;
  677: }
  678: 
  679: /*
  680:  * ConsoleSessionWrite()
  681:  */
  682: 
  683: static void 
  684: ConsoleSessionWrite(ConsoleSession cs, const char *fmt, ...)
  685: {
  686:   va_list vl;
  687: 
  688:   va_start(vl, fmt);
  689:   ConsoleSessionWriteV(cs, fmt, vl);
  690:   va_end(vl);
  691: }
  692: 
  693: /*
  694:  * ConsoleSessionWriteV()
  695:  */
  696: 
  697: static void 
  698: ConsoleSessionWriteV(ConsoleSession cs, const char *fmt, va_list vl)
  699: {
  700:     char	*buf;
  701:     size_t	len;
  702:     
  703:     len = vasprintf(&buf, fmt, vl);
  704:     write(cs->fd, buf, len);
  705:     free(buf);
  706: }
  707: 
  708: /*
  709:  * StdConsoleSessionWrite()
  710:  */
  711: 
  712: static void 
  713: StdConsoleSessionWrite(ConsoleSession cs, const char *fmt, ...)
  714: {
  715:   va_list vl;
  716: 
  717:   va_start(vl, fmt);
  718:   StdConsoleSessionWriteV(cs, fmt, vl);
  719:   va_end(vl);
  720: }
  721: 
  722: /*
  723:  * StdConsoleSessionWriteV()
  724:  */
  725: 
  726: static void 
  727: StdConsoleSessionWriteV(ConsoleSession cs, const char *fmt, va_list vl)
  728: {
  729:     (void)cs;
  730:     vprintf(fmt, vl);
  731:     fflush(stdout);
  732: }
  733: 
  734: /*
  735:  * ConsoleShowPrompt()
  736:  */
  737: 
  738: static void
  739: ConsoleSessionShowPrompt(ConsoleSession cs)
  740: {
  741:   switch(cs->state) {
  742:   case STATE_USERNAME:
  743:     cs->write(cs, "Username: ");
  744:     break;
  745:   case STATE_PASSWORD:
  746:     cs->write(cs, "Password: ");
  747:     break;
  748:   case STATE_AUTHENTIC:
  749:     if (cs->context.lnk)
  750: 	cs->write(cs, "[%s] ", cs->context.lnk->name);
  751:     else if (cs->context.bund)
  752: 	cs->write(cs, "[%s] ", cs->context.bund->name);
  753:     else if (cs->context.rep)
  754: 	cs->write(cs, "[%s] ", cs->context.rep->name);
  755:     else
  756: 	cs->write(cs, "[] ");
  757:     break;
  758:   }
  759: }
  760: 
  761: 
  762: /*
  763:  * ConsoleUserHash
  764:  *
  765:  * Fowler/Noll/Vo- hash
  766:  * see http://www.isthe.com/chongo/tech/comp/fnv/index.html
  767:  *
  768:  * By:
  769:  *  chongo <Landon Curt Noll> /\oo/\
  770:  *  http://www.isthe.com/chongo/
  771:  */
  772: 
  773: static u_int32_t
  774: ConsoleUserHash(struct ghash *g, const void *item)
  775: {
  776:   const struct console_user *u = (const struct console_user *) item;
  777:   const u_char *s = (const u_char *) u->username;
  778:   u_int32_t hash = 0x811c9dc5;
  779: 
  780:   (void)g;
  781:   while (*s) {
  782:     hash += (hash<<1) + (hash<<4) + (hash<<7) + (hash<<8) + (hash<<24);
  783:     /* xor the bottom with the current octet */
  784:     hash ^= (u_int32_t)*s++;
  785:   }
  786: 
  787:   return hash;
  788: }
  789: 
  790: /*
  791:  * ConsoleUserHashEqual
  792:  */
  793: 
  794: static int
  795: ConsoleUserHashEqual(struct ghash *g, const void *item1, const void *item2)
  796: {
  797:   const struct console_user *u1 = (const struct console_user *) item1;
  798:   const struct console_user *u2 = (const struct console_user *) item2;
  799: 
  800:   (void)g;
  801:   if (u1 && u2)
  802:     return (strcmp(u1->username, u2->username) == 0);
  803:   else
  804:     return 0;
  805: }
  806: 
  807: /*
  808:  * ConsoleSetCommand()
  809:  */
  810: 
  811: static int
  812: ConsoleSetCommand(Context ctx, int ac, const char *const av[], const void *arg) 
  813: {
  814:   Console	 	c = &gConsole;
  815:   ConsoleSession	cs = ctx->cs;
  816:   int			port;
  817: 
  818:   switch ((intptr_t)arg) {
  819: 
  820:     case SET_OPEN:
  821:       ConsoleOpen(c);
  822:       break;
  823: 
  824:     case SET_CLOSE:
  825:       ConsoleClose(c);
  826:       break;
  827: 
  828:     case SET_ENABLE:
  829:       if (cs) {
  830: 	if (strcmp(av[0], "auth") != 0)
  831: 	    EnableCommand(ac, av, &cs->options, sConfList);
  832:       } else
  833: 	EnableCommand(ac, av, &c->options, gConfList);
  834:       break;
  835: 
  836:     case SET_DISABLE:
  837:       if (cs) {
  838: 	if (strcmp(av[0], "auth") != 0)
  839: 	    DisableCommand(ac, av, &cs->options, sConfList);
  840:       } else
  841: 	DisableCommand(ac, av, &c->options, gConfList);
  842:       break;
  843: 
  844:     case SET_SELF:
  845:       if (ac < 1 || ac > 2)
  846: 	return(-1);
  847: 
  848:       if (!ParseAddr(av[0], &c->addr, ALLOW_IPV4|ALLOW_IPV6)) 
  849: 	Error("CONSOLE: Bogus IP address given %s", av[0]);
  850: 
  851:       if (ac == 2) {
  852:         port =  strtol(av[1], NULL, 10);
  853:         if (port < 1 || port > 65535)
  854: 	    Error("CONSOLE: Bogus port given %s", av[1]);
  855:         c->port=port;
  856:       }
  857:       break;
  858: 
  859:     default:
  860:       return(-1);
  861: 
  862:   }
  863: 
  864:   return 0;
  865: }
  866: 
  867: /*
  868:  * ConsoleShutdown()
  869:  */
  870: 
  871: void
  872: ConsoleShutdown(Console c)
  873: {
  874:     ConsoleSession s, tmp;
  875: 
  876:     SLIST_FOREACH_SAFE(s, &c->sessions, next, tmp) {
  877: 	if (s->close != NULL)
  878: 		s->close(s);
  879:     }
  880: }
  881: 
  882: /*
  883:  * UserCommand()
  884:  */
  885: 
  886: int
  887: UserCommand(Context ctx, int ac, const char *const av[], const void *arg) 
  888: {
  889:     ConsoleUser		u;
  890: 
  891:     (void)arg;
  892:     (void)ctx;
  893: 
  894:     if (ac < 2 || ac > 3) 
  895: 	return(-1);
  896: 
  897:     u = Malloc(MB_CONS, sizeof(*u));
  898:     strlcpy(u->username, av[0], sizeof(u->username));
  899:     strlcpy(u->password, av[1], sizeof(u->password));
  900:     if (ac == 3) {
  901: 	if (!strcmp(av[2],"admin"))
  902: 	    u->priv = 2;
  903: 	else if (!strcmp(av[2],"operator"))
  904: 	    u->priv = 1;
  905: 	else if (!strcmp(av[2],"user"))
  906: 	    u->priv = 0;
  907: 	else {
  908: 	    Freee(u);
  909: 	    return (-1);
  910: 	}
  911:     }
  912:     RWLOCK_WRLOCK(gUsersLock);
  913:     ghash_put(gUsers, u);
  914:     RWLOCK_UNLOCK(gUsersLock);
  915: 
  916:     return (0);
  917: }
  918: 
  919: /*
  920:  * UserStat()
  921:  */
  922: 
  923: int
  924: UserStat(Context ctx, int ac, const char *const av[], const void *arg)
  925:     NO_THREAD_SAFETY_ANALYSIS
  926: {
  927:     struct ghash_walk	walk;
  928:     ConsoleUser		u;
  929: 
  930:     (void)ac;
  931:     (void)av;
  932:     (void)arg;
  933: 
  934:     Printf("Configured users:\r\n");
  935:     pthread_cleanup_push(ConsoleCancelCleanup, gUsersLock);
  936:     RWLOCK_RDLOCK(gUsersLock);
  937:     ghash_walk_init(gUsers, &walk);
  938:     while ((u = ghash_walk_next(gUsers, &walk)) !=  NULL) {
  939: 	Printf("\tUsername: %-15s Priv: %s\r\n", u->username,
  940: 	    ((u->priv == 2)?"admin":((u->priv == 1)?"operator":"user")));
  941:     }
  942:     pthread_cleanup_pop(1);
  943: 
  944:     return 0;
  945: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>