view src/lib/lib.c @ 22664:fea53c2725c0

director: Fix director_max_parallel_moves/kicks type Should be uint, not time.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 09 Nov 2017 12:24:16 +0200
parents f4076be3efe9
children cb108f786fb4
line wrap: on
line source

/* Copyright (c) 2001-2017 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "dovecot-version.h"
#include "array.h"
#include "env-util.h"
#include "hostpid.h"
#include "fd-close-on-exec.h"
#include "ipwd.h"
#include "process-title.h"
#include "var-expand-private.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;

int close_keep_errno(int *fd)
{
	int ret, old_errno = errno;

	i_assert(*fd != -1);

	ret = close(*fd);
	*fd = -1;
	errno = old_errno;
	return ret;
}

void fd_close_maybe_stdio(int *fd_in, int *fd_out)
{
	int *fdp[2] = { fd_in, fd_out };

	if (*fd_in == *fd_out)
		*fd_in = -1;

	for (unsigned int i = 0; i < N_ELEMENTS(fdp); i++) {
		if (*fdp[i] == -1)
			;
		else if (*fdp[i] > 1)
			i_close_fd(fdp[i]);
		else if (dup2(dev_null_fd, *fdp[i]) == *fdp[i])
			*fdp[i] = -1;
		else
			i_fatal("dup2(/dev/null, %d) failed: %m", *fdp[i]);
	}
}

#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_init(void)
{
	struct timeval tv;
	i_assert(!lib_initialized);

	/* standard way to get rand() return different values. */
	if (gettimeofday(&tv, NULL) < 0)
		i_fatal("gettimeofday(): %m");
	rand_set_seed((unsigned int) (tv.tv_sec ^ tv.tv_usec ^ getpid()));

	data_stack_init();
	hostpid_init();
	lib_open_non_stdio_dev_null();
	var_expand_extensions_init();

	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();
	i_close_fd(&dev_null_fd);
	data_stack_deinit();
	env_deinit();
	failures_deinit();
	process_title_deinit();
}