mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 20:43:02 +02:00
statd: break out operational status into new daemon
This patch does two main things: 1) Breaks "statd" free from "confd". 2) Introduces netlink monitoring of interfaces for sysrepo data. Prior to this patch, sysrepo operation status was handled by code named "statd" inside "confd". (Where confd is a sysrepo-plugin and not a real daemon, which its name implies) In this patch, we break out the interface part of the operational sysrepo status, previously "statd", into a new daemon, also called "statd". The main reason for doing this is to avoid sysrepo threading, which allows the new "statd" to have control over sysrepo events in the same event pool as netlink events from the kernel. This in turn allows statd to subscribe and unsubscribe dynamically to interfaces that comes and goes during runtime (such as vlans). It also makes sense that "statd" is in fact a finit daemon. Signed-off-by: Richard Alpe <richard@bit42.se>
This commit is contained in:
committed by
Joachim Wiberg
parent
e26d4663d0
commit
b3213c6228
@@ -105,6 +105,7 @@ BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_SULOGIN=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
|
||||
@@ -110,6 +110,7 @@ BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_SULOGIN=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
|
||||
@@ -59,6 +59,7 @@ BR2_PACKAGE_SYSKLOGD_LOGGER=y
|
||||
BR2_TARGET_ROOTFS_SQUASHFS=y
|
||||
# BR2_TARGET_ROOTFS_TAR is not set
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_SULOGIN=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/confd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/statd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/conmon/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/factory/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/faux/Config.in"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
config BR2_PACKAGE_STATD
|
||||
bool "statd"
|
||||
help
|
||||
Operational Status Daemon. Responsible for handling sysrepo
|
||||
operational run-time info. Such as interface state and address.
|
||||
All data supplied should be fetched from the running system,
|
||||
which might differ from data configured through confd.
|
||||
@@ -0,0 +1 @@
|
||||
service name:statd log [S12345789] <pid/sysrepo> statd -f -p /run/statd.pid -n -- Status daemon
|
||||
@@ -0,0 +1,28 @@
|
||||
################################################################################
|
||||
#
|
||||
# statd
|
||||
#
|
||||
################################################################################
|
||||
STATD_VERSION = 1.0
|
||||
STATD_LICENSE = MIT
|
||||
STATD_SITE_METHOD = local
|
||||
STATD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/statd
|
||||
STATD_DEPENDENCIES = sysrepo libev jansson
|
||||
|
||||
define STATD_BUILD_CMDS
|
||||
$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D) \
|
||||
LDFLAGS="$(TARGET_LDFLAGS)"
|
||||
endef
|
||||
|
||||
define STATD_INSTALL_TARGET_CMDS
|
||||
$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D) \
|
||||
DESTDIR="$(TARGET_DIR)" install
|
||||
endef
|
||||
|
||||
define STATD_INSTALL_EXTRA
|
||||
cp $(STATD_PKGDIR)/statd.conf $(FINIT_D)/available/
|
||||
ln -sf ../available/statd.conf $(FINIT_D)/enabled/statd.conf
|
||||
endef
|
||||
STATD_TARGET_FINALIZE_HOOKS += STATD_INSTALL_EXTRA
|
||||
|
||||
$(eval $(generic-package))
|
||||
@@ -10,7 +10,6 @@ AC_CONFIG_FILES([
|
||||
Makefile
|
||||
src/Makefile
|
||||
src/confd/Makefile
|
||||
src/statd/Makefile
|
||||
yang/Makefile
|
||||
])
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
SUBDIRS = confd statd
|
||||
SUBDIRS = confd
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
#include <sys/wait.h>
|
||||
#include <libite/lite.h>
|
||||
|
||||
/* TODO remove once confd / statd lib situation is resolved */
|
||||
#ifndef vasprintf
|
||||
int vasprintf(char **strp, const char *fmt, va_list ap);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Run cmd in background after delay microseconds.
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#ifndef CONFD_HELPERS_H_
|
||||
#define CONFD_HELPERS_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
int vasprintf(char **strp, const char *fmt, va_list ap);
|
||||
|
||||
int runbg(char *const args[], int delay);
|
||||
int run_status(int pid);
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
CFLAGS += -Wall -Wextra -Werror -Wno-unused-parameter
|
||||
AM_CPPFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE -D_GNU_SOURCE -DYANG_PATH_=\"$(YANGDIR)\"
|
||||
|
||||
plugindir = $(srpdplugindir)
|
||||
plugin_LTLIBRARIES = statd-plugin.la
|
||||
|
||||
statd_plugin_la_CFLAGS = $(augeas_CFLAGS) $(jansson_CFLAGS) $(libite_CFLAGS) $(sysrepo_CFLAGS) $(CFLAGS)
|
||||
statd_plugin_la_LIBADD = $(augeas_LIBS) $(jansson_LIBS) $(libite_LIBS) $(sysrepo_LIBS)
|
||||
statd_plugin_la_LDFLAGS = -module -avoid-version -shared
|
||||
statd_plugin_la_SOURCES = core.c ../lib/common.h \
|
||||
../lib/helpers.h ../lib/helpers.c \
|
||||
../lib/lyx.h ../lib/lyx.c \
|
||||
../lib/srx_module.h ../lib/srx_module.c
|
||||
@@ -0,0 +1,27 @@
|
||||
# TODO: Libs are included fron confd. They should be broken out of confd and
|
||||
# shared between statd and confd in some nice way.
|
||||
CONFD_LIB = $(BASE_DIR)/../src/confd/lib/
|
||||
CONFD_SRC_LIB = $(BASE_DIR)/../src/confd/src/lib
|
||||
|
||||
CFLAGS += -Wall -Wextra -Werror
|
||||
LDLIBS += -lsysrepo -lev -lyang -ljansson -lite
|
||||
CPPFLAGS := -I$(CONFD_SRC_LIB)
|
||||
|
||||
TARGET = statd
|
||||
SRC = statd.c
|
||||
LIB_SRC = $(CONFD_SRC_LIB)/helpers.c $(CONFD_SRC_LIB)/lyx.c $(CONFD_LIB)/vasprintf.c
|
||||
LIB_HDR = $(CONFD_SRC_LIB)/helpers.h $(CONFD_SRC_LIB)/lyx.h
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC) $(LIB_SRC) $(LIB_HDR)
|
||||
$(CC) $(CPPFLAGS) $(LDLIBS) $(CFLAGS) -o $(TARGET) $(SRC) $(LIB_SRC)
|
||||
|
||||
clean:
|
||||
-rm statd
|
||||
|
||||
distclean: clean
|
||||
-rm *~
|
||||
|
||||
install:
|
||||
install -D statd $(DESTDIR)/sbin/
|
||||
@@ -0,0 +1,491 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sysrepo.h>
|
||||
#include <ev.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include <ctype.h>
|
||||
#include <linux/if.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "helpers.h"
|
||||
#include "lyx.h"
|
||||
|
||||
#define XPATH_MAX PATH_MAX
|
||||
#define XPATH_IFACE_BASE "/ietf-interfaces:interfaces"
|
||||
|
||||
#define SOCK_RMEM_SIZE 1000000 /* Arbitrary chosen, default = 212992 */
|
||||
#define NL_BUF_SIZE 4096 /* Arbitrary chosen */
|
||||
|
||||
TAILQ_HEAD(sub_head, sub);
|
||||
|
||||
/* This should, with some modifications, be able to hold other subscription
|
||||
* types, not only interfaces.
|
||||
*/
|
||||
struct sub {
|
||||
char ifname[IFNAMSIZ];
|
||||
struct ev_io watcher;
|
||||
sr_subscription_ctx_t *sr_sub;
|
||||
|
||||
TAILQ_ENTRY(sub) entries;
|
||||
};
|
||||
|
||||
struct netlink {
|
||||
int sd;
|
||||
struct ev_io watcher;
|
||||
};
|
||||
|
||||
struct statd {
|
||||
struct netlink nl;
|
||||
struct ev_loop *ev_loop;
|
||||
struct sub_head subs;
|
||||
sr_session_ctx_t *sr_ses;
|
||||
};
|
||||
|
||||
static void set_sock_rcvbuf(int sd, int size)
|
||||
{
|
||||
if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) {
|
||||
perror("setsockopt");
|
||||
return;
|
||||
}
|
||||
DEBUG("Socket receive buffer size increased to: %d bytes\n", size);
|
||||
}
|
||||
|
||||
static void str_to_lowercase(char *str)
|
||||
{
|
||||
for (int i = 0; str[i]; i++)
|
||||
str[i] = tolower((unsigned char)str[i]);
|
||||
}
|
||||
|
||||
static int nl_sock_init(void)
|
||||
{
|
||||
struct sockaddr_nl addr;
|
||||
int sock;
|
||||
|
||||
sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (sock < 0) {
|
||||
ERROR("Error, creating netlink socket: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_groups = RTMGRP_LINK;
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
ERROR("Error, binding netlink socket: %s\n", strerror(errno));
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static struct sub *sub_find_iface(struct sub_head *subs, const char *ifname)
|
||||
{
|
||||
struct sub *sub;
|
||||
|
||||
TAILQ_FOREACH(sub, subs, entries) {
|
||||
if (strcmp(sub->ifname, ifname) == 0)
|
||||
return sub;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sub_delete(struct ev_loop *loop, struct sub_head *subs, struct sub *sub)
|
||||
{
|
||||
TAILQ_REMOVE(subs, sub, entries);
|
||||
ev_io_stop(loop, &sub->watcher);
|
||||
sr_unsubscribe(sub->sr_sub);
|
||||
free(sub);
|
||||
}
|
||||
|
||||
|
||||
static json_t *json_get_ip_link(char *ifname)
|
||||
{
|
||||
char cmd[512] = {}; /* Size is arbitrary */
|
||||
json_error_t j_err;
|
||||
json_t *j_root;
|
||||
FILE *proc;
|
||||
|
||||
if (ifname)
|
||||
snprintf(cmd, sizeof(cmd), "ip -d -j link show dev %s 2>/dev/null", ifname);
|
||||
else
|
||||
snprintf(cmd, sizeof(cmd), "ip -d -j link show 2>/dev/null");
|
||||
|
||||
proc = popenf("re", cmd);
|
||||
if (!proc) {
|
||||
ERROR("Error, running ip link command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
j_root = json_loadf(proc, 0, &j_err);
|
||||
pclose(proc);
|
||||
if (!j_root) {
|
||||
ERROR("Error, parsing ip link JSON");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!json_is_array(j_root)) {
|
||||
ERROR("Expected a JSON array from ip link");
|
||||
json_decref(j_root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return j_root;
|
||||
}
|
||||
|
||||
static int ly_add_ip_link_data(const struct ly_ctx *ctx, struct lyd_node **parent,
|
||||
char *xpath, json_t *iface)
|
||||
{
|
||||
json_t *j_val;
|
||||
char *val;
|
||||
int err;
|
||||
|
||||
j_val = json_object_get(iface, "ifindex");
|
||||
if (!json_is_integer(j_val)) {
|
||||
ERROR("Expected a JSON integer for 'ifindex'");
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
err = lydx_new_path(ctx, parent, xpath, "if-index", "%lld",
|
||||
json_integer_value(j_val));
|
||||
if (err) {
|
||||
ERROR("Error, adding 'if-index' to data tree, libyang error %d", err);
|
||||
return SR_ERR_LY;
|
||||
}
|
||||
|
||||
j_val = json_object_get(iface, "operstate");
|
||||
if (!json_is_string(j_val)) {
|
||||
ERROR("Expected a JSON string for 'operstate'");
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
val = strdup(json_string_value(j_val));
|
||||
if (!val)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
str_to_lowercase(val);
|
||||
|
||||
err = lydx_new_path(ctx, parent, xpath, "oper-status", val);
|
||||
free(val);
|
||||
if (err) {
|
||||
ERROR("Error, adding 'oper-status' to data tree, libyang error %d", err);
|
||||
return SR_ERR_LY;
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int ly_add_ip_link(const struct ly_ctx *ctx, struct lyd_node **parent, char *ifname)
|
||||
{
|
||||
char xpath[XPATH_MAX] = {};
|
||||
json_t *j_root;
|
||||
json_t *j_iface;
|
||||
int err;
|
||||
|
||||
j_root = json_get_ip_link(ifname);
|
||||
if (!j_root) {
|
||||
ERROR("Error, parsing ip-link JSON");
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
if (json_array_size(j_root) != 1) {
|
||||
ERROR("Error, expected JSON array of single iface");
|
||||
json_decref(j_root);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
j_iface = json_array_get(j_root, 0);
|
||||
|
||||
snprintf(xpath, sizeof(xpath), "%s/interface[name='%s']",
|
||||
XPATH_IFACE_BASE, ifname);
|
||||
|
||||
err = ly_add_ip_link_data(ctx, parent, xpath, j_iface);
|
||||
if (err) {
|
||||
ERROR("Error, adding ip-link info for %s", ifname);
|
||||
json_decref(j_root);
|
||||
return err;
|
||||
}
|
||||
json_decref(j_root);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int sr_ifaces_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
const char *, const char *, uint32_t,
|
||||
struct lyd_node **parent, void *priv)
|
||||
{
|
||||
struct sub *sub = priv;
|
||||
const struct ly_ctx *ctx;
|
||||
sr_conn_ctx_t *con;
|
||||
int err;
|
||||
|
||||
DEBUG("Incoming query for xpath: %s\n", path);
|
||||
|
||||
con = sr_session_get_connection(session);
|
||||
if (!con) {
|
||||
ERROR("Error, getting connection\n");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
ctx = sr_acquire_context(con);
|
||||
if (!ctx) {
|
||||
ERROR("Error, acquiring context\n");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
err = ly_add_ip_link(ctx, parent, sub->ifname);
|
||||
if (err)
|
||||
ERROR("Error, adding ip link info\n");
|
||||
|
||||
sr_release_context(con);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sig_event_cb(struct ev_loop *loop, struct ev_signal *, int)
|
||||
{
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
}
|
||||
|
||||
static void sr_event_cb(struct ev_loop *, struct ev_io *w, int)
|
||||
{
|
||||
struct sub *sub = (struct sub *)w->data;
|
||||
|
||||
sr_subscription_process_events(sub->sr_sub, NULL, NULL);
|
||||
}
|
||||
|
||||
static int sub_to_iface(struct statd *statd, const char *ifname)
|
||||
{
|
||||
char path[XPATH_MAX] = {};
|
||||
struct sub *sub;
|
||||
int sr_ev_pipe;
|
||||
int err;
|
||||
|
||||
sub = sub_find_iface(&statd->subs, ifname);
|
||||
if (sub) {
|
||||
DEBUG("Interface %s already subscribed\n", ifname);
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
sub = malloc(sizeof(struct sub));
|
||||
if (!sub)
|
||||
return SR_ERR_INTERNAL;
|
||||
|
||||
memset(sub, 0, sizeof(struct sub));
|
||||
|
||||
snprintf(sub->ifname, sizeof(sub->ifname), "%s", ifname);
|
||||
snprintf(path, sizeof(path), "%s/interface[name='%s']",
|
||||
XPATH_IFACE_BASE, ifname);
|
||||
|
||||
DEBUG("Subscribe to events for \"%s\"\n", path);
|
||||
err = sr_oper_get_subscribe(statd->sr_ses, "ietf-interfaces",
|
||||
path, sr_ifaces_cb, sub,
|
||||
SR_SUBSCR_DEFAULT | SR_SUBSCR_NO_THREAD | SR_SUBSCR_DONE_ONLY,
|
||||
&sub->sr_sub);
|
||||
if (err) {
|
||||
ERROR("Error, subscribing to path \"%s\": %s\n", path, sr_strerror(err));
|
||||
free(sub);
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
err = sr_get_event_pipe(sub->sr_sub, &sr_ev_pipe);
|
||||
if (err) {
|
||||
ERROR("Error, getting sysrepo event pipe: %s\n", sr_strerror(err));
|
||||
sr_unsubscribe(sub->sr_sub);
|
||||
free(sub);
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
TAILQ_INSERT_TAIL(&statd->subs, sub, entries);
|
||||
|
||||
ev_io_init(&sub->watcher, sr_event_cb, sr_ev_pipe, EV_READ);
|
||||
sub->watcher.data = sub;
|
||||
ev_io_start(statd->ev_loop, &sub->watcher);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static void unsub_to_ifaces(struct statd *statd)
|
||||
{
|
||||
struct sub *sub;
|
||||
|
||||
while (!TAILQ_EMPTY(&statd->subs)) {
|
||||
sub = TAILQ_FIRST(&statd->subs);
|
||||
DEBUG("Unsubscribe from \"%s\" (all)\n", sub->ifname);
|
||||
sub_delete(statd->ev_loop, &statd->subs, sub);
|
||||
}
|
||||
}
|
||||
|
||||
static int unsub_to_iface(struct statd *statd, char *ifname)
|
||||
{
|
||||
struct sub *sub;
|
||||
|
||||
sub = sub_find_iface(&statd->subs, ifname);
|
||||
if (!sub) {
|
||||
ERROR("Error, can't find interface to delete (%s)", ifname);
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
DEBUG("Unsubscribe from \"%s\"\n", sub->ifname);
|
||||
sub_delete(statd->ev_loop, &statd->subs, sub);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int nl_process_msg(struct nlmsghdr *nlh, struct statd *statd)
|
||||
{
|
||||
struct ifinfomsg *iface;
|
||||
struct rtattr *attr;
|
||||
int attr_len;
|
||||
|
||||
iface = NLMSG_DATA(nlh);
|
||||
attr = IFLA_RTA(iface);
|
||||
attr_len = IFLA_PAYLOAD(nlh);
|
||||
|
||||
for (; RTA_OK(attr, attr_len); attr = RTA_NEXT(attr, attr_len)) {
|
||||
if (attr->rta_type == IFLA_IFNAME) {
|
||||
char *ifname = (char *)RTA_DATA(attr);
|
||||
|
||||
if (nlh->nlmsg_type == RTM_NEWLINK)
|
||||
return sub_to_iface(statd, ifname);
|
||||
else if (nlh->nlmsg_type == RTM_DELLINK)
|
||||
return unsub_to_iface(statd, ifname);
|
||||
else
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ignore nl messages with no interface name */
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static void nl_event_cb(struct ev_loop *, struct ev_io *w, int)
|
||||
{
|
||||
struct statd *statd = (struct statd *) w->data;
|
||||
char buf[NL_BUF_SIZE];
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
int len;
|
||||
|
||||
len = recv(statd->nl.sd, buf, sizeof(buf), 0);
|
||||
if (len < 0) {
|
||||
ERROR("Error, netlink recv failed: %s\n", strerror(errno));
|
||||
close(statd->nl.sd);
|
||||
/* NOTE: This is likely caused by a full kernel buffer, which
|
||||
* means we can't trust our list. So we exit hard and let finit
|
||||
* respawn us to handle this.
|
||||
*/
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
|
||||
err = nl_process_msg(nlh, statd);
|
||||
if (err)
|
||||
ERROR("Error, processing netlink message: %s\n", sr_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
static int sub_to_ifaces(struct statd *statd)
|
||||
{
|
||||
json_t *j_iface;
|
||||
json_t *j_root;
|
||||
size_t i;
|
||||
|
||||
j_root = json_get_ip_link(NULL);
|
||||
if (!j_root) {
|
||||
ERROR("Error, parsing ip-link JSON");
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
json_array_foreach(j_root, i, j_iface) {
|
||||
json_t *j_ifname;
|
||||
int err;
|
||||
|
||||
j_ifname = json_object_get(j_iface, "ifname");
|
||||
if (!json_is_string(j_ifname)) {
|
||||
ERROR("Got unexpected JSON type for 'ifname'");
|
||||
continue;
|
||||
}
|
||||
|
||||
err = sub_to_iface(statd, json_string_value(j_ifname));
|
||||
if (err) {
|
||||
ERROR("Unable to subscribe to %s", json_string_value(j_ifname));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
json_decref(j_root);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct ev_signal sig_watcher;
|
||||
struct statd statd = {};
|
||||
sr_conn_ctx_t *sr_conn;
|
||||
int err;
|
||||
|
||||
TAILQ_INIT(&statd.subs);
|
||||
statd.ev_loop = EV_DEFAULT;
|
||||
|
||||
statd.nl.sd = nl_sock_init();
|
||||
if (statd.nl.sd < 0) {
|
||||
ERROR("Error, opening netlink socket\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
INFO("Status daemon starting\n");
|
||||
|
||||
set_sock_rcvbuf(statd.nl.sd, SOCK_RMEM_SIZE);
|
||||
|
||||
err = sr_connect(SR_CONN_DEFAULT, &sr_conn);
|
||||
if (err) {
|
||||
ERROR("Error, connecting to sysrepo: %s\n", sr_strerror(err));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
DEBUG("Connected to sysrepo\n");
|
||||
|
||||
err = sr_session_start(sr_conn, SR_DS_OPERATIONAL, &statd.sr_ses);
|
||||
if (err) {
|
||||
ERROR("Error, start sysrepo session: %s\n", sr_strerror(err));
|
||||
sr_disconnect(sr_conn);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
DEBUG("Session started (%p)\n", statd.sr_ses);
|
||||
|
||||
DEBUG("Attempting to register existing interfaces\n");
|
||||
err = sub_to_ifaces(&statd);
|
||||
if (err) {
|
||||
ERROR("Error, registering existing interfaces\n");
|
||||
sr_disconnect(sr_conn);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ev_signal_init(&sig_watcher, sig_event_cb, SIGINT);
|
||||
sig_watcher.data = &statd;
|
||||
ev_signal_start(statd.ev_loop, &sig_watcher);
|
||||
|
||||
ev_io_init(&statd.nl.watcher, nl_event_cb, statd.nl.sd, EV_READ);
|
||||
statd.nl.watcher.data = &statd;
|
||||
ev_io_start(statd.ev_loop, &statd.nl.watcher);
|
||||
|
||||
INFO("Status daemon entering main event loop\n");
|
||||
ev_run(statd.ev_loop, 0);
|
||||
|
||||
/* We should never get here during normal operation */
|
||||
INFO("Status daemon shutting down\n");
|
||||
unsub_to_ifaces(&statd);
|
||||
sr_session_stop(statd.sr_ses);
|
||||
sr_disconnect(sr_conn);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user