30c61de8114bfe34a74d5939d551833ddf065b7f
[sip-router] / daemonize.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2001-2003 Fhg Fokus
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License 
24  * along with this program; if not, write to the Free Software 
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 /*
28  * 
29  * History:
30  * --------
31  *  2004-02-20  removed from ser main.c into its own file (andrei)
32  *  2004-03-04  moved setuid/setgid in do_suid() (andrei)
33  *  2004-03-25  added increase_open_fds & set_core_dump (andrei)
34  *  2004-05-03  applied pgid patch from janakj
35  */
36
37 #define _XOPEN_SOURCE
38 #define _XOPEN_SOURCE_EXTENDED
39
40 #include <sys/types.h>
41 #include <unistd.h>
42 #include <signal.h>
43 #include <syslog.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <sys/time.h>      /* setrlimit */
49 #include <sys/resource.h> /* setrlimit */
50
51 #include "daemonize.h"
52 #include "globals.h"
53 #include "dprint.h"
54
55
56 #define MAX_FD 32 /* maximum number of inherited open file descriptors,
57                     (normally it shouldn't  be bigger  than 3) */
58
59
60
61 /* daemon init, return 0 on success, -1 on error */
62 int daemonize(char*  name)
63 {
64         FILE *pid_stream;
65         pid_t pid;
66         int r, p;
67
68
69         p=-1;
70
71         /* flush std file descriptors to avoid flushes after fork
72          *  (same message appearing multiple times)
73          *  and switch to unbuffered
74          */
75         setbuf(stdout, 0);
76         setbuf(stderr, 0);
77         if (chroot_dir&&(chroot(chroot_dir)<0)){
78                 LOG(L_CRIT, "Cannot chroot to %s: %s\n", chroot_dir, strerror(errno));
79                 goto error;
80         }
81         
82         if (chdir(working_dir)<0){
83                 LOG(L_CRIT,"cannot chdir to %s: %s\n", working_dir, strerror(errno));
84                 goto error;
85         }
86
87         /* fork to become!= group leader*/
88         if ((pid=fork())<0){
89                 LOG(L_CRIT, "Cannot fork:%s\n", strerror(errno));
90                 goto error;
91         }else if (pid!=0){
92                 /* parent process => exit*/
93                 exit(0);
94         }
95         /* become session leader to drop the ctrl. terminal */
96         if (setsid()<0){
97                 LOG(L_WARN, "setsid failed: %s\n",strerror(errno));
98         }else{
99                 own_pgid=1;/* we have our own process group */
100         }
101         /* fork again to drop group  leadership */
102         if ((pid=fork())<0){
103                 LOG(L_CRIT, "Cannot  fork:%s\n", strerror(errno));
104                 goto error;
105         }else if (pid!=0){
106                 /*parent process => exit */
107                 exit(0);
108         }
109
110         /* added by noh: create a pid file for the main process */
111         if (pid_file!=0){
112                 
113                 if ((pid_stream=fopen(pid_file, "r"))!=NULL){
114                         fscanf(pid_stream, "%d", &p);
115                         fclose(pid_stream);
116                         if (p==-1){
117                                 LOG(L_CRIT, "pid file %s exists, but doesn't contain a valid"
118                                         " pid number\n", pid_file);
119                                 goto error;
120                         }
121                         if (kill((pid_t)p, 0)==0 || errno==EPERM){
122                                 LOG(L_CRIT, "running process found in the pid file %s\n",
123                                         pid_file);
124                                 goto error;
125                         }else{
126                                 LOG(L_WARN, "pid file contains old pid, replacing pid\n");
127                         }
128                 }
129                 pid=getpid();
130                 if ((pid_stream=fopen(pid_file, "w"))==NULL){
131                         LOG(L_WARN, "unable to create pid file %s: %s\n", 
132                                 pid_file, strerror(errno));
133                         goto error;
134                 }else{
135                         fprintf(pid_stream, "%i\n", (int)pid);
136                         fclose(pid_stream);
137                 }
138         }
139
140         if (pgid_file!=0){
141                 if ((pid_stream=fopen(pgid_file, "r"))!=NULL){
142                         fscanf(pid_stream, "%d", &p);
143                         fclose(pid_stream);
144                         if (p==-1){
145                                 LOG(L_CRIT, "pgid file %s exists, but doesn't contain a valid"
146                                     " pgid number\n", pgid_file);
147                                 goto error;
148                         }
149                 }
150                 if (own_pgid){
151                         pid=getpgid(0);
152                         if ((pid_stream=fopen(pgid_file, "w"))==NULL){
153                                 LOG(L_WARN, "unable to create pgid file %s: %s\n",
154                                         pgid_file, strerror(errno));
155                                 goto error;
156                         }else{
157                                 fprintf(pid_stream, "%i\n", (int)pid);
158                                 fclose(pid_stream);
159                         }
160                 }else{
161                         LOG(L_WARN, "we don't have our own process so we won't save"
162                                         " our pgid\n");
163                         unlink(pgid_file); /* just to be sure nobody will miss-use the old
164                                                                   value*/
165                 }
166         }
167         
168         /* try to replace stdin, stdout & stderr with /dev/null */
169         if (freopen("/dev/null", "r", stdin)==0){
170                 LOG(L_ERR, "unable to replace stdin with /dev/null: %s\n",
171                                 strerror(errno));
172                 /* continue, leave it open */
173         };
174         if (freopen("/dev/null", "w", stdout)==0){
175                 LOG(L_ERR, "unable to replace stdout with /dev/null: %s\n",
176                                 strerror(errno));
177                 /* continue, leave it open */
178         };
179         /* close stderr only if log_stderr=0 */
180         if ((!log_stderr) &&(freopen("/dev/null", "w", stderr)==0)){
181                 LOG(L_ERR, "unable to replace stderr with /dev/null: %s\n",
182                                 strerror(errno));
183                 /* continue, leave it open */
184         };
185         
186         /* close any open file descriptors */
187         closelog();
188         for (r=3;r<MAX_FD; r++){
189                         close(r);
190         }
191         
192         if (log_stderr==0)
193                 openlog(name, LOG_PID|LOG_CONS, log_facility);
194                 /* LOG_CONS, LOG_PERRROR ? */
195
196         return  0;
197
198 error:
199         return -1;
200 }
201
202
203
204 int do_suid()
205 {
206         if (gid){
207                 if(setgid(gid)<0){
208                         LOG(L_CRIT, "cannot change gid to %d: %s\n", gid, strerror(errno));
209                         goto error;
210                 }
211         }
212         
213         if(uid){
214                 if(setuid(uid)<0){
215                         LOG(L_CRIT, "cannot change uid to %d: %s\n", uid, strerror(errno));
216                         goto error;
217                 }
218         }
219         return 0;
220 error:
221         return -1;
222 }
223
224
225
226 /* try to increase the open file limit */
227 int increase_open_fds(int target)
228 {
229         struct rlimit lim;
230         struct rlimit orig;
231         
232         if (getrlimit(RLIMIT_NOFILE, &lim)<0){
233                 LOG(L_CRIT, "cannot get the maximum number of file descriptors: %s\n",
234                                 strerror(errno));
235                 goto error;
236         }
237         orig=lim;
238         DBG("current open file limits: %lu/%lu\n",
239                         (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
240         if ((lim.rlim_cur==RLIM_INFINITY) || (target<=lim.rlim_cur))
241                 /* nothing to do */
242                 goto done;
243         else if ((lim.rlim_max==RLIM_INFINITY) || (target<=lim.rlim_max)){
244                 lim.rlim_cur=target; /* increase soft limit to target */
245         }else{
246                 /* more than the hard limit */
247                 LOG(L_INFO, "trying to increase the open file limit"
248                                 " past the hard limit (%ld -> %d)\n", 
249                                 (unsigned long)lim.rlim_max, target);
250                 lim.rlim_max=target;
251                 lim.rlim_cur=target;
252         }
253         DBG("increasing open file limits to: %lu/%lu\n",
254                         (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
255         if (setrlimit(RLIMIT_NOFILE, &lim)<0){
256                 LOG(L_CRIT, "cannot increase the open file limit to"
257                                 " %lu/%lu: %s\n",
258                                 (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max,
259                                 strerror(errno));
260                 if (orig.rlim_max>orig.rlim_cur){
261                         /* try to increase to previous maximum, better than not increasing
262                         * at all */
263                         lim.rlim_max=orig.rlim_max;
264                         lim.rlim_cur=orig.rlim_max;
265                         if (setrlimit(RLIMIT_NOFILE, &lim)==0){
266                                 LOG(L_CRIT, " maximum number of file descriptors increased to"
267                                                 " %u\n",(unsigned)orig.rlim_max);
268                         }
269                 }
270                 goto error;
271         }
272 done:
273         return 0;
274 error:
275         return -1;
276 }
277
278
279
280 /* enable core dumps */
281 int set_core_dump(int enable, int size)
282 {
283         struct rlimit lim;
284         struct rlimit newlim;
285         
286         if (enable){
287                 if (getrlimit(RLIMIT_CORE, &lim)<0){
288                         LOG(L_CRIT, "cannot get the maximum core size: %s\n",
289                                         strerror(errno));
290                         goto error;
291                 }
292                 if (lim.rlim_cur<size){
293                         /* first try max limits */
294                         newlim.rlim_max=RLIM_INFINITY;
295                         newlim.rlim_cur=newlim.rlim_max;
296                         if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
297                         /* now try with size */
298                         if (lim.rlim_max<size){
299                                 newlim.rlim_max=size;
300                         }
301                         newlim.rlim_cur=newlim.rlim_max;
302                         if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
303                         /* if this failed too, try rlim_max, better than nothing */
304                         newlim.rlim_max=lim.rlim_max;
305                         newlim.rlim_cur=newlim.rlim_max;
306                         if (setrlimit(RLIMIT_CORE, &newlim)<0){
307                                 LOG(L_CRIT, "could increase core limits at all: %s\n",
308                                                 strerror (errno));
309                         }else{
310                                 LOG(L_CRIT, "core limits increased only to %lu\n",
311                                                 (unsigned long)lim.rlim_max);
312                         }
313                         goto error; /* it's an error we haven't got the size we wanted*/
314                 }
315                 goto done; /*nothing to do */
316         }else{
317                 /* disable */
318                 newlim.rlim_cur=0;
319                 newlim.rlim_max=0;
320                 if (setrlimit(RLIMIT_CORE, &newlim)<0){
321                         LOG(L_CRIT, "failed to disable core dumps: %s\n",
322                                         strerror(errno));
323                         goto error;
324                 }
325         }
326 done:
327         DBG("core dump limits set to %lu\n", (unsigned long)newlim.rlim_cur);
328         return 0;
329 error:
330         return -1;
331 }