Coverage Report

Created: 2026-10-17 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/daemonize.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2001-2003 FhG Fokus
3
 *
4
 * This file is part of opensips, a free SIP server.
5
 *
6
 * opensips is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version
10
 *
11
 * opensips is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19
 *
20
 * History:
21
 * --------
22
 *  2004-02-20  removed from ser main.c into its own file (andrei)
23
 *  2004-03-04  moved setuid/setgid in do_suid() (andrei)
24
 *  2004-03-25  added increase_open_fds & set_core_dump (andrei)
25
 *  2004-05-03  applied pgid patch from janakj
26
 */
27
28
/*!
29
 * \file
30
 * \brief Setup the OpenSIPS daemon prozess
31
 */
32
33
34
#include <sys/types.h>
35
36
#define _XOPEN_SOURCE   /* needed on linux for the  getpgid prototype, but
37
                           openbsd 3.2 won't include common types (uint a.s.o)
38
                           if defined before including sys/types.h */
39
#define _XOPEN_SOURCE_EXTENDED /* same as above */
40
#define __USE_XOPEN_EXTENDED /* same as above, overrides features.h */
41
#define __EXTENSIONS__ /* needed on solaris: if XOPEN_SOURCE is defined
42
                          struct timeval definition from <sys/time.h> won't
43
                          be included => workarround define _EXTENSIONS_ */
44
#include <signal.h>
45
#include <syslog.h>
46
#include <errno.h>
47
#include <string.h>
48
#include <stdio.h>
49
#include <stdlib.h>
50
#include <sys/time.h>
51
#include <sys/resource.h> /* setrlimit */
52
#include <sys/syscall.h>
53
#include <unistd.h>
54
#if defined(__OS_linux) && !defined(SYS_close_range)
55
#include <dirent.h>
56
#endif
57
#ifdef __OS_linux
58
#include <sys/prctl.h>
59
#endif
60
61
#include "mem/shm_mem.h"
62
#include "daemonize.h"
63
#include "sr_module.h"
64
#include "globals.h"
65
#include "dprint.h"
66
#include "pt.h"
67
68
/* working dir at startup, before daemonizing; may be NULL if daemonizing 
69
 * was not performed. It points to a allocated buffer in system memory */
70
char *startup_wdir = NULL;
71
72
static int status_pipe[2];
73
74
/* creates the status pipe which will be used for
75
 * proper status code returning
76
 *
77
 * must be called before any forking */
78
int create_status_pipe(void)
79
0
{
80
0
  int rc;
81
82
0
  status_pipe[0] = -1;
83
0
  status_pipe[1] = -1;
84
85
0
retry:
86
0
  rc = pipe(status_pipe);
87
0
  if (rc < 0) {
88
0
    if (errno == EINTR)
89
0
      goto retry;
90
91
0
    LM_ERR("pipe() failed (%d): %d, %s\n", rc, errno, strerror(errno));
92
0
  } else {
93
0
    LM_DBG("created status pipe, fds=[%d, %d]\n",
94
0
           status_pipe[0], status_pipe[1]);
95
0
  }
96
97
0
  return rc;
98
0
}
99
100
/* attempts to send the val
101
 * status code to the waiting end */
102
int send_status_code(char val)
103
0
{
104
0
  int rc;
105
106
0
retry:
107
0
  rc = write(status_pipe[1], &val, 1);
108
0
  if (rc < 0) {
109
0
    if (errno == EINTR)
110
0
      goto retry;
111
112
0
    LM_ERR("write(%d) failed (%d): %d, %s\n", val, rc,
113
0
           errno, strerror(errno));
114
0
  } else {
115
0
    LM_DBG("sent code %d (%d byte)\n", val, rc);
116
0
  }
117
118
0
  if (rc == 1)
119
0
    return 0;
120
121
0
  return -1;
122
0
}
123
124
/* blockingly waits on the pipe
125
 * until a child sends a status code */
