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

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

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