mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			333 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			333 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
UNIX daemonizer.  Daemonizes any non-interactive console program and watches over it.
 | 
						|
Whenever a signal is sent to this process, it halts the daemonized process as well.
 | 
						|
 | 
						|
To compile:	cc -o daemonize daemonize.c
 | 
						|
Usage:		./daemonize -?
 | 
						|
Users of this:	catalina initscript
 | 
						|
*/
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <syslog.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <getopt.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <pwd.h>
 | 
						|
 | 
						|
#define RUNNING_DIR	"/"
 | 
						|
#define PIDFILE		"/var/run/daemonize.pid"
 | 
						|
#define VARLOGFILE 	"/var/log/daemon.log"
 | 
						|
#define PROGNAME	"daemonized"
 | 
						|
#define DEFAULTUSER	"root"
 | 
						|
 | 
						|
char * pidfile = PIDFILE;
 | 
						|
char * varlogfile = VARLOGFILE;
 | 
						|
char * progname = PROGNAME;
 | 
						|
char * user = PROGNAME;
 | 
						|
 | 
						|
void initialize_syslog(const char*pn) {
 | 
						|
	openlog(pn,LOG_PID,LOG_DAEMON);
 | 
						|
	syslog(LOG_INFO, "syslog connection opened");
 | 
						|
}
 | 
						|
 | 
						|
void cleanup_syslog() {
 | 
						|
	syslog(LOG_INFO, "syslog connection closed");
 | 
						|
	closelog(); 
 | 
						|
}
 | 
						|
 | 
						|
int killed = 0;
 | 
						|
int killsignal = 0;
 | 
						|
int pidfile_fd;
 | 
						|
int varlogfile_fd;
 | 
						|
int uid = 0; int gid = 0;
 | 
						|
struct passwd *creds;
 | 
						|
 | 
						|
void signal_handler(sig)
 | 
						|