126
static int wait_status_code(char *code)
127
0
{
128
0
  int rc;
129
130
0
  if (status_pipe[0] == -1) {
131
0
    LM_DBG("invalid read pipe\n");
132
0
    goto error;
133
0
  }
134
135
0
retry:
136
0
  rc = read(status_pipe[0], code, 1);
137
0
  if (rc < 0) {
138
0
    if (errno == EINTR)
139
0
      goto retry;
140
141
0
    LM_ERR("read(1) failed (%d): %d, %s\n", rc, errno, strerror(errno));
142
0
  } else {
143
0
    LM_DBG("read code %d (%d byte)\n", *code, rc);
144
0
  }
145
146
0
  if (rc == 1)
147
0
    return 0;
148
149
0
error:
150
0
  *code = -1;
151
0
  return -1;
152
0
}
153
154
int wait_for_one_child(void)
155
0
{
156
0
  char rc;
157
158
0
  if (wait_status_code(&rc)<0 || rc < 0)
159
0
    return -1;
160
161
0
  return 0;
162
0
}
163
164
int wait_for_all_children(void)
165
0
{
166
0
  int procs_no,i,ret;
167
0
  char rc;
168
169
0
  clean_write_pipeend();
170
171
0
  procs_no = count_init_child_processes();
172
0
  for (i=0;i<procs_no;i++) {
173
0
    ret = wait_status_code(&rc);
174
0
    if (ret < 0 || rc < 0)
175
0
      return -1;
176
0
  }
177
178
0
  return 0;
179
0
}
180
181
/* cleans read pipe end
182
 * for processes done reading */
183
void clean_read_pipeend(void)
184
0
{
185
0
  if (status_pipe[0] != -1) {
186
0
    close(status_pipe[0]);
187
0
    status_pipe[0] = -1;
188
0
  }
189
0
}
190
191
/* cleans write pipe end
192
 * for processes done writing the status code
193
194
 * MUST be called to ensure that the original
195
 * parent process does not keep waiting forever */
196
void clean_write_pipeend(void)
197
0
{
198
0
  if (status_pipe[1] != -1) {
199
0
    close(status_pipe[1]);
200
0
    status_pipe[1] = -1;
201
0
  }
202
0
}
203
204
/*!
205
 * \brief daemon init
206
 * \param name daemon name
207
 * \param own_pgid daemon process group
208
 * \return return 0 on success, -1 on error
209
 */
