From d469e2f56e299a9d767d7db1f00646741f7ce367 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 28 Aug 2020 15:50:19 -0500 Subject: configd: T2582: add shim as config daemon client --- src/shim/Makefile | 20 ++++ src/shim/vyshim.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 307 insertions(+) create mode 100644 src/shim/Makefile create mode 100644 src/shim/vyshim.c (limited to 'src') diff --git a/src/shim/Makefile b/src/shim/Makefile new file mode 100644 index 000000000..c8487e3c2 --- /dev/null +++ b/src/shim/Makefile @@ -0,0 +1,20 @@ +DEBUG = 0 + +CC := gcc +CFLAGS := -I./mkjson -L./mkjson/lib -DDEBUG=${DEBUG} +LIBS := -lmkjson -lzmq + +.PHONY: vyshim +vyshim: vyshim.c libmkjson + $(CC) $(CFLAGS) -o $@ $< $(LIBS) + +.PHONY: libmkjson +libmkjson: + $(MAKE) -C mkjson + +all: vyshim + +.PHONY: clean +clean: + $(MAKE) -C mkjson clean + rm -f vyshim diff --git a/src/shim/vyshim.c b/src/shim/vyshim.c new file mode 100644 index 000000000..8b6feab99 --- /dev/null +++ b/src/shim/vyshim.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2020 VyOS maintainers and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or later as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mkjson.h" + +/* + * + * + */ + +#if DEBUG +#define DEBUG_ON 1 +#else +#define DEBUG_ON 0 +#endif +#define debug_print(fmt, ...) \ + do { if (DEBUG_ON) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0) +#define debug_call(f) \ + do { if (DEBUG_ON) f; } while (0) + +#define SOCKET_PATH "ipc:///run/vyos-configd.sock" + +#define GET_ACTIVE "cli-shell-api --show-active-only --show-show-defaults --show-ignore-edit showConfig" +#define GET_SESSION "cli-shell-api --show-working-only --show-show-defaults --show-ignore-edit showConfig" + +#define COMMIT_MARKER "/var/tmp/initial_in_commit" + +enum { + SUCCESS = 1 << 0, + ERROR_COMMIT = 1 << 1, + ERROR_DAEMON = 1 << 2, + PASS = 1 << 3 +}; + +volatile int init_alarm = 0; +volatile int timeout = 0; + +int initialization(void *); +int pass_through(char **, int); +void timer_handler(int); + +double get_posix_clock_time(void); + +int main(int argc, char* argv[]) +{ + // string for node data: conf_mode script and tagnode, if applicable + char string_node_data[256]; + string_node_data[0] = '\0'; + + void *context = zmq_ctx_new(); + void *requester = zmq_socket(context, ZMQ_REQ); + + int init_timeout = 0; + + debug_print("Connecting to vyos-configd ...\n"); + zmq_connect(requester, SOCKET_PATH); + + if (access(COMMIT_MARKER, F_OK) != -1) { + init_timeout = initialization(requester); + if (!init_timeout) remove(COMMIT_MARKER); + } + + int end = argc > 3 ? 2 : argc - 1; + + // if initial communication failed, pass through execution of script + if (init_timeout) { + int ret = pass_through(argv, end); + return ret; + } + + for (int i = end; i > 0 ; i--) { + strncat(&string_node_data[0], argv[i], 127); + } + + char error_code[1]; + debug_print("Sending node data ...\n"); + char *string_node_data_msg = mkjson(MKJSON_OBJ, 2, + MKJSON_STRING, "type", "node", + MKJSON_STRING, "data", &string_node_data[0]); + + zmq_send(requester, string_node_data_msg, strlen(string_node_data_msg), 0); + zmq_recv(requester, error_code, 1, 0); + debug_print("Received node data receipt\n"); + + int err = (int)error_code[0]; + + free(string_node_data_msg); + + zmq_close(requester); + zmq_ctx_destroy(context); + + if (err & PASS) { + debug_print("Received PASS\n"); + int ret = pass_through(argv, end); + return ret; + } + + if (err & ERROR_DAEMON) { + debug_print("Received ERROR_DAEMON\n"); + int ret = pass_through(argv, end); + return ret; + } + + if (err & ERROR_COMMIT) { + debug_print("Received ERROR_COMMIT\n"); + return -1; + } + + return 0; +} + +int initialization(void* Requester) +{ + char *active_str = NULL; + size_t active_len = 0; + + char *session_str = NULL; + size_t session_len = 0; + + char *empty_string = "\n"; + + char buffer[16]; + + struct sigaction sa; + struct itimerval timer, none_timer; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &timer_handler; + sigaction(SIGALRM, &sa, NULL); + + timer.it_value.tv_sec = 0; + timer.it_value.tv_usec = 10000; + timer.it_interval.tv_sec = timer.it_interval.tv_usec = 0; + none_timer.it_value.tv_sec = none_timer.it_value.tv_usec = 0; + none_timer.it_interval.tv_sec = none_timer.it_interval.tv_usec = 0; + + double prev_time_value, time_value; + double time_diff; + + debug_print("Sending init announcement\n"); + char *init_announce = mkjson(MKJSON_OBJ, 1, + MKJSON_STRING, "type", "init"); + + // check for timeout on initial contact + while (!init_alarm) { + debug_call(prev_time_value = get_posix_clock_time()); + + setitimer(ITIMER_REAL, &timer, NULL); + + zmq_send(Requester, init_announce, strlen(init_announce), 0); + zmq_recv(Requester, buffer, 16, 0); + + setitimer(ITIMER_REAL, &none_timer, &timer); + + debug_call(time_value = get_posix_clock_time()); + + debug_print("Received init receipt\n"); + debug_call(time_diff = time_value - prev_time_value); + debug_print("time elapse %f\n", time_diff); + + break; + } + + free(init_announce); + + if (timeout) return -1; + + FILE *fp_a = popen(GET_ACTIVE, "r"); + getdelim(&active_str, &active_len, '\0', fp_a); + int ret = pclose(fp_a); + + if (!ret) { + debug_print("Sending active config\n"); + zmq_send(Requester, active_str, active_len - 1, 0); + zmq_recv(Requester, buffer, 16, 0); + debug_print("Received active receipt\n"); + } else { + debug_print("Sending empty active config\n"); + zmq_send(Requester, empty_string, 0, 0); + zmq_recv(Requester, buffer, 16, 0); + debug_print("Received active receipt\n"); + } + + free(active_str); + + FILE *fp_s = popen(GET_SESSION, "r"); + getdelim(&session_str, &session_len, '\0', fp_s); + pclose(fp_s); + + debug_print("Sending session config\n"); + zmq_send(Requester, session_str, session_len - 1, 0); + zmq_recv(Requester, buffer, 16, 0); + debug_print("Received session receipt\n"); + + free(session_str); + + return 0; +} + +int pass_through(char **argv, int end) +{ + char *newargv[] = { NULL, NULL }; + pid_t child_pid; + + newargv[0] = argv[end]; + if (end > 1) { + putenv(argv[end - 1]); + } + + debug_print("pass-through invoked\n"); + + if ((child_pid=fork()) < 0) { + debug_print("fork() failed\n"); + return -1; + } else if (child_pid == 0) { + if (-1 == execv(argv[end], newargv)) { + debug_print("pass_through execve failed %s: %s\n", + argv[end], strerror(errno)); + return -1; + } + } else if (child_pid > 0) { + int status; + pid_t wait_pid = waitpid(child_pid, &status, 0); + if (wait_pid < 0) { + debug_print("waitpid() failed\n"); + return -1; + } else if (wait_pid == child_pid) { + if (WIFEXITED(status)) { + debug_print("child exited with code %d\n", + WEXITSTATUS(status)); + return WEXITSTATUS(status); + } + } + } + + return 0; +} + +void timer_handler(int signum) +{ + debug_print("timer_handler invoked\n"); + timeout = 1; + init_alarm = 1; + + return; +} + +#ifdef _POSIX_MONOTONIC_CLOCK +double get_posix_clock_time(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + return (double) (ts.tv_sec + ts.tv_nsec / 1000000000.0); + } else { + return 0; + } +} +#else +double get_posix_clock_time(void) +{return (double)0;} +#endif -- cgit v1.2.3