Annotation of embedaddon/mpd/src/console.c, revision 1.1.1.5

1.1       misho       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);
1.1.1.5 ! misho      61:   static int   ConsoleSetCommand(Context ctx, int ac, const char *const av[], const void *arg);
1.1       misho      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 },
1.1.1.5 ! misho      79:     { NULL, NULL, NULL, NULL, 0, NULL },
1.1       misho      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[] = {
1.1.1.3   misho      90:     { 0,       CONSOLE_AUTH,           "auth"          },
                     91:     { 0,       0,                      NULL            },
                     92:   };
                     93: 
                     94:   static const struct confinfo sConfList[] = {
1.1       misho      95:     { 0,       CONSOLE_LOGGING,        "logging"       },
                     96:     { 0,       0,                      NULL            },
                     97:   };
                     98: 
1.1.1.5 ! misho      99: static struct termios  gOrigTermiosAttrs;
        !           100: static int             gOrigFlags;
1.1       misho     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;
1.1.1.3   misho     115:   Enable(&c->options, CONSOLE_AUTH);
1.1       misho     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: 
1.1.1.4   misho     178: void
1.1.1.5 ! misho     179: ConsoleCancelCleanup(void *rwlock) NO_THREAD_SAFETY_ANALYSIS
1.1.1.4   misho     180: {
                    181:   pthread_rwlock_t p = (pthread_rwlock_t)rwlock;
                    182: 
                    183:   RWLOCK_UNLOCK(p);
                    184: }
                    185: 
1.1       misho     186: /*
                    187:  * ConsoleStat()
                    188:  */
                    189: 
                    190: int
1.1.1.5 ! misho     191: ConsoleStat(Context ctx, int ac, const char *const av[], const void *arg)
        !           192:     NO_THREAD_SAFETY_ANALYSIS
1.1       misho     193: {
                    194:   Console              c = &gConsole;
                    195:   ConsoleSession       s;
                    196:   ConsoleSession       cs = ctx->cs;
                    197:   char                 addrstr[INET6_ADDRSTRLEN];
                    198: 
1.1.1.5 ! misho     199:   (void)ac;
        !           200:   (void)av;
        !           201:   (void)arg;
        !           202: 
1.1       misho     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: 
1.1.1.4   misho     208:   pthread_cleanup_push(ConsoleCancelCleanup, c->lock);
1.1       misho     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:   }
1.1.1.4   misho     215:   pthread_cleanup_pop(1);
1.1       misho     216: 
1.1.1.3   misho     217:   Printf("Global options:\r\n");
                    218:   OptStat(ctx, &c->options, gConfList);
1.1       misho     219:   if (cs) {
                    220:     Printf("This session options:\r\n");
1.1.1.3   misho     221:     OptStat(ctx, &cs->options, sConfList);
1.1       misho     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;
1.1.1.5 ! misho     242: 
        !           243:   (void)type;  
1.1       misho     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;
1.1.1.3   misho     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:   }
1.1       misho     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: 
1.1.1.5 ! misho     401:   (void)type;
1.1       misho     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 {
1.1.1.2   misho     409:         if (cs->fd == 0 && isatty(cs->fd))
1.1       misho     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));
1.1.1.5 ! misho     622:         DoCommand(&cs->context, ac, (const char *const *)av, NULL, 0);
1.1       misho     623:       } else {
1.1.1.5 ! misho     624:         HelpCommand(&cs->context, ac, (const char *const *)av, NULL);
1.1       misho     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: {
1.1.1.5 ! misho     729:     (void)cs;
1.1       misho     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: {
1.1.1.5 ! misho     776:   const struct console_user *u = (const struct console_user *) item;
        !           777:   const u_char *s = (const u_char *) u->username;
1.1       misho     778:   u_int32_t hash = 0x811c9dc5;
                    779: 
1.1.1.5 ! misho     780:   (void)g;
1.1       misho     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: {
1.1.1.5 ! misho     797:   const struct console_user *u1 = (const struct console_user *) item1;
        !           798:   const struct console_user *u2 = (const struct console_user *) item2;
1.1       misho     799: 
1.1.1.5 ! misho     800:   (void)g;
1.1       misho     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
1.1.1.5 ! misho     812: ConsoleSetCommand(Context ctx, int ac, const char *const av[], const void *arg) 
1.1       misho     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:
1.1.1.3   misho     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);
1.1       misho     834:       break;
                    835: 
                    836:     case SET_DISABLE:
1.1.1.3   misho     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);
1.1       misho     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
1.1.1.5 ! misho     887: UserCommand(Context ctx, int ac, const char *const av[], const void *arg) 
1.1       misho     888: {
                    889:     ConsoleUser                u;
                    890: 
1.1.1.5 ! misho     891:     (void)arg;
        !           892:     (void)ctx;
        !           893: 
1.1       misho     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
1.1.1.5 ! misho     924: UserStat(Context ctx, int ac, const char *const av[], const void *arg)
        !           925:     NO_THREAD_SAFETY_ANALYSIS
1.1       misho     926: {
                    927:     struct ghash_walk  walk;
                    928:     ConsoleUser                u;
                    929: 
1.1.1.5 ! misho     930:     (void)ac;
        !           931:     (void)av;
        !           932:     (void)arg;
        !           933: 
1.1       misho     934:     Printf("Configured users:\r\n");
1.1.1.4   misho     935:     pthread_cleanup_push(ConsoleCancelCleanup, gUsersLock);
1.1       misho     936:     RWLOCK_RDLOCK(gUsersLock);
                    937:     ghash_walk_init(gUsers, &walk);
                    938:     while ((u = ghash_walk_next(gUsers, &walk)) !=  NULL) {
1.1.1.3   misho     939:        Printf("\tUsername: %-15s Priv: %s\r\n", u->username,
1.1       misho     940:            ((u->priv == 2)?"admin":((u->priv == 1)?"operator":"user")));
                    941:     }
1.1.1.4   misho     942:     pthread_cleanup_pop(1);
1.1       misho     943: 
                    944:     return 0;
                    945: }

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