/////////////////////////////////////////////////////////////////////////////////////// // File : shell.c // Date : july 2015 // authors : Clément Guérin and Alain Greiner /////////////////////////////////////////////////////////////////////////////////////// // Simple shell for the GIET_VM. /////////////////////////////////////////////////////////////////////////////////////// #include "stdio.h" #include "stdlib.h" #include "malloc.h" #include "string.h" #define MAX_SIZE (128) // max number of characters in one command #define LOG_DEPTH (128) // max number of commands in log #define MAX_ARGS (32) // max number of arguments in a command #define FIFO_SIZE (1024) // FIFO depth for recursive ls //////////////////////////////////////////////////////////////////////////////// // Global Variables //////////////////////////////////////////////////////////////////////////////// char log_buf[LOG_DEPTH][MAX_SIZE]; // registered command strings unsigned int log_count[LOG_DEPTH]; // registered command lengths unsigned int ptw; // write pointer in log unsigned int ptr; // read pointer in log struct command_t { char *name; char *desc; void (*fn)(int, char**); }; // this array initialised afer commands definition struct command_t cmd[]; //////////////////////////////////////////////////////////////////////////////// // Shell Commands //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////// static void cmd_cat(int argc, char** argv) { if (argc != 2) { giet_tty_printf(" usage : cat pathname \n"); return; } unsigned int x,y,p; // processor coordinates unsigned int fd; // file descriptor fat_file_info_t info; // file info unsigned int size; // buffer size (file_size + 1) unsigned int bytes; // number of bytes to be mapped char* buf = NULL; // temporary buffer // get processor coordinates giet_proc_xyp( &x , &y , &p ); // open the file to display fd = giet_fat_open( argv[1] , 0 ); if (fd < 0) { giet_tty_printf(" error : cannot open %s\n", argv[1]); goto exit; } // get file size giet_fat_file_info( fd, &info ); if ( info.is_dir ) { giet_tty_printf(" error : %s is a directory\n", argv[1] ); goto exit; } size = info.size; // extend size to 4 Kbytes boundary if required if ( (size+1) & 0xFFF) bytes = (size & 0xFFFFF000) + 0x1000; else bytes = size + 1; // map local buffer to Cache_file buf = giet_fat_mmap( NULL, bytes, MAP_PROT_READ | MAP_PROT_WRITE, MAP_SHARED, fd, 0 ); if ( buf == NULL ) { giet_tty_printf(" error : cannot map %s\n", argv[1] ); goto exit; } // set terminating '0' buf[size] = 0; // display the file content giet_tty_printf("%s", buf ); exit: if ( fd >= 0 ) giet_fat_close( fd ); if ( buf != NULL ) giet_fat_munmap( buf , bytes ); } // end cmd_cat() ///////////////////////////////////////////// static void cmd_context(int argc, char** argv) { if (argc < 3) { giet_tty_printf(" usage : %s vspace_name thread_name\n", argv[0] ); return; } giet_pthread_control( THREAD_CMD_CONTEXT , argv[1] , argv[2] ); } // end cmd_context() ///////////////////////////////////////// static void cmd_cp(int argc, char** argv) { if (argc < 3) { giet_tty_printf(" usage : cp src_pathname dst_pathname>\n"); return; } char buf[1024]; int src_fd = -1; int dst_fd = -1; fat_file_info_t info; int size; int i; src_fd = giet_fat_open( argv[1] , O_RDONLY ); if (src_fd < 0) { giet_tty_printf(" error : cannot open %s / err = %d\n", argv[1], src_fd); goto exit; } giet_fat_file_info(src_fd, &info); if (info.is_dir) { giet_tty_printf(" error : %s is a directory\n", argv[1] ); goto exit; } size = info.size; dst_fd = giet_fat_open( argv[2] , O_CREAT | O_TRUNC); if (dst_fd < 0) { giet_tty_printf(" error : cannot open %s / err = %d\n", argv[2], dst_fd); goto exit; } giet_fat_file_info(dst_fd, &info); if (info.is_dir) { giet_tty_printf(" error : %s is a directory\n", argv[2] ); // TODO goto exit; } i = 0; while (i < size) { int len = (size - i < 1024 ? size - i : 1024); int wlen; len = giet_fat_read(src_fd, &buf, len); wlen = giet_fat_write(dst_fd, &buf, len); if (wlen != len) { giet_tty_printf(" error : cannot write on device\n"); goto exit; } i += len; } exit: if (src_fd >= 0) giet_fat_close(src_fd); if (dst_fd >= 0) giet_fat_close(dst_fd); } // end cmd_cp() /////////////////////////////////////////// static void cmd_dump(int argc, char** argv) { if ((argc == 2) && (strcmp( argv[1] , "-bs" ) == 0)) { giet_fat_dump( DUMP_BS , NULL , 0 ); } else if ((argc == 2) && (strcmp( argv[1] , "-fs" ) == 0)) { giet_fat_dump( DUMP_FS , NULL , 0 ); } else if ((argc == 3) && (strcmp( argv[1] , "-fat" ) == 0)) { giet_fat_dump( DUMP_FAT , NULL , atoi( argv[2] ) ); } else if ((argc == 4) && (strcmp( argv[1] , "-file" ) == 0)) { giet_fat_dump( DUMP_FILE , argv[2] , atoi( argv[3] ) ); } else if ((argc == 4) && (strcmp( argv[1] , "-dir" ) == 0)) { giet_fat_dump( DUMP_DIR , argv[2] , atoi( argv[3] ) ); } else { giet_tty_printf(" usage : dump [-bs] [-fs] [-fat block] " "[-file pathname block] [-dir pathname block]\n"); return; } } // end cmd_dump() /////////////////////////////////////////// static void cmd_exec(int argc, char **argv) { if (argc < 2) { giet_tty_printf(" usage : %s vspace_name\n", argv[0]); return; } int ret = giet_exec_application(argv[1]); if ( ret == -1 ) { giet_tty_printf(" error : %s not found\n", argv[1] ); } } // end cmd_exec() /////////////////////////////////////////// static void cmd_help(int argc, char** argv) { int i; giet_tty_printf("available commands:\n"); for (i = 0; cmd[i].name; i++) { giet_tty_printf("\t%s\t : %s\n", cmd[i].name , cmd[i].desc ); } } // end cmd_help() /////////////////////////////////////////// static void cmd_kill(int argc, char **argv) { if (argc < 2) { giet_tty_printf(" usage : %s vspace_name\n", argv[0]); return; } int ret = giet_kill_application(argv[1]); if ( ret == -1 ) { giet_tty_printf(" error : %s not found\n", argv[1] ); } if ( ret == -2 ) { giet_tty_printf(" error : %s cannot be killed\n", argv[1] ); } } // end cmd_kill() /////////////////////////////////////////// static void cmd_log( int argc, char** argv) { giet_tty_printf("--- registered commands ---\n"); unsigned int i; for ( i = 0 ; i < LOG_DEPTH ; i++ ) { giet_tty_printf(" - %d\t: %s\n", i , &log_buf[i][0] ); } } // end cmd_log() ///////////////////////////////////////// static void cmd_ls(int argc, char** argv) { fat_dirent_t entry; unsigned int recursive; char* paths[FIFO_SIZE]; unsigned int ptr = 0; unsigned int ptw = 0; // analyse arguments if (argc == 2) { // allocate a buffer for root directory // pathname, and push it in FIFO paths[ptw] = malloc( strlen(argv[1]) ); strcpy( paths[ptw] , argv[1] ); ptw = (ptw + 1) % FIFO_SIZE; // not recursive recursive = 0; } else if ( (argc == 3) && (strcmp( argv[1] , "-r" ) == 0) ) { // allocate a buffer for root directory // pathname, and push it in FIFO paths[ptw] = malloc( strlen(argv[2]) ); strcpy( paths[ptw] , argv[2] ); ptw = (ptw + 1) % FIFO_SIZE; // recursive recursive = 1; } else { giet_tty_printf(" usage : ls [-r] pathname\n"); return; } // loop on registered directories do { // open directory int fd = giet_fat_opendir( paths[ptr] ); if (fd < 0) { giet_tty_printf(" error : cannot open %s\n", paths[ptr] ); return; } // display directory pathname giet_tty_printf("--- %s ---\n", paths[ptr] ); // loop on directory entries while (giet_fat_readdir(fd, &entry) == 0) { // display entry if ( entry.is_dir ) giet_tty_printf("dir "); else giet_tty_printf("file"); giet_tty_printf(" | size = %d \t| cluster = %x \t| %s\n", entry.size, entry.cluster, entry.name ); // allocate a buffer for subdirectory pathname // and push it in FIFO if required if ( entry.is_dir && recursive && ( strcmp( entry.name , "." ) != 0 ) && ( strcmp( entry.name , ".." ) != 0 ) ) { // check FIFO full if ( ((ptr - ptw) % FIFO_SIZE) == 1 ) { giet_tty_printf(" sorry, not enough memory for recursive ls\n"); return; } unsigned int length = strlen(paths[ptr]) + strlen(entry.name) + 2; paths[ptw] = malloc( length ); if ( strcmp( paths[ptr] , "/" ) == 0 ) { snprintf( paths[ptw] , length , "/%s" , entry.name ); } else { snprintf( paths[ptw] , length , "%s/%s" , paths[ptr] , entry.name ); } ptw = (ptw + 1) % FIFO_SIZE; } } // end loop on entries // close directory giet_fat_closedir(fd); // release the directory pathname buffer // and pop it from FIFO free( paths[ptr] ); ptr = (ptr + 1) % FIFO_SIZE; } while ( ptr != ptw ); } // end cmd_ls() //////////////////////////////////////////// static void cmd_mkdir(int argc, char** argv) { if (argc < 2) { giet_tty_printf(" usage : mkdir pathname\n"); return; } int ret = giet_fat_mkdir(argv[1]); if (ret < 0) { giet_tty_printf(" error : cannot create directory %s / err = %d\n", argv[1], ret); } } // end cmd_mkdir() ///////////////////////////////////////// static void cmd_mv(int argc, char **argv) { if (argc < 3) { giet_tty_printf(" usage : %s src_pathname dst_pathname\n", argv[0]); return; } int ret = giet_fat_rename(argv[1], argv[2]); if (ret < 0) { giet_tty_printf("error : cannot move %s to %s / err = %d\n", argv[1], argv[2], ret ); } } // end cmd_mv() //////////////////////////////////////////// static void cmd_pause(int argc, char** argv) { if (argc < 3) { giet_tty_printf(" usage : %s vspace_name thread_name\n", argv[0] ); return; } giet_pthread_control( THREAD_CMD_PAUSE , argv[1] , argv[2] ); } // end cmd_pause() ///////////////////////////////////////// static void cmd_ps(int argc, char** argv) { if (argc == 1) { giet_applications_status( NULL ); } else { giet_applications_status( argv[1] ); } } // end cmd_ps() ///////////////////////////////////////////// static void cmd_resume(int argc, char** argv) { if (argc < 3) { giet_tty_printf(" usage : %s vspace_name thread_name\n", argv[0] ); return; } giet_pthread_control( THREAD_CMD_RESUME , argv[1] , argv[2] ); } // end cmd_resume() ///////////////////////////////////////// static void cmd_rm(int argc, char **argv) { if (argc < 2) { giet_tty_printf(" usage : rm pathname\n"); return; } int ret = giet_fat_remove(argv[1], 0); if (ret < 0) { giet_tty_printf(" error : cannot remove %s / err = %d\n", argv[1], ret ); } } // end cmd_rm() //////////////////////////////////////////// static void cmd_rmdir(int argc, char **argv) { if (argc < 2) { giet_tty_printf(" usage : rmdir pathname\n"); return; } int ret = giet_fat_remove(argv[1], 1); if (ret < 0) { giet_tty_printf(" error : cannot remove %s / err = %d\n", argv[1], ret ); } } // end cmd_rmdir() /////////////////////////////////////////// static void cmd_time(int argc, char** argv) { giet_tty_printf(" cycle = %d\n", giet_proctime()); } ///////////////////////////////////////////////////////////////////////////////////// struct command_t cmd[] = { { "cat", "display file content", cmd_cat }, { "context", "display a thread context", cmd_context }, { "cp", "replicate a file in file system", cmd_cp }, { "dump", "display content of disk sector", cmd_dump }, { "exec", "start an application", cmd_exec }, { "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 }, { "pause", "pause a thread", cmd_pause }, { "ps", "list all mapped applications status", cmd_ps }, { "resume", "resume a thread", cmd_resume }, { "rm", "remove a file from file system", cmd_rm }, { "rmdir", "remove a directory from file system", cmd_rmdir }, { "time", "return current date", cmd_time }, { NULL, NULL, NULL } }; ///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // This function analyses one command (with arguments) /////////////////////////////////////////////////////////////////// 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; // try to match typed command with built-ins for (i = 0; cmd[i].name; i++) { if (strcmp(argv[0], cmd[i].name) == 0) { // invoke cmd[i].fn(argc, argv); found = 1; break; } } if (!found) { giet_tty_printf("\n undefined command %s\n", argv[0]); } } } // end parse() ////////////////////////////////////////// __attribute__ ((constructor)) void main() ////////////////////////////////////////// { char c; // read character char buf[MAX_SIZE]; // buffer for one command unsigned int count = 0; // pointer in buf unsigned int i , j; // indexes for loops enum fsm_states { NORMAL, ESCAPE, BRAKET, }; // get a private TTY giet_tty_alloc( 0 ); giet_tty_printf( "~~~ shell ~~~\n\n" ); // log_buf initialisation ptw = 0; ptr = 0; for ( i = 0 ; i < LOG_DEPTH ; i++ ) { for ( j = 0 ; j < MAX_SIZE ; j++ ) { log_buf[i][j] = 0; } } // heap initialisation unsigned int x_id; // x cluster coordinate unsigned int y_id; // y cluster coordinate unsigned int p_id; // local processor index giet_proc_xyp( &x_id , &y_id , &p_id ); heap_init( x_id , y_id ); // command buffer initialisation for ( i = 0 ; i < sizeof(buf) ; i++ ) buf[i] = 0x20; count = 0; // display first prompt giet_tty_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 thee 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; while (1) { giet_tty_getc(&c); switch ( state ) { case NORMAL: { if ( (c == '\b') || (c == 0x7F) ) // backspace => remove one character { if (count > 0) { giet_tty_printf("\b \b"); count--; } } else if ( c == '\n' ) // new line => call parser to execute command { if (count > 0) { // complete commande buf[count] = '\0'; // register command in log arrays strcpy( &log_buf[ptw][0] , buf ); log_count[ptw] = count; ptw = (ptw + 1) % LOG_DEPTH; ptr = ptw; // execute command giet_tty_printf("\n"); parse((char*)&buf); // reinitialise buffer and display prompt for ( i = 0 ; i < sizeof(buf) ; i++ ) buf[i] = 0x20; count = 0; giet_tty_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++ ) giet_tty_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) { giet_tty_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 ) { giet_tty_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) { giet_tty_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++ ) giet_tty_printf("\b \b"); count = 0; // copy log command into buf ptr = (ptr - 1) % LOG_DEPTH; strcpy( buf , &log_buf[ptr][0] ); count = log_count[ptr]; // display log command giet_tty_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++ ) giet_tty_printf("\b \b"); count = 0; // copy log command into buf ptr = (ptr + 1) % LOG_DEPTH; strcpy( buf , &log_buf[ptr][0] ); count = log_count[ptr]; // display log command giet_tty_printf( "%s" , buf ); // get next user char state = NORMAL; } else // other character => do nothing { // get next user char state = NORMAL; } break; } } // end switch on state } // end while } // end main() // Local Variables: // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4