186ac43abf98d20c52149ac017e605fb86a12dff
[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 xwrite(int fd, const char *s, size_t l)
163 {
164     while (l > 0) {
165         int nb = write(fd, s, l);
166         if (nb < 0) {
167             if (errno == EINTR || errno == EAGAIN)
168                 continue;
169             return -1;
170         }
171         l -= nb;
172     }
173     return 0;
174 }
175
176 int daemon_detach(void)
177 {
178     pid_t pid;
179
180     close(STDIN_FILENO);
181     close(STDOUT_FILENO);
182     close(STDERR_FILENO);
183
184     open("/dev/null", O_RDWR);
185     open("/dev/null", O_RDWR);
186     open("/dev/null", O_RDWR);
187
188     pid = fork();
189     if (pid < 0)
190         return -1;
191     if (pid)
192         exit(0);
193
194     setsid();
195     return 0;
196 }
197
198 int drop_privileges(const char *user, const char *group)
199 {
200     if (!geteuid()) {
201         struct passwd *pw;
202         struct group *gr;
203
204         if (group) {
205             gr = getgrnam(group);
206             if (!gr)
207                 return -1;
208             setgid(gr->gr_gid);
209         }
210
211         pw = getpwnam(user);
212         if (!pw)
213             return -1;
214         if (!group) {
215             setgid(pw->pw_gid);
216         }
217         setuid(pw->pw_uid);
218     }
219
220     return 0;
221 }
222
223 int pidfile_open(const char *name)
224 {
225     if (name) {
226         pidfile = fopen(name, "w");
227         if (!pidfile)
228             return -1;
229         fprintf(pidfile, "%d\n", getpid());
230         return fflush(pidfile);
231     }
232     return 0;
233 }
234
235 int pidfile_refresh(void)
236 {
237     if (pidfile) {
238         rewind(pidfile);
239         ftruncate(fileno(pidfile), 0);
240         fprintf(pidfile, "%d\n", getpid());
241         return fflush(pidfile);
242     }
243     return 0;
244 }
245
246 static void pidfile_close(void)
247 {
248     if (pidfile) {
249         rewind(pidfile);
250         ftruncate(fileno(pidfile), 0);
251         fclose(pidfile);
252         pidfile = NULL;
253     }
254 }
255
256 extern initcall_t __madinit[], __madexit[];
257
258 static void common_shutdown(void)
259 {
260     pidfile_close();
261
262     for (int i = -1; __madexit[i]; i--) {
263         (*__madexit[i])();
264     }
265 }
266
267 static void __attribute__((__constructor__,__used__))
268 common_initialize(void)
269 {
270     if (atexit(common_shutdown)) {
271         fputs("Cannot hook my atexit function, quitting !\n", stderr);
272         abort();
273     }
274
275     for (int i = 0; __madinit[i]; i++) {
276         if ((*__madinit[i])()) {
277             exit(EXIT_FAILURE);
278         }
279     }
280 }
281