X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/f2ef3bcc89d2ecfdf083257665a7db032a963f91..dff9e15c44ab6340d27215957c56fa72fad246a2:/tools/tesh/tesh.c diff --git a/tools/tesh/tesh.c b/tools/tesh/tesh.c index abbaf22177..7b1a4c0a0d 100644 --- a/tools/tesh/tesh.c +++ b/tools/tesh/tesh.c @@ -13,494 +13,243 @@ #pragma hdrstop #endif -#include "portable.h" -#include "xbt/sysdep.h" -#include "xbt/function_types.h" -#include "xbt/log.h" -#include "xbt/str.h" - -#include -#include - -/** - ** Buffer code - **/ -typedef struct { - char *data; - int used,size; -} buff_t; - -static void buff_empty(buff_t *b) { - b->used=0; - b->data[0]='\n'; - b->data[1]='\0'; -} -static buff_t *buff_new(void) { - buff_t *res=malloc(sizeof(buff_t)); - res->data=malloc(512); - res->size=512; - buff_empty(res); - return res; -} -static void buff_free(buff_t *b) { - if (b) { - if (b->data) - free(b->data); - free(b); - } -} -static void buff_append(buff_t *b, char *toadd) { - int addlen=strlen(toadd); - int needed_space=b->used+addlen+1; +#include "tesh.h" +#include "xbt.h" - if (needed_space > b->size) { - b->data = realloc(b->data, needed_space); - b->size = needed_space; - } - strcpy(b->data+b->used, toadd); - b->used += addlen; -} -static void buff_chomp(buff_t *b) { - while (b->data[b->used] == '\n') { - b->data[b->used] = '\0'; - if (b->used) - b->used--; - } -} +XBT_LOG_NEW_DEFAULT_CATEGORY(tesh, "TEst SHell utility"); -static void buff_trim(buff_t* b) -{ - trim(b->data," "); - b->used = strlen(b->data); -} +/*** Options ***/ +int timeout_value = 5; /* child timeout value */ -typedef struct s_signal_entry +char *testsuite_name; +static void handle_line(const char *filepos, char *line) { - const char* name; - int number; -}s_signal_entry_t,* signal_entry_t; - -static const s_signal_entry_t signals[] = -{ - {"SIGHUP" ,1}, - {"SIGINT" ,2}, - {"SIGQUIT" ,3}, - {"SIGILL" ,4}, - {"SIGTRAP" ,5}, - {"SIGABRT" ,6}, - {"SIGEMT" ,7}, - {"SIGFPE" ,8}, - {"SIGKILL" ,9}, - {"SIGBUS" ,10}, - {"SIGSEGV" ,11}, - {"SIGSYS" ,12}, - {"SIGPIPE" ,13}, - {"SIGALRM" ,14}, - {"SIGTERM" ,15}, - {"SIGURG" ,16}, - {"SIGSTOP" ,17}, - {"SIGTSTP" ,18}, - {"SIGCONT" ,19}, - {"SIGCHLD" ,20}, - {"SIGTTIN" ,21}, - {"SIGTTOU" ,22}, - {"SIGIO" ,23}, - {"SIGXCPU" ,24}, - {"SIGXFSZ" ,25}, - {"SIGVTALRM",26}, - {"SIGPROF" ,27}, - {"SIGWINCH" ,28}, - {"SIGINFO" ,29}, - {"SIGUSR1" ,30}, - {"SIGUSR2" ,31} -}; - -#define SIGMAX 31 -#define SIGUNKNW SIGMAX + 1 - -/* returns the name of the signal from it number */ -const char* -signal_name(unsigned int number); - -/** - ** Options - **/ -int timeout_value = 5; /* child timeout value */ -char* expected_signal=NULL; /* !=NULL if the following command should raise a signal */ -int expected_return=0; /* the exepeted return code of following command */ -int verbose=0; /* wheather we should wine on problems */ - -/** - ** Dealing with timeouts - **/ -int timeouted; -static void timeout_handler(int sig) { - timeouted = 1; -} -/** - ** Dealing with timeouts - **/ -int brokenpipe; -static void pipe_handler(int sig) { - brokenpipe = 1; -} - -/** - ** Launching a child - **/ -buff_t *input; -buff_t *output_wanted; -buff_t *output_got; - -static void check_output() { - if (output_wanted->used==0 - && output_got->used==0) - return; - buff_chomp(output_got); - buff_chomp(output_wanted); - buff_trim(output_got); -buff_trim(output_wanted); - - if ( output_got->used != output_wanted->used - || strcmp(output_got->data, output_wanted->data)) { - fprintf(stderr,"Output don't match expectations\n"); - fprintf(stderr,">>>>> Expected %d chars:\n%s\n<<<<< Expected\n", - output_wanted->used,output_wanted->data); - fprintf(stderr,">>>>> Got %d chars:\n%s\n<<<<< Got\n", - output_got->used,output_got->data); - exit(2); - } - buff_empty(output_wanted); - buff_empty(output_got); - -} - -static void exec_cmd(char *cmd) { - int child_stdin[2]; - int child_stdout[2]; - - if (pipe(child_stdin) || pipe(child_stdout)) { - perror("Cannot open the pipes"); - exit(4); - } - - int pid=fork(); - if (pid<0) { - perror("Cannot fork the command"); - exit(4); - } - - if (pid) { /* father */ - char buffout[4096]; - int posw,posr; - int status; - close(child_stdin[0]); - fcntl(child_stdin[1], F_SETFL, O_NONBLOCK); - close(child_stdout[1]); - fcntl(child_stdout[0], F_SETFL, O_NONBLOCK); - - brokenpipe = 0; - for (posw=0; poswused && !brokenpipe; ) { - int got; - // fprintf(stderr,"Still %d chars to write\n",input->used-posw); - got=write(child_stdin[1],input->data+posw,input->used-posw); - if (got>0) - posw+=got; - if (got<0 && errno!=EINTR && errno!=EAGAIN && errno!=EPIPE) { - perror("Error while writing input to child"); - exit(4); - } - // fprintf(stderr,"written %d chars so far\n",posw); - - posr=read(child_stdout[0],&buffout,4096); - // fprintf(stderr,"got %d chars\n",posr); - if (posr<0 && errno!=EINTR && errno!=EAGAIN) { - perror("Error while reading output of child"); - exit(4); - } - if (posr>0) { - buffout[posr]='\0'; - buff_append(output_got,buffout); - } - - if (got <= 0 && posr <= 0) - usleep(100); - } - input->data[0]='\0'; - input->used=0; - close(child_stdin[1]); - - timeouted = 0; - alarm(timeout_value); - do { - posr=read(child_stdout[0],&buffout,4096); - if (posr<0 && errno!=EINTR && errno!=EAGAIN) { - perror("Error while reading output of child"); - exit(4); - } - if (posr>0) { - buffout[posr]='\0'; - buff_append(output_got,buffout); - } else { - usleep(100); - } - } while (!timeouted && posr!=0); - - /* Check for broken pipe */ - if (brokenpipe && verbose) { - fprintf(stderr,"Warning: Child did not consume all its input (I got broken pipe)\n"); - } - - /* Check for timeouts */ - if (timeouted) { - fprintf(stderr,"Child timeouted (waited %d sec)\n",timeout_value); - exit(3); - } - alarm(0); - - - /* Wait for child, and check why it terminated */ - wait(&status); - - if (WIFSIGNALED(status) && strcmp(signal_name(WTERMSIG(status)),expected_signal)) { - fprintf(stderr,"Child got signal %s instead of signal %s\n",signal_name(WTERMSIG(status)), expected_signal); - exit(WTERMSIG(status)+4); - } - - if (!WIFSIGNALED(status) && expected_signal) { - fprintf(stderr,"Child didn't got expected signal %s\n", - expected_signal); - exit(5); - } - - if (WIFEXITED(status) && WEXITSTATUS(status) != expected_return ) { - if (expected_return) - fprintf(stderr,"Child returned code %d instead of %d\n", - WEXITSTATUS(status), expected_return); - else - fprintf(stderr,"Child returned code %d\n", WEXITSTATUS(status)); - exit(40+WEXITSTATUS(status)); - } - expected_return = 0; - - if(expected_signal){ - free(expected_signal); - expected_signal = NULL; - } - - } else { /* child */ - - close(child_stdin[1]); - close(child_stdout[0]); - dup2(child_stdin[0],0); - close(child_stdin[0]); - dup2(child_stdout[1],1); - dup2(child_stdout[1],2); - close(child_stdout[1]); - - execlp ("/bin/sh", "sh", "-c", cmd, NULL); - } -} + /* Search end */ + xbt_str_rtrim(line + 2, "\n"); + + /* + DEBUG7("rctx={%s,in={%d,>>%10s<<},exp={%d,>>%10s<<},got={%d,>>%10s<<}}", + rctx->cmd, + rctx->input->used, rctx->input->data, + rctx->output_wanted->used,rctx->output_wanted->data, + rctx->output_got->used, rctx->output_got->data); + */ + DEBUG2("[%s] %s", filepos, line); -static void run_cmd(char *cmd) { - if (cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' ') { - int pos = 2; - /* Search end */ - pos = strlen(cmd)-1; - while (cmd[pos] == '\n' || cmd[pos] == ' ' || cmd[pos] == '\t') - cmd[pos--] = '\0'; - /* search begining */ - pos = 2; - while (cmd[pos++] == ' '); - pos--; - // fprintf(stderr,"Saw cd '%s'\n",cmd+pos); - if (chdir(cmd+pos)) { - perror("Chdir failed"); - exit(4); - } - - } else { - exec_cmd(cmd); - } -} - -static void handle_line(int nl, char *line) { - - // printf("%d: %s",nl,line); fflush(stdout); switch (line[0]) { - case '#': break; - case '$': - check_output(); /* Check that last command ran well */ - - printf("[%d] %s",nl,line); - fflush(stdout); - run_cmd(line+2); - break; - - case '<': - buff_append(input,line+2); + case '#': break; + case '$': + /* further trim useless chars which are significant for in/output */ + xbt_str_rtrim(line + 2, " \t"); + + /* Deal with CD commands here, not in rctx */ + if (!strncmp("cd ", line + 2, 3)) { + char *dir = line + 4; + + if (rctx->cmd) + rctx_start(); + + /* search beginning */ + while (*(dir++) == ' '); + dir--; + VERB1("Saw cd '%s'", dir); + if (chdir(dir)) { + ERROR2("Chdir to %s failed: %s", dir, strerror(errno)); + ERROR1("Test suite `%s': NOK (system error)", testsuite_name); + rctx_armageddon(rctx, 4); + } + break; + } /* else, pushline */ + case '&': + case '<': case '>': - buff_append(output_wanted,line+2); - break; - case '!': - if (!strncmp(line+2,"set timeout ",strlen("set timeout "))) { - timeout_value=atoi(line+2+strlen("set timeout")); - printf("[%d] (new timeout value: %d)\n", - nl,timeout_value); - - } else if (!strncmp(line+2,"expect signal ",strlen("expect signal "))) { - expected_signal = strdup(line+2 + strlen("expect signal ")); - trim(expected_signal," \n"); - printf("[%d] (next command must raise signal %s)\n", nl, expected_signal); - - } else if (!strncmp(line+2,"expect return ",strlen("expect return "))) { - expected_return = atoi(line+2+strlen("expect return ")); - printf("[%d] (next command must return code %d)\n", - nl, expected_return); - - } else if (!strncmp(line+2,"verbose on",strlen("verbose on"))) { - verbose = 1; - printf("[%d] (increase verbosity)\n", nl); - - } else if (!strncmp(line+2,"verbose off",strlen("verbose off"))) { - verbose = 1; - printf("[%d] (decrease verbosity)\n", nl); - - } else { - fprintf(stderr,"%d: Malformed metacommand: %s",nl,line); - exit(1); - } + rctx_pushline(filepos, line[0], line + 2 /* pass '$ ' stuff */ ); break; case 'p': - printf("[%d] %s",nl,line+2); + INFO2("[%s] %s", filepos, line + 2); + break; + case 'P': + CRITICAL2("[%s] %s", filepos, line + 2); break; default: - fprintf(stderr,"Syntax error line %d: %s",nl, line); - exit(1); + ERROR2("[%s] Syntax error: %s", filepos, line); + ERROR1("Test suite `%s': NOK (syntax error)", testsuite_name); + rctx_armageddon(rctx, 1); break; } } -static int handle_suite(FILE* IN) { - int len; - char * line = NULL; - int line_num=0; +static void handle_suite(const char *filename, FILE * IN) +{ + size_t len; + char *line = NULL; + int line_num = 0; + char file_pos[256]; + + xbt_strbuff_t buff = xbt_strbuff_new(); + int buffbegin = 0; - buff_t *buff=buff_new(); - int buffbegin = 0; + rctx = rctx_new(); - while (getline(&line,(size_t*) &len, IN) != -1) { + while (getline(&line, &len, IN) != -1) { line_num++; /* Count the line length while checking wheather it's blank */ - int blankline=1; - int linelen = 0; + int blankline = 1; + int linelen = 0; while (line[linelen] != '\0') { - if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n') - blankline = 0; + if (line[linelen] != ' ' && line[linelen] != '\t' + && line[linelen] != '\n') + blankline = 0; linelen++; } - - if (blankline) + + if (blankline) { + if (!rctx->cmd && !rctx->is_empty) { + ERROR1("[%d] Error: no command found in this chunk of lines.", + buffbegin); + ERROR1("Test suite `%s': NOK (syntax error)", testsuite_name); + rctx_armageddon(rctx, 1); + } + if (rctx->cmd) + rctx_start(); + continue; + } /* Deal with \ at the end of the line, and call handle_line on result */ int to_be_continued = 0; - if (linelen>1 && line[linelen-2]=='\\') { - if (linelen>2 && line[linelen-3] == '\\') { - /* Damn. Escaped \ */ - line[linelen-2] = '\n'; - line[linelen-1] = '\0'; + if (linelen > 1 && line[linelen - 2] == '\\') { + if (linelen > 2 && line[linelen - 3] == '\\') { + /* Damn. Escaped \ */ + line[linelen - 2] = '\n'; + line[linelen - 1] = '\0'; } else { - to_be_continued = 1; - line[linelen-2] = '\0'; - linelen -= 2; - if (!buff->used) - buffbegin = line_num; + to_be_continued = 1; + line[linelen - 2] = '\0'; + linelen -= 2; + if (!buff->used) + buffbegin = line_num; } } - if (buff->used || to_be_continued) { - buff_append(buff,line); + if (buff->used || to_be_continued) { + xbt_strbuff_append(buff, line); if (!to_be_continued) { - handle_line(buffbegin, buff->data); - buff_empty(buff); + snprintf(file_pos, 256, "%s:%d", filename, buffbegin); + handle_line(file_pos, buff->data); + xbt_strbuff_empty(buff); } - + } else { - handle_line(line_num,line); + snprintf(file_pos, 256, "%s:%d", filename, line_num); + handle_line(file_pos, line); } } - check_output(); /* Check that last command ran well */ + /* Check that last command of the file ran well */ + if (rctx->cmd) + rctx_start(); + + /* Wait all background commands */ + + rctx_free(rctx); /* Clear buffers */ if (line) free(line); - buff_free(buff); - return 1; -} + xbt_strbuff_free(buff); -int main(int argc,char *argv[]) { +} - /* Setup the signal handlers */ - struct sigaction newact,oldact; - memset(&newact,0,sizeof(newact)); - newact.sa_handler=timeout_handler; - sigaction(SIGALRM,&newact,&oldact); +static void parse_environ() +{ + char *p; + int i; + env = xbt_dict_new(); + for (i = 0; environ[i]; i++) { + p = environ[i]; + char *eq = strchr(p, '='); + char *key = bprintf("%.*s", (int) (eq - p), p); + xbt_dict_set(env, key, xbt_strdup(eq + 1), xbt_free_f); + free(key); + } +} - newact.sa_handler=pipe_handler; - sigaction(SIGPIPE,&newact,&oldact); - - /* Setup the input/output buffers */ - input=buff_new(); - output_wanted=buff_new(); - output_got=buff_new(); +int main(int argc, char *argv[]) +{ - /* Find the description file */ FILE *IN; + int i; + + /* Ignore pipe issues. + They will show up when we try to send data to dead buddies, + but we will stop doing so when we're done with provided input */ + struct sigaction newact; + memset(&newact, 0, sizeof(newact)); + newact.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &newact, NULL); + + xbt_init(&argc, argv); + rctx_init(); + parse_environ(); + + /* Get args */ + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "--cd", strlen("--cd") + 1)) { + if (i == argc - 1) { + ERROR0("--cd argument requires an argument"); + exit(1); + } + if (chdir(argv[i + 1])) { + ERROR2("Cannot change directory to %s: %s", argv[i + 1], + strerror(errno)); + exit(1); + } + INFO1("Change directory to %s", argv[i + 1]); + memmove(argv + i, argv + i + 2, argc - i - 1); + argc -= 2; + } + } + /* Find the description file */ if (argc == 1) { - printf("Test suite from stdin\n");fflush(stdout); - handle_suite(stdin); - fprintf(stderr,"Test suite from stdin OK\n"); - + INFO0("Test suite from stdin"); + testsuite_name = xbt_strdup("(stdin)"); + handle_suite("stdin", stdin); + INFO0("Test suite from stdin OK"); + } else { - int i; - - for (i=1; ileads to segfault on amd64... - fprintf(stderr,"Test suite `%s' OK\n",argv[i]); + for (i = 1; i < argc; i++) { + char *suitename = xbt_strdup(argv[i]); + if (!strcmp("./", suitename)) + memmove(suitename, suitename + 2, strlen(suitename + 2)); + + if (!strcmp(".tesh", suitename + strlen(suitename) - 5)) + suitename[strlen(suitename) - 5] = '\0'; + + INFO1("Test suite `%s'", suitename); + testsuite_name = suitename; + IN = fopen(argv[i], "r"); + if (!IN) { + perror(bprintf("Impossible to open the suite file `%s'", argv[i])); + ERROR1("Test suite `%s': NOK (system error)", testsuite_name); + rctx_armageddon(rctx, 1); + } + handle_suite(suitename, IN); + rctx_wait_bg(); + fclose(IN); + INFO1("Test suite `%s' OK", suitename); + free(suitename); } } - - buff_free(input); - buff_free(output_wanted); - buff_free(output_got); - return 0; -} -const char* -signal_name(unsigned int number) -{ - if(number > SIGMAX) - return "SIGUNKNW"; - - /* special case of SIGSEGV and SIGBUS (be conditional of the implementation)*/ - if((number == SIGBUS) && strcmp("SIGBUS",expected_signal)) - number = SIGSEGV; - - return (signals[number - 1].name); + rctx_exit(); + xbt_exit(); + return 0; }