/////////////////////////////////////////////////////////////////////////////// // File : ksh.c // Date : October 2017 // Author : Alain Greiner /////////////////////////////////////////////////////////////////////////////// // This single thread application implement a simple shell for ALMOS-MKH. /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #define CMD_MAX_SIZE (256) // max number of characters in one command #define LOG_DEPTH (256) // max number of registered commands #define MAX_ARGS (32) // max number of arguments in a command #define FIFO_SIZE (1024) // FIFO depth for recursive ls //////////////////////////////////////////////////////////////////////////////// // Structures //////////////////////////////////////////////////////////////////////////////// // one entry in the registered commands array typedef struct log_entry_s { char buf[CMD_MAX_SIZE]; unsigned int count; } log_entry_t; // one entry in the supported command types array typedef struct ksh_cmd_s { char * name; char * desc; void (*fn)( int , char ** ); } ksh_cmd_t; //////////////////////////////////////////////////////////////////////////////// // Global Variables //////////////////////////////////////////////////////////////////////////////// ksh_cmd_t cmd[]; // array of supported commands log_entry_t log_entries[LOG_DEPTH]; // array of registered commands unsigned int ptw; // write pointer in log_entries[] unsigned int ptr; // read pointer in log_entries[] //////////////////////////////////////////////////////////////////////////////// // Shell Commands //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////// static void cmd_cat( int argc , char **argv ) { char * path; if (argc != 2) { printf(" usage: cat pathname\n"); return; } path = argv[1]; printf(" error: not implemented yet\n"); /* // open the file fd = open( path , O_RDONLY , 0 ); if (fd < 0) { printf(" error: cannot open %s\n", path); goto exit; } // get file size if (stat(path, &st) == -1) { printf(" error: cannot stat %s\n", path); goto exit; } if (S_ISDIR(st.st_mode)) { printf(" error: %s is a directory\n", path); goto exit; } size = st.st_size; // mmap the file buf = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if (buf == NULL || buf == (char *)-1) { printf(" error: cannot map %s\n", path); goto exit; } // set terminating '0' XXX buf[size-1] = 0; // display the file content printf("%s", buf); exit: if (buf != NULL) munmap(buf, size); if (fd >= 0) close(fd); */ } // end cmd_cat() //////////////////////////////////////////// static void cmd_cd( int argc , char **argv ) { char * path; if (argc != 2) { printf(" usage: cd pathname\n"); return; } path = argv[1]; printf(" error: not implemented yet\n"); /* path = argv[1]; if (chdir(path) == -1) { printf(" error: cannot cd to %s\n", path); } */ } // end cmd_cd() ///////////////////////////////////////// static void cmd_cp(int argc, char **argv) { // int src_fd = -1, dst_fd = -1; // char *srcpath, *dstpath; // struct stat st; // size_t size, i; // char buf[1024]; if (argc != 3) { printf(" usage: cp src_pathname dst_pathname\n"); return; } printf(" error: not implemented yet\n"); /* srcpath = argv[1]; dstpath = argv[2]; // open the src file src_fd = open(srcpath, O_RDONLY, 0); if (src_fd < 0) { printf(" error: cannot open %s / err = %d\n", srcpath, errno); goto exit; } // get file size if (stat(srcpath, &st) == -1) { printf(" error: cannot stat %s\n", srcpath); goto exit; } if (S_ISDIR(st.st_mode)) { printf(" error: %s is a directory\n", srcpath); goto exit; } size = st.st_size; // open the dst file dst_fd = open(dstpath, O_CREAT|O_TRUNC|O_RDWR, 0); if (dst_fd < 0) { printf(" error: cannot open %s / err = %d\n", dstpath, errno); goto exit; } if (stat(dstpath, &st) == -1) { printf(" error: cannot stat %s\n", dstpath); goto exit; } if (S_ISDIR(st.st_mode)) { printf(" error: %s is a directory\n", dstpath); goto exit; } i = 0; while (i < size) { size_t rlen = (size - i < 1024 ? size - i : 1024); size_t wlen; ssize_t ret; // read the source ret = read(src_fd, buf, rlen); if (ret == -1) { printf(" error: cannot read from file %s\n", srcpath); goto exit; } rlen = (size_t)ret; // write to the destination ret = write(dst_fd, buf, rlen); if (ret == -1) { printf(" error: cannot write to file %s\n", dstpath); goto exit; } wlen = (size_t)ret; // check if (wlen != rlen) { printf(" error: cannot write on device\n"); goto exit; } i += rlen; } exit: if (src_fd >= 0) close(src_fd); if (dst_fd >= 0) close(dst_fd); */ } // end cmd_cp() ////////////////////////////////////////////// static void cmd_help( int argc , char **argv ) { unsigned int i; if (argc != 1) { printf(" usage: %s\n", argv[0]); return; } printf("available commands:\n"); for (i = 0 ; cmd[i].name ; i++) { printf("\t%s\t : %s\n", cmd[i].name , cmd[i].desc); } } // end cmd_help() ////////////////////////////////////////////// static void cmd_kill( int argc , char **argv ) { unsigned int pid; if (argc != 2) { printf(" usage: %s pid\n", argv[0]); return; } pid = atoi(argv[1]); if( kill( pid , 9 ) ) // TODO replace 9 by SIGKILL { printf(" error: unable to kill process %x\n", pid ); } } // end cmd_kill() ////////////////////////////////////////////// static void cmd_load( int argc , char **argv ) { unsigned int pid; unsigned int error; char * pathname; if (argc != 2) { printf(" usage: %s pathname \n", argv[0] ); return; } pathname = argv[1]; // fork system call pid = fork(); if (pid == 0) // it is the child process { // exec system call error = exec( pathname , NULL , NULL ); if( error ) { printf(" error: new process unable to exec <%s>\n", pathname ); exit(0); } } else if ( pid < 0 ) // it is a failure reported to parent { printf(" error: unable to fork\n"); } } // end cmd_load ///////////////////////////////////////////// static void cmd_log( int argc , char **argv ) { unsigned int i; printf("--- registered commands ---\n"); for (i = 0; i < LOG_DEPTH; i++) { printf(" - %zu\t: %s\n", i, &log_entries[i].buf); } } //////////////////////////////////////////// static void cmd_ls( int argc , char **argv ) { char * path; // struct dirent * file; // DIR *dir; if (argc == 1) { path = "."; } else if (argc == 2) { path = argv[1]; } else { printf(" usage: ls [path]\n"); return; } printf(" error: not implemented yet\n"); /* dir = opendir( path ); while ((file = readdir(dir)) != NULL) { printf(" %s\n", file->d_name); } closedir(dir); */ } /////////////////////////////////////////////// static void cmd_mkdir( int argc , char **argv ) { char * pathname; if (argc != 2) { printf(" usage: mkdir pathname\n"); return; } pathname = argv[1]; printf(" error: not implemented yet\n"); /* if ( mkdir( path, 0x700) == -1 ) { printf(" error: cannot create directory %s\n", path); } */ } //////////////////////////////////////////// static void cmd_mv( int argc , char **argv ) { if (argc < 3) { printf(" usage : %s src_pathname dst_pathname\n", argv[0]); return; } printf(" error: not implemented yet\n"); /* int ret = giet_fat_rename(argv[1], argv[2]); if (ret < 0) { printf(" error : cannot move %s to %s / err = %d\n", argv[1], argv[2], ret ); } */ } ///////////////////////////////////////////// static void cmd_pwd( int argc , char **argv ) { char buf[1024]; if (argc != 1) { printf(" usage: pwd\n"); return; } if ( getcwd( buf , 1024 ) ) { printf(" error: unable to get current directory\n"); } else { printf("%s\n", buf); } } //////////////////////////////////////////// static void cmd_rm( int argc , char **argv ) { char * pathname; if (argc != 2) { printf(" usage: rm pathname\n"); return; } pathname = argv[1]; printf(" error: not implemented yet\n"); /* if (remove(path) == -1) { printf(" error: cannot remove %s\n", path); } */ } /////////////////////////////////////////////// static void cmd_rmdir( int argc , char **argv ) { cmd_rm(argc, argv); } /////////////////////////////////////////////// static void cmd_sched( int argc , char **argv ) { unsigned int cxy; unsigned int lid; if (argc != 3) { printf(" usage: sched cxy lid\n"); return; } cxy = atoi(argv[1]); lid = atoi(argv[2]); if( get_sched( cxy , lid ) ) { printf(" error: illegal arguments\n"); } } ////////////////////////////////////////////////////////////////// // Array of commands ////////////////////////////////////////////////////////////////// ksh_cmd_t cmd[] = { { "cat", "display file content", cmd_cat }, { "cd", "change current directory", cmd_cd }, { "cp", "replicate a file in file system", cmd_cp }, { "load", "load an user application", cmd_load }, { "help", "list available commands", cmd_help }, { "kill", "kill an application (all threads)", cmd_kill }, { "log", "list registered commands", cmd_log }, { "ls", "list directory entries", cmd_ls }, { "mkdir", "create a new directory", cmd_mkdir }, { "mv", "move a file in file system", cmd_mv }, { "pwd", "print current working directory", cmd_pwd }, { "rm", "remove a file from file system", cmd_rm }, { "rmdir", "remove a directory from file system", cmd_rmdir }, { "sched", "display scheduler state", cmd_sched }, { NULL, NULL, NULL } }; //////////////////////////////////////////////////////////////////////////////////// // This function analyses one command (with arguments), execute it, and return. //////////////////////////////////////////////////////////////////////////////////// static void parse(char *buf) { int argc = 0; char *argv[MAX_ARGS]; int i; int len = strlen(buf); // build argc/argv for (i = 0; i < len; i++) { if (buf[i] == ' ') { buf[i] = '\0'; } else if (i == 0 || buf[i - 1] == '\0') { if (argc < MAX_ARGS) { argv[argc] = &buf[i]; argc++; } } } if (argc > 0) { int found = 0; argv[argc] = NULL; // try to match typed command for (i = 0; cmd[i].name; i++) { if (strcmp(argv[0], cmd[i].name) == 0) { cmd[i].fn(argc, argv); found = 1; break; } } if (!found) { printf("\n undefined command %s\n", argv[0]); } } } /////////////////////////////////// int main( int argc , char *argv[] ) { char c; // read character char buf[CMD_MAX_SIZE]; // buffer for one command unsigned int count = 0; // pointer in buf unsigned int i; // index for loops enum fsm_states { NORMAL, ESCAPE, BRAKET, }; // log buffer initialisation memset( &log_entries , 0, sizeof(log_entries)); ptw = 0; ptr = 0; printf( "~~~ shell ~~~\n\n" ); // command buffer initialisation memset( buf, 0x20 , sizeof(buf) ); count = 0; // display first prompt printf("# "); // This lexical analyser writes one command line in the buf buffer. // It is implemented as a 3 states FSM to handle the following sequences: // - ESC [ A : up arrow // - ESC [ B : down arrow // - ESC [ C : right arrow // - ESC [ D : left arrow // The three states have the following semantic: // - NORMAL : no (ESC) character has been found // - ESCAPE : the character (ESC) has been found // - BRAKET : the wo characters (ESC,[) have been found unsigned int state = NORMAL; // @@@ // parse("load /bin/user/sort.elf"); // @@@ while (1) { c = (char)getchar(); if( c == 0 ) continue; switch (state) { case NORMAL: { if ((c == '\b') || (c == 0x7F)) // backspace => remove one character { if (count > 0) { printf("\b \b"); count--; } } else if (c == '\n') // new line => call parser to execute command { if (count > 0) { // complete command buf[count] = '\0'; // register command in log arrays strcpy(log_entries[ptw].buf, buf); log_entries[ptw].count = count; ptw = (ptw + 1) % LOG_DEPTH; ptr = ptw; // execute command printf("\n"); parse((char *)&buf); // reinitialise buffer and display prompt for ( i = 0 ; i < sizeof(buf) ; i++ ) buf[i] = 0x20; count = 0; printf("# "); } } else if (c == '\t') // tabulation => do nothing { } else if (c == 0x1B) // ESC => start an escape sequence { state = ESCAPE; } else if (c == 0x03) // ^C => cancel current command { for (i = 0; i < count; i++) printf("\b \b"); for (i = 0; i < sizeof(buf); i++) buf[i] = 0x20; count = 0; } else // register character in command buffer { if (count < sizeof(buf) - 1) { printf("%c", c); buf[count] = c; count++; } } break; } case ESCAPE: { if (c == '[') // valid sequence => continue { state = BRAKET; } else // invalid sequence => do nothing { state = NORMAL; } break; } case BRAKET: { if (c == 'D') // valid LEFT sequence => move buf pointer left { if (count > 0) { printf("\b"); count--; } // get next user char state = NORMAL; } else if (c == 'C') // valid RIGHT sequence => move buf pointer right { if (count < sizeof(buf) - 1) { printf("%c", buf[count]); count++; } // get next user char state = NORMAL; } else if (c == 'A') // valid UP sequence => move log pointer backward { // cancel current command for (i = 0; i < count; i++) printf("\b \b"); count = 0; // copy log command into buf ptr = (ptr - 1) % LOG_DEPTH; strcpy(buf, log_entries[ptr].buf); count = log_entries[ptr].count; // display log command printf("%s", buf); // get next user char state = NORMAL; } else if (c == 'B') // valid DOWN sequence => move log pointer forward { // cancel current command for (i = 0 ; i < count; i++) printf("\b \b"); count = 0; // copy log command into buf ptr = (ptr + 1) % LOG_DEPTH; strcpy(buf, log_entries[ptr].buf); count = log_entries[ptr].count; // display log command printf("%s", buf); // get next user char state = NORMAL; } else // other character => do nothing { // get next user char state = NORMAL; } break; } } } }