From 183022ebe465cfa9f910f05a4301fa24139691a9 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Wed, 18 Feb 2015 16:53:57 +0100 Subject: [PATCH] Initial support for RFC 868 (rdate), internal inetd service. Signed-off-by: Joachim Nilsson --- plugin.h | 8 ++++- plugins/Makefile | 2 +- plugins/time.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ svc.c | 36 +++++++++++++++----- svc.h | 1 + 5 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 plugins/time.c diff --git a/plugin.h b/plugin.h index fa0a9d1b..4f685e3c 100644 --- a/plugin.h +++ b/plugin.h @@ -83,7 +83,7 @@ typedef struct plugin { uev_t watcher; /* Plugin name, defaults to basename of plugin path if unset. - * NOTE: Must be same as @cmd for service plugins! */ + * NOTE: Must match cmd for services or inetd plugins! */ char *name; /* Service callback to be called once per lap of runloop. */ @@ -106,6 +106,12 @@ typedef struct plugin { void (*cb)(void *arg, int fd, int events); } io; + /* Inetd Plugin, stdio used as client socket. + * @type argument will be either SOCK_DGRAM or SOCK_STREAM */ + struct { + int (*cmd)(int type); + } inetd; + char *depends[PLUGIN_DEP_MAX]; /* List of other .name's this depends on. */ } plugin_t; diff --git a/plugins/Makefile b/plugins/Makefile index 0cabbddb..9f5294b4 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -4,7 +4,7 @@ CPPFLAGS += -I.. LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS)) LDFLAGS += -shared PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \ - resolvconf.so urandom.so x11-common.so tty.so + resolvconf.so urandom.so x11-common.so tty.so time.so DEPS = $(addprefix .,$(PLUGINS:.so=.d)) TOPDIR ?= $(shell pwd) diff --git a/plugins/time.c b/plugins/time.c new file mode 100644 index 00000000..56c27e98 --- /dev/null +++ b/plugins/time.c @@ -0,0 +1,87 @@ +/* Optional inetd plugin for time (rdate) protocol RFC 868 + * + * Copyright (c) 2015 Joachim Nilsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include /* STDIN_FILENO */ +#include + +#include "plugin.h" + +/* UNIX epoch starts midnight, 1st Jan, 1970 */ +#define EPOCH_OFFSET 2208988800ULL + +/* Return number of seconds since midnight, 1st Jan, 1900 */ +static int rfc868(int type) +{ + int sd = STDIN_FILENO; + time_t now; + struct sockaddr_storage sa; + socklen_t sa_len = sizeof(sa); + + if (SOCK_DGRAM == type) { + int dummy; + + /* Read empty datagram in UDP mode before sending reply. */ + if (-1 == recvfrom(sd, &dummy, sizeof(dummy), MSG_DONTWAIT, (struct sockaddr *)&sa, &sa_len)) + return -1; /* On error, close connection. */ + } + + now = time(NULL); + if ((time_t)-1 == now) + return -1; /* On error, close connection. */ + + /* Account for UNIX epoch offset */ + now += EPOCH_OFFSET; + + /* Convert to network byte order, not explicitly stated in RFC */ + now = htonl(now); + + /* Send reply, ignore error, simply close connection. */ + return sendto(sd, &now, sizeof(now), MSG_DONTWAIT, (struct sockaddr *)&sa, sa_len); +} + +static plugin_t plugin = { + .name = "time", + .inetd = { + .cmd = rfc868 + } +}; + +PLUGIN_INIT(plugin_init) +{ + plugin_register(&plugin); +} + +PLUGIN_EXIT(plugin_exit) +{ + plugin_unregister(&plugin); +} + +/** + * Local Variables: + * version-control: t + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/svc.c b/svc.c index fd55341f..ca1d4b6e 100644 --- a/svc.c +++ b/svc.c @@ -265,6 +265,7 @@ int svc_register(int type, char *line, char *username) char *service = NULL, *proto = NULL; char *cmd, *desc, *runlevels = NULL; svc_t *svc; + plugin_t *plugin = NULL; struct servent *sv = NULL; struct protoent *pv = NULL; @@ -322,6 +323,15 @@ int svc_register(int type, char *line, char *username) _d("Got service %s:%d proto %s:%d", service, ntohs(sv->s_port), sv->s_proto, pv->p_proto); } + /* Find plugin that provides a callback for this inetd service */ + if (type == SVC_CMD_INETD && !strncasecmp(cmd, "internal", 8)) { + plugin = plugin_find(service); + if (!plugin || !plugin->inetd.cmd) { + _e("Inetd service %s has no internal plugin, skipping.", service); + return errno = ENOENT; + } + } + svc = svc_new(); if (!svc) { _e("Out of memory, cannot register service %s", cmd); @@ -357,12 +367,18 @@ int svc_register(int type, char *line, char *username) svc->forking = forking; } - strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); + if (plugin) { + /* Internal plugin provides this service */ + svc->internal_cmd = plugin->inetd.cmd; + } else { + /* External program to call */ + strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); - strlcpy(svc->args[i++], cmd, sizeof(svc->args[0])); - while ((cmd = strtok(NULL, " "))) strlcpy(svc->args[i++], cmd, sizeof(svc->args[0])); - svc->args[i][0] = 0; + while ((cmd = strtok(NULL, " "))) + strlcpy(svc->args[i++], cmd, sizeof(svc->args[0])); + svc->args[i][0] = 0; + } svc->runlevels = parse_runlevels(runlevels); _d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels); @@ -527,7 +543,7 @@ int svc_start(svc_t *svc) respawn = svc->pid != 0; /* Don't try and start service if it doesn't exist. */ - if (!fexist(svc->cmd)) { + if (!fexist(svc->cmd) && !svc->internal_cmd) { char msg[80]; snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd); @@ -579,6 +595,7 @@ int svc_start(svc_t *svc) sigprocmask(SIG_SETMASK, &omask, NULL); if (pid == 0) { int i = 0; + int status; int uid = getuser(svc->username); struct sigaction sa; char *args[MAX_NUM_SVC_ARGS]; @@ -628,11 +645,12 @@ int svc_start(svc_t *svc) _e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf); } - /* XXX: Maybe change to use execve() to be able to launch scripts? */ - execv(svc->cmd, args); + if (svc->internal_cmd) + status = svc->internal_cmd(svc->sock_type); + else + status = execv(svc->cmd, args); /* XXX: Maybe use execve() to be able to launch scripts? */ - /* Only reach this point if exec() fails. */ - exit(0); + exit(status); } svc->pid = pid; diff --git a/svc.h b/svc.h index 58f6b1f6..83afceb8 100644 --- a/svc.h +++ b/svc.h @@ -73,6 +73,7 @@ typedef struct svc { int proto; int forking; char service[10]; + int (*internal_cmd)(int sock_type); /* Incremented for each restart by service monitor. */ unsigned int restart_counter;