MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 373 lines 11 kB view raw
1#include "watch.h" 2#include "messages.h" 3 4#include <uv.h> 5#include <stdio.h> 6#include <signal.h> 7#include <stdint.h> 8#include <stdlib.h> 9#include <string.h> 10#include <stdbool.h> 11#include <crprintf.h> 12 13#ifdef _WIN32 14#include <direct.h> 15#endif 16 17#ifndef SIGKILL 18#define SIGKILL SIGTERM 19#endif 20 21typedef struct watch_state { 22 uv_loop_t loop; 23 uv_fs_event_t fs_event; 24 uv_signal_t sigint; 25 uv_signal_t sigterm; 26 uv_timer_t kill_timer; 27 uv_process_t *child; 28 char **child_argv; 29 char *watch_path; 30 bool no_clear_screen; 31 bool stop_requested; 32 bool restart_pending; 33 bool child_running; 34 bool fs_event_inited; 35 bool sigint_inited; 36 bool sigterm_inited; 37 bool kill_timer_inited; 38 bool kill_timer_running; 39 int final_exit_code; 40} watch_state_t; 41 42static void watch_clear_screen(void) { 43 fputs("\033[3J\033[2J\033[H", stdout); 44 fflush(stdout); 45} 46 47char *ant_watch_resolve_path(const char *path) { 48#ifdef _WIN32 49 char *resolved = _fullpath(NULL, path, 0); 50 if (resolved) return resolved; 51 return _strdup(path); 52#else 53 char abs_path[PATH_MAX]; 54 const char *resolved = realpath(path, abs_path); 55 return strdup(resolved ? resolved : path); 56#endif 57} 58 59int ant_watch_start( 60 uv_loop_t *loop, 61 uv_fs_event_t *event, 62 const char *path, 63 uv_fs_event_cb callback, 64 void *data, 65 unsigned int flags, 66 char **resolved_path_out 67) { 68 char *watch_path = NULL; 69 int rc = 0; 70 71 if (!loop || !event || !path || !callback) return UV_EINVAL; 72 73 watch_path = ant_watch_resolve_path(path); 74 if (!watch_path) return UV_ENOMEM; 75 76 rc = uv_fs_event_init(loop, event); 77 if (rc != 0) goto cleanup; 78 79 event->data = data; 80 rc = uv_fs_event_start(event, callback, watch_path, flags); 81 if (rc != 0) goto cleanup; 82 83 if (resolved_path_out) *resolved_path_out = watch_path; 84 else free(watch_path); 85 return 0; 86 87cleanup: 88 free(watch_path); 89 return rc; 90} 91 92void ant_watch_stop(uv_fs_event_t *event) { 93 if (!event) return; 94 uv_fs_event_stop(event); 95} 96 97static char **watch_build_child_argv(int argc, char **argv) { 98 char **child_argv = calloc((size_t)argc + 1, sizeof(char *)); 99 if (!child_argv) return NULL; 100 101 int out = 0; 102 for (int i = 0; i < argc; i++) { 103 if (strcmp(argv[i], "--watch") == 0) continue; 104 if (strcmp(argv[i], "--no-clear-screen") == 0) continue; 105 child_argv[out++] = argv[i]; 106 } 107 108 child_argv[out] = NULL; 109 if (out == 0) { 110 free(child_argv); 111 return NULL; 112 } 113 114 return child_argv; 115} 116 117static void watch_stop_kill_timer(watch_state_t *state) { 118 if (!state->kill_timer_inited || !state->kill_timer_running) return; 119 uv_timer_stop(&state->kill_timer); 120 state->kill_timer_running = false; 121} 122 123static void watch_on_child_closed(uv_handle_t *handle) { free(handle); } 124static void watch_request_shutdown(watch_state_t *state, int exit_code); 125static void watch_try_spawn_child(watch_state_t *state); 126 127static void watch_on_kill_timer(uv_timer_t *timer) { 128 watch_state_t *state = (watch_state_t *)timer->data; 129 state->kill_timer_running = false; 130 131 if (state->child_running && state->child) 132 uv_process_kill(state->child, SIGKILL); 133 if (state->stop_requested && !uv_is_closing((uv_handle_t *)&state->kill_timer)) 134 uv_close((uv_handle_t *)&state->kill_timer, NULL); 135} 136 137static void watch_on_child_exit(uv_process_t *proc, int64_t exit_status, int term_signal) { 138 watch_state_t *state = (watch_state_t *)proc->data; 139 state->child_running = false; 140 state->child = NULL; 141 watch_stop_kill_timer(state); 142 143 if (state->stop_requested) { 144 if (state->kill_timer_inited && !uv_is_closing((uv_handle_t *)&state->kill_timer)) 145 uv_close((uv_handle_t *)&state->kill_timer, NULL); 146 } else if (state->restart_pending) { 147 watch_try_spawn_child(state); 148 } else if (term_signal > 0) { 149 state->final_exit_code = 128 + term_signal; 150 } else state->final_exit_code = (int)exit_status; 151 152 uv_close((uv_handle_t *)proc, watch_on_child_closed); 153} 154 155static int watch_spawn_child(watch_state_t *state) { 156 uv_process_t *proc = calloc(1, sizeof(*proc)); 157 if (!proc) return UV_ENOMEM; 158 159 uv_stdio_container_t stdio[3]; 160 stdio[0].flags = UV_INHERIT_FD; stdio[0].data.fd = 0; 161 stdio[1].flags = UV_INHERIT_FD; stdio[1].data.fd = 1; 162 stdio[2].flags = UV_INHERIT_FD; stdio[2].data.fd = 2; 163 164 uv_process_options_t options = {0}; 165 options.file = state->child_argv[0]; 166 options.args = state->child_argv; 167 options.exit_cb = watch_on_child_exit; 168 options.stdio_count = 3; 169 options.stdio = stdio; 170 171 int rc = uv_spawn(&state->loop, proc, &options); 172 if (rc != 0) { 173 free(proc); 174 return rc; 175 } 176 177 proc->data = state; 178 state->child = proc; 179 state->child_running = true; 180 state->restart_pending = false; 181 return 0; 182} 183 184static void watch_try_spawn_child(watch_state_t *state) { 185 if (state->stop_requested) return; 186 if (!state->no_clear_screen && state->restart_pending) watch_clear_screen(); 187 int rc = watch_spawn_child(state); 188 189 if (rc != 0) { 190 crfprintf(stderr, msg.watch_spawn_failed, uv_strerror(rc)); 191 watch_request_shutdown(state, EXIT_FAILURE); 192 } 193} 194 195static void watch_start_kill_timer(watch_state_t *state, uint64_t timeout_ms) { 196 if (!state->kill_timer_inited || state->kill_timer_running) return; 197 int rc = uv_timer_start(&state->kill_timer, watch_on_kill_timer, timeout_ms, 0); 198 if (rc == 0) state->kill_timer_running = true; 199} 200 201static void watch_close_signals_and_watcher(watch_state_t *state) { 202 if (state->fs_event_inited && !uv_is_closing((uv_handle_t *)&state->fs_event)) { 203 uv_fs_event_stop(&state->fs_event); 204 uv_close((uv_handle_t *)&state->fs_event, NULL); 205 } 206 207 if (state->sigint_inited && !uv_is_closing((uv_handle_t *)&state->sigint)) { 208 uv_signal_stop(&state->sigint); 209 uv_close((uv_handle_t *)&state->sigint, NULL); 210 } 211 212 if (state->sigterm_inited && !uv_is_closing((uv_handle_t *)&state->sigterm)) { 213 uv_signal_stop(&state->sigterm); 214 uv_close((uv_handle_t *)&state->sigterm, NULL); 215 } 216} 217 218static void watch_request_shutdown(watch_state_t *state, int exit_code) { 219 if (state->stop_requested) return; 220 221 state->stop_requested = true; 222 state->restart_pending = false; 223 state->final_exit_code = exit_code; 224 225 watch_close_signals_and_watcher(state); 226 227 if (state->child_running && state->child) { 228 int rc = uv_process_kill(state->child, SIGTERM); 229 if (rc != 0 && rc != UV_ESRCH) { 230 crfprintf(stderr, msg.watch_graceful_term, uv_strerror(rc)); 231 } 232 watch_start_kill_timer(state, 1000); 233 } else if (state->kill_timer_inited && !uv_is_closing((uv_handle_t *)&state->kill_timer)) { 234 uv_close((uv_handle_t *)&state->kill_timer, NULL); 235 } 236} 237 238static void watch_request_restart(watch_state_t *state) { 239 if (state->stop_requested) return; 240 if (!state->restart_pending) {} 241 state->restart_pending = true; 242 243 if (state->child_running && state->child) { 244 int rc = uv_process_kill(state->child, SIGTERM); 245 if (rc != 0 && rc != UV_ESRCH) { 246 crfprintf(stderr, msg.watch_child_error, uv_strerror(rc)); 247 watch_request_shutdown(state, EXIT_FAILURE); 248 return; 249 } 250 watch_start_kill_timer(state, 1000); 251 } else watch_try_spawn_child(state); 252} 253 254static void watch_on_fs_event(uv_fs_event_t *handle, const char *filename, int events, int status) { 255 watch_state_t *state = (watch_state_t *)handle->data; 256 257 if (status < 0) { 258 crfprintf(stderr, msg.watch_warn_normal, state->watch_path, uv_strerror(status)); 259 return; 260 } 261 262 if ((events & (UV_CHANGE | UV_RENAME)) == 0) return; 263 watch_request_restart(state); 264} 265 266static void watch_on_signal(uv_signal_t *handle, int signum) { 267 watch_state_t *state = (watch_state_t *)handle->data; 268 watch_request_shutdown(state, 128 + signum); 269} 270 271int ant_watch_run(int argc, char **argv, const char *entry_file, bool no_clear_screen) { 272 if (!entry_file || !*entry_file) { 273 crfprintf(stderr, msg.watch_entrypoint_missing); 274 return EXIT_FAILURE; 275 } 276 277 watch_state_t state = {0}; 278 state.child_argv = watch_build_child_argv(argc, argv); 279 state.watch_path = ant_watch_resolve_path(entry_file); 280 state.no_clear_screen = no_clear_screen; 281 state.final_exit_code = EXIT_SUCCESS; 282 283 if (!state.child_argv || !state.watch_path) { 284 free(state.child_argv); 285 free(state.watch_path); 286 crfprintf(stderr, msg.watch_start_fatal); 287 return EXIT_FAILURE; 288 } 289 290 int rc = uv_loop_init(&state.loop); 291 if (rc != 0) { 292 crfprintf(stderr, msg.watch_loop_fatal, uv_strerror(rc)); 293 free(state.child_argv); free(state.watch_path); 294 return EXIT_FAILURE; 295 } 296 297 rc = ant_watch_start( 298 &state.loop, 299 &state.fs_event, 300 state.watch_path, 301 watch_on_fs_event, 302 &state, 0, NULL 303 ); 304 305 if (rc == 0) state.fs_event_inited = true; 306 307 if (rc != 0) { 308 crfprintf(stderr, msg.watch_file_failed, state.watch_path, uv_strerror(rc)); 309 watch_request_shutdown(&state, EXIT_FAILURE); 310 } 311 312 if (rc == 0) { 313 rc = uv_signal_init(&state.loop, &state.sigint); 314 if (rc == 0) state.sigint_inited = true; 315 if (rc == 0) { 316 state.sigint.data = &state; 317 rc = uv_signal_start(&state.sigint, watch_on_signal, SIGINT); 318 } 319 } 320 321 if (rc == 0) { 322 rc = uv_signal_init(&state.loop, &state.sigterm); 323 if (rc == 0) state.sigterm_inited = true; 324 if (rc == 0) { 325 state.sigterm.data = &state; 326 int sigterm_rc = uv_signal_start(&state.sigterm, watch_on_signal, SIGTERM); 327 if (sigterm_rc != 0 && sigterm_rc != UV_ENOSYS && sigterm_rc != UV_EINVAL) { 328 rc = sigterm_rc; 329 } else if (sigterm_rc != 0) { 330 uv_signal_stop(&state.sigterm); 331 uv_close((uv_handle_t *)&state.sigterm, NULL); 332 state.sigterm_inited = false; 333 } 334 } 335 } 336 337 if (rc == 0) { 338 rc = uv_timer_init(&state.loop, &state.kill_timer); 339 if (rc == 0) { 340 state.kill_timer_inited = true; 341 state.kill_timer.data = &state; 342 } 343 } 344 345 if (rc != 0) { 346 crfprintf(stderr, msg.watch_loop_handles_fatal, uv_strerror(rc)); 347 watch_request_shutdown(&state, EXIT_FAILURE); 348 } 349 350 if (!state.stop_requested) watch_try_spawn_child(&state); 351 while (uv_run(&state.loop, UV_RUN_DEFAULT) != 0) {} 352 353 if (state.fs_event_inited && !uv_is_closing((uv_handle_t *)&state.fs_event)) { 354 uv_close((uv_handle_t *)&state.fs_event, NULL); 355 } 356 if (state.sigint_inited && !uv_is_closing((uv_handle_t *)&state.sigint)) { 357 uv_close((uv_handle_t *)&state.sigint, NULL); 358 } 359 if (state.sigterm_inited && !uv_is_closing((uv_handle_t *)&state.sigterm)) { 360 uv_close((uv_handle_t *)&state.sigterm, NULL); 361 } 362 if (state.kill_timer_inited && !uv_is_closing((uv_handle_t *)&state.kill_timer)) { 363 uv_close((uv_handle_t *)&state.kill_timer, NULL); 364 } 365 while (uv_run(&state.loop, UV_RUN_DEFAULT) != 0) {} 366 367 rc = uv_loop_close(&state.loop); 368 if (rc != 0) crfprintf(stderr, msg.watch_loop_cleanup, uv_strerror(rc)); 369 370 free(state.child_argv); 371 free(state.watch_path); 372 return state.final_exit_code; 373}