af95efa7378d96b383a3b27951b7eb4a6466aa1b
[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                 struct flock lock;
240                 p_clear(&lock, 1);
241                 lock.l_type = F_WRLCK;
242     if (name) {
243         pidfile = fopen(name, "w");
244         if (!pidfile)
245             return -1;
246                                 if (fcntl(fileno(pidfile), F_SETLK, &lock) == -1) {
247                                                 crit("program already started");
248                                                 fclose(pidfile);
249                                                 pidfile = NULL;
250                                                 return -1;
251                                 }
252                                 fprintf(pidfile, "%d\n", getpid());
253         return fflush(pidfile);
254     }
255     return 0;
256 }
257
258 int pidfile_refresh(void)
259 {
260     if (pidfile) {
261         rewind(pidfile);
262         ftruncate(fileno(pidfile), 0);
263         fprintf(pidfile, "%d\n", getpid());
264         return fflush(pidfile);
265     }
266     return 0;
267 }
268
269 static void pidfile_close(void)
270 {
271                 struct flock lock;
272                 p_clear(&lock, 1);
273                 lock.l_type = F_UNLCK;
274     if (pidfile) {
275         rewind(pidfile);
276         ftruncate(fileno(pidfile), 0);
277         fcntl(fileno(pidfile), F_SETLK, &lock);
278                                 fclose(pidfile);
279         pidfile = NULL;
280     }
281 }
282
283 int common_setup(const char* pidfilename, bool unsafe, const char* runas_user,
284                  const char* runas_group, bool daemonize)
285 {
286     if (!unsafe && drop_privileges(runas_user, runas_group) < 0) {
287         crit("unable to drop privileges");
288         return EXIT_FAILURE;
289     }
290
291     if (daemonize && daemon_detach() < 0) {
292         crit("unable to fork");
293         return EXIT_FAILURE;
294     }
295
296                 if (pidfile_open(pidfilename) < 0) {
297         crit("unable to write pidfile %s", pidfilename);
298         return EXIT_FAILURE;
299     }
300
301     pidfile_refresh();
302     return EXIT_SUCCESS;
303 }
304
305 extern initcall_t __madinit[];
306 extern exitcall_t __madexit[];
307
308 static void common_shutdown(void)
309 {
310                 if (daemon_process) {
311                                 info("stopping...");
312                 }
313                 pidfile_close();
314     for (int i = -1; __madexit[i]; i--) {
315         (*__madexit[i])();
316     }
317 }
318
319 static void __attribute__((__constructor__,__used__))
320 common_initialize(void)
321 {
322     if (atexit(common_shutdown)) {
323         fputs("Cannot hook my atexit function, quitting !\n", stderr);
324         abort();
325     }
326
327     for (int i = 0; __madinit[i]; i++) {
328         if ((*__madinit[i])()) {
329             exit(EXIT_FAILURE);
330         }
331     }
332 }
333