int sig;
 | 
						|
{
 | 
						|
	killsignal = sig;
 | 
						|
	switch(sig) {
 | 
						|
	case SIGCHLD:
 | 
						|
		syslog(LOG_INFO,"sigchild signal caught");
 | 
						|
		break;
 | 
						|
	case SIGHUP:
 | 
						|
		syslog(LOG_INFO,"hangup signal caught");
 | 
						|
		killed = 1;
 | 
						|
		break;
 | 
						|
	case SIGTERM:
 | 
						|
		syslog(LOG_INFO,"terminate signal caught");
 | 
						|
		killed = 1;
 | 
						|
		break;
 | 
						|
	case SIGINT:
 | 
						|
		syslog(LOG_INFO,"keyboard interrupt signal caught");
 | 
						|
		killed = 1;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int daemonize(const char*prog_name)
 | 
						|
{
 | 
						|
 | 
						|
	char str[10];
 | 
						|
	int i;
 | 
						|
	int bufsize=1024; char *buf = malloc(1024);
 | 
						|
	
 | 
						|
	umask( S_IWGRP | S_IROTH | S_IWOTH ); /* set newly created file permissions */
 | 
						|
	
 | 
						|
	/* test logfile */
 | 
						|
	varlogfile_fd=open(varlogfile,O_RDWR|O_CREAT|O_APPEND,0666);
 | 
						|
	if (varlogfile_fd == -1) {
 | 
						|
		snprintf(buf,bufsize,"Could not open output file %s -- exiting",varlogfile); perror(buf);
 | 
						|
		return 1; /* exitvalue */
 | 
						|
	}
 | 
						|
	if (uid != 0) {
 | 
						|
		chown(varlogfile,uid,gid);
 | 
						|
	}
 | 
						|
	close(varlogfile_fd);
 | 
						|
	pidfile_fd=open(pidfile,O_RDWR|O_CREAT,0666);
 | 
						|
	if (pidfile_fd<0) {
 | 
						|
		snprintf(buf,bufsize,"The PID file %s cannot be opened -- exiting",pidfile); perror(buf);
 | 
						|
		return 2; /* exitvalue */
 | 
						|
	}
 | 
						|
	if (lockf(pidfile_fd,F_TEST,0)==1) {
 | 
						|
		snprintf(buf,bufsize,"A daemon is already running (cannot lock PID file %s) -- exiting",pidfile); perror(buf);
 | 
						|
		return 3; /* exitvalue */
 | 
						|
	}
 | 
						|
	close(pidfile_fd);
 | 
						|
	
 | 
						|
	if(getppid()==1) return 0; /* already a daemon */
 | 
						|
	i=fork();
 | 
						|
	if (i < 0) return 4; /* exitvalue */ /* fork error */
 | 
						|
	if (i > 0) exit(0); /* parent exits */
 | 
						|
 | 
						|
	/* child (daemon) continues */
 | 
						|
	setsid(); /* obtain a new process group */
 | 
						|
 | 
						|
	chdir(RUNNING_DIR); /* change running directory */
 | 
						|
	 
 | 
						|
	/* close FDs and reopen to logfile */
 | 
						|
	for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */
 | 
						|
	varlogfile_fd=open(varlogfile,O_RDWR|O_APPEND,0666); dup(varlogfile_fd); dup(varlogfile_fd); /* handle standart I/O */
 | 
						|
	initialize_syslog(prog_name); /* set up syslog */
 | 
						|
	
 | 
						|
	/* PID file */
 | 
						|
	pidfile_fd=open(pidfile,O_RDWR|O_CREAT,0666);
 | 
						|
	if (pidfile_fd<0) {
 | 
						|
		syslog(LOG_ERR,"The PID file %s cannot be opened (%m) -- exiting",pidfile);
 | 
						|
		return 2; /* exitvalue */
 | 
						|
	}
 | 
						|
	if (lockf(pidfile_fd,F_TLOCK,0)<0) {
 | 
						|
		syslog(LOG_ERR,"A daemon is already running -- cannot lock PID file %s (%m) -- exiting",pidfile);
 | 
						|
		return 3; /* exitvalue */
 | 
						|
	}
 | 
						|
 | 
						|
	/* first instance continues */
 | 
						|
	
 | 
						|
	/* record pid to pidfile */
 | 
						|
	sprintf(str,"%d\n",getpid());
 | 
						|
	if (write(pidfile_fd,str,strlen(str)) < strlen(str)) {
 | 
						|
		syslog(LOG_ERR,"Could not write PID into PID file %s (%m) -- exiting",pidfile);
 | 
						|
		return 5; /* exitvalue */
 | 
						|
	}
 | 
						|
	signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
 | 
						|
	signal(SIGTTOU,SIG_IGN);
 | 
						|
	signal(SIGTTIN,SIG_IGN);
 | 
						|
	signal(SIGHUP,signal_handler); /* catch hangup signal */
 | 
						|
	signal(SIGTERM,signal_handler); /* catch kill signal */
 | 
						|
	signal(SIGINT,signal_handler); /* catch keyboard interrupt signal */
 | 
						|
	
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void cleanup() {
 | 
						|
	cleanup_syslog();
 | 
						|
	unlink(pidfile);
 | 
						|
	close(pidfile_fd);
 | 
						|
	close(varlogfile_fd);
 | 
						|
}
 | 
						|
 | 
						|
void usage(char * cmdname) {
 | 
						|
	fprintf (stderr,
 | 
						|
		"Usage: %s [options...] -- <command> [command-specific arguments...]\n"
 | 
						|
		"Daemonize any program.\n"
 | 
						|
		"\n"
 | 
						|
		"Options:\n"
 | 
						|
		"\n"
 | 
						|
		"	-l <logfile>:   log stdout/stderr to this *absolute* path (default "VARLOGFILE")\n"
 | 
						|
		"	-u <username>:   setuid() to this user name before starting the program (default "DEFAULTUSER")\n"
 | 
						|
		"	-p <pidfile>:   lock and write the PID to this *absolute* path (default "PIDFILE")\n"
 | 
						|
		"	-n <progname>:  name the daemon assumes (default "PROGNAME")\n"
 | 
						|
		"	-h: show this usage guide\n"
 | 
						|
		"\n"
 | 
						|
		"Exit status:\n"
 | 
						|
		" 0      if daemonized correctly\n"
 | 
						|
		" other  if an error took place\n"
 | 
						|
		"", cmdname);
 | 
						|
	exit(0);
 | 
						|
}
 | 
						|
 | 
						|
int parse_args(int argc,char ** argv) {
 | 
						|
	int index;
 | 
						|
	int c;
 | 
						|
	
 | 
						|
// 	pidfile = PIDFILE;
 | 
						|
// 	varlogfile = VARLOGFILE;
 | 
						|
// 	progname = PROGNAME;
 | 
						|
	
 | 
						|
	opterr = 0;
 | 
						|
	
 | 
						|
	while ((c = getopt (argc, argv, "l:p:n:u:")) != -1)
 | 
						|
		switch (c)
 | 
						|
		{
 | 
						|
			case 'l':
 | 
						|
				varlogfile = optarg;
 | 
						|
				break;
 | 
						|
			case 'p':
 | 
						|
				pidfile = optarg;
 | 
						|
				break;
 | 
						|
			case 'n':
 | 
						|
				progname = optarg;
 | 
						|
				break;
 | 
						|
			case 'u':
 | 
						|
				if (getuid() != 0) {
 | 
						|
					fprintf (stderr, "-u can only be used by root.\nSee help with -h\n", user);
 | 
						|
					exit(64);
 | 
						|
				}
 | 
						|
				user = optarg;
 | 
						|
				creds = getpwnam(user);
 | 
						|
				if (creds == NULL) {
 | 
						|
					fprintf (stderr, "User %s was not found in the user database.\nSee help with -h\n", user);
 | 
						|
					exit(63);
 | 
						|
				}
 | 
						|
				uid = creds->pw_uid; gid = creds->pw_gid;
 | 
						|
				break;
 | 
						|
// 			case 'h':
 | 
						|
// 				break;
 | 
						|
// 				usage(argv[0]); /* halts after this */
 | 
						|
			case '?':
 | 
						|
				if (optopt == '?' || optopt == 'h')
 | 
						|
					usage(argv[0]); /* halts after this */
 | 
						|
				if (optopt == 'l' || optopt == 'p' || optopt == 'n')
 | 
						|
					fprintf (stderr, "Option -%c requires an argument.\nSee help with -h\n", optopt);
 | 
						|
				else if (isprint (optopt))
 | 
						|
					fprintf (stderr, "Unknown option `-%c'.\nSee help with -h\n", optopt);
 | 
						|
				else
 | 
						|
					fprintf (stderr, "Unknown option character `\\x%x'.\nSee help with -h\n", optopt);
 | 
						|
				exit(64); /* exitvalue */
 | 
						|
			default:
 | 
						|
				abort ();
 | 
						|
		}
 | 
						|
	
 | 
						|
	for (index = optind; index < argc; index++);
 | 
						|
 | 
						|
	if (index == optind) {
 | 
						|
		fprintf (stderr, "You need to specify a command to run.\nSee help with -h\n", optopt);
 | 
						|
		exit(64); /* exitvalue */
 | 
						|
	}
 | 
						|
 | 
						|
	return optind;
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char** argv)
 | 
						|
{
 | 
						|
	/* parse command line arguments, we will use the first non-option one as the starting point */
 | 
						|
	int i;
 | 
						|
	char ** newargv = calloc(argc+1, sizeof(char**));
 | 
						|
	int startat = parse_args(argc,argv);
 | 
						|
	int newargc = argc - startat; 
 | 
						|
	for (i = startat; i < argc; i++) { newargv[i-startat] = argv[i]; }
 | 
						|
 | 
						|
	/* try and daemonize */
 | 
						|
	int daemonret = daemonize(progname);
 | 
						|
	if (daemonret) exit(daemonret);
 | 
						|
	syslog(LOG_INFO,"successfully daemonized");
 | 
						|
 | 
						|
	/* fork */
 | 
						|
	int pid, wpid, status, execret;
 | 
						|
	syslog(LOG_INFO,"starting %s in subprocess",newargv[0]);
 | 
						|
	pid = fork();
 | 
						|
	if (pid < 0) {
 | 
						|
		/* failed to fork, damnit! */
 | 
						|
		syslog(LOG_ERR,"could not fork to run %s as a child process (%m)",newargv[0]);
 | 
						|
		exit(4); /* exitvalue */
 | 
						|
	}
 | 
						|
	else if (pid == 0) {
 | 
						|
		/* child */
 | 
						|
		if (uid != 0) {
 | 
						|
			execret = setgid(gid);
 | 
						|
			if (execret == -1) {
 | 
						|
				syslog(LOG_ERR,"could not setgid() to gid %d",gid);
 | 
						|
				exit(8); /* exitvalue */
 | 
						|
			}
 | 
						|
			execret = setuid(uid);
 | 
						|
			if (execret == -1) {
 | 
						|
				syslog(LOG_ERR,"could not setuid() to uid %d",uid);
 | 
						|
				exit(8); /* exitvalue */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		execret = execvp(newargv[0],newargv);
 | 
						|
		if (errno == 2) {
 | 
						|
			syslog(LOG_ERR,"could not run program: no such file or directory");
 | 
						|
			exit(127);
 | 
						|
		}
 | 
						|
		if (errno == 13) {
 | 
						|
			syslog(LOG_ERR,"could not run program: permission denied");
 | 
						|
			exit(126);
 | 
						|
		}
 | 
						|
		syslog(LOG_ERR,"could not run program: unknown reason");
 | 
						|
		exit(255);
 | 
						|
	}
 | 
						|
 | 
						|
	/* parent continues here */
 | 
						|
	syslog(LOG_INFO,"successfully started subprocess -- PID %d",pid);
 | 
						|
	int finalexit = 0;
 | 
						|
	int waitret = 0;
 | 
						|
	while (1) {
 | 
						|
		if (killed) {
 | 
						|
			kill(pid,killsignal);
 | 
						|
			killed = 0;
 | 
						|
		}
 | 
						|
		waitret = waitpid(pid,&status,WNOHANG);
 | 
						|
		if (waitret == pid) break;
 | 
						|
		usleep(250000);
 | 
						|
	}
 | 
						|
	
 | 
						|
	
 | 
						|
	if WIFEXITED(status) {
 | 
						|
		switch (WEXITSTATUS(status)) {
 | 
						|
			case 0:
 | 
						|
				syslog(LOG_INFO,"%s exited normally",newargv[0]);
 | 
						|
				break;
 | 
						|
			case 126:
 | 
						|
				syslog(LOG_ERR,"%s: permission denied",newargv[0]);
 | 
						|
				finalexit = 126; /* exitvalue */
 | 
						|
				break;
 | 
						|
			case 127:
 | 
						|
				syslog(LOG_ERR,"%s: command not found",newargv[0]);
 | 
						|
				finalexit = 127; /* exitvalue */
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				syslog(LOG_INFO,"%s exited abnormally with status %d",newargv[0],WEXITSTATUS(status));
 | 
						|
				finalexit = 6; /* exitvalue */
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if WIFSIGNALED(status) {
 | 
						|
		syslog(LOG_INFO,"%s was killed with signal %d",newargv[0],WTERMSIG(status));
 | 
						|
		finalexit = 7; /* exitvalue */
 | 
						|
	}
 | 
						|
 | 
						|
	syslog(LOG_INFO,"shutting down");
 | 
						|
	cleanup();
 | 
						|
	exit(finalexit);
 | 
						|
}
 | 
						|
 | 
						|
/* EOF */
 | 
						|
 |