Subversion Repositories HelenOS

Rev

Rev 534 | Rev 548 | 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);
  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);
  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);
  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);
  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.     ipl = interrupts_disable();
  153.     spinlock_lock(&cmd_lock);
  154.    
  155.     /*
  156.      * Make sure the command is not already listed.
  157.      */
  158.     for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
  159.         cmd_info_t *hlp;
  160.        
  161.         hlp = list_get_instance(cur, cmd_info_t, link);
  162.  
  163.         if (hlp == cmd) {
  164.             /* The command is already there. */
  165.             spinlock_unlock(&cmd_lock);
  166.             interrupts_restore(ipl);
  167.             return 0;
  168.         }
  169.  
  170.         /* Avoid deadlock. */
  171.         if (hlp < cmd) {
  172.             spinlock_lock(&hlp->lock);
  173.             spinlock_lock(&cmd->lock);
  174.         } else {
  175.             spinlock_lock(&cmd->lock);
  176.             spinlock_lock(&hlp->lock);
  177.         }
  178.        
  179.         if ((strncmp(hlp->name, cmd->name, strlen(cmd->name)) == 0)) {
  180.             /* The command is already there. */
  181.             spinlock_unlock(&hlp->lock);
  182.             spinlock_unlock(&cmd->lock);
  183.             spinlock_unlock(&cmd_lock);
  184.             interrupts_restore(ipl);
  185.             return 0;
  186.         }
  187.        
  188.         spinlock_unlock(&hlp->lock);
  189.         spinlock_unlock(&cmd->lock);
  190.     }
  191.    
  192.     /*
  193.      * Now the command can be added.
  194.      */
  195.     list_append(&cmd->link, &cmd_head);
  196.    
  197.     spinlock_unlock(&cmd_lock);
  198.     interrupts_restore(ipl);
  199.     return 1;
  200. }
  201.  
  202. /** Kernel console managing thread.
  203.  *
  204.  * @param arg Not used.
  205.  */
  206. void kconsole(void *arg)
  207. {
  208.     char cmdline[MAX_CMDLINE+1];
  209.     cmd_info_t *cmd_info;
  210.     count_t len;
  211.  
  212.     if (!stdin) {
  213.         printf("%s: no stdin\n", __FUNCTION__);
  214.         return;
  215.     }
  216.    
  217.     while (true) {
  218.         printf("%s> ", __FUNCTION__);
  219.         if (!(len = gets(stdin, cmdline, sizeof(cmdline))))
  220.             continue;
  221.         cmdline[len] = '\0';
  222.         cmd_info = parse_cmdline(cmdline, len);
  223.         if (!cmd_info)
  224.             continue;
  225.         (void) cmd_info->func(cmd_info->argv);
  226.     }
  227. }
  228.  
  229. /** Parse command line.
  230.  *
  231.  * @param cmdline Command line as read from input device.
  232.  * @param len Command line length.
  233.  *
  234.  * @return Structure describing the command.
  235.  */
  236. cmd_info_t *parse_cmdline(char *cmdline, size_t len)
  237. {
  238.     index_t start = 0, end = 0;
  239.     cmd_info_t *cmd = NULL;
  240.     link_t *cur;
  241.     ipl_t ipl;
  242.     int i;
  243.    
  244.     if (!parse_argument(cmdline, len, &start, &end)) {
  245.         /* Command line did not contain alphanumeric word. */
  246.         return NULL;
  247.     }
  248.  
  249.     ipl = interrupts_disable();
  250.     spinlock_lock(&cmd_lock);
  251.    
  252.     for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
  253.         cmd_info_t *hlp;
  254.        
  255.         hlp = list_get_instance(cur, cmd_info_t, link);
  256.         spinlock_lock(&hlp->lock);
  257.        
  258.         if (strncmp(hlp->name, &cmdline[start], (end - start) + 1) == 0) {
  259.             cmd = hlp;
  260.             break;
  261.         }
  262.        
  263.         spinlock_unlock(&hlp->lock);
  264.     }
  265.    
  266.     spinlock_unlock(&cmd_lock);
  267.    
  268.     if (!cmd) {
  269.         /* Unknown command. */
  270.         printf("Unknown command.\n");
  271.         interrupts_restore(ipl);
  272.         return NULL;
  273.     }
  274.  
  275.     /* cmd == hlp is locked */
  276.    
  277.     /*
  278.      * The command line must be further analyzed and
  279.      * the parameters therefrom must be matched and
  280.      * converted to those specified in the cmd info
  281.      * structure.
  282.      */
  283.  
  284.     for (i = 0; i < cmd->argc; i++) {
  285.         char *buf;
  286.         start = end + 1;
  287.         if (!parse_argument(cmdline, len, &start, &end)) {
  288.             printf("Too few arguments.\n");
  289.             spinlock_unlock(&cmd->lock);
  290.             interrupts_restore(ipl);
  291.             return NULL;
  292.         }
  293.        
  294.         switch (cmd->argv[i].type) {
  295.             case ARG_TYPE_STRING:
  296.                 buf = cmd->argv[i].buffer;
  297.                 strncpy(buf, (const char *) &cmdline[start], min((end - start) + 1, cmd->argv[i].len - 1));
  298.             buf[min((end - start) + 1, cmd->argv[i].len - 1)] = '\0';
  299.             break;
  300.             case ARG_TYPE_INT:
  301.             case ARG_TYPE_INVALID:
  302.             default:
  303.             panic("invalid argument type\n");
  304.             break;
  305.         }
  306.     }
  307.    
  308.     start = end + 1;
  309.     if (parse_argument(cmdline, len, &start, &end)) {
  310.         printf("Too many arguments.\n");
  311.         spinlock_unlock(&cmd->lock);
  312.         interrupts_restore(ipl);
  313.         return NULL;
  314.     }
  315.    
  316.     spinlock_unlock(&cmd->lock);
  317.     interrupts_restore(ipl);
  318.     return cmd;
  319. }
  320.  
  321. /** Parse argument.
  322.  *
  323.  * Find start and end positions of command line argument.
  324.  *
  325.  * @param cmdline Command line as read from the input device.
  326.  * @param len Number of characters in cmdline.
  327.  * @param start On entry, 'start' contains pointer to the index
  328.  *        of first unprocessed character of cmdline.
  329.  *        On successful exit, it marks beginning of the next argument.
  330.  * @param end Undefined on entry. On exit, 'end' points to the last character
  331.  *        of the next argument.
  332.  *
  333.  * @return false on failure, true on success.
  334.  */
  335. bool parse_argument(char *cmdline, size_t len, index_t *start, index_t *end)
  336. {
  337.     int i;
  338.     bool found_start = false;
  339.    
  340.     ASSERT(start != NULL);
  341.     ASSERT(end != NULL);
  342.    
  343.     for (i = *start; i < len; i++) {
  344.         if (!found_start) {
  345.             if (is_white(cmdline[i]))
  346.                 (*start)++;
  347.             else
  348.                 found_start = true;
  349.         } else {
  350.             if (is_white(cmdline[i]))
  351.                 break;
  352.         }
  353.     }
  354.     *end = i - 1;
  355.  
  356.     return found_start;
  357. }
  358.  
  359.  
  360. /** List supported commands.
  361.  *
  362.  * @param argv Argument vector.
  363.  *
  364.  * @return 0 on failure, 1 on success.
  365.  */
  366. int cmd_help(cmd_arg_t *argv)
  367. {
  368.     link_t *cur;
  369.     ipl_t ipl;
  370.  
  371.     ipl = interrupts_disable();
  372.     spinlock_lock(&cmd_lock);
  373.    
  374.     for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
  375.         cmd_info_t *hlp;
  376.        
  377.         hlp = list_get_instance(cur, cmd_info_t, link);
  378.         spinlock_lock(&hlp->lock);
  379.        
  380.         printf("%s - %s\n", hlp->name, hlp->description);
  381.  
  382.         spinlock_unlock(&hlp->lock);
  383.     }
  384.    
  385.     spinlock_unlock(&cmd_lock);
  386.     interrupts_restore(ipl);
  387.  
  388.     return 1;
  389. }
  390.  
  391. /** Describe specified command.
  392.  *
  393.  * @param argv Argument vector.
  394.  *
  395.  * @return 0 on failure, 1 on success.
  396.  */
  397. int cmd_desc(cmd_arg_t *argv)
  398. {
  399.     link_t *cur;
  400.     ipl_t ipl;
  401.  
  402.     ipl = interrupts_disable();
  403.     spinlock_lock(&cmd_lock);
  404.    
  405.     for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
  406.         cmd_info_t *hlp;
  407.        
  408.         hlp = list_get_instance(cur, cmd_info_t, link);
  409.         spinlock_lock(&hlp->lock);
  410.  
  411.         if (strncmp(hlp->name, (const char *) argv->buffer, strlen(hlp->name)) == 0) {
  412.             printf("%s - %s\n", hlp->name, hlp->description);
  413.             if (hlp->help)
  414.                 hlp->help();
  415.             spinlock_unlock(&hlp->lock);
  416.             break;
  417.         }
  418.  
  419.         spinlock_unlock(&hlp->lock);
  420.     }
  421.    
  422.     spinlock_unlock(&cmd_lock);
  423.     interrupts_restore(ipl);
  424.  
  425.     return 1;
  426. }
  427.  
  428. /** Halt the kernel.
  429.  *
  430.  * @param argv Argument vector.
  431.  *
  432.  * @return 0 on failure, 1 on success.
  433.  */
  434. int cmd_halt(cmd_arg_t *argv)
  435. {
  436.     halt();
  437.     return 1;
  438. }
  439.  
  440. /** Print detailed description of 'describe' command. */
  441. void desc_help(void)
  442. {
  443.     printf("Syntax: describe command_name\n");
  444. }
  445.