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;
struct failure_context ctx;
va_list args;
+ lib_set_clean_exit(TRUE);
i_zero(&ctx);
ctx.type = LOG_TYPE_PANIC;
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;
struct failure_context ctx;
va_list args;
+ lib_set_clean_exit(TRUE);
i_zero(&ctx);
ctx.type = LOG_TYPE_FATAL;
ctx.exit_status = status;
};
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,
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);
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;
}
failures_deinit();
process_title_deinit();
random_deinit();
+
+ lib_clean_exit = TRUE;
}
/* 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);
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,