Fix #101: remove built-in inetd from finit

This patch removes the built-in inetd support from Finit.  We recommend
using an external inetd instead, e.g. xinetd.

If you liked the feature set our inetd provided; filtering per interface
and port redirection, then please let us know or use the code in this
patch (MIT licensed) to recreate it.  We are open to reintroducing it,
but then as a stand-alone daemon like the bundled watchdogd and getty.

So long for now, old friend.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2021-02-11 12:46:40 +01:00
1 parent 673a9a55a2
commit e88a9b14ff
19 files changed
+60 -1835

No files matched your search

+1 -4
View File
@@ -39,10 +39,7 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
tty.c tty.h \
util.c util.h \
utmp-api.c utmp-api.h
pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h log.h plugin.h svc.h
if INETD
finit_SOURCES += inetd.c inetd.h
endif
pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
finit_CFLAGS += $(lite_CFLAGS) $(uev_CFLAGS)
-21
View File
@@ -158,19 +158,6 @@ static svc_t *do_find(char *buf, size_t len)
return svc_find_by_nameid(input, id);
}
#ifdef INETD_ENABLED
static int do_query_inetd(char *buf, size_t len)
{
svc_t *svc;
svc = do_find(buf, len);
if (!svc || !svc_is_inetd(svc))
return 1;
return inetd_filter_str(&svc->inetd, buf, len);
}
#endif /* INETD_ENABLED */
typedef struct {
char *event;
void (*cb)(void);
@@ -291,14 +278,6 @@ static void api_cb(uev_t *w, void *arg, int events)
result = do_restart(rq.data, sizeof(rq.data));
break;
#ifdef INETD_ENABLED
case INIT_CMD_QUERY_INETD:
_d("query inetd");
strterm(rq.data, sizeof(rq.data));
result = do_query_inetd(rq.data, sizeof(rq.data));
break;
#endif
case INIT_CMD_GET_RUNLEVEL:
_d("get runlevel");
rq.runlevel = runlevel;
+2 -12
View File
@@ -500,16 +500,6 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
return;
}
/* Classic inetd service */
if (MATCH_CMD(line, "inetd ", x)) {
#ifdef INETD_ENABLED
service_register(SVC_TYPE_INETD, x, rlimit, file);
#else
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
#endif
return;
}
/* Read resource limits */
if (MATCH_CMD(line, "rlimit ", x)) {
conf_parse_rlimit(x, rlimit);
@@ -574,8 +564,8 @@ static int parse_conf(char *file)
/*
* Get current global limits, which may be overridden from both
* finit.conf, for Finit and its services like inetd+getty, and
* *.conf in finit.d/, for each service(s) listed there.
* finit.conf, for Finit and its services like getty+watchdogd,
* and *.conf in finit.d/, for each service(s) listed there.
*/
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
-803
View File
@@ -1,803 +0,0 @@
/* Classic inetd services launcher for Finit
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
*
* 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 <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <uev/uev.h>
#include <lite/lite.h>
#include "finit.h"
#include "inetd.h"
#include "helpers.h"
#include "private.h"
#include "service.h"
#define ENABLE_SOCKOPT(sd, level, opt) \
do { \
int val = 1; \
if (setsockopt(sd, level, opt, &val, sizeof(val)) < 0) \
logit(LOG_CRIT, "Failed enabling %s on %s service", \
#opt, inetd->name); \
} while (0);
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
static int inetd_dgram_peek(int sd, char *ifname, size_t len)
{
struct cmsghdr *cmsg;
struct msghdr msgh;
char cmbuf[0x100];
memset(ifname, 0, len);
memset(&msgh, 0, sizeof(msgh));
msgh.msg_control = cmbuf;
msgh.msg_controllen = sizeof(cmbuf);
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
return -1;
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
char tmp[IF_NAMESIZE + 1] = { 0 };
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
continue;
if_indextoname(ipi->ipi_ifindex, tmp);
strlcpy(ifname, tmp, len);
return 0;
}
return -1;
}
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
static void inetd_dgram_drop(int sd, const char *ifname)
{
char pkt_interface[IF_NAMESIZE + 1];
char buf[BUFSIZ];
while (1) {
inetd_dgram_peek(sd, pkt_interface, sizeof(pkt_interface));
if (string_compare(pkt_interface, ifname)) {
if (recv(sd, buf, sizeof(buf), 0) < 0)
break;
continue;
}
break;
}
}
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
static int inetd_stream_peek(int sd, char *ifname, size_t ilen)
{
struct ifaddrs *ifaddr, *ifa;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
memset(ifname, 0, ilen);
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
return -1;
if (-1 == getifaddrs(&ifaddr))
return -1;
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
size_t len = sizeof(struct in_addr);
struct sockaddr_in *iin;
if (!ifa->ifa_addr)
continue;
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
iin = (struct sockaddr_in *)ifa->ifa_addr;
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
strlcpy(ifname, ifa->ifa_name, ilen);
break;
}
}
freeifaddrs(ifaddr);
return 0;
}
static int get_stdin(svc_t *svc, char *iifname, size_t len)
{
int stdin = svc->inetd.watcher.fd;
char ifname[IF_NAMESIZE + 1] = "UNKNOWN";
memset(iifname, 0, len);
if (svc->inetd.type == SOCK_STREAM) {
/* Open new client socket from server socket */
stdin = accept(stdin, NULL, NULL);
if (stdin < 0) {
logit(LOG_CRIT, "Failed accepting inetd service %d/tcp", svc->inetd.port);
return -1;
}
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
inetd_stream_peek(stdin, ifname, sizeof(ifname));
} else { /* SOCK_DGRAM */
inetd_dgram_peek(stdin, ifname, sizeof(ifname));
}
if (!inetd_is_allowed(&svc->inetd, ifname)) {
logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port);
if (svc->inetd.type == SOCK_STREAM)
close(stdin);
else
inetd_dgram_drop(stdin, ifname);
return -1;
}
/* Return ingress interface */
strlcpy(iifname, ifname, len);
return stdin;
}
/* Socket callback, looks up correct svc and starts it as an inetd service */
static void socket_cb(uev_t *w, void *arg, int events)
{
svc_t *svc = (svc_t *)arg, *task;
const char *conn = " connection";
char iifname[IF_NAMESIZE + 1] = "UNKNOWN";
char id[MAX_ID_LEN];
int stdin;
_d("%s: Got socket event ...", svc->cmd);
if (UEV_ERROR == events) {
logit(LOG_INFO, "%s: Socket error, aborting: %s", svc->cmd, strerror(errno));
return;
}
stdin = get_stdin(svc, iifname, sizeof(iifname));
if (stdin < 0) {
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
return;
}
/*
* Make sure to disable O_NONBLOCK on the descriptor before
* passing it to the inetd service, that's what is expected.
*/
if (fcntl(stdin, F_SETFL, fcntl(stdin, F_GETFL, 0) & ~O_NONBLOCK) < 0) {
logit(LOG_CRIT, "Failed disabling non-blocking on %s socket", svc->cmd);
if (svc->inetd.type == SOCK_STREAM)
close(stdin);
return;
}
snprintf(id, sizeof(id), "%d", svc->inetd.next_id++);
task = svc_new(svc->cmd, id, SVC_TYPE_INETD_CONN);
if (!task) {
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client", svc->cmd);
if (svc->inetd.type == SOCK_STREAM)
close(stdin);
return;
}
if (!svc->inetd.forking) {
svc_busy(svc);
service_step(svc);
}
/* Copy inherited attributes from inetd service's svc */
task->runlevels = svc->runlevels;
/*
* Only copy the most relevant parts of inetd, in particular we
* must *not* copy the watcher data to the clone!
*/
task->inetd.svc = svc;
task->inetd.cmd = svc->inetd.cmd;
task->inetd.type = svc->inetd.type;
memcpy(task->rlimit, svc->rlimit, sizeof(task->rlimit));
memcpy(task->cond, svc->cond, sizeof(task->cond));
memcpy(task->username, svc->username, sizeof(task->username));
memcpy(task->group, svc->group, sizeof(task->group));
memcpy(task->args, svc->args, sizeof(task->args));
strlcpy(task->desc, svc->desc, sizeof(task->desc) - strlen(conn));
strlcat(task->desc, conn, sizeof(task->desc));
strlcpy(task->iifname, iifname, sizeof(task->iifname));
strlcpy(task->name, svc->name, sizeof(task->name));
task->stdin_fd = stdin;
service_step(task);
}
/*
* Refuse service if the request specifies a reply port corresponding to any internal service.
* This is done as a defense against looping attacks; the remote IP address is logged.
* http://www.freebsd.org/cgi/man.cgi?inetd(8)
* https://svnweb.freebsd.org/base/head/usr.sbin/inetd/inetd.c?revision=298909&view=markup#l2094
*/
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
{
svc_t *svc, *iter = NULL;
char pname[NI_MAXHOST];
for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
inetd_t *i = &svc->inetd;
if (!i->builtin || i->type != SOCK_DGRAM)
continue;
if (((const struct sockaddr_in *)sa)->sin_port == i->port) {
getnameinfo(sa, len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST);
logit(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s", i->name, "UDP", name, "UDP", pname);
return 1;
}
}
return 0;
}
/* Launch Inet socket for service.
* TODO: Add filtering ALLOW/DENY per interface.
*/
static int spawn_socket(inetd_t *inetd)
{
int sd;
socklen_t len = sizeof(struct sockaddr);
struct sockaddr_in s;
if (!inetd->type) {
logit(LOG_CRIT, "Invalid inetd service %s, skipping ...", inetd->name);
return -EINVAL;
}
_d("Spawning server socket for inetd %s, type %s ...", inetd->name, inetd->type == SOCK_STREAM ? "stream" : "dgram");
sd = socket(AF_INET, inetd->type | SOCK_NONBLOCK | SOCK_CLOEXEC, inetd->proto);
if (-1 == sd) {
logit(LOG_CRIT, "Failed opening inetd socket type %d proto %d", inetd->type, inetd->proto);
return -errno;
}
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEADDR);
#ifdef SO_REUSEPORT
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEPORT);
#endif
memset(&s, 0, sizeof(s));
s.sin_family = AF_INET;
s.sin_addr.s_addr = INADDR_ANY;
s.sin_port = htons(inetd->port);
if (bind(sd, (struct sockaddr *)&s, len) < 0) {
logit(LOG_CRIT, "Failed binding to port %d, maybe another %s server is already running?",
inetd->port, inetd->name);
close(sd);
return -errno;
}
if (inetd->port) {
if (inetd->type == SOCK_STREAM) {
if (-1 == listen(sd, 10)) {
logit(LOG_CRIT, "Failed listening to inetd service %s", inetd->name);
close(sd);
return -errno;
}
} else { /* SOCK_DGRAM */
/* Set extra sockopt to get ifindex from inbound packets */
ENABLE_SOCKOPT(sd, SOL_IP, IP_PKTINFO);
}
}
if (uev_io_init(ctx, &inetd->watcher, socket_cb, inetd->svc, sd, UEV_READ)) {
logit(LOG_CRIT, "Failed setting up inetd watcher for %s", inetd->name);
close(sd);
return -errno;
}
return 0;
}
int inetd_start(inetd_t *inetd)
{
int sd;
char buf[BUFSIZ];
ssize_t len;
sd = inetd->watcher.fd;
if (sd == -1)
return spawn_socket(inetd);
/* Read anything lingering, or clean up socket after failure */
len = recv(sd, buf, sizeof(buf), MSG_DONTWAIT);
if (len > 0)
_d("Read %zd lingering bytes from socket before (re)starting %s ...", len, inetd->svc->cmd);
/* Restore O_NONBLOCK for socket */
if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK)) {
logit(LOG_CRIT, "Cannot safely (re)start %s inetd service", inetd->svc->cmd);
return -errno;
}
_d("Re-starting %s socket watcher ...", inetd->svc->cmd);
uev_io_start(&inetd->watcher);
return 0;
}
void inetd_stop_children(inetd_t *inetd, int check_allowed)
{
svc_t *svc, *iter = NULL;
svc = svc_job_iterator(&iter, 1, inetd->svc->job);
while (svc) {
if (!svc_is_inetd(svc)) {
if (!check_allowed || !inetd_is_allowed(inetd, svc->iifname)) {
svc_stop(svc);
service_step(svc);
}
}
svc = svc_job_iterator(&iter, 0, inetd->svc->job);
}
}
void inetd_stop(inetd_t *inetd)
{
if (!inetd || !inetd->svc) {
_e("Invalid inetd, cannot stop it...");
return;
}
if (inetd->watcher.fd != -1) {
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
uev_io_stop(&inetd->watcher);
/*
* For dgram inetd services we block the parent SVC
* and halt the watcher, so don't close the socket!
*/
if (!svc_is_busy(inetd->svc)) {
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
close(inetd->watcher.fd);
inetd->watcher.fd = -1;
inetd_stop_children(inetd, 0);
}
}
}
static struct servent *fallback_service(char *service, char *proto)
{
int service_num;
static struct servent lfs;
service_num = fgetint("/etc/services", " \n\t", service);
if (service_num > 0) {
lfs.s_name = service;
lfs.s_port = htons(service_num);
lfs.s_proto = NULL;
if (!strcmp("tcp", proto) || !strcmp("udp", proto))
lfs.s_proto = proto;
return &lfs;
}
return NULL;
}
static struct servent *getent_service(char *service, char *proto)
{
struct servent *ent;
#ifdef ENABLE_STATIC
ent = fallback_service(service, proto);
#else
ent = getservbyname(service, proto);
if (!ent)
ent = fallback_service(service, proto);
#endif
return ent;
}
static struct protoent *fallback_proto(char *proto)
{
int proto_num;
static struct protoent lfp;
proto_num = fgetint("/etc/protocols", " \n\t", proto);
if (proto_num > 0) {
lfp.p_name = proto;
lfp.p_proto = proto_num;
return &lfp;
}
return NULL;
}
static struct protoent *getent_proto(char *proto)
{
struct protoent *ent;
#ifdef ENABLE_STATIC
ent = fallback_proto(proto);
#else
ent = getprotobyname(proto);
if (!ent)
ent = fallback_proto(proto);
#endif
return ent;
}
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
{
if (!fexist("/etc/services") || !fexist("/etc/protocols")) {
_w("Cannot register inetd %s/%s, system missing /etc/services or /etc/protocols", service, proto);
return errno = ECANCELED;
}
*sv = getent_service(service, proto);
if (!*sv) {
const char *errstr;
static struct servent s;
s.s_name = service;
s.s_port = strtonum(service, 1, UINT16_MAX, &errstr);
s.s_proto = NULL;
if (!strcmp("tcp", proto) || !strcmp("udp", proto))
s.s_proto = proto;
if (errstr || !s.s_proto) {
_e("Invalid/unknown inetd service, cannot create custom entry");
return errno = EINVAL;
}
_d("Creating cutom inetd service %s/%s", service, proto);
s.s_port = htons(s.s_port);
*sv = &s;
}
if (pv && (*sv)->s_proto) {
*pv = getent_proto((*sv)->s_proto);
if (!*pv) {
_e("Cannot find proto %s, skipping ...", (*sv)->s_proto);
return errno = EINVAL;
}
}
return 0;
}
/*
* Find exact match.
*/
static inetd_filter_t *find_filter(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter;
if (!ifname)
ifname = "*";
TAILQ_FOREACH(filter, &inetd->filters, link) {
_d("Checking filters for %s: '%s' vs '%s' (exact match) ...",
inetd->name, filter->ifname, ifname);
if (!strcmp(filter->ifname, ifname))
return filter;
}
return NULL;
}
/*
* First try exact match, then fall back to any match.
*/
inetd_filter_t *inetd_filter_match(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter = find_filter(inetd, ifname);
if (filter)
return filter;
TAILQ_FOREACH(filter, &inetd->filters, link) {
_d("Checking filters for %s: '%s' vs '%s' (any match) ...",
inetd->name, filter->ifname, ifname);
if (!strcmp(filter->ifname, "*"))
return filter;
}
return NULL;
}
int inetd_flush(inetd_t *inetd)
{
inetd_filter_t *filter, *next;
TAILQ_FOREACH_SAFE(filter, &inetd->filters, link, next) {
TAILQ_REMOVE(&inetd->filters, filter, link);
free(filter);
}
return 0;
}
/* Poor man's tcpwrappers filtering */
int inetd_allow(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter;
if (!inetd)
return errno = EINVAL;
if (!ifname)
ifname = "*";
filter = inetd_filter_match(inetd, ifname);
if (filter) {
_d("Filter %s for inetd %s already exists, skipping ...", ifname, inetd->name);
return 0;
}
_d("Allow iface %s for service %s (port %d)", ifname, inetd->name, inetd->port);
filter = calloc(1, sizeof(*filter));
if (!filter) {
_e("Out of memory, cannot add filter to service %s", inetd->name);
return errno = ENOMEM;
}
filter->deny = 0;
strlcpy(filter->ifname, ifname, sizeof(filter->ifname));
TAILQ_INSERT_TAIL(&inetd->filters, filter, link);
return 0;
}
int inetd_deny(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter;
if (!inetd)
return errno = EINVAL;
if (!ifname)
ifname = "*";
filter = find_filter(inetd, ifname);
if (filter) {
_d("%s filter %s for inetd %s already exists, cannot set deny filter for same, skipping ...",
filter->deny ? "Deny" : "Allow", ifname, inetd->name);
return 1;
}
_d("Deny iface %s for service %s (port %d)", ifname, inetd->name, inetd->port);
filter = calloc(1, sizeof(*filter));
if (!filter) {
_e("Out of memory, cannot add filter to service %s", inetd->name);
return errno = ENOMEM;
}
filter->deny = 1;
strlcpy(filter->ifname, ifname, sizeof(filter->ifname));
TAILQ_INSERT_TAIL(&inetd->filters, filter, link);
return 0;
}
int inetd_is_allowed(inetd_t *inetd, char *ifname)
{
inetd_filter_t *filter;
if (!inetd) {
errno = EINVAL;
return 0;
}
filter = inetd_filter_match(inetd, ifname);
if (filter) {
_d("Found matching filter for %s, deny: %d ... ", inetd->name, filter->deny);
return !filter->deny;
}
_d("No matching filter for %s ... ", inetd->name);
return 0;
}
int inetd_match(inetd_t *inetd, char *service, char *proto)
{
struct servent *sv = NULL;
struct protoent *pv = NULL;
if (!inetd || !service || !proto)
return errno = EINVAL;
if (strncmp(inetd->name, service, sizeof(inetd->name)))
return 0;
if (getent(service, proto, &sv, &pv))
return 0;
if (inetd->proto == pv->p_proto &&
inetd->port == ntohs(sv->s_port))
return 1;
return 0;
}
/* Compose presentable string of inetd filters: !eth0,eth1,eth2,!eth3 */
int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
{
int prev = 0;
char buf[42];
inetd_filter_t *filter;
if (!inetd || !str || len <= 0) {
_e("Dafuq?");
return 1;
}
snprintf(str, len, "%s allow %s ", inetd->name,
inetd->type == SOCK_DGRAM ? "UDP" : "TCP");
TAILQ_FOREACH(filter, &inetd->filters, link) {
char ifname[IFNAMSIZ];
if (filter->deny)
continue;
if (!strlen(filter->ifname))
snprintf(ifname, sizeof(ifname), "*");
else
strlcpy(ifname, filter->ifname, sizeof(ifname));
snprintf(buf, sizeof(buf), "%s%s:%d", prev ? "," : "",
ifname, inetd->port);
prev = 1;
strlcat(str, buf, len);
}
prev = 0;
TAILQ_FOREACH(filter, &inetd->filters, link) {
char ifname[IFNAMSIZ];
if (!filter->deny)
continue;
if (!prev) {
snprintf(buf, sizeof(buf), " deny ");
strlcat(str, buf, len);
}
if (!strlen(filter->ifname))
snprintf(ifname, sizeof(ifname), "*");
else
strlcpy(ifname, filter->ifname, sizeof(ifname));
snprintf(buf, sizeof(buf), "%s%s", prev ? "," : "", ifname);
prev = 1;
strlcat(str, buf, len);
}
return 0;
}
svc_t *inetd_find_svc(char *path, char *service, char *proto)
{
svc_t *svc, *iter = NULL;
for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
continue;
if (inetd_match(&svc->inetd, service, proto)) {
_d("Found a matching inetd svc for %s %s %s", path, service, proto);
return svc;
}
}
return NULL;
}
/*
* This function is called to add a new, unique, inetd service. When an
* ifname is given as argument this means *only* run service on this
* interface. If a similar service runs on another port, this function
* must add this ifname as "deny" to that other service.
*
* Example:
* inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
* inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i
*
* In this example eth0:222 is very specific, so when ssh/tcp (default)
* is added we must find the previous 'ssh' service and add its eth0 as
* deny, so we don't accept port 22 session on eth0.
*
* In the reverse case, where the default (ssh/tcp) entry is listed
* before the specific (eth0:222), the new inetd service must find the
* (any!) previous rule and add its ifname to their deny list.
*
* If equivalent service exists already service_register() will instead call
* inetd_allow().
*/
int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc)
{
int result;
struct servent *sv = NULL;
struct protoent *pv = NULL;
if (!inetd || !service || !proto)
return errno = EINVAL;
result = getent(service, proto, &sv, &pv);
if (result)
return result;
/* Setup defaults */
inetd->std = 1;
inetd->port = ntohs(sv->s_port);
inetd->proto = pv->p_proto;
inetd->forking = !!forking;
inetd->next_id = 2;
if (!name)
name = service;
strlcpy(inetd->name, name, sizeof(inetd->name));
TAILQ_INIT(&inetd->filters);
/* Naïve mapping tcp->stream, udp->dgram, other->dgram */
if (!strcasecmp(sv->s_proto, "tcp"))
inetd->type = SOCK_STREAM;
else
inetd->type = SOCK_DGRAM;
if (inetd->type == SOCK_DGRAM && inetd->forking) {
logit(LOG_WARNING, "%s: 'nowait' is not applicable on UDP services, ignoring", svc->cmd);
inetd->forking = 0;
}
/* Reset descriptor, used internally */
inetd->watcher.fd = -1;
/* Setup socket callback argument */
inetd->svc = svc;
_d("New service %s (default port %d proto %s:%d)", name, inetd->port, sv->s_proto, pv->p_proto);
return 0;
}
int inetd_del(inetd_t *inetd)
{
svc_unblock(inetd->svc);
inetd_stop(inetd);
return inetd_flush(inetd);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
-83
View File
@@ -1,83 +0,0 @@
/* Classic inetd services launcher for Finit
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
*
* 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.
*/
#ifndef FINIT_INETD_H_
#define FINIT_INETD_H_
#include <netdb.h>
#include <net/if.h>
#include <uev/uev.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
typedef struct svc svc_t;
typedef struct inetd_filter {
TAILQ_ENTRY(inetd_filter) link;
int deny; /* 0:allow, 1:deny */
char ifname[IFNAMSIZ]; /* E.g., eth0 */
} inetd_filter_t;
typedef struct {
uev_t watcher;
svc_t *svc; /* svc_t pointer for the socket callback */
int type; /* Socket type: SOCK_STREAM/SOCK_DGRAM */
int std; /* Standard proto/port from /etc/services */
int proto;
int port;
int forking;
int builtin; /* Set by built-in inetd services only */
int next_id; /* Next child job's id */
char name[10];
int (*cmd)(int type); /* internal inetd service, like 'time' */
TAILQ_HEAD(, inetd_filter) filters;
} inetd_t;
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name);
int inetd_start (inetd_t *inetd);
void inetd_stop (inetd_t *inetd);
void inetd_stop_children (inetd_t *inetd, int check_allowed);
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
int inetd_del (inetd_t *inetd);
svc_t *inetd_find_svc (char *path, char *service, char *proto);
int inetd_match (inetd_t *inetd, char *service, char *proto);
int inetd_filter_str(inetd_t *inetd, char *str, size_t len);
int inetd_flush (inetd_t *inetd);
int inetd_allow (inetd_t *inetd, char *ifname);
int inetd_deny (inetd_t *inetd, char *ifname);
int inetd_is_allowed(inetd_t *inetd, char *ifname);
#endif /* FINIT_INETD_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+6 -44
View File
@@ -421,17 +421,14 @@ static int show_status(char *arg)
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char jobid[20], args[512] = "", *lvls;
int i;
if (!svc->id[0])
snprintf(jobid, sizeof(jobid), "%d", svc->job);
else
snprintf(jobid, sizeof(jobid), "%d:%s", svc->job, svc->id);
printf("%-9s %8s ", jobid, svc_status(svc));
if (svc_is_inetd(svc))
printf("inetd ");
else
printf("%-6d ", svc->pid);
printf("%-9s %8s %-6d ", jobid, svc_status(svc), svc->pid);
lvls = runlevel_string(runlevel, svc->runlevels);
if (strchr(lvls, '\e'))
@@ -445,47 +442,12 @@ static int show_status(char *arg)
continue;
}
#ifdef INETD_ENABLED
if (svc_is_inetd(svc)) {
char *info;
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_QUERY_INETD,
};
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
if (client_send(&rq, sizeof(rq))) {
snprintf(args, sizeof(args), "Unknown inetd");
info = args;
} else {
size_t len = sizeof(rq.data);
info = rq.data;
info[len - 1] = 0;
if (!string_match("internal", svc->cmd)) {
char *ptr;
ptr = strchr(info, ' ');
if (ptr)
info = ptr + 1;
}
}
printf("%s %s\n", svc->cmd, info);
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
strlcat(args, svc->args[i], sizeof(args));
strlcat(args, " ", sizeof(args));
}
else
#endif /* INETD_ENABLED */
{
int i;
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
strlcat(args, svc->args[i], sizeof(args));
strlcat(args, " ", sizeof(args));
}
printf("%s %s\n", svc->cmd, args);
}
printf("%s %s\n", svc->cmd, args);
}
return 0;
+39 -239
View File
@@ -37,7 +37,6 @@
#include "cond.h"
#include "finit.h"
#include "helpers.h"
#include "inetd.h"
#include "pid.h"
#include "private.h"
#include "sig.h"
@@ -248,17 +247,6 @@ static int lredirect(svc_t *svc)
*/
static int redirect(svc_t *svc)
{
/* Redirect inetd socket to stdin for connection */
#ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc)) {
dup2(svc->stdin_fd, STDIN_FILENO);
close(svc->stdin_fd);
dup2(STDIN_FILENO, STDOUT_FILENO);
dup2(STDIN_FILENO, STDERR_FILENO);
return 0;
}
#endif
stdin_redirect();
if (svc->log.enabled) {
@@ -306,19 +294,14 @@ static int service_start(svc_t *svc)
return 1;
/* Don't try and start service if it doesn't exist. */
if (!whichp(svc->cmd) && !svc->inetd.cmd) {
if (!whichp(svc->cmd)) {
print(1, "Service %s does not exist", svc->cmd);
svc_missing(svc);
return 1;
}
#ifdef INETD_ENABLED
if (svc_is_inetd(svc))
return inetd_start(&svc->inetd);
#endif
if (svc_is_sysv(svc)) {
if (svc_is_sysv(svc))
logit(LOG_CONSOLE | LOG_NOTICE, "Calling '%s start' ...", svc->cmd);
}
if (!svc->desc[0])
do_progress = 0;
@@ -404,22 +387,11 @@ static int service_start(svc_t *svc)
redirect(svc);
sig_unblock();
if (svc->inetd.cmd)
status = svc->inetd.cmd(svc->inetd.type);
else if (svc_is_runtask(svc))
if (svc_is_runtask(svc))
status = exec_runtask(svc->cmd, args);
else
status = execvp(svc->cmd, args);
#ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc)) {
if (svc->inetd.type == SOCK_STREAM) {
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
} else
#endif
_exit(status);
} else if (log_is_debug()) {
char buf[CMD_SIZE] = "";
@@ -455,13 +427,6 @@ static int service_start(svc_t *svc)
pid_file_create(svc);
break;
#ifdef INETD_ENABLED
case SVC_TYPE_INETD_CONN:
if (svc->inetd.type == SOCK_STREAM)
close(svc->stdin_fd);
break;
#endif
default:
break;
}
@@ -519,22 +484,6 @@ static int service_stop(svc_t *svc)
if (svc->state <= SVC_STOPPING_STATE)
return 0;
#ifdef INETD_ENABLED
if (svc_is_inetd(svc)) {
int do_progress = runlevel != 1 && !svc_is_busy(svc);
if (do_progress)
print_desc("Stopping ", svc->desc);
inetd_stop(&svc->inetd);
if (do_progress)
print_result(0);
svc_set_state(svc, SVC_STOPPING_STATE);
return 0;
} else
#endif
service_timeout_cancel(svc);
if (!svc_is_sysv(svc)) {
@@ -792,7 +741,7 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
* service_register - Register service, task or run commands
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
* @cfg: Configuration, complete command, with -- for description text
* @rlimit: Limits for this service/task/run/inetd, may be global limits
* @rlimit: Limits for this service/task/run, may be global limits
* @file: The file name service was loaded from
*
* This function is used to register commands to be run on different
@@ -807,7 +756,6 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
* service @username [!0-6,S] <!COND> /path/to/daemon arg -- Description
* task @username [!0-6,S] /path/to/task arg -- Description
* run @username [!0-6,S] /path/to/cmd arg -- Description
* inetd tcp/ssh nowait [2345] @root:root /sbin/sshd -i -- Description
*
* If the username is left out the command is started as root. The []
* brackets denote the allowed runlevels, if left out the default for a
@@ -817,46 +765,43 @@ static void parse_cmdline_args(svc_t *svc, char *cmd)
* command is listed in more than the [S] runlevel they will be called
* when changing runlevel.
*
* Services (daemons, not inetd services) also support an optional <!EV>
* argument. This is for services that, e.g., require a system gateway
* or interface to be up before they are started. Or restarted, or even
* SIGHUP'ed, when the gateway changes or interfaces come and go. The
* special case when a service is declared with <!> means it does not
* support SIGHUP but must be STOP/START'ed at system reconfiguration.
* Services (daemons) also support an optional <!condition> argument.
* This is for services that depend on another service, e.g. Quagga ripd
* depends on zebra, or require a system gateway or interface to be up
* before they are started. Or restarted, or even SIGHUP'ed, when the
* gateway changes or interfaces come and go. The special case when a
* service is declared with <!> means it does not support SIGHUP but
* must be STOP/START'ed at system reconfiguration.
*
* Service conditions can be: pid/<PATH> for PID files, net/<IFNAME>/up
* and net/<IFNAME>/exists. The condition handling is further described
* in doc/conditions.md, but worth mentioning here is that the condition
* name itself can be modified using the :ID and name:foo syntax.
* Conditions can for example be: pid/NAME:ID for process dependencies,
* net/<IFNAME>/up or net/<IFNAME>/exists. The condition handling is
* further described in doc/conditions.md, but worth mentioning here is
* that the condition a services *provides* can be modified using the
* :ID and name:foo syntax.
*
* For multiple instances of the same command, e.g. multiple DHCP
* clients, the user must enter an ID, using the :ID syntax.
*
* service :1 /sbin/udhcpc -i eth1
* service :2 /sbin/udhcpc -i eth2
* service :eth1 /sbin/udhcpc -i eth1
* service :eth2 /sbin/udhcpc -i eth2
*
* Without the :ID syntax Finit will overwrite the first service line
* with the contents of the second. The :ID must be [1,MAXINT].
* Without the :ID syntax, Finit replaces the first service line with
* the contents of the second. The :ID can be any string value and
* defaults to "" (emtpy string).
*
* Returns:
* POSIX OK(0) on success, or non-zero errno exit status on failure.
*/
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
{
#ifdef INETD_ENABLED
char id_str[MAX_ID_LEN];
int forking = 0;
#endif
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
char *username = NULL, *log = NULL, *pid = NULL;
char *name = NULL, *halt = NULL, *delay = NULL;
char *id = NULL;
int levels = 0;
int manual = 0;
char *line;
char *id = NULL;
char *username = NULL, *log = NULL, *pid = NULL;
char *service = NULL, *proto = NULL, *ifaces = NULL;
char *cmd, *desc, *runlevels = NULL, *cond = NULL;
char *name = NULL, *halt = NULL, *delay = NULL;
svc_t *svc;
plugin_t *plugin = NULL;
if (!cfg) {
_e("Invalid input argument");
@@ -902,12 +847,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
cond = &cmd[1];
else if (cmd[0] == ':') /* :ID */
id = &cmd[1];
#ifdef INETD_ENABLED
else if (!strncasecmp(cmd, "nowait", 6))
forking = 1;
else if (!strncasecmp(cmd, "wait", 4))
forking = 0;
#endif
else if (!strncasecmp(cmd, "log", 3))
log = cmd;
else if (!strncasecmp(cmd, "pid", 3))
@@ -920,8 +859,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
halt = &cmd[5];
else if (!strncasecmp(cmd, "kill:", 5))
delay = &cmd[5];
else if (cmd[0] != '/' && strchr(cmd, '/'))
service = cmd; /* inetd service/proto */
else
break;
@@ -938,55 +875,6 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
return 0;
}
/* Example: inetd ssh/tcp@eth0,eth1 or 222/tcp@eth2 */
if (service) {
ifaces = strchr(service, '@');
if (ifaces)
*ifaces++ = 0;
proto = strchr(service, '/');
if (!proto)
goto incomplete;
*proto++ = 0;
}
#ifdef INETD_ENABLED
/* Find plugin that provides a callback for this inetd service */
if (type == SVC_TYPE_INETD) {
if (!strncasecmp(cmd, "internal", 8)) {
char *ptr, *ps = service;
/* internal.service */
ptr = strchr(cmd, '.');
if (ptr) {
*ptr++ = 0;
ps = ptr;
}
plugin = plugin_find(ps);
if (!plugin || !plugin->inetd.cmd) {
_w("Inetd service %s has no internal plugin, skipping ...", service);
free(line);
return errno = ENOENT;
}
}
/* Check if known inetd, update command line, then add ifnames for filtering only. */
svc = inetd_find_svc(cmd, service, proto);
if (svc) {
parse_cmdline_args(svc, cmd);
goto inetd_setup;
}
/* inetd services need a unique ID, so we must always set one. */
if (!id) {
snprintf(id_str, sizeof(id_str), "%d", svc_next_id_int(cmd));
id = id_str;
}
}
recreate:
#endif
if (!id)
id = "";
@@ -1004,16 +892,6 @@ recreate:
svc_stop(svc);
}
}
#ifdef INETD_ENABLED
else {
if (svc_is_inetd(svc) && type != SVC_TYPE_INETD) {
_d("Service was previously inetd, deregistering ...");
inetd_del(&svc->inetd);
svc_del(svc);
goto recreate;
}
}
#endif
/* Always clear svc PID file, for now. See TODO */
svc->pidfile[0] = 0;
@@ -1031,12 +909,7 @@ recreate:
strlcpy(svc->username, username, sizeof(svc->username));
}
if (plugin) {
/* Internal plugin provides this service */
svc->inetd.cmd = plugin->inetd.cmd;
svc->inetd.builtin = 1;
} else
parse_cmdline_args(svc, cmd);
parse_cmdline_args(svc, cmd);
svc->runlevels = levels;
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
@@ -1053,35 +926,6 @@ recreate:
if (desc)
strlcpy(svc->desc, desc, sizeof(svc->desc));
#ifdef INETD_ENABLED
if (svc_is_inetd(svc)) {
char *iface, *name = service;
if (svc->inetd.cmd && plugin)
name = plugin->name;
if (inetd_new(&svc->inetd, name, service, proto, forking, svc)) {
_e("Failed registering new inetd service %s/%s", service, proto);
free(line);
return svc_del(svc);
}
inetd_setup:
inetd_flush(&svc->inetd);
if (!ifaces) {
_d("No specific iface listed for %s, allowing ANY", service);
inetd_allow(&svc->inetd, NULL);
} else {
for (iface = strtok(ifaces, ","); iface; iface = strtok(NULL, ",")) {
if (iface[0] == '!')
inetd_deny(&svc->inetd, &iface[1]);
else
inetd_allow(&svc->inetd, iface);
}
}
}
#endif
/* Set configured limits */
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
@@ -1101,44 +945,18 @@ recreate:
/*
* This function is called when cleaning up lingering (stopped) services
* after a .conf reload, as well as when an inetd connection terminates.
* after a .conf reload.
*
* We need to ensure we properly stop the service before removing it,
* including stopping any pending restart or SIGKILL timers before we
* proceed to free() the svc itself.
*
* Remember to not try to stop inetd connections, they only get here
* when already stopped, if we do we end up in a recursive loop.
*/
void service_unregister(svc_t *svc)
{
if (!svc)
return;
/*
* Only try stopping @svc if it's *not* an inetd connection.
* Prevents infinite recursion when called from service_step()
*/
switch (svc->type) {
#ifdef INETD_ENABLED
case SVC_TYPE_INETD:
inetd_del(&svc->inetd);
break;
case SVC_TYPE_INETD_CONN:
/* inetd connection, if UDP unblock parent */
if (svc_is_busy(svc->inetd.svc)) {
svc_unblock(svc->inetd.svc);
service_step(svc->inetd.svc);
}
break;
#endif
default:
service_stop(svc);
break;
}
service_stop(svc);
svc_del(svc);
}
@@ -1238,7 +1056,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
*state = new;
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
if ((*state == SVC_STOPPING_STATE) && !svc_is_inetd(svc)) {
if ((*state == SVC_STOPPING_STATE)) {
_d("%s is stopping, wait %d sec before sending SIGKILL ...",
svc->cmd, svc->killdelay / 1000);
service_timeout_cancel(svc);
@@ -1247,7 +1065,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
}
/*
* Transition inetd/task/run/service
* Transition task/run/service
*
* Returns: non-zero if the @svc is no longer valid (removed)
*/
@@ -1275,12 +1093,6 @@ restart:
break;
case SVC_DONE_STATE:
#ifdef INETD_ENABLED
if (svc_is_inetd_conn(svc)) {
service_unregister(svc);
return -1;
}
#endif
if (svc_is_changed(svc))
svc_set_state(svc, SVC_HALTED_STATE);
break;
@@ -1293,11 +1105,8 @@ restart:
switch (svc->type) {
case SVC_TYPE_SERVICE:
case SVC_TYPE_INETD:
svc_set_state(svc, SVC_HALTED_STATE);
break;
case SVC_TYPE_INETD_CONN:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
@@ -1322,8 +1131,7 @@ restart:
err = service_start(svc);
if (err) {
(*restart_cnt)++;
if (!svc_is_inetd_conn(svc))
break;
break;
}
/* Everything went fine, clean and set state */
@@ -1352,12 +1160,6 @@ restart:
break;
}
/* Collected inetd connection, drive it to stopping */
if (svc_is_inetd_conn(svc)) {
svc_set_state(svc, SVC_STOPPING_STATE);
break;
}
if (svc_is_runtask(svc)) {
if (svc_is_sysv(svc)) {
if (!svc->started)
@@ -1384,18 +1186,16 @@ restart:
case COND_ON:
if (svc_is_changed(svc)) {
if (svc->sighup) {
/* wait until all processes has been stopped before continuing... */
/*
* wait until all processes have been
* stopped before continuing...
*/
if (sm_is_in_teardown(&sm))
break;
service_restart(svc);
} else {
#ifdef INETD_ENABLED
if (svc_is_inetd(svc))
inetd_stop_children(&svc->inetd, 1);
else
#endif
service_stop(svc);
}
} else
service_stop(svc);
svc_mark_clean(svc);
}
break;
@@ -1465,7 +1265,7 @@ void service_step_all(int types)
void service_worker(void *unused)
{
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK);
}
/**
+3 -3
View File
@@ -222,7 +222,7 @@ restart:
_d("Stopping services not allowed after reconf ...");
sm->in_teardown = 1;
cond_reload();
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
service_step_all(SVC_TYPE_SERVICE);
tty_reload(NULL);
sm->state = SM_RELOAD_WAIT_STATE;
@@ -244,12 +244,12 @@ restart:
svc_clean_dynamic(service_unregister);
_d("Starting services after reconf ...");
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
service_step_all(SVC_TYPE_SERVICE);
_d("Calling reconf hooks ...");
plugin_run_hooks(HOOK_SVC_RECONF);
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
service_step_all(SVC_TYPE_SERVICE);
_d("Reconfiguration done");
sm->state = SM_RUNNING_STATE;
+2 -49
View File
@@ -72,9 +72,9 @@ static void svc_gc(void *arg)
/**
* svc_new - Create a new service
* @cmd: External program to call, or 'internal' for internal inetd services
* @cmd: External program to call
* @id: Instance id
* @type: Service type, one of service, task, run or inetd
* @type: Service type, one of service, task, run
*
* Returns:
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
@@ -169,28 +169,6 @@ svc_t *svc_iterator(svc_t **iter, int first)
return svc;
}
/**
* svc_inetd_iterator - Naive iterator over all registered inetd services.
* @iter: Iterator, must be a valid pointer
* @first: If set, get first &svc_t, otherwise get next
*
* Returns:
* The first inetd &svc_t when %NULL is given as argument, otherwise the
* next inetd &svc_t until the end when %NULL is returned.
*/
svc_t *svc_inetd_iterator(svc_t **iter, int first)
{
svc_t *svc;
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
if (svc_is_inetd(svc))
return svc;
}
return NULL;
}
/**
* svc_named_iterator - Iterates over all instances of a service.
* @iter: Iterator, must be a valid pointer
@@ -455,8 +433,6 @@ void svc_mark_dynamic(void)
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->protect)
continue;
if (svc_is_inetd_conn(svc))
continue;
*((int *)&svc->dirty) = -1;
}
@@ -547,29 +523,6 @@ int svc_enabled(svc_t *svc)
return 1;
}
/*
* Same base service, return unique ID as an integer
* Note: intended for use with INETD services.
*/
int svc_next_id_int(char *cmd)
{
int n = 1;
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
char id[MAX_ID_LEN];
snprintf(id, sizeof(id), "%d", n);
if (!strcmp(svc->cmd, cmd) && strcmp(svc->id, id))
return n;
n++;
}
return 1;
}
int svc_is_unique(svc_t *svc)
{
svc_t *s, *iter = NULL;
+2 -14
View File
@@ -30,8 +30,8 @@
#include <sys/types.h> /* pid_t */
#include <lite/lite.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#include <uev/uev.h>
#include "inetd.h"
#include "helpers.h"
typedef int svc_cmd_t;
@@ -41,8 +41,6 @@ typedef enum {
SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */
SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
SVC_TYPE_INETD = 8, /* Classic inetd service */
SVC_TYPE_INETD_CONN = 16, /* Single inetd connection */
SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */
} svc_type_t;
@@ -103,7 +101,7 @@ typedef struct svc {
int started; /* Set for run/task/sysv to track if started */
int status; /* From waitpid() when process is collected */
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
svc_type_t type; /* Service, run, task, inetd, ... */
svc_type_t type; /* Service, run, task, ... */
int protect; /* Services like dbus-daemon & udev by Finit */
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
int starting; /* ... waiting for pidfile to be re-asserted */
@@ -117,11 +115,6 @@ typedef struct svc {
char once; /* run/task, (at least) once per runlevel */
const char restart_cnt; /* Incremented for each restart by service monitor. */
/* For inetd services */
inetd_t inetd;
int stdin_fd;
char iifname[IF_NAMESIZE + 1]; /* Ingress interface for connection */
/* Set for services we need to redirect stdout/stderr to syslog */
struct {
char enabled;
@@ -162,7 +155,6 @@ svc_t *svc_find_by_nameid (char *name, char *id);
svc_t *svc_find_by_pidfile (char *fn);
svc_t *svc_iterator (svc_t **iter, int first);
svc_t *svc_inetd_iterator (svc_t **iter, int first);
svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
svc_t *svc_job_iterator (svc_t **iter, int first, int job);
@@ -179,13 +171,10 @@ int svc_clean_bootstrap (svc_t *svc);
void svc_prune_bootstrap (void);
int svc_enabled (svc_t *svc);
int svc_next_id_int (char *cmd);
int svc_is_unique (svc_t *svc);
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *));
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; }
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
@@ -246,7 +235,6 @@ static inline char *svc_status(svc_t *svc)
case SVC_STOPPING_STATE:
switch (svc->type) {
case SVC_TYPE_INETD_CONN:
case SVC_TYPE_RUN:
case SVC_TYPE_TASK:
return "active";