00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #ifndef _XOPEN_SOURCE
00027 #define _XOPEN_SOURCE 500
00028 #endif
00029 #include <stdlib.h>
00030 #include <stdio.h>
00031 #include <unistd.h>
00032 #include <string.h>
00033 #include <errno.h>
00034 #include <poll.h>
00035 #include <fcntl.h>
00036 #include <sys/time.h>
00037 #include <sys/wait.h>
00038 #include <signal.h>
00039 #include <stdio.h>
00040 #include <sched.h>
00041 #include <libxml/xmlstring.h>
00042
00043 #include "remote_executor.h"
00044 #include "executor.h"
00045 #include "log.h"
00046
00047
00048
00049
00050
00051
00052
00053 extern int bail_out;
00054 extern char *global_failure;
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074 static volatile sig_atomic_t timer_value = 0;
00075 static struct sigaction default_alarm_action = { .sa_handler = NULL };
00076 static testrunner_lite_options *options;
00077 static exec_data *current_data;
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090 #if 0
00091 static void parse_command_args(const char* command, char* argv[], int max_args);
00092 static void free_args(char* argv[]);
00093 #endif
00094 static pid_t fork_process_redirect(int* stdout_fd, int* stderr_fd,
00095 const char *command);
00096 static pid_t fork_process(const char *command);
00097 static void* stream_data_realloc(stream_data* data, int size);
00098 static void stream_data_free(stream_data* data);
00099 static void stream_data_append(stream_data* data, char* src);
00100 static int read_and_append(int fd, stream_data* data);
00101 static void timer_handler(int signum);
00102 static int set_timer(long secs);
00103 static void reset_timer();
00104 static int execution_terminated(exec_data* data);
00105 static void process_output_streams(int stdout_fd, int stderr_fd,
00106 exec_data* data);
00107 static void communicate(int stdout_fd, int stderr_fd, exec_data* data);
00108 static void strip_ctrl_chars (stream_data* data);
00109 static void utf8_check (stream_data* data, const char *id, pid_t pid);
00110
00111
00112
00113
00114
00115
00116
00117 #if 0
00118
00123 static void parse_command_args(const char* command, char* argv[], int max_args)
00124 {
00125 int n = 0;
00126 int size = strlen(command) + 1;
00127 char* buff = NULL;
00128 char* str = NULL;
00129
00130 buff = (char*)malloc(size);
00131
00132 if (buff != NULL) {
00133
00134
00135
00136 strncpy(buff, command, size);
00137 buff[size - 1] = '\0';
00138
00139
00140 str = strtok(buff, " ");
00141
00142 for ( ;str != NULL && n < max_args; n++) {
00143 argv[n] = (char*)malloc(strlen(str) + 1);
00144 if (argv[n]) {
00145 strcpy(argv[n], str);
00146 }
00147 str = strtok(NULL, " ");
00148 }
00149 }
00150 argv[n] = NULL;
00151
00152 free(buff);
00153 }
00154
00158 static void free_args(char* argv[]) {
00159 while(*argv != NULL) {
00160 free(*argv);
00161 argv++;
00162 }
00163 }
00164 #endif
00165
00169 static int exec_wrapper(const char *command)
00170 {
00171 int ret = 0;
00172
00173 if (options->target_address) {
00174 ret = ssh_execute (options->target_address, command);
00175 }
00176 else
00177
00178 ret = execl(SHELLCMD, SHELLCMD, SHELLCMD_ARGS,
00179 command, (char*)NULL);
00180
00181 return ret;
00182 }
00183
00192 static pid_t fork_process_redirect(int* stdout_fd, int* stderr_fd, const char *command) {
00193 int out_pipe[2];
00194 int err_pipe[2];
00195 pid_t pid;
00196
00197 if (pipe(out_pipe) < 0)
00198 goto error_out;
00199
00200 if (pipe(err_pipe) < 0)
00201 goto error_err;
00202
00203 pid = fork();
00204 if (pid > 0) {
00205 LOG_MSG(LOG_DEBUG, "Forked new process %d", pid);
00206
00207 close(out_pipe[1]);
00208 close(err_pipe[1]);
00209 *stdout_fd = out_pipe[0];
00210 *stderr_fd = err_pipe[0];
00211
00212 } else if (pid == 0) {
00213
00214
00215
00216 setsid();
00217
00218 close(out_pipe[0]);
00219 close(err_pipe[0]);
00220
00221
00222 close(1);
00223 if (dup(out_pipe[1]) < 0) {
00224 perror("dup(out_pipe[1]");
00225 }
00226
00227
00228 close(2);
00229 if (dup(err_pipe[1]) < 0) {
00230 perror("dup(err_pipe[1])");
00231 }
00232
00233 exec_wrapper(command);
00234
00235 exit(1);
00236 } else {
00237 LOG_MSG(LOG_ERR, "Fork failed: %s", strerror(errno));
00238 goto error_fork;
00239 }
00240
00241 return pid;
00242
00243 error_fork:
00244 close(err_pipe[0]);
00245 close(err_pipe[1]);
00246 error_err:
00247 close(out_pipe[0]);
00248 close(out_pipe[1]);
00249 error_out:
00250 return -1;
00251 }
00252
00257 static pid_t fork_process(const char *command) {
00258 pid_t pid = fork();
00259
00260 if (pid > 0) {
00261 LOG_MSG(LOG_DEBUG, "Forked new process %d", pid);
00262 } else if (pid == 0) {
00263
00264
00265
00266 setsid();
00267
00268 exec_wrapper(command);
00269
00270 exit(1);
00271 } else {
00272 LOG_MSG(LOG_ERR, "Fork failed: %s", strerror(errno));
00273 }
00274
00275 return pid;
00276 }
00277
00283 static void* stream_data_realloc(stream_data* data, int size) {
00284 unsigned char* newptr = (unsigned char*)malloc(size);
00285 unsigned char* oldptr = data->buffer;
00286 int length = data->size <= size ? data->size : size;
00287
00288 if (newptr) {
00289 if (data->buffer != NULL) {
00290 memcpy(newptr, oldptr, length);
00291 }
00292 data->buffer = newptr;
00293 data->size = size;
00294 free(oldptr);
00295 } else {
00296 LOG_MSG(LOG_ERR, "Stream data memory allocation failed");
00297 }
00298
00299 return (void*)newptr;
00300 }
00301
00305 static void stream_data_free(stream_data* data) {
00306 free(data->buffer);
00307 data->buffer = NULL;
00308 data->size = 0;
00309 data->length = 0;
00310 }
00311
00316 static void stream_data_append(stream_data* data, char* src) {
00317 int length = strlen(src);
00318
00319 if (data->size - data->length >= length + 1 ||
00320 stream_data_realloc(data, data->length + length + 1) != NULL) {
00321 strcpy((char *)&data->buffer[data->length], src);
00322 data->length += length;
00323 }
00324
00325 }
00326
00333 static int read_and_append(int fd, stream_data* data) {
00334 const int read_size = 255;
00335 int ret = 0;
00336
00337 do {
00338
00339
00340
00341
00342 if (data->size - data->length < read_size + 1) {
00343 if (stream_data_realloc(data, data->size + 1024)
00344 == NULL) {
00345
00346 return -3;
00347 }
00348 }
00349
00350 ret = read(fd, &data->buffer[data->length], read_size);
00351
00352 if (ret > 0) {
00353
00354 data->buffer[data->length + ret] = '\0';
00355 if (options->print_step_output)
00356 printf ("%s", &data->buffer[data->length]);
00357
00358 data->length += ret;
00359
00360 }
00361 }
00362 while (ret > 0);
00363
00364 return ret;
00365 }
00366
00370 static void timer_handler(int signum) {
00371 if (signum == SIGALRM) {
00372 timer_value = 1;
00373 }
00374 }
00375
00381 static int set_timer(long secs) {
00382 struct sigaction act;
00383 struct itimerval timer;
00384
00385 act.sa_handler = timer_handler;
00386 sigemptyset(&act.sa_mask);
00387 act.sa_flags = 0;
00388
00389 timer.it_interval.tv_sec = 0;
00390 timer.it_interval.tv_usec = 0;
00391 timer.it_value.tv_sec = secs;
00392 timer.it_value.tv_usec = 0;
00393
00394 timer_value = 0;
00395
00396
00397
00398 if (sigaction(SIGALRM, &act, &default_alarm_action) < 0) {
00399 perror("sigaction");
00400 goto erraction;
00401 }
00402
00403 if (setitimer(ITIMER_REAL, &timer, NULL) < 0) {
00404 perror("setitimer");
00405 goto errtimer;
00406 }
00407
00408 LOG_MSG(LOG_DEBUG, "Set timeout timer to %lu seconds", secs);
00409
00410 return 0;
00411
00412 errtimer:
00413 if (sigaction(SIGALRM, &default_alarm_action, NULL) < 0) {
00414 perror("sigaction");
00415 }
00416 erraction:
00417 return -1;
00418 }
00419
00422 static void reset_timer() {
00423 struct itimerval timer;
00424
00425 timer.it_interval.tv_sec = 0;
00426 timer.it_interval.tv_usec = 0;
00427 timer.it_value.tv_sec = 0;
00428 timer.it_value.tv_usec = 0;
00429
00430 if (setitimer(ITIMER_REAL, &timer, NULL) < 0) {
00431 perror("setitimer");
00432 }
00433
00434
00435 if (default_alarm_action.sa_handler != NULL &&
00436 sigaction(SIGALRM, &default_alarm_action, NULL) < 0) {
00437 perror("sigaction");
00438 }
00439
00440 timer_value = 0;
00441
00442 LOG_MSG(LOG_DEBUG, "Reset timeout timer");
00443 }
00444
00450 static int execution_terminated(exec_data* data) {
00451 pid_t pid = 0;
00452 pid_t pgroup = getpgid(data->pid);
00453 int ret = 0;
00454 int status = 0;
00455 char fail_str [100];
00456
00457 pid = waitpid(-pgroup, &status, WNOHANG);
00458
00459 switch (pid) {
00460 case -1:
00461 if (errno == ECHILD) {
00462
00463 LOG_MSG(LOG_DEBUG, "waitpid reported no more children");
00464 ret = 1;
00465 } else {
00466 LOG_MSG(LOG_ERR, "waitpid: %s", strerror (errno));
00467 }
00468
00469 break;
00470 case 0:
00471
00472 break;
00473 default:
00474
00475
00476 if (pid != data->pid) {
00477 break;
00478 }
00479
00480 if (WIFEXITED(status)) {
00481
00482 data->result = WEXITSTATUS(status);
00483 if (options->target_address) {
00484 if (data->result== 255 ||
00485 (data->result > 64 && data->result < 80) ||
00486 (data->result > 128 && data->result < 144))
00487 {
00488
00489
00490 if (ssh_check_conn (options->
00491 target_address)) {
00492 bail_out = TESTRUNNER_LITE_SSH_FAIL;
00493 global_failure = "connection "
00494 "fail";
00495 LOG_MSG(LOG_ERR,
00496 "ssh connection "
00497 "failure");
00498
00499 }
00500 }
00501 }
00502 LOG_MSG(LOG_DEBUG,
00503 "Process %d exited with status %d",
00504 pid, WEXITSTATUS(status));
00505 } else if (WIFSIGNALED(status)) {
00506
00507 data->result = WTERMSIG(status);
00508 sprintf (fail_str, " terminated by signal %d ",
00509 WTERMSIG(status));
00510 stream_data_append(&data->failure_info,
00511 fail_str);
00512 LOG_MSG(LOG_DEBUG,
00513 "Process %d was terminated by signal %d",
00514 pid, WTERMSIG(status));
00515
00516
00517
00518 if (options->target_address &&
00519 (ret = ssh_check_conn (options->target_address))) {
00520 LOG_MSG(LOG_ERR, "ssh connection failure "
00521 "(%d)", ret);
00522 bail_out = TESTRUNNER_LITE_SSH_FAIL;
00523 global_failure = "connection fail";
00524 }
00525
00526 } else {
00527 data->result = -1;
00528 LOG_MSG(LOG_ERR,
00529 "Unexpected return status %d from process %d",
00530 status, pid);
00531 }
00532 break;
00533 }
00534
00535 return ret;
00536 }
00537
00543 static void process_output_streams(int stdout_fd, int stderr_fd,
00544 exec_data* data)
00545 {
00546 struct pollfd fds[2];
00547 int i = 0;
00548
00549 fds[0].fd = stdout_fd;
00550 fds[0].events = POLLIN;
00551 fds[1].fd = stderr_fd;
00552 fds[1].events = POLLIN;
00553
00554 switch(poll(fds, 2, POLL_TIMEOUT_MS)) {
00555 case 0:
00556
00557 break;
00558 case -1:
00559
00560 break;
00561 default:
00562 for(i = 0; i < 2; i++) {
00563 if (fds[i].revents & POLLIN) {
00564 if (fds[i].fd == stdout_fd) {
00565 LOG_MSG(LOG_DEBUG,
00566 "Reading stdout of process %d",
00567 data->pid);
00568 read_and_append(stdout_fd,
00569 &data->stdout_data);
00570 } else if (fds[i].fd == stderr_fd) {
00571 LOG_MSG(LOG_DEBUG,
00572 "Reading stderr of process %d",
00573 data->pid);
00574 read_and_append(stderr_fd,
00575 &data->stderr_data);
00576 }
00577 }
00578 }
00579 break;
00580 }
00581
00582 }
00583
00589 static void communicate(int stdout_fd, int stderr_fd, exec_data* data) {
00590 pid_t pgroup = 0;
00591 int terminated = 0;
00592 int killed = 0;
00593 int ready = 0;
00594
00595 LOG_MSG(LOG_DEBUG, "Communicating with process %d", data->pid);
00596
00597 if (data->redirect_output == REDIRECT_OUTPUT) {
00598
00599 fcntl(stdout_fd, F_SETFL, O_NONBLOCK);
00600 fcntl(stderr_fd, F_SETFL, O_NONBLOCK);
00601 }
00602
00603 set_timer(data->soft_timeout);
00604
00605 while (!ready) {
00606 ready = execution_terminated(data);
00607
00608 if (data->redirect_output == REDIRECT_OUTPUT) {
00609
00610 process_output_streams(stdout_fd, stderr_fd, data);
00611 } else {
00612 usleep(POLL_TIMEOUT_US);
00613 }
00614
00615 if (timer_value && !terminated) {
00616
00617 LOG_MSG(LOG_DEBUG, "Timeout, terminating process %d",
00618 data->pid);
00619
00620 pgroup = getpgid(data->pid);
00621 kill_pgroup(pgroup, SIGTERM);
00622
00623 if (options->target_address && !bail_out) {
00624 ssh_kill (options->target_address, data->pid);
00625 }
00626 stream_data_append(&data->failure_info,
00627 FAILURE_INFO_TIMEOUT);
00628
00629 terminated = 1;
00630 reset_timer();
00631 set_timer(data->hard_timeout);
00632 } else if (timer_value && !killed) {
00633
00634 LOG_MSG(LOG_DEBUG, "Timeout, killing process %d",
00635 data->pid);
00636
00637 pgroup = getpgid(data->pid);
00638 kill_pgroup(pgroup, SIGKILL);
00639
00640 if (options->target_address && !bail_out) {
00641 ssh_kill (options->target_address, data->pid);
00642 }
00643
00644 killed = 1;
00645 }
00646 }
00647
00648
00649
00650 if (pgroup > 0) {
00651 kill_pgroup(pgroup, SIGKILL);
00652 }
00653
00654 reset_timer();
00655
00656 if (options->target_address && !terminated && !killed && !bail_out) {
00657
00658
00659 ;
00660 }
00661 if (data->redirect_output == REDIRECT_OUTPUT) {
00662 close(stdout_fd);
00663 close(stderr_fd);
00664 }
00665 }
00666
00667
00671 static void strip_ctrl_chars (stream_data* data)
00672 {
00673 size_t clean_len = 0, tmp;
00674 char *p = (char *)data->buffer, *endp;
00675
00676
00677 const char rej[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
00678 0x0B,
00679 0x0C,
00680 0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
00681 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
00682 0x7F, '\0'};
00683
00684
00685 do {
00686 tmp = strcspn(p, rej);
00687 clean_len += tmp;
00688 p += tmp;
00689 if (clean_len < data->length)
00690 *p = ' ';
00691 } while (clean_len < data->length);
00692
00693
00694 endp = (char *)&data->buffer [data->length];
00695 do {
00696 p = rawmemchr(data->buffer, '\0');
00697
00698 if (p && p != endp)
00699 *p = ' ';
00700 } while (p != endp);
00701
00702 }
00703
00704
00710 static void utf8_check (stream_data* data, const char *id, pid_t pid)
00711 {
00712 char *fname = NULL;
00713 FILE *ofile = NULL;
00714 size_t written = 0;
00715
00716 if (xmlCheckUTF8 (data->buffer)) {
00717 return;
00718 }
00719
00720 fname = (char *)malloc (strlen (options->output_folder) +
00721 strlen ("id") + 10 + 1 + 1);
00722 if (!fname) {
00723 LOG_MSG(LOG_ERR, "OOM");
00724 goto error;
00725 }
00726 sprintf (fname, "%s/%s.%d", options->output_folder,
00727 id, pid);
00728
00729 ofile = fopen (fname, "w+");
00730 if (!ofile) {
00731 LOG_MSG (LOG_ERR, "%s:%s:failed to open file %s %s\n",
00732 PROGNAME, __FUNCTION__, fname,
00733 strerror(errno));
00734 goto error;
00735 }
00736 written = fwrite (data->buffer, 1, data->length, ofile);
00737 if (written != data->length) {
00738 LOG_MSG(LOG_WARNING, "failed to write full stdout to"
00739 "%s\n", fname, strerror(errno));
00740 }
00741 data->buffer = (unsigned char *)realloc (data->buffer,
00742 strlen("non utf-8 output "
00743 "detected - see file ")
00744 + strlen (fname) + 1);
00745 if (!data->buffer) {
00746 LOG_MSG(LOG_ERR, "OOM");
00747 goto error;
00748 }
00749
00750 sprintf ((char *)data->buffer,
00751 "non utf-8 output detected - see file %s.%d", id, pid);
00752
00753 LOG_MSG (LOG_DEBUG, "non utf-8 ouput from test step - wrote to "
00754 "%s instead of results xml", fname);
00755
00756 fclose (ofile);
00757 free (fname);
00758 return;
00759 error:
00760 if (ofile) fclose (ofile);
00761 if (fname) free (fname);
00762
00763 memset (data->buffer, 'a', data->length - 1);
00764 return;
00765 }
00766
00767
00768
00769
00770
00776 int execute(const char* command, exec_data* data) {
00777 int stdout_fd = -1;
00778 int stderr_fd = -1;
00779 pid_t ppgid;
00780
00781 data->start_time = time(NULL);
00782
00783 if (command != NULL) {
00784 LOG_MSG(LOG_DEBUG, "Executing command \'%s\'", command);
00785 }
00786
00787 if (data->redirect_output == REDIRECT_OUTPUT) {
00788 data->pid = fork_process_redirect(&stdout_fd,
00789 &stderr_fd,
00790 command);
00791 } else {
00792 data->pid = fork_process(command);
00793 }
00794 current_data = data;
00795
00796 if (data->pid > 0) {
00797
00798 ppgid = getpgid (0);
00799 if (ppgid == -1)
00800 LOG_MSG (LOG_ERR, "getpgid() failed %s",
00801 strerror (errno));
00802 else
00803 while (ppgid == getpgid(data->pid)) sched_yield();
00804 data->pgid = getpgid(data->pid);
00805 LOG_MSG(LOG_DEBUG, "Process %d got process group %d",
00806 data->pid, data->pgid);
00807 communicate(stdout_fd, stderr_fd, data);
00808 }
00809 current_data = NULL;
00810 data->end_time = time(NULL);
00811 if (data->stdout_data.length) strip_ctrl_chars (&data->stdout_data);
00812 if (data->stderr_data.length) strip_ctrl_chars (&data->stderr_data);
00813 if (data->stdout_data.length) utf8_check (&data->stdout_data, "stdout",
00814 data->pid);
00815 if (data->stderr_data.length) utf8_check (&data->stderr_data, "stderr",
00816 data->pid);
00817
00818
00819 return 0;
00820 }
00821
00825 void init_exec_data(exec_data* data) {
00826
00827 data->redirect_output = REDIRECT_OUTPUT;
00828 data->soft_timeout = COMMON_SOFT_TIMEOUT;
00829 data->hard_timeout = COMMON_HARD_TIMEOUT;
00830 data->pid = 0;
00831 data->pgid = 0;
00832 init_stream_data(&data->stdout_data, 1024);
00833 init_stream_data(&data->stderr_data, 1024);
00834 init_stream_data(&data->failure_info, 0);
00835 }
00836
00837 void clean_exec_data(exec_data* data) {
00838 clean_stream_data(&data->stdout_data);
00839 clean_stream_data(&data->stderr_data);
00840 clean_stream_data(&data->failure_info);
00841 }
00842
00847 void init_stream_data(stream_data* data, int allocate) {
00848 data->buffer = NULL;
00849 data->size = 0;
00850 data->length = 0;
00851
00852
00853 if (allocate && stream_data_realloc(data, allocate)) {
00854
00855 data->buffer[0] = '\0';
00856 }
00857 }
00858
00862 void clean_stream_data(stream_data* data) {
00863 stream_data_free(data);
00864 }
00865
00870 void kill_pgroup(int pgroup, int sig) {
00871 if (pgroup <= 1) {
00872 LOG_MSG(LOG_ERR, "Invalid pgid %d", pgroup);
00873 return;
00874 }
00875
00876
00877 if (pgroup == getpgid(0)) {
00878 LOG_MSG(LOG_ERR, "Pgid equals the pgid of testrunner-lite");
00879 return;
00880 }
00881
00882 LOG_MSG(LOG_DEBUG, "Sending signal %d to process group %d",
00883 sig, pgroup);
00884
00885 if (killpg(pgroup, sig) < 0 && errno != ESRCH) {
00886 LOG_MSG(LOG_ERR, "killpg failed: %s", strerror(errno));
00887 }
00888 }
00889
00893 void executor_init(testrunner_lite_options *opts)
00894 {
00895
00896 options = opts;
00897 if (options->target_address)
00898 ssh_executor_init(options->target_address);
00899 }
00900
00903 void executor_close()
00904 {
00905
00906 if (options->target_address && !bail_out)
00907 ssh_executor_close(options->target_address);
00908 }
00909
00910
00911
00912 void handle_sigint (int signum)
00913 {
00914 global_failure = "Interrupted by signal (2)";
00915 bail_out = 255+SIGINT;
00916 if (current_data) {
00917 if (options->target_address)
00918 ssh_kill (options->target_address, current_data->pid);
00919 else
00920 kill_pgroup(current_data->pgid, SIGKILL);
00921 }
00922 }
00923
00924
00925
00926 void handle_sigterm (int signum)
00927 {
00928 global_failure = "Interrupted by signal (15)";
00929 bail_out = 255+SIGTERM;
00930 if (current_data) {
00931 if (options->target_address)
00932 ssh_kill (options->target_address, current_data->pid);
00933 else
00934 kill_pgroup(current_data->pgid, SIGKILL);
00935 }
00936 }
00937
00938
00939
00940
00941
00942
00943
00944