listen on a port (postlicyd)
[apps/pfixtools.git] / 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  */
35
36 #include <fcntl.h>
37 #include <grp.h>
38 #include <pwd.h>
39 #include <sys/un.h>
40
41 #include "common.h"
42
43 sig_atomic_t sigint  = false;
44 sig_atomic_t sighup  = false;
45
46 static FILE *pidfile = NULL;
47
48 void common_sighandler(int sig)
49 {
50     static time_t lastintr = 0;
51     time_t now = time(NULL);
52
53     switch (sig) {
54       case SIGINT:
55         if (sigint) {
56             if (now - lastintr >= 1)
57                 break;
58         } else {
59             lastintr = now;
60             sigint   = true;
61         }
62         return;
63
64       case SIGHUP:
65         sighup = true;
66         return;
67
68       default:
69         syslog(LOG_ERR, "Killed (got signal %d)...", sig);
70         exit(-1);
71     }
72 }
73
74 static int setnonblock(int sock)
75 {
76     int res = fcntl(sock, F_GETFL);
77
78     if (res < 0) {
79         UNIXERR("fcntl");
80         return -1;
81     }
82
83     if (fcntl(sock, F_SETFL, res | O_NONBLOCK) < 0) {
84         UNIXERR("fcntl");
85         return -1;
86     }
87
88     return 0;
89 }
90
91 int tcp_bind(const struct sockaddr *addr, socklen_t len)
92 {
93     int sock;
94
95     switch (addr->sa_family) {
96       case AF_UNIX:
97         unlink(((struct sockaddr_un *)addr)->sun_path);
98         sock = socket(PF_UNIX, SOCK_STREAM, 0);
99         break;
100       case AF_INET:
101         sock = socket(PF_INET, SOCK_STREAM, 0);
102         break;
103       case AF_INET6:
104         sock = socket(PF_INET6, SOCK_STREAM, 0);
105         break;
106       default:
107         errno = EINVAL;
108         return -1;
109     }
110
111     if (sock < 0) {
112         UNIXERR("socket");
113         return -1;
114     }
115
116     if (addr->sa_family != AF_UNIX) {
117         int v = 1;
118         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) < 0) {
119             UNIXERR("setsockopt(SO_REUSEADDR)");
120             close(sock);
121             return -1;
122         }
123     }
124
125     if (bind(sock, addr, len) < 0) {
126         UNIXERR("bind");
127         close(sock);
128         return -1;
129     }
130
131     return sock;
132 }
133
134 int tcp_listen(const struct sockaddr *addr, socklen_t len)
135 {
136     int sock = tcp_bind(addr, len);
137     if (listen(sock, 0) < 0) {
138         UNIXERR("bind");
139         close(sock);
140         return -1;
141     }
142     return sock;
143 }
144
145 int tcp_listen_nonblock(const struct sockaddr *addr, socklen_t len)
146 {
147     int sock = tcp_bind(addr, len);
148     if (setnonblock(sock)) {
149         close(sock);
150         return -1;
151     }
152     if (listen(sock, 0) < 0) {
153         UNIXERR("bind");
154         close(sock);
155         return -1;
156     }
157     return sock;
158 }
159
160 int accept_nonblock(int fd)
161 {
162     int sock = accept(fd, NULL, 0);
163
164     if (sock < 0) {
165         UNIXERR("accept");
166         return -1;
167     }
168
169     if (setnonblock(sock)) {
170         close(sock);
171         return -1;
172     }
173
174     return sock;
175 }
176
177 int xwrite(int fd, const char *s, size_t l)
178 {
179     while (l > 0) {
180         int nb = write(fd, s, l);
181         if (nb < 0) {
182             if (errno == EINTR || errno == EAGAIN)
183                 continue;
184             return -1;
185         }
186         l -= nb;
187     }
188     return 0;
189 }
190
191 int daemon_detach(void)
192 {
193     pid_t pid;
194
195     close(STDIN_FILENO);
196     close(STDOUT_FILENO);
197     close(STDERR_FILENO);
198
199     open("/dev/null", O_RDWR);
200     open("/dev/null", O_RDWR);
201     open("/dev/null", O_RDWR);
202
203     pid = fork();
204     if (pid < 0)
205         return -1;
206     if (pid)
207         exit(0);
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         rewind(pidfile);
265         ftruncate(fileno(pidfile), 0);
266         fclose(pidfile);
267         pidfile = NULL;
268     }
269 }
270
271 extern initcall_t __madinit[], __madexit[];
272
273 static void common_shutdown(void)
274 {
275     pidfile_close();
276
277     for (int i = -1; __madexit[i]; i--) {
278         (*__madexit[i])();
279     }
280 }
281
282 static void __attribute__((__constructor__,__used__))
283 common_initialize(void)
284 {
285     if (atexit(common_shutdown)) {
286         fputs("Cannot hook my atexit function, quitting !\n", stderr);
287         abort();
288     }
289
290     for (int i = 0; __madinit[i]; i++) {
291         if ((*__madinit[i])()) {
292             exit(EXIT_FAILURE);
293         }
294     }
295 }
296