summaryrefslogtreecommitdiffstats
path: root/src/lib/lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/lib.c')
-rw-r--r--src/lib/lib.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/src/lib/lib.c b/src/lib/lib.c
new file mode 100644
index 0000000..bd2da91
--- /dev/null
+++ b/src/lib/lib.c
@@ -0,0 +1,206 @@
+/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "dovecot-version.h"
+#include "array.h"
+#include "event-filter.h"
+#include "env-util.h"
+#include "hostpid.h"
+#include "ipwd.h"
+#include "process-title.h"
+#include "restrict-access.h"
+#include "var-expand-private.h"
+#include "randgen.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+/* Mainly for including the full version information in core dumps.
+ NOTE: Don't set this const - otherwise it won't end up in core dumps. */
+char dovecot_build_info[] = DOVECOT_BUILD_INFO;
+
+static bool lib_initialized = FALSE;
+int dev_null_fd = -1;
+
+struct atexit_callback {
+ int priority;
+ lib_atexit_callback_t *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,
+ unsigned int source_linenum)
+{
+ if (unlink(path) < 0) {
+ i_error("unlink(%s) failed: %m (in %s:%u)",
+ path, source_fname, source_linenum);
+ return -1;
+ }
+ return 0;
+}
+
+#undef i_unlink_if_exists
+int i_unlink_if_exists(const char *path, const char *source_fname,
+ unsigned int source_linenum)
+{
+ if (unlink(path) == 0)
+ return 1;
+ else if (errno == ENOENT)
+ return 0;
+ else {
+ i_error("unlink(%s) failed: %m (in %s:%u)",
+ path, source_fname, source_linenum);
+ return -1;
+ }
+}
+
+void i_getopt_reset(void)
+{
+#ifdef __GLIBC__
+ /* a) for subcommands allow -options anywhere in command line
+ b) this is actually required for the reset to work (glibc bug?) */
+ optind = 0;
+#else
+ optind = 1;
+#endif
+}
+
+void lib_atexit(lib_atexit_callback_t *callback)
+{
+ lib_atexit_priority(callback, 0);
+}
+
+void lib_atexit_priority(lib_atexit_callback_t *callback, int priority)
+{
+ struct atexit_callback *cb;
+ const struct atexit_callback *callbacks;
+ unsigned int i, count;
+
+ if (!array_is_created(&atexit_callbacks))
+ i_array_init(&atexit_callbacks, 8);
+ else {
+ /* skip if it's already added */
+ callbacks = array_get(&atexit_callbacks, &count);
+ for (i = count; i > 0; i--) {
+ if (callbacks[i-1].callback == callback) {
+ i_assert(callbacks[i-1].priority == priority);
+ return;
+ }
+ }
+ }
+ cb = array_append_space(&atexit_callbacks);
+ cb->priority = priority;
+ cb->callback = callback;
+}
+
+static int atexit_callback_priority_cmp(const struct atexit_callback *cb1,
+ const struct atexit_callback *cb2)
+{
+ return cb1->priority - cb2->priority;
+}
+
+void lib_atexit_run(void)
+{
+ const struct atexit_callback *cb;
+
+ if (array_is_created(&atexit_callbacks)) {
+ array_sort(&atexit_callbacks, atexit_callback_priority_cmp);
+ array_foreach(&atexit_callbacks, cb)
+ (*cb->callback)();
+ array_free(&atexit_callbacks);
+ }
+}
+
+static void lib_open_non_stdio_dev_null(void)
+{
+ dev_null_fd = open("/dev/null", O_WRONLY);
+ if (dev_null_fd == -1)
+ i_fatal("open(/dev/null) failed: %m");
+ /* Make sure stdin, stdout and stderr fds exist. We especially rely on
+ stderr being available and a lot of code doesn't like fd being 0.
+ We'll open /dev/null as write-only also for stdin, since if any
+ reads are attempted from it we'll want them to fail. */
+ while (dev_null_fd < STDERR_FILENO) {
+ dev_null_fd = dup(dev_null_fd);
+ if (dev_null_fd == -1)
+ i_fatal("dup(/dev/null) failed: %m");
+ }
+ /* close the actual /dev/null fd on exec*(), but keep it in stdio fds */
+ 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);
+ random_init();
+ data_stack_init();
+ hostpid_init();
+ lib_open_non_stdio_dev_null();
+ lib_event_init();
+ 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;
+}
+
+bool lib_is_initialized(void)
+{
+ return lib_initialized;
+}
+
+void lib_deinit(void)
+{
+ i_assert(lib_initialized);
+ lib_initialized = FALSE;
+ lib_atexit_run();
+ ipwd_deinit();
+ hostpid_deinit();
+ var_expand_extensions_deinit();
+ event_filter_deinit();
+ data_stack_deinit_event();
+ lib_event_deinit();
+ restrict_access_deinit();
+ i_close_fd(&dev_null_fd);
+ data_stack_deinit();
+ failures_deinit();
+ process_title_deinit();
+ random_deinit();
+
+ lib_clean_exit = TRUE;
+}