mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +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 */
|
|
|