/* This is called at main or AST level. It is at AST level for DONTWAITFORCHILD and at main level otherwise. In any case it is called when a child process terminated. At AST level it won't get interrupted by anything except a inner mode level AST. */ int vmsHandleChildTerm(struct child *child) { int status; register struct child *lastc, *c; int child_failed; vms_jobsefnmask &= ~(1 << (child->efn - 32)); lib$free_ef (&child->efn); if (child->comname) { if (!ISDB (DB_JOBS) && !ctrlYPressed) unlink (child->comname); free (child->comname); } (void) sigblock (fatal_signal_mask); child_failed = !(child->cstatus & 1 || ((child->cstatus & 7) == 0)); /* Search for a child matching the deceased one. */ lastc = 0; #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */ for (c = children; c != 0 && c != child; lastc = c, c = c->next) ; #else c = child; #endif if (child_failed && !c->noerror && !ignore_errors_flag) { /* The commands failed. Write an error message, delete non-precious targets, and abort. */ child_error (c, c->cstatus, 0, 0, 0); c->file->update_status = us_failed; delete_child_targets (c); } else { if (child_failed) { /* The commands failed, but we don't care. */ child_error (c, c->cstatus, 0, 0, 1); child_failed = 0; } #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */ /* If there are more commands to run, try to start them. */ start_job (c); switch (c->file->command_state) { case cs_running: /* Successfully started. */ break; case cs_finished: if (c->file->update_status != us_success) /* We failed to start the commands. */ delete_child_targets (c); break; default: OS (error, NILF, _("internal error: '%s' command_state"), c->file->name); abort (); break; } #endif /* RECURSIVEJOBS */ } /* Set the state flag to say the commands have finished. */ c->file->command_state = cs_finished; notice_finished_file (c->file); #if defined(RECURSIVEJOBS) /* I've had problems with recursive stuff and process handling */ /* Remove the child from the chain and free it. */ if (lastc == 0) children = c->next; else lastc->next = c->next; free_child (c); #endif /* RECURSIVEJOBS */ /* There is now another slot open. */ if (job_slots_used > 0) --job_slots_used; /* If the job failed, and the -k flag was not given, die. */ if (child_failed && !keep_going_flag) die (EXIT_FAILURE); (void) sigsetmask (sigblock (0) & ~(fatal_signal_mask)); return 1; }
RETSIGTYPE fatal_error_signal (int sig) { #ifdef __MSDOS__ extern int dos_status, dos_command_running; if (dos_command_running) { /* That was the child who got the signal, not us. */ dos_status |= (sig << 8); return; } remove_intermediates (1); exit (EXIT_FAILURE); #else /* not __MSDOS__ */ #ifdef _AMIGA remove_intermediates (1); if (sig == SIGINT) fputs (_("*** Break.\n"), stderr); exit (10); #else /* not Amiga */ #ifdef WINDOWS32 extern HANDLE main_thread; /* Windows creates a sperate thread for handling Ctrl+C, so we need to suspend the main thread, or else we will have race conditions when both threads call reap_children. */ if (main_thread) { DWORD susp_count = SuspendThread (main_thread); if (susp_count != 0) fprintf (stderr, "SuspendThread: suspend count = %ld\n", susp_count); else if (susp_count == (DWORD)-1) { DWORD ierr = GetLastError (); fprintf (stderr, "SuspendThread: error %ld: %s\n", ierr, map_windows32_error_to_string (ierr)); } } #endif handling_fatal_signal = 1; /* Set the handling for this signal to the default. It is blocked now while we run this handler. */ signal (sig, SIG_DFL); /* A termination signal won't be sent to the entire process group, but it means we want to kill the children. */ if (sig == SIGTERM) { struct child *c; for (c = children; c != 0; c = c->next) if (!c->remote) (void) kill (c->pid, SIGTERM); } /* If we got a signal that means the user wanted to kill make, remove pending targets. */ if (sig == SIGTERM || sig == SIGINT #ifdef SIGHUP || sig == SIGHUP #endif #ifdef SIGQUIT || sig == SIGQUIT #endif ) { struct child *c; /* Remote children won't automatically get signals sent to the process group, so we must send them. */ for (c = children; c != 0; c = c->next) if (c->remote) (void) remote_kill (c->pid, sig); for (c = children; c != 0; c = c->next) delete_child_targets (c); /* Clean up the children. We don't just use the call below because we don't want to print the "Waiting for children" message. */ while (job_slots_used > 0) reap_children (1, 0); } else /* Wait for our children to die. */ while (job_slots_used > 0) reap_children (1, 1); /* Delete any non-precious intermediate files that were made. */ remove_intermediates (1); #ifdef SIGQUIT if (sig == SIGQUIT) /* We don't want to send ourselves SIGQUIT, because it will cause a core dump. Just exit instead. */ exit (EXIT_FAILURE); #endif #ifdef WINDOWS32 if (main_thread) CloseHandle (main_thread); /* Cannot call W32_kill with a pid (it needs a handle). The exit status of 130 emulates what happens in Bash. */ exit (130); #else /* Signal the same code; this time it will really be fatal. The signal will be unblocked when we return and arrive then to kill us. */ if (kill (getpid (), sig) < 0) pfatal_with_name ("kill"); #endif /* not WINDOWS32 */ #endif /* not Amiga */ #endif /* not __MSDOS__ */ }