Hi *, since this topic often occurs, not the least when porting to Syllable, Plan 9, Linux/µclibc-ng/nios2, etc. here’s a patch relative to mksh R52c (last formal release), enabling a num‐ ber of debugging-related output lines whenever something re‐ lated to job signal handling occurs (start a process, handle SIGCHLD, wait for a process, etc). Well, some of it.
I’m posting this here after a quick test in the hope that it helps someone, especially in comparing output against a code base that is known to work, ceteris paribus. Index: jobs.c =================================================================== RCS file: /cvs/src/bin/mksh/jobs.c,v retrieving revision 1.120 diff -u -p -r1.120 jobs.c --- jobs.c 4 Mar 2016 14:26:13 -0000 1.120 +++ jobs.c 26 Jun 2016 16:04:36 -0000 @@ -139,7 +139,9 @@ static int const tt_sigs[] = { SIGTSTP, static void j_set_async(Job *); static void j_startjob(Job *); static int j_waitj(Job *, int, const char *); -static void j_sigchld(int); +static void j_sigchld_impl(int); +static void j_sigchld_manual(int); +static void j_sigchld_intr(int); static void j_print(Job *, int, struct shf *); static Job *j_lookup(const char *, int *); static Job *new_job(void); @@ -169,7 +171,7 @@ j_init(void) (void)sigemptyset(&sm_sigchld); (void)sigaddset(&sm_sigchld, SIGCHLD); - setsig(&sigtraps[SIGCHLD], j_sigchld, + setsig(&sigtraps[SIGCHLD], j_sigchld_intr, SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); #else /* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */ @@ -499,6 +501,23 @@ exchild(struct op *t, int flags, /* create child process */ forksleep = 1; + shellf("{D: exchild (no-sigsuspend %s, unemployed %s, noprospectofwork %s) before fork<%s>}\n", +#ifdef MKSH_NO_SIGSUSPEND + "on", +#else + "off", +#endif +#ifdef MKSH_UNEMPLOYED + "on", +#else + "off", +#endif +#ifdef MKSH_NOPROSPECTOFWORK + "on", +#else + "off", +#endif + p->command); while ((cldpid = fork()) < 0 && errno == EAGAIN && forksleep < 32) { if (intrsig) /* allow user to ^C out... */ @@ -518,6 +537,8 @@ exchild(struct op *t, int flags, errorf("can't fork - try again"); } p->pid = cldpid ? cldpid : (procpid = getpid()); + shellf("{D: after fork, child %d, in %s}\n", (int)p->pid, + cldpid ? "parent" : "child"); #ifndef MKSH_UNEMPLOYED /* job control set up */ @@ -595,6 +616,7 @@ exchild(struct op *t, int flags, #endif Flag(FTALKING) = 0; cleartraps(); + shellf("{D:child %d, handing over to exec}\n", (int)p->pid); /* no return */ execute(t, (flags & XERROK) | XEXEC, NULL); #ifndef MKSH_SMALL @@ -621,6 +643,7 @@ exchild(struct op *t, int flags, coproc.job = (void *)j; } if (flags & XBGND) { + shellf("{D:parent, backgrounding child %d}\n", (int)p->pid); j_set_async(j); if (Flag(FTALKING)) { shf_fprintf(shl_out, "[%d]", j->job); @@ -630,8 +653,10 @@ exchild(struct op *t, int flags, shf_putchar('\n', shl_out); shf_flush(shl_out); } - } else + } else { + shellf("{D:parent, waiting for child %d}\n", (int)p->pid); rv = j_waitj(j, jwflags, "jw:last proc"); + } } #ifndef MKSH_NOPROSPECTOFWORK @@ -1115,6 +1140,7 @@ j_waitj(Job *j, #ifdef MKSH_NO_SIGSUSPEND sigset_t omask; #endif + shellf("{D:j_waitj called}\n"); /* * No auto-notify on the job we are waiting on. @@ -1132,17 +1158,21 @@ j_waitj(Job *j, ((flags & JW_STOPPEDWAIT) && j->state == PSTOPPED)) { #ifndef MKSH_NOPROSPECTOFWORK #ifdef MKSH_NO_SIGSUSPEND + shellf("{D:j_waitj waiting for job: pause (no sigsuspend)}\n"); sigprocmask(SIG_SETMASK, &sm_default, &omask); pause(); /* note that handlers may run here so they need to know */ sigprocmask(SIG_SETMASK, &omask, NULL); #else + shellf("{D:j_waitj waiting for job: default (sigsuspend)}\n"); sigsuspend(&sm_default); #endif #else - j_sigchld(SIGCHLD); + shellf("{D:j_waitj waiting for job: manual (once)}\n"); + j_sigchld_manual(SIGCHLD); #endif if (fatal_trap) { + shellf("{D:j_waitj waiting for job resulted in fatal_trap}\n"); int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY); j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); runtraps(TF_FATAL); @@ -1150,9 +1180,11 @@ j_waitj(Job *j, j->flags |= oldf; } if ((flags & JW_INTERRUPT) && (rv = trap_pending())) { + shellf("{D:j_waitj waiting for job resulted in trap_pending}\n"); j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); return (-rv); } + shellf("{D:j_waitj waiting for job resulted ok}\n"); } j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY); @@ -1305,7 +1337,7 @@ j_waitj(Job *j, */ /* ARGSUSED */ static void -j_sigchld(int sig MKSH_A_UNUSED) +j_sigchld_impl(int sig MKSH_A_UNUSED) { int saved_errno = errno; Job *j; @@ -1336,6 +1368,8 @@ j_sigchld(int sig MKSH_A_UNUSED) getrusage(RUSAGE_CHILDREN, &ru0); do { + char dbg[1000]; + #ifndef MKSH_NOPROSPECTOFWORK pid = waitpid(-1, &status, (WNOHANG | #if defined(WCONTINUED) && defined(WIFCONTINUED) @@ -1345,6 +1379,8 @@ j_sigchld(int sig MKSH_A_UNUSED) #else pid = wait(&status); #endif + snprintf(dbg, sizeof(dbg), "{D:j_sigchld waited for job, got %d}\n", pid); + write(2, dbg, strlen(dbg)); /* * return if this would block (0) or no children @@ -1362,6 +1398,7 @@ j_sigchld(int sig MKSH_A_UNUSED) goto found; found: if (j == NULL) { + write(2, "{D:j_sigchld did not find job}\n", 31); /* Can occur if process has kids, then execs shell warningf(true, "bad process waited for (pid = %d)", pid); @@ -1369,6 +1406,7 @@ j_sigchld(int sig MKSH_A_UNUSED) ru0 = ru1; continue; } + write(2, "{D:j_sigchld found job}\n", 24); timeradd(&j->usrtime, &ru1.ru_utime, &j->usrtime); timersub(&j->usrtime, &ru0.ru_utime, &j->usrtime); @@ -1398,16 +1436,33 @@ j_sigchld(int sig MKSH_A_UNUSED) } #ifndef MKSH_NOPROSPECTOFWORK while (/* CONSTCOND */ 1); + write(2, "{D:j_sigchld ended waiting continuously}\n", 41); #else while (/* CONSTCOND */ 0); + write(2, "{D:j_sigchld ended waiting once}\n", 33); #endif + goto j_sigchld_out2; j_sigchld_out: + write(2, "{D:j_sigchld ended waiting via goto}\n", 37); + j_sigchld_out2: #ifdef MKSH_NO_SIGSUSPEND sigprocmask(SIG_SETMASK, &omask, NULL); #endif errno = saved_errno; } +static void +j_sigchld_manual(int sig) +{ + write(2, "{D:j_sigchld called manually}\n", 30); + j_sigchld_impl(sig); +} +static void +j_sigchld_intr(int sig) +{ + write(2, "{D:j_sigchld called from interrupt handler}\n", 44); + j_sigchld_impl(sig); +} /* * Called only when a process in j has exited/stopped (ie, called only Enjoy, //mirabilos -- Stéphane, I actually don’t block Googlemail, they’re just too utterly stupid to successfully deliver to me (or anyone else using Greylisting and not whitelisting their ranges). Same for a few other providers such as Hotmail. Some spammers (Yahoo) I do block.