Modifying the return value of cfg_set* functions, in order to make
[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  *  2007-06-07  added mlock_pages (no swap) support (andrei)
36   *             added set_rt_prio() (andrei)
37  */
38
39
40 #include <sys/types.h>
41
42 #define _XOPEN_SOURCE   /* needed on linux for the  getpgid prototype,  but
43                            openbsd 3.2 won't include common types (uint a.s.o)
44                            if defined before including sys/types.h */
45 #define _XOPEN_SOURCE_EXTENDED /* same as above */
46 #define __USE_XOPEN_EXTENDED /* same as above, overrides features.h */
47 #define __EXTENSIONS__ /* needed on solaris: if XOPEN_SOURCE is defined
48                           struct timeval defintion from <sys/time.h> won't
49                           be included => workarround define _EXTENSIONS_
50                            -andrei */
51 #include <signal.h>
52 #include <syslog.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <sys/time.h>    
58 #include <sys/resource.h> /* setrlimit */
59 #include <unistd.h>
60
61 #ifdef HAVE_SCHED_SETSCHEDULER
62 #include <sched.h>
63 #endif
64
65 #ifdef _POSIX_MEMLOCK
66 #define HAVE_MLOCKALL
67 #include <sys/mman.h>
68 #endif
69
70 #include "daemonize.h"
71 #include "globals.h"
72 #include "dprint.h"
73 #include "signals.h"
74
75
76 #define MAX_FD 32 /* maximum number of inherited open file descriptors,
77                     (normally it shouldn't  be bigger  than 3) */
78
79 /* daemon init, return 0 on success, -1 on error */
80 int daemonize(char*  name)
81 {
82         FILE *pid_stream;
83         pid_t pid;
84         int r, p;
85
86
87         p=-1;
88
89         /* flush std file descriptors to avoid flushes after fork
90          *  (same message appearing multiple times)
91          *  and switch to unbuffered
92          */
93         setbuf(stdout, 0);
94         setbuf(stderr, 0);
95         if (chroot_dir&&(chroot(chroot_dir)<0)){
96                 LOG(L_CRIT, "Cannot chroot to %s: %s\n", chroot_dir, strerror(errno));
97                 goto error;
98         }
99         
100         if (chdir(working_dir)<0){
101                 LOG(L_CRIT,"cannot chdir to %s: %s\n", working_dir, strerror(errno));
102                 goto error;
103         }
104
105         if (!dont_daemonize) {
106                 /* fork to become!= group leader*/
107                 if ((pid=fork())<0){
108                         LOG(L_CRIT, "Cannot fork:%s\n", strerror(errno));
109                         goto error;
110                 }else if (pid!=0){      
111                         /*parent process => exit */
112                         exit(0);
113                 }
114                 /* become session leader to drop the ctrl. terminal */
115                 if (setsid()<0){
116                         LOG(L_WARN, "setsid failed: %s\n",strerror(errno));
117                 }else{
118                         own_pgid=1;/* we have our own process group */
119                 }
120                 /* fork again to drop group  leadership */
121                 if ((pid=fork())<0){
122                         LOG(L_CRIT, "Cannot  fork:%s\n", strerror(errno));
123                         goto error;
124                 }else if (pid!=0){
125                         /*parent process => exit */
126                         exit(0);
127                 }
128         }
129         /* added by noh: create a pid file for the main process */
130         if (pid_file!=0){
131                 
132                 if ((pid_stream=fopen(pid_file, "r"))!=NULL){
133                         fscanf(pid_stream, "%d", &p);
134                         fclose(pid_stream);
135                         if (p==-1){
136                                 LOG(L_CRIT, "pid file %s exists, but doesn't contain a valid"
137                                         " pid number\n", pid_file);
138                                 goto error;
139                         }
140                         if (kill((pid_t)p, 0)==0 || errno==EPERM){
141                                 LOG(L_CRIT, "running process found in the pid file %s\n",
142                                         pid_file);
143                                 goto error;
144                         }else{
145                                 LOG(L_WARN, "pid file contains old pid, replacing pid\n");
146                         }
147                 }
148                 pid=getpid();
149                 if ((pid_stream=fopen(pid_file, "w"))==NULL){
150                         LOG(L_WARN, "unable to create pid file %s: %s\n", 
151                                 pid_file, strerror(errno));
152                         goto error;
153                 }else{
154                         fprintf(pid_stream, "%i\n", (int)pid);
155                         fclose(pid_stream);
156                 }
157         }
158
159         if (pgid_file!=0){
160                 if ((pid_stream=fopen(pgid_file, "r"))!=NULL){
161                         fscanf(pid_stream, "%d", &p);
162                         fclose(pid_stream);
163                         if (p==-1){
164                                 LOG(L_CRIT, "pgid file %s exists, but doesn't contain a valid"
165                                     " pgid number\n", pgid_file);
166                                 goto error;
167                         }
168                 }
169                 if (own_pgid){
170                         pid=getpgid(0);
171                         if ((pid_stream=fopen(pgid_file, "w"))==NULL){
172                                 LOG(L_WARN, "unable to create pgid file %s: %s\n",
173                                         pgid_file, strerror(errno));
174                                 goto error;
175                         }else{
176                                 fprintf(pid_stream, "%i\n", (int)pid);
177                                 fclose(pid_stream);
178                         }
179                 }else{
180                         LOG(L_WARN, "we don't have our own process so we won't save"
181                                         " our pgid\n");
182                         unlink(pgid_file); /* just to be sure nobody will miss-use the old
183                                                                   value*/
184                 }
185         }
186         
187         /* try to replace stdin, stdout & stderr with /dev/null */
188         if (freopen("/dev/null", "r", stdin)==0){
189                 LOG(L_ERR, "unable to replace stdin with /dev/null: %s\n",
190                                 strerror(errno));
191                 /* continue, leave it open */
192         };
193         if (freopen("/dev/null", "w", stdout)==0){
194                 LOG(L_ERR, "unable to replace stdout with /dev/null: %s\n",
195                                 strerror(errno));
196                 /* continue, leave it open */
197         };
198         /* close stderr only if log_stderr=0 */
199         if ((!log_stderr) &&(freopen("/dev/null", "w", stderr)==0)){
200                 LOG(L_ERR, "unable to replace stderr with /dev/null: %s\n",
201                                 strerror(errno));
202                 /* continue, leave it open */
203         };
204         
205         /* close any open file descriptors */
206         closelog();
207         for (r=3;r<MAX_FD; r++){
208                         close(r);
209         }
210         
211         if (log_stderr==0)
212                 openlog(name, LOG_PID|LOG_CONS, log_facility);
213                 /* LOG_CONS, LOG_PERRROR ? */
214
215         return  0;
216
217 error:
218         return -1;
219 }
220
221
222
223 int do_suid()
224 {
225         if (gid){
226                 if(setgid(gid)<0){
227                         LOG(L_CRIT, "cannot change gid to %d: %s\n", gid, strerror(errno));
228                         goto error;
229                 }
230         }
231         
232         if(uid){
233                 if(setuid(uid)<0){
234                         LOG(L_CRIT, "cannot change uid to %d: %s\n", uid, strerror(errno));
235                         goto error;
236                 }
237         }
238         return 0;
239 error:
240         return -1;
241 }
242
243
244
245 /* try to increase the open file limit */
246 int increase_open_fds(int target)
247 {
248         struct rlimit lim;
249         struct rlimit orig;
250         
251         if (getrlimit(RLIMIT_NOFILE, &lim)<0){
252                 LOG(L_CRIT, "cannot get the maximum number of file descriptors: %s\n",
253                                 strerror(errno));
254                 goto error;
255         }
256         orig=lim;
257         DBG("current open file limits: %lu/%lu\n",
258                         (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
259         if ((lim.rlim_cur==RLIM_INFINITY) || (target<=lim.rlim_cur))
260                 /* nothing to do */
261                 goto done;
262         else if ((lim.rlim_max==RLIM_INFINITY) || (target<=lim.rlim_max)){
263                 lim.rlim_cur=target; /* increase soft limit to target */
264         }else{
265                 /* more than the hard limit */
266                 LOG(L_INFO, "trying to increase the open file limit"
267                                 " past the hard limit (%ld -> %d)\n", 
268                                 (unsigned long)lim.rlim_max, target);
269                 lim.rlim_max=target;
270                 lim.rlim_cur=target;
271         }
272         DBG("increasing open file limits to: %lu/%lu\n",
273                         (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
274         if (setrlimit(RLIMIT_NOFILE, &lim)<0){
275                 LOG(L_CRIT, "cannot increase the open file limit to"
276                                 " %lu/%lu: %s\n",
277                                 (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max,
278                                 strerror(errno));
279                 if (orig.rlim_max>orig.rlim_cur){
280                         /* try to increase to previous maximum, better than not increasing
281                         * at all */
282                         lim.rlim_max=orig.rlim_max;
283                         lim.rlim_cur=orig.rlim_max;
284                         if (setrlimit(RLIMIT_NOFILE, &lim)==0){
285                                 LOG(L_CRIT, " maximum number of file descriptors increased to"
286                                                 " %u\n",(unsigned)orig.rlim_max);
287                         }
288                 }
289                 goto error;
290         }
291 done:
292         return 0;
293 error:
294         return -1;
295 }
296
297
298
299 /* enable core dumps */
300 int set_core_dump(int enable, int size)
301 {
302         struct rlimit lim;
303         struct rlimit newlim;
304         
305         if (enable){
306                 if (getrlimit(RLIMIT_CORE, &lim)<0){
307                         LOG(L_CRIT, "cannot get the maximum core size: %s\n",
308                                         strerror(errno));
309                         goto error;
310                 }
311                 if (lim.rlim_cur<size){
312                         /* first try max limits */
313                         newlim.rlim_max=RLIM_INFINITY;
314                         newlim.rlim_cur=newlim.rlim_max;
315                         if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
316                         /* now try with size */
317                         if (lim.rlim_max<size){
318                                 newlim.rlim_max=size;
319                         }
320                         newlim.rlim_cur=newlim.rlim_max;
321                         if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
322                         /* if this failed too, try rlim_max, better than nothing */
323                         newlim.rlim_max=lim.rlim_max;
324                         newlim.rlim_cur=newlim.rlim_max;
325                         if (setrlimit(RLIMIT_CORE, &newlim)<0){
326                                 LOG(L_CRIT, "could increase core limits at all: %s\n",
327                                                 strerror (errno));
328                         }else{
329                                 LOG(L_CRIT, "core limits increased only to %lu\n",
330                                                 (unsigned long)lim.rlim_max);
331                         }
332                         goto error; /* it's an error we haven't got the size we wanted*/
333                 }
334                 goto done; /*nothing to do */
335         }else{
336                 /* disable */
337                 newlim.rlim_cur=0;
338                 newlim.rlim_max=0;
339                 if (setrlimit(RLIMIT_CORE, &newlim)<0){
340                         LOG(L_CRIT, "failed to disable core dumps: %s\n",
341                                         strerror(errno));
342                         goto error;
343                 }
344         }
345 done:
346         DBG("core dump limits set to %lu\n", (unsigned long)newlim.rlim_cur);
347         return 0;
348 error:
349         return -1;
350 }
351
352
353
354 /* lock pages in memory (make the process not swapable) */
355 int mem_lock_pages()
356 {
357 #ifdef HAVE_MLOCKALL
358         if (mlockall(MCL_CURRENT|MCL_FUTURE) !=0){
359                 LOG(L_WARN,"failed to lock the memory pages (disable swap): %s [%d]\n",
360                                 strerror(errno), errno);
361                 goto error;
362         }
363         return 0;
364 error:
365         return -1;
366 #else /* if MLOCKALL not defined return error */
367                 LOG(L_WARN,"failed to lock the memory pages: no mlockall support\n");
368         return -1;
369 #endif 
370 }
371
372
373 /* tries to set real time priority 
374  * policy: 0 - SCHED_OTHER, 1 - SCHED_RR, 2 - SCHED_FIFO */
375 int set_rt_prio(int prio, int policy)
376 {
377 #ifdef HAVE_SCHED_SETSCHEDULER
378         struct sched_param sch_p;
379         int min_prio, max_prio;
380         int sched_policy;
381         
382         switch(policy){
383                 case 0:
384                         sched_policy=SCHED_OTHER;
385                         break;
386                 case 1:
387                         sched_policy=SCHED_RR;
388                         break;
389                 case 2:
390                         sched_policy=SCHED_FIFO;
391                         break;
392                 default:
393                         LOG(L_WARN, "WARNING: invalid scheduling policy,using"
394                                                 " SCHED_OTHER\n");
395                         sched_policy=SCHED_OTHER;
396         }
397         memset(&sch_p, 0, sizeof(sch_p));
398         max_prio=sched_get_priority_max(policy);
399         min_prio=sched_get_priority_min(policy);
400         if (prio<min_prio){
401                 LOG(L_WARN, "scheduling priority %d too small, using minimum value"
402                                         " (%d)\n", prio, min_prio);
403                 prio=min_prio;
404         }else if (prio>max_prio){
405                 LOG(L_WARN, "scheduling priority %d too big, using maximum value"
406                                         " (%d)\n", prio, max_prio);
407                 prio=max_prio;
408         }
409         sch_p.sched_priority=prio;
410         if (sched_setscheduler(0, sched_policy, &sch_p) != 0){
411                 LOG(L_WARN, "could not switch to real time priority: %s [%d]\n",
412                                         strerror(errno), errno);
413                 return -1;
414         };
415         return 0;
416 #else
417         LOG(L_WARN, "real time support not available\n");
418         return -1;
419 #endif
420 }
421
422