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