Compare commits

...
Author SHA1 Message Date
Tobias Waldekranz 964a4c755e [TEMP] save 2023-04-12 09:36:50 +02:00
Tobias Waldekranz 7eec8c2964 sysrest: Initial add
A RESTCONF front-end for sysrepo.
2023-04-05 17:00:01 +02:00
15 changed files with 680 additions and 2 deletions
+6 -1
View File
@@ -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;
}
+1
View File
@@ -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"
+1 -1
View File
@@ -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
+6
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
service if:<boot/netconf> [2345789] sysrest -- RESTCONF daemon
+20
View File
@@ -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))
+33
View File
@@ -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
+25
View File
@@ -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.
+3
View File
@@ -0,0 +1,3 @@
SUBDIRS = src
DISTCLEANFILES = *~ *.d
ACLOCAL_AMFLAGS = -I m4
+15
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
autoreconf -W portability -vifm
+51
View File
@@ -0,0 +1,51 @@
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([fcgi], [fcgi >= 2.4.2])
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
PKG_CHECK_MODULES([libyang], [libyang >= 2.1.55])
# 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
+6
View File
@@ -0,0 +1,6 @@
sbin_PROGRAMS = sysrest
sysrest_SOURCES = main.c
sysrest_CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter \
$(libyang_CFLAGS) $(sysrepo_CFLAGS) $(fcgi_CFLAGS)
sysrest_LDADD = $(libyang_LIBS) $(sysrepo_LIBS) $(fcgi_LIBS)
+404
View File
@@ -0,0 +1,404 @@
#include <errno.h>
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcgiapp.h>
#include <libyang/libyang.h>
#include <libyang/plugins_exts.h>
#include <sysrepo.h>
#include <sysrepo_types.h>
static const char *rootpath = "/restconf/";
enum http_method {
HTTP_GET,
HTTP_HEAD,
HTTP_PATCH,
HTTP_POST,
HTTP_PUT,
HTTP_INVALID
};
/* struct memstream { */
/* FILE *fp; */
/* char *buf; */
/* size_t len; */
/* }; */
/* struct res { */
/* struct memstream head; */
/* struct memstream entity; */
/* }; */
/* int res_push_header(struct res *res, const char *key, const char *valfmt, ...) */
/* { */
/* fprintf */
/* } */
/* int res_respond_to(struct res *res, FCGX_Request *req) */
/* { */
/* } */
/* int res_init(struct res *res) */
/* { */
/* res->head.fp = open_memstream(&res->head.buf, &res->head.len); */
/* if (!res->head.fp) */
/* goto err; */
/* res->entity.fp = open_memstream(&res->entity.buf, &res->entity.len); */
/* if (!res->entity.fp) */
/* goto err_free_head; */
/* return 0; */
/* err_free_head: */
/* fclose(res->head.fp); */
/* err: */
/* return -ENOMEM; */
/* } */
struct req {
FCGX_Request r;
enum http_method method;
char *uri;
union {
struct {
char *path;
} data;
};
};
static enum http_method req_parse_method(struct req *req)
{
const char *str = FCGX_GetParam("REQUEST_METHOD", req->r.envp);
if (!str)
return HTTP_INVALID;
if (!strcmp(str, "GET"))
return HTTP_GET;
if (!strcmp(str, "HEAD"))
return HTTP_HEAD;
if (!strcmp(str, "PATCH"))
return HTTP_PATCH;
if (!strcmp(str, "POST"))
return HTTP_POST;
if (!strcmp(str, "PUT"))
return HTTP_PUT;
return HTTP_INVALID;
}
static int req_init(struct req *req)
{
const char *uri;
req->method = req_parse_method(req);
uri = FCGX_GetParam("REQUEST_URI", req->r.envp);
if (!uri)
return -EINVAL;
req->uri = strdup(uri);
if (!req->uri)
return -EINVAL;
return 0;
}
static int req_fini(struct req *req)
{
free(req->uri);
return 0;
}
static void dumpenv(char **envp)
{
fprintf(stderr, "ENV:\n");
for (; *envp; envp++) {
fprintf(stderr, " %s\n", *envp);
}
}
int serve_host_meta(struct req *req)
{
size_t len;
char *buf;
FILE *fp;
switch (req->method) {
case HTTP_GET:
case HTTP_HEAD:
break;
default:
return -EINVAL;
}
fp = open_memstream(&buf, &len);
if (!fp)
return -ENOMEM;
fprintf(fp,
"<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>"
"<Link rel='restconf' href='%s'/>"
"</XRD>", rootpath);
fclose(fp);
FCGX_FPrintF(req->r.out, "Content-Type: application/xrd+xml\r\n");
FCGX_FPrintF(req->r.out, "Content-Length: %zd\r\n", len);
FCGX_FPrintF(req->r.out, "Connection: close\r\n");
FCGX_PutS("\r\n", req->r.out);
if (req->method == HTTP_GET)
FCGX_PutStr(buf, len, req->r.out);
free(buf);
FCGX_Finish_r(&req->r);
return 0;
}
int hex2bin(char *hex)
{
int i, out;
for (i = 0, out = 0; i < 2; i++) {
out <<= 4;
if (hex[i] >= '0' && hex[i] <= '9')
out |= hex[0] - '0';
else if (hex[i] >= 'a' && hex[i] <= 'f')
out |= hex[0] - 'a';
else if (hex[i] >= 'A' && hex[i] <= 'F')
out |= hex[0] - 'A';
else
return -EINVAL;
}
if (out < 0 || out >= 0x80)
/* No UTF-8 support */
return -EINVAL;
return out;
}
int unpercent(char *tok)
{
char *in, *out, *end;
int escaped;
in = out = tok;
for (; *in; in++, out++) {
if (*in != '%') {
*out = *in;
continue;
}
escaped = hex2bin(in + 1);
if (escaped < 0)
return escaped;
*out = escaped;
in += 2;
}
*out = '\0';
return 0;
}
int xpath_from_uri(struct ly_ctx *ly, char *uri, char **xpathp)
{
}
int uri_resolve_r(sr_data_t **nodep, char **urip, char **toksave)
{
char *tok;
int err;
tok = strtok_r(*toksave ? NULL : *urip, "/", toksave);
if (!tok)
return 0;
err = unpercent(tok);
if (err)
return err;
}
int uri_resolve(sr_session_ctx_t *sess, char *uri, struct lyd_node **nodep)
{
sr_data_t *node;
char *toksave = NULL;
int err;
err = sr_get_subtree(sess, "/", 0, &node);
if (err)
return err;
err = uri_resolve_r(&node, &uri, &toksave);
if (err)
return err;
tok = srst_uritok(uri);
if (!tok)
return 0;
}
int serve_restconf_data(struct req *req)
{
sr_session_ctx_t *sess;
sr_conn_ctx_t *conn;
sr_data_t *data;
int err = 0;
FILE *fp;
char *buf;
size_t len = 0;
if (req->method != HTTP_GET) {
err = -EINVAL;
goto err;
}
if (sr_connect(SR_CONN_DEFAULT, &conn)) {
err = -EIO;
goto err;
}
if (sr_session_start(conn, SR_DS_RUNNING, &sess)) {
err = -EIO;
goto err_disconnect;
}
sr_get_data(sess, req->data.path, 0, 0, 0, &data);
fp = open_memstream(&buf, &len);
lyd_print_file(fp, data ? data->tree : NULL, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
fclose(fp);
FCGX_FPrintF(req->r.out, "Content-Type: application/yang-data+json\r\n");
FCGX_FPrintF(req->r.out, "Content-Length: %zd\r\n", len);
FCGX_FPrintF(req->r.out, "Connection: close\r\n");
FCGX_PutS("\r\n", req->r.out);
FCGX_PutStr(buf, len, req->r.out);
free(buf);
FCGX_Finish_r(&req->r);
return 0;
err_disconnect:
sr_disconnect(conn);
err:
return err;
}
int serve_restconf(struct req *req)
{
char *uri = req->uri + strlen(rootpath);
if (!strncmp(uri, "data/", strlen("data/"))) {
req->data.path = uri + strlen("data/");
return serve_restconf_data(req);
}
return -EINVAL;
}
int serve(int sd)
{
struct req req;
int err = 0;
for (FCGX_InitRequest(&req.r, sd, 0);
FCGX_Accept_r(&req.r) == 0;
FCGX_InitRequest(&req.r, sd, 0)) {
if (req_init(&req))
continue;
dumpenv(req.r.envp);
if (!strcmp(req.uri, "/.well-known/host-meta"))
err = serve_host_meta(&req);
else if (!strncmp(req.uri, rootpath, strlen(rootpath)))
err = serve_restconf(&req);
else
err = -EINVAL;
if (err) {
FCGX_FPrintF(req.r.out, "Status: 400 Bad Request\r\n");
FCGX_FPrintF(req.r.out, "Content-Type: text/html\r\n");
FCGX_FPrintF(req.r.out, "Connection: close\r\n");
FCGX_PutS("\r\n", req.r.out);
FCGX_Finish_r(&req.r);
}
req_fini(&req);
}
FCGX_Free(&req.r, 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);
}
+105
View File
@@ -0,0 +1,105 @@
#include <errno.h>
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libyang/libyang.h>
int hex2bin(char *hex)
{
int i, out;
for (i = 0, out = 0; i < 2; i++) {
out <<= 4;
if (hex[i] >= '0' && hex[i] <= '9')
out |= hex[0] - '0';
else if (hex[i] >= 'a' && hex[i] <= 'f')
out |= hex[0] - 'a';
else if (hex[i] >= 'A' && hex[i] <= 'F')
out |= hex[0] - 'A';
else
return -EINVAL;
}
if (out < 0 || out >= 0x80)
/* No UTF-8 support */
return -EINVAL;
return out;
}
int unpercent(char *text)
{
char *in, *out, *end;
int escaped;
in = out = text;
for (; *in; in++, out++) {
if (*in != '%') {
*out = *in;
continue;
}
escaped = hex2bin(in + 1);
if (escaped < 0)
return escaped;
*out = escaped;
in += 2;
}
*out = '\0';
return 0;
}
struct uri_node {
char *module;
char *name;
char *keyv[8];
};
int uri_node_from_seg(char *seg, struct uri_node *n)
{
int keyc = 0;
char *delim;
memset(n, 0, sizeof(*n));
delim = strchr(seg, ':');
if (delim) {
*delim++ = '\0';
n->module = seg;
n->name = seg = delim;
} else {
n->name = seg;
}
delim = strchr(seg, '=');
if (!delim)
return 0;
do {
if (keyc >= 7)
return -EINVAL;
*delim++ = '\0';
n->keyv[keyc++] = seg = delim;
} while ((delim = strchr(seg, ','); keyc++));
return 0;
}
int xpath_from_uri(struct ly_ctx *ly, char *uri, char **xpathp)
{
}
int main(void)
{
}