Move some code.
[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 bool log_syslog      = false;
50
51 static FILE *pidfile = NULL;
52
53 void common_sighandler(int sig)
54 {
55     switch (sig) {
56       case SIGTERM:
57       case SIGINT:
58         sigint = true;
59         return;
60
61       case SIGHUP:
62         sighup = true;
63         return;
64
65       default:
66         err("Killed (got signal %d)...", sig);
67         exit(-1);
68     }
69 }
70
71 int setnonblock(int sock)
72 {
73     int res = fcntl(sock, F_GETFL);
74
75     if (res < 0) {
76         UNIXERR("fcntl");
77         return -1;
78     }
79
80     if (fcntl(sock, F_SETFL, res | O_NONBLOCK) < 0) {
81         UNIXERR("fcntl");
82         return -1;
83     }
84
85     return 0;
86 }
87
88 int tcp_bind(const struct sockaddr *addr, socklen_t len)
89 {
90     int sock;
91
92     switch (addr->sa_family) {
93       case AF_UNIX:
94         unlink(((struct sockaddr_un *)addr)->sun_path);
95         sock = socket(PF_UNIX, SOCK_STREAM, 0);
96         break;
97       case AF_INET:
98         sock = socket(PF_INET, SOCK_STREAM, 0);
99         break;
100       case AF_INET6:
101         sock = socket(PF_INET6, SOCK_STREAM, 0);
102         break;
103       default:
104         errno = EINVAL;
105         return -1;
106     }
107
108     if (sock < 0) {
109         UNIXERR("socket");
110         return -1;
111     }
112
113     if (addr->sa_family != AF_UNIX) {
114         int v = 1;
115         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) < 0) {
116             UNIXERR("setsockopt(SO_REUSEADDR)");
117             close(sock);
118             return -1;
119         }
120     }
121
122     if (bind(sock, addr, len) < 0) {
123         UNIXERR("bind");
124         close(sock);
125         return -1;
126     }
127
128     return sock;
129 }
130
131 int tcp_listen(const struct sockaddr *addr, socklen_t len)
132 {
133     int sock = tcp_bind(addr, len);
134     if (listen(sock, 0) < 0) {
135         UNIXERR("bind");
136         close(sock);
137         return -1;
138     }
139     return sock;
140 }
141
142 int tcp_listen_nonblock(const struct sockaddr *addr, socklen_t len)
143 {
144     int sock = tcp_bind(addr, len);
145     if (setnonblock(sock)) {
146         close(sock);
147         return -1;
148     }
149     if (listen(sock, 0) < 0) {
150         UNIXERR("bind");
151         close(sock);
152         return -1;
153     }
154     return sock;
155 }
156
157 int accept_nonblock(int fd)
158 {
159     int sock = accept(fd, NULL, 0);
160
161     if (sock < 0) {
162         UNIXERR("accept");
163         return -1;
164     }
165
166     if (setnonblock(sock)) {
167         close(sock);
168         return -1;
169     }
170
171     return sock;
172 }
173
174 int xwrite(int fd, const char *s, size_t l)
175 {
176     while (l > 0) {
177         int nb = write(fd, s, l);
178         if (nb < 0) {
179             if (errno == EINTR || errno == EAGAIN)
180                 continue;
181             return -1;
182         }
183         l -= nb;
184     }
185     return 0;
186 }
187
188 int daemon_detach(void)
189 {
190     pid_t pid;
191
192     close(STDIN_FILENO);
193     close(STDOUT_FILENO);
194     close(STDERR_FILENO);
195
196     open("/dev/null", O_RDWR);
197     open("/dev/null", O_RDWR);
198     open("/dev/null", O_RDWR);
199
200     pid = fork();
201     if (pid < 0) {
202         return -1;
203     }
204     if (pid) {
205         daemon_process = false;
206         exit(0);
207     }
208
209     setsid();
210     return 0;
211 }
212
213 int drop_privileges(const char *user, const char *group)
214 {
215     if (!geteuid()) {
216         struct passwd *pw;
217         struct group *gr;
218
219         if (group) {
220             gr = getgrnam(group);
221             if (!gr)
222                 return -1;
223             setgid(gr->gr_gid);
224         }
225
226         pw = getpwnam(user);
227         if (!pw)
228             return -1;
229         if (!group) {
230             setgid(pw->pw_gid);
231         }
232         setuid(pw->pw_uid);
233     }
234
235     return 0;
236 }
237
238 int pidfile_open(const char *name)
239 {
240     if (name) {
241         pidfile = fopen(name, "w");
242         if (!pidfile)
243             return -1;
244         fprintf(pidfile, "%d\n", getpid());
245         return fflush(pidfile);
246     }
247     return 0;
248 }
249
250 int pidfile_refresh(void)
251 {
252     if (pidfile) {
253         rewind(pidfile);
254         ftruncate(fileno(pidfile), 0);
255         fprintf(pidfile, "%d\n", getpid());
256         return fflush(pidfile);
257     }
258     return 0;
259 }
260
261 static void pidfile_close(void)
262 {
263     if (pidfile) {
264         if (daemon_process) {
265             rewind(pidfile);
266             ftruncate(fileno(pidfile), 0);
267         }
268         fclose(pidfile);
269         pidfile = NULL;
270     }
271 }
272
273 int common_setup(const char* pidfilename, bool unsafe, const char* runas_user,
274                  const char* runas_group, bool daemonize)
275 {
276     if (pidfile_open(pidfilename) < 0) {
277         crit("unable to write pidfile %s", pidfilename);
278         return EXIT_FAILURE;
279     }
280
281     if (!unsafe && drop_privileges(runas_user, runas_group) < 0) {
282         crit("unable to drop privileges");
283         return EXIT_FAILURE;
284     }
285
286     if (daemonize && daemon_detach() < 0) {
287         crit("unable to fork");
288         return EXIT_FAILURE;
289     }
290
291     pidfile_refresh();
292     return EXIT_SUCCESS;
293 }
294
295 extern initcall_t __madinit[];
296 extern exitcall_t __madexit[];
297
298 static void common_shutdown(void)
299 {
300     if (daemon_process && log_syslog) {
301         info("stopping...");
302     }
303     pidfile_close();
304     for (int i = -1; __madexit[i]; i--) {
305         (*__madexit[i])();
306     }
307 }
308
309 static void __attribute__((__constructor__,__used__))
310 common_initialize(void)
311 {
312     if (atexit(common_shutdown)) {
313         fputs("Cannot hook my atexit function, quitting !\n", stderr);
314         abort();
315     }
316
317     for (int i = 0; __madinit[i]; i++) {
318         if ((*__madinit[i])()) {
319             exit(EXIT_FAILURE);
320         }
321     }
322 }
323