Browse files

Fix TODO: Support for reloading/restarting server config on SIGHUP.

  • Loading branch information...
1 parent d446cff commit 279ad9225cf6f4fc4a6745a580303d84e885eac1 @troglobit committed Nov 2, 2011
Showing with 302 additions and 92 deletions.
  1. +1 −7 TODO
  2. +27 −1 doc/smcroute.8
  3. +1 −1 src/Makefile.in
  4. +2 −0 src/ifvc.c
  5. +12 −10 src/mcgroup.c
  6. +5 −0 src/mclab.h
  7. +70 −0 src/mroute-api.c
  8. +105 −0 src/pidfile.c
  9. +79 −73 src/smcroute.c
View
8 TODO
@@ -1,10 +1,4 @@
-* Support for reloading/restarting server config on SIGHUP
-Optimal behavior would be to handle this without losing any traffic
-over established/active routing rules. This is however not required,
-like in link up/down below...
-
-* Support for graceful shutdown of server on SIGTERM/SIGQUIT
-Maybe not necessary, kernel does all cleanup anyway?
+ -*-org-*-
* Support for detecting link up/down on interfaces
Also requires updating VIF/MIFs and mroutes accordingly.
View
28 doc/smcroute.8
@@ -223,10 +223,36 @@ More than 200
Max. 20
.El
.Pp
+.Sh SIGNALS
+.Nm
+responds to the following signals:
+.Pp
+.Bl -tag -width TERM -compact
+.It HUP
+Restarts
+.Nm .
+The configuration file is reread every time this signal is received.
+.It INT
+Terminates execution gracefully.
+.It TERM
+The same as INT.
+.El
+.Pp
+For convenience in sending signals,
+.Nm
+writes its process ID to
+.Pa /var/run/smcroute.pid
+upon startup.
+.Pp
.Sh FILES
.Bl -tag -width /proc/net/ip6_mr_cache -compact
.It Pa /etc/smcroute.conf
-Routes to be added/restored when starting, or restarting the daemon on SIGHUP.
+Routes to be added/restored when starting, or restarting the daemon on
+SIGHUP.
+.It Pa /var/run/smcroute.pid
+Pidfile (re)created by
+.Nm
+daemon when it has started up and is ready to receive commands.
.It Pa /proc/net/ip_mr_cache
Holds active IPv4 multicast routes.
.It Pa /proc/net/ip_mr_vif
View
2 src/Makefile.in
@@ -23,7 +23,7 @@ install:
mcsender: mcsender.o syslog.o config.h
-smcroute: smcroute.o mroute-api.o ifvc.o cmdpkt.o ipc.o syslog.o udpsock.o mcgroup.o parse-conf.o
+smcroute: smcroute.o mroute-api.o ifvc.o cmdpkt.o ipc.o syslog.o udpsock.o mcgroup.o parse-conf.o pidfile.o
smcroute.c: build.h config.h
build.h:
View
2 src/ifvc.c
@@ -45,6 +45,7 @@ void iface_init(void)
struct iface *iface;
struct ifaddrs *ifaddr, *ifa;
+ num_ifaces = 0;
memset(iface_list, 0, sizeof(iface_list));
if (getifaddrs(&ifaddr) == -1) {
@@ -59,6 +60,7 @@ void iface_init(void)
/* Skip non-IPv4 and non-IPv6 interfaces */
if ((family != AF_INET) && (family != AF_INET6))
continue;
+
/* Skip interface without internet address */
if (ifa->ifa_addr == NULL)
continue;
View
22 src/mcgroup.c
@@ -110,10 +110,11 @@ static void mcgroup6_init(void)
static int mcgroup_join_leave_ipv4(int sd, int cmd, const char *ifname, struct in_addr group)
{
+ int joinleave = cmd == 'j' ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
+ char buf[INET_ADDRSTRLEN];
+ const char *command = cmd == 'j' ? "Join" : "Leave";
struct ip_mreq mreq;
struct iface *iface = iface_find_by_name(ifname);
- const char *command = cmd == 'j' ? "Join" : "Leave";
- char buf[INET_ADDRSTRLEN];
if (!iface) {
smclog(LOG_WARNING, 0, "%s multicast group, unknown interface %s", command, ifname);
@@ -125,9 +126,9 @@ static int mcgroup_join_leave_ipv4(int sd, int cmd, const char *ifname, struct i
mreq.imr_multiaddr.s_addr = group.s_addr;
mreq.imr_interface.s_addr = iface->inaddr.s_addr;
- if (setsockopt(sd, IPPROTO_IP, cmd == 'j' ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP,
- (void *)&mreq, sizeof(mreq))) {
- smclog(LOG_WARNING, errno, "%s MEMBERSHIP failed", cmd == 'j' ? "ADD" : "DROP");
+ if (setsockopt(sd, IPPROTO_IP, joinleave, (void *)&mreq, sizeof(mreq))) {
+ if (EADDRINUSE != errno)
+ smclog(LOG_WARNING, errno, "%s MEMBERSHIP failed", cmd == 'j' ? "ADD" : "DROP");
return 1;
}
@@ -139,10 +140,11 @@ static int mcgroup_join_leave_ipv6(int sd, int cmd, const char *ifname, struct i
#ifndef HAVE_IPV6_MULTICAST_HOST
return 0;
#else
+ int joinleave = cmd == 'j' ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
+ char buf[INET6_ADDRSTRLEN];
+ const char *command = cmd == 'j' ? "Join" : "Leave";
struct ipv6_mreq mreq;
struct iface *iface = iface_find_by_name(ifname);
- const char *command = cmd == 'j' ? "Join" : "Leave";
- char buf[INET6_ADDRSTRLEN];
if (!iface) {
smclog(LOG_WARNING, 0, "%s multicast group, unknown interface %s", command, ifname);
@@ -154,9 +156,9 @@ static int mcgroup_join_leave_ipv6(int sd, int cmd, const char *ifname, struct i
mreq.ipv6mr_multiaddr = group;
mreq.ipv6mr_interface = iface->ifindex;
- if (setsockopt(sd, IPPROTO_IPV6, cmd == 'j' ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP,
- (void *)&mreq, sizeof(mreq))) {
- smclog(LOG_WARNING, errno, "%s MEMBERSHIP failed", cmd == 'j' ? "ADD" : "DROP");
+ if (setsockopt(sd, IPPROTO_IPV6, joinleave, (void *)&mreq, sizeof(mreq))) {
+ if (EADDRINUSE != errno)
+ smclog(LOG_WARNING, errno, "%s MEMBERSHIP failed", cmd == 'j' ? "ADD" : "DROP");
return 1;
}
View
5 src/mclab.h
@@ -158,12 +158,14 @@ extern int mroute4_socket;
*/
extern int mroute6_socket;
+int mroute4_init (void);
int mroute4_enable (void);
void mroute4_disable (void);
int mroute4_add (struct mroute4 *mroute);
int mroute4_del (struct mroute4 *mroute);
void mroute4_add_vif (struct iface *iface);
+int mroute6_init (void);
int mroute6_enable (void);
void mroute6_disable (void);
int mroute6_add (struct mroute6 *mroute);
@@ -229,6 +231,9 @@ static inline int IN6_MULTICAST(const struct in6_addr *addr)
int parse_conf_file(const char *file);
+/* pidfile.c */
+int pidfile(const char *basename);
+
/**
* Local Variables:
* version-control: t
View
70 src/mroute-api.c
@@ -75,6 +75,41 @@ static struct mif {
* IPv4
*******************************************************************/
+/* Inits the necessary resources for IPv4 MRouter. */
+int mroute4_init(void)
+{
+ int code;
+ unsigned i;
+ struct iface *iface;
+
+ memset(vif_list, 0, sizeof(vif_list));
+
+ code = mroute4_enable();
+ switch (code) {
+ case 0:
+ break;
+ case EADDRINUSE:
+ smclog(LOG_INIT, EADDRINUSE, "MC-Router IPv4 API already in use");
+ return -1;
+#ifdef EOPNOTSUPP
+ case EOPNOTSUPP:
+#endif
+ case ENOPROTOOPT:
+ smclog(LOG_WARNING, 0, "Kernel does not support IPv4 multicast routing, skipping...");
+ return -1;
+ default:
+ smclog(LOG_INIT, code, "MRT_INIT failed");
+ return -1;
+ }
+
+ /* create VIFs for all IP, non-loop interfaces */
+ for (i = 0; (iface = iface_find_by_index(i)); i++)
+ if (iface->inaddr.s_addr && !(iface->flags & IFF_LOOPBACK))
+ mroute4_add_vif(iface);
+
+ return 0;
+}
+
/*
** Initialises the mrouted API and locks it by this exclusively.
**
@@ -255,6 +290,41 @@ static int proc_set_val (char *file, int val)
return 0;
}
+/* Inits the necessary resources for IPv6 MRouter. */
+int mroute6_init(void)
+{
+ int code;
+ unsigned i;
+ struct iface *iface;
+
+ memset(mif_list, 0, sizeof(vif_list));
+
+ code = mroute6_enable();
+ switch (code) {
+ case 0:
+ break;
+ case EADDRINUSE:
+ smclog(LOG_INIT, EADDRINUSE, "MC-Router IPv6 API already in use");
+ return -1;
+#ifdef EOPNOTSUPP
+ case EOPNOTSUPP:
+#endif
+ case ENOPROTOOPT:
+ smclog(LOG_WARNING, 0, "Kernel does not support IPv6 multicast routing, skipping...");
+ return -1;
+ default:
+ smclog(LOG_INIT, code, "MRT6_INIT failed");
+ return -1;
+ }
+
+ /* create MIFs for all IP, non-loop interfaces */
+ for (i = 0; (iface = iface_find_by_index(i)); i++)
+ if (iface->inaddr.s_addr && !(iface->flags & IFF_LOOPBACK))
+ mroute6_add_mif(iface);
+
+ return 0;
+}
+
/*
** Initialises the mrouted API and locks it by this exclusively.
**
View
105 src/pidfile.c
@@ -0,0 +1,105 @@
+/* $OpenBSD: pidfile.c,v 1.8 2008/06/26 05:42:05 ray Exp $ */
+/* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static char *pidfile_path;
+static pid_t pidfile_pid;
+
+static void pidfile_cleanup(void);
+
+extern char *__progname;
+
+int
+pidfile(const char *basename)
+{
+ int save_errno, result;
+ pid_t pid;
+ FILE *f;
+
+ if (basename == NULL)
+ basename = __progname;
+
+ if (pidfile_path != NULL) {
+ free(pidfile_path);
+ pidfile_path = NULL;
+ }
+
+ /* _PATH_VARRUN includes trailing / */
+ result = asprintf(&pidfile_path, "%s%s.pid", _PATH_VARRUN, basename);
+ if (result == -1 || pidfile_path == NULL)
+ return (-1);
+
+ if ((f = fopen(pidfile_path, "w")) == NULL) {
+ save_errno = errno;
+ free(pidfile_path);
+ pidfile_path = NULL;
+ errno = save_errno;
+ return (-1);
+ }
+
+ pid = getpid();
+ if (fprintf(f, "%ld\n", (long)pid) <= 0 || fclose(f) != 0) {
+ save_errno = errno;
+ (void) unlink(pidfile_path);
+ free(pidfile_path);
+ pidfile_path = NULL;
+ errno = save_errno;
+ return (-1);
+ }
+
+ pidfile_pid = pid;
+ if (atexit(pidfile_cleanup) < 0) {
+ save_errno = errno;
+ (void) unlink(pidfile_path);
+ free(pidfile_path);
+ pidfile_path = NULL;
+ pidfile_pid = 0;
+ errno = save_errno;
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+pidfile_cleanup(void)
+{
+
+ if (pidfile_path != NULL && pidfile_pid == getpid())
+ (void) unlink(pidfile_path);
+}
View
152 src/smcroute.c
@@ -33,6 +33,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <signal.h>
#include <unistd.h>
#include "mclab.h"
@@ -75,9 +76,43 @@ static const char usage_info[] =
" -j <IFNAME> <MULTICAST-GROUP>\n"
" -l <IFNAME> <MULTICAST-GROUP>\n";
+static int sighandled = 0;
+#define GOT_SIGINT 0x01
+#define GOT_SIGHUP 0x02
+
int do_debug_logging = 0;
/*
+ * Signal handler. Take note of the fact that the signal arrived
+ * so that the main loop can take care of it.
+ */
+static void handler(int sig)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ sighandled |= GOT_SIGINT;
+ break;
+
+ case SIGHUP:
+ sighandled |= GOT_SIGHUP;
+ break;
+ }
+}
+
+static void signal_init(void)
+{
+ struct sigaction sa;
+
+ sa.sa_handler = handler;
+ sa.sa_flags = 0; /* Interrupt system calls */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+}
+
+/*
** Counts the number of arguments belonging to an option. Option is any argument
** begining with a '-'.
**
@@ -103,6 +138,17 @@ static int num_option_arguments(const char *argv[])
return ptr - argv;
}
+/* Parse .conf file and setup routes */
+static void read_conf_file(const char *conf_file)
+{
+ if (access(conf_file, R_OK)) {
+ smclog(LOG_WARNING, errno, "Failed loading %s", conf_file);
+ } else {
+ if (parse_conf_file(conf_file))
+ smclog(LOG_WARNING, errno, "Failed reading %s", conf_file);
+ }
+}
+
/* Cleans up, i.e. releases allocated resources. Called via atexit() */
static void clean(void)
{
@@ -112,70 +158,17 @@ static void clean(void)
ipc_exit();
}
-/* Inits the necessary resources for IPv4 MRouter. */
-static int mroute4_init(void)
+static void restart(void)
{
- int code;
- unsigned i;
- struct iface *iface;
-
- code = mroute4_enable();
- switch (code) {
- case 0:
- break;
- case EADDRINUSE:
- smclog(LOG_INIT, EADDRINUSE, "MC-Router IPv4 API already in use");
- return -1;
-#ifdef EOPNOTSUPP
- case EOPNOTSUPP:
-#endif
- case ENOPROTOOPT:
- smclog(LOG_WARNING, 0, "Kernel does not support IPv4 multicast routing, skipping...");
- return -1;
- default:
- smclog(LOG_INIT, code, "MRT_INIT failed");
- return -1;
- }
-
- /* create VIFs for all IP, non-loop interfaces */
- for (i = 0; (iface = iface_find_by_index(i)); i++)
- if (iface->inaddr.s_addr && !(iface->flags & IFF_LOOPBACK))
- mroute4_add_vif(iface);
-
- return 0;
-}
-
-/* Inits the necessary resources for IPv6 MRouter. */
-static int mroute6_init(void)
-{
- int code;
- unsigned i;
- struct iface *iface;
-
- code = mroute6_enable();
- switch (code) {
- case 0:
- break;
- case EADDRINUSE:
- smclog(LOG_INIT, EADDRINUSE, "MC-Router IPv6 API already in use");
- return -1;
-#ifdef EOPNOTSUPP
- case EOPNOTSUPP:
-#endif
- case ENOPROTOOPT:
- smclog(LOG_WARNING, 0, "Kernel does not support IPv6 multicast routing, skipping...");
- return -1;
- default:
- smclog(LOG_INIT, code, "MRT6_INIT failed");
- return -1;
- }
-
- /* create MIFs for all IP, non-loop interfaces */
- for (i = 0; (iface = iface_find_by_index(i)); i++)
- if (iface->inaddr.s_addr && !(iface->flags & IFF_LOOPBACK))
- mroute6_add_mif(iface);
+ smclog(LOG_DEBUG, 0, "Restart handler called");
+ mroute4_disable();
+ mroute6_disable();
+ /* No need to close the IPC, only at cleanup. */
- return 0;
+ /* Update list of interfaces and create new virtual interface mappings in kernel. */
+ iface_init();
+ mroute4_init();
+ mroute6_init();
}
static int daemonize(void)
@@ -198,7 +191,7 @@ static int daemonize(void)
static void server_loop(int sd, const char *conf_file)
{
- int result;
+ int result, once = 1;
uint8 buf[MX_CMDPKT_SZ];
fd_set fds;
#ifdef HAVE_IPV6_MULTICAST_ROUTING
@@ -210,17 +203,28 @@ static void server_loop(int sd, const char *conf_file)
struct cmd *packet;
struct mroute mroute;
- /* Parse .conf file and setup routes */
- if (access(conf_file, R_OK)) {
- smclog(LOG_WARNING, errno, "Configuration file %s", conf_file);
- } else {
- smclog(LOG_NOTICE, 0, "Using configuration file %s", conf_file);
- if (parse_conf_file(conf_file))
- smclog(LOG_WARNING, errno, "Failed reading file %s", conf_file);
- }
+ smclog(LOG_NOTICE, 0, "Attempting to load %s", conf_file);
+ read_conf_file (conf_file);
+
+ /* Ready for input, tell clients that by creating the pidfile */
+ if (pidfile(NULL))
+ smclog(LOG_WARNING, errno, "Failed creating pidfile");
/* Watch the MRouter and the IPC socket to the smcroute client */
while (1) {
+ if (sighandled) {
+ if (sighandled & GOT_SIGINT) {
+ sighandled &= ~GOT_SIGINT;
+ break;
+ }
+ if (sighandled & GOT_SIGHUP) {
+ sighandled &= ~GOT_SIGHUP;
+ restart();
+ smclog(LOG_NOTICE, 0, "Got SIGHUP, reloading %s ...", conf_file);
+ read_conf_file (conf_file);
+ }
+ }
+
FD_ZERO(&fds);
FD_SET(sd, &fds);
FD_SET(mroute4_socket, &fds);
@@ -232,8 +236,9 @@ static void server_loop(int sd, const char *conf_file)
/* wait for input */
result = select(max_fd_num + 1, &fds, NULL, NULL, NULL);
if (result <= 0) {
- /* log and ignore failures */
- smclog(LOG_WARNING, errno, "select() failure");
+ /* Log all errors, except when signalled, ignore failures. */
+ if (EINTR != errno)
+ smclog(LOG_WARNING, errno, "select() failure");
continue;
}
@@ -388,6 +393,7 @@ static void start_server(int background, const char *conf_file)
if (!pid) {
smclog(LOG_NOTICE, 0, "Entering smcroute daemon main loop.");
atexit(clean);
+ signal_init();
server_loop(sd, conf_file);
}
}

0 comments on commit 279ad92

Please sign in to comment.