source: mainline/kernel/generic/src/console/kconsole.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 19.2 KB
Line 
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/** @addtogroup kernel_generic_console
30 * @{
31 */
32
33/**
34 * @file kconsole.c
35 * @brief Kernel console.
36 *
37 * This file contains kernel thread managing the kernel console.
38 *
39 */
40
41#include <assert.h>
42#include <console/kconsole.h>
43#include <console/console.h>
44#include <console/chardev.h>
45#include <console/cmd.h>
46#include <console/prompt.h>
47#include <stdio.h>
48#include <panic.h>
49#include <typedefs.h>
50#include <adt/list.h>
51#include <arch.h>
52#include <macros.h>
53#include <debug.h>
54#include <halt.h>
55#include <str.h>
56#include <sysinfo/sysinfo.h>
57#include <symtab.h>
58#include <errno.h>
59#include <putchar.h>
60#include <stdlib.h>
61
62/** Simple kernel console.
63 *
64 * The console is realized by kernel thread kconsole.
65 * It doesn't understand any useful command on its own,
66 * but makes it possible for other kernel subsystems to
67 * register their own commands.
68 */
69
70/** Locking.
71 *
72 * There is a list of cmd_info_t structures. This list
73 * is protected by cmd_lock spinlock. Note that specially
74 * the link elements of cmd_info_t are protected by
75 * this lock.
76 *
77 * Each cmd_info_t also has its own lock, which protects
78 * all elements thereof except the link element.
79 *
80 * cmd_lock must be acquired before any cmd_info lock.
81 * When locking two cmd info structures, structure with
82 * lower address must be locked first.
83 */
84
85SPINLOCK_INITIALIZE(cmd_lock); /**< Lock protecting command list. */
86LIST_INITIALIZE(cmd_list); /**< Command list. */
87
88static wchar_t history[KCONSOLE_HISTORY][MAX_CMDLINE] = { };
89static size_t history_pos = 0;
90
91/** Initialize kconsole data structures
92 *
93 * This is the most basic initialization, almost no
94 * other kernel subsystem is ready yet.
95 *
96 */
97void kconsole_init(void)
98{
99 unsigned int i;
100
101 cmd_init();
102 for (i = 0; i < KCONSOLE_HISTORY; i++)
103 history[i][0] = 0;
104}
105
106/** Register kconsole command.
107 *
108 * @param cmd Structure describing the command.
109 *
110 * @return False on failure, true on success.
111 *
112 */
113bool cmd_register(cmd_info_t *cmd)
114{
115 spinlock_lock(&cmd_lock);
116
117 /*
118 * Make sure the command is not already listed.
119 */
120 list_foreach(cmd_list, link, cmd_info_t, hlp) {
121 if (hlp == cmd) {
122 /* The command is already there. */
123 spinlock_unlock(&cmd_lock);
124 return false;
125 }
126
127 /* Avoid deadlock. */
128 if (hlp < cmd) {
129 spinlock_lock(&hlp->lock);
130 spinlock_lock(&cmd->lock);
131 } else {
132 spinlock_lock(&cmd->lock);
133 spinlock_lock(&hlp->lock);
134 }
135
136 if (str_cmp(hlp->name, cmd->name) == 0) {
137 /* The command is already there. */
138 spinlock_unlock(&hlp->lock);
139 spinlock_unlock(&cmd->lock);
140 spinlock_unlock(&cmd_lock);
141 return false;
142 }
143
144 spinlock_unlock(&hlp->lock);
145 spinlock_unlock(&cmd->lock);
146 }
147
148 /*
149 * Now the command can be added.
150 */
151 list_append(&cmd->link, &cmd_list);
152
153 spinlock_unlock(&cmd_lock);
154 return true;
155}
156
157/** Print count times a character */
158NO_TRACE static void print_cc(wchar_t ch, size_t count)
159{
160 size_t i;
161 for (i = 0; i < count; i++)
162 putwchar(ch);
163}
164
165/** Try to find a command beginning with prefix */
166const char *cmdtab_enum(const char *name, const char **h, void **ctx)
167{
168 link_t **startpos = (link_t **) ctx;
169 size_t namelen = str_code_points(name);
170
171 spinlock_lock(&cmd_lock);
172
173 if (*startpos == NULL)
174 *startpos = cmd_list.head.next;
175
176 for (; *startpos != &cmd_list.head; *startpos = (*startpos)->next) {
177 cmd_info_t *hlp = list_get_instance(*startpos, cmd_info_t, link);
178
179 const char *curname = hlp->name;
180 if (str_code_points(curname) < namelen)
181 continue;
182
183 if (str_lcmp(curname, name, namelen) == 0) {
184 *startpos = (*startpos)->next;
185 if (h)
186 *h = hlp->description;
187
188 spinlock_unlock(&cmd_lock);
189 return (curname + str_lbytes(curname, namelen));
190 }
191 }
192
193 spinlock_unlock(&cmd_lock);
194 return NULL;
195}
196
197/** Command completion of the commands
198 *
199 * @param name String to match, changed to hint on exit
200 * @param size Input buffer size
201 *
202 * @return Number of found matches
203 *
204 */
205NO_TRACE static int cmdtab_compl(char *input, size_t size, indev_t *indev,
206 hints_enum_func_t hints_enum)
207{
208 const char *name = input;
209
210 size_t found = 0;
211
212 /*
213 * Maximum Match Length: Length of longest matching common
214 * substring in case more than one match is found.
215 */
216 size_t max_match_len = size;
217 size_t max_match_len_tmp = size;
218 void *pos = NULL;
219 const char *hint;
220 const char *help;
221 size_t hints_to_show = MAX_TAB_HINTS - 1;
222 size_t total_hints_shown = 0;
223 bool continue_showing_hints = true;
224
225 char *output = malloc(MAX_CMDLINE);
226 if (!output) {
227 // TODO: fix the function so that it does not need allocation
228 printf("Can't complete command, out of memory.\n");
229 return 0;
230 }
231
232 output[0] = 0;
233
234 while ((hint = hints_enum(name, NULL, &pos))) {
235 if ((found == 0) || (str_code_points(hint) > str_code_points(output)))
236 str_cpy(output, MAX_CMDLINE, hint);
237
238 found++;
239 }
240
241 /*
242 * If the number of possible completions is more than MAX_TAB_HINTS,
243 * ask the user whether to display them or not.
244 */
245 if (found > MAX_TAB_HINTS) {
246 printf("\n");
247 continue_showing_hints =
248 console_prompt_display_all_hints(indev, found);
249 }
250
251 if ((found > 1) && (str_code_points(output) != 0)) {
252 printf("\n");
253 pos = NULL;
254 while ((hint = hints_enum(name, &help, &pos))) {
255
256 if (continue_showing_hints) {
257 if (help)
258 printf("%s%s (%s)\n", name, hint, help);
259 else
260 printf("%s%s\n", name, hint);
261
262 --hints_to_show;
263 ++total_hints_shown;
264
265 if ((hints_to_show == 0) && (total_hints_shown != found)) {
266 /* Ask user to continue */
267 continue_showing_hints =
268 console_prompt_more_hints(indev, &hints_to_show);
269 }
270 }
271
272 max_match_len_tmp = 0;
273 while ((output[max_match_len_tmp] ==
274 hint[max_match_len_tmp]) &&
275 (max_match_len_tmp < max_match_len))
276 ++max_match_len_tmp;
277
278 max_match_len = max_match_len_tmp;
279 }
280
281 /* Keep only the characters common in all completions */
282 output[max_match_len] = 0;
283 }
284
285 if (found > 0)
286 str_cpy(input, size, output);
287
288 free(output);
289 return found;
290}
291
292NO_TRACE static cmd_info_t *parse_cmd(const wchar_t *cmdline)
293{
294 size_t start = 0;
295 size_t end;
296 char *tmp;
297
298 while (isspace(cmdline[start]))
299 start++;
300
301 end = start + 1;
302
303 while (!isspace(cmdline[end]))
304 end++;
305
306 tmp = malloc(STR_BOUNDS(end - start + 1));
307 if (!tmp)
308 return NULL;
309
310 wstr_to_str(tmp, end - start + 1, &cmdline[start]);
311
312 spinlock_lock(&cmd_lock);
313
314 list_foreach(cmd_list, link, cmd_info_t, hlp) {
315 spinlock_lock(&hlp->lock);
316
317 if (str_cmp(hlp->name, tmp) == 0) {
318 spinlock_unlock(&hlp->lock);
319 spinlock_unlock(&cmd_lock);
320 free(tmp);
321 return hlp;
322 }
323
324 spinlock_unlock(&hlp->lock);
325 }
326
327 free(tmp);
328 spinlock_unlock(&cmd_lock);
329
330 return NULL;
331}
332
333NO_TRACE static wchar_t *clever_readline(const char *prompt, indev_t *indev,
334 char *tmp)
335{
336 printf("%s> ", prompt);
337
338 size_t position = 0;
339 wchar_t *current = history[history_pos];
340 current[0] = 0;
341
342 while (true) {
343 wchar_t ch = indev_pop_character(indev);
344
345 if (ch == '\n') {
346 /* Enter */
347 putwchar(ch);
348 break;
349 }
350
351 if (ch == '\b') {
352 /* Backspace */
353 if (position == 0)
354 continue;
355
356 if (wstr_remove(current, position - 1)) {
357 position--;
358 putwchar('\b');
359 printf("%ls ", current + position);
360 print_cc('\b', wstr_code_points(current) - position + 1);
361 continue;
362 }
363 }
364
365 if (ch == '\t') {
366 /* Tab completion */
367
368 /* Move to the end of the word */
369 for (; (current[position] != 0) && (!isspace(current[position]));
370 position++)
371 putwchar(current[position]);
372
373 /*
374 * Find the beginning of the word
375 * and copy it to tmp
376 */
377 size_t beg;
378 unsigned narg = 0;
379 if (position == 0) {
380 tmp[0] = '\0';
381 beg = 0;
382 } else {
383 beg = position - 1;
384 while ((beg > 0) && (!isspace(current[beg])))
385 beg--;
386
387 if (isspace(current[beg]))
388 beg++;
389
390 wstr_to_str(tmp, position - beg + 1, current + beg);
391 }
392
393 /* Count which argument number are we tabbing (narg=0 is cmd) */
394 bool sp = false;
395 for (; beg > 0; beg--) {
396 if (isspace(current[beg])) {
397 if (!sp) {
398 narg++;
399 sp = true;
400 }
401 } else
402 sp = false;
403 }
404
405 if (narg && isspace(current[0]))
406 narg--;
407
408 int found;
409 if (narg == 0) {
410 /* Command completion */
411 found = cmdtab_compl(tmp, STR_BOUNDS(MAX_CMDLINE), indev,
412 cmdtab_enum);
413 } else {
414 /* Arguments completion */
415 cmd_info_t *cmd = parse_cmd(current);
416 if (!cmd || !cmd->hints_enum || cmd->argc < narg)
417 continue;
418 found = cmdtab_compl(tmp, STR_BOUNDS(MAX_CMDLINE), indev,
419 cmd->hints_enum);
420 }
421
422 if (found == 0)
423 continue;
424
425 /*
426 * We have hints, possibly many. In case of more than one hint,
427 * tmp will contain the common prefix.
428 */
429 size_t off = 0;
430 size_t i = 0;
431 while ((ch = str_decode(tmp, &off, STR_NO_LIMIT)) != 0) {
432 if (!wstr_linsert(current, ch, position + i, MAX_CMDLINE))
433 break;
434
435 i++;
436 }
437
438 if (found > 1) {
439 /* No unique hint, list was printed */
440 printf("%s> ", prompt);
441 printf("%ls", current);
442 position += str_code_points(tmp);
443 print_cc('\b', wstr_code_points(current) - position);
444 continue;
445 }
446
447 /* We have a hint */
448
449 printf("%ls", current + position);
450 position += str_code_points(tmp);
451 print_cc('\b', wstr_code_points(current) - position);
452
453 if (position == wstr_code_points(current)) {
454 /* Insert a space after the last completed argument */
455 if (wstr_linsert(current, ' ', position, MAX_CMDLINE)) {
456 printf("%ls", current + position);
457 position++;
458 }
459 }
460 continue;
461 }
462
463 if (ch == U_LEFT_ARROW) {
464 /* Left */
465 if (position > 0) {
466 putwchar('\b');
467 position--;
468 }
469 continue;
470 }
471
472 if (ch == U_RIGHT_ARROW) {
473 /* Right */
474 if (position < wstr_code_points(current)) {
475 putwchar(current[position]);
476 position++;
477 }
478 continue;
479 }
480
481 if ((ch == U_UP_ARROW) || (ch == U_DOWN_ARROW)) {
482 /* Up, down */
483 print_cc('\b', position);
484 print_cc(' ', wstr_code_points(current));
485 print_cc('\b', wstr_code_points(current));
486
487 if (ch == U_UP_ARROW) {
488 /* Up */
489 if (history_pos == 0)
490 history_pos = KCONSOLE_HISTORY - 1;
491 else
492 history_pos--;
493 } else {
494 /* Down */
495 history_pos++;
496 history_pos = history_pos % KCONSOLE_HISTORY;
497 }
498 current = history[history_pos];
499 printf("%ls", current);
500 position = wstr_code_points(current);
501 continue;
502 }
503
504 if (ch == U_HOME_ARROW) {
505 /* Home */
506 print_cc('\b', position);
507 position = 0;
508 continue;
509 }
510
511 if (ch == U_END_ARROW) {
512 /* End */
513 printf("%ls", current + position);
514 position = wstr_code_points(current);
515 continue;
516 }
517
518 if (ch == U_DELETE) {
519 /* Delete */
520 if (position == wstr_code_points(current))
521 continue;
522
523 if (wstr_remove(current, position)) {
524 printf("%ls ", current + position);
525 print_cc('\b', wstr_code_points(current) - position + 1);
526 }
527 continue;
528 }
529
530 if (wstr_linsert(current, ch, position, MAX_CMDLINE)) {
531 printf("%ls", current + position);
532 position++;
533 print_cc('\b', wstr_code_points(current) - position);
534 }
535 }
536
537 if (wstr_code_points(current) > 0) {
538 history_pos++;
539 history_pos = history_pos % KCONSOLE_HISTORY;
540 }
541
542 return current;
543}
544
545bool kconsole_check_poll(void)
546{
547 return check_poll(stdin);
548}
549
550NO_TRACE static bool parse_int_arg(const char *text, size_t len,
551 sysarg_t *result)
552{
553 bool isaddr = false;
554 bool isptr = false;
555
556 /* If we get a name, try to find it in symbol table */
557 if (text[0] == '&') {
558 isaddr = true;
559 text++;
560 len--;
561 } else if (text[0] == '*') {
562 isptr = true;
563 text++;
564 len--;
565 }
566
567 if ((text[0] < '0') || (text[0] > '9')) {
568 char symname[MAX_SYMBOL_NAME];
569 str_ncpy(symname, MAX_SYMBOL_NAME, text, len + 1);
570
571 uintptr_t symaddr;
572 errno_t rc = symtab_addr_lookup(symname, &symaddr);
573 switch (rc) {
574 case ENOENT:
575 printf("Symbol %s not found.\n", symname);
576 return false;
577 case EOVERFLOW:
578 printf("Duplicate symbol %s.\n", symname);
579 symtab_print_search(symname);
580 return false;
581 case ENOTSUP:
582 printf("No symbol information available.\n");
583 return false;
584 case EOK:
585 if (isaddr)
586 *result = (sysarg_t) symaddr;
587 else if (isptr)
588 *result = **((sysarg_t **) symaddr);
589 else
590 *result = *((sysarg_t *) symaddr);
591 break;
592 default:
593 printf("Unknown error.\n");
594 return false;
595 }
596 } else {
597 /* It's a number - convert it */
598 uint64_t value;
599 char *end;
600 errno_t rc = str_uint64_t(text, &end, 0, false, &value);
601 if (end != text + len)
602 rc = EINVAL;
603 switch (rc) {
604 case EINVAL:
605 printf("Invalid number '%s'.\n", text);
606 return false;
607 case EOVERFLOW:
608 printf("Integer overflow in '%s'.\n", text);
609 return false;
610 case EOK:
611 *result = (sysarg_t) value;
612 if (isptr)
613 *result = *((sysarg_t *) *result);
614 break;
615 default:
616 printf("Unknown error parsing '%s'.\n", text);
617 return false;
618 }
619 }
620
621 return true;
622}
623
624/** Parse argument.
625 *
626 * Find start and end positions of command line argument.
627 *
628 * @param cmdline Command line as read from the input device.
629 * @param size Size (in bytes) of the string.
630 * @param start On entry, 'start' contains pointer to the offset
631 * of the first unprocessed character of cmdline.
632 * On successful exit, it marks beginning of the next argument.
633 * @param end Undefined on entry. On exit, 'end' is the offset of the first
634 * character behind the next argument.
635 *
636 * @return False on failure, true on success.
637 *
638 */
639NO_TRACE static bool parse_argument(const char *cmdline, size_t size,
640 size_t *start, size_t *end)
641{
642 assert(start != NULL);
643 assert(end != NULL);
644
645 bool found_start = false;
646 size_t offset = *start;
647 size_t prev = *start;
648 wchar_t ch;
649
650 while ((ch = str_decode(cmdline, &offset, size)) != 0) {
651 if (!found_start) {
652 if (!isspace(ch)) {
653 *start = prev;
654 found_start = true;
655 }
656 } else {
657 if (isspace(ch))
658 break;
659 }
660
661 prev = offset;
662 }
663 *end = prev;
664
665 return found_start;
666}
667
668/** Parse command line.
669 *
670 * @param cmdline Command line as read from input device.
671 * @param size Size (in bytes) of the string.
672 *
673 * @return Structure describing the command.
674 *
675 */
676NO_TRACE static cmd_info_t *parse_cmdline(const char *cmdline, size_t size)
677{
678 size_t start = 0;
679 size_t end = 0;
680 if (!parse_argument(cmdline, size, &start, &end)) {
681 /* Command line did not contain alphanumeric word. */
682 return NULL;
683 }
684 spinlock_lock(&cmd_lock);
685
686 cmd_info_t *cmd = NULL;
687
688 list_foreach(cmd_list, link, cmd_info_t, hlp) {
689 spinlock_lock(&hlp->lock);
690
691 if (str_lcmp(hlp->name, cmdline + start,
692 max(str_code_points(hlp->name),
693 str_ncode_points(cmdline + start, (size_t) (end - start)))) == 0) {
694 cmd = hlp;
695 break;
696 }
697
698 spinlock_unlock(&hlp->lock);
699 }
700
701 spinlock_unlock(&cmd_lock);
702
703 if (!cmd) {
704 /* Unknown command. */
705 printf("Unknown command.\n");
706 return NULL;
707 }
708
709 /* cmd == hlp is locked */
710
711 /*
712 * The command line must be further analyzed and
713 * the parameters therefrom must be matched and
714 * converted to those specified in the cmd info
715 * structure.
716 */
717
718 bool error = false;
719 size_t i;
720 for (i = 0; i < cmd->argc; i++) {
721 char *buf;
722
723 start = end;
724 if (!parse_argument(cmdline, size, &start, &end)) {
725 if (cmd->argv[i].type == ARG_TYPE_STRING_OPTIONAL) {
726 buf = (char *) cmd->argv[i].buffer;
727 str_cpy(buf, cmd->argv[i].len, "");
728 continue;
729 }
730
731 printf("Too few arguments.\n");
732 spinlock_unlock(&cmd->lock);
733 return NULL;
734 }
735
736 switch (cmd->argv[i].type) {
737 case ARG_TYPE_STRING:
738 case ARG_TYPE_STRING_OPTIONAL:
739 buf = (char *) cmd->argv[i].buffer;
740 str_ncpy(buf, cmd->argv[i].len, cmdline + start,
741 end - start);
742 break;
743 case ARG_TYPE_INT:
744 if (!parse_int_arg(cmdline + start, end - start,
745 &cmd->argv[i].intval))
746 error = true;
747 break;
748 case ARG_TYPE_VAR:
749 if ((start < end - 1) && (cmdline[start] == '"')) {
750 if (cmdline[end - 1] == '"') {
751 buf = (char *) cmd->argv[i].buffer;
752 str_ncpy(buf, cmd->argv[i].len,
753 cmdline + start + 1,
754 (end - start) - 1);
755 cmd->argv[i].intval = (sysarg_t) buf;
756 cmd->argv[i].vartype = ARG_TYPE_STRING;
757 } else {
758 printf("Wrong syntax.\n");
759 error = true;
760 }
761 } else if (parse_int_arg(cmdline + start,
762 end - start, &cmd->argv[i].intval)) {
763 cmd->argv[i].vartype = ARG_TYPE_INT;
764 } else {
765 printf("Unrecognized variable argument.\n");
766 error = true;
767 }
768 break;
769 case ARG_TYPE_INVALID:
770 default:
771 printf("Invalid argument type\n");
772 error = true;
773 break;
774 }
775 }
776
777 if (error) {
778 spinlock_unlock(&cmd->lock);
779 return NULL;
780 }
781
782 start = end;
783 if (parse_argument(cmdline, size, &start, &end)) {
784 printf("Too many arguments.\n");
785 spinlock_unlock(&cmd->lock);
786 return NULL;
787 }
788
789 spinlock_unlock(&cmd->lock);
790 return cmd;
791}
792
793/** Kernel console prompt.
794 *
795 * @param prompt Kernel console prompt (e.g kconsole/panic).
796 * @param msg Message to display in the beginning.
797 * @param kcon Wait for keypress to show the prompt
798 * and never exit.
799 *
800 */
801void kconsole(const char *prompt, const char *msg, bool kcon)
802{
803 if (!stdin) {
804 LOG("No stdin for kernel console");
805 return;
806 }
807
808 if (msg)
809 printf("%s", msg);
810
811 if (kcon)
812 indev_pop_character(stdin);
813 else
814 printf("Type \"exit\" to leave the console.\n");
815
816 char *buffer = malloc(STR_BOUNDS(MAX_CMDLINE));
817 char *cmdline = malloc(STR_BOUNDS(MAX_CMDLINE));
818 if (!buffer || !cmdline) {
819 // TODO: fix the function so that it does not need allocations
820 printf("Can't start console, out of memory.\n");
821 free(buffer);
822 free(cmdline);
823 return;
824 }
825
826 while (true) {
827 wchar_t *tmp = clever_readline((char *) prompt, stdin, buffer);
828 size_t len = wstr_code_points(tmp);
829 if (!len)
830 continue;
831
832 wstr_to_str(cmdline, STR_BOUNDS(MAX_CMDLINE), tmp);
833
834 if ((!kcon) && (len == 4) && (str_lcmp(cmdline, "exit", 4) == 0))
835 break;
836
837 cmd_info_t *cmd_info = parse_cmdline(cmdline, STR_BOUNDS(MAX_CMDLINE));
838 if (!cmd_info)
839 continue;
840
841 (void) cmd_info->func(cmd_info->argv);
842 }
843 free(buffer);
844 free(cmdline);
845}
846
847/** Kernel console managing thread.
848 *
849 */
850void kconsole_thread(void *data)
851{
852 kconsole("kconsole", "Kernel console ready (press any key to activate)\n", true);
853}
854
855/** @}
856 */
Note: See TracBrowser for help on using the repository browser.