Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 31 May 2015 21:27:27 GMT
From:      roam@FreeBSD.org
To:        svn-soc-all@FreeBSD.org
Subject:   socsvn commit: r286476 - soc2015/roam/ng_ayiya
Message-ID:  <201505312127.t4VLRR7M035786@socsvn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: roam
Date: Sun May 31 21:27:26 2015
New Revision: 286476
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=286476

Log:
  Start a Netgraph AYIYA module.
  
  Not doing much just yet, busy being created.
  
  ObQuote:	"A beginning is a very delicate time."

Added:
  soc2015/roam/ng_ayiya/
  soc2015/roam/ng_ayiya/Makefile
  soc2015/roam/ng_ayiya/ng_ayiya.c
  soc2015/roam/ng_ayiya/ng_ayiya.h
  soc2015/roam/ng_ayiya/scaffold.pl   (contents, props changed)

Added: soc2015/roam/ng_ayiya/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2015/roam/ng_ayiya/Makefile	Sun May 31 21:27:26 2015	(r286476)
@@ -0,0 +1,45 @@
+#!/usr/bin/make
+#
+# Copyright (c) 2015  Peter Pentchev
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+
+KMOD=		ng_ayiya
+SRCS=		ng_ayiya.c
+
+.include <bsd.kmod.mk>
+
+SUDO?=		sudo
+PERL?=		perl
+SCAFFOLD_PL?=	scaffold.pl
+SCAFFOLD_V?=	-v
+SCAFFOLD?=	${SUDO} ${PERL} ${SCAFFOLD_PL} ${SCAFFOLD_V}
+
+up:
+	${SCAFFOLD} setup
+
+down:
+	${SCAFFOLD} shutdown
+
+down-all:
+	${SCAFFOLD} shutdown all

Added: soc2015/roam/ng_ayiya/ng_ayiya.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2015/roam/ng_ayiya/ng_ayiya.c	Sun May 31 21:27:26 2015	(r286476)
@@ -0,0 +1,261 @@
+/**
+ * Copyright (c) 2015  Peter Pentchev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Netgraph AYIYA node.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/sbuf.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+
+#include "ng_ayiya.h"
+
+#ifdef NG_SEPARATE_MALLOC
+static MALLOC_DEFINE(M_NETGRAPH_AYIYA, "netgraph_ayiya", "netgraph AYIYA node");
+#else
+#define M_NETGRAPH_AYIYA	M_NETGRAPH
+#endif
+
+static ng_constructor_t	ng_ayiya_constructor;
+static ng_rcvmsg_t	ng_ayiya_rcvmsg;
+static ng_newhook_t	ng_ayiya_newhook;
+static ng_disconnect_t	ng_ayiya_disconnect;
+static ng_shutdown_t	ng_ayiya_shutdown;
+
+static struct ng_type typestruct = {
+	.version = NG_ABI_VERSION,
+	.name = NG_AYIYA_NODE_TYPE,
+
+	.constructor = ng_ayiya_constructor,
+	.rcvmsg = ng_ayiya_rcvmsg,
+	.newhook = ng_ayiya_newhook,
+	.disconnect = ng_ayiya_disconnect,
+	.shutdown = ng_ayiya_shutdown,
+};
+NETGRAPH_INIT(ayiya, &typestruct);
+
+enum {
+	AYIYA_HOOK_CONTROL,
+	AYIYA_HOOK_INET6,
+	AYIYA_HOOK_AYIYA,
+	AYIYA_HOOK_LAST
+};
+
+#define NG_AYIYA_DECLARE_HOOK(i, n) { .idx = i, .name = n, .len = sizeof(n) - 1 }
+static const struct {
+	int idx;
+	const char *name;
+	size_t len;
+} hookdefs[AYIYA_HOOK_LAST] = {
+	NG_AYIYA_DECLARE_HOOK(AYIYA_HOOK_CONTROL, "control"),
+	NG_AYIYA_DECLARE_HOOK(AYIYA_HOOK_INET6, "inet6"),
+	NG_AYIYA_DECLARE_HOOK(AYIYA_HOOK_AYIYA, "ayiya"),
+};
+
+struct ng_ayiya_private {
+	node_p	node;
+	char *	secrethash;
+	hook_p	hooks[AYIYA_HOOK_LAST];
+};
+typedef struct ng_ayiya_private *priv_p;
+
+static int
+ng_ayiya_constructor(const node_p node)
+{
+	priv_p priv;
+
+	priv = malloc(sizeof(*priv), M_NETGRAPH_AYIYA, M_WAITOK | M_ZERO);
+	NG_NODE_SET_PRIVATE(node, priv);
+	priv->node = node;
+	return (0);
+}
+
+#define ERROUT(x)	do { error = (x); goto done; } while (0)
+
+static void
+build_status(struct sbuf * const sb, const node_p node, const bool json)
+{
+	priv_p priv = NG_NODE_PRIVATE(node);
+	if (json)
+		sbuf_printf(sb,
+		    "{\n"
+		    "\t\"id\":\t\"%x\",\n"
+		    "\t\"name\":\t\"%s\",\n"
+		    "\t\"has_secret\":\t%s,\n"
+		    "\t\"hooks\": {\n",
+		    node->nd_ID, node->nd_name,
+		    priv->secrethash? "true": "false");
+	else
+		sbuf_printf(sb,
+		    "Node [%x] %s\n"
+		    "Secret hash %sset\n",
+		    node->nd_ID, node->nd_name,
+		    priv->secrethash? "": "not ");
+
+	for (int idx = 0; idx < AYIYA_HOOK_LAST; idx++) {
+		const char * const hname = hookdefs[idx].name;
+		const size_t hidx = hookdefs[idx].idx;
+		const hook_p hook = priv->hooks[hidx];
+
+		if (hook != NULL) {
+			const node_p peer = NG_PEER_NODE(hook);
+			if (json)
+				sbuf_printf(sb,
+				    "\t\t\"%s\": {\n"
+				    "\t\t\t\"hook_name\":\t\"%s\",\n"
+				    "\t\t\t\"id\":\t\"%x\",\n"
+				    "\t\t\t\"name\":\t\"%s\",\n"
+				    "\t\t\t\"type\":\t\"%s\"\n"
+				    "\t\t}%s\n",
+				    hname, NG_HOOK_NAME(hook),
+				    peer->nd_ID, peer->nd_name,
+				    peer->nd_type->name,
+				    idx < AYIYA_HOOK_LAST - 1? ",": "");
+			else
+				sbuf_printf(sb,
+				    "Hook '%s' (%s) connected to node "
+				    "[%x] '%s' of type '%s'.\n",
+				    NG_HOOK_NAME(hook), hname,
+				    peer->nd_ID, peer->nd_name,
+				    peer->nd_type->name);
+		} else {
+			if (json)
+				sbuf_printf(sb, "\t\t\"%s\":\tnull%s\n",
+				    hname,
+				    idx < AYIYA_HOOK_LAST - 1? ",": "");
+			else
+				sbuf_printf(sb,
+				    "Hook '%s' not connected yet.\n",
+				    hname);
+		}
+	}
+
+	if (json)
+		sbuf_printf(sb, "\t}\n}");
+}
+
+static int
+ng_ayiya_rcvmsg(const node_p node, item_p item, const hook_p lasthook)
+{
+	struct ng_mesg *resp = NULL;
+	int error = 0;
+	struct ng_mesg *msg;
+
+	NGI_GET_MSG(item, msg);
+	switch (msg->header.typecookie) {
+	case NGM_GENERIC_COOKIE:
+		switch (msg->header.cmd) {
+		case NGM_TEXT_CONFIG:
+		case NGM_TEXT_STATUS:
+			if (msg->header.arglen != 0)
+				ERROUT(EINVAL);
+
+			struct sbuf *sb = sbuf_new_auto();
+			build_status(sb, node,
+			    msg->header.cmd == NGM_TEXT_CONFIG);
+			if (sbuf_finish(sb) != 0) {
+				sbuf_delete(sb);
+				ERROUT(ENOMEM);
+			}
+			const ssize_t len = sbuf_len(sb);
+			NG_MKRESPONSE(resp, msg, len + 1, M_WAITOK);
+			bcopy(sbuf_data(sb), resp->data, len);
+			resp->data[len] = '\0';
+			sbuf_delete(sb);
+			break;
+
+		default:
+			error = EINVAL;
+			break;
+		}
+		break;
+
+	default:
+		error = EINVAL;
+		break;
+	}
+
+done:
+	NG_RESPOND_MSG(error, node, item, resp);
+	NG_FREE_MSG(msg);
+	return (error);
+}
+
+static int
+ng_ayiya_newhook(const node_p node, const hook_p hook,
+	const char * const name)
+{
+	int idx;
+	for (idx = 0; idx < AYIYA_HOOK_LAST; idx++) {
+		const char * const hname = hookdefs[idx].name;
+		const size_t hlen = hookdefs[idx].len;
+
+		if (strncmp(name, hname, hlen) == 0 &&
+		    (name[hlen] == '\0' || name[hlen] == '/'))
+		{
+			idx = hookdefs[idx].idx;
+			break;
+		}
+	}
+	if (idx == AYIYA_HOOK_LAST)
+		return (EINVAL);
+	const priv_p priv = NG_NODE_PRIVATE(node);
+	if (priv->hooks[idx] != NULL)
+		return (EISCONN);
+
+	/* TODO: some more checks here */
+
+	priv->hooks[idx] = hook;
+	return (0);
+}
+
+static int
+ng_ayiya_disconnect(const hook_p hook)
+{
+	priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+	int idx;
+	for (idx = 0; idx < AYIYA_HOOK_LAST; idx++)
+		if (priv->hooks[idx] == hook)
+			break;
+	if (idx == AYIYA_HOOK_LAST)
+		panic("%s", __func__);
+	priv->hooks[idx] = NULL;
+	return (0);
+}
+
+static int
+ng_ayiya_shutdown(const node_p node)
+{
+	const priv_p priv = NG_NODE_PRIVATE(node);
+
+	free(priv, M_NETGRAPH_AYIYA);
+	NG_NODE_SET_PRIVATE(node, NULL);
+	NG_NODE_UNREF(node);
+	return (0);
+}

Added: soc2015/roam/ng_ayiya/ng_ayiya.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2015/roam/ng_ayiya/ng_ayiya.h	Sun May 31 21:27:26 2015	(r286476)
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2015  Peter Pentchev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef _NETGRAPH_NG_AYIYA_H
+#define _NETGRAPH_NG_AYIYA_H
+
+/* Node type name and magic cookie */
+#define NG_AYIYA_NODE_TYPE	"ayiya"
+#define NGM_AYIYA_COOKIE	1432823247
+
+#endif

Added: soc2015/roam/ng_ayiya/scaffold.pl
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2015/roam/ng_ayiya/scaffold.pl	Sun May 31 21:27:26 2015	(r286476)
@@ -0,0 +1,358 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 2015  Peter Pentchev
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+
+use v5.12;
+use strict;
+use warnings;
+
+use Getopt::Std;
+use JSON::PP;
+use POSIX qw/:sys_wait_h/;
+
+my $debug = 0;
+my ($tempfile, $tempname);
+
+sub debug($);
+sub usage($);
+sub version();
+
+sub get_ayiya;
+sub ngctl_list();
+sub ngctl($ @);
+sub run_command(@);
+sub check_wait_result($ $ $);
+
+sub cmd_help($ @);
+sub cmd_setup($ @);
+sub cmd_shutdown($ @);
+sub cmd_status($ @);
+sub cmd_version($ @);
+
+my %cmds = (
+	build => \&cmd_setup,
+	erect => \&cmd_setup,
+	help => \&cmd_help,
+	setup => \&cmd_setup,
+	shutdown => \&cmd_shutdown,
+	teardown => \&cmd_shutdown,
+	status => \&cmd_status,
+	version => \&cmd_version,
+);
+
+MAIN:
+{
+	my %opts;
+
+	getopts('hVv', \%opts) or usage(1);
+	version() if $opts{V};
+	usage 0 if $opts{h};
+	exit 0 if $opts{V} || $opts{h};
+	$debug = $opts{v};
+
+	usage 1 unless @ARGV;
+	my $cmd = shift @ARGV;
+	my $handler = $cmds{$cmd};
+	if (!defined $handler) {
+		say STDERR "Invalid command '$cmd'";
+		usage 1;
+	} else {
+		$handler->($cmd, @ARGV);
+	}
+}
+
+sub usage($)
+{
+	my ($err) = @_;
+	my $s = <<EOUSAGE
+Usage:	scaffold help
+	scaffold [-v] setup
+	scaffold [-v] shutdown [all]
+	scaffold [-v] status
+	scaffold -V | -h
+
+	-h	display program usage information and exit
+	-V	display program version information and exit
+	-v	verbose operation; display diagnostic output
+EOUSAGE
+	;
+
+	if ($err) {
+		die $s;
+	} else {
+		print "$s";
+	}
+}
+
+sub version()
+{
+	say 'scaffold 0.1.0';
+}
+
+sub debug($)
+{
+	say STDERR "RDBG $_[0]" if $debug;
+}
+
+sub check_wait_result($ $ $)
+{
+	my ($stat, $pid, $name) = @_;
+
+	if (WIFEXITED($stat)) {
+		if (WEXITSTATUS($stat) != 0) {
+			die "Program '$name' (pid $pid) exited with ".
+			    "non-zero status ".WEXITSTATUS($stat)."\n";
+		}
+	} elsif (WIFSIGNALED($stat)) {
+		die "Program '$name' (pid $pid) was killed by signal ".
+		    WTERMSIG($stat)."\n";
+	} elsif (WIFSTOPPED($stat)) {
+		die "Program '$name' (pid $pid) was stopped by signal ".
+		    WSTOPSIG($stat)."\n";
+	} else {
+		die "Program '$name' (pid $pid) neither exited nor was ".
+		    "it killed or stopped; what does wait(2) status $stat ".
+		    "mean?!\n";
+	}
+}
+
+sub cmd_help($ @)
+{
+	usage 0;
+}
+
+sub cmd_version($ @)
+{
+	version;
+}
+
+sub cmd_status($ @)
+{
+	my ($cmd, @args) = @_;
+
+	if (@args) {
+		say STDERR "The 'status' command does not need any arguments";
+		usage 1;
+	}
+
+	my $ay = get_ayiya;
+	my @all = @{$ay->{all}};
+	if (!@all) {
+		say 'No AYIYA nodes configured';
+		return;
+	}
+	say scalar(@all).' AYIYA node'.(@all == 1? '': 's').' found'.
+	    (@all? ': '.(join ', ', map "[$_->{id}] $_->{name}", @all): '');
+	my $node = $ay->{ours};
+	if (!defined $node) {
+		say "Ours is not there";
+		return;
+	}
+	say "Ours is there: [$node->{id}] $node->{name}";
+	# TODO: config, status, hooks, interfaces
+}
+
+sub cmd_shutdown($ @)
+{
+	my ($cmd, @args) = @_;
+
+	my $all;
+	if (@args == 1 && $args[0] eq 'all') {
+		$all = 1;
+	} elsif (@args) {
+		say STDERR "Invalid arguments to 'shutdown'";
+		usage 1;
+	}
+
+	my $ay = get_ayiya;
+	if ($ay->{ours}) {
+		debug "Shutting down our node $ay->{ours}->{id}";
+		ngctl 'shutdown', "[$ay->{ours}->{id}]:";
+	} else {
+		say "Our node not found";
+	}
+	if ($all) {
+		debug "Shutting down ".scalar(@{$ay->{others}}).
+		    " other node(s)";
+		for (@{$ay->{others}}) {
+			debug "- $_->{id}";
+			ngctl 'shutdown', "[$_->{id}]:";
+		}
+	}
+
+	if ((run_command 'kldstat') =~ /ng_ayiya\.ko/) {
+		run_command 'make', 'unload';
+	} else {
+		debug "The ng_ayiya.ko module is not even loaded";
+	}
+}
+
+sub cmd_setup($ @)
+{
+	my ($cmd, @args) = @_;
+
+	if (@args) {
+		say STDERR "The 'status' command does not need any arguments";
+		usage 1;
+	}
+
+	if ((run_command 'kldstat') !~ /ng_ayiya\.ko/) {
+		debug "Trying to build the ng_ayiya.ko module";
+		debug run_command 'make', 'depend';
+		debug run_command 'make';
+		debug "Trying to load the ng_ayiya.ko module";
+		run_command 'make', 'load';
+	} else {
+		debug "The ng_ayiya.ko module seems to be loaded already";
+	}
+
+	my $ay = get_ayiya;
+	if (!$ay->{ours}) {
+		my %found = map { ($_->{id}, 1) } @{$ay->{all}};
+
+		debug "Creating a new AYIYA node";
+		ngctl 'mkpeer', 'ayiya', 'a', 'control/create';
+		$ay = get_ayiya;
+		my @new = map $_->{id}, @{$ay->{all}};
+		debug "Looking for an ID in (".join(' ', sort @new).
+		    ") that's not in (".join(' ', sort keys %found).")";
+		my $id;
+		for (@new) {
+			if (!defined $found{$_}) {
+				$id = $_;
+				last;
+			}
+		}
+		if (!defined $id) {
+			die "Internal error: no new AYIYA nodes\n";
+		}
+		debug "- found $id";
+		ngctl 'name', "[$id]:", 'sc_ayiya';
+		$ay = get_ayiya;
+		if (!$ay->{ours} || $ay->{ours}->{id} ne $id) {
+			die "Internal error: get_ayiya() did not recognize ".
+			    "node [$id] as ours\n";
+		}
+	} else {
+		debug "Our node already there";
+	}
+
+	# TODO: interfaces, hooks...
+	debug "Setup complete, our node is ".
+	    "[$ay->{ours}->{id}] $ay->{ours}->{name}";
+}
+
+sub get_ayiya()
+{
+	my $nodes = ngctl_list;
+	my $res = {
+		all => $nodes->{type}{ayiya},
+	};
+	my $node;
+	my @ay = @{$res->{all} // []};
+	debug "Got ".scalar(@ay)." AYIYA node(s), looking for ours...";
+	for(my $i = 0; $i < @ay; $i++) {
+		if ($ay[$i]->{name} eq 'sc_ayiya') {
+			debug "- found it at position $i";
+			($node) = splice @ay, $i, 1;
+			last;
+		}
+	}
+	$res->{ours} = $node;
+	$res->{others} = \@ay;
+
+	if (defined $node) {
+		my $js = ngctl 'config', "$node->{name}:";
+		$js =~ s/\A[^{]*//s;
+		debug "- got ".length($js)." characters of JSON";
+		my $d = decode_json $js;
+		if (!defined($d) || ref $d ne 'HASH' ||
+		    grep !exists $d->{$_}, qw/id name has_secret hooks/) {
+			die "Node [$node->{id}] '$node->{name}' returned ".
+			    "an invalid JSON configuration\n";
+		}
+		debug "- got keys: ".join(' ', sort keys %{$d});
+	}
+	return $res;
+}
+
+sub ngctl_list()
+{
+	my $s = ngctl 'list';
+
+	my %nodes;
+	my @lines = split /\n+/, $s;
+	for (@lines) {
+		if (/^\s*Name:\s*(\S+)\s+Type:\s*(\S+)\s+ID:\s*0*(\S+)\s+Num hooks:\s*(\S+)\s*$/) {
+			my ($name, $type, $id, $hooks) = ($1, $2, $3, $4);
+			my $node = {
+				name => $name,
+				type => $type,
+				id => $id,
+				num_hooks => $hooks,
+			};
+			$nodes{id}{$id} = $node;
+			push @{$nodes{type}{$type}}, $node;
+			if ($name ne '<unnamed>') {
+				$nodes{name}{$name} = $node;
+			}
+		}
+	}
+	return \%nodes;
+}
+
+sub ngctl($ @)
+{
+	my ($cmd, @args) = @_;
+
+	return run_command 'ngctl', $cmd, @args;
+}
+
+sub run_command(@)
+{
+	my @cmd = @_;
+	debug "About to run @cmd";
+	my $pid = open(my $pipe, '-|');
+	if (!defined $pid) {
+		die "Could not fork for '@cmd': $!\n";
+	} elsif ($pid == 0) {
+		exec { $cmd[0] } @cmd;
+		die "Could not run '@cmd': $!\n";
+	}
+
+	my $output;
+	{
+		local $/;
+		$output = <$pipe>;
+	}
+	my $res = close $pipe;
+	my $msg = $!;
+	my $status = $?;
+	check_wait_result $status, $pid, "@cmd";
+	if (!$res) {
+		die "Some error occurred closing the pipe from '@cmd': $msg\n";
+	}
+	return $output;
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201505312127.t4VLRR7M035786>