diff --git a/README.md b/README.md index 7ae9412..0dd43a4 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ Optional arguments: -a, --auth pass Simple text password (only in VRRPv2) -f, --foreground Execute uvrrpd in foreground -s, --script Path of hook script (default /etc/uvrrpd/uvrrpd-switch.sh) + -F --pidfile Create pid file 'name + Default /var/run/uvrrp_${vrid}.pid -d, --debug -h, --help ``` diff --git a/uvrrpd.c b/uvrrpd.c index 84600fb..9664f67 100644 --- a/uvrrpd.c +++ b/uvrrpd.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "uvrrpd.h" #include "vrrp.h" @@ -35,12 +36,19 @@ #include "log.h" +/* global constants */ unsigned long reg = 0UL; int background = 1; char *loglevel = NULL; +char *pidfile_name = NULL; +/* local methods */ static void signal_handler(int sig); static void signal_setup(void); +static int pidfile_init(int vrid); +static void pidfile_unlink(void); +static void pidfile_check(int vrid); +static void pidfile(int vrid); /** * main() - entry point @@ -64,6 +72,12 @@ int main(int argc, char *argv[]) if (! !vrrp_options(&vrrp, &vnet, argc, argv)) exit(EXIT_FAILURE); + /* pidfile init && check */ + if (pidfile_init(vrrp.vrid) != 0) + exit(EXIT_FAILURE); + + pidfile_check(vrrp.vrid); + /* logs */ log_open("uvrrpd", (char const *) loglevel); @@ -96,6 +110,10 @@ int main(int argc, char *argv[]) else chdir("/"); + + /* pidfile */ + pidfile(vrrp.vrid); + /* process */ set_bit(KEEP_GOING, ®); while (test_bit(KEEP_GOING, ®) && !vrrp_process(&vrrp, &vnet)); @@ -105,7 +123,7 @@ int main(int argc, char *argv[]) if (vnet.family == AF_INET) vrrp_arp_cleanup(&vnet); - else /* AF_INET6 */ + else /* AF_INET6 */ vrrp_na_cleanup(&vnet); vrrp_cleanup(&vrrp); @@ -114,6 +132,8 @@ int main(int argc, char *argv[]) log_close(); free(loglevel); + pidfile_unlink(); + free(pidfile_name); return EXIT_SUCCESS; } @@ -201,3 +221,119 @@ static void signal_setup(void) sigprocmask(SIG_BLOCK, &sa.sa_mask, NULL); } + +/** + * pidfile_init() + */ +static int pidfile_init(int vrid) +{ + int max_len = NAME_MAX + PATH_MAX; + if (pidfile_name == NULL) { + pidfile_name = malloc(max_len); + if (pidfile_name == NULL) { + log_error("vrid %d :: malloc - %m", vrid); + return -1; + } + + snprintf(pidfile_name, max_len, PIDFILE_NAME, vrid); + } + + return 0; +} + +/** + * pidfile_unlink() - remove pidfile + */ +static void pidfile_unlink(void) +{ + if (pidfile_name) + unlink(pidfile_name); +} + +/** + * pidfile_check() + */ +static void pidfile_check(int vrid) +{ + struct flock fl; + int err, fd; + + fd = open(pidfile_name, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + if (errno == ENOENT) + return; + fprintf(stderr, "vrid %d :: error opening PID file %s: %m\n", + vrid, pidfile_name); + exit(EXIT_FAILURE); + } + + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + err = fcntl(fd, F_GETLK, &fl); + close(fd); + if (err < 0) { + fprintf(stderr, "vrid %d :: error getting PID file %s lock: %m", + vrid, pidfile_name); + exit(EXIT_FAILURE); + } + + if (fl.l_type == F_UNLCK) + return; + + fprintf(stderr, "vrid %d :: uvrrpd is already running (pid %d)\n", vrid, + (int) fl.l_pid); + exit(EXIT_FAILURE); +} + +/** + * pid_file() + */ +static void pidfile(int vrid) +{ + struct flock fl; + char buf[16]; + int err, fd; + + fd = open(pidfile_name, + O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (fd < 0) { + log_error("vrid %d :: error opening PID file %s: %m\n", vrid, + pidfile_name); + exit(EXIT_FAILURE); + } + + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + err = fcntl(fd, F_SETLK, &fl); + if (err < 0) { + if (errno == EACCES || errno == EAGAIN) { + log_error("vrid %d :: uvrrpd is already running\n", + vrid); + exit(EXIT_FAILURE); + } + log_error("vrid %d :: error setting PID file %s lock: %m\n", + vrid, pidfile_name); + exit(EXIT_FAILURE); + } + + atexit(pidfile_unlink); + + err = snprintf(buf, sizeof(buf), "%d\n", (int) getpid()); + if (err < 0) { + perror("snprintf"); + exit(EXIT_FAILURE); + } + + err = write(fd, buf, err); + if (err < 0) { + log_error("vrid %d :: error writing PID to PID file %s: %m\n", + vrid, pidfile_name); + exit(EXIT_FAILURE); + } +} diff --git a/uvrrpd.h b/uvrrpd.h index ca8ea73..901d9ad 100644 --- a/uvrrpd.h +++ b/uvrrpd.h @@ -24,6 +24,8 @@ #include "bits.h" +#define PIDFILE_NAME "/var/run/uvrrpd_%d.pid" + /** * uvrrpd_control * Enum server control register flags diff --git a/vrrp_options.c b/vrrp_options.c index b20979f..1e64074 100644 --- a/vrrp_options.c +++ b/vrrp_options.c @@ -33,6 +33,7 @@ /* from uvrrpd.c */ extern int background; extern char *loglevel; +extern char *pidfile_name; /** * vrrp_usage() @@ -58,9 +59,8 @@ static void vrrp_usage(void) " -a, --auth pass Simple text password (only in VRRPv2)\n" " -f, --foreground Execute uvrrpd in foreground\n" " -s, --script Path of hook script (default /etc/uvrrpd/uvrrpd-switch.sh)\n" -#if 0 /* todo */ - " --pidfile name Create pid file 'name'\n" -#endif + " -F --pidfile name Create pid file 'name'\n" + " Default /var/run/uvrrp_${vrid}.pid\n" " -d, --debug\n" " -h, --help\n"); } @@ -84,13 +84,14 @@ int vrrp_options(struct vrrp *vrrp, struct vrrp_net *vnet, int argc, {"auth", required_argument, 0, 'a'}, {"foreground", no_argument, 0, 'f'}, {"script", required_argument, 0, 's'}, + {"pidfile", required_argument, 0, 'F'}, {"debug", no_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {NULL, 0, 0, 0} }; while ((optc = - getopt_long(argc, argv, "v:i:p:t:P:r:6a:fs:dh", opts, + getopt_long(argc, argv, "v:i:p:t:P:r:6a:fs:F:dh", opts, NULL)) != EOF) { switch (optc) { @@ -193,6 +194,11 @@ int vrrp_options(struct vrrp *vrrp, struct vrrp_net *vnet, int argc, } break; + /* pidfile */ + case 'F': + pidfile_name = strndup(optarg, NAME_MAX + PATH_MAX); + break; + /* debug */ case 'd': loglevel = strndup("debug", 6);