210
int daemonize(char* name, int * own_pgid)
211
0
{
212
0
  FILE *pid_stream = NULL;
213
0
  pid_t pid;
214
0
  int r, p,rc;
215
0
  int pid_items;
216
217
0
  p=-1;
218
219
0
  if ( (startup_wdir=getcwd(NULL,0))==NULL) {
220
0
    LM_ERR("failed to determin the working dir %d/%s\n", errno,
221
0
      strerror(errno));
222
0
    goto error;
223
0
  }
224
225
  /* flush std file descriptors to avoid flushes after fork
226
   *  (same message appearing multiple times)
227
   *  and switch to unbuffered
228
   */
229
0
  setbuf(stdout, 0);
230
0
  setbuf(stderr, 0);
231
0
  if (chroot_dir&&(chroot(chroot_dir)<0)){
232
0
    LM_CRIT("Cannot chroot to %s: %s\n", chroot_dir, strerror(errno));
233
0
    goto error;
234
0
  }
235
236
0
  if (chdir(working_dir)<0){
237
0
    LM_CRIT("Cannot chdir to %s: %s\n", working_dir, strerror(errno));
238
0
    goto error;
239
0
  }
240
241
0
  if (!no_daemon_mode) {
242
    /* fork to become!= group leader*/
243
0
    if ((pid=fork())<0){
244
0
      LM_CRIT("Cannot fork:%s\n", strerror(errno));
245
0
      goto error;
246
0
    }else if (pid!=0){
247
      /* parent process => wait for status codes from children*/
248
0
      clean_write_pipeend();
249
0
      LM_DBG("waiting for status code from children\n");
250
0
      rc = wait_for_all_children();
251
0
      LM_INFO("pre-daemon process exiting with %d\n",rc);
252
0
      exit(rc);
253
0
    }
254
255
    /* cleanup read end - nobody should
256
     * need to read from status pipe from this point on */
257
0
    clean_read_pipeend();
258
259
    /* become session leader to drop the ctrl. terminal */
260
0
    if (setsid()<0){
261
0
      LM_WARN("setsid failed: %s\n",strerror(errno));
262
0
    }else{
263
0
      *own_pgid=1;/* we have our own process group */
264
0
    }
265
    /* fork again to drop group  leadership */
266
0
    if ((pid=fork())<0){
267
0
      LM_CRIT("Cannot fork: %s\n", strerror(errno));
268
0
      goto error;
269
0
    }else if (pid!=0){
270
      /*parent process => exit */
271
0
      exit(0);
272
0
    }
273
274
0
    is_pre_daemon = 0;  /* attendant process at this point */
275
0
  }
276
277
0
#ifdef __OS_linux
278
  /* setsid may disables core dumping on linux, reenable it */
279
0
  if ( !disable_core_dump && prctl(PR_SET_DUMPABLE, 1)) {
280
0
    LM_ERR("Cannot enable core dumping after setuid\n");
281
0
  }
282
0
#endif
283
284
  /* added by noh: create a pid file for the main process */
285
0
  if (pid_file!=0){
286
287
0
    if ((pid_stream=fopen(pid_file, "r"))!=NULL){
288
0
      pid_items=fscanf(pid_stream, "%d", &p);
289
0
      fclose(pid_stream);
290
0
      if (p==-1 || pid_items <= 0){
291
0
        LM_WARN("pid file %s exists, but doesn't contain a valid"
292
0
          " pid number, replacing...\n", pid_file);
293
0
      } else
294
0
      if (kill((pid_t)p, 0)==0 || errno==EPERM){
295
0
        LM_CRIT("running process found in the pid file %s\n",
296
0
          pid_file);
297
0
        goto error;
298
0
      }else{
299
0
        LM_WARN("pid file contains old pid, replacing pid\n");
300
0
      }
301
0
    }
302
0
    pid=getpid();
303
0
    if ((pid_stream=fopen(pid_file, "w"))==NULL){
304
0
      LM_ERR("unable to create pid file %s: %s\n",
305
0
        pid_file, strerror(errno));
306
0
      goto error;
307
0
    }else{
308
0
      r = fprintf(pid_stream, "%i\n", (int)pid);
309
0
      fclose(pid_stream);
310
0
      if (r<=0)  {
311
0
        LM_ERR("unable to write pid to file %s: %s\n",
312
0
          pid_file, strerror(errno));
313
0
        goto error;
314
0
      }
315
0
    }
316
0
  }
317
318
0
  if (pgid_file!=0){
319
0
    if ((pid_stream=fopen(pgid_file, "r"))!=NULL){
320
0
      pid_items=fscanf(pid_stream, "%d", &p);
321
0
      fclose(pid_stream);
322
0
      if (p==-1 || pid_items <= 0){
323
0
        LM_WARN("pgid file %s exists, but doesn't contain a valid"
324
0
          " pgid number, replacing...\n", pgid_file);
325
0
      }
326
0
    }
327
0
    if (own_pgid){
328
0
      pid=getpgid(0);
329
0
      if ((pid_stream=fopen(pgid_file, "w"))==NULL){
330
0
        LM_ERR("unable to create pgid file %s: %s\n",
331
0
          pgid_file, strerror(errno));
332
0
        goto error;
333
0
      }else{
334
0
        r = fprintf(pid_stream, "%i\n", (int)pid);
335
0
        fclose(pid_stream);
336
0
        if (r<=0)  {
337
0
          LM_ERR("unable to write pgid to file %s: %s\n",
338
0
            pid_file, strerror(errno));
339
0
          goto error;
340
0
        }
341
0
      }
342
0
    }else{
343
0
      LM_WARN("we don't have our own process so we won't save"
344
0
          " our pgid\n");
345
0
      unlink(pgid_file); /* just to be sure nobody will miss-use the old
346
                  value*/
347
0
    }
348
0
  }
349
350
  /* try to replace stdin, stdout & stderr with /dev/null */
351
0
  if (freopen("/dev/null", "r", stdin)==0){
352
0
    LM_WARN("unable to replace stdin with /dev/null: %s\n",
353
0
      strerror(errno));
354
    /* continue, leave it open */
355
0
  };
356
0
  if (!log_stdout && freopen("/dev/null", "w", stdout)==0){
357
0
    LM_WARN("unable to replace stdout with /dev/null: %s\n",
358
0
      strerror(errno));
359
    /* continue, leave it open */
360
0
  };
361
362
  /* close any open file descriptors */
363
0
  closelog();
364
0
  close_open_fds_except(status_pipe[1]);
365
366
0
  if (syslog_enabled)
367
0
    openlog(name, LOG_PID|LOG_CONS, log_facility);
368
    /* LOG_CONS, LOG_PERRROR ? */
369
370
0
  return  0;
371
372
0
error:
373
0
  return -1;
374
0
}
375
376
377
/*!
378
 * \brief set daemon user and group id
379
 * \param uid user id
380
 * \param gid group id
381
 * \return return 0 on success, -1 on error
382
 */
