daemonize.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (C) 2001-2003 FhG Fokus
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. /*
  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. * 2007-06-07 added mlock_pages (no swap) support (andrei)
  27. * added set_rt_prio() (andrei)
  28. */
  29. /*!
  30. * \file
  31. * \brief SIP-router core ::
  32. * \ingroup core
  33. * Module: \ref core
  34. */
  35. #include <sys/types.h>
  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 \ref _XOPEN_SOURCE */
  40. #define __USE_XOPEN_EXTENDED /*!< same as \ref _XOPEN_SOURCE, overrides features.h */
  41. #define __EXTENSIONS__ /*!< needed on solaris: if XOPEN_SOURCE is defined
  42. struct timeval defintion from <sys/time.h> won't
  43. be included => workarround define _EXTENSIONS_
  44. -andrei */
  45. #include <signal.h>
  46. #include <syslog.h>
  47. #include <errno.h>
  48. #include <string.h>
  49. #include <stdio.h>
  50. #include <stdlib.h>
  51. #include <sys/time.h>
  52. #include <sys/resource.h> /* setrlimit */
  53. #include <unistd.h>
  54. #include <pwd.h>
  55. #include <grp.h>
  56. #ifdef __OS_linux
  57. #include <sys/prctl.h>
  58. #endif
  59. #ifdef HAVE_SCHED_SETSCHEDULER
  60. #include <sched.h>
  61. #endif
  62. #ifdef _POSIX_MEMLOCK
  63. #define HAVE_MLOCKALL
  64. #include <sys/mman.h>
  65. #endif
  66. #include "daemonize.h"
  67. #include "globals.h"
  68. #include "dprint.h"
  69. #include "signals.h"
  70. #include "cfg/cfg.h"
  71. #define MAX_FD 32 /* maximum number of inherited open file descriptors,
  72. (normally it shouldn't be bigger than 3) */
  73. /** temporary pipe FDs for sending exit status back to the ancestor process.
  74. * This pipe is used to send the desired exit status to the initial process,
  75. * that waits for it in the foreground. This way late errors preventing
  76. * startup (e.g. during modules child inits or TCP late init) can still be
  77. * reported back.
  78. */
  79. static int daemon_status_fd[2];
  80. /** init daemon status reporting.
  81. * Must be called before any other daemon_status function has a chance to
  82. * run.
  83. */
  84. void daemon_status_init()
  85. {
  86. daemon_status_fd[0] = -1;
  87. daemon_status_fd[1] = -1;
  88. }
  89. /** pre-daemonize init for daemon status reporting.
  90. * Must be called before forking.
  91. * Typically the parent process will call daemon_status_wait() while
  92. * one of the children will call daemon_status_send() at some point.
  93. *
  94. * @return 0 on success, -1 on error (and sets errno).
  95. */
  96. int daemon_status_pre_daemonize(void)
  97. {
  98. int ret;
  99. retry:
  100. ret = pipe(daemon_status_fd);
  101. if (ret < 0 && errno == EINTR)
  102. goto retry;
  103. return ret;
  104. }
  105. /** wait for an exit status to be send by daemon_status_send().
  106. * @param status - filled with the sent status (a char).
  107. * @return 0 on success, -1 on error (e.g. process died before sending
  108. * status, not intialized a.s.o.).
  109. * Side-effects: it will close the write side of the pipe
  110. * (must not be used from the same process as the daemon_status_send()).
  111. * Note: if init is not complete (only init, but no pre-daemonize)
  112. * it will return success always and status 0.
  113. */
  114. int daemon_status_wait(char* status)
  115. {
  116. int ret;
  117. /* close the output side of the pipe */
  118. if (daemon_status_fd[1] != -1) {
  119. close(daemon_status_fd[1]);
  120. daemon_status_fd[1] = -1;
  121. }
  122. if (daemon_status_fd[0] == -1) {
  123. *status = 0;
  124. return -1;
  125. }
  126. retry:
  127. ret = read(daemon_status_fd[0], status, 1);
  128. if (ret < 0 && errno == EINTR)
  129. goto retry;
  130. return (ret ==1 ) ? 0 : -1;
  131. }
  132. /** send 'status' to a waiting process running daemon_status_wait().
  133. * @param status - status byte
  134. * @return 0 on success, -1 on error.
  135. * Note: if init is not complete (only init, but no pre-daemonize)
  136. * it will return success always.
  137. */
  138. int daemon_status_send(char status)
  139. {
  140. int ret;
  141. if (daemon_status_fd[1] == -1)
  142. return 0;
  143. retry:
  144. ret = write(daemon_status_fd[1], &status, 1);
  145. if (ret < 0 && errno == EINTR)
  146. goto retry;
  147. return (ret ==1 ) ? 0 : -1;
  148. }
  149. /** cleanup functions for new processes.
  150. * Should be called after fork(), for each new process that _does_ _not_
  151. * use daemon_status_send() or daemon_status_wait().
  152. */
  153. void daemon_status_on_fork_cleanup()
  154. {
  155. if (daemon_status_fd[0] != -1) {
  156. close(daemon_status_fd[0]);
  157. daemon_status_fd[0] = -1;
  158. }
  159. if (daemon_status_fd[1] != -1) {
  160. close(daemon_status_fd[1]);
  161. daemon_status_fd[1] = -1;
  162. }
  163. }
  164. /** cleanup functions for processes that don't intead to wait.
  165. * Should be called after fork(), for each new process that doesn't
  166. * use daemon_status_wait().
  167. */
  168. void daemon_status_no_wait()
  169. {
  170. if (daemon_status_fd[0] != -1) {
  171. close(daemon_status_fd[0]);
  172. daemon_status_fd[0] = -1;
  173. }
  174. }
  175. /**
  176. * enable dumpable flag for core dumping after setuid() & friends
  177. * @return 0 when no critical error occured, -1 on such error
  178. */
  179. int enable_dumpable(void)
  180. {
  181. #ifdef __OS_linux
  182. struct rlimit lim;
  183. /* re-enable core dumping on linux after setuid() & friends */
  184. if(disable_core_dump==0) {
  185. LM_DBG("trying enable core dumping...\n");
  186. if(prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)<=0) {
  187. LM_DBG("core dumping is disabled now...\n");
  188. if(prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)<0) {
  189. LM_WARN("cannot re-enable core dumping!\n");
  190. } else {
  191. LM_DBG("core dumping has just been enabled...\n");
  192. if (getrlimit(RLIMIT_CORE, &lim)<0){
  193. LM_CRIT( "cannot get the maximum core size: %s\n",
  194. strerror(errno));
  195. return -1;
  196. } else {
  197. LM_DBG("current core file limit: %lu (max: %lu)\n",
  198. (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
  199. }
  200. }
  201. } else {
  202. LM_DBG("core dumping is enabled now (%d)...\n",
  203. prctl(PR_GET_DUMPABLE, 0, 0, 0, 0));
  204. }
  205. }
  206. #endif
  207. return 0;
  208. }
  209. /** daemon init.
  210. *@param name - daemon name used for logging (used when opening syslog).
  211. *@param status_wait - if 1 the original process will wait until it gets
  212. * an exit code send using daemon_status_send().
  213. *@return 0 in the child process (in case of daemonize mode),
  214. * -1 on error.
  215. * The original process that called daemonize() will be terminated if
  216. * dont_daemonize == 0. The exit code depends on status_wait. If status_wait
  217. * is non-zero, the original process will wait for a status code, that
  218. * must be sent with daemon_status_send() (daemon_status_send() must be
  219. * called or the original process will remain waiting until all the children
  220. * close()). If status_wait is 0, the original process will exit immediately
  221. * with exit(0).
  222. * Global variables/config params used:
  223. * dont_daemonize
  224. * chroot_dir
  225. * working_dir
  226. * pid_file - if set the pid will be written here (ascii).
  227. * pgid_file - if set, the pgid will be written here (ascii).
  228. * log_stderr - if not set syslog will be opened (openlog(name,...))
  229. *
  230. *
  231. * Side-effects:
  232. * sets own_pgid after becoming session leader (own process group).
  233. */
  234. int daemonize(char* name, int status_wait)
  235. {
  236. FILE *pid_stream;
  237. pid_t pid;
  238. int r, p;
  239. char pipe_status;
  240. uid_t pid_uid;
  241. gid_t pid_gid;
  242. if(uid) pid_uid = uid;
  243. else pid_uid = -1;
  244. if(gid) pid_gid = gid;
  245. else pid_gid = -1;
  246. p=-1;
  247. /* flush std file descriptors to avoid flushes after fork
  248. * (same message appearing multiple times)
  249. * and switch to unbuffered
  250. */
  251. setbuf(stdout, 0);
  252. setbuf(stderr, 0);
  253. if (chroot_dir&&(chroot(chroot_dir)<0)){
  254. LM_CRIT("Cannot chroot to %s: %s\n", chroot_dir, strerror(errno));
  255. goto error;
  256. }
  257. if (chdir(working_dir)<0){
  258. LM_CRIT("cannot chdir to %s: %s\n", working_dir, strerror(errno));
  259. goto error;
  260. }
  261. if (!dont_daemonize) {
  262. if (status_wait) {
  263. if (daemon_status_pre_daemonize() < 0)
  264. goto error;
  265. }
  266. /* fork to become!= group leader*/
  267. if ((pid=fork())<0){
  268. LM_CRIT("Cannot fork:%s\n", strerror(errno));
  269. goto error;
  270. }else if (pid!=0){
  271. if (status_wait) {
  272. if (daemon_status_wait(&pipe_status) == 0)
  273. exit((int)pipe_status);
  274. else{
  275. LM_ERR("Main process exited before writing to pipe\n");
  276. exit(-1);
  277. }
  278. }
  279. exit(0);
  280. }
  281. if (status_wait)
  282. daemon_status_no_wait(); /* clean unused read fd */
  283. /* become session leader to drop the ctrl. terminal */
  284. if (setsid()<0){
  285. LM_WARN("setsid failed: %s\n",strerror(errno));
  286. }else{
  287. own_pgid=1;/* we have our own process group */
  288. }
  289. /* fork again to drop group leadership */
  290. if ((pid=fork())<0){
  291. LM_CRIT("Cannot fork:%s\n", strerror(errno));
  292. goto error;
  293. }else if (pid!=0){
  294. /*parent process => exit */
  295. exit(0);
  296. }
  297. }
  298. if(enable_dumpable()<0)
  299. goto error;
  300. /* added by noh: create a pid file for the main process */
  301. if (pid_file!=0){
  302. if ((pid_stream=fopen(pid_file, "r"))!=NULL){
  303. if (fscanf(pid_stream, "%d", &p) < 0) {
  304. LM_WARN("could not parse pid file %s\n", pid_file);
  305. }
  306. fclose(pid_stream);
  307. if (p==-1){
  308. LM_CRIT("pid file %s exists, but doesn't contain a valid"
  309. " pid number\n", pid_file);
  310. goto error;
  311. }
  312. if (kill((pid_t)p, 0)==0 || errno==EPERM){
  313. LM_CRIT("running process found in the pid file %s\n",
  314. pid_file);
  315. goto error;
  316. }else{
  317. LM_WARN("pid file contains old pid, replacing pid\n");
  318. }
  319. }
  320. pid=getpid();
  321. if ((pid_stream=fopen(pid_file, "w"))==NULL){
  322. LM_WARN("unable to create pid file %s: %s\n",
  323. pid_file, strerror(errno));
  324. goto error;
  325. }else{
  326. fprintf(pid_stream, "%i\n", (int)pid);
  327. fclose(pid_stream);
  328. if(chown(pid_file, pid_uid, pid_gid)<0) {
  329. LM_ERR("failed to chwon PID file: %s\n", strerror(errno));
  330. goto error;
  331. }
  332. }
  333. }
  334. if (pgid_file!=0){
  335. if ((pid_stream=fopen(pgid_file, "r"))!=NULL){
  336. if (fscanf(pid_stream, "%d", &p) < 0) {
  337. LM_WARN("could not parse pgid file %s\n", pgid_file);
  338. }
  339. fclose(pid_stream);
  340. if (p==-1){
  341. LM_CRIT("pgid file %s exists, but doesn't contain a valid"
  342. " pgid number\n", pgid_file);
  343. goto error;
  344. }
  345. }
  346. if (own_pgid){
  347. pid=getpgid(0);
  348. if ((pid_stream=fopen(pgid_file, "w"))==NULL){
  349. LM_WARN("unable to create pgid file %s: %s\n",
  350. pgid_file, strerror(errno));
  351. goto error;
  352. }else{
  353. fprintf(pid_stream, "%i\n", (int)pid);
  354. fclose(pid_stream);
  355. if(chown(pid_file, pid_uid, pid_gid)<0) {
  356. LM_ERR("failed to chwon PGID file: %s\n", strerror(errno));
  357. goto error;
  358. }
  359. }
  360. }else{
  361. LM_WARN("we don't have our own process so we won't save"
  362. " our pgid\n");
  363. unlink(pgid_file); /* just to be sure nobody will miss-use the old
  364. value*/
  365. }
  366. }
  367. /* try to replace stdin, stdout & stderr with /dev/null */
  368. if (freopen("/dev/null", "r", stdin)==0){
  369. LM_ERR("unable to replace stdin with /dev/null: %s\n",
  370. strerror(errno));
  371. /* continue, leave it open */
  372. };
  373. if (freopen("/dev/null", "w", stdout)==0){
  374. LM_ERR("unable to replace stdout with /dev/null: %s\n",
  375. strerror(errno));
  376. /* continue, leave it open */
  377. };
  378. /* close stderr only if log_stderr=0 */
  379. if ((!log_stderr) &&(freopen("/dev/null", "w", stderr)==0)){
  380. LM_ERR("unable to replace stderr with /dev/null: %s\n",
  381. strerror(errno));
  382. /* continue, leave it open */
  383. };
  384. /* close all but the daemon_status_fd output as the main process
  385. must still write into it to tell the parent to exit with 0 */
  386. closelog();
  387. for (r=3;r<MAX_FD; r++){
  388. if(r != daemon_status_fd[1])
  389. close(r);
  390. }
  391. if (log_stderr==0)
  392. openlog(name, LOG_PID|LOG_CONS, cfg_get(core, core_cfg, log_facility));
  393. /* LOG_CONS, LOG_PERRROR ? */
  394. return 0;
  395. error:
  396. return -1;
  397. }
  398. int do_suid()
  399. {
  400. struct passwd *pw;
  401. if (gid){
  402. if(setgid(gid)<0){
  403. LM_CRIT("cannot change gid to %d: %s\n", gid, strerror(errno));
  404. goto error;
  405. }
  406. }
  407. if(uid){
  408. if (!(pw = getpwuid(uid))){
  409. LM_CRIT("user lookup failed: %s\n", strerror(errno));
  410. goto error;
  411. }
  412. if(initgroups(pw->pw_name, pw->pw_gid)<0){
  413. LM_CRIT("cannot set supplementary groups: %s\n",
  414. strerror(errno));
  415. goto error;
  416. }
  417. if(setuid(uid)<0){
  418. LM_CRIT("cannot change uid to %d: %s\n", uid, strerror(errno));
  419. goto error;
  420. }
  421. }
  422. if(enable_dumpable()<0)
  423. goto error;
  424. return 0;
  425. error:
  426. return -1;
  427. }
  428. /*! \brief try to increase the open file limit */
  429. int increase_open_fds(int target)
  430. {
  431. struct rlimit lim;
  432. struct rlimit orig;
  433. if (getrlimit(RLIMIT_NOFILE, &lim)<0){
  434. LM_CRIT("cannot get the maximum number of file descriptors: %s\n",
  435. strerror(errno));
  436. goto error;
  437. }
  438. orig=lim;
  439. DBG("current open file limits: %lu/%lu\n",
  440. (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
  441. if ((lim.rlim_cur==RLIM_INFINITY) || (target<=lim.rlim_cur))
  442. /* nothing to do */
  443. goto done;
  444. else if ((lim.rlim_max==RLIM_INFINITY) || (target<=lim.rlim_max)){
  445. lim.rlim_cur=target; /* increase soft limit to target */
  446. }else{
  447. /* more than the hard limit */
  448. LM_INFO("trying to increase the open file limit"
  449. " past the hard limit (%ld -> %d)\n",
  450. (unsigned long)lim.rlim_max, target);
  451. lim.rlim_max=target;
  452. lim.rlim_cur=target;
  453. }
  454. DBG("increasing open file limits to: %lu/%lu\n",
  455. (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max);
  456. if (setrlimit(RLIMIT_NOFILE, &lim)<0){
  457. LM_CRIT("cannot increase the open file limit to"
  458. " %lu/%lu: %s\n",
  459. (unsigned long)lim.rlim_cur, (unsigned long)lim.rlim_max,
  460. strerror(errno));
  461. if (orig.rlim_max>orig.rlim_cur){
  462. /* try to increase to previous maximum, better than not increasing
  463. * at all */
  464. lim.rlim_max=orig.rlim_max;
  465. lim.rlim_cur=orig.rlim_max;
  466. if (setrlimit(RLIMIT_NOFILE, &lim)==0){
  467. LM_CRIT(" maximum number of file descriptors increased to"
  468. " %u\n",(unsigned)orig.rlim_max);
  469. }
  470. }
  471. goto error;
  472. }
  473. done:
  474. return 0;
  475. error:
  476. return -1;
  477. }
  478. /*! \brief enable core dumps */
  479. int set_core_dump(int enable, long unsigned int size)
  480. {
  481. struct rlimit lim;
  482. struct rlimit newlim;
  483. if (enable){
  484. if (getrlimit(RLIMIT_CORE, &lim)<0){
  485. LM_CRIT("cannot get the maximum core size: %s\n",
  486. strerror(errno));
  487. goto error;
  488. }
  489. if (lim.rlim_cur<size){
  490. /* first try max limits */
  491. newlim.rlim_max=RLIM_INFINITY;
  492. newlim.rlim_cur=newlim.rlim_max;
  493. if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
  494. /* now try with size */
  495. if (lim.rlim_max<size){
  496. newlim.rlim_max=size;
  497. }
  498. newlim.rlim_cur=newlim.rlim_max;
  499. if (setrlimit(RLIMIT_CORE, &newlim)==0) goto done;
  500. /* if this failed too, try rlim_max, better than nothing */
  501. newlim.rlim_max=lim.rlim_max;
  502. newlim.rlim_cur=newlim.rlim_max;
  503. if (setrlimit(RLIMIT_CORE, &newlim)<0){
  504. LM_CRIT("could increase core limits at all: %s\n",
  505. strerror (errno));
  506. }else{
  507. LM_CRIT("core limits increased only to %lu\n",
  508. (unsigned long)lim.rlim_max);
  509. }
  510. goto error; /* it's an error we haven't got the size we wanted*/
  511. }else{
  512. newlim.rlim_cur=lim.rlim_cur;
  513. newlim.rlim_max=lim.rlim_max;
  514. goto done; /*nothing to do */
  515. }
  516. }else{
  517. /* disable */
  518. newlim.rlim_cur=0;
  519. newlim.rlim_max=0;
  520. if (setrlimit(RLIMIT_CORE, &newlim)<0){
  521. LM_CRIT("failed to disable core dumps: %s\n",
  522. strerror(errno));
  523. goto error;
  524. }
  525. }
  526. done:
  527. DBG("core dump limits set to %lu\n", (unsigned long)newlim.rlim_cur);
  528. return 0;
  529. error:
  530. return -1;
  531. }
  532. /*! \brief lock pages in memory (make the process not swapable) */
  533. int mem_lock_pages()
  534. {
  535. #ifdef HAVE_MLOCKALL
  536. if (mlockall(MCL_CURRENT|MCL_FUTURE) !=0){
  537. LM_WARN("failed to lock the memory pages (disable swap): %s [%d]\n",
  538. strerror(errno), errno);
  539. goto error;
  540. }
  541. return 0;
  542. error:
  543. return -1;
  544. #else /* if MLOCKALL not defined return error */
  545. LM_WARN("failed to lock the memory pages: no mlockall support\n");
  546. return -1;
  547. #endif
  548. }
  549. /*! \brief tries to set real time priority
  550. * policy: 0 - SCHED_OTHER, 1 - SCHED_RR, 2 - SCHED_FIFO */
  551. int set_rt_prio(int prio, int policy)
  552. {
  553. #ifdef HAVE_SCHED_SETSCHEDULER
  554. struct sched_param sch_p;
  555. int min_prio, max_prio;
  556. int sched_policy;
  557. switch(policy){
  558. case 0:
  559. sched_policy=SCHED_OTHER;
  560. break;
  561. case 1:
  562. sched_policy=SCHED_RR;
  563. break;
  564. case 2:
  565. sched_policy=SCHED_FIFO;
  566. break;
  567. default:
  568. LM_WARN("invalid scheduling policy,using SCHED_OTHER\n");
  569. sched_policy=SCHED_OTHER;
  570. }
  571. memset(&sch_p, 0, sizeof(sch_p));
  572. max_prio=sched_get_priority_max(policy);
  573. min_prio=sched_get_priority_min(policy);
  574. if (prio<min_prio){
  575. LM_WARN("scheduling priority %d too small, using minimum value"
  576. " (%d)\n", prio, min_prio);
  577. prio=min_prio;
  578. }else if (prio>max_prio){
  579. LM_WARN("scheduling priority %d too big, using maximum value"
  580. " (%d)\n", prio, max_prio);
  581. prio=max_prio;
  582. }
  583. sch_p.sched_priority=prio;
  584. if (sched_setscheduler(0, sched_policy, &sch_p) != 0){
  585. LM_WARN("could not switch to real time priority: %s [%d]\n",
  586. strerror(errno), errno);
  587. return -1;
  588. };
  589. return 0;
  590. #else
  591. LM_WARN("real time support not available\n");
  592. return -1;
  593. #endif
  594. }