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

4.104.114.84.95
Last change on this file since b2712e3 was b2712e3, checked in by Joel Sherrill <joel.sherrill@…>, on 05/24/01 at 21:58:39

2000-05-24 Fernando Ruiz Casas <fernando.ruiz@…>

  • monitor/mon-prmisc.c: Correct print line.
  • shell/Makefile.am: Added new file telnetd.c.
  • shell/telnetd.c, shell/telnetd.h, shell/pty.c: New files.
  • shell/shell.c, shell/cmds.c, shell/shell.h: Numerous improvments:
    • The shell_init has a new parameter 'forever' because in /dev/console you need that this process runs forever but in tcp/ip not. (respawn?)
    • A new task for every session opened trought tcp/ip telnet client. (the chargen,daytime and more are possible of implementation but I ask me if they are necesary)
    • Exit from the session delete the task and when the client fails too.
    • More cmds have been implemented. (very reduced version of these) umask, chmod, id, whoami, rm, cat, ...
    • A reduced line edit has been implemented.

Ctrl-C abort the input,
Ctrl-d in the first position gives EOF (logout).
'\b' and DEL makes the rubout operation.
I think that readline() for every session spents a lot of resources.

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