From 7eec8c2964af9045b58c2f40f970fb80325ea6c8 Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 5 Apr 2023 11:47:12 +0200 Subject: [PATCH] sysrest: Initial add A RESTCONF front-end for sysrepo. --- board/common/rootfs/etc/nginx/nginx.conf | 7 +- package/Config.in | 1 + package/confd/clean-etc | 2 +- package/sysrest/Config.in | 6 + package/sysrest/sysrest.conf | 1 + package/sysrest/sysrest.mk | 20 +++ src/sysrest/.gitignore | 33 +++++ src/sysrest/LICENSE | 25 ++++ src/sysrest/Makefile.am | 3 + src/sysrest/README.md | 15 +++ src/sysrest/autogen.sh | 3 + src/sysrest/configure.ac | 50 ++++++++ src/sysrest/src/Makefile.am | 6 + src/sysrest/src/main.c | 147 +++++++++++++++++++++++ 14 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 package/sysrest/Config.in create mode 100644 package/sysrest/sysrest.conf create mode 100644 package/sysrest/sysrest.mk create mode 100644 src/sysrest/.gitignore create mode 100644 src/sysrest/LICENSE create mode 100644 src/sysrest/Makefile.am create mode 100644 src/sysrest/README.md create mode 100755 src/sysrest/autogen.sh create mode 100644 src/sysrest/configure.ac create mode 100644 src/sysrest/src/Makefile.am create mode 100644 src/sysrest/src/main.c diff --git a/board/common/rootfs/etc/nginx/nginx.conf b/board/common/rootfs/etc/nginx/nginx.conf index 13189508..0f469881 100644 --- a/board/common/rootfs/etc/nginx/nginx.conf +++ b/board/common/rootfs/etc/nginx/nginx.conf @@ -45,8 +45,13 @@ http { index index.html index.htm; } + location /.well-known/host-meta { + fastcgi_pass unix:/run/sysrest.sock; + include fastcgi_params; + } + location /restconf/ { - fastcgi_pass unix:/var/run/clixon/restconf.sock; + fastcgi_pass unix:/run/sysrest.sock; include fastcgi_params; } diff --git a/package/Config.in b/package/Config.in index f9e24aff..fc4b16c3 100644 --- a/package/Config.in +++ b/package/Config.in @@ -18,6 +18,7 @@ source "$BR2_EXTERNAL_INFIX_PATH/package/python-templating/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/skeleton-init-finit/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/sysrepo-plugins-common/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/sysrepo-plugin-system/Config.in" +source "$BR2_EXTERNAL_INFIX_PATH/package/sysrest/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/tetris/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/umgmt/Config.in" source "$BR2_EXTERNAL_INFIX_PATH/package/querierd/Config.in" diff --git a/package/confd/clean-etc b/package/confd/clean-etc index 342f47e4..68950034 100755 --- a/package/confd/clean-etc +++ b/package/confd/clean-etc @@ -7,7 +7,7 @@ # ... except for some core services reserved() { - for svc in dnsmasq getty klish lldpd nginx sysklogd sysrepo; do + for svc in dnsmasq getty klish lldpd nginx sysklogd sysrepo sysrest; do [ "$1" = "${svc}.conf" ] && return 0 done diff --git a/package/sysrest/Config.in b/package/sysrest/Config.in new file mode 100644 index 00000000..ac1e8dc5 --- /dev/null +++ b/package/sysrest/Config.in @@ -0,0 +1,6 @@ +config BR2_PACKAGE_SYSREST + bool "sysrest" + depends on BR2_PACKAGE_SYSREPO + select BR2_PACKAGE_LIBCFGI + help + RESTCONF front-end for Sysrepo diff --git a/package/sysrest/sysrest.conf b/package/sysrest/sysrest.conf new file mode 100644 index 00000000..a66a4f84 --- /dev/null +++ b/package/sysrest/sysrest.conf @@ -0,0 +1 @@ +service if: [2345789] sysrest -- RESTCONF daemon diff --git a/package/sysrest/sysrest.mk b/package/sysrest/sysrest.mk new file mode 100644 index 00000000..57dd0a0c --- /dev/null +++ b/package/sysrest/sysrest.mk @@ -0,0 +1,20 @@ +################################################################################ +# +# sysrest +# +################################################################################ + +SYSREST_VERSION = 1.0 +SYSREST_LICENSE = BSD-3-Clause +SYSREST_SITE_METHOD = local +SYSREST_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/sysrest +SYSREST_DEPENDENCIES = sysrepo libfcgi +SYSREST_AUTORECONF = YES + +define SYSREST_INSTALL_EXTRA + cp $(SYSREST_PKGDIR)/sysrest.conf $(FINIT_D)/available/ + ln -sf ../available/sysrest.conf $(FINIT_D)/enabled/sysrest.conf +endef +SYSREST_TARGET_FINALIZE_HOOKS += SYSREST_INSTALL_EXTRA + +$(eval $(autotools-package)) diff --git a/src/sysrest/.gitignore b/src/sysrest/.gitignore new file mode 100644 index 00000000..e9e8e747 --- /dev/null +++ b/src/sysrest/.gitignore @@ -0,0 +1,33 @@ +*~ +*.o +*.la +*.lo +*.so +.deps +.libs + +/aclocal.m4 +/autom4te.cache/ +/aux +/clixon.xml +/compile +/config.h +/config.h.in +/config.guess +/config.log +/config.status +/config.sub +/configure +/depcomp +/install-sh +/libtool +/ltmain.sh +/m4 +/missing + +GPATH +GRTAGS +GTAGS +Makefile +Makefile.in + diff --git a/src/sysrest/LICENSE b/src/sysrest/LICENSE new file mode 100644 index 00000000..39b47d31 --- /dev/null +++ b/src/sysrest/LICENSE @@ -0,0 +1,25 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* 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. + +* Neither the name of copyright holders nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + diff --git a/src/sysrest/Makefile.am b/src/sysrest/Makefile.am new file mode 100644 index 00000000..66442e23 --- /dev/null +++ b/src/sysrest/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src +DISTCLEANFILES = *~ *.d +ACLOCAL_AMFLAGS = -I m4 diff --git a/src/sysrest/README.md b/src/sysrest/README.md new file mode 100644 index 00000000..02951ac8 --- /dev/null +++ b/src/sysrest/README.md @@ -0,0 +1,15 @@ +Configuration Daemon +==================== + +`confd` is the Infix configuration daemon that serves as the glue +between sysrepo and netopeer2 (NETCONF) and the UNIX system under +it all. + + +Origin & References +------------------- + +Based on the Open Source [dklibc/sysrepo_plugin_ietf_system][0] +project by Denis Kalashnikov. + +[0]: https://github.com/dklibc/sysrepo_plugin_ietf_system diff --git a/src/sysrest/autogen.sh b/src/sysrest/autogen.sh new file mode 100755 index 00000000..69ad0e18 --- /dev/null +++ b/src/sysrest/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +autoreconf -W portability -vifm diff --git a/src/sysrest/configure.ac b/src/sysrest/configure.ac new file mode 100644 index 00000000..16b7e3e1 --- /dev/null +++ b/src/sysrest/configure.ac @@ -0,0 +1,50 @@ +AC_PREREQ(2.61) +AC_INIT([sysrest], [1.0.0], + [https://github.com/kernelkit/infix/issues]) +AM_INIT_AUTOMAKE(1.11 foreign subdir-objects) +AM_SILENT_RULES(yes) + +LT_INIT + +AC_CONFIG_FILES([ + Makefile + src/Makefile +]) + +AC_PROG_CC +AC_PROG_INSTALL + +# Check for pkg-config first, warn if it's not installed +PKG_PROG_PKG_CONFIG + +PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36]) +PKG_CHECK_MODULES([fcgi], [fcgi >= 2.4.2]) + +# Plugin installation path for sysrepo-plugind +dnl PKG_CHECK_VAR([srpdplugindir], [sysrepo], [SRPD_PLUGINS_PATH]) +dnl AC_SUBST(srpdplugindir) + +dnl test "x$prefix" = xNONE && prefix=$ac_default_prefix +dnl test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +dnl DATAROOTDIR=`eval echo $datarootdir` +dnl DATAROOTDIR=`eval echo $DATAROOTDIR` +dnl AC_SUBST(DATAROOTDIR) + +dnl LIBDIR=`eval echo $libdir` +dnl LIBDIR=`eval echo $LIBDIR` +dnl AC_SUBST(LIBDIR) + +dnl LOCALSTATEDIR=`eval echo $localstatedir` +dnl LOCALSTATEDIR=`eval echo $LOCALSTATEDIR` +dnl AC_SUBST(LOCALSTATEDIR) + +dnl RUNSTATEDIR=`eval echo $runstatedir` +dnl RUNSTATEDIR=`eval echo $RUNSTATEDIR` +dnl AC_SUBST(RUNSTATEDIR) + +dnl SYSCONFDIR=`eval echo $sysconfdir` +dnl SYSCONFDIR=`eval echo $SYSCONFDIR` +dnl AC_SUBST(SYSCONFDIR) + +AC_OUTPUT diff --git a/src/sysrest/src/Makefile.am b/src/sysrest/src/Makefile.am new file mode 100644 index 00000000..34439ecf --- /dev/null +++ b/src/sysrest/src/Makefile.am @@ -0,0 +1,6 @@ +sbin_PROGRAMS = sysrest + +sysrest_SOURCES = main.c +sysrest_CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter \ + $(sysrepo_CFLAGS) $(fcgi_CFLAGS) +sysrest_LDADD = $(sysrepo_LIBS) $(fcgi_LIBS) diff --git a/src/sysrest/src/main.c b/src/sysrest/src/main.c new file mode 100644 index 00000000..596338be --- /dev/null +++ b/src/sysrest/src/main.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include + +static const char *rootpath = "/restconf/"; + +static void dumpenv(char **envp) +{ + fprintf(stderr, "ENV:\n"); + for (; *envp; envp++) { + fprintf(stderr, " %s\n", *envp); + } +} + +int serve_host_meta(FCGX_Request *req) +{ + const char *method; + size_t len; + char *buf; + FILE *fp; + + method = FCGX_GetParam("REQUEST_METHOD", req->envp); + if (!method || (strcmp(method, "GET") && strcmp(method, "HEAD"))) + return -EINVAL; + + fp = open_memstream(&buf, &len); + if (!fp) + return -ENOMEM; + + fprintf(fp, + "" + "" + "", rootpath); + fclose(fp); + + FCGX_FPrintF(req->out, "Content-Type: application/xrd+xml\r\n"); + FCGX_FPrintF(req->out, "Content-Length: %zd\r\n", len); + FCGX_FPrintF(req->out, "Connection: close\r\n"); + FCGX_PutS("\r\n", req->out); + + if (!strcmp(method, "GET")) + FCGX_PutStr(buf, len, req->out); + + free(buf); + + FCGX_Finish_r(req); + return 0; +} + +int serve_restconf(FCGX_Request *req) +{ + FCGX_FPrintF(req->out, "Content-type: text/html\r\n\r\n"); + FCGX_FPrintF(req->out, "RESTCONF\r\n"); + FCGX_Finish_r(req); + return 0; +} + +int serve(int sd) +{ + FCGX_Request req; + const char *uri; + int err = 0; + + for (FCGX_InitRequest(&req, sd, 0); + FCGX_Accept_r(&req) == 0; + FCGX_InitRequest(&req, sd, 0)) { + dumpenv(req.envp); + + uri = FCGX_GetParam("REQUEST_URI", req.envp); + if (!uri) + continue; + + if (!strcmp(uri, "/.well-known/host-meta")) + err = serve_host_meta(&req); + else if (!strncmp(uri, rootpath, strlen(rootpath))) + err = serve_restconf(&req); + else + err = -EINVAL; + + if (err) { + FCGX_FPrintF(req.out, "Status: 400 Bad Request\r\n"); + FCGX_FPrintF(req.out, "Content-Type: text/html\r\n"); + FCGX_FPrintF(req.out, "Connection: close\r\n"); + FCGX_PutS("\r\n", req.out); + FCGX_Finish_r(&req); + break; + } + } + + FCGX_Free(&req, sd); + + if (err) + fprintf(stderr, "Unable to handle request: %d\n", err); + + return err ? 1 : 0; +} + +int setup_sock(const char *sockpath, const char *groupname) +{ + struct group *g; + int sd; + + sd = FCGX_OpenSocket(sockpath, 10); + if (sd < 0) { + perror("Unable to create socket"); + return -1; + } + + g = getgrnam(groupname); + if (!g) + return -1; + + if (chown(sockpath, -1, g->gr_gid)) { + perror("Unable to set socket group"); + return -1; + } + + if (chmod(sockpath, S_IRWXU|S_IRWXG|S_IROTH) < 0){ + perror("Unable to set socket permissions"); + return -1; + } + + return sd; +} + +int main(int argc, char **argv, char **envp) +{ + int sd; + + if (FCGX_Init()) { + perror("Unable to initialize libfcgi"); + return 1; + } + + sd = setup_sock("/run/sysrest.sock", "www-data"); + if (sd < 0) + return 1; + + return serve(sd); +}