383
int do_suid(const int uid, const int gid)
384
0
{
385
  /* if running in debug mode, do not do anything about the PID file
386
   * as they are not created (daemonize() is not used in debug mode) */
387
0
  if (!debug_mode) {
388
0
    if (pid_file) {
389
      /* pid file should be already created by deamonize function
390
         -> change the owner and group also
391
      */
392
0
      if (chown( pid_file , uid?uid:-1, gid?gid:-1)!=0) {
393
0
        LM_ERR("failed to change owner of pid file %s: %s(%d)\n",
394
0
          pid_file, strerror(errno), errno);
395
0
        goto error;
396
0
      }
397
0
    }
398
0
    if (pgid_file) {
399
      /* pgid file should be already created by deamonize function
400
         -> change the owner and group also
401
      */
402
0
      if (chown( pgid_file , uid?uid:-1, gid?gid:-1)!=0) {
403
0
        LM_ERR("failed to change owner of pid file %s: %s(%d)\n",
404
0
          pgid_file, strerror(errno), errno);
405
0
        goto error;
406
0
      }
407
0
    }
408
0
  }
409
410
0
  if (gid){
411
0
    if(setgid(gid)<0){
412
0
      LM_CRIT("cannot change gid to %d: %s\n", gid, strerror(errno));
413
0
      goto error;
414
0
    }
415
0
  }
416
417
0
  if(uid){
418
0
    if(setuid(uid)<0){
419
0
      LM_CRIT("cannot change uid to %d: %s\n", uid, strerror(errno));
420
0
      goto error;
421
0
    }
422
0
  }
423
424
0
#ifdef __OS_linux
425
  /* setuid disables core dumping on linux, reenable it */
426
0
  if ( !disable_core_dump && prctl(PR_SET_DUMPABLE, 1)) {
427
0
    LM_ERR("Cannot enable core dumping after setuid\n");
428
0
  }
429
0
#endif
430
431
0
  return 0;
432
0
error:
433
0
  return -1;
434
0
}
435
436
437
438
/*!
439
 * \brief try to increase the open file limit to the value given by the global
440
 *        option "open_files_limit" ; the value is updated back in case of a
441
 *        partial increase of the limit
442
 * \return return 0 on success, -1 on error
443
 */
444
int set_open_fds_limit(void)
445
0
{
446
0
  struct rlimit lim, orig;
447
448
0
  if (getrlimit(RLIMIT_NOFILE, &lim)<0){
449
0
    LM_CRIT("cannot get the maximum number of file descriptors: %s\n",
450
0
        strerror(errno));
451
0
    goto error;
452
0
  }
453
0
  orig=lim;
454
0
  LM_DBG("current open file limits: %lu/%lu\n",
455
0
      (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
456
0
  if ((lim.rlim_cur==RLIM_INFINITY) || (open_files_limit<=lim.rlim_cur))
457
    /* nothing to do (we do no reduce the limit) */
458
0
    goto done;
459
0
  if ((lim.rlim_max==RLIM_INFINITY) || (open_files_limit<=lim.rlim_max)) {
460
0
    lim.rlim_cur=open_files_limit; /* increase soft limit to target */
461
0
  } else {
462
    /* more than the hard limit */
463
0
    LM_INFO("trying to increase the open file limit"
464
0
        " past the hard limit (%ld -> %d)\n",
465
0
        (unsigned long)lim.rlim_max, open_files_limit);
466
0
    lim.rlim_max=open_files_limit;
467
0
    lim.rlim_cur=open_files_limit;
468
0
  }
469
0
  LM_DBG("increasing open file limits to: %lu/%lu\n",
470
0
      (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
471
0
  if (setrlimit(RLIMIT_NOFILE, &lim)<0){
472
0
    LM_CRIT("cannot increase the open file limit to"
473
0
        " %lu/%lu: %s\n",
474
0
        (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max,
475
0
        strerror(errno));
476
0
    if (orig.rlim_max>orig.rlim_cur){
477
      /* try to increase to previous maximum, better than not increasing
478
      * at all */
479
0
      lim.rlim_max=orig.rlim_max;
480
0
      lim.rlim_cur=orig.rlim_max;
481
0
      if (setrlimit(RLIMIT_NOFILE, &lim)==0){
482
0
        LM_CRIT("maximum number of file descriptors increased to"
483
0
          " %u\n",(unsigned)orig.rlim_max);
484
0
        open_files_limit = orig.rlim_max;
485
0
        goto done;
486
0
      }
487
0
    }
488
0
    goto error;
489
0
  }
490
0
done:
491
0
  LM_DBG("open files limit set to %d\n",open_files_limit);
492
0
  return 0;
493
0
error:
494
0
  return -1;
495
0
}
496
497
498
499
/*!
500
 * \brief enable or disable core dumps
501
 * \param enable set to 1 to enable, to 0 to disable
502
 * \param size core dump size
503
 * \return return 0 on success, -1 on error
504
 */
505
int set_core_dump(int enable, unsigned int size)
506
0
{
507
0
  struct rlimit lim, newlim;
508
509
0
  if (enable){
510
0
    if (getrlimit(RLIMIT_CORE, &lim)<0){
511
0
      LM_CRIT("cannot get the maximum core size: %s\n",
512
0
          strerror(errno));
513
0
      goto error;
514
0
    }
515
0
    if (lim.rlim_cur<size){
516
      /* first try max limits */
517
0
      newlim.rlim_max=RLIM_INFINITY;
518
0
      newlim.rlim_cur=newlim.rlim_max;
519
0
      if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
520
      /* now try with size */
521
0
      if (lim.rlim_max<size){
522
0
        newlim.rlim_max=size;
523
0
      }
524
0
      newlim.rlim_cur=newlim.rlim_max;
525
0
      if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
526
      /* if this failed too, try rlim_max, better than nothing */
527
0
      newlim.rlim_max=lim.rlim_max;
528
0
      newlim.rlim_cur=newlim.rlim_max;
529
0
      if (setrlimit(RLIMIT_CORE, &newlim)<0){
530
0
        LM_CRIT("could increase core limits at all: %s\n",
531
0
          strerror (errno));
532
0
      }else{
533
0
        LM_CRIT("core limits increased only to %lu\n",
534
0
          (unsigned long)lim.rlim_max);
535
0
      }
536
0
      goto error; /* it's an error we haven't got the size we wanted*/
537
0
    } else {
538
      /* using the same limit as before - disable uninitialized warning */
539
0
      newlim.rlim_cur = lim.rlim_cur;
540
0
    }
541
0
    goto done; /*nothing to do */
542
0
  }else{
543
    /* disable */
544
0
    newlim.rlim_cur=0;
545
0
    newlim.rlim_max=0;
546
0
    if (setrlimit(RLIMIT_CORE, &newlim)<0){
547
0
      LM_CRIT("failed to disable core dumps: %s\n",
548
0
        strerror(errno));
549
0
      goto error;
550
0
    }
551
0
  }
552
0
done:
553
0
  LM_DBG("core dump limits set to %lu\n", (unsigned long)newlim.rlim_cur);
554
0
  return 0;
555
0
error:
556
0
  return -1;
557
0
}
558
559
560
int get_open_fds_limit(void)
561
0
{
562
0
  struct rlimit rl;
563
0
  int open_max;
564
0
  if (open_files_limit > 0)
565
0
    return open_files_limit;
566
0
  if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
567
0
    open_max = sysconf(_SC_OPEN_MAX);
568
0
    return (open_max < 0?_POSIX_OPEN_MAX:open_max);
569
0
  }
570
0
  return rl.rlim_cur;
571
0
}
572
573
void close_open_fds(void)
574
0
{
575
0
  close_open_fds_except(-1);
576
0
}
577
578
void close_open_fds_except(int except)
579
0
{
580
#ifdef SYS_close_range
581
  if (except < 3) {
582
    syscall(SYS_close_range, 3U, ~0U, 0);
583
    return;
584
  }
585
586
  if (except > 3)
587
    syscall(SYS_close_range, 3U, (unsigned int)except - 1U, 0);
588
  syscall(SYS_close_range, (unsigned int)except + 1U, ~0U, 0);
589
#else
590
0
#ifdef __OS_linux
591
0
  DIR *dir;
592
0
  struct dirent *ent;
593
0
  char *end;
594
0
  long dfd;
595
0
  int dir_fd;
596
597
0
  dir = opendir("/proc/self/fd");
598
0
  if (!dir)
599
0
    return;
600
601
0
  dir_fd = dirfd(dir);
602
0
  while ((ent = readdir(dir)) != NULL) {
603
0
    errno = 0;
604
0
    dfd = strtol(ent->d_name, &end, 10);
605
0
    if (errno || end == ent->d_name || *end != '\0' ||
606
0
        dfd < 3 || dfd == except || dfd == dir_fd)
607
0
      continue;
608
0
    syscall(SYS_close, dfd);
609
0
  }
610
611
0
  closedir(dir);
612
#else
613
  int fd, open_max;
614
  open_max = get_open_fds_limit();
615
  for (fd = 3; fd < open_max; fd++)
616
    if (fd != except)
617
      syscall(SYS_close, fd);
618
#endif /* __OS_linux */
619
0
#endif /* SYS_close_range */
620
0
}