]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: abort() on unexpected exit()
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 5 May 2021 09:45:25 +0000 (12:45 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 10 May 2021 18:38:22 +0000 (18:38 +0000)
This can especially be useful in catching bugs in Lua script that could
unexpectedly exit.

src/lib-master/master-service.c
src/lib/failures.c
src/lib/lib.c
src/lib/lib.h
src/master/main.c

index 703bf44d9bc9a98c3eb3ca2800689178c443cd48..017a4a2894cdad2a4870d33ec5dd54b343c3325d 100644 (file)
@@ -661,6 +661,9 @@ void master_service_init_finish(struct master_service *service)
        i_assert(!service->init_finished);
        service->init_finished = TRUE;
 
+       /* From now on we'll abort() if exit() is called unexpectedly. */
+       lib_set_clean_exit(FALSE);
+
        /* set default signal handlers */
        if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0)
                sigint_flags |= LIBSIG_FLAG_RESTART;
index 4a9abd68f706674ba28703ae17430ba0c8985cdd..ab9470029676553ed064a728222d271f59050bf5 100644 (file)
@@ -516,6 +516,7 @@ void i_panic(const char *format, ...)
        struct failure_context ctx;
        va_list args;
 
+       lib_set_clean_exit(TRUE);
        i_zero(&ctx);
        ctx.type = LOG_TYPE_PANIC;
 
@@ -530,6 +531,7 @@ void i_fatal(const char *format, ...)
        struct failure_context ctx;
        va_list args;
 
+       lib_set_clean_exit(TRUE);
        i_zero(&ctx);
        ctx.type = LOG_TYPE_FATAL;
        ctx.exit_status = FATAL_DEFAULT;
@@ -545,6 +547,7 @@ void i_fatal_status(int status, const char *format, ...)
        struct failure_context ctx;
        va_list args;
 
+       lib_set_clean_exit(TRUE);
        i_zero(&ctx);
        ctx.type = LOG_TYPE_FATAL;
        ctx.exit_status = status;
index ab08da66e9070866a706d8c0494329a06e38189f..bd2da917c14204f9afe267ca6c9e73f29a2003bf 100644 (file)
@@ -29,6 +29,7 @@ struct atexit_callback {
 };
 
 static ARRAY(struct atexit_callback) atexit_callbacks = ARRAY_INIT;
+static bool lib_clean_exit;
 
 #undef i_unlink
 int i_unlink(const char *path, const char *source_fname,
@@ -132,6 +133,31 @@ static void lib_open_non_stdio_dev_null(void)
        fd_close_on_exec(dev_null_fd, TRUE);
 }
 
+void lib_set_clean_exit(bool set)
+{
+       lib_clean_exit = set;
+}
+
+void lib_exit(int status)
+{
+       lib_set_clean_exit(TRUE);
+       exit(status);
+}
+
+static void lib_atexit_handler(void)
+{
+       /* We're already in exit code path. Avoid using any functions that
+          might cause strange breakage. Especially anything that could call
+          exit() again could cause infinite looping in some OSes. */
+       if (!lib_clean_exit) {
+               const char *error = "Unexpected exit - converting to abort\n";
+               if (write(STDERR_FILENO, error, strlen(error)) < 0) {
+                       /* ignore */
+               }
+               abort();
+       }
+}
+
 void lib_init(void)
 {
        i_assert(!lib_initialized);
@@ -143,6 +169,13 @@ void lib_init(void)
        event_filter_init();
        var_expand_extensions_init();
 
+       /* Default to clean exit. Otherwise there would be too many accidents
+          with e.g. command line parsing errors that try to return instead
+          of using lib_exit(). master_service_init_finish() will change this
+          again to be FALSE. */
+       lib_set_clean_exit(TRUE);
+       atexit(lib_atexit_handler);
+
        lib_initialized = TRUE;
 }
 
@@ -168,4 +201,6 @@ void lib_deinit(void)
        failures_deinit();
        process_title_deinit();
        random_deinit();
+
+       lib_clean_exit = TRUE;
 }
index e16b4ce48cf7c3a4dd110de286a0ccde62516fa4..9c3ca34ffbab287d3d6038c94406a11f53a38bc7 100644 (file)
@@ -81,7 +81,11 @@ void lib_atexit_priority(lib_atexit_callback_t *callback, int priority);
 /* Manually run the atexit callbacks. lib_deinit() also does this if not
    explicitly called. */
 void lib_atexit_run(void);
-#define lib_exit(status) exit(status)
+/* Unless this or lib_deinit() is called, any unexpected exit() will result
+   in abort(). This can be helpful in catching unexpected exits. */
+void lib_set_clean_exit(bool set);
+/* Same as lib_set_clean_exit(TRUE) followed by exit(status). */
+void lib_exit(int status) ATTR_NORETURN;
 
 void lib_init(void);
 bool lib_is_initialized(void);
index a537b3a3cca8a924a26cb22a9bb54f08ac7b5648..4eb126fd1ad7fbd7c11ce7a21330f2271808a396 100644 (file)
@@ -885,6 +885,7 @@ int main(int argc, char *argv[])
        pidfile_path =
                i_strconcat(set->base_dir, "/"MASTER_PID_FILE_NAME, NULL);
 
+       lib_set_clean_exit(TRUE);
        master_service_init_log(master_service);
        startup_early_errors_flush();
        i_get_failure_handlers(&orig_fatal_callback, &orig_error_callback,