Subversion Repositories HelenOS

Rev

Rev 552 | Rev 582 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (C) 2005 Jakub Jermar
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. #include <console/kconsole.h>
  30. #include <console/console.h>
  31. #include <console/chardev.h>
  32. #include <print.h>
  33. #include <panic.h>
  34. #include <typedefs.h>
  35. #include <arch/types.h>
  36. #include <list.h>
  37. #include <arch.h>
  38. #include <func.h>
  39. #include <macros.h>
  40. #include <debug.h>
  41.  
  42. #define MAX_CMDLINE 256
  43.  
  44. /** Simple kernel console.
  45.  *
  46.  * The console is realized by kernel thread kconsole.
  47.  * It doesn't understand any useful command on its own,
  48.  * but makes it possible for other kernel subsystems to
  49.  * register their own commands.
  50.  */
  51.  
  52. /** Locking.
  53.  *
  54.  * There is a list of cmd_info_t structures. This list
  55.  * is protected by cmd_lock spinlock. Note that specially
  56.  * the link elements of cmd_info_t are protected by
  57.  * this lock.
  58.  *
  59.  * Each cmd_info_t also has its own lock, which protects
  60.  * all elements thereof except the link element.
  61.  *
  62.  * cmd_lock must be acquired before any cmd_info lock.
  63.  * When locking two cmd info structures, structure with
  64.  * lower address must be locked first.
  65.  */
  66.  
  67. spinlock_t cmd_lock;    /**< Lock protecting command list. */
  68. link_t cmd_head;    /**< Command list. */
  69.  
  70. static cmd_info_t *parse_cmdline(char *cmdline, size_t len);
  71. static bool parse_argument(char *cmdline, size_t len, index_t *start, index_t *end);
  72.  
  73. /** Data and methods for 'help' command. */
  74. static int cmd_help(cmd_arg_t *argv);
  75. static cmd_info_t help_info;
  76.  
  77. /** Data and methods for 'description' command. */
  78. static int cmd_desc(cmd_arg_t *argv);
  79. static void desc_help(void);
  80. static cmd_info_t desc_info;
  81. static char desc_buf[MAX_CMDLINE+1];
  82. static cmd_arg_t desc_argv = {
  83.     .type = ARG_TYPE_STRING,
  84.     .buffer = desc_buf,
  85.     .len = sizeof(desc_buf)
  86. };
  87.  
  88. /** Data and methods for 'halt' command. */
  89. static int cmd_halt(cmd_arg_t *argv);
  90. static cmd_info_t halt_info;
  91.  
  92. /** Initialize kconsole data structures. */
  93. void kconsole_init(void)
  94. {
  95.     spinlock_initialize(&cmd_lock, "kconsole_cmd");
  96.     list_initialize(&cmd_head);
  97.    
  98.     help_info.name = "help";
  99.     help_info.description = "List supported commands.";
  100.     help_info.func = cmd_help;
  101.     help_info.help = NULL;
  102.     help_info.argc = 0;
  103.     help_info.argv = NULL;
  104.  
  105.     spinlock_initialize(&help_info.lock, "kconsole_help");
  106.     link_initialize(&help_info.link);
  107.  
  108.     if (!cmd_register(&help_info))
  109.         panic("could not register command %s\n", help_info.name);
  110.  
  111.  
  112.     desc_info.name = "describe";
  113.     desc_info.description = "Describe specified command.";
  114.     desc_info.help = desc_help;
  115.     desc_info.func = cmd_desc;
  116.     desc_info.argc = 1;
  117.     desc_info.argv = &desc_argv;
  118.    
  119.     spinlock_initialize(&desc_info.lock, "kconsole_desc");
  120.     link_initialize(&desc_info.link);
  121.    
  122.     if (!cmd_register(&desc_info))
  123.         panic("could not register command %s\n", desc_info.name);
  124.    
  125.    
  126.     halt_info.name = "halt";
  127.     halt_info.description = "Halt the kernel.";
  128.     halt_info.func = cmd_halt;
  129.     halt_info.help = NULL;
  130.     halt_info.argc = 0;
  131.     halt_info.argv = NULL;
  132.  
  133.     spinlock_initialize(&halt_info.lock, "kconsole_halt");
  134.     link_initialize(&halt_info.link);
  135.  
  136.     if (!cmd_register(&halt_info))
  137.         panic("could not register command %s\n", halt_info.name);
  138. }
  139.  
  140.  
  141. /** Register kconsole command.
  142.  *
  143.  * @param cmd Structure describing the command.
  144.  *
  145.  * @return 0 on failure, 1 on success.
  146.  */
  147. int cmd_register(cmd_info_t *cmd)
  148. {
  149.     ipl_t ipl;
  150.     link_t *cur;
  151.    
  152.     spinlock_lock(&cmd_lock);
  153.    
  154.     /*
  155.      * Make sure the command is not already listed.
  156.      */
  157.     for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
  158.         cmd_info_t *hlp;
  159.        
  160.         hlp = list_get_instance(cur, cmd_info_t, link);
  161.  
  162.         if (hlp == cmd) {
  163.             /* The command is already there. */
  164.             spinlock_unlock(&cmd_lock);
  165.             return 0;
  166.         }
  167.  
  168.         /* Avoid deadlock. */
  169.         if (hlp < cmd) {
  170.             spinlock_lock(&hlp->lock);
  171.             spinlock_lock(&cmd->lock);
  172.         } else {
  173.             spinlock_lock(&cmd->lock);
  174.             spinlock_lock(&hlp->lock);
  175.         }
  176.        
  177.         if ((strncmp(hlp->name, cmd->name, strlen(cmd->name)) == 0)) {
  178.             /* The command is already there. */
  179.             spinlock_unlock(&hlp->lock);
  180.             spinlock_unlock(&cmd->lock);
  181.             spinlock_unlock(&cmd_lock);
  182.             return 0;
  183.         }
  184.        
  185.         spinlock_unlock(&hlp->lock);
  186.         spinlock_unlock(&cmd->lock);
  187.     }
  188.    
  189.     /*
  190.      * Now the command can be added.
  191.      */
  192.     list_append(&cmd->link, &cmd_head);
  193.    
  194.     spinlock_unlock(&cmd_lock);
  195.     return 1;
  196. }
  197.  
  198. /** Kernel console managing thread.
  199.  *
  200.  * @param arg Not used.
  201.  */
  202. void kconsole(void *arg)
  203. {
  204.     char cmdline[MAX_CMDLINE+1];
  205.     cmd_info_t *cmd_info;
  206.     count_t len;
  207.  
  208.     if (!stdin) {
  209.         printf("%s: no stdin\n", __FUNCTION__);
  210.         return;
  211.     }
  212.    
  213.     while (true) {
  214.         printf("%s> ", __FUNCTION__);
  215.         if (!(len = gets(stdin, cmdline, sizeof(cmdline))))
  216.             continue;
  217.         cmdline[len] = '\0';
  218.         cmd_info = parse_cmdline(cmdline, len);
  219.         if (!cmd_info)
  220.             continue;
  221.         (void) cmd_info->func(cmd_info->argv);
  222.     }
  223. }
  224.  
  225. /** Parse command line.
  226.  *
  227.  * @param cmdline Command line as read from input device.
  228.  * @param len Command line length.
  229.  *
  230.  * @return Structure describing the command.
  231.  */
  232. cmd_info_t *parse_cmdline(char *cmdline, size_t len)
  233. {
  234.     index_t start = 0, end = 0;
  235.     cmd_info_t *cmd = NULL;
  236.     link_t *cur;
  237.     ipl_t ipl;
  238.     int i;
  239.    
  240.     if (!parse_argument(cmdline, len, &start, &end)) {
  241.         /* Command line did not contain alphanumeric word. */
  242.         return NULL;
  243.     }
  244.  
  245.     spinlock_lock(&cmd_lock);
  246.    
  247.     for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
  248.         cmd_info_t *hlp;
  249.        
  250.         hlp = list_get_instance(cur, cmd_info_t, link);
  251.         spinlock_lock(&hlp->lock);
  252.        
  253.         if (strncmp(hlp->name, &cmdline[start], (end - start) + 1) == 0) {
  254.             cmd = hlp;
  255.             break;
  256.         }
  257.        
  258.         spinlock_unlock(&hlp->lock);
  259.     }
  260.    
  261.     spinlock_unlock(&cmd_lock);
  262.    
  263.     if (!cmd) {
  264.         /* Unknown command. */
  265.         printf("Unknown command.\n");
  266.         return NULL;
  267.     }
  268.  
  269.     /* cmd == hlp is locked */
  270.    
  271.     /*
  272.      * The command line must be further analyzed and
  273.      * the parameters therefrom must be matched and
  274.      * converted to those specified in the cmd info
  275.      * structure.
  276.      */
  277.  
  278.     for (i = 0; i < cmd->argc; i++) {
  279.         char *buf;
  280.         start = end + 1;
  281.         if (!parse_argument(cmdline, len, &start, &end)) {
  282.             printf("Too few arguments.\n");
  283.             spinlock_unlock(&cmd->lock);
  284.             return NULL;
  285.         }
  286.        
  287.         switch (cmd->argv[i].type) {
  288.             case ARG_TYPE_STRING:
  289.                 buf = cmd->argv[i].buffer;
  290.                 strncpy(buf, (const char *) &cmdline[start], min((end - start) + 1, cmd->argv[i].len - 1));
  291.             buf[min((end - start) + 1, cmd->argv[i].len - 1)] = '\0';
  292.             break;
  293.             case ARG_TYPE_INT:
  294.             case ARG_TYPE_INVALID:
  295.             default:
  296.             panic("invalid argument type\n");
  297.             break;
  298.         }
  299.     }
  300.    
  301.     start = end + 1;
  302.     if (parse_argument(cmdline, len, &start, &end)) {
  303.         printf("Too many arguments.\n");
  304.         spinlock_unlock(&cmd->lock);
  305.         return NULL;
  306.     }
  307.    
  308.     spinlock_unlock(&cmd->lock);
  309.     return cmd;
  310. }
  311.  
  312. /** Parse argument.
  313.  *
  314.  * Find start and end positions of command line argument.
  315.  *
  316.  * @param cmdline Command line as read from the input device.
  317.  * @param len Number of characters in cmdline.
  318.  * @param start On entry, 'start' contains pointer to the index
  319.  *        of first unprocessed character of cmdline.
  320.  *        On successful exit, it marks beginning of the next argument.
  321.  * @param end Undefined on entry. On exit, 'end' points to the last character
  322.  *        of the next argument.
  323.  *
  324.  * @return false on failure, true on success.
  325.  */
  326. bool parse_argument(char *cmdline, size_t len, index_t *start, index_t *end)
  327. {
  328.     int i;
  329.     bool found_start = false;
  330.    
  331.     ASSERT(start != NULL);
  332.     ASSERT(end != NULL);
  333.    
  334.     for (i = *start; i < len; i++) {
  335.         if (!found_start) {
  336.             if (is_white(cmdline[i]))
  337.                 (*start)++;
  338.             else
  339.                 found_start = true;
  340.         } else {
  341.             if (is_white(cmdline[i]))
  342.                 break;
  343.         }
  344.     }
  345.     *end = i - 1;
  346.  
  347.     return found_start;
  348. }
  349.  
  350.  
  351. /** List supported commands.
  352.  *
  353.  * @param argv Argument vector.
  354.  *
  355.  * @return 0 on failure, 1 on success.
  356.  */
  357. int cmd_help(cmd_arg_t *argv)
  358. {
  359.     link_t *cur;
  360.     ipl_t ipl;
  361.  
  362.     spinlock_lock(&cmd_lock);
  363.    
  364.     for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
  365.         cmd_info_t *hlp;
  366.        
  367.         hlp = list_get_instance(cur, cmd_info_t, link);
  368.         spinlock_lock(&hlp->lock);
  369.        
  370.         printf("%s - %s\n", hlp->name, hlp->description);
  371.  
  372.         spinlock_unlock(&hlp->lock);
  373.     }
  374.    
  375.     spinlock_unlock(&cmd_lock);
  376.  
  377.     return 1;
  378. }
  379.  
  380. /** Describe specified command.
  381.  *
  382.  * @param argv Argument vector.
  383.  *
  384.  * @return 0 on failure, 1 on success.
  385.  */
  386. int cmd_desc(cmd_arg_t *argv)
  387. {
  388.     link_t *cur;
  389.     ipl_t ipl;
  390.  
  391.     spinlock_lock(&cmd_lock);
  392.    
  393.     for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
  394.         cmd_info_t *hlp;
  395.        
  396.         hlp = list_get_instance(cur, cmd_info_t, link);
  397.         spinlock_lock(&hlp->lock);
  398.  
  399.         if (strncmp(hlp->name, (const char *) argv->buffer, strlen(hlp->name)) == 0) {
  400.             printf("%s - %s\n", hlp->name, hlp->description);
  401.             if (hlp->help)
  402.                 hlp->help();
  403.             spinlock_unlock(&hlp->lock);
  404.             break;
  405.         }
  406.  
  407.         spinlock_unlock(&hlp->lock);
  408.     }
  409.    
  410.     spinlock_unlock(&cmd_lock);
  411.  
  412.     return 1;
  413. }
  414.  
  415. /** Print detailed description of 'describe' command. */
  416. void desc_help(void)
  417. {
  418.     printf("Syntax: describe command_name\n");
  419. }
  420.  
  421. /** Halt the kernel.
  422.  *
  423.  * @param argv Argument vector (ignored).
  424.  *
  425.  * @return 0 on failure, 1 on success (never returns).
  426.  */
  427. int cmd_halt(cmd_arg_t *argv)
  428. {
  429.     halt();
  430.     return 1;
  431. }
  432.