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