greylist initializer.
[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_listen_nonblock(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     if (setnonblock(sock)) {
132         close(sock);
133         return -1;
134     }
135
136     if (listen(sock, 0) < 0) {
137         UNIXERR("bind");
138         close(sock);
139         return -1;
140     }
141
142     return sock;
143 }
144
145 int accept_nonblock(int fd)
146 {
147     int sock = accept(fd, NULL, 0);
148
149     if (sock < 0) {
150         UNIXERR("accept");
151         return -1;
152     }
153
154     if (setnonblock(sock)) {
155         close(sock);
156         return -1;
157     }
158
159     return sock;
160 }
161
162 int daemon_detach(void)
163 {
164     pid_t pid;
165
166     close(STDIN_FILENO);
167     close(STDOUT_FILENO);
168     close(STDERR_FILENO);
169
170     open("/dev/null", O_RDWR);
171     open("/dev/null", O_RDWR);
172     open("/dev/null", O_RDWR);
173
174     pid = fork();
175     if (pid < 0)
176         return -1;
177     if (pid)
178         exit(0);
179
180     setsid();
181     return 0;
182 }
183
184 int drop_privileges(const char *user, const char *group)
185 {
186     if (!geteuid()) {
187         struct passwd *pw;
188         struct group *gr;
189
190         if (group) {
191             gr = getgrnam(group);
192             if (!gr)
193                 return -1;
194             setgid(gr->gr_gid);
195         }
196
197         pw = getpwnam(user);
198         if (!pw)
199             return -1;
200         if (!group) {
201             setgid(pw->pw_gid);
202         }
203         setuid(pw->pw_uid);
204     }
205
206     return 0;
207 }
208
209 int pidfile_open(const char *name)
210 {
211     if (name) {
212         pidfile = fopen(name, "w");
213         if (!pidfile)
214             return -1;
215         fprintf(pidfile, "%d\n", getpid());
216         return fflush(pidfile);
217     }
218     return 0;
219 }
220
221 int pidfile_refresh(void)
222 {
223     if (pidfile) {
224         rewind(pidfile);
225         ftruncate(fileno(pidfile), 0);
226         fprintf(pidfile, "%d\n", getpid());
227         return fflush(pidfile);
228     }
229     return 0;
230 }
231
232 static void pidfile_close(void)
233 {
234     if (pidfile) {
235         rewind(pidfile);
236         ftruncate(fileno(pidfile), 0);
237         fclose(pidfile);
238         pidfile = NULL;
239     }
240 }
241
242 extern initcall_t __madinit[], __madexit[];
243
244 static void common_shutdown(void)
245 {
246     pidfile_close();
247
248     for (int i = -1; __madexit[i]; i--) {
249         (*__madexit[i])();
250     }
251 }
252
253 static void __attribute__((__constructor__,__used__))
254 common_initialize(void)
255 {
256     if (atexit(common_shutdown)) {
257         fputs("Cannot hook my atexit function, quitting !\n", stderr);
258         abort();
259     }
260
261     for (int i = 0; __madinit[i]; i++) {
262         if ((*__madinit[i])()) {
263             exit(EXIT_FAILURE);
264         }
265     }
266 }
267