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

4.104.114.84.95
Last change on this file since 84206479 was 84206479, checked in by Ralf Corsepius <ralf.corsepius@…>, on 08/30/02 at 13:13:07

2002-08-30 Ralf Corsepius <corsepiu@…>

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