source: rtems/cpukit/libmisc/shell/shell.c @ dba2ecd7

4.104.114.84.95
Last change on this file since dba2ecd7 was dba2ecd7, checked in by Joel Sherrill <joel.sherrill@…>, on Jan 30, 2002 at 2:37:11 PM

2002-01-28 Ralf Corsepius <corsepiu@…>

  • shell/shell.c: Remove #undef STRICT_ANSI.
  • Property mode set to 100644
File size: 18.7 KB
Line 
1/*
2 *
3 *  Instantatiate a new terminal shell.
4 *
5 *  Author:
6 *
7 *   WORK: fernando.ruiz@ctv.es
8 *   HOME: correo@fernando-ruiz.com
9 *
10 *   Thanks at:
11 *    Chris John
12 *
13 *  $Id$
14 */
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include <stdio.h>
20
21#include <rtems.h>
22#include <rtems/error.h>
23#include <rtems/libio.h>
24#include <rtems/libio_.h> 
25#include <rtems/system.h> 
26#include <rtems/shell.h>
27
28#include <termios.h>
29#include <string.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <sys/stat.h>
33#include <unistd.h>
34#include <errno.h>
35#include <pwd.h>
36
37/* ----------------------------------------------- *
38 * This is a stupidity but is cute.
39 * ----------------------------------------------- */
40rtems_unsigned32 new_rtems_name(char * rtems_name) {
41        static char b[5];
42        sprintf(b,"%-4.4s",rtems_name);
43        return rtems_build_name(b[0],b[1],b[2],b[3]);
44}
45/* **************************************************************
46 * common linked list of shell commands.
47 * Because the help report is very long
48 * I have a topic for each command.
49 * Help list the topics
50 * help [topic] list the commands for the topic
51 * help [command] help for the command
52 * Can you see help rtems monitor report?
53 * ************************************************************** */
54
55struct shell_topic_tt;
56typedef struct shell_topic_tt shell_topic_t;
57
58struct shell_topic_tt {
59        char * topic;
60        shell_topic_t * next;
61};
62
63
64static shell_cmd_t   * shell_first_cmd;
65static shell_topic_t * shell_first_topic;
66/* ----------------------------------------------- *
67 * Using Chain I can reuse the rtems code.
68 * I am more comfortable with this, sorry.
69 * ----------------------------------------------- */
70shell_topic_t * shell_lookup_topic(char * topic) {
71  shell_topic_t * shell_topic;
72  shell_topic=shell_first_topic;
73  while (shell_topic) {
74   if (!strcmp(shell_topic->topic,topic)) return shell_topic;
75   shell_topic=shell_topic->next;
76  };
77  return (shell_topic_t *) NULL;
78}
79/* ----------------------------------------------- */
80shell_topic_t * shell_add_topic(char * topic) {
81 shell_topic_t * current,*aux;
82 if (!shell_first_topic) {
83  aux=malloc(sizeof(shell_topic_t));   
84  aux->topic=topic;
85  aux->next=(shell_topic_t*)NULL;
86  return shell_first_topic=aux;
87 } else {
88  current=shell_first_topic;
89  if (!strcmp(topic,current->topic)) return current;
90  while (current->next) {
91   if (!strcmp(topic,current->next->topic)) return current->next;
92   current=current->next;
93  }; 
94  aux=malloc(sizeof(shell_topic_t));   
95  aux->topic=topic;
96  aux->next=(shell_topic_t*)NULL;
97  current->next=aux;
98  return aux;
99 };
100}
101/* ----------------------------------------------- */
102shell_cmd_t * shell_lookup_cmd(char * cmd) {
103  shell_cmd_t * shell_cmd;
104  shell_cmd=shell_first_cmd;
105  while (shell_cmd) {
106   if (!strcmp(shell_cmd->name,cmd)) return shell_cmd;
107   shell_cmd=shell_cmd->next;
108  };
109  return (shell_cmd_t *) NULL;
110}
111/* ----------------------------------------------- */
112shell_cmd_t * shell_add_cmd(char * cmd,
113                      char * topic,
114                      char * usage,
115                      shell_command_t command) {
116  int shell_help(int argc,char * argv[]);
117  shell_cmd_t * shell_cmd,*shell_pvt;
118  if (!shell_first_cmd) {
119   shell_first_cmd=(shell_cmd_t *) malloc(sizeof(shell_cmd_t));
120   shell_first_cmd->name   ="help";
121   shell_first_cmd->topic  ="help";
122   shell_first_cmd->usage  ="help [topic] # list of usage of commands";
123   shell_first_cmd->command=shell_help;
124   shell_first_cmd->alias  =(shell_cmd_t *) NULL;
125   shell_first_cmd->next   =(shell_cmd_t *) NULL;
126   shell_add_topic(shell_first_cmd->topic);
127   register_cmds();
128  };
129  if (!cmd)     return (shell_cmd_t *) NULL;
130  if (!command) return (shell_cmd_t *) NULL;
131  shell_cmd=(shell_cmd_t *) malloc(sizeof(shell_cmd_t));
132  shell_cmd->name   =cmd;
133  shell_cmd->topic  =topic;
134  shell_cmd->usage  =usage;
135  shell_cmd->command=command;
136  shell_cmd->alias  =(shell_cmd_t *) NULL;
137  shell_cmd->next   =(shell_cmd_t *) NULL;
138  shell_add_topic(shell_cmd->topic);
139  shell_pvt=shell_first_cmd;
140  while (shell_pvt->next) shell_pvt=shell_pvt->next;
141  return shell_pvt->next=shell_cmd;
142}
143/* ----------------------------------------------- *
144 * you can make an alias for every command.
145 * ----------------------------------------------- */
146shell_cmd_t * shell_alias_cmd(char * cmd, char * alias) {
147  shell_cmd_t * shell_cmd,* shell_aux;
148  shell_aux=(shell_cmd_t *) NULL;
149  if (alias) {
150   if ((shell_aux=shell_lookup_cmd(alias))!=NULL) {
151    return NULL;
152   };
153   if ((shell_cmd=shell_lookup_cmd(cmd))!=NULL) {
154    shell_aux=shell_add_cmd(alias,shell_cmd->topic,
155                            shell_cmd->usage,shell_cmd->command);
156    if (shell_aux) shell_aux->alias=shell_cmd;
157   };
158  };
159  return shell_aux;
160}
161/* ----------------------------------------------- *
162 * Poor but enough..
163 * TODO: Redirection capture. "" evaluate, ... C&S welcome.
164 * ----------------------------------------------- */
165int shell_make_args(char * cmd,
166                 int  * pargc,
167                 char * argv[]) {
168  int argc=0;
169  while ((cmd=strtok(cmd," \t\r\n"))!=NULL) {
170    argv[argc++]=cmd;
171    cmd=(char*)NULL;
172   };
173  argv[argc]=(char*)NULL;
174  return *pargc=argc;
175}
176/* ----------------------------------------------- *
177 * show the help for one command.
178 * ----------------------------------------------- */
179int shell_help_cmd(shell_cmd_t * shell_cmd) {
180  char * pc;
181  int    col,line;
182  printf("%-10.10s -",shell_cmd->name);
183  col=12;
184  line=1;
185  if (shell_cmd->alias) {
186   printf("is an <alias> for command '%s'",shell_cmd->alias->name);
187  } else 
188  if (shell_cmd->usage) {
189   pc=shell_cmd->usage;
190   while (*pc) {
191    switch(*pc) {
192     case '\r':break;
193     case '\n':putchar('\n');
194               col=0;
195               break;
196     default  :putchar(*pc);
197               col++;
198               break;
199    };
200    pc++;           
201    if(col>78) { /* What daring... 78?*/
202     if (*pc) {
203      putchar('\n');
204      col=0;
205     };
206    };
207    if (!col && *pc) {
208      printf("            ");
209      col=12;line++;
210    }; 
211   };
212  };
213  puts("");
214  return line;
215}
216/* ----------------------------------------------- *
217 * show the help. The first command implemented.
218 * Can you see the header of routine? Known?
219 * The same with all the commands....
220 * ----------------------------------------------- */
221int shell_help(int argc,char * argv[]) {
222  int col,line,arg;     
223  shell_topic_t *topic;
224  shell_cmd_t * shell_cmd=shell_first_cmd;
225  if (argc<2) {
226   printf("help: ('r' repeat last cmd - 'e' edit last cmd)\n"     
227          "  TOPIC? The topics are\n");   
228   topic=shell_first_topic;
229   col=0;
230   while (topic) {
231    if (!col){
232     col=printf("   %s",topic->topic);
233    } else {
234     if ((col+strlen(topic->topic)+2)>78){
235      printf("\n");
236      col=printf("   %s",topic->topic);
237     } else {
238      col+=printf(", %s",topic->topic);
239     }; 
240    };
241    topic=topic->next;
242   };
243   printf("\n");
244   return 1;
245  };
246  line=0;
247  for (arg=1;arg<argc;arg++) {
248   if (line>16) {
249    printf("Press any key to continue...");getchar();
250    printf("\n");
251    line=0;
252   };
253   topic=shell_lookup_topic(argv[arg]);
254   if (!topic){
255    if ((shell_cmd=shell_lookup_cmd(argv[arg]))==NULL) {
256     printf("help: topic or cmd '%s' not found. Try <help> alone for a list\n",argv[arg]);
257     line++;
258    } else {
259     line+=shell_help_cmd(shell_cmd);
260    }
261    continue;
262   };
263   printf("help: list for the topic '%s'\n",argv[arg]);
264   line++;
265   while (shell_cmd) {
266    if (!strcmp(topic->topic,shell_cmd->topic)) 
267     line+=shell_help_cmd(shell_cmd);
268    if (line>16) {
269     printf("Press any key to continue...");getchar();
270     printf("\n");
271     line=0;
272    };
273    shell_cmd=shell_cmd->next;
274   };
275  }; 
276  puts("");
277  return 0;
278}
279/* ----------------------------------------------- *
280 * TODO: Add improvements. History, edit vi or emacs, ...
281 * ----------------------------------------------- */
282int shell_scanline(char * line,int size,FILE * in,FILE * out) {
283  int c,col;
284  col=0;
285  if (*line) {
286   col=strlen(line);     
287   if (out) fprintf(out,"%s",line);
288  };
289  tcdrain(fileno(in ));
290  if (out) tcdrain(fileno(out));
291  for (;;) {
292   line[col]=0;   
293   c=fgetc(in);
294   switch (c) {
295    case 0x04:/*Control-d*/
296              if (col) break;
297    case EOF :return 0;
298    case '\n':break;
299    case '\f':if (out) fputc('\f',out);
300    case 0x03:/*Control-C*/
301              line[0]=0; 
302    case '\r':if (out) fputc('\n',out);
303              return 1;
304    case  127:
305    case '\b':if (col) {
306               if (out) {
307                fputc('\b',out);
308                fputc(' ',out);
309                fputc('\b',out);
310               };
311               col--;
312              } else {
313               if (out) fputc('\a',out);
314              };
315              break;
316    default  :if (!iscntrl(c)) {
317                if (col<size-1) {
318                 line[col++]=c; 
319                 if (out) fputc(c,out);
320                } else {
321                  if (out) fputc('\a',out);
322                };
323              } else {
324                if (out) 
325                 if (c=='\a') fputc('\a',out);
326              };
327              break;
328   };
329  };
330}
331/* ----------------------------------------------- *
332 * - The shell TASK                               
333 * Poor but enough..
334 * TODO: Redirection. Tty Signals. ENVVARs. Shell language.
335 * ----------------------------------------------- */
336shell_env_t global_shell_env ,
337          * current_shell_env=&global_shell_env; 
338
339extern char **environ;   
340
341void cat_file(FILE * out,char * name) {
342        FILE * fd;
343        int c;
344        if (out) {
345          fd=fopen(name,"r");
346          if (fd) {
347           while ((c=fgetc(fd))!=EOF) fputc(c,out);
348           fclose(fd);
349          };
350        }; 
351}
352
353void write_file(char * name,char * content) {
354        FILE * fd;
355        fd=fopen(name,"w");
356        if (fd) {
357         fwrite(content,1,strlen(content),fd);
358         fclose(fd);
359        }; 
360}
361
362void init_issue(void) {
363 static char issue_inited=FALSE;
364 struct stat buf;
365 if (issue_inited) return;
366 issue_inited=TRUE;
367 getpwnam("root"); /* dummy call to init /etc dir */
368 if (stat("/etc/issue",&buf)) 
369  write_file("/etc/issue",
370             "Welcome to @V\\n"
371             "Login into @S(@L)\\n");
372 if (stat("/etc/issue.net",&buf)) 
373  write_file("/etc/issue.net",
374             "Welcome to %v\n"
375             "running on %m\n");
376}
377
378int shell_login(FILE * in,FILE * out) {
379        FILE * fd;
380        int c;
381        time_t t;
382        int times;
383        char name[128];
384        char pass[128];
385        struct passwd * passwd;
386        init_issue();
387        setuid(0);
388        setgid(0);
389        rtems_current_user_env->euid=
390        rtems_current_user_env->egid=0;
391        if (out) {
392         if((current_shell_env->devname[5]!='p')||
393            (current_shell_env->devname[6]!='t')||
394            (current_shell_env->devname[7]!='y')) {
395          fd=fopen("/etc/issue","r");
396          if (fd) {
397           while ((c=fgetc(fd))!=EOF) {
398            if (c=='@')  {
399             switch(c=fgetc(fd)) {
400              case 'L':fprintf(out,"%s",current_shell_env->devname);
401                      break;
402              case 'B':fprintf(out,"0");
403                      break;
404              case 'T':
405              case 'D':time(&t);
406                      fprintf(out,"%s",ctime(&t));
407                      break;
408              case 'S':fprintf(out,"RTEMS");
409                      break;
410              case 'V':fprintf(out,"%s\n%s",_RTEMS_version,_Copyright_Notice);
411                      break;
412              case '@':fprintf(out,"@");
413                      break;
414              default :fprintf(out,"@%c",c);
415                      break;
416             };
417            } else 
418            if (c=='\\')  {
419             switch(c=fgetc(fd)) {
420              case '\\':fprintf(out,"\\");
421                      break;
422              case 'b':fprintf(out,"\b"); break;
423              case 'f':fprintf(out,"\f"); break;
424              case 'n':fprintf(out,"\n"); break;
425              case 'r':fprintf(out,"\r"); break;
426              case 's':fprintf(out," "); break;
427              case 't':fprintf(out,"\t"); break;
428              case '@':fprintf(out,"@"); break;
429             };
430            } else {
431             fputc(c,out);                             
432            }; 
433           };
434           fclose(fd);
435          } 
436         } else { 
437          fd=fopen("/etc/issue.net","r");
438          if (fd) {
439           while ((c=fgetc(fd))!=EOF) {
440            if (c=='%')  {
441             switch(c=fgetc(fd)) {
442              case 't':fprintf(out,"%s",current_shell_env->devname);
443                      break;
444              case 'h':fprintf(out,"0");
445                      break;
446              case 'D':fprintf(out," ");
447                      break;
448              case 'd':time(&t);
449                      fprintf(out,"%s",ctime(&t));
450                      break;
451              case 's':fprintf(out,"RTEMS");
452                      break;
453              case 'm':fprintf(out,"(" CPU_NAME "/" CPU_MODEL_NAME ")");
454                      break;
455              case 'r':fprintf(out,_RTEMS_version);
456                      break;
457              case 'v':fprintf(out,"%s\n%s",_RTEMS_version,_Copyright_Notice);
458                      break;
459              case '%':fprintf(out,"%%");
460                      break;
461              default :fprintf(out,"%%%c",c);
462                      break;
463             };
464            } else {
465             fputc(c,out);                             
466            }; 
467           };
468           fclose(fd);
469          } 
470         };
471        };
472        times=0;
473        strcpy(name,"");
474        strcpy(pass,"");
475        for (;;) {
476                times++;
477                if (times>3) break;
478                if (out) fprintf(out,"\nlogin: ");
479                if (!shell_scanline(name,sizeof(name),in,out )) break;
480                if (out) fprintf(out,"Password: ");
481                if (!shell_scanline(pass,sizeof(pass),in,NULL)) break;
482                if (out) fprintf(out,"\n");
483                if ((passwd=getpwnam(name))) {
484                 if (strcmp(passwd->pw_passwd,"!")) { /* valid user */
485                  setuid(passwd->pw_uid);                       
486                  setgid(passwd->pw_gid);                       
487                  rtems_current_user_env->euid=
488                  rtems_current_user_env->egid=0;
489                  chown(current_shell_env->devname,passwd->pw_uid,0);
490                  rtems_current_user_env->euid=passwd->pw_uid;                 
491                  rtems_current_user_env->egid=passwd->pw_gid;                 
492                  if (!strcmp(passwd->pw_passwd,"*")) {
493                   /* /etc/shadow */
494                   return 0;
495                  } else {
496                   /* crypt() */
497                   return 0;
498                  };
499                 };
500                };
501                if (out) fprintf(out,"Login incorrect\n");
502                strcpy(name,"");
503                strcpy(pass,"");
504        };
505        return -1;
506}
507
508rtems_task shell_shell(rtems_task_argument task_argument) {
509
510  shell_env_t * shell_env =(shell_env_t*) task_argument;
511  shell_cmd_t * shell_cmd;
512
513  rtems_status_code sc;
514
515  struct termios term; 
516  char * devname;
517
518  char curdir[256];
519  char cmd[256];
520  char last_cmd[256]; /* to repeat 'r' */
521  int  argc;
522  char * argv[128];
523
524  sc=rtems_task_variable_add(RTEMS_SELF,(void*)&current_shell_env,free);
525  if (sc!=RTEMS_SUCCESSFUL) {
526   rtems_error(sc,"rtems_task_variable_add(current_shell_env):");       
527   rtems_task_delete(RTEMS_SELF);
528  }; 
529
530  current_shell_env=shell_env; 
531 
532  sc=rtems_libio_set_private_env();
533  if (sc!=RTEMS_SUCCESSFUL) {
534   rtems_error(sc,"rtems_libio_set_private_env():");     
535   rtems_task_delete(RTEMS_SELF);
536  }; 
537
538
539  devname=shell_env->devname;
540  setuid(0);
541  setgid(0);
542  rtems_current_user_env->euid=
543  rtems_current_user_env->egid=0;
544
545  stdin =fopen(devname,"r+");
546   
547  if (!stdin) {
548   fprintf(stderr,"shell:unable to open stdin.%s:%s\n",devname,strerror(errno));
549   rtems_task_delete(RTEMS_SELF);
550  };
551  setvbuf(stdin,NULL,_IONBF,0); /* Not buffered*/
552  /* make a raw terminal,Linux MANuals */
553  if (tcgetattr (fileno(stdin), &term)>=0) {
554   term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
555   term.c_oflag &= ~OPOST;
556   term.c_oflag |= (OPOST|ONLCR); /* But with cr+nl on output */
557   term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
558   term.c_cflag  = CLOCAL | CREAD |(shell_env->tcflag);
559   term.c_cc[VMIN]  = 1;
560   term.c_cc[VTIME] = 0;
561   if (tcsetattr (fileno(stdin), TCSADRAIN, &term) < 0) {
562     fprintf(stderr,"shell:cannot set terminal attributes(%s)\n",devname);
563   };
564   stdout=fopen(devname,"r+");
565   if (!stdout) {
566    fprintf(stderr,"shell:unable to open stdout.%s:%s\n",devname,strerror(errno));
567   };
568   setvbuf(stdout,NULL,_IONBF,0); /* Not buffered*/
569   stderr=fopen(devname,"r+");
570   if (!stderr) {
571    printf("shell:unable to open stderr.%s:%s\n",devname,strerror(errno));
572   };
573   /* when the future user environment runs ok
574    * a freopen() reopens the terminals. Now this don't work
575    * (sorry but you can't use because FILENO_STDIN!=0. Better fileno(stdin))
576    */
577  };
578  shell_add_cmd(NULL,NULL,NULL,NULL); /* init the chain list*/
579  do {
580   /* Set again root user and root filesystem, side effect of set_priv..*/       
581  sc=rtems_libio_set_private_env();
582  if (sc!=RTEMS_SUCCESSFUL) {
583   rtems_error(sc,"rtems_libio_set_private_env():");     
584   rtems_task_delete(RTEMS_SELF);
585  };
586  if (!shell_login(stdin,stdout))  {
587   cat_file(stdout,"/etc/motd");
588   strcpy(last_cmd,"");
589   strcpy(cmd,"");
590   printf("\n"
591          "RTEMS SHELL (Ver.1.0-FRC):%s. "__DATE__". 'help' to list commands.\n",devname);
592   chdir("/"); /* XXX: chdir to getpwent homedir */
593   shell_env->exit_shell=FALSE;
594   for (;;) {
595    /* Prompt section */         
596    /* XXX: show_prompt user adjustable */
597    getcwd(curdir,sizeof(curdir));
598    printf("%s [%s] %c ",shell_env->taskname,curdir,geteuid()?'$':'#');
599    /* getcmd section */         
600    if (!shell_scanline(cmd,sizeof(cmd),stdin,stdout)) break; /*EOF*/
601    /* evaluate cmd section */   
602    if (!strcmp(cmd,"e")) {  /* edit last command */
603     strcpy(cmd,last_cmd);
604     continue;
605    } else 
606    if (!strcmp(cmd,"r")) {  /* repeat last command */
607     strcpy(cmd,last_cmd);
608    } else 
609    if (strcmp(cmd,"")) { /* only for get a new prompt */
610     strcpy(last_cmd,cmd);
611    }; 
612    /* exec cmd section */       
613    /* TODO:
614     *  To avoid user crash catch the signals.
615     *  Open a new stdio files with posibility of redirection *
616     *  Run in a new shell task background. (unix &)
617     *  Resuming. A little bash.
618     */   
619    if (shell_make_args(cmd,&argc,argv)) {
620     if ((shell_cmd=shell_lookup_cmd(argv[0]))!=NULL) {
621      shell_env->errorlevel=shell_cmd->command(argc,argv);
622     } else {
623      printf("shell:%s command not found\n",argv[0]);
624      shell_env->errorlevel=-1;
625     };
626    };
627    /* end exec cmd section */   
628    if (shell_env->exit_shell)  break;
629    cmd[0]=0;
630   };
631   printf("\nGoodbye from RTEMS SHELL :-(\n");
632  };
633  } while (shell_env->forever);
634  fclose(stdin );
635  fclose(stdout); 
636  fclose(stderr);
637  rtems_task_delete(RTEMS_SELF);
638}
639/* ----------------------------------------------- */
640rtems_status_code   shell_init (char * task_name,
641                                rtems_unsigned32    task_stacksize,
642                                rtems_task_priority task_priority,
643                                char * devname,
644                                tcflag_t tcflag,
645                                int forever) {
646 rtems_id task_id;
647 rtems_status_code sc;
648 shell_env_t * shell_env;
649 sc=rtems_task_create(new_rtems_name(task_name),
650                     task_priority,
651                     task_stacksize?task_stacksize:RTEMS_MINIMUM_STACK_SIZE,
652                     RTEMS_DEFAULT_MODES,
653                     RTEMS_DEFAULT_ATTRIBUTES,
654                     &task_id);
655 if (sc!=RTEMS_SUCCESSFUL) {
656  rtems_error(sc,"creating task %s in shell_init()",task_name); 
657  return sc;
658 }; 
659 shell_env=malloc(sizeof(shell_env_t));
660 if (!shell_env) {
661  rtems_task_delete(task_id);
662  sc=RTEMS_NO_MEMORY;   
663  rtems_error(sc,"allocating shell_env %s in shell_init()",task_name);   
664  return sc;
665 };
666 if (global_shell_env.magic!=new_rtems_name("SENV")) {
667  global_shell_env.magic     =new_rtems_name("SENV");
668  global_shell_env.devname   ="/dev/console";
669  global_shell_env.taskname  ="GLOBAL";
670  global_shell_env.tcflag    =0;
671  global_shell_env.exit_shell=0;
672  global_shell_env.forever   =TRUE;
673 }; 
674 shell_env->magic     =global_shell_env.magic; 
675 shell_env->devname   =devname;
676 shell_env->taskname  =task_name;
677 shell_env->tcflag    =tcflag;
678 shell_env->exit_shell=FALSE;
679 shell_env->forever   =forever;
680 return rtems_task_start(task_id,shell_shell,(rtems_task_argument) shell_env);
681}
Note: See TracBrowser for help on using the repository browser.