10e552da7Schristos /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
20e552da7Schristos *
30e552da7Schristos * Permission is hereby granted, free of charge, to any person obtaining a copy
40e552da7Schristos * of this software and associated documentation files (the "Software"), to
50e552da7Schristos * deal in the Software without restriction, including without limitation the
60e552da7Schristos * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
70e552da7Schristos * sell copies of the Software, and to permit persons to whom the Software is
80e552da7Schristos * furnished to do so, subject to the following conditions:
90e552da7Schristos *
100e552da7Schristos * The above copyright notice and this permission notice shall be included in
110e552da7Schristos * all copies or substantial portions of the Software.
120e552da7Schristos *
130e552da7Schristos * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
140e552da7Schristos * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
150e552da7Schristos * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
160e552da7Schristos * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
170e552da7Schristos * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
180e552da7Schristos * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
190e552da7Schristos * IN THE SOFTWARE.
200e552da7Schristos */
210e552da7Schristos
220e552da7Schristos #include "runner-unix.h"
230e552da7Schristos #include "runner.h"
240e552da7Schristos
250e552da7Schristos #include <limits.h>
260e552da7Schristos #include <stdint.h> /* uintptr_t */
270e552da7Schristos
280e552da7Schristos #include <errno.h>
290e552da7Schristos #include <unistd.h> /* usleep */
300e552da7Schristos #include <string.h> /* strdup */
310e552da7Schristos #include <stdio.h>
320e552da7Schristos #include <stdlib.h>
330e552da7Schristos #include <sys/types.h>
340e552da7Schristos #include <signal.h>
350e552da7Schristos #include <sys/wait.h>
360e552da7Schristos #include <sys/stat.h>
370e552da7Schristos #include <assert.h>
380e552da7Schristos
390e552da7Schristos #include <sys/select.h>
400e552da7Schristos #include <sys/time.h>
410e552da7Schristos #include <pthread.h>
420e552da7Schristos
430e552da7Schristos extern char** environ;
440e552da7Schristos
closefd(int fd)450e552da7Schristos static void closefd(int fd) {
460e552da7Schristos if (close(fd) == 0 || errno == EINTR || errno == EINPROGRESS)
470e552da7Schristos return;
480e552da7Schristos
490e552da7Schristos perror("close");
500e552da7Schristos abort();
510e552da7Schristos }
520e552da7Schristos
530e552da7Schristos
notify_parent_process(void)540e552da7Schristos void notify_parent_process(void) {
550e552da7Schristos char* arg;
560e552da7Schristos int fd;
570e552da7Schristos
580e552da7Schristos arg = getenv("UV_TEST_RUNNER_FD");
590e552da7Schristos if (arg == NULL)
600e552da7Schristos return;
610e552da7Schristos
620e552da7Schristos fd = atoi(arg);
630e552da7Schristos assert(fd > STDERR_FILENO);
640e552da7Schristos unsetenv("UV_TEST_RUNNER_FD");
650e552da7Schristos closefd(fd);
660e552da7Schristos }
670e552da7Schristos
680e552da7Schristos
690e552da7Schristos /* Do platform-specific initialization. */
platform_init(int argc,char ** argv)700e552da7Schristos void platform_init(int argc, char **argv) {
710e552da7Schristos /* Disable stdio output buffering. */
720e552da7Schristos setvbuf(stdout, NULL, _IONBF, 0);
730e552da7Schristos setvbuf(stderr, NULL, _IONBF, 0);
740e552da7Schristos signal(SIGPIPE, SIG_IGN);
750e552da7Schristos snprintf(executable_path, sizeof(executable_path), "%s", argv[0]);
760e552da7Schristos }
770e552da7Schristos
780e552da7Schristos
790e552da7Schristos /* Invoke "argv[0] test-name [test-part]". Store process info in *p. Make sure
800e552da7Schristos * that all stdio output of the processes is buffered up. */
process_start(char * name,char * part,process_info_t * p,int is_helper)810e552da7Schristos int process_start(char* name, char* part, process_info_t* p, int is_helper) {
820e552da7Schristos FILE* stdout_file;
830e552da7Schristos int stdout_fd;
840e552da7Schristos const char* arg;
850e552da7Schristos char* args[16];
860e552da7Schristos int pipefd[2];
870e552da7Schristos char fdstr[8];
880e552da7Schristos ssize_t rc;
890e552da7Schristos int n;
900e552da7Schristos pid_t pid;
910e552da7Schristos
920e552da7Schristos arg = getenv("UV_USE_VALGRIND");
930e552da7Schristos n = 0;
940e552da7Schristos
950e552da7Schristos /* Disable valgrind for helpers, it complains about helpers leaking memory.
960e552da7Schristos * They're killed after the test and as such never get a chance to clean up.
970e552da7Schristos */
980e552da7Schristos if (is_helper == 0 && arg != NULL && atoi(arg) != 0) {
990e552da7Schristos args[n++] = "valgrind";
1000e552da7Schristos args[n++] = "--quiet";
1010e552da7Schristos args[n++] = "--leak-check=full";
1020e552da7Schristos args[n++] = "--show-reachable=yes";
1030e552da7Schristos args[n++] = "--error-exitcode=125";
1040e552da7Schristos }
1050e552da7Schristos
1060e552da7Schristos args[n++] = executable_path;
1070e552da7Schristos args[n++] = name;
1080e552da7Schristos args[n++] = part;
1090e552da7Schristos args[n++] = NULL;
1100e552da7Schristos
1110e552da7Schristos stdout_file = tmpfile();
1120e552da7Schristos stdout_fd = fileno(stdout_file);
1130e552da7Schristos if (!stdout_file) {
1140e552da7Schristos perror("tmpfile");
1150e552da7Schristos return -1;
1160e552da7Schristos }
1170e552da7Schristos
1180e552da7Schristos if (is_helper) {
1190e552da7Schristos if (pipe(pipefd)) {
1200e552da7Schristos perror("pipe");
1210e552da7Schristos return -1;
1220e552da7Schristos }
1230e552da7Schristos
1240e552da7Schristos snprintf(fdstr, sizeof(fdstr), "%d", pipefd[1]);
1250e552da7Schristos if (setenv("UV_TEST_RUNNER_FD", fdstr, /* overwrite */ 1)) {
1260e552da7Schristos perror("setenv");
1270e552da7Schristos return -1;
1280e552da7Schristos }
1290e552da7Schristos }
1300e552da7Schristos
1310e552da7Schristos p->terminated = 0;
1320e552da7Schristos p->status = 0;
1330e552da7Schristos
1340e552da7Schristos pid = fork();
1350e552da7Schristos
1360e552da7Schristos if (pid < 0) {
1370e552da7Schristos perror("fork");
1380e552da7Schristos return -1;
1390e552da7Schristos }
1400e552da7Schristos
1410e552da7Schristos if (pid == 0) {
1420e552da7Schristos /* child */
1430e552da7Schristos if (is_helper)
1440e552da7Schristos closefd(pipefd[0]);
1450e552da7Schristos dup2(stdout_fd, STDOUT_FILENO);
1460e552da7Schristos dup2(stdout_fd, STDERR_FILENO);
1470e552da7Schristos execve(args[0], args, environ);
1480e552da7Schristos perror("execve()");
1490e552da7Schristos _exit(127);
1500e552da7Schristos }
1510e552da7Schristos
1520e552da7Schristos /* parent */
1530e552da7Schristos p->pid = pid;
1540e552da7Schristos p->name = strdup(name);
1550e552da7Schristos p->stdout_file = stdout_file;
1560e552da7Schristos
1570e552da7Schristos if (!is_helper)
1580e552da7Schristos return 0;
1590e552da7Schristos
1600e552da7Schristos closefd(pipefd[1]);
1610e552da7Schristos unsetenv("UV_TEST_RUNNER_FD");
1620e552da7Schristos
1630e552da7Schristos do
1640e552da7Schristos rc = read(pipefd[0], &n, 1);
1650e552da7Schristos while (rc == -1 && errno == EINTR);
1660e552da7Schristos
1670e552da7Schristos closefd(pipefd[0]);
1680e552da7Schristos
1690e552da7Schristos if (rc == -1) {
1700e552da7Schristos perror("read");
1710e552da7Schristos return -1;
1720e552da7Schristos }
1730e552da7Schristos
1740e552da7Schristos if (rc > 0) {
1750e552da7Schristos fprintf(stderr, "EOF expected but got data.\n");
1760e552da7Schristos return -1;
1770e552da7Schristos }
1780e552da7Schristos
1790e552da7Schristos return 0;
1800e552da7Schristos }
1810e552da7Schristos
1820e552da7Schristos
1830e552da7Schristos typedef struct {
1840e552da7Schristos int pipe[2];
1850e552da7Schristos process_info_t* vec;
1860e552da7Schristos int n;
1870e552da7Schristos } dowait_args;
1880e552da7Schristos
1890e552da7Schristos
1900e552da7Schristos /* This function is run inside a pthread. We do this so that we can possibly
1910e552da7Schristos * timeout.
1920e552da7Schristos */
dowait(void * data)1930e552da7Schristos static void* dowait(void* data) {
1940e552da7Schristos dowait_args* args = data;
1950e552da7Schristos
1960e552da7Schristos int i, r;
1970e552da7Schristos process_info_t* p;
1980e552da7Schristos
1990e552da7Schristos for (i = 0; i < args->n; i++) {
200*5f2f4271Schristos p = &args->vec[i];
2010e552da7Schristos if (p->terminated) continue;
2020e552da7Schristos r = waitpid(p->pid, &p->status, 0);
2030e552da7Schristos if (r < 0) {
2040e552da7Schristos perror("waitpid");
2050e552da7Schristos return NULL;
2060e552da7Schristos }
2070e552da7Schristos p->terminated = 1;
2080e552da7Schristos }
2090e552da7Schristos
2100e552da7Schristos if (args->pipe[1] >= 0) {
2110e552da7Schristos /* Write a character to the main thread to notify it about this. */
2120e552da7Schristos ssize_t r;
2130e552da7Schristos
2140e552da7Schristos do
2150e552da7Schristos r = write(args->pipe[1], "", 1);
2160e552da7Schristos while (r == -1 && errno == EINTR);
2170e552da7Schristos }
2180e552da7Schristos
2190e552da7Schristos return NULL;
2200e552da7Schristos }
2210e552da7Schristos
2220e552da7Schristos
2230e552da7Schristos /* Wait for all `n` processes in `vec` to terminate. Time out after `timeout`
2240e552da7Schristos * msec, or never if timeout == -1. Return 0 if all processes are terminated,
2250e552da7Schristos * -1 on error, -2 on timeout. */
process_wait(process_info_t * vec,int n,int timeout)2260e552da7Schristos int process_wait(process_info_t* vec, int n, int timeout) {
2270e552da7Schristos int i;
2280e552da7Schristos int r;
2290e552da7Schristos int retval;
2300e552da7Schristos process_info_t* p;
2310e552da7Schristos dowait_args args;
2320e552da7Schristos pthread_t tid;
2330e552da7Schristos pthread_attr_t attr;
2340e552da7Schristos unsigned int elapsed_ms;
2350e552da7Schristos struct timeval timebase;
2360e552da7Schristos struct timeval tv;
2370e552da7Schristos fd_set fds;
2380e552da7Schristos
2390e552da7Schristos args.vec = vec;
2400e552da7Schristos args.n = n;
2410e552da7Schristos args.pipe[0] = -1;
2420e552da7Schristos args.pipe[1] = -1;
2430e552da7Schristos
2440e552da7Schristos /* The simple case is where there is no timeout */
2450e552da7Schristos if (timeout == -1) {
2460e552da7Schristos dowait(&args);
2470e552da7Schristos return 0;
2480e552da7Schristos }
2490e552da7Schristos
2500e552da7Schristos /* Hard case. Do the wait with a timeout.
2510e552da7Schristos *
2520e552da7Schristos * Assumption: we are the only ones making this call right now. Otherwise
2530e552da7Schristos * we'd need to lock vec.
2540e552da7Schristos */
2550e552da7Schristos
2560e552da7Schristos r = pipe((int*)&(args.pipe));
2570e552da7Schristos if (r) {
2580e552da7Schristos perror("pipe()");
2590e552da7Schristos return -1;
2600e552da7Schristos }
2610e552da7Schristos
2620e552da7Schristos if (pthread_attr_init(&attr))
2630e552da7Schristos abort();
2640e552da7Schristos
2650e552da7Schristos #if defined(__MVS__)
2660e552da7Schristos if (pthread_attr_setstacksize(&attr, 1024 * 1024))
2670e552da7Schristos #else
2680e552da7Schristos if (pthread_attr_setstacksize(&attr, 256 * 1024))
2690e552da7Schristos #endif
2700e552da7Schristos abort();
2710e552da7Schristos
2720e552da7Schristos r = pthread_create(&tid, &attr, dowait, &args);
2730e552da7Schristos
2740e552da7Schristos if (pthread_attr_destroy(&attr))
2750e552da7Schristos abort();
2760e552da7Schristos
2770e552da7Schristos if (r) {
2780e552da7Schristos perror("pthread_create()");
2790e552da7Schristos retval = -1;
2800e552da7Schristos goto terminate;
2810e552da7Schristos }
2820e552da7Schristos
2830e552da7Schristos if (gettimeofday(&timebase, NULL))
2840e552da7Schristos abort();
2850e552da7Schristos
2860e552da7Schristos tv = timebase;
2870e552da7Schristos for (;;) {
2880e552da7Schristos /* Check that gettimeofday() doesn't jump back in time. */
2890e552da7Schristos assert(tv.tv_sec > timebase.tv_sec ||
2900e552da7Schristos (tv.tv_sec == timebase.tv_sec && tv.tv_usec >= timebase.tv_usec));
2910e552da7Schristos
2920e552da7Schristos elapsed_ms =
2930e552da7Schristos (tv.tv_sec - timebase.tv_sec) * 1000 +
2940e552da7Schristos (tv.tv_usec / 1000) -
2950e552da7Schristos (timebase.tv_usec / 1000);
2960e552da7Schristos
2970e552da7Schristos r = 0; /* Timeout. */
2980e552da7Schristos if (elapsed_ms >= (unsigned) timeout)
2990e552da7Schristos break;
3000e552da7Schristos
3010e552da7Schristos tv.tv_sec = (timeout - elapsed_ms) / 1000;
3020e552da7Schristos tv.tv_usec = (timeout - elapsed_ms) % 1000 * 1000;
3030e552da7Schristos
3040e552da7Schristos FD_ZERO(&fds);
3050e552da7Schristos FD_SET(args.pipe[0], &fds);
3060e552da7Schristos
3070e552da7Schristos r = select(args.pipe[0] + 1, &fds, NULL, NULL, &tv);
3080e552da7Schristos if (!(r == -1 && errno == EINTR))
3090e552da7Schristos break;
3100e552da7Schristos
3110e552da7Schristos if (gettimeofday(&tv, NULL))
3120e552da7Schristos abort();
3130e552da7Schristos }
3140e552da7Schristos
3150e552da7Schristos if (r == -1) {
3160e552da7Schristos perror("select()");
3170e552da7Schristos retval = -1;
3180e552da7Schristos
3190e552da7Schristos } else if (r) {
3200e552da7Schristos /* The thread completed successfully. */
3210e552da7Schristos retval = 0;
3220e552da7Schristos
3230e552da7Schristos } else {
3240e552da7Schristos /* Timeout. Kill all the children. */
3250e552da7Schristos for (i = 0; i < n; i++) {
326*5f2f4271Schristos p = &vec[i];
3270e552da7Schristos kill(p->pid, SIGTERM);
3280e552da7Schristos }
3290e552da7Schristos retval = -2;
3300e552da7Schristos }
3310e552da7Schristos
3320e552da7Schristos if (pthread_join(tid, NULL))
3330e552da7Schristos abort();
3340e552da7Schristos
3350e552da7Schristos terminate:
336*5f2f4271Schristos closefd(args.pipe[0]);
337*5f2f4271Schristos closefd(args.pipe[1]);
3380e552da7Schristos return retval;
3390e552da7Schristos }
3400e552da7Schristos
3410e552da7Schristos
3420e552da7Schristos /* Returns the number of bytes in the stdio output buffer for process `p`. */
process_output_size(process_info_t * p)3430e552da7Schristos long int process_output_size(process_info_t *p) {
3440e552da7Schristos /* Size of the p->stdout_file */
3450e552da7Schristos struct stat buf;
3460e552da7Schristos
3470e552da7Schristos int r = fstat(fileno(p->stdout_file), &buf);
3480e552da7Schristos if (r < 0) {
3490e552da7Schristos return -1;
3500e552da7Schristos }
3510e552da7Schristos
3520e552da7Schristos return (long)buf.st_size;
3530e552da7Schristos }
3540e552da7Schristos
3550e552da7Schristos
3560e552da7Schristos /* Copy the contents of the stdio output buffer to `fd`. */
process_copy_output(process_info_t * p,FILE * stream)3570e552da7Schristos int process_copy_output(process_info_t* p, FILE* stream) {
3580e552da7Schristos char buf[1024];
3590e552da7Schristos int r;
3600e552da7Schristos
3610e552da7Schristos r = fseek(p->stdout_file, 0, SEEK_SET);
3620e552da7Schristos if (r < 0) {
3630e552da7Schristos perror("fseek");
3640e552da7Schristos return -1;
3650e552da7Schristos }
3660e552da7Schristos
3670e552da7Schristos /* TODO: what if the line is longer than buf */
3680e552da7Schristos while ((r = fread(buf, 1, sizeof(buf), p->stdout_file)) != 0)
3690e552da7Schristos print_lines(buf, r, stream);
3700e552da7Schristos
3710e552da7Schristos if (ferror(p->stdout_file)) {
3720e552da7Schristos perror("read");
3730e552da7Schristos return -1;
3740e552da7Schristos }
3750e552da7Schristos
3760e552da7Schristos return 0;
3770e552da7Schristos }
3780e552da7Schristos
3790e552da7Schristos
3800e552da7Schristos /* Copy the last line of the stdio output buffer to `buffer` */
process_read_last_line(process_info_t * p,char * buffer,size_t buffer_len)3810e552da7Schristos int process_read_last_line(process_info_t *p,
3820e552da7Schristos char* buffer,
3830e552da7Schristos size_t buffer_len) {
3840e552da7Schristos char* ptr;
3850e552da7Schristos
3860e552da7Schristos int r = fseek(p->stdout_file, 0, SEEK_SET);
3870e552da7Schristos if (r < 0) {
3880e552da7Schristos perror("fseek");
3890e552da7Schristos return -1;
3900e552da7Schristos }
3910e552da7Schristos
3920e552da7Schristos buffer[0] = '\0';
3930e552da7Schristos
3940e552da7Schristos while (fgets(buffer, buffer_len, p->stdout_file) != NULL) {
3950e552da7Schristos for (ptr = buffer; *ptr && *ptr != '\r' && *ptr != '\n'; ptr++)
3960e552da7Schristos ;
3970e552da7Schristos *ptr = '\0';
3980e552da7Schristos }
3990e552da7Schristos
4000e552da7Schristos if (ferror(p->stdout_file)) {
4010e552da7Schristos perror("read");
4020e552da7Schristos buffer[0] = '\0';
4030e552da7Schristos return -1;
4040e552da7Schristos }
4050e552da7Schristos return 0;
4060e552da7Schristos }
4070e552da7Schristos
4080e552da7Schristos
4090e552da7Schristos /* Return the name that was specified when `p` was started by process_start */
process_get_name(process_info_t * p)4100e552da7Schristos char* process_get_name(process_info_t *p) {
4110e552da7Schristos return p->name;
4120e552da7Schristos }
4130e552da7Schristos
4140e552da7Schristos
4150e552da7Schristos /* Terminate process `p`. */
process_terminate(process_info_t * p)4160e552da7Schristos int process_terminate(process_info_t *p) {
4170e552da7Schristos return kill(p->pid, SIGTERM);
4180e552da7Schristos }
4190e552da7Schristos
4200e552da7Schristos
4210e552da7Schristos /* Return the exit code of process p. On error, return -1. */
process_reap(process_info_t * p)4220e552da7Schristos int process_reap(process_info_t *p) {
4230e552da7Schristos if (WIFEXITED(p->status)) {
4240e552da7Schristos return WEXITSTATUS(p->status);
4250e552da7Schristos } else {
4260e552da7Schristos return p->status; /* ? */
4270e552da7Schristos }
4280e552da7Schristos }
4290e552da7Schristos
4300e552da7Schristos
4310e552da7Schristos /* Clean up after terminating process `p` (e.g. free the output buffer etc.). */
process_cleanup(process_info_t * p)4320e552da7Schristos void process_cleanup(process_info_t *p) {
4330e552da7Schristos fclose(p->stdout_file);
4340e552da7Schristos free(p->name);
4350e552da7Schristos }
4360e552da7Schristos
4370e552da7Schristos
4380e552da7Schristos /* Move the console cursor one line up and back to the first column. */
rewind_cursor(void)4390e552da7Schristos void rewind_cursor(void) {
4400e552da7Schristos #if defined(__MVS__)
4410e552da7Schristos fprintf(stderr, "\047[2K\r");
4420e552da7Schristos #else
4430e552da7Schristos fprintf(stderr, "\033[2K\r");
4440e552da7Schristos #endif
4450e552da7Schristos }
446