Subversion Repositories HelenOS-historic

Rev

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