1e66f31c5Sopenharmony_ci/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2e66f31c5Sopenharmony_ci * 3e66f31c5Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy 4e66f31c5Sopenharmony_ci * of this software and associated documentation files (the "Software"), to 5e66f31c5Sopenharmony_ci * deal in the Software without restriction, including without limitation the 6e66f31c5Sopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7e66f31c5Sopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is 8e66f31c5Sopenharmony_ci * furnished to do so, subject to the following conditions: 9e66f31c5Sopenharmony_ci * 10e66f31c5Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 11e66f31c5Sopenharmony_ci * all copies or substantial portions of the Software. 12e66f31c5Sopenharmony_ci * 13e66f31c5Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14e66f31c5Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15e66f31c5Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16e66f31c5Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17e66f31c5Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18e66f31c5Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19e66f31c5Sopenharmony_ci * IN THE SOFTWARE. 20e66f31c5Sopenharmony_ci */ 21e66f31c5Sopenharmony_ci 22e66f31c5Sopenharmony_ci#include "runner-unix.h" 23e66f31c5Sopenharmony_ci#include "runner.h" 24e66f31c5Sopenharmony_ci 25e66f31c5Sopenharmony_ci#include <limits.h> 26e66f31c5Sopenharmony_ci#include <stdint.h> /* uintptr_t */ 27e66f31c5Sopenharmony_ci 28e66f31c5Sopenharmony_ci#include <errno.h> 29e66f31c5Sopenharmony_ci#include <unistd.h> /* usleep */ 30e66f31c5Sopenharmony_ci#include <string.h> /* strdup */ 31e66f31c5Sopenharmony_ci#include <stdio.h> 32e66f31c5Sopenharmony_ci#include <stdlib.h> 33e66f31c5Sopenharmony_ci#include <sys/types.h> 34e66f31c5Sopenharmony_ci#include <signal.h> 35e66f31c5Sopenharmony_ci#include <sys/wait.h> 36e66f31c5Sopenharmony_ci#include <sys/stat.h> 37e66f31c5Sopenharmony_ci#include <assert.h> 38e66f31c5Sopenharmony_ci 39e66f31c5Sopenharmony_ci#include <sys/select.h> 40e66f31c5Sopenharmony_ci#include <sys/time.h> 41e66f31c5Sopenharmony_ci#include <pthread.h> 42e66f31c5Sopenharmony_ci 43e66f31c5Sopenharmony_ci#ifdef __APPLE__ 44e66f31c5Sopenharmony_ci#include <TargetConditionals.h> 45e66f31c5Sopenharmony_ci#endif 46e66f31c5Sopenharmony_ci 47e66f31c5Sopenharmony_ciextern char** environ; 48e66f31c5Sopenharmony_ci 49e66f31c5Sopenharmony_cistatic void closefd(int fd) { 50e66f31c5Sopenharmony_ci if (close(fd) == 0 || errno == EINTR || errno == EINPROGRESS) 51e66f31c5Sopenharmony_ci return; 52e66f31c5Sopenharmony_ci 53e66f31c5Sopenharmony_ci perror("close"); 54e66f31c5Sopenharmony_ci abort(); 55e66f31c5Sopenharmony_ci} 56e66f31c5Sopenharmony_ci 57e66f31c5Sopenharmony_ci 58e66f31c5Sopenharmony_civoid notify_parent_process(void) { 59e66f31c5Sopenharmony_ci char* arg; 60e66f31c5Sopenharmony_ci int fd; 61e66f31c5Sopenharmony_ci 62e66f31c5Sopenharmony_ci arg = getenv("UV_TEST_RUNNER_FD"); 63e66f31c5Sopenharmony_ci if (arg == NULL) 64e66f31c5Sopenharmony_ci return; 65e66f31c5Sopenharmony_ci 66e66f31c5Sopenharmony_ci fd = atoi(arg); 67e66f31c5Sopenharmony_ci assert(fd > STDERR_FILENO); 68e66f31c5Sopenharmony_ci unsetenv("UV_TEST_RUNNER_FD"); 69e66f31c5Sopenharmony_ci closefd(fd); 70e66f31c5Sopenharmony_ci} 71e66f31c5Sopenharmony_ci 72e66f31c5Sopenharmony_ci 73e66f31c5Sopenharmony_ci/* Do platform-specific initialization. */ 74e66f31c5Sopenharmony_civoid platform_init(int argc, char **argv) { 75e66f31c5Sopenharmony_ci /* Disable stdio output buffering. */ 76e66f31c5Sopenharmony_ci setvbuf(stdout, NULL, _IONBF, 0); 77e66f31c5Sopenharmony_ci setvbuf(stderr, NULL, _IONBF, 0); 78e66f31c5Sopenharmony_ci signal(SIGPIPE, SIG_IGN); 79e66f31c5Sopenharmony_ci snprintf(executable_path, sizeof(executable_path), "%s", argv[0]); 80e66f31c5Sopenharmony_ci} 81e66f31c5Sopenharmony_ci 82e66f31c5Sopenharmony_ci 83e66f31c5Sopenharmony_ci/* Invoke "argv[0] test-name [test-part]". Store process info in *p. Make sure 84e66f31c5Sopenharmony_ci * that all stdio output of the processes is buffered up. */ 85e66f31c5Sopenharmony_ciint process_start(char* name, char* part, process_info_t* p, int is_helper) { 86e66f31c5Sopenharmony_ci FILE* stdout_file; 87e66f31c5Sopenharmony_ci int stdout_fd; 88e66f31c5Sopenharmony_ci const char* arg; 89e66f31c5Sopenharmony_ci char* args[16]; 90e66f31c5Sopenharmony_ci int pipefd[2]; 91e66f31c5Sopenharmony_ci char fdstr[8]; 92e66f31c5Sopenharmony_ci ssize_t rc; 93e66f31c5Sopenharmony_ci int n; 94e66f31c5Sopenharmony_ci pid_t pid; 95e66f31c5Sopenharmony_ci 96e66f31c5Sopenharmony_ci arg = getenv("UV_USE_VALGRIND"); 97e66f31c5Sopenharmony_ci n = 0; 98e66f31c5Sopenharmony_ci 99e66f31c5Sopenharmony_ci /* Disable valgrind for helpers, it complains about helpers leaking memory. 100e66f31c5Sopenharmony_ci * They're killed after the test and as such never get a chance to clean up. 101e66f31c5Sopenharmony_ci */ 102e66f31c5Sopenharmony_ci if (is_helper == 0 && arg != NULL && atoi(arg) != 0) { 103e66f31c5Sopenharmony_ci args[n++] = "valgrind"; 104e66f31c5Sopenharmony_ci args[n++] = "--quiet"; 105e66f31c5Sopenharmony_ci args[n++] = "--leak-check=full"; 106e66f31c5Sopenharmony_ci args[n++] = "--show-reachable=yes"; 107e66f31c5Sopenharmony_ci args[n++] = "--error-exitcode=125"; 108e66f31c5Sopenharmony_ci } 109e66f31c5Sopenharmony_ci 110e66f31c5Sopenharmony_ci args[n++] = executable_path; 111e66f31c5Sopenharmony_ci args[n++] = name; 112e66f31c5Sopenharmony_ci args[n++] = part; 113e66f31c5Sopenharmony_ci args[n++] = NULL; 114e66f31c5Sopenharmony_ci 115e66f31c5Sopenharmony_ci stdout_file = tmpfile(); 116e66f31c5Sopenharmony_ci stdout_fd = fileno(stdout_file); 117e66f31c5Sopenharmony_ci if (!stdout_file) { 118e66f31c5Sopenharmony_ci perror("tmpfile"); 119e66f31c5Sopenharmony_ci return -1; 120e66f31c5Sopenharmony_ci } 121e66f31c5Sopenharmony_ci 122e66f31c5Sopenharmony_ci if (is_helper) { 123e66f31c5Sopenharmony_ci if (pipe(pipefd)) { 124e66f31c5Sopenharmony_ci perror("pipe"); 125e66f31c5Sopenharmony_ci return -1; 126e66f31c5Sopenharmony_ci } 127e66f31c5Sopenharmony_ci 128e66f31c5Sopenharmony_ci snprintf(fdstr, sizeof(fdstr), "%d", pipefd[1]); 129e66f31c5Sopenharmony_ci if (setenv("UV_TEST_RUNNER_FD", fdstr, /* overwrite */ 1)) { 130e66f31c5Sopenharmony_ci perror("setenv"); 131e66f31c5Sopenharmony_ci return -1; 132e66f31c5Sopenharmony_ci } 133e66f31c5Sopenharmony_ci } 134e66f31c5Sopenharmony_ci 135e66f31c5Sopenharmony_ci p->terminated = 0; 136e66f31c5Sopenharmony_ci p->status = 0; 137e66f31c5Sopenharmony_ci 138e66f31c5Sopenharmony_ci#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) 139e66f31c5Sopenharmony_ci pid = -1; 140e66f31c5Sopenharmony_ci#else 141e66f31c5Sopenharmony_ci pid = fork(); 142e66f31c5Sopenharmony_ci#endif 143e66f31c5Sopenharmony_ci 144e66f31c5Sopenharmony_ci if (pid < 0) { 145e66f31c5Sopenharmony_ci perror("fork"); 146e66f31c5Sopenharmony_ci return -1; 147e66f31c5Sopenharmony_ci } 148e66f31c5Sopenharmony_ci 149e66f31c5Sopenharmony_ci if (pid == 0) { 150e66f31c5Sopenharmony_ci /* child */ 151e66f31c5Sopenharmony_ci if (is_helper) 152e66f31c5Sopenharmony_ci closefd(pipefd[0]); 153e66f31c5Sopenharmony_ci dup2(stdout_fd, STDOUT_FILENO); 154e66f31c5Sopenharmony_ci dup2(stdout_fd, STDERR_FILENO); 155e66f31c5Sopenharmony_ci#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) 156e66f31c5Sopenharmony_ci execve(args[0], args, environ); 157e66f31c5Sopenharmony_ci#endif 158e66f31c5Sopenharmony_ci perror("execve()"); 159e66f31c5Sopenharmony_ci _exit(127); 160e66f31c5Sopenharmony_ci } 161e66f31c5Sopenharmony_ci 162e66f31c5Sopenharmony_ci /* parent */ 163e66f31c5Sopenharmony_ci p->pid = pid; 164e66f31c5Sopenharmony_ci p->name = strdup(name); 165e66f31c5Sopenharmony_ci p->stdout_file = stdout_file; 166e66f31c5Sopenharmony_ci 167e66f31c5Sopenharmony_ci if (!is_helper) 168e66f31c5Sopenharmony_ci return 0; 169e66f31c5Sopenharmony_ci 170e66f31c5Sopenharmony_ci closefd(pipefd[1]); 171e66f31c5Sopenharmony_ci unsetenv("UV_TEST_RUNNER_FD"); 172e66f31c5Sopenharmony_ci 173e66f31c5Sopenharmony_ci do 174e66f31c5Sopenharmony_ci rc = read(pipefd[0], &n, 1); 175e66f31c5Sopenharmony_ci while (rc == -1 && errno == EINTR); 176e66f31c5Sopenharmony_ci 177e66f31c5Sopenharmony_ci closefd(pipefd[0]); 178e66f31c5Sopenharmony_ci 179e66f31c5Sopenharmony_ci if (rc == -1) { 180e66f31c5Sopenharmony_ci perror("read"); 181e66f31c5Sopenharmony_ci return -1; 182e66f31c5Sopenharmony_ci } 183e66f31c5Sopenharmony_ci 184e66f31c5Sopenharmony_ci if (rc > 0) { 185e66f31c5Sopenharmony_ci fprintf(stderr, "EOF expected but got data.\n"); 186e66f31c5Sopenharmony_ci return -1; 187e66f31c5Sopenharmony_ci } 188e66f31c5Sopenharmony_ci 189e66f31c5Sopenharmony_ci return 0; 190e66f31c5Sopenharmony_ci} 191e66f31c5Sopenharmony_ci 192e66f31c5Sopenharmony_ci 193e66f31c5Sopenharmony_citypedef struct { 194e66f31c5Sopenharmony_ci int pipe[2]; 195e66f31c5Sopenharmony_ci process_info_t* vec; 196e66f31c5Sopenharmony_ci int n; 197e66f31c5Sopenharmony_ci} dowait_args; 198e66f31c5Sopenharmony_ci 199e66f31c5Sopenharmony_ci 200e66f31c5Sopenharmony_ci/* This function is run inside a pthread. We do this so that we can possibly 201e66f31c5Sopenharmony_ci * timeout. 202e66f31c5Sopenharmony_ci */ 203e66f31c5Sopenharmony_cistatic void* dowait(void* data) { 204e66f31c5Sopenharmony_ci dowait_args* args = data; 205e66f31c5Sopenharmony_ci 206e66f31c5Sopenharmony_ci int i, r; 207e66f31c5Sopenharmony_ci process_info_t* p; 208e66f31c5Sopenharmony_ci 209e66f31c5Sopenharmony_ci for (i = 0; i < args->n; i++) { 210e66f31c5Sopenharmony_ci p = &args->vec[i]; 211e66f31c5Sopenharmony_ci if (p->terminated) continue; 212e66f31c5Sopenharmony_ci r = waitpid(p->pid, &p->status, 0); 213e66f31c5Sopenharmony_ci if (r < 0) { 214e66f31c5Sopenharmony_ci perror("waitpid"); 215e66f31c5Sopenharmony_ci return NULL; 216e66f31c5Sopenharmony_ci } 217e66f31c5Sopenharmony_ci p->terminated = 1; 218e66f31c5Sopenharmony_ci } 219e66f31c5Sopenharmony_ci 220e66f31c5Sopenharmony_ci if (args->pipe[1] >= 0) { 221e66f31c5Sopenharmony_ci /* Write a character to the main thread to notify it about this. */ 222e66f31c5Sopenharmony_ci ssize_t r; 223e66f31c5Sopenharmony_ci 224e66f31c5Sopenharmony_ci do 225e66f31c5Sopenharmony_ci r = write(args->pipe[1], "", 1); 226e66f31c5Sopenharmony_ci while (r == -1 && errno == EINTR); 227e66f31c5Sopenharmony_ci } 228e66f31c5Sopenharmony_ci 229e66f31c5Sopenharmony_ci return NULL; 230e66f31c5Sopenharmony_ci} 231e66f31c5Sopenharmony_ci 232e66f31c5Sopenharmony_ci 233e66f31c5Sopenharmony_ci/* Wait for all `n` processes in `vec` to terminate. Time out after `timeout` 234e66f31c5Sopenharmony_ci * msec, or never if timeout == -1. Return 0 if all processes are terminated, 235e66f31c5Sopenharmony_ci * -1 on error, -2 on timeout. */ 236e66f31c5Sopenharmony_ciint process_wait(process_info_t* vec, int n, int timeout) { 237e66f31c5Sopenharmony_ci int i; 238e66f31c5Sopenharmony_ci int r; 239e66f31c5Sopenharmony_ci int retval; 240e66f31c5Sopenharmony_ci process_info_t* p; 241e66f31c5Sopenharmony_ci dowait_args args; 242e66f31c5Sopenharmony_ci pthread_t tid; 243e66f31c5Sopenharmony_ci pthread_attr_t attr; 244e66f31c5Sopenharmony_ci unsigned int elapsed_ms; 245e66f31c5Sopenharmony_ci struct timeval timebase; 246e66f31c5Sopenharmony_ci struct timeval tv; 247e66f31c5Sopenharmony_ci fd_set fds; 248e66f31c5Sopenharmony_ci 249e66f31c5Sopenharmony_ci args.vec = vec; 250e66f31c5Sopenharmony_ci args.n = n; 251e66f31c5Sopenharmony_ci args.pipe[0] = -1; 252e66f31c5Sopenharmony_ci args.pipe[1] = -1; 253e66f31c5Sopenharmony_ci 254e66f31c5Sopenharmony_ci /* The simple case is where there is no timeout */ 255e66f31c5Sopenharmony_ci if (timeout == -1) { 256e66f31c5Sopenharmony_ci dowait(&args); 257e66f31c5Sopenharmony_ci return 0; 258e66f31c5Sopenharmony_ci } 259e66f31c5Sopenharmony_ci 260e66f31c5Sopenharmony_ci /* Hard case. Do the wait with a timeout. 261e66f31c5Sopenharmony_ci * 262e66f31c5Sopenharmony_ci * Assumption: we are the only ones making this call right now. Otherwise 263e66f31c5Sopenharmony_ci * we'd need to lock vec. 264e66f31c5Sopenharmony_ci */ 265e66f31c5Sopenharmony_ci 266e66f31c5Sopenharmony_ci r = pipe((int*)&(args.pipe)); 267e66f31c5Sopenharmony_ci if (r) { 268e66f31c5Sopenharmony_ci perror("pipe()"); 269e66f31c5Sopenharmony_ci return -1; 270e66f31c5Sopenharmony_ci } 271e66f31c5Sopenharmony_ci 272e66f31c5Sopenharmony_ci if (pthread_attr_init(&attr)) 273e66f31c5Sopenharmony_ci abort(); 274e66f31c5Sopenharmony_ci 275e66f31c5Sopenharmony_ci#if defined(__MVS__) 276e66f31c5Sopenharmony_ci if (pthread_attr_setstacksize(&attr, 1024 * 1024)) 277e66f31c5Sopenharmony_ci#else 278e66f31c5Sopenharmony_ci if (pthread_attr_setstacksize(&attr, 256 * 1024)) 279e66f31c5Sopenharmony_ci#endif 280e66f31c5Sopenharmony_ci abort(); 281e66f31c5Sopenharmony_ci 282e66f31c5Sopenharmony_ci r = pthread_create(&tid, &attr, dowait, &args); 283e66f31c5Sopenharmony_ci 284e66f31c5Sopenharmony_ci if (pthread_attr_destroy(&attr)) 285e66f31c5Sopenharmony_ci abort(); 286e66f31c5Sopenharmony_ci 287e66f31c5Sopenharmony_ci if (r) { 288e66f31c5Sopenharmony_ci perror("pthread_create()"); 289e66f31c5Sopenharmony_ci retval = -1; 290e66f31c5Sopenharmony_ci goto terminate; 291e66f31c5Sopenharmony_ci } 292e66f31c5Sopenharmony_ci 293e66f31c5Sopenharmony_ci if (gettimeofday(&timebase, NULL)) 294e66f31c5Sopenharmony_ci abort(); 295e66f31c5Sopenharmony_ci 296e66f31c5Sopenharmony_ci tv = timebase; 297e66f31c5Sopenharmony_ci for (;;) { 298e66f31c5Sopenharmony_ci /* Check that gettimeofday() doesn't jump back in time. */ 299e66f31c5Sopenharmony_ci assert(tv.tv_sec > timebase.tv_sec || 300e66f31c5Sopenharmony_ci (tv.tv_sec == timebase.tv_sec && tv.tv_usec >= timebase.tv_usec)); 301e66f31c5Sopenharmony_ci 302e66f31c5Sopenharmony_ci elapsed_ms = 303e66f31c5Sopenharmony_ci (tv.tv_sec - timebase.tv_sec) * 1000 + 304e66f31c5Sopenharmony_ci (tv.tv_usec / 1000) - 305e66f31c5Sopenharmony_ci (timebase.tv_usec / 1000); 306e66f31c5Sopenharmony_ci 307e66f31c5Sopenharmony_ci r = 0; /* Timeout. */ 308e66f31c5Sopenharmony_ci if (elapsed_ms >= (unsigned) timeout) 309e66f31c5Sopenharmony_ci break; 310e66f31c5Sopenharmony_ci 311e66f31c5Sopenharmony_ci tv.tv_sec = (timeout - elapsed_ms) / 1000; 312e66f31c5Sopenharmony_ci tv.tv_usec = (timeout - elapsed_ms) % 1000 * 1000; 313e66f31c5Sopenharmony_ci 314e66f31c5Sopenharmony_ci FD_ZERO(&fds); 315e66f31c5Sopenharmony_ci FD_SET(args.pipe[0], &fds); 316e66f31c5Sopenharmony_ci 317e66f31c5Sopenharmony_ci r = select(args.pipe[0] + 1, &fds, NULL, NULL, &tv); 318e66f31c5Sopenharmony_ci if (!(r == -1 && errno == EINTR)) 319e66f31c5Sopenharmony_ci break; 320e66f31c5Sopenharmony_ci 321e66f31c5Sopenharmony_ci if (gettimeofday(&tv, NULL)) 322e66f31c5Sopenharmony_ci abort(); 323e66f31c5Sopenharmony_ci } 324e66f31c5Sopenharmony_ci 325e66f31c5Sopenharmony_ci if (r == -1) { 326e66f31c5Sopenharmony_ci perror("select()"); 327e66f31c5Sopenharmony_ci retval = -1; 328e66f31c5Sopenharmony_ci 329e66f31c5Sopenharmony_ci } else if (r) { 330e66f31c5Sopenharmony_ci /* The thread completed successfully. */ 331e66f31c5Sopenharmony_ci retval = 0; 332e66f31c5Sopenharmony_ci 333e66f31c5Sopenharmony_ci } else { 334e66f31c5Sopenharmony_ci /* Timeout. Kill all the children. */ 335e66f31c5Sopenharmony_ci for (i = 0; i < n; i++) { 336e66f31c5Sopenharmony_ci p = &vec[i]; 337e66f31c5Sopenharmony_ci kill(p->pid, SIGTERM); 338e66f31c5Sopenharmony_ci } 339e66f31c5Sopenharmony_ci retval = -2; 340e66f31c5Sopenharmony_ci } 341e66f31c5Sopenharmony_ci 342e66f31c5Sopenharmony_ci if (pthread_join(tid, NULL)) 343e66f31c5Sopenharmony_ci abort(); 344e66f31c5Sopenharmony_ci 345e66f31c5Sopenharmony_citerminate: 346e66f31c5Sopenharmony_ci closefd(args.pipe[0]); 347e66f31c5Sopenharmony_ci closefd(args.pipe[1]); 348e66f31c5Sopenharmony_ci return retval; 349e66f31c5Sopenharmony_ci} 350e66f31c5Sopenharmony_ci 351e66f31c5Sopenharmony_ci 352e66f31c5Sopenharmony_ci/* Returns the number of bytes in the stdio output buffer for process `p`. */ 353e66f31c5Sopenharmony_cilong int process_output_size(process_info_t *p) { 354e66f31c5Sopenharmony_ci /* Size of the p->stdout_file */ 355e66f31c5Sopenharmony_ci struct stat buf; 356e66f31c5Sopenharmony_ci 357e66f31c5Sopenharmony_ci memset(&buf, 0, sizeof(buf)); 358e66f31c5Sopenharmony_ci int r = fstat(fileno(p->stdout_file), &buf); 359e66f31c5Sopenharmony_ci if (r < 0) { 360e66f31c5Sopenharmony_ci return -1; 361e66f31c5Sopenharmony_ci } 362e66f31c5Sopenharmony_ci 363e66f31c5Sopenharmony_ci return (long)buf.st_size; 364e66f31c5Sopenharmony_ci} 365e66f31c5Sopenharmony_ci 366e66f31c5Sopenharmony_ci 367e66f31c5Sopenharmony_ci/* Copy the contents of the stdio output buffer to `fd`. */ 368e66f31c5Sopenharmony_ciint process_copy_output(process_info_t* p, FILE* stream) { 369e66f31c5Sopenharmony_ci char buf[1024]; 370e66f31c5Sopenharmony_ci int r; 371e66f31c5Sopenharmony_ci 372e66f31c5Sopenharmony_ci r = fseek(p->stdout_file, 0, SEEK_SET); 373e66f31c5Sopenharmony_ci if (r < 0) { 374e66f31c5Sopenharmony_ci perror("fseek"); 375e66f31c5Sopenharmony_ci return -1; 376e66f31c5Sopenharmony_ci } 377e66f31c5Sopenharmony_ci 378e66f31c5Sopenharmony_ci /* TODO: what if the line is longer than buf */ 379e66f31c5Sopenharmony_ci while ((r = fread(buf, 1, sizeof(buf), p->stdout_file)) != 0) 380e66f31c5Sopenharmony_ci print_lines(buf, r, stream); 381e66f31c5Sopenharmony_ci 382e66f31c5Sopenharmony_ci if (ferror(p->stdout_file)) { 383e66f31c5Sopenharmony_ci perror("read"); 384e66f31c5Sopenharmony_ci return -1; 385e66f31c5Sopenharmony_ci } 386e66f31c5Sopenharmony_ci 387e66f31c5Sopenharmony_ci return 0; 388e66f31c5Sopenharmony_ci} 389e66f31c5Sopenharmony_ci 390e66f31c5Sopenharmony_ci 391e66f31c5Sopenharmony_ci/* Copy the last line of the stdio output buffer to `buffer` */ 392e66f31c5Sopenharmony_ciint process_read_last_line(process_info_t *p, 393e66f31c5Sopenharmony_ci char* buffer, 394e66f31c5Sopenharmony_ci size_t buffer_len) { 395e66f31c5Sopenharmony_ci char* ptr; 396e66f31c5Sopenharmony_ci 397e66f31c5Sopenharmony_ci int r = fseek(p->stdout_file, 0, SEEK_SET); 398e66f31c5Sopenharmony_ci if (r < 0) { 399e66f31c5Sopenharmony_ci perror("fseek"); 400e66f31c5Sopenharmony_ci return -1; 401e66f31c5Sopenharmony_ci } 402e66f31c5Sopenharmony_ci 403e66f31c5Sopenharmony_ci buffer[0] = '\0'; 404e66f31c5Sopenharmony_ci 405e66f31c5Sopenharmony_ci while (fgets(buffer, buffer_len, p->stdout_file) != NULL) { 406e66f31c5Sopenharmony_ci for (ptr = buffer; *ptr && *ptr != '\r' && *ptr != '\n'; ptr++) 407e66f31c5Sopenharmony_ci ; 408e66f31c5Sopenharmony_ci *ptr = '\0'; 409e66f31c5Sopenharmony_ci } 410e66f31c5Sopenharmony_ci 411e66f31c5Sopenharmony_ci if (ferror(p->stdout_file)) { 412e66f31c5Sopenharmony_ci perror("read"); 413e66f31c5Sopenharmony_ci buffer[0] = '\0'; 414e66f31c5Sopenharmony_ci return -1; 415e66f31c5Sopenharmony_ci } 416e66f31c5Sopenharmony_ci return 0; 417e66f31c5Sopenharmony_ci} 418e66f31c5Sopenharmony_ci 419e66f31c5Sopenharmony_ci 420e66f31c5Sopenharmony_ci/* Return the name that was specified when `p` was started by process_start */ 421e66f31c5Sopenharmony_cichar* process_get_name(process_info_t *p) { 422e66f31c5Sopenharmony_ci return p->name; 423e66f31c5Sopenharmony_ci} 424e66f31c5Sopenharmony_ci 425e66f31c5Sopenharmony_ci 426e66f31c5Sopenharmony_ci/* Terminate process `p`. */ 427e66f31c5Sopenharmony_ciint process_terminate(process_info_t *p) { 428e66f31c5Sopenharmony_ci return kill(p->pid, SIGTERM); 429e66f31c5Sopenharmony_ci} 430e66f31c5Sopenharmony_ci 431e66f31c5Sopenharmony_ci 432e66f31c5Sopenharmony_ci/* Return the exit code of process p. On error, return -1. */ 433e66f31c5Sopenharmony_ciint process_reap(process_info_t *p) { 434e66f31c5Sopenharmony_ci if (WIFEXITED(p->status)) { 435e66f31c5Sopenharmony_ci return WEXITSTATUS(p->status); 436e66f31c5Sopenharmony_ci } else { 437e66f31c5Sopenharmony_ci return p->status; /* ? */ 438e66f31c5Sopenharmony_ci } 439e66f31c5Sopenharmony_ci} 440e66f31c5Sopenharmony_ci 441e66f31c5Sopenharmony_ci 442e66f31c5Sopenharmony_ci/* Clean up after terminating process `p` (e.g. free the output buffer etc.). */ 443e66f31c5Sopenharmony_civoid process_cleanup(process_info_t *p) { 444e66f31c5Sopenharmony_ci fclose(p->stdout_file); 445e66f31c5Sopenharmony_ci free(p->name); 446e66f31c5Sopenharmony_ci} 447e66f31c5Sopenharmony_ci 448e66f31c5Sopenharmony_ci 449e66f31c5Sopenharmony_ci/* Move the console cursor one line up and back to the first column. */ 450e66f31c5Sopenharmony_civoid rewind_cursor(void) { 451e66f31c5Sopenharmony_ci#if defined(__MVS__) 452e66f31c5Sopenharmony_ci fprintf(stderr, "\047[2K\r"); 453e66f31c5Sopenharmony_ci#else 454e66f31c5Sopenharmony_ci fprintf(stderr, "\033[2K\r"); 455e66f31c5Sopenharmony_ci#endif 456e66f31c5Sopenharmony_ci} 457