Fix the copyright and licensing stuff.
[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 CONTRIBUTORS ``AS IS'' AND ANY EXPRESS   */
20 /*  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED         */
21 /*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE    */
22 /*  DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY         */
23 /*  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL        */
24 /*  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS   */
25 /*  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)     */
26 /*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,       */
27 /*  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN  */
28 /*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
29 /*  POSSIBILITY OF SUCH DAMAGE.                                               */
30 /*                                                                            */
31 /*   Copyright (c) 2006-2008 the Authors                                      */
32 /*   see AUTHORS and source files for details                                 */
33 /******************************************************************************/
34
35 /*
36  * Copyright © 2007 Pierre Habouzit
37  * Copyright © 2008 Florent Bruneau
38  */
39
40 #include <fcntl.h>
41 #include <grp.h>
42 #include <pwd.h>
43 #include <sys/un.h>
44
45 #include "common.h"
46
47 bool daemon_process   = true;
48 int  log_level        = LOG_INFO;
49 bool log_syslog       = false;
50 const char *log_state = "";
51
52 static FILE *pidfile = NULL;
53
54 void common_sighandler(int sig)
55 {
56     switch (sig) {
57       default:
58         err("Killed (got signal %d)...", sig);
59         exit(-1);
60     }
61 }
62
63 int setnonblock(int sock)
64 {
65     int res = fcntl(sock, F_GETFL);
66
67     if (res < 0) {
68         UNIXERR("fcntl");
69         return -1;
70     }
71
72     if (fcntl(sock, F_SETFL, res | O_NONBLOCK) < 0) {
73         UNIXERR("fcntl");
74         return -1;
75     }
76
77     return 0;
78 }
79
80 int tcp_bind(const struct sockaddr *addr, socklen_t len)
81 {
82     int sock;
83
84     switch (addr->sa_family) {
85       case AF_UNIX:
86         unlink(((struct sockaddr_un *)addr)->sun_path);
87         sock = socket(PF_UNIX, SOCK_STREAM, 0);
88         break;
89       case AF_INET:
90         sock = socket(PF_INET, SOCK_STREAM, 0);
91         break;
92       case AF_INET6:
93         sock = socket(PF_INET6, SOCK_STREAM, 0);
94         break;
95       default:
96         errno = EINVAL;
97         return -1;
98     }
99
100     if (sock < 0) {
101         UNIXERR("socket");
102         return -1;
103     }
104
105     if (addr->sa_family != AF_UNIX) {
106         int v = 1;
107         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)) < 0) {
108             UNIXERR("setsockopt(SO_REUSEADDR)");
109             close(sock);
110             return -1;
111         }
112     }
113
114     if (bind(sock, addr, len) < 0) {
115         UNIXERR("bind");
116         close(sock);
117         return -1;
118     }
119
120     return sock;
121 }
122
123 int tcp_listen(const struct sockaddr *addr, socklen_t len)
124 {
125     int sock = tcp_bind(addr, len);
126     if (listen(sock, 0) < 0) {
127         UNIXERR("bind");
128         close(sock);
129         return -1;
130     }
131     return sock;
132 }
133
134 int tcp_listen_nonblock(const struct sockaddr *addr, socklen_t len)
135 {
136     int sock = tcp_bind(addr, len);
137     if (setnonblock(sock)) {
138         close(sock);
139         return -1;
140     }
141     if (listen(sock, 0) < 0) {
142         UNIXERR("bind");
143         close(sock);
144         return -1;
145     }
146     return sock;
147 }
148
149 int accept_nonblock(int fd)
150 {
151     int sock = accept(fd, NULL, 0);
152
153     if (sock < 0) {
154         UNIXERR("accept");
155         return -1;
156     }
157
158     if (setnonblock(sock)) {
159         close(sock);
160         return -1;
161     }
162
163     return sock;
164 }
165
166 int xwrite(int fd, const char *s, size_t l)
167 {
168     while (l > 0) {
169         int nb = write(fd, s, l);
170         if (nb < 0) {
171             if (errno == EINTR || errno == EAGAIN)
172                 continue;
173             return -1;
174         }
175         l -= nb;
176     }
177     return 0;
178 }
179
180 int daemon_detach(void)
181 {
182     pid_t pid;
183
184     close(STDIN_FILENO);
185     close(STDOUT_FILENO);
186     close(STDERR_FILENO);
187
188     open("/dev/null", O_RDWR);
189     open("/dev/null", O_RDWR);
190     open("/dev/null", O_RDWR);
191
192     pid = fork();
193     if (pid < 0) {
194         return -1;
195     }
196     if (pid) {
197         daemon_process = false;
198         exit(0);
199     }
200
201     setsid();
202     return 0;
203 }
204
205 int drop_privileges(const char *user, const char *group)
206 {
207     if (!geteuid()) {
208         struct passwd *pw;
209         struct group *gr;
210
211         if (group) {
212             gr = getgrnam(group);
213             if (!gr)
214                 return -1;
215             setgid(gr->gr_gid);
216         }
217
218         pw = getpwnam(user);
219         if (!pw)
220             return -1;
221         if (!group) {
222             setgid(pw->pw_gid);
223         }
224         setuid(pw->pw_uid);
225     }
226
227     return 0;
228 }
229
230 int pidfile_open(const char *name)
231 {
232     if (name) {
233         pidfile = fopen(name, "w");
234         if (!pidfile)
235             return -1;
236         fprintf(pidfile, "%d\n", getpid());
237         return fflush(pidfile);
238     }
239     return 0;
240 }
241
242 int pidfile_refresh(void)
243 {
244     if (pidfile) {
245         rewind(pidfile);
246         ftruncate(fileno(pidfile), 0);
247         fprintf(pidfile, "%d\n", getpid());
248         return fflush(pidfile);
249     }
250     return 0;
251 }
252
253 static void pidfile_close(void)
254 {
255     if (pidfile) {
256         if (daemon_process) {
257             rewind(pidfile);
258             ftruncate(fileno(pidfile), 0);
259         }
260         fclose(pidfile);
261         pidfile = NULL;
262     }
263 }
264
265 int common_setup(const char* pidfilename, bool unsafe, const char* runas_user,
266                  const char* runas_group, bool daemonize)
267 {
268     if (pidfile_open(pidfilename) < 0) {
269         crit("unable to write pidfile %s", pidfilename);
270         return EXIT_FAILURE;
271     }
272
273     if (!unsafe && drop_privileges(runas_user, runas_group) < 0) {
274         crit("unable to drop privileges");
275         return EXIT_FAILURE;
276     }
277
278     if (daemonize && daemon_detach() < 0) {
279         crit("unable to fork");
280         return EXIT_FAILURE;
281     }
282
283     pidfile_refresh();
284     return EXIT_SUCCESS;
285 }
286
287 #include "array.h"
288
289 ARRAY(exitcall_t)
290
291 static A(exitcall_t) __exit = ARRAY_INIT;
292
293 void common_register_exit(exitcall_t exitcall)
294 {
295     array_add(__exit, exitcall);
296 }
297
298 static void common_shutdown(void)
299 {
300     log_state = "stopping ";
301     if (daemon_process && log_syslog) {
302         info("");
303     }
304     pidfile_close();
305     for (int i = array_len(__exit) - 1 ; i >= 0 ; --i) {
306         array_elt(__exit, i)();
307     }
308     array_wipe(__exit);
309 }
310
311 void common_init(void)
312 {
313     static bool __ran = false;
314     if (__ran) {
315         return;
316     }
317     log_state = "starting ";
318     if (atexit(common_shutdown)) {
319         fputs("Cannot hook my atexit function, quitting !\n", stderr);
320         abort();
321     }
322     __ran = true;
323 }
324