Fix startup sequence.
[apps/pfixtools.git] / common / common.c
1 /******************************************************************************/
2 /*          pfixtools: a collection of postfix related tools                  */
3 /*          ~~~~~~~~~                                                         */
4 /*  ________________________________________________________________________  */
5 /*                                                                            */
6 /*  Redistribution and use in source and binary forms, with or without        */
7 /*  modification, are permitted provided that the following conditions        */
8 /*  are met:                                                                  */
9 /*                                                                            */
10 /*  1. Redistributions of source code must retain the above copyright         */
11 /*     notice, this list of conditions and the following disclaimer.          */
12 /*  2. Redistributions in binary form must reproduce the above copyright      */
13 /*     notice, this list of conditions and the following disclaimer in the    */
14 /*     documentation and/or other materials provided with the distribution.   */
15 /*  3. The names of its contributors may not be used to endorse or promote    */
16 /*     products derived from this software without specific prior written     */
17 /*     permission.                                                            */
18 /*                                                                            */
19 /*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND   */
20 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE     */
21 /*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        */
22 /*  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS    */
23 /*  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    */
24 /*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF      */
25 /*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
26 /*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN   */
27 /*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)   */
28 /*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF    */
29 /*  THE POSSIBILITY OF SUCH DAMAGE.                                           */
30 /******************************************************************************/
31
32 /*
33  * Copyright © 2007 Pierre Habouzit
34  * Copyright © 2008 Florent Bruneau
35  */
36
37 #include <fcntl.h>
38 #include <grp.h>
39 #include <pwd.h>
40 #include <sys/un.h>
41
42 #include "common.h"
43
44 sig_atomic_t sigint  = false;
45 sig_atomic_t sighup  = false;
46
47 bool daemon_process  = true;
48 int  log_level       = LOG_INFO;
49
50 static FILE *pidfile = NULL;
51
52 void common_sighandler(int sig)
53 {
54     switch (sig) {
55                         case SIGTERM:
56       case SIGINT:
57         sigint = true;
58         return;
59
60       case SIGHUP:
61         sighup = true;
62         return;
63
64       default:
65                                 err("Killed (got signal %d)...", sig);
66         exit(-1);
67     }
68 }
69
70 static int setnonblock(int sock)
71 {
72     int res = fcntl(sock, F_GETFL);
73
74     if (res < 0) {
75         UNIXERR("fcntl");
76         return -1;
77     }
78
79     if (fcntl(sock, F_SETFL, res | O_NONBLOCK) < 0) {
80         UNIXERR("fcntl");
81         return -1;
82     }
83
84     return 0;
85 }
86
87 int tcp_bind(const struct sockaddr *addr, socklen_t len)
88 {
89     int sock;
90
91     switch (addr->sa_family) {
92       case AF_UNIX:
93         unlink(((struct sockaddr_un *)addr)->sun_path);
94         sock = socket(PF_UNIX, SOCK_STREAM, 0);
95         break;
96       case AF_INET:
97         sock = socket(PF_INET, SOCK_STREAM, 0);
98         break;
99       case AF_INET6:
100         sock = socket(PF_INET6, SOCK_STREAM, 0);
101         break;
102       default:
103         errno = EINVAL;
104         return -1;
105     }
106
107     if (sock < 0) {
108         UNIXERR("socket");
109         return -1;
110     }
111
112     if (addr->sa_family != AF_UNIX) {
113         int v = 1;
114         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) < 0) {
115             UNIXERR("setsockopt(SO_REUSEADDR)");
116             close(sock);
117             return -1;
118         }
119     }
120
121     if (bind(sock, addr, len) < 0) {
122         UNIXERR("bind");
123         close(sock);
124         return -1;
125     }
126
127     return sock;
128 }
129
130 int tcp_listen(const struct sockaddr *addr, socklen_t len)
131 {
132     int sock = tcp_bind(addr, len);
133     if (listen(sock, 0) < 0) {
134         UNIXERR("bind");
135         close(sock);
136         return -1;
137     }
138     return sock;
139 }
140
141 int tcp_listen_nonblock(const struct sockaddr *addr, socklen_t len)
142 {
143     int sock = tcp_bind(addr, len);
144     if (setnonblock(sock)) {
145         close(sock);
146         return -1;
147     }
148     if (listen(sock, 0) < 0) {
149         UNIXERR("bind");
150         close(sock);
151         return -1;
152     }
153     return sock;
154 }
155
156 int accept_nonblock(int fd)
157 {
158     int sock = accept(fd, NULL, 0);
159
160     if (sock < 0) {
161         UNIXERR("accept");
162         return -1;
163     }
164
165     if (setnonblock(sock)) {
166         close(sock);
167         return -1;
168     }
169
170     return sock;
171 }
172
173 int xwrite(int fd, const char *s, size_t l)
174 {
175     while (l > 0) {
176         int nb = write(fd, s, l);
177         if (nb < 0) {
178             if (errno == EINTR || errno == EAGAIN)
179                 continue;
180             return -1;
181         }
182         l -= nb;
183     }
184     return 0;
185 }
186
187 int daemon_detach(void)
188 {
189     pid_t pid;
190
191     close(STDIN_FILENO);
192     close(STDOUT_FILENO);
193     close(STDERR_FILENO);
194
195     open("/dev/null", O_RDWR);
196     open("/dev/null", O_RDWR);
197     open("/dev/null", O_RDWR);
198
199     pid = fork();
200     if (pid < 0) {
201         return -1;
202                 }
203     if (pid) {
204                                 daemon_process = false;
205         exit(0);
206                 }
207
208     setsid();
209     return 0;
210 }
211
212 int drop_privileges(const char *user, const char *group)
213 {
214     if (!geteuid()) {
215         struct passwd *pw;
216         struct group *gr;
217
218         if (group) {
219             gr = getgrnam(group);
220             if (!gr)
221                 return -1;
222             setgid(gr->gr_gid);
223         }
224
225         pw = getpwnam(user);
226         if (!pw)
227             return -1;
228         if (!group) {
229             setgid(pw->pw_gid);
230         }
231         setuid(pw->pw_uid);
232     }
233
234     return 0;
235 }
236
237 int pidfile_open(const char *name)
238 {
239     if (name) {
240         pidfile = fopen(name, "w");
241         if (!pidfile)
242             return -1;
243                                 fprintf(pidfile, "%d\n", getpid());
244         return fflush(pidfile);
245     }
246     return 0;
247 }
248
249 int pidfile_refresh(void)
250 {
251     if (pidfile) {
252         rewind(pidfile);
253         ftruncate(fileno(pidfile), 0);
254         fprintf(pidfile, "%d\n", getpid());
255         return fflush(pidfile);
256     }
257     return 0;
258 }
259
260 static void pidfile_close(void)
261 {
262     if (pidfile) {
263         rewind(pidfile);
264         ftruncate(fileno(pidfile), 0);
265                                 fclose(pidfile);
266         pidfile = NULL;
267     }
268 }
269
270 int common_setup(const char* pidfilename, bool unsafe, const char* runas_user,
271                  const char* runas_group, bool daemonize)
272 {
273     if (!unsafe && drop_privileges(runas_user, runas_group) < 0) {
274         crit("unable to drop privileges");
275         return EXIT_FAILURE;
276     }
277
278     if (daemonize && daemon_detach() < 0) {
279         crit("unable to fork");
280         return EXIT_FAILURE;
281     }
282
283                 if (pidfile_open(pidfilename) < 0) {
284         crit("unable to write pidfile %s", pidfilename);
285         return EXIT_FAILURE;
286     }
287
288     pidfile_refresh();
289     return EXIT_SUCCESS;
290 }
291
292 extern initcall_t __madinit[];
293 extern exitcall_t __madexit[];
294
295 static void common_shutdown(void)
296 {
297                 if (daemon_process) {
298                                 info("stopping...");
299                     pidfile_close();
300                 }
301     for (int i = -1; __madexit[i]; i--) {
302         (*__madexit[i])();
303     }
304 }
305
306 static void __attribute__((__constructor__,__used__))
307 common_initialize(void)
308 {
309     if (atexit(common_shutdown)) {
310         fputs("Cannot hook my atexit function, quitting !\n", stderr);
311         abort();
312     }
313
314     for (int i = 0; __madinit[i]; i++) {
315         if ((*__madinit[i])()) {
316             exit(EXIT_FAILURE);
317         }
318     }
319 }
320