Contiki 3.x
shell.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2008, Swedish Institute of Computer Science.
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  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * The shell application
36  * \author
37  * Adam Dunkels <adam@sics.se>
38  */
39 
40 /**
41  * \addtogroup shell
42  * @{
43  */
44 
45 #include "contiki.h"
46 #include "contiki-lib.h"
47 
48 #include "net/rime/rime.h"
49 
50 #include "shell.h"
51 
52 #include <ctype.h>
53 #include <string.h>
54 #include <stdio.h>
55 
56 #ifdef SHELL_CONF_PROMPT
57 extern char shell_prompt_text[];
58 #else
59 static char shell_prompt_text[] = "Contiki> ";
60 #endif
61 
62 #ifdef SHELL_CONF_BANNER
63 extern char shell_banner_text[];
64 #else
65 static char shell_banner_text[] = "Contiki command shell";
66 #endif
67 
68 LIST(commands);
69 
71 
72 static struct process *front_process;
73 
74 static unsigned long time_offset;
75 
76 PROCESS(shell_process, "Shell");
77 PROCESS(shell_server_process, "Shell server");
78 /*---------------------------------------------------------------------------*/
79 PROCESS(help_command_process, "help");
80 SHELL_COMMAND(help_command, "help", "help: shows this help",
81  &help_command_process);
82 SHELL_COMMAND(question_command, "?", "?: shows this help",
83  &help_command_process);
84 PROCESS(shell_killall_process, "killall");
85 SHELL_COMMAND(killall_command, "killall", "killall: stop all running commands",
86  &shell_killall_process);
87 PROCESS(shell_kill_process, "kill");
88 SHELL_COMMAND(kill_command, "kill", "kill <command>: stop a specific command",
89  &shell_kill_process);
90 PROCESS(shell_null_process, "null");
91 SHELL_COMMAND(null_command, "null", "null: discard input",
92  &shell_null_process);
93 PROCESS(shell_exit_process, "exit");
94 SHELL_COMMAND(exit_command, "exit", "exit: exit shell",
95  &shell_exit_process);
96 SHELL_COMMAND(quit_command, "quit", "quit: exit shell",
97  &shell_exit_process);
98 /*---------------------------------------------------------------------------*/
99 PROCESS_THREAD(shell_null_process, ev, data)
100 {
101  struct shell_input *input;
102  PROCESS_BEGIN();
103  while(1) {
105  input = data;
106 
107  if(input->len1 + input->len2 == 0) {
108  PROCESS_EXIT();
109  }
110  }
111  PROCESS_END();
112 }
113 /*---------------------------------------------------------------------------*/
114 static void
115 command_kill(struct shell_command *c)
116 {
117  if(c != NULL) {
118  shell_output_str(&killall_command, "Stopping command ", c->command);
119  process_exit(c->process);
120  }
121 }
122 /*---------------------------------------------------------------------------*/
123 static void
124 killall(void)
125 {
126  struct shell_command *c;
127  for(c = list_head(commands);
128  c != NULL;
129  c = c->next) {
130  if(c != &killall_command && process_is_running(c->process)) {
131  command_kill(c);
132  }
133  }
134 }
135 /*---------------------------------------------------------------------------*/
136 PROCESS_THREAD(shell_killall_process, ev, data)
137 {
138 
139  PROCESS_BEGIN();
140 
141  killall();
142 
143  PROCESS_END();
144 }
145 /*---------------------------------------------------------------------------*/
146 PROCESS_THREAD(shell_kill_process, ev, data)
147 {
148  struct shell_command *c;
149  char *name;
150  PROCESS_BEGIN();
151 
152  name = data;
153  if(name == NULL || strlen(name) == 0) {
154  shell_output_str(&kill_command,
155  "kill <command>: command name must be given", "");
156  }
157 
158  for(c = list_head(commands);
159  c != NULL;
160  c = c->next) {
161  if(strcmp(name, c->command) == 0 &&
162  c != &kill_command &&
163  process_is_running(c->process)) {
164  command_kill(c);
165  PROCESS_EXIT();
166  }
167  }
168 
169  shell_output_str(&kill_command, "Command not found: ", name);
170 
171  PROCESS_END();
172 }
173 /*---------------------------------------------------------------------------*/
174 PROCESS_THREAD(help_command_process, ev, data)
175 {
176  struct shell_command *c;
177  PROCESS_BEGIN();
178 
179  shell_output_str(&help_command, "Available commands:", "");
180  for(c = list_head(commands);
181  c != NULL;
182  c = c->next) {
183  shell_output_str(&help_command, c->description, "");
184  }
185 
186  PROCESS_END();
187 }
188 /*---------------------------------------------------------------------------*/
189 PROCESS_THREAD(shell_exit_process, ev, data)
190 {
191  PROCESS_BEGIN();
192 
193  shell_exit();
194 
195  PROCESS_END();
196 }
197 /*---------------------------------------------------------------------------*/
198 static void
199 replace_braces(char *commandline)
200 {
201  char *ptr;
202  int level = 0;
203 
204  for(ptr = commandline; *ptr != 0; ++ptr) {
205  if(*ptr == '{') {
206  if(level == 0) {
207  *ptr = ' ';
208  }
209  ++level;
210  } else if(*ptr == '}') {
211  --level;
212  if(level == 0) {
213  *ptr = ' ';
214  }
215  }
216  }
217 }
218 /*---------------------------------------------------------------------------*/
219 static char *
220 find_pipe(char *commandline)
221 {
222  char *ptr;
223  int level = 0;
224 
225  for(ptr = commandline; *ptr != 0; ++ptr) {
226  if(*ptr == '{') {
227  ++level;
228  } else if(*ptr == '}') {
229  --level;
230  } else if(*ptr == '|') {
231  if(level == 0) {
232  return ptr;
233  }
234  }
235  }
236  return NULL;
237 }
238 /*---------------------------------------------------------------------------*/
239 static struct shell_command *
240 start_command(char *commandline, struct shell_command *child)
241 {
242  char *next, *args;
243  int command_len;
244  struct shell_command *c;
245 
246  /* Shave off any leading spaces. */
247  while(*commandline == ' ') {
248  commandline++;
249  }
250 
251  /* Find the next command in a pipeline and start it. */
252  next = find_pipe(commandline);
253  if(next != NULL) {
254  *next = 0;
255  child = start_command(next + 1, child);
256  }
257 
258  /* Separate the command arguments, and remove braces. */
259  replace_braces(commandline);
260  args = strchr(commandline, ' ');
261  if(args != NULL) {
262  args++;
263  }
264 
265  /* Shave off any trailing spaces. */
266  command_len = (int)strlen(commandline);
267  while(command_len > 0 && commandline[command_len - 1] == ' ') {
268  commandline[command_len - 1] = 0;
269  command_len--;
270  }
271 
272  if(args == NULL) {
273  command_len = (int)strlen(commandline);
274  args = &commandline[command_len];
275  } else {
276  command_len = (int)(args - commandline - 1);
277  }
278 
279 
280 
281  /* Go through list of commands to find a match for the first word in
282  the command line. */
283  for(c = list_head(commands);
284  c != NULL &&
285  !(strncmp(c->command, commandline, command_len) == 0 &&
286  c->command[command_len] == 0);
287  c = c->next);
288 
289  if(c == NULL) {
290  shell_output_str(NULL, commandline, ": command not found (try 'help')");
291  command_kill(child);
292  c = NULL;
293  } else if(process_is_running(c->process) || child == c) {
294  shell_output_str(NULL, commandline, ": command already running");
295  c->child = NULL;
296  c = NULL;
297  } else {
298  c->child = child;
299  /* printf("shell: start_command starting '%s'\n", c->process->name);*/
300  /* Start a new process for the command. */
301  process_start(c->process, (void *)args);
302  }
303 
304  return c;
305 }
306 /*---------------------------------------------------------------------------*/
307 int
308 shell_start_command(char *commandline, int commandline_len,
309  struct shell_command *child,
310  struct process **started_process)
311 {
312  struct shell_command *c;
313  int background = 0;
314 
315  if(commandline_len == 0) {
316  if(started_process != NULL) {
317  *started_process = NULL;
318  }
319  return SHELL_NOTHING;
320  }
321 
322  if(commandline[commandline_len - 1] == '&') {
323  commandline[commandline_len - 1] = 0;
324  background = 1;
325  commandline_len--;
326  }
327 
328  c = start_command(commandline, child);
329 
330  /* Return a pointer to the started process, so that the caller can
331  wait for the process to complete. */
332  if(c != NULL && started_process != NULL) {
333  *started_process = c->process;
334  if(background) {
335  return SHELL_BACKGROUND;
336  } else {
337  return SHELL_FOREGROUND;
338  }
339  }
340  return SHELL_NOTHING;
341 }
342 /*---------------------------------------------------------------------------*/
343 static void
344 input_to_child_command(struct shell_command *c,
345  char *data1, int len1,
346  const char *data2, int len2)
347 {
348  struct shell_input input;
349  if(process_is_running(c->process)) {
350  input.data1 = data1;
351  input.len1 = len1;
352  input.data2 = data2;
353  input.len2 = len2;
354  process_post_synch(c->process, shell_event_input, &input);
355  }
356 }
357 /*---------------------------------------------------------------------------*/
358 void
359 shell_input(char *commandline, int commandline_len)
360 {
361  struct shell_input input;
362 
363  /* printf("shell_input front_process '%s'\n", front_process->name);*/
364 
365  if(commandline[0] == '~' &&
366  commandline[1] == 'K') {
367  /* process_start(&shell_killall_process, (void *)commandline);*/
368  if(front_process != &shell_process) {
369  process_exit(front_process);
370  }
371  } else {
372  if(process_is_running(front_process)) {
373  input.data1 = commandline;
374  input.len1 = commandline_len;
375  input.data2 = "";
376  input.len2 = 0;
377  process_post_synch(front_process, shell_event_input, &input);
378  }
379  }
380 }
381 /*---------------------------------------------------------------------------*/
382 void
383 shell_output_str(struct shell_command *c, char *text1, const char *text2)
384 {
385  if(c != NULL && c->child != NULL) {
386  input_to_child_command(c->child, text1, (int)strlen(text1),
387  text2, (int)strlen(text2));
388  } else {
389  shell_default_output(text1, (int)strlen(text1),
390  text2, (int)strlen(text2));
391  }
392 }
393 /*---------------------------------------------------------------------------*/
394 void
396  void *data1, int len1,
397  const void *data2, int len2)
398 {
399  if(c != NULL && c->child != NULL) {
400  input_to_child_command(c->child, data1, len1, data2, len2);
401  } else {
402  shell_default_output(data1, len1, data2, len2);
403  }
404 }
405 /*---------------------------------------------------------------------------*/
406 void
408 {
409  list_remove(commands, c);
410 }
411 /*---------------------------------------------------------------------------*/
412 void
414 {
415  struct shell_command *i, *p;
416 
417  p = NULL;
418  for(i = list_head(commands);
419  i != NULL &&
420  strcmp(i->command, c->command) < 0;
421  i = i->next) {
422  p = i;
423  }
424  if(p == NULL) {
425  list_push(commands, c);
426  } else if(i == NULL) {
427  list_add(commands, c);
428  } else {
429  list_insert(commands, p, c);
430  }
431 }
432 /*---------------------------------------------------------------------------*/
433 PROCESS_THREAD(shell_process, ev, data)
434 {
435  static struct process *started_process;
436  struct shell_input *input;
437  int ret;
438  PROCESS_BEGIN();
439 
440  /* Let the system start up before showing the prompt. */
441  PROCESS_PAUSE();
442 
443  while(1) {
444  shell_prompt(shell_prompt_text);
445 
447  {
448  input = data;
449  ret = shell_start_command(input->data1, input->len1, NULL,
450  &started_process);
451 
452  if(started_process != NULL &&
453  ret == SHELL_FOREGROUND &&
454  process_is_running(started_process)) {
455  front_process = started_process;
456  PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_EXITED &&
457  data == started_process);
458  }
459  front_process = &shell_process;
460  }
461  }
462 
463  PROCESS_END();
464 }
465 /*---------------------------------------------------------------------------*/
466 PROCESS_THREAD(shell_server_process, ev, data)
467 {
468  struct process *p;
469  struct shell_command *c;
470  static struct etimer etimer;
471  PROCESS_BEGIN();
472 
474  while(1) {
476  if(ev == PROCESS_EVENT_EXITED) {
477  p = data;
478  /* printf("process exited '%s' (front '%s')\n", p->name,
479  front_process->name);*/
480  for(c = list_head(commands);
481  c != NULL && c->process != p;
482  c = c->next);
483  while(c != NULL) {
484  if(c->child != NULL && c->child->process != NULL) {
485  /* printf("Killing '%s'\n", c->process->name);*/
486  input_to_child_command(c->child, "", 0, "", 0);
487  /* process_exit(c->process);*/
488  }
489  c = c->child;
490  }
491  } else if(ev == PROCESS_EVENT_TIMER) {
493  shell_set_time(shell_time());
494  }
495  }
496 
497  PROCESS_END();
498 }
499 /*---------------------------------------------------------------------------*/
500 void
502 {
503  list_init(commands);
504  shell_register_command(&help_command);
505  shell_register_command(&question_command);
506  shell_register_command(&killall_command);
507  shell_register_command(&kill_command);
508  shell_register_command(&null_command);
509  shell_register_command(&exit_command);
510  shell_register_command(&quit_command);
511 
513 
514  process_start(&shell_process, NULL);
515  process_start(&shell_server_process, NULL);
516 
517  front_process = &shell_process;
518 }
519 /*---------------------------------------------------------------------------*/
520 unsigned long
521 shell_strtolong(const char *str, const char **retstr)
522 {
523  int i;
524  unsigned long num = 0;
525  const char *strptr = str;
526 
527  if(str == NULL) {
528  return 0;
529  }
530 
531  while(*strptr == ' ') {
532  ++strptr;
533  }
534 
535  for(i = 0; i < 10 && isdigit(strptr[i]); ++i) {
536  num = num * 10 + strptr[i] - '0';
537  }
538  if(retstr != NULL) {
539  if(i == 0) {
540  *retstr = str;
541  } else {
542  *retstr = strptr + i;
543  }
544  }
545 
546  return num;
547 }
548 /*---------------------------------------------------------------------------*/
549 unsigned long
550 shell_time(void)
551 {
552  return clock_seconds() + time_offset;
553 }
554 /*---------------------------------------------------------------------------*/
555 void
556 shell_set_time(unsigned long seconds)
557 {
558  time_offset = seconds - clock_seconds();
559 }
560 /*---------------------------------------------------------------------------*/
561 void
563 {
564  shell_output_str(NULL, shell_banner_text, "");
565  shell_output_str(NULL, "Type '?' and return for help", "");
566  shell_prompt(shell_prompt_text);
567 }
568 /*---------------------------------------------------------------------------*/
569 void
571 {
572  killall();
573 }
574 /*---------------------------------------------------------------------------*/
575 void
577 {
578  shell_stop();
579  process_exit(&shell_process);
580  process_exit(&shell_server_process);
581 }
582 /*---------------------------------------------------------------------------*/
583 
584 /** @} */
void shell_prompt(char *str)
Print a prompt.
Definition: serial-shell.c:82
int process_is_running(struct process *p)
Check if a process is running.
Definition: process.c:383
void shell_stop(void)
Stop the shell.
Definition: shell.c:570
void shell_init(void)
Initialize the shell.
Definition: shell.c:501
void shell_output_str(struct shell_command *c, char *text1, const char *text2)
Output strings from a shell command.
Definition: shell.c:383
int shell_start_command(char *commandline, int commandline_len, struct shell_command *child, struct process **started_process)
Start a shell command from another shell command.
Definition: shell.c:308
void list_push(list_t list, void *item)
Add an item to the start of the list.
Definition: list.c:165
#define PROCESS_EXIT()
Exit the currently running process.
Definition: process.h:200
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
void process_post_synch(struct process *p, process_event_t ev, process_data_t data)
Post a synchronous event to a process.
Definition: process.c:362
Main header file for the Contiki shell
#define NULL
The null pointer.
void shell_input(char *commandline, int commandline_len)
Send a line of input to the shell.
Definition: shell.c:359
void shell_quit(void)
Quit the shell.
Definition: shell.c:576
process_event_t process_alloc_event(void)
Allocate a global event number.
Definition: process.c:93
void shell_start(void)
Start the shell.
Definition: shell.c:562
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:240
Header file for the Rime stack
void shell_output(struct shell_command *c, void *data1, int len1, const void *data2, int len2)
Output data from a shell command.
Definition: shell.c:395
void list_insert(list_t list, void *previtem, void *newitem)
Insert an item after a specified item on the list.
Definition: list.c:303
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
unsigned long shell_strtolong(const char *str, const char **retstr)
Convert a string to a number.
Definition: shell.c:521
void list_init(list_t list)
Initialize a list.
Definition: list.c:66
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
Structure for shell input data.
Definition: shell.h:365
void shell_register_command(struct shell_command *c)
Register a command with the shell.
Definition: shell.c:413
#define PROCESS_PAUSE()
Yield the process for a short while.
Definition: process.h:221
#define PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
Definition: process.h:157
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
#define LIST(name)
Declare a linked list.
Definition: list.h:86
CCIF unsigned long clock_seconds(void)
Get the current value of the platform seconds.
Definition: clock.c:57
void etimer_reset(struct etimer *et)
Reset an event timer with the same interval as was previously set.
Definition: etimer.c:184
int shell_event_input
The event number for shell input data.
Definition: shell.c:70
void shell_unregister_command(struct shell_command *c)
Unregister a previously registered shell command.
Definition: shell.c:407
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition: etimer.c:177
Holds a information about a shell command.
Definition: shell.h:73
void shell_default_output(const char *text1, int len1, const char *text2, int len2)
Print a line of output from the shell.
Definition: serial-shell.c:58
void shell_exit(void)
Request shell exit.
Definition: serial-shell.c:89
A timer.
Definition: etimer.h:76
void process_exit(struct process *p)
Cause a process to exit.
Definition: process.c:202
#define SHELL_COMMAND(name, command, description, process)
Define a shell command.
Definition: shell.h:219
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82