xref: /netbsd-src/external/mit/libuv/dist/test/runner-unix.c (revision 5f2f42719cd62ff11fd913b40b7ce19f07c4fd25)
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