/* * @(#) mshell.c - 'MultiShell' for Unix. * (c) 1995 Ivan Maidanski http://ivmai.chat.ru * Freeware program source. All rights reserved. ** * Language: C * Tested with: BSD GNU CC * Last modified: 1995-12-19 14:05:00 GMT+03:00 */ #include #include #include #include #include #include #include /* #include */ /* #include */ /* for terminal input */ #define SHPROMPT "(mshell) " typedef int pid_t; typedef int fd_t; typedef enum {FALSE,TRUE} tbool; typedef enum { PRGNO,PRGLOAD,PRGRUN, PRGEXIT,PRGSTOP,PRGKILL, PRGEXITD,PRGSTOPD } tpstat; typedef struct tplist { pid_t pid; tpstat stat; fd_t fin[2],fout[2]; char *name; struct tplist *prev,*next; } tplist; static char *sh_commands[]= /* in lower case */ { "cd","pwd","exit", "task","tasklist","taskeof", "deltask","killtask", "newshell","help","?",0 }; static char *errmsg[]= { " Error: No enough memory.\n", " Error: No memory for Fork.\n", " Error: Cannot execute program.\n", " Error: No one task loaded.\n", " Error: Cannot change current directory.\n", " Error: Cannot send this symbol to task.\n", " Error: Background mode...\n" }; static tplist *plist=0; static tbool breakvar; tbool ins_new_task(char *name,fd_t fin[2],fd_t fout[2]) { tplist *task=(tplist *)malloc(sizeof(tplist)); if (!task) return FALSE; task->fin[0]=fin[0]; task->fin[1]=fin[1]; task->fout[0]=fout[0]; task->fout[1]=fout[1]; task->name=(char *)malloc(strlen(name)+1); if (!task->name) { free((void *)task); return FALSE; } strcpy(task->name,name); if (plist) { task->prev=plist->prev; task->next=plist; task->next->prev=task->prev->next=task; } else task->prev=task->next=task; plist=task; return TRUE; } void del_one_task(tplist *ptr) { close(ptr->fin[0]); close(ptr->fin[1]); close(ptr->fout[0]); close(ptr->fout[1]); free((void *)ptr->name); ptr->prev->next=ptr->next; ptr->next->prev=ptr->prev; if (ptr==plist) plist=ptr->next; if (ptr==plist) plist=0; free((void *)ptr); } tpstat get_prog_status(pid_t *pid) { union wait stat; *pid=wait3(&stat,WNOHANG | WUNTRACED,0); if (*pid>0) if (WIFEXITED(stat)) return PRGEXIT; else if (WIFSTOPPED(stat)) return PRGSTOP; else return PRGKILL; return PRGNO; } tplist *find_prog_pid(pid_t pid) { tplist *ptr=plist; if (plist) do if (ptr->pid==pid) return ptr; while ((ptr=ptr->next)!=plist); return 0; } tbool get_term_info(void) { tbool event=FALSE; tplist *ptr; pid_t pid; tpstat stat; while ((stat=get_prog_status(&pid))!=PRGNO) { event=TRUE; if ((ptr=find_prog_pid(pid))) ptr->stat=(ptr->stat==PRGLOAD) ? PRGNO : stat; } return event; } void through_del_tasks(void) { tplist *ptr; do { ptr=plist; if (plist) do if (ptr->stat==PRGNO) { del_one_task(ptr); break; } while ((ptr=ptr->next)!=plist); } while (ptr!=plist); } pid_t run_prog(char *name,fd_t fldin,fd_t fldout) { pid_t pid=fork(); if (!pid) { char *sh=(char *)getenv("SHELL"); dup2(fldin,0); dup2(fldout,1); dup2(fldout,2); if (name) execl(sh,sh,"-c",name,0); else execl(sh,sh,0); exit(1); } return pid; } void execprog(char *name) { fd_t fin[2],fout[2]; if (pipe(fin) || pipe(fout)) printf(errmsg[0]); else if (ins_new_task(name ? name : "Shell...",fin,fout)==FALSE) { printf(errmsg[0]); close(fin[0]); close(fin[1]); close(fout[0]); close(fout[1]); } else { fcntl(fout[0],F_SETFL,O_NDELAY); if ((plist->pid=run_prog(name,fin[0],fout[1]))<0) { printf(errmsg[1]); del_one_task(plist); } else plist->stat=PRGRUN; } } void print_task_list(void) { int n=0; tplist *ptr=plist; if (plist) do printf(" %2d. (%04d) %s\n",++n,ptr->pid,ptr->name); while ((ptr=ptr->next)!=plist); } void print_term_info(void) { int n=1; tplist *ptr=plist; if (plist) do switch (ptr->stat) { case PRGKILL: printf(" Task killed: %2d. (%04d) %s\n", n,ptr->pid,ptr->name); ptr->stat=PRGNO; break; case PRGSTOPD: printf(" Task stopped: %2d. (%04d) %s\n", n,ptr->pid,ptr->name); ptr->stat=PRGNO; break; case PRGEXITD: printf(" Task stopped: %2d. (%04d) %s\n", n,ptr->pid,ptr->name); ptr->stat=PRGNO; break; case PRGNO: printf(" Cannot load program (%s).\n",ptr->name); default: break; } while (n++,(ptr=ptr->next)!=plist); } int find_lexem(char **list,char *lexem) { int n; for (n=0;list[n];n++) if (!strcmp(lexem,list[n])) return n+1; return 0; } void exit_shell(void) { while (plist) { kill(plist->pid,SIGKILL); del_one_task(plist); } printf(" Shell exiting...\n\n"); signal(SIGINT,SIG_IGN); close(0); } void goto_task(int n) { while (--n) plist=plist->next; printf(" %s\n",plist->name); } void send_symbol(unsigned char ch) { if (write(plist->fin[1],&ch,sizeof(unsigned char))<=0) printf(errmsg[5]); } int receive_symbol(void) { unsigned char ch; if (read(plist->fout[0],&ch,sizeof(unsigned char))<=0) return -1; return (int)ch; } void send_all_symbols(void) { int c; if ((c=getchar())==EOF || c=='\x1B') breakvar=TRUE; else if (plist->fin[1]>=0) send_symbol((unsigned char)c); if (c=='\x1B') getchar(); } void receive_all_symbols(void) { int c; while ((c=receive_symbol())>=0) putchar(c); } tbool interprete(char *cmd) { int n; char *arg=strchr(cmd,' '); if (arg) *arg='\0'; n=find_lexem(sh_commands,cmd); if (arg) *arg=' '; if (arg) do arg++; while (*arg==' ' || *arg=='\t'); switch (n) { case 1: /* cd */ if (chdir(arg ? arg : (char *)getenv("HOME"))) printf(errmsg[4]); break; case 2: /* pwd */ { char path[MAXPATHLEN]; printf(" %s\n",getwd(path,MAXPATHLEN)); break; } case 3: /* exit */ if (plist) { int c; printf(" There are unstopped jobs. Exit (y/n)? "); if ((c=getchar())!=EOF) getchar(); if (c!=EOF && c!='Y' && c!='y') return FALSE; } return TRUE; case 4: /* task */ if (plist) goto_task(arg ? atoi(arg) : 2); else printf(errmsg[3]); break; case 5: /* tasklist */ if (plist) print_task_list(); else printf(errmsg[3]); break; case 6: /* taskeof */ if (plist) if (plist->fin[1]>=0) { close(plist->fin[0]); close(plist->fin[1]); plist->fin[1]=-1; } else printf(errmsg[5]); else printf(errmsg[3]); break; case 7: /* deltask */ if (plist) kill(plist->pid,SIGTERM); else printf(errmsg[3]); break; case 8: /* killtask */ if (plist) { kill(plist->pid,SIGKILL); del_one_task(plist); } else printf(errmsg[3]); break; case 9: /* newshell */ printf("Loading New Command Shell...\n"); execprog(0); break; case 10: /* help */ case 11: /* ? */ printf("Shell Commands:\n" " cd - change working directory\n" " pwd - show work directory\n" " exit - exit from shell\n" " task - change current task\n" " tasklist - show list of loaded tasks\n" " taskeof - send eof-symbol to task\n" " deltask - terminate current task\n" " killtask - kill current task\n" " newshell - load another standard shell\n" " help - show this help info\n" " ? - show this help info\n" "Shell Control symbols:\n" " [Escape] (task) - jump to shell\n" " [Enter] (monitor) - jump to task\n" " [Tab] (monitor) - show tasks list\n" " [Ctrl-C] (monitor) - reset monitor\n" " [Ctrl-D] (monitor) - exit shell\n" "Shell Task Loading:\n" " - load and run another task\n"); break; default: /* load */ printf("Loading...\n"); execprog(cmd); } return FALSE; } tbool task_mode(void) { tbool term; printf("\n"); do { receive_all_symbols(); term=get_term_info(); if (plist->stat==PRGEXIT || plist->stat==PRGSTOP) { plist->stat=(plist->stat==PRGEXIT) ? PRGEXITD : PRGSTOPD; term=TRUE; } if (term==FALSE && breakvar==FALSE) send_all_symbols(); } while (term==FALSE && breakvar==FALSE); return term; } int getstr(char *s,int len) { int c,i=0; len--; do { if (i