1%% options 2 3copyright owner = Dirk Krause 4copyright year = 2019-xxxx 5SPDX-License-Identifier: BSD-3-Clause 6 7 8%% header 9 10/** @file dk4dmt.h Daemon tool functions. 11 12The functions in this module can be used to program standing daemons. 13Three processes are involved in a standing daemon: 14 15* master process, originally started, 16* intermediate process, forked by master, decouples from terminal and 17* daemon process, forked by intermediate process. 18 19When a standing daemon is started by a systemd service unit, the master 20process must not exit before the daemon process has created the PID file. 21 22If you plan to give up root privileges in the child processes 23(use setgid() and setuid() to change effective user and group), 24do not use /var/run/<i>program</i>.pid as PID file name, better 25use /var/run/<i>program</i>/<i>program</i>.pid instead and make 26sure to set ownership and group on /var/run/<i>program</i> 27to the unprivileged user and group used by the child process. 28User and group need write permission to the directory, so the unprivileged 29child process can remove the PID file when exiting. 30 31The general usage structure looks like this: 32 33@code 34#define PROGRAM_NAME "my-new-service" 35const char program_name[] = { 36 PROGRAM_NAME 37}; 38const char pid_file_name[] = { 39 "/run/" PROGRAM_NAME "/" PROGRAM_NAME ".pid" 40}; 41dk4dmt_t dmt; 42 43... 44 45pid_t cpid; 46dk4dmt_init(&dmt); 47if (0 != read_configuration_file()) { 48 dk4dmt_set_program(&dmt, program_name); 49 dk4dmt_set_pidfile(&dmt, pid_file_name); 50 dk4dmt_set_syslog_feature(&dmt, syslog_feature); 51 if (0 != dk4dmt_parent_before_fork(&dmt)) { 52 cpid = fork(); 53 if ((pid_t)0 == cpid) { 54 if (0 != dk4dmt_intermediate_before_fork(&dmt)) { 55 cpid = fork(); 56 if ((pid_t)0 == cpid) { 57 if (0 != dk4dmt_daemon_start(&dmt)) { 58 do_the_service(); 59 } 60 dk4dmt_daemon_end(&dmt); 61 } 62 else { 63 if ((pid_t)(-1) != cpid) { 64 dk4dmt_intermediate_after_fork(&dmt); 65 } 66 else { 67 dk4dmt_intermediate_fork_failed(&dmt); 68 } 69 } 70 } 71 } 72 else { 73 if ((pid_t)(-1) != cpid) { 74 dk4dmt_parent_after_fork(&dmt); 75 } 76 else { 77 dk4dmt_parent_fork_failed(&dmt); 78 } 79 } 80 } 81} 82else { 83 dk4dmt_error_config(&dmt); 84} 85exval = dk4dmt_get_exit_status(&dmt); 86exit(exval); 87@endcode 88 89*/ 90 91#ifndef DK4CONF_H_INCLUDED 92#if DK4_BUILDING_DKTOOLS4 93#include "dk4conf.h" 94#else 95#include <dktools-4/dk4conf.h> 96#endif 97#endif 98 99 100/** Data collection for daemon. 101*/ 102typedef struct { 103 char const *prgname; /**< Program name. */ 104 char const *pidfile; /**< PID file name. */ 105 int pfd[2]; /**< Pipe file descriptors. */ 106 int logstderr; /**< Flag: Logging to stderr allowed. */ 107 int slf; /**< Syslog feature. */ 108 int exv; /**< Exit status code to return. */ 109} dk4dmt_t; 110 111 112 113#ifdef __cplusplus 114extern "C" { 115#endif 116 117/** Initialize daemon tool structure. 118 @param pdmt Daemon tool structure to initialize. 119*/ 120 121void 122dk4dmt_init(dk4dmt_t *pdmt); 123 124 125/** Set program name for daemon tool structure. 126 The function does not create a copy of the program name, 127 it keeps the original pointer in the structure. 128 So the progname buffer must exist and contain data as long as 129 the structure is used. 130 @param pdmt Daemon tool structure to set up. 131 @param progname Program name. 132*/ 133 134void 135dk4dmt_set_program(dk4dmt_t *pdmt, const char *progname); 136 137 138/** Set PID file name for daemon tool structure. 139 The function does not create a copy of the file name, it keeps the original 140 pointer in the structure. 141 So the pidfile buffer must exist and contain data as long as 142 the structure is used. 143 @param pdmt Daemon tool structure to set up. 144 @param pidfile PID file name. 145*/ 146 147void 148dk4dmt_set_pidfile(dk4dmt_t *pdmt, const char *pidfile); 149 150 151/** Enable or disable logging to stderr for daemon tool structure. 152 @param pdmt Daemon tool structure to set up. 153 @param flag Allow or deny logging to stderr. 154*/ 155 156void 157dk4dmt_set_log_stderr(dk4dmt_t *pdmt, int flag); 158 159 160/** Set syslog feature for daemon tool structure. 161 @param pdmt Daemon tool structure to set up. 162 @param feat Syslog feature to use. 163*/ 164 165void 166dk4dmt_set_syslog_feature(dk4dmt_t *pdmt, int feat); 167 168 169/** Process initialization before first fork attempt. 170 Open pipe for communication from daemon to parent. 171 @param pdmt Daemon tool structure to use. 172 @return 1 on success, 0 on error. 173*/ 174 175int 176dk4dmt_parent_before_fork(dk4dmt_t *pdmt); 177 178 179/** Wait for startup completion notification in parent process. 180 Close write head, read exit status, close read head. 181 @param pdmt Daemon tool structure to use. 182*/ 183 184void 185dk4dmt_parent_after_fork(dk4dmt_t *pdmt); 186 187 188/** React on failed fork attempt in parent process. 189 Close both pipe heads. 190 @param pdmt Daemon tool structure to use. 191*/ 192 193void 194dk4dmt_parent_fork_failed(dk4dmt_t *pdmt); 195 196 197/** Prepare for second fork attempt in intermediate process. 198 Close read head. 199 On error write exit status code and close write pipe head. 200 @param pdmt Daemon tool structure to use. 201 @return 1 on success, 0 on error. 202*/ 203 204int 205dk4dmt_intermediate_before_fork(dk4dmt_t *pdmt); 206 207 208/** Clean up in intermediate process after forking the daemon. 209 Close write pipe head. 210 @param pdmt Daemon tool structure to use. 211*/ 212 213void 214dk4dmt_intermediate_after_fork(dk4dmt_t *pdmt); 215 216 217/** React on failed fork attempt in intermediate process. 218 Write exit status code and close write pipe head. 219 @param pdmt Daemon tool structure to use. 220*/ 221 222void 223dk4dmt_intermediate_fork_failed(dk4dmt_t *pdmt); 224 225 226/** Create PID file and notify main process to exit. 227 Write exit status and close write pipe head. 228 @param pdmt Daemon tool structure to use. 229*/ 230 231int 232dk4dmt_daemon_start(dk4dmt_t *pdmt); 233 234 235/** Clean up daemon structure before exiting daemon process (remove PID file). 236 @param pdmt Daemon tool structure to use. 237*/ 238 239void 240dk4dmt_daemon_end(dk4dmt_t *pdmt); 241 242 243/** Set exit code in daemon structure for failed system function. 244 @param pdmt Daemon tool structure to use. 245*/ 246 247void 248dk4dmt_error_sysfct(dk4dmt_t *pdmt); 249 250 251/** Set exit code in daemon structure for failed signal process mask. 252 @param pdmt Daemon tool structure to use. 253*/ 254 255void 256dk4dmt_error_sigprocmask(dk4dmt_t *pdmt); 257 258 259/** Set exit code in daemon structure to indicate missing signal functionality. 260 @param pdmt Daemon tool structure to use. 261*/ 262 263void 264dk4dmt_error_no_signal_function(dk4dmt_t *pdmt); 265 266 267/** Set exit code in daemon structure to indicate PID file exists. 268 @param pdmt Daemon tool structure to use. 269*/ 270 271void 272dk4dmt_error_pid_file_exists(dk4dmt_t *pdmt); 273 274 275/** Set exit code in daemon structure to indicate PID file not configured. 276 @param pdmt Daemon tool structure to use. 277*/ 278 279void 280dk4dmt_error_pid_file_name_missing(dk4dmt_t *pdmt); 281 282 283/** Set exit code in daemon structure to indicate write PID file failed. 284 @param pdmt Daemon tool structure to use. 285*/ 286 287void 288dk4dmt_error_write_pid_file(dk4dmt_t *pdmt); 289 290/** Set exit code in daemon structure to indicate pipe was not opened (bug). 291 @param pdmt Daemon tool structure to use. 292*/ 293 294void 295dk4dmt_error_pipe_not_open(dk4dmt_t *pdmt); 296 297 298/** Set exit code in daemon structure to indicate reading from pipe failed. 299 @param pdmt Daemon tool structure to use. 300*/ 301 302void 303dk4dmt_error_pipe_read_failed(dk4dmt_t *pdmt); 304 305 306/** Set exit code in daemon structure to indicate fork failed. 307 @param pdmt Daemon tool structure to use. 308*/ 309 310void 311dk4dmt_error_fork_failed(dk4dmt_t *pdmt); 312 313 314/** Set exit code in daemon structure to indicate setsid failed. 315 @param pdmt Daemon tool structure to use. 316*/ 317 318void 319dk4dmt_error_setsid(dk4dmt_t *pdmt); 320 321 322/** Set exit code in daemon structure to indicate dup2 failed. 323 @param pdmt Daemon tool structure to use. 324*/ 325 326void 327dk4dmt_error_dup2_failed(dk4dmt_t *pdmt); 328 329 330/** Set exit code in daemon structure to indicate reading /dev/null failed. 331 @param pdmt Daemon tool structure to use. 332*/ 333 334void 335dk4dmt_error_failed_read_dev_null(dk4dmt_t *pdmt); 336 337 338/** Set exit code in daemon structure to indicate writing /dev/null failed. 339 @param pdmt Daemon tool structure to use. 340*/ 341 342void 343dk4dmt_error_failed_write_dev_null(dk4dmt_t *pdmt); 344 345 346/** Set exit code in daemon structure to indicate chdir to / failed. 347 @param pdmt Daemon tool structure to use. 348*/ 349 350void 351dk4dmt_error_failed_chdir_root(dk4dmt_t *pdmt); 352 353 354/** Set exit code in daemon structure for failed signal set cleanup. 355 @param pdmt Daemon tool structure to use. 356*/ 357 358void 359dk4dmt_error_sigemptyset(dk4dmt_t *pdmt); 360 361 362/** Set exit code in daemon structure for unusable configuration. 363 @param pdmt Daemon tool structure to use. 364*/ 365 366void 367dk4dmt_error_config(dk4dmt_t *pdmt); 368 369 370/** Set exit code in daemon structure to indicate wrong call arguments. 371 @param pdmt Daemon tool structure to use. 372*/ 373 374void 375dk4dmt_error_usage(dk4dmt_t *pdmt); 376 377 378/** Indicate success. 379 @param pdmt Daemon tool structure to use. 380*/ 381void 382dk4dmt_success(dk4dmt_t *pdmt); 383 384 385/** Retrieve exit status code. 386 @param pdmt Daemon tool structure to use. 387 @return Exit status code to return. 388*/ 389 390int 391dk4dmt_get_exit_status(dk4dmt_t *pdmt); 392 393#ifdef __cplusplus 394} 395#endif 396 397%% module 398 399#include "dk4conf.h" 400 401#include <stdio.h> 402 403#if DK4_HAVE_SYS_TYPES_H 404#ifndef SYS_TYPES_H_INCLUDED 405#include <sys/types.h> 406#define SYS_TYPES_H_INCLUDED 1 407#endif 408#endif 409 410#if DK4_HAVE_SYS_STAT_H 411#ifndef SYS_STAT_H_INCLUDED 412#include <sys/stat.h> 413#define SYS_STAT_H_INCLUDED 1 414#endif 415#endif 416 417#if DK4_HAVE_STDLIB_H 418#ifndef STDLIB_H_INCLUDED 419#include <stdlib.h> 420#define STDLIB_H_INCLUDED 1 421#endif 422#endif 423 424#if DK4_HAVE_UNISTD_H 425#ifndef UNISTD_H_INCLUDED 426#include <unistd.h> 427#define UNISTD_H_INCLUDED 1 428#endif 429#endif 430 431#if DK4_HAVE_FCNTL_H 432#ifndef FCNTL_H_INCLUDED 433#include <fcntl.h> 434#define FCNTL_H_INCLUDED 1 435#endif 436#endif 437 438#if DK4_HAVE_SIGNAL_H 439#ifndef SIGNAL_H_INCLUDED 440#include <signal.h> 441#define SIGNAL_H_INCLUDED 1 442#endif 443#endif 444 445#if DK4_HAVE_IO_H 446#ifndef IO_H_INCLUDED 447#include <io.h> 448#define IO_H_INCLUDED 1 449#endif 450#endif 451 452#if DK4_HAVE_SYSLOG_H 453#ifndef SYSLOG_H_INCLUDED 454#include <syslog.h> 455#define SYSLOG_H_INCLUDED 1 456#endif 457#endif 458 459#if DK4_HAVE_SYSEXITS_H 460#ifndef SYSEXITS_H_INCLUDED 461#include <sysexits.h> 462#define SYSEXITS_H_INCLUDED 463#endif 464#endif 465 466#ifndef DK4TYPES_H_INCLUDED 467#include <libdk4base/dk4types.h> 468#endif 469 470#ifndef DK4DMT_H_INCLUDED 471#include <libdk4c/dk4dmt.h> 472#endif 473 474#ifndef DK4MEM_H_INCLUDED 475#include <libdk4base/dk4mem.h> 476#endif 477 478#ifndef DK4MAO8D_H_INCLUDED 479#include <libdk4maio8d/dk4mao8d.h> 480#endif 481 482#ifndef DK4STAT8_H_INCLUDED 483#include <libdk4c/dk4stat8.h> 484#endif 485 486#ifndef DK4STR8_H_INCLUDED 487#include <libdk4base/dk4str8.h> 488#endif 489 490#ifndef DK4FOPC8_H_INCLUDED 491#include <libdk4c/dk4fopc8.h> 492#endif 493 494#ifndef DK4MKDH8_H_INCLUDED 495#include <libdk4c/dk4mkdh8.h> 496#endif 497 498$!trace-include 499 500 501 502/** Constant texts used by module. 503*/ 504static char const * const dk4dmt_kw[] = { 505$!string-table 506# 507# 0 Newline 508# 509\n 510# 511# 1 File mode for text writing 512# 513w 514# 515# 2 File name to open /dev/null 516# 517/dev/null 518# 519# 3 The root directory 520# 521/ 522# 523# 4 ERROR: No signal handling functions available! 524# 525No signal handling functions available! 526# 527# 5,6 ERROR: Failed to create empty set for signal xxx! 528# 529Failed to create empty set for signal 530! 531# 532# 7,8 ERROR: Failed to reset handler for signal xxx! 533# 534Failed to reset handler for signal 535! 536# 537# 9 ERROR: Failed to unblock all signals! 538# 539Failed to unblock all signals! 540# 541# 10 ERROR: PID file already exists! 542# 543PID file already exists! 544# 545# 11 ERROR: Pipe not open! 546# 547Pipe not open (bug)! 548# 549# 12 ERROR: Failed to read from pipe! 550# 551Failed to read from pipe! 552# 553# 13 ERROR: Failed to fork new process! 554# 555Failed to fork new process! 556# 557# 14 ERROR: Failed to decouple from terminal! 558# 559Failed to decouple from terminal using setsid()! 560# 561# 15 ERROR: Missing function... 562# 563Missing function to decouple from terminal (setsid/setpgrp)! 564# 565# 16 ERROR: Failed to write PID file! 566# 567Failed to write PID file! 568# 569# 17 ERROR: No PID file specified! 570# 571No PID file name specified (bug)! 572# 573# 18 ERROR: Failed to duplicate standard file handle! 574# 575Failed to duplicate standard file handle using dup2()! 576# 577# 19 ERROR: Failed to open /dev/null for read access! 578# 579Failed to open /dev/null for read access! 580# 581# 20 ERROR: Failed to open /dev/null for write access! 582# 583Failed to open /dev/null for write access! 584# 585# 21 ERROR: Failed to change to / directory! 586# 587Failed to change to / directory! 588# 589# 22,23 ERROR: Failed to remove PID file! 590# 591Failed to remove PID file " 592"! 593# 594# 24 Module name 595# 596dk4dmt.o 597$!end 598}; 599 600 601 602static 603void 604dk4dmt_log_1(dk4dmt_t *pdmt, size_t i) 605{ 606#if DK4_HAVE_SYSLOG 607 const char *progname; 608#endif 609 if (0 != pdmt->logstderr) { 610 fputs(dk4dmt_kw[i], stderr); 611 fputc('\n', stderr); 612 } 613#if DK4_HAVE_SYSLOG 614 progname = pdmt->prgname; 615 if (NULL == progname) { progname = dk4dmt_kw[24]; } 616 openlog(progname, LOG_PID, pdmt->slf); 617 syslog(LOG_ERR, "%s", dk4dmt_kw[i]); 618 closelog(); 619#endif 620} 621 622 623 624static 625void 626dk4dmt_log_3(dk4dmt_t *pdmt, size_t i1, size_t i2, const char *s) 627{ 628#if DK4_HAVE_SYSLOG 629 const char *progname; 630#endif 631 if (0 != pdmt->logstderr) { 632 fputs(dk4dmt_kw[i1], stderr); 633 fputs(s, stderr); 634 fputs(dk4dmt_kw[i2], stderr); 635 fputc('\n', stderr); 636 } 637#if DK4_HAVE_SYSLOG 638 progname = pdmt->prgname; 639 if (NULL == progname) { progname = dk4dmt_kw[24]; } 640 openlog(progname, LOG_PID, pdmt->slf); 641 syslog(LOG_ERR, "%s%s%s", dk4dmt_kw[i1], s, dk4dmt_kw[i2]); 642 closelog(); 643#endif 644} 645 646 647 648/** Set exit status in structure. 649 @param pdmt Structure to modify. 650 @param syse One of the codes defined in the sysexits header. 651 @param sdes Systemd error code. 652*/ 653static 654void 655dk4dmt_set_error_exit_status(dk4dmt_t *pdmt, int syse, int sdes) 656{ 657 if (NULL != pdmt) { 658 if (EXIT_SUCCESS == pdmt->exv) { 659#if DK4_HAVE_SYSTEMD 660 if (0 != sdes) { 661 pdmt->exv = sdes; 662 } 663 else { 664 pdmt->exv = syse; 665 } 666#else 667 if (0 != syse) { 668 pdmt->exv = syse; 669 } 670 else { 671 switch (sdes) { 672 default : { 673#if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) 674 pdmt->exv = EX_OSERR; 675#else 676 pdmt->exv = EXIT_FAILURE; 677#endif 678 } break; 679 } 680 } 681#endif 682 } 683 } 684} 685 686 687 688void 689dk4dmt_init(dk4dmt_t *pdmt) 690{ 691 if (NULL != pdmt) { 692 DK4_MEMRES(pdmt,sizeof(dk4dmt_t)); 693 pdmt->prgname = NULL; 694 pdmt->pidfile = NULL; 695 pdmt->pfd[0] = -1; 696 pdmt->pfd[1] = -1; 697 pdmt->logstderr = 0; 698#if (DK4_HAVE_SYSLOG_H) && defined(LOG_DAEMON) 699 pdmt->slf = LOG_DAEMON; 700#else 701 pdmt->slf = (3<<3); 702#endif 703 pdmt->exv = EXIT_SUCCESS; 704 } 705} 706 707 708 709void 710dk4dmt_set_program(dk4dmt_t *pdmt, const char *progname) 711{ 712 if ((NULL != pdmt) && (NULL != progname)) { 713 pdmt->prgname = progname; 714 } 715} 716 717 718 719void 720dk4dmt_set_pidfile(dk4dmt_t *pdmt, const char *pidfile) 721{ 722 if ((NULL != pdmt) && (NULL != pidfile)) { 723 pdmt->pidfile = pidfile; 724 } 725} 726 727 728 729void 730dk4dmt_set_log_stderr(dk4dmt_t *pdmt, int flag) 731{ 732 if (NULL != pdmt) { 733 pdmt->logstderr = flag; 734 } 735} 736 737 738 739void 740dk4dmt_set_syslog_feature(dk4dmt_t *pdmt, int feat) 741{ 742 if (NULL != pdmt) { 743 pdmt->slf = feat; 744 } 745} 746 747 748 749void 750dk4dmt_error_sigprocmask(dk4dmt_t *pdmt) 751{ 752#if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) 753 dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 207); 754#else 755 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 207); 756#endif 757} 758 759 760 761void 762dk4dmt_error_sigemptyset(dk4dmt_t *pdmt) 763{ 764#if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) 765 dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 207); 766#else 767 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 207); 768#endif 769} 770 771 772 773void 774dk4dmt_error_no_signal_function(dk4dmt_t *pdmt) 775{ 776#if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) 777 dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 2); 778#else 779 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 2); 780#endif 781} 782 783 784 785void 786dk4dmt_error_pid_file_exists(dk4dmt_t *pdmt) 787{ 788#if (DK4_HAVE_SYSEXITS_H) && defined(EX_SOFTWARE) 789 dk4dmt_set_error_exit_status(pdmt, EX_SOFTWARE, 0); 790#else 791 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 792#endif 793} 794 795 796 797void 798dk4dmt_error_pid_file_name_missing(dk4dmt_t *pdmt) 799{ 800#if (DK4_HAVE_SYSEXITS_H) && defined(EX_SOFTWARE) 801 dk4dmt_set_error_exit_status(pdmt, EX_SOFTWARE, 0); 802#else 803 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 804#endif 805} 806 807 808 809void 810dk4dmt_error_pipe_not_open(dk4dmt_t *pdmt) 811{ 812#if (DK4_HAVE_SYSEXITS_H) && defined(EX_SOFTWARE) 813 dk4dmt_set_error_exit_status(pdmt, EX_SOFTWARE, 0); 814#else 815 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 816#endif 817} 818 819 820 821void 822dk4dmt_error_pipe_read_failed(dk4dmt_t *pdmt) 823{ 824#if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) 825 dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); 826#else 827 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 828#endif 829} 830 831 832 833void 834dk4dmt_error_fork_failed(dk4dmt_t *pdmt) 835{ 836#if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) 837 dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 0); 838#else 839 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 840#endif 841} 842 843 844 845void 846dk4dmt_error_dup2_failed(dk4dmt_t *pdmt) 847{ 848#if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) 849 dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); 850#else 851 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 852#endif 853} 854 855 856 857void 858dk4dmt_error_failed_read_dev_null(dk4dmt_t *pdmt) 859{ 860#if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) 861 dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); 862#else 863 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 864#endif 865} 866 867 868 869void 870dk4dmt_error_failed_write_dev_null(dk4dmt_t *pdmt) 871{ 872#if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) 873 dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); 874#else 875 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 876#endif 877} 878 879 880 881void 882dk4dmt_error_failed_chdir_root(dk4dmt_t *pdmt) 883{ 884#if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) 885 dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 200); 886#else 887 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 200); 888#endif 889} 890 891 892 893void 894dk4dmt_error_setsid(dk4dmt_t *pdmt) 895{ 896#if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) 897 dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 220); 898#else 899 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 220); 900#endif 901} 902 903 904void 905dk4dmt_error_write_pid_file(dk4dmt_t *pdmt) 906{ 907#if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) 908 dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); 909#else 910 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 911#endif 912} 913 914 915 916void 917dk4dmt_error_sysfct(dk4dmt_t *pdmt) 918{ 919 if (NULL != pdmt) { 920#if (DK4_HAVE_SYSEXITS_H) && defined(EX_OSERR) 921 dk4dmt_set_error_exit_status(pdmt, EX_OSERR, 0); 922#else 923 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 924#endif 925 } 926} 927 928 929 930void 931dk4dmt_error_usage(dk4dmt_t *pdmt) 932{ 933 if (NULL != pdmt) { 934#if (DK4_HAVE_SYSEXITS_H) && defined(EX_USAGE) 935 dk4dmt_set_error_exit_status(pdmt, EX_USAGE, 0); 936#else 937 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 938#endif 939 } 940} 941 942 943 944void 945dk4dmt_error_config(dk4dmt_t *pdmt) 946{ 947 if (NULL != pdmt) { 948#if (DK4_HAVE_SYSEXITS_H) && defined(EX_CONFIG) 949 dk4dmt_set_error_exit_status(pdmt, EX_CONFIG, 0); 950#else 951 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 952#endif 953 } 954} 955 956 957 958static 959void 960dk4dmt_reset_all_signals(int *pback, dk4dmt_t *pdmt) 961{ 962#if DK4_HAVE_SIGACTION 963 struct sigaction sigact; /* Information for sigaction() */ 964#endif 965 char buf[16*sizeof(int)]; 966 int maxsig; /* Maximum signal number */ 967 int signo; /* Current signal number to process */ 968 int go; /* Flag: Reconfigure signal */ 969 int ec; /* Error code */ 970 971 ec = 0; 972#ifdef _NSIG 973 maxsig = _NSIG; 974#else 975 maxsig = 16; 976#endif 977 for (signo = 1; ((maxsig >= signo) && (1 == *pback)); signo++) { 978 go = 1; 979#ifdef SIGKILL 980 if (SIGKILL == signo) { go = 0; } 981#else 982 if (9 == signo) { go = 0; } 983#endif 984#ifdef SIGSTOP 985 if (SIGSTOP == signo) { go = 0; } 986#else 987 if (19 == signo) { go = 0; } 988#endif 989 if (0 != go) { 990#if DK4_HAVE_SIGACTION 991 DK4_MEMRES(&sigact, sizeof(sigact)); 992 sigact.sa_handler = SIG_DFL; 993 sigact.sa_flags = 0; 994 if (0 != sigemptyset(&(sigact.sa_mask))) { 995 if (16 > signo) { 996 *pback = 0; 997 dk4dmt_error_sigemptyset(pdmt); 998 /* ERROR: Failed to empty set */ 999#if DK4_HAVE_SNPRINTF 1000 snprintf(buf, sizeof(buf), "%d", signo); 1001#else 1002 sprintf(buf, "%d", signo); 1003#endif 1004 dk4dmt_log_3(pdmt, 5, 6, buf); 1005 } 1006 } 1007 else { 1008 if (0 != sigaction(signo, &sigact, NULL)) { 1009 if (16 > signo) { 1010 *pback = 0; 1011 dk4dmt_error_sigemptyset(pdmt); 1012 /* ERROR: sigaction failed */ 1013#if DK4_HAVE_SNPRINTF 1014 snprintf(buf, sizeof(buf), "%d", signo); 1015#else 1016 sprintf(buf, "%d", signo); 1017#endif 1018 dk4dmt_log_3(pdmt, 7, 8, buf); 1019 } 1020 } 1021 } 1022#else 1023#if DK4_HAVE_SIGSET 1024 sigset(signo, SIG_DFL); 1025#else 1026#if DK4_HAVE_SIGNAL 1027 signal(signo, SIG_DFL); 1028#else 1029 *pback = 0; 1030 dk4dmt_error_no_signal_function(pdmt); 1031 /* ERROR: No function available to modify signals */ 1032 if (0 == ec) { ec = 3; } 1033#endif 1034#endif 1035#endif 1036 } 1037 } 1038 switch (ec) { 1039 case 3: { 1040 dk4dmt_log_1(pdmt, 4); 1041 } break; 1042 } 1043} 1044 1045 1046 1047static 1048void 1049dk4dmt_unblock_all_signals(int *pback, dk4dmt_t *pdmt) 1050{ 1051 sigset_t set; 1052 if (0 == sigemptyset(&set)) { 1053 if (0 != sigprocmask(SIG_SETMASK, &set, NULL)) { 1054 *pback = 0; 1055 dk4dmt_error_sigprocmask(pdmt); 1056 /* ERROR: sigprocmask failed */ 1057 dk4dmt_log_1(pdmt, 9); 1058 } 1059 } 1060 else { 1061 *pback = 0; 1062 dk4dmt_error_sigemptyset(pdmt); 1063 /* ERROR: Failed to sigemptyset */ 1064 dk4dmt_log_1(pdmt, 9); 1065 } 1066} 1067 1068 1069 1070static 1071void 1072dk4dmt_close_non_std_file_descriptors(void) 1073{ 1074#if DK4_HAVE_SYS_RESOURCE_H && DK4_HAVE_GETRLIMIT && defined(RLIMIT_NOFILE) 1075 struct rlimit rl; 1076#endif 1077 int maxfd = 1024; 1078 int fd; 1079 1080 /* Find maximum file descriptor 1081 */ 1082#if DK4_HAVE_SYS_RESOURCE_H && DK4_HAVE_GETRLIMIT && defined(RLIMIT_NOFILE) 1083 if (0 == getrlimit(RLIMIT_NOFILE, &rl)) { 1084#if defined(RLIM_INFINITY) 1085 if (RLIM_INFINITY != rl.rlim_max) { 1086 back = rl.rlim_max; 1087 } 1088#else 1089 back = rl.rlim_max; 1090#endif 1091 } 1092#else 1093#endif 1094 /* 1095 Close file descriptors 1096 */ 1097 for (fd = 3; fd <= maxfd; fd++) { 1098 (void)close(fd); 1099 } 1100} 1101 1102 1103 1104int 1105dk4dmt_parent_before_fork(dk4dmt_t *pdmt) 1106{ 1107 dk4_stat_t stb; 1108 int back = 0; 1109 1110 if (NULL != pdmt) { 1111 back = 1; 1112 /* 1113 Close non-standard file descriptors 1114 */ 1115 dk4dmt_close_non_std_file_descriptors(); 1116 /* 1117 Reset all signals 1118 */ 1119 dk4dmt_reset_all_signals(&back, pdmt); 1120 if (1 == back) { 1121 /* 1122 Unblock all signals 1123 */ 1124 dk4dmt_unblock_all_signals(&back, pdmt); 1125 if (1 == back) { 1126 if (NULL != pdmt->pidfile) { 1127 if (0 != dk4stat_c8(&stb, pdmt->pidfile, NULL)) { 1128 back = 0; 1129 dk4dmt_error_pid_file_exists(pdmt); 1130 /* ERROR: PID file exists */ 1131 dk4dmt_log_1(pdmt, 10); 1132 } 1133 } 1134 if (1 == back) { 1135 if (0 != pipe(&(pdmt->pfd[0]))) { 1136 back = 0; 1137 pdmt->pfd[0] = -1; 1138 pdmt->pfd[1] = -1; 1139 } 1140 } 1141 } 1142 } 1143 } 1144 return back; 1145} 1146 1147 1148 1149void 1150dk4dmt_parent_after_fork(dk4dmt_t *pdmt) 1151{ 1152 int v; 1153 1154 if (NULL != pdmt) { 1155 /* 1156 Close write head 1157 */ 1158 if (-1 != pdmt->pfd[1]) { 1159 close(pdmt->pfd[1]); 1160 pdmt->pfd[1] = -1; 1161 } 1162 else { 1163 dk4dmt_error_pipe_not_open(pdmt); 1164 /* ERROR: Pipe was not open (bug) */ 1165 dk4dmt_log_1(pdmt, 11); 1166 } 1167 /* 1168 Read exit status code and close read head 1169 */ 1170 if (-1 != pdmt->pfd[0]) { 1171 if ((int)sizeof(int) == (int)read(pdmt->pfd[0], &v, sizeof(int))) { 1172 pdmt->exv = v; 1173 } 1174 else { 1175 dk4dmt_error_pipe_read_failed(pdmt); 1176 /* ERROR: Failed to read from pipe */ 1177 dk4dmt_log_1(pdmt, 12); 1178 } 1179 close(pdmt->pfd[0]); 1180 pdmt->pfd[0] = -1; 1181 } 1182 else { 1183 dk4dmt_error_pipe_not_open(pdmt); 1184 /* ERROR: Pipe was not open (bug) */ 1185 dk4dmt_log_1(pdmt, 11); 1186 } 1187 } 1188} 1189 1190 1191 1192void 1193dk4dmt_parent_fork_failed(dk4dmt_t *pdmt) 1194{ 1195 if (NULL != pdmt) { 1196 /* 1197 Write error exit status code 1198 */ 1199 dk4dmt_error_fork_failed(pdmt); 1200 /* ERROR: Fork failed */ 1201 dk4dmt_log_1(pdmt, 13); 1202 /* 1203 Close write head 1204 */ 1205 if (-1 != pdmt->pfd[1]) { 1206 close(pdmt->pfd[1]); 1207 pdmt->pfd[1] = -1; 1208 } 1209 /* 1210 Close read head 1211 */ 1212 if (-1 != pdmt->pfd[0]) { 1213 close(pdmt->pfd[0]); 1214 pdmt->pfd[0] = -1; 1215 } 1216 } 1217} 1218 1219 1220 1221int 1222dk4dmt_intermediate_before_fork(dk4dmt_t *pdmt) 1223{ 1224 int back = 0; 1225 if (NULL != pdmt) { 1226 back = 1; 1227 /* 1228 Close read head 1229 */ 1230 if (-1 != pdmt->pfd[0]) { 1231 close(pdmt->pfd[0]); 1232 pdmt->pfd[0] = -1; 1233 } 1234#if DK4_HAVE_SETSID 1235 if ((pid_t)(-1) == setsid()) { 1236 back = 0; 1237 dk4dmt_error_setsid(pdmt); 1238 /* ERROR: setsid() failed */ 1239 dk4dmt_log_1(pdmt, 14); 1240 } 1241#else 1242#if DK4_HAVE_SETPGRP 1243 setpgrp(); 1244#else 1245 back = 0; 1246 dk4dmt_error_setsid(pdmt); 1247 /* ERROR: No function to decouple from terminal */ 1248 dk4dmt_log_1(pdmt, 15); 1249#endif 1250#endif 1251 } 1252 return back; 1253} 1254 1255 1256 1257void 1258dk4dmt_intermediate_after_fork(dk4dmt_t *pdmt) 1259{ 1260 if (NULL != pdmt) { 1261 /* 1262 Close write head 1263 */ 1264 if (-1 != pdmt->pfd[1]) { 1265 close(pdmt->pfd[1]); 1266 pdmt->pfd[1] = -1; 1267 } 1268 } 1269} 1270 1271 1272 1273void 1274dk4dmt_intermediate_fork_failed(dk4dmt_t *pdmt) 1275{ 1276 int v; 1277 1278 if (NULL != pdmt) { 1279 /* 1280 Set exit status code 1281 */ 1282 dk4dmt_parent_fork_failed(pdmt); 1283 /* ERROR: Fork failed */ 1284 dk4dmt_log_1(pdmt, 13); 1285 /* 1286 Close write head 1287 */ 1288 if (-1 != pdmt->pfd[1]) { 1289 v = pdmt->exv; 1290 if ((int)sizeof(v) != (int)write(pdmt->pfd[1], &v, sizeof(v))) { 1291#if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) 1292 dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); 1293#else 1294 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 1295#endif 1296 } 1297 close(pdmt->pfd[1]); 1298 pdmt->pfd[1] = -1; 1299 } 1300 } 1301} 1302 1303 1304 1305int 1306dk4dmt_daemon_start(dk4dmt_t *pdmt) 1307{ 1308 char pidbuf[16*sizeof(dk4_um_t)]; 1309 FILE *fipo; 1310 int res; 1311 int v; 1312 int fdout; 1313 int fdin; 1314 int back = 0; 1315 1316 if (NULL != pdmt) { 1317 if (NULL != pdmt->pidfile) { 1318 res = dk4ma_write_c8_decimal_unsigned( 1319 pidbuf, sizeof(pidbuf), (dk4_um_t)getpid(), 0, NULL 1320 ); 1321 if (0 != res) { 1322 if (0 != dk4str8_cat_s(pidbuf,sizeof(pidbuf),dk4dmt_kw[0],NULL)) 1323 { 1324 (void)dk4mkdir_hierarchy_c8(pdmt->pidfile, 0, NULL); 1325 fipo = dk4fopen_c8( 1326 pdmt->pidfile,dk4dmt_kw[1],DK4_FOPEN_SC_PRIVILEGED,NULL 1327 ); 1328 if (NULL != fipo) { 1329 back = 1; 1330 if (EOF == fputs(pidbuf, fipo)) { 1331 back = 0; 1332 dk4dmt_error_write_pid_file(pdmt); 1333 /* ERROR: Failed to write PID file */ 1334 dk4dmt_log_1(pdmt, 16); 1335 } 1336 if (0 != fclose(fipo)) { 1337 back = 0; 1338 dk4dmt_error_write_pid_file(pdmt); 1339 /* ERROR: Failed to write PID file */ 1340 dk4dmt_log_1(pdmt, 16); 1341 } 1342 } 1343 else { 1344 dk4dmt_error_write_pid_file(pdmt); 1345 /* ERROR: Failed to write PID file */ 1346 dk4dmt_log_1(pdmt, 16); 1347 } 1348 } 1349 } 1350 if (0 == back) { 1351 unlink(pdmt->pidfile); 1352 } 1353 } 1354 else { 1355 dk4dmt_error_pid_file_name_missing(pdmt); 1356 /* ERROR: Missing PID file name */ 1357 dk4dmt_log_1(pdmt, 17); 1358 } 1359 /* 1360 Connect /dev/null to stdin, stdout and stderr 1361 */ 1362 if (0 != back) { 1363 fdin = open(dk4dmt_kw[2], O_RDONLY); 1364 if (-1 < fdin) { 1365 if (-1 == dup2(fdin, 0)) { 1366 back = 0; 1367 dk4dmt_error_dup2_failed(pdmt); 1368 /* ERROR: Failed to duplicate file handle */ 1369 dk4dmt_log_1(pdmt, 18); 1370 } 1371 close(fdin); 1372 } 1373 else { 1374 back = 0; 1375 dk4dmt_error_failed_read_dev_null(pdmt); 1376 /* ERROR: Failed to read /dev/null */ 1377 dk4dmt_log_1(pdmt, 19); 1378 } 1379 fdout = open(dk4dmt_kw[2], O_WRONLY); 1380 if (-1 < fdout) { 1381 if (-1 == dup2(fdout, 1)) { 1382 back = 0; 1383 dk4dmt_error_dup2_failed(pdmt); 1384 /* ERROR: Failed to duplicate file handle */ 1385 dk4dmt_log_1(pdmt, 18); 1386 } 1387 if (-1 == dup2(fdout, 2)) { 1388 back = 0; 1389 dk4dmt_error_dup2_failed(pdmt); 1390 /* ERROR: Failed to duplicate file handle */ 1391 dk4dmt_log_1(pdmt, 18); 1392 } 1393 close(fdout); 1394 } 1395 else { 1396 back = 0; 1397 dk4dmt_error_failed_write_dev_null(pdmt); 1398 /* ERROR: Failed to write /dev/null */ 1399 dk4dmt_log_1(pdmt, 20); 1400 } 1401 } 1402 /* 1403 Use exact permissions when creating new files or directories 1404 */ 1405 if (0 != back) { 1406 umask(0); 1407 } 1408 /* 1409 Change to root directory 1410 */ 1411 if (0 != back) { 1412 if (0 != chdir(dk4dmt_kw[3])) { 1413 back = 0; 1414 dk4dmt_error_failed_chdir_root(pdmt); 1415 /* ERROR: Failed to change into root directory */ 1416 dk4dmt_log_1(pdmt, 21); 1417 } 1418 } 1419 /* Send exit status code to main process 1420 */ 1421 if (-1 != pdmt->pfd[1]) { 1422 v = pdmt->exv; 1423 if ((int)sizeof(v) != (int)write(pdmt->pfd[1], &v, sizeof(v))) { 1424#if (DK4_HAVE_SYSEXITS_H) && defined(EX_IOERR) 1425 dk4dmt_set_error_exit_status(pdmt, EX_IOERR, 0); 1426#else 1427 dk4dmt_set_error_exit_status(pdmt, EXIT_FAILURE, 0); 1428#endif 1429 } 1430 close(pdmt->pfd[1]); 1431 pdmt->pfd[1] = -1; 1432 } 1433 } 1434 return back; 1435} 1436 1437 1438 1439void 1440dk4dmt_daemon_end(dk4dmt_t *pdmt) 1441{ 1442 if (NULL != pdmt) { 1443 if (NULL != pdmt->pidfile) { 1444 if (0 != unlink(pdmt->pidfile)) { 1445 /* ERROR: Failed to remove PID file */ 1446 dk4dmt_log_3(pdmt, 22, 23, pdmt->pidfile); 1447 } 1448 } 1449 } 1450} 1451 1452 1453 1454void 1455dk4dmt_success(dk4dmt_t *pdmt) 1456{ 1457 if (NULL != pdmt) { 1458 pdmt->exv = EXIT_SUCCESS; 1459 } 1460} 1461 1462 1463int 1464dk4dmt_get_exit_status(dk4dmt_t *pdmt) 1465{ 1466 int back = EXIT_FAILURE; 1467 if (NULL != pdmt) { 1468 back = pdmt->exv; 1469 } 1470 return back; 1471} 1472 1473 1474/* vim: set ai sw=4 ts=4 : */ 1475 1476