Coverage Report

Created: 2025-10-10 06:07

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
#ifdef __OS_linux
55
#include <sys/prctl.h>
56
#endif
57
58
#include "mem/shm_mem.h"
59
#include "daemonize.h"
60
#include "sr_module.h"
61
#include "globals.h"
62
#include "dprint.h"
63
#include "pt.h"
64
65
/* working dir at startup, before daemonizing; may be NULL if daemonizing 
66
 * was not performed. It points to a allocated buffer in system memory */
67
char *startup_wdir = NULL;
68
69
static int status_pipe[2];
70
71
/* creates the status pipe which will be used for
72
 * proper status code returning
73
 *
74
 * must be called before any forking */
75
int create_status_pipe(void)
76
0
{
77
0
  int rc;
78
79
0
  status_pipe[0] = -1;
80
0
  status_pipe[1] = -1;
81
82
0
retry:
83
0
  rc = pipe(status_pipe);
84
0
  if (rc < 0) {
85
0
    if (errno == EINTR)
86
0
      goto retry;
87
88
0
    LM_ERR("pipe() failed (%d): %d, %s\n", rc, errno, strerror(errno));
89
0
  } else {
90
0
    LM_DBG("created status pipe, fds=[%d, %d]\n",
91
0
           status_pipe[0], status_pipe[1]);
92
0
  }
93
94
0
  return rc;
95
0
}
96
97
/* attempts to send the val
98
 * status code to the waiting end */
99
int send_status_code(char val)
100
0
{
101
0
  int rc;
102
103
0
retry:
104
0
  rc = write(status_pipe[1], &val, 1);
105
0
  if (rc < 0) {
106
0
    if (errno == EINTR)
107
0
      goto retry;
108
109
0
    LM_ERR("write(%d) failed (%d): %d, %s\n", val, rc,
110
0
           errno, strerror(errno));
111
0
  } else {
112
0
    LM_DBG("sent code %d (%d byte)\n", val, rc);
113
0
  }
114
115
0
  if (rc == 1)
116
0
    return 0;
117
118
0
  return -1;
119
0
}
120
121
/* blockingly waits on the pipe
122
 * until a child sends a status code */
123
static int wait_status_code(char *code)
124
0
{
125
0
  int rc;
126
127
0
  if (status_pipe[0] == -1) {
128
0
    LM_DBG("invalid read pipe\n");
129
0
    goto error;
130
0
  }
131
132
0
retry:
133
0
  rc = read(status_pipe[0], code, 1);
134
0
  if (rc < 0) {
135
0
    if (errno == EINTR)
136
0
      goto retry;
137
138
0
    LM_ERR("read(1) failed (%d): %d, %s\n", rc, errno, strerror(errno));
139
0
  } else {
140
0
    LM_DBG("read code %d (%d byte)\n", *code, rc);
141
0
  }
142
143
0
  if (rc == 1)
144
0
    return 0;
145
146
0
error:
147
0
  *code = -1;
148
0
  return -1;
149
0
}
150
151
int wait_for_one_child(void)
152
0
{
153
0
  char rc;
154
155
0
  if (wait_status_code(&rc)<0 || rc < 0)
156
0
    return -1;
157
158
0
  return 0;
159
0
}
160
161
int wait_for_all_children(void)
162
0
{
163
0
  int procs_no,i,ret;
164
0
  char rc;
165
166
0
  clean_write_pipeend();
167
168
0
  procs_no = count_init_child_processes();
169
0
  for (i=0;i<procs_no;i++) {
170
0
    ret = wait_status_code(&rc);
171
0
    if (ret < 0 || rc < 0)
172
0
      return -1;
173
0
  }
174
175
0
  return 0;
176
0
}
177
178
/* cleans read pipe end
179
 * for processes done reading */
180
void clean_read_pipeend(void)
181
0
{
182
0
  if (status_pipe[0] != -1) {
183
0
    close(status_pipe[0]);
184
0
    status_pipe[0] = -1;
185
0
  }
186
0
}
187
188
/* cleans write pipe end
189
 * for processes done writing the status code
190
191
 * MUST be called to ensure that the original
192
 * parent process does not keep waiting forever */
193
void clean_write_pipeend(void)
194
0
{
195
0
  if (status_pipe[1] != -1) {
196
0
    close(status_pipe[1]);
197
0
    status_pipe[1] = -1;
198
0
  }
199
0
}
200
201
/*!
202
 * \brief daemon init
203
 * \param name daemon name
204
 * \param own_pgid daemon process group
205
 * \return return 0 on success, -1 on error
206
 */
207
int daemonize(char* name, int * own_pgid)
208
0
{
209
0
  FILE *pid_stream = NULL;
210
0
  pid_t pid;
211
0
  int r, p,rc;
212
0
  int pid_items;
213
214
0
  p=-1;
215
216
0
  if ( (startup_wdir=getcwd(NULL,0))==NULL) {
217
0
    LM_ERR("failed to determin the working dir %d/%s\n", errno,
218
0
      strerror(errno));
219
0
    goto error;
220
0
  }
221
222
  /* flush std file descriptors to avoid flushes after fork
223
   *  (same message appearing multiple times)
224
   *  and switch to unbuffered
225
   */
226
0
  setbuf(stdout, 0);
227
0
  setbuf(stderr, 0);
228
0
  if (chroot_dir&&(chroot(chroot_dir)<0)){
229
0
    LM_CRIT("Cannot chroot to %s: %s\n", chroot_dir, strerror(errno));
230
0
    goto error;
231
0
  }
232
233
0
  if (chdir(working_dir)<0){
234
0
    LM_CRIT("Cannot chdir to %s: %s\n", working_dir, strerror(errno));
235
0
    goto error;
236
0
  }
237
238
0
  if (!no_daemon_mode) {
239
    /* fork to become!= group leader*/
240
0
    if ((pid=fork())<0){
241
0
      LM_CRIT("Cannot fork:%s\n", strerror(errno));
242
0
      goto error;
243
0
    }else if (pid!=0){
244
      /* parent process => wait for status codes from children*/
245
0
      clean_write_pipeend();
246
0
      LM_DBG("waiting for status code from children\n");
247
0
      rc = wait_for_all_children();
248
0
      LM_INFO("pre-daemon process exiting with %d\n",rc);
249
0
      exit(rc);
250
0
    }
251
252
    /* cleanup read end - nobody should
253
     * need to read from status pipe from this point on */
254
0
    clean_read_pipeend();
255
256
    /* become session leader to drop the ctrl. terminal */
257
0
    if (setsid()<0){
258
0
      LM_WARN("setsid failed: %s\n",strerror(errno));
259
0
    }else{
260
0
      *own_pgid=1;/* we have our own process group */
261
0
    }
262
    /* fork again to drop group  leadership */
263
0
    if ((pid=fork())<0){
264
0
      LM_CRIT("Cannot fork: %s\n", strerror(errno));
265
0
      goto error;
266
0
    }else if (pid!=0){
267
      /*parent process => exit */
268
0
      exit(0);
269
0
    }
270
271
0
    is_pre_daemon = 0;  /* attendant process at this point */
272
0
  }
273
274
0
#ifdef __OS_linux
275
  /* setsid may disables core dumping on linux, reenable it */
276
0
  if ( !disable_core_dump && prctl(PR_SET_DUMPABLE, 1)) {
277
0
    LM_ERR("Cannot enable core dumping after setuid\n");
278
0
  }
279
0
#endif
280
281
  /* added by noh: create a pid file for the main process */
282
0
  if (pid_file!=0){
283
284
0
    if ((pid_stream=fopen(pid_file, "r"))!=NULL){
285
0
      pid_items=fscanf(pid_stream, "%d", &p);
286
0
      fclose(pid_stream);
287
0
      if (p==-1 || pid_items <= 0){
288
0
        LM_WARN("pid file %s exists, but doesn't contain a valid"
289
0
          " pid number, replacing...\n", pid_file);
290
0
      } else
291
0
      if (kill((pid_t)p, 0)==0 || errno==EPERM){
292
0
        LM_CRIT("running process found in the pid file %s\n",
293
0
          pid_file);
294
0
        goto error;
295
0
      }else{
296
0
        LM_WARN("pid file contains old pid, replacing pid\n");
297
0
      }
298
0
    }
299
0
    pid=getpid();
300
0
    if ((pid_stream=fopen(pid_file, "w"))==NULL){
301
0
      LM_ERR("unable to create pid file %s: %s\n",
302
0
        pid_file, strerror(errno));
303
0
      goto error;
304
0
    }else{
305
0
      r = fprintf(pid_stream, "%i\n", (int)pid);
306
0
      fclose(pid_stream);
307
0
      if (r<=0)  {
308
0
        LM_ERR("unable to write pid to file %s: %s\n",
309
0
          pid_file, strerror(errno));
310
0
        goto error;
311
0
      }
312
0
    }
313
0
  }
314
315
0
  if (pgid_file!=0){
316
0
    if ((pid_stream=fopen(pgid_file, "r"))!=NULL){
317
0
      pid_items=fscanf(pid_stream, "%d", &p);
318
0
      fclose(pid_stream);
319
0
      if (p==-1 || pid_items <= 0){
320
0
        LM_WARN("pgid file %s exists, but doesn't contain a valid"
321
0
          " pgid number, replacing...\n", pgid_file);
322
0
      }
323
0
    }
324
0
    if (own_pgid){
325
0
      pid=getpgid(0);
326
0
      if ((pid_stream=fopen(pgid_file, "w"))==NULL){
327
0
        LM_ERR("unable to create pgid file %s: %s\n",
328
0
          pgid_file, strerror(errno));
329
0
        goto error;
330
0
      }else{
331
0
        r = fprintf(pid_stream, "%i\n", (int)pid);
332
0
        fclose(pid_stream);
333
0
        if (r<=0)  {
334
0
          LM_ERR("unable to write pgid to file %s: %s\n",
335
0
            pid_file, strerror(errno));
336
0
          goto error;
337
0
        }
338
0
      }
339
0
    }else{
340
0
      LM_WARN("we don't have our own process so we won't save"
341
0
          " our pgid\n");
342
0
      unlink(pgid_file); /* just to be sure nobody will miss-use the old
343
                  value*/
344
0
    }
345
0
  }
346
347
  /* try to replace stdin, stdout & stderr with /dev/null */
348
0
  if (freopen("/dev/null", "r", stdin)==0){
349
0
    LM_WARN("unable to replace stdin with /dev/null: %s\n",
350
0
      strerror(errno));
351
    /* continue, leave it open */
352
0
  };
353
0
  if (!log_stdout && freopen("/dev/null", "w", stdout)==0){
354
0
    LM_WARN("unable to replace stdout with /dev/null: %s\n",
355
0
      strerror(errno));
356
    /* continue, leave it open */
357
0
  };
358
359
  /* close any open file descriptors */
360
0
  closelog();
361
0
  close_open_fds_except(status_pipe[1]);
362
363
0
  if (syslog_enabled)
364
0
    openlog(name, LOG_PID|LOG_CONS, log_facility);
365
    /* LOG_CONS, LOG_PERRROR ? */
366
367
0
  return  0;
368
369
0
error:
370
0
  return -1;
371
0
}
372
373
374
/*!
375
 * \brief set daemon user and group id
376
 * \param uid user id
377
 * \param gid group id
378
 * \return return 0 on success, -1 on error
379
 */
380
int do_suid(const int uid, const int gid)
381
0
{
382
  /* if running in debug mode, do not do anything about the PID file
383
   * as they are not created (daemonize() is not used in debug mode) */
384
0
  if (!debug_mode) {
385
0
    if (pid_file) {
386
      /* pid file should be already created by deamonize function
387
         -> change the owner and group also
388
      */
389
0
      if (chown( pid_file , uid?uid:-1, gid?gid:-1)!=0) {
390
0
        LM_ERR("failed to change owner of pid file %s: %s(%d)\n",
391
0
          pid_file, strerror(errno), errno);
392
0
        goto error;
393
0
      }
394
0
    }
395
0
    if (pgid_file) {
396
      /* pgid file should be already created by deamonize function
397
         -> change the owner and group also
398
      */
399
0
      if (chown( pgid_file , uid?uid:-1, gid?gid:-1)!=0) {
400
0
        LM_ERR("failed to change owner of pid file %s: %s(%d)\n",
401
0
          pgid_file, strerror(errno), errno);
402
0
        goto error;
403
0
      }
404
0
    }
405
0
  }
406
407
0
  if (gid){
408
0
    if(setgid(gid)<0){
409
0
      LM_CRIT("cannot change gid to %d: %s\n", gid, strerror(errno));
410
0
      goto error;
411
0
    }
412
0
  }
413
414
0
  if(uid){
415
0
    if(setuid(uid)<0){
416
0
      LM_CRIT("cannot change uid to %d: %s\n", uid, strerror(errno));
417
0
      goto error;
418
0
    }
419
0
  }
420
421
0
#ifdef __OS_linux
422
  /* setuid disables core dumping on linux, reenable it */
423
0
  if ( !disable_core_dump && prctl(PR_SET_DUMPABLE, 1)) {
424
0
    LM_ERR("Cannot enable core dumping after setuid\n");
425
0
  }
426
0
#endif
427
428
0
  return 0;
429
0
error:
430
0
  return -1;
431
0
}
432
433
434
435
/*!
436
 * \brief try to increase the open file limit to the value given by the global
437
 *        option "open_files_limit" ; the value is updated back in case of a
438
 *        partial increase of the limit
439
 * \return return 0 on success, -1 on error
440
 */
441
int set_open_fds_limit(void)
442
0
{
443
0
  struct rlimit lim, orig;
444
445
0
  if (getrlimit(RLIMIT_NOFILE, &lim)<0){
446
0
    LM_CRIT("cannot get the maximum number of file descriptors: %s\n",
447
0
        strerror(errno));
448
0
    goto error;
449
0
  }
450
0
  orig=lim;
451
0
  LM_DBG("current open file limits: %lu/%lu\n",
452
0
      (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
453
0
  if ((lim.rlim_cur==RLIM_INFINITY) || (open_files_limit<=lim.rlim_cur))
454
    /* nothing to do (we do no reduce the limit) */
455
0
    goto done;
456
0
  if ((lim.rlim_max==RLIM_INFINITY) || (open_files_limit<=lim.rlim_max)) {
457
0
    lim.rlim_cur=open_files_limit; /* increase soft limit to target */
458
0
  } else {
459
    /* more than the hard limit */
460
0
    LM_INFO("trying to increase the open file limit"
461
0
        " past the hard limit (%ld -> %d)\n",
462
0
        (unsigned long)lim.rlim_max, open_files_limit);
463
0
    lim.rlim_max=open_files_limit;
464
0
    lim.rlim_cur=open_files_limit;
465
0
  }
466
0
  LM_DBG("increasing open file limits to: %lu/%lu\n",
467
0
      (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
468
0
  if (setrlimit(RLIMIT_NOFILE, &lim)<0){
469
0
    LM_CRIT("cannot increase the open file limit to"
470
0
        " %lu/%lu: %s\n",
471
0
        (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max,
472
0
        strerror(errno));
473
0
    if (orig.rlim_max>orig.rlim_cur){
474
      /* try to increase to previous maximum, better than not increasing
475
      * at all */
476
0
      lim.rlim_max=orig.rlim_max;
477
0
      lim.rlim_cur=orig.rlim_max;
478
0
      if (setrlimit(RLIMIT_NOFILE, &lim)==0){
479
0
        LM_CRIT("maximum number of file descriptors increased to"
480
0
          " %u\n",(unsigned)orig.rlim_max);
481
0
        open_files_limit = orig.rlim_max;
482
0
        goto done;
483
0
      }
484
0
    }
485
0
    goto error;
486
0
  }
487
0
done:
488
0
  LM_DBG("open files limit set to %d\n",open_files_limit);
489
0
  return 0;
490
0
error:
491
0
  return -1;
492
0
}
493
494
495
496
/*!
497
 * \brief enable or disable core dumps
498
 * \param enable set to 1 to enable, to 0 to disable
499
 * \param size core dump size
500
 * \return return 0 on success, -1 on error
501
 */
502
int set_core_dump(int enable, unsigned int size)
503
0
{
504
0
  struct rlimit lim, newlim;
505
506
0
  if (enable){
507
0
    if (getrlimit(RLIMIT_CORE, &lim)<0){
508
0
      LM_CRIT("cannot get the maximum core size: %s\n",
509
0
          strerror(errno));
510
0
      goto error;
511
0
    }
512
0
    if (lim.rlim_cur<size){
513
      /* first try max limits */
514
0
      newlim.rlim_max=RLIM_INFINITY;
515
0
      newlim.rlim_cur=newlim.rlim_max;
516
0
      if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
517
      /* now try with size */
518
0
      if (lim.rlim_max<size){
519
0
        newlim.rlim_max=size;
520
0
      }
521
0
      newlim.rlim_cur=newlim.rlim_max;
522
0
      if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
523
      /* if this failed too, try rlim_max, better than nothing */
524
0
      newlim.rlim_max=lim.rlim_max;
525
0
      newlim.rlim_cur=newlim.rlim_max;
526
0
      if (setrlimit(RLIMIT_CORE, &newlim)<0){
527
0
        LM_CRIT("could increase core limits at all: %s\n",
528
0
          strerror (errno));
529
0
      }else{
530
0
        LM_CRIT("core limits increased only to %lu\n",
531
0
          (unsigned long)lim.rlim_max);
532
0
      }
533
0
      goto error; /* it's an error we haven't got the size we wanted*/
534
0
    } else {
535
      /* using the same limit as before - disable uninitialized warning */
536
0
      newlim.rlim_cur = lim.rlim_cur;
537
0
    }
538
0
    goto done; /*nothing to do */
539
0
  }else{
540
    /* disable */
541
0
    newlim.rlim_cur=0;
542
0
    newlim.rlim_max=0;
543
0
    if (setrlimit(RLIMIT_CORE, &newlim)<0){
544
0
      LM_CRIT("failed to disable core dumps: %s\n",
545
0
        strerror(errno));
546
0
      goto error;
547
0
    }
548
0
  }
549
0
done:
550
0
  LM_DBG("core dump limits set to %lu\n", (unsigned long)newlim.rlim_cur);
551
0
  return 0;
552
0
error:
553
0
  return -1;
554
0
}
555
556
557
int get_open_fds_limit(void)
558
0
{
559
0
  struct rlimit rl;
560
0
  int open_max;
561
0
  if (open_files_limit > 0)
562
0
    return open_files_limit;
563
0
  if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
564
0
    open_max = sysconf(_SC_OPEN_MAX);
565
0
    return (open_max < 0?_POSIX_OPEN_MAX:open_max);
566
0
  }
567
0
  return rl.rlim_cur;
568
0
}
569
570
void close_open_fds(void)
571
0
{
572
0
  int fd, open_max = get_open_fds_limit();
573
0
  for (fd = 3; fd < open_max; fd++)
574
0
    syscall(SYS_close, fd); /* does not set errno */
575
0
}
576
577
void close_open_fds_except(int except)
578
0
{
579
0
  int fd, open_max = get_open_fds_limit();
580
0
  for (fd = 3; fd < open_max; fd++)
581
0
    if (fd != except)
582
0
      syscall(SYS_close, fd);
583
0
}