1 /* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "dovecot-version.h"
5 #include "array.h"
6 #include "event-filter.h"
7 #include "env-util.h"
8 #include "hostpid.h"
9 #include "ipwd.h"
10 #include "process-title.h"
11 #include "restrict-access.h"
12 #include "var-expand-private.h"
13 #include "randgen.h"
14 
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <sys/time.h>
18 
19 /* Mainly for including the full version information in core dumps.
20    NOTE: Don't set this const - otherwise it won't end up in core dumps. */
21 char dovecot_build_info[] = DOVECOT_BUILD_INFO;
22 
23 static bool lib_initialized = FALSE;
24 int dev_null_fd = -1;
25 
26 struct atexit_callback {
27 	int priority;
28 	lib_atexit_callback_t *callback;
29 };
30 
31 static ARRAY(struct atexit_callback) atexit_callbacks = ARRAY_INIT;
32 static bool lib_clean_exit;
33 
34 #undef i_unlink
i_unlink(const char * path,const char * source_fname,unsigned int source_linenum)35 int i_unlink(const char *path, const char *source_fname,
36 	     unsigned int source_linenum)
37 {
38 	if (unlink(path) < 0) {
39 		i_error("unlink(%s) failed: %m (in %s:%u)",
40 			path, source_fname, source_linenum);
41 		return -1;
42 	}
43 	return 0;
44 }
45 
46 #undef i_unlink_if_exists
i_unlink_if_exists(const char * path,const char * source_fname,unsigned int source_linenum)47 int i_unlink_if_exists(const char *path, const char *source_fname,
48 		       unsigned int source_linenum)
49 {
50 	if (unlink(path) == 0)
51 		return 1;
52 	else if (errno == ENOENT)
53 		return 0;
54 	else {
55 		i_error("unlink(%s) failed: %m (in %s:%u)",
56 			path, source_fname, source_linenum);
57 		return -1;
58 	}
59 }
60 
i_getopt_reset(void)61 void i_getopt_reset(void)
62 {
63 #ifdef __GLIBC__
64 	/* a) for subcommands allow -options anywhere in command line
65 	   b) this is actually required for the reset to work (glibc bug?) */
66 	optind = 0;
67 #else
68 	optind = 1;
69 #endif
70 }
71 
lib_atexit(lib_atexit_callback_t * callback)72 void lib_atexit(lib_atexit_callback_t *callback)
73 {
74 	lib_atexit_priority(callback, 0);
75 }
76 
lib_atexit_priority(lib_atexit_callback_t * callback,int priority)77 void lib_atexit_priority(lib_atexit_callback_t *callback, int priority)
78 {
79 	struct atexit_callback *cb;
80 	const struct atexit_callback *callbacks;
81 	unsigned int i, count;
82 
83 	if (!array_is_created(&atexit_callbacks))
84 		i_array_init(&atexit_callbacks, 8);
85 	else {
86 		/* skip if it's already added */
87 		callbacks = array_get(&atexit_callbacks, &count);
88 		for (i = count; i > 0; i--) {
89 			if (callbacks[i-1].callback == callback) {
90 				i_assert(callbacks[i-1].priority == priority);
91 				return;
92 			}
93 		}
94 	}
95 	cb = array_append_space(&atexit_callbacks);
96 	cb->priority = priority;
97 	cb->callback = callback;
98 }
99 
atexit_callback_priority_cmp(const struct atexit_callback * cb1,const struct atexit_callback * cb2)100 static int atexit_callback_priority_cmp(const struct atexit_callback *cb1,
101 					const struct atexit_callback *cb2)
102 {
103 	return cb1->priority - cb2->priority;
104 }
105 
lib_atexit_run(void)106 void lib_atexit_run(void)
107 {
108 	const struct atexit_callback *cb;
109 
110 	if (array_is_created(&atexit_callbacks)) {
111 		array_sort(&atexit_callbacks, atexit_callback_priority_cmp);
112 		array_foreach(&atexit_callbacks, cb)
113 			(*cb->callback)();
114 		array_free(&atexit_callbacks);
115 	}
116 }
117 
lib_open_non_stdio_dev_null(void)118 static void lib_open_non_stdio_dev_null(void)
119 {
120 	dev_null_fd = open("/dev/null", O_WRONLY);
121 	if (dev_null_fd == -1)
122 		i_fatal("open(/dev/null) failed: %m");
123 	/* Make sure stdin, stdout and stderr fds exist. We especially rely on
124 	   stderr being available and a lot of code doesn't like fd being 0.
125 	   We'll open /dev/null as write-only also for stdin, since if any
126 	   reads are attempted from it we'll want them to fail. */
127 	while (dev_null_fd < STDERR_FILENO) {
128 		dev_null_fd = dup(dev_null_fd);
129 		if (dev_null_fd == -1)
130 			i_fatal("dup(/dev/null) failed: %m");
131 	}
132 	/* close the actual /dev/null fd on exec*(), but keep it in stdio fds */
133 	fd_close_on_exec(dev_null_fd, TRUE);
134 }
135 
lib_set_clean_exit(bool set)136 void lib_set_clean_exit(bool set)
137 {
138 	lib_clean_exit = set;
139 }
140 
lib_exit(int status)141 void lib_exit(int status)
142 {
143 	lib_set_clean_exit(TRUE);
144 	exit(status);
145 }
146 
lib_atexit_handler(void)147 static void lib_atexit_handler(void)
148 {
149 	/* We're already in exit code path. Avoid using any functions that
150 	   might cause strange breakage. Especially anything that could call
151 	   exit() again could cause infinite looping in some OSes. */
152 	if (!lib_clean_exit) {
153 		const char *error = "Unexpected exit - converting to abort\n";
154 		if (write(STDERR_FILENO, error, strlen(error)) < 0) {
155 			/* ignore */
156 		}
157 		abort();
158 	}
159 }
160 
lib_init(void)161 void lib_init(void)
162 {
163 	i_assert(!lib_initialized);
164 	random_init();
165 	data_stack_init();
166 	hostpid_init();
167 	lib_open_non_stdio_dev_null();
168 	lib_event_init();
169 	event_filter_init();
170 	var_expand_extensions_init();
171 
172 	/* Default to clean exit. Otherwise there would be too many accidents
173 	   with e.g. command line parsing errors that try to return instead
174 	   of using lib_exit(). master_service_init_finish() will change this
175 	   again to be FALSE. */
176 	lib_set_clean_exit(TRUE);
177 	atexit(lib_atexit_handler);
178 
179 	lib_initialized = TRUE;
180 }
181 
lib_is_initialized(void)182 bool lib_is_initialized(void)
183 {
184 	return lib_initialized;
185 }
186 
lib_deinit(void)187 void lib_deinit(void)
188 {
189 	i_assert(lib_initialized);
190 	lib_initialized = FALSE;
191 	lib_atexit_run();
192 	ipwd_deinit();
193 	hostpid_deinit();
194 	var_expand_extensions_deinit();
195 	event_filter_deinit();
196 	data_stack_deinit_event();
197 	lib_event_deinit();
198 	restrict_access_deinit();
199 	i_close_fd(&dev_null_fd);
200 	data_stack_deinit();
201 	failures_deinit();
202 	process_title_deinit();
203 	random_deinit();
204 
205 	lib_clean_exit = TRUE;
206 }
207