aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/lib.c17
-rw-r--r--lib/lib.h1
-rw-r--r--lib/xwrap.c25
-rw-r--r--toys.h1
-rw-r--r--www/code.html19
5 files changed, 56 insertions, 7 deletions
diff --git a/lib/lib.c b/lib/lib.c
index 1c1f2248..200f77d6 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -757,11 +757,24 @@ void generic_signal(int sig)
toys.signal = sig;
}
-// Install the same handler on every signal that defaults to killing the process
+void exit_signal(int sig)
+{
+ toys.exitval = sig|128;
+ xexit();
+}
+
+// Install the same handler on every signal that defaults to killing the
+// process, and
void sigatexit(void *handler)
{
+ struct arg_list *al = xmalloc(sizeof(struct arg_list));
int i;
- for (i=0; signames[i].num != SIGCHLD; i++) signal(signames[i].num, handler);
+
+ for (i=0; signames[i].num != SIGCHLD; i++)
+ signal(signames[i].num, exit_signal);
+ al->next = toys.xexit;
+ al->arg = handler;
+ toys.xexit = al;
}
// Convert name to signal number. If name == NULL print names.
diff --git a/lib/lib.h b/lib/lib.h
index 917be0c1..b12a110b 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -96,6 +96,7 @@ void show_help(FILE *out);
// xwrap.c
void xstrncpy(char *dest, char *src, size_t size);
void xstrncat(char *dest, char *src, size_t size);
+void _xexit(void) noreturn;
void xexit(void) noreturn;
void *xmalloc(size_t size);
void *xzalloc(size_t size);
diff --git a/lib/xwrap.c b/lib/xwrap.c
index 78103555..0b1ab8e9 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -28,13 +28,32 @@ void xstrncat(char *dest, char *src, size_t size)
strcpy(dest+len, src);
}
+// We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and
+// sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1)
+// instead of exiting, lets xexit() report stdout flush failures to stderr
+// and change the exit code to indicate error, lets our toys.exit function
+// change happen for signal exit paths and lets us remove the functions
+// after we've called them.
+
+void _xexit(void)
+{
+ if (toys.rebound) siglongjmp(*toys.rebound, 1);
+
+ _exit(toys.exitval);
+}
+
void xexit(void)
{
- if (toys.rebound) longjmp(*toys.rebound, 1);
+ // Call toys.xexit functions in reverse order added.
+ while (toys.xexit) {
+ // This is typecasting xexit->arg to a function pointer,then calling it.
+ ((void (*)(void))(toys.xexit->arg))();
+
+ free(llist_pop(&toys.xexit));
+ }
if (fflush(NULL) || ferror(stdout))
if (!toys.exitval) perror_msg("write");
-
- exit(toys.exitval);
+ _xexit();
}
// Die unless we can allocate memory.
diff --git a/toys.h b/toys.h
index 8a29730b..414f439c 100644
--- a/toys.h
+++ b/toys.h
@@ -113,6 +113,7 @@ extern struct toy_context {
// This is at the end so toy_init() doesn't zero it.
jmp_buf *rebound; // longjmp here instead of exit when do_rebound set
+ struct arg_list *xexit; // atexit() functions for xexit(), set by sigatexit()
void *stacktop; // nested toy_exec() call count, or 0 if vforked
} toys;
diff --git a/www/code.html b/www/code.html
index 7e15e181..c133611c 100644
--- a/www/code.html
+++ b/www/code.html
@@ -632,13 +632,28 @@ itoa().</p>
errors, to eliminate common error checking. This prints an error message
and the strerror() string for the errno encountered.</p>
-<p>You can intercept this exit by assigning a setjmp/longjmp buffer to
+<p>We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and
+sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1)
+instead of exiting, lets xexit() report stdout flush failures to stderr
+and change the exit code to indicate error, lets our toys.exit function
+change happen for signal exit paths and lets us remove the functions
+after we've called them.</p>
+
+<p>You can intercept our exit by assigning a setjmp/longjmp buffer to
toys.rebound (set it back to zero to restore the default behavior).
If you do this, cleaning up resource leaks is your problem.</p>
<ul>
<li><b>void xstrncpy(char *dest, char *src, size_t size)</b></li>
-<li><b>void xexit(void)</b></li>
+<li><p><b><p>void _xexit(void)</b></p>
+<p>Calls siglongjmp(toys.rebound, 1), or else _exit(toys.exitval). This
+lets you ignore errors with the NO_EXIT() macro wrapper, or intercept
+them with WOULD_EXIT().</p>
+<li><b><p>void xexit(void)</b></p>
+<p>Calls toys.xexit functions (if any) and flushes stdout/stderr (reporting
+failure to write to stdout both to stderr and in the exit code), then
+calls _xexit().</p>
+</li>
<li><b>void *xmalloc(size_t size)</b></li>
<li><b>void *xzalloc(size_t size)</b></li>
<li><b>void *xrealloc(void *ptr, size_t size)</b></li>