1133 lines
27 KiB
C
1133 lines
27 KiB
C
/*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <syslog.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <fcgi_stdio.h>
|
|
#include <signal.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <curl/curl.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clicon */
|
|
#include <clixon/clixon.h>
|
|
|
|
#include "restconf_lib.h"
|
|
|
|
#define signal_set_mask(set) sigprocmask(SIG_SETMASK, (set), NULL)
|
|
#define signal_get_mask(set) sigprocmask (0, NULL, (set))
|
|
|
|
/* Some hardcoded paths */
|
|
#define CLI_BIN "/usr/local/bin/clixon_cli"
|
|
#define CLI_OPTS "-1 -q"
|
|
|
|
/*
|
|
* Types (curl)
|
|
*/
|
|
struct curlbuf{
|
|
size_t b_len;
|
|
char *b_buf;
|
|
};
|
|
|
|
#ifdef notused
|
|
static int _log = 0; /* to log or not to log */
|
|
/*!
|
|
*/
|
|
int
|
|
dbg_init(char *ident)
|
|
{
|
|
openlog(ident, LOG_PID, LOG_USER);
|
|
_log++;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
int
|
|
dbg(char *format, ...)
|
|
{
|
|
va_list args;
|
|
int len;
|
|
int retval = -1;
|
|
char *msg = NULL;
|
|
|
|
if (_log == 0)
|
|
return 0;
|
|
/* first round: compute length of debug message */
|
|
va_start(args, format);
|
|
len = vsnprintf(NULL, 0, format, args);
|
|
va_end(args);
|
|
/* allocate a message string exactly fitting the message length */
|
|
if ((msg = malloc(len+1)) == NULL){
|
|
fprintf(stderr, "malloc: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
|
|
goto done;
|
|
}
|
|
/* second round: compute write message from format and args */
|
|
va_start(args, format);
|
|
if (vsnprintf(msg, len+1, format, args) < 0){
|
|
va_end(args);
|
|
fprintf(stderr, "vsnprintf: %s\n", strerror(errno));
|
|
goto done;
|
|
}
|
|
va_end(args);
|
|
syslog(LOG_MAKEPRI(LOG_USER, LOG_DEBUG), "%s", msg);
|
|
retval = 0;
|
|
done:
|
|
if (msg)
|
|
free(msg);
|
|
return retval;
|
|
}
|
|
#endif /* notused */
|
|
/*!
|
|
*/
|
|
int
|
|
notfound(FCGX_Request *r)
|
|
{
|
|
char *path;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
|
FCGX_FPrintF(r->out, "Status: 404\r\n"); /* 404 not found */
|
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
|
FCGX_FPrintF(r->out, "<h1>Grideye Not Found</h1>\n");
|
|
FCGX_FPrintF(r->out, "The requested URL %s was not found on this server.\n",
|
|
path);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*! Map from keywords grideye config to influxdb interval format
|
|
*/
|
|
char *
|
|
ival2influxdb(char *ival)
|
|
{
|
|
if (strcmp(ival, "minute")==0)
|
|
return "1m";
|
|
else if (strcmp(ival, "hour")==0)
|
|
return "1h";
|
|
else if (strcmp(ival, "day")==0)
|
|
return "1d";
|
|
else if (strcmp(ival, "week")==0)
|
|
return "7d"; /* 1w is sunday to sunday */
|
|
else if (strcmp(ival, "month")==0)
|
|
return "30d";
|
|
else if (strcmp(ival, "year")==0 || strcmp(ival, "all")==0)
|
|
return "365d";
|
|
return "1w";
|
|
}
|
|
|
|
/* ripped from clicon_proc.c: clicon_proc_run()
|
|
* @inparam[in] instr pipe to process stdin.
|
|
*/
|
|
static int
|
|
proc_run(char *cmd,
|
|
char *instr,
|
|
void (outcb)(char *, void *),
|
|
void *arg)
|
|
{
|
|
char **argv;
|
|
char buf[512];
|
|
int outfd[2] = { -1, -1 };
|
|
int infd[2] = { -1, -1 };
|
|
int n;
|
|
int argc;
|
|
int status;
|
|
int retval = -1;
|
|
pid_t child;
|
|
sigfn_t oldhandler = NULL;
|
|
sigset_t oset;
|
|
|
|
clicon_debug(1, "%s %s", __FUNCTION__, cmd);
|
|
argv = clicon_sepsplit (cmd, " \t", &argc, __FUNCTION__);
|
|
if (!argv)
|
|
return -1;
|
|
|
|
if (pipe (outfd) == -1)
|
|
goto done;
|
|
if (pipe (infd) == -1)
|
|
goto done;
|
|
|
|
signal_get_mask(&oset);
|
|
// set_signal(SIGINT, clicon_proc_sigint, &oldhandler);
|
|
|
|
if ((child = fork ()) < 0) {
|
|
retval = -1;
|
|
goto done;
|
|
}
|
|
|
|
if (child == 0) { /* Child */
|
|
|
|
/* Unblock all signals except TSTP */
|
|
clicon_signal_unblock (0);
|
|
signal (SIGTSTP, SIG_IGN);
|
|
|
|
close (outfd[0]); /* Close unused read ends */
|
|
outfd[0] = -1;
|
|
|
|
close (infd[1]); /* Close unused read ends */
|
|
infd[1] = -1;
|
|
|
|
/* Divert stdout and stderr to pipes */
|
|
dup2 (outfd[1], STDOUT_FILENO);
|
|
if (0)
|
|
dup2 (outfd[1], STDERR_FILENO);
|
|
|
|
dup2 (infd[0], STDIN_FILENO);
|
|
execvp (argv[0], argv);
|
|
perror("execvp");
|
|
_exit(-1);
|
|
}
|
|
|
|
/* Parent */
|
|
/* Close unused read ends */
|
|
close (infd[0]);
|
|
infd[0] = -1;
|
|
if (instr){
|
|
if (write(infd[1], instr, strlen(instr)) < 0){
|
|
perror("write");
|
|
goto done;
|
|
}
|
|
close(infd[1]);
|
|
}
|
|
|
|
/* Close unused write ends */
|
|
close (outfd[1]);
|
|
outfd[1] = -1;
|
|
/* Read from pipe */
|
|
while ((n = read (outfd[0], buf, sizeof (buf)-1)) != 0) {
|
|
if (n < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
break;
|
|
}
|
|
buf[n] = '\0';
|
|
/* Pass read data to callback function is defined */
|
|
if (outcb)
|
|
outcb (buf, arg);
|
|
}
|
|
/* Wait for child to finish */
|
|
if(waitpid (child, &status, 0) == child)
|
|
retval = WEXITSTATUS(status);
|
|
else
|
|
retval = -1;
|
|
done:
|
|
|
|
/* Clean up all pipes */
|
|
if (outfd[0] != -1)
|
|
close (outfd[0]);
|
|
if (outfd[1] != -1)
|
|
close (outfd[1]);
|
|
|
|
/* Restore sigmask and fn */
|
|
signal_set_mask (&oset);
|
|
set_signal(SIGINT, oldhandler, NULL);
|
|
|
|
unchunk_group (__FUNCTION__);
|
|
clicon_debug(1, "%s end %d", __FUNCTION__, retval);
|
|
return retval;
|
|
}
|
|
|
|
/*! Open dir/filename and pipe to fast-cgi output
|
|
* @param[in] r Fastcgi request handle
|
|
* Either absolute path in filename _or_ concatenation of dir/filename.
|
|
*/
|
|
int
|
|
openfile(FCGX_Request *r,
|
|
char *dir,
|
|
char *filename)
|
|
{
|
|
int retval = -1;
|
|
char buf[512];
|
|
int f;
|
|
int n;
|
|
cbuf *cb = NULL;
|
|
|
|
if ((cb = cbuf_new()) == NULL)
|
|
goto done;
|
|
if (dir)
|
|
cprintf(cb, "%s/%s", dir, filename);
|
|
else
|
|
cprintf(cb, "%s", filename);
|
|
clicon_debug(1, "%s: %s", __FUNCTION__, cbuf_get(cb));
|
|
if ((f = open(cbuf_get(cb), O_RDONLY)) < 0){
|
|
clicon_debug(1, "open error: %s", strerror(errno));
|
|
perror("open");
|
|
goto done;
|
|
}
|
|
while ((n = read(f, buf, sizeof (buf)-1)) != 0) {
|
|
if (n < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
perror("read");
|
|
goto done;
|
|
}
|
|
buf[n] = '\0';
|
|
FCGX_FPrintF(r->out, "%s", buf);
|
|
}
|
|
retval = 0;
|
|
done:
|
|
cbuf_free(cb);
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
* @param[in] r Fastcgi request handle
|
|
*/
|
|
int
|
|
errorfn(FCGX_Request *r,
|
|
char *root,
|
|
char *reason)
|
|
{
|
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n");
|
|
FCGX_FPrintF(r->out, "\r\n");
|
|
openfile(r, root, "www/login.html"); /* "user not specified" */
|
|
FCGX_FPrintF(r->out, "<div class=\"errbox\">%s</div>\r\n", reason);
|
|
return 0;
|
|
}
|
|
|
|
/*! Split a string into a cligen variable vector using 1st and 2nd delimiter
|
|
* Split a string first into elements delimited by delim1, then into
|
|
* pairs delimited by delim2.
|
|
* @param[in] string String to split
|
|
* @param[in] delim1 First delimiter char that delimits between elements
|
|
* @param[in] delim2 Second delimiter char for pairs within an element
|
|
* @param[out] cvp Created cligen variable vector, NOTE: can be NULL
|
|
* @retval 0 on OK
|
|
* @retval -1 error
|
|
*
|
|
* Example, assuming delim1 = '&' and delim2 = '='
|
|
* a=b&c=d -> [[a,"b"][c="d"]
|
|
* kalle&c=d -> [[c="d"]] # Discard elements with no delim2
|
|
* XXX differentiate between error and null cvec.
|
|
*/
|
|
int
|
|
str2cvec(char *string,
|
|
char delim1,
|
|
char delim2,
|
|
cvec **cvp)
|
|
{
|
|
int retval = -1;
|
|
char *s;
|
|
char *s0 = NULL;;
|
|
char *val; /* value */
|
|
char *valu; /* unescaped value */
|
|
char *snext; /* next element in string */
|
|
cvec *cvv = NULL;
|
|
cg_var *cv;
|
|
|
|
clicon_debug(1, "%s %s", __FUNCTION__, string);
|
|
if ((s0 = strdup(string)) == NULL){
|
|
clicon_debug(1, "error strdup %s", strerror(errno));
|
|
goto err;
|
|
}
|
|
s = s0;
|
|
if ((cvv = cvec_new(0)) ==NULL){
|
|
clicon_debug(1, "error cvec_new %s", strerror(errno));
|
|
goto err;
|
|
}
|
|
while (s != NULL) {
|
|
/*
|
|
* In the pointer algorithm below:
|
|
* name1=val1; name2=val2;
|
|
* ^ ^ ^
|
|
* | | |
|
|
* s val snext
|
|
*/
|
|
if ((snext = index(s, delim1)) != NULL)
|
|
*(snext++) = '\0';
|
|
if ((val = index(s, delim2)) != NULL){
|
|
*(val++) = '\0';
|
|
if ((valu = curl_easy_unescape(NULL, val, 0, NULL)) == NULL){
|
|
clicon_debug(1, "curl_easy_unescape %s", strerror(errno));
|
|
goto err;
|
|
}
|
|
if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
|
|
clicon_debug(1, "error cvec_add %s", strerror(errno));
|
|
goto err;
|
|
}
|
|
cv_name_set(cv, s);
|
|
cv_string_set(cv, valu);
|
|
free(valu);
|
|
}
|
|
else{
|
|
if (strlen(s)){
|
|
if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
|
|
clicon_debug(1, "error cvec_add %s", strerror(errno));
|
|
goto err;
|
|
}
|
|
cv_name_set(cv, s);
|
|
cv_string_set(cv, "");
|
|
}
|
|
}
|
|
s = snext;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
*cvp = cvv;
|
|
if (s0)
|
|
free(s0);
|
|
return retval;
|
|
err:
|
|
if (cvv){
|
|
cvec_free(cvv);
|
|
cvv = NULL;
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void
|
|
appendfn(char *str,
|
|
void *arg)
|
|
{
|
|
cbuf *cb = (cbuf *)arg;
|
|
|
|
cprintf(cb, "%s", str);
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
static void
|
|
outputfn(char *str,
|
|
void *arg)
|
|
{
|
|
FCGX_Request *r = (FCGX_Request *)arg;
|
|
|
|
FCGX_FPrintF(r->out, "%s", str);
|
|
clicon_debug(1, "%s: %s", __FUNCTION__, str);
|
|
}
|
|
|
|
/*! Send an RPC to netconf client and return result as string
|
|
* param[out] result output from cli as cligen buf
|
|
* param[in] format stdarg variable list format a la printf followed by args
|
|
*/
|
|
int
|
|
netconf_rpc(cbuf *result,
|
|
char *format, ...)
|
|
{
|
|
int retval = -1;
|
|
cbuf *cb = NULL;
|
|
va_list args;
|
|
int len;
|
|
char *data = NULL;
|
|
char *endtag;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
if ((cb = cbuf_new()) == NULL)
|
|
goto done;
|
|
va_start(args, format);
|
|
len = vsnprintf(NULL, 0, format, args);
|
|
va_end(args);
|
|
if ((data = malloc(len+1)) == NULL){
|
|
fprintf(stderr, "malloc: %s\n", strerror(errno));
|
|
goto done;
|
|
}
|
|
va_start(args, format);
|
|
vsnprintf(data, len+1, format, args);
|
|
va_end(args);
|
|
cprintf(cb, "%s -f %s %s",
|
|
NETCONF_BIN, CONFIG_FILE, NETCONF_OPTS);
|
|
clicon_debug(1, "%s: cmd:%s", __FUNCTION__, cbuf_get(cb));
|
|
clicon_debug(1, "%s: data=%s", __FUNCTION__, data);
|
|
if (proc_run(cbuf_get(cb), data, appendfn, result) < 0)
|
|
goto done;
|
|
if ((endtag = strstr(cbuf_get(result), "]]>]]>")) != NULL)
|
|
*endtag = '\0';
|
|
retval = 0;
|
|
done:
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
if (data)
|
|
free(data);
|
|
return retval;
|
|
}
|
|
|
|
/*! send netconf command and return output to fastcgi request
|
|
* @param[in] r Fastcgi request handle
|
|
*/
|
|
int
|
|
netconf_cmd(FCGX_Request *r,
|
|
char *data)
|
|
{
|
|
int retval = -1;
|
|
cbuf *cb = NULL;
|
|
|
|
if ((cb = cbuf_new()) == NULL)
|
|
goto done;
|
|
cprintf(cb, "%s -f %s %s", NETCONF_BIN, CONFIG_FILE, NETCONF_OPTS);
|
|
if (proc_run(cbuf_get(cb), data, outputfn, r) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
return retval;
|
|
}
|
|
|
|
/*! Send an rpc to cli client and return result as string
|
|
* param[out] result output from cli as cligen buf
|
|
* param[in] mode CLI mode, typically "configure" or "operation"
|
|
* param[in] format stdarg variable list format a la printf followed by args
|
|
*/
|
|
int
|
|
cli_rpc(cbuf *result,
|
|
char *mode,
|
|
char *format, ...)
|
|
{
|
|
int retval = -1;
|
|
cbuf *cb = NULL;
|
|
va_list args;
|
|
int len;
|
|
char *cmd = NULL;
|
|
|
|
if ((cb = cbuf_new()) == NULL)
|
|
goto done;
|
|
va_start(args, format);
|
|
len = vsnprintf(NULL, 0, format, args);
|
|
va_end(args);
|
|
if ((cmd = malloc(len+1)) == NULL){
|
|
fprintf(stderr, "malloc: %s\n", strerror(errno));
|
|
goto done;
|
|
}
|
|
va_start(args, format);
|
|
vsnprintf(cmd, len+1, format, args);
|
|
va_end(args);
|
|
cprintf(cb, "%s -f %s -m %s %s -- %s",
|
|
CLI_BIN, CONFIG_FILE, mode, CLI_OPTS, cmd);
|
|
if (proc_run(cbuf_get(cb), NULL, appendfn, result) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
if (cmd)
|
|
free(cmd);
|
|
return retval;
|
|
}
|
|
|
|
/*! send cli command and return output to fastcgi request
|
|
* @param[in] r Fastcgi request handle
|
|
* @param[in] mode Command mode, eg configure, operation
|
|
* @param[in] cmd Command to run in CLI
|
|
*/
|
|
int
|
|
cli_cmd(FCGX_Request *r,
|
|
char *mode,
|
|
char *cmd)
|
|
{
|
|
int retval = -1;
|
|
cbuf *cb = NULL;
|
|
|
|
if ((cb = cbuf_new()) == NULL)
|
|
goto done;
|
|
cprintf(cb, "%s -f %s -m %s %s -- %s",
|
|
CLI_BIN, CONFIG_FILE, mode, CLI_OPTS, cmd);
|
|
if (proc_run(cbuf_get(cb), NULL, outputfn, r) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
return retval;
|
|
}
|
|
|
|
/*! Check in db if user exists, then if passwd matches or if passwd == null
|
|
* @param[in] passwd Passwd. If NULL dont do passwd check
|
|
* @param[in] cx Parse-tree with matching user
|
|
*/
|
|
int
|
|
check_credentials(char *passwd,
|
|
cxobj *cx)
|
|
{
|
|
int retval = 0;
|
|
cxobj *x;
|
|
|
|
if ((x = xpath_first(cx, "//user/resultdb/password")) == NULL)
|
|
goto done;
|
|
clicon_debug(1, "%s passwd=%s", __FUNCTION__, xml_body(x));
|
|
if (passwd == NULL || strcmp(passwd, xml_body(x))==0)
|
|
retval = 1;
|
|
done:
|
|
return retval;
|
|
}
|
|
|
|
/*! Get matching top-level list entry given an attribute value pair, eg name="foo"
|
|
* Construct an XPATH query and send to netconf clixon client to get config xml.
|
|
* The xpath is canonically formed as: //<entry>[<attr>=<val>]
|
|
* @param[in] entry XPATH base path
|
|
* @param[in] attr Attribute name, if not given skip the [addr=val]
|
|
* @param[in] val Value of attribute
|
|
* @param[out] cx This is an xml tree containing the result
|
|
*/
|
|
int
|
|
get_db_entry(char *entry,
|
|
char *attr,
|
|
char *val,
|
|
cxobj **cx)
|
|
{
|
|
int retval = -1;
|
|
cbuf *resbuf = NULL;
|
|
char *xmlstr;
|
|
|
|
clicon_debug(1, "%s //%s[%s=%s]", __FUNCTION__, entry, attr, val);
|
|
if ((resbuf = cbuf_new()) == NULL)
|
|
goto done;
|
|
if (attr){
|
|
if (netconf_rpc(resbuf,
|
|
"<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"//%s[%s=%s]\" /></get-config></rpc>]]>]]>",
|
|
entry, attr, val) < 0){
|
|
clicon_debug(1, "%s error", __FUNCTION__);
|
|
goto done;
|
|
}
|
|
}
|
|
else
|
|
if (netconf_rpc(resbuf,
|
|
"<rpc><get-config><source><running/></source><filter type=\"xpath\" select=\"//%s\" /></get-config></rpc>]]>]]>",
|
|
entry) < 0){
|
|
clicon_debug(1, "%s error", __FUNCTION__);
|
|
goto done;
|
|
}
|
|
xmlstr = cbuf_get(resbuf);
|
|
// clicon_debug(1, "%s: %s\n", __FUNCTION__, xmlstr);
|
|
if (clicon_xml_parse_string(&xmlstr, cx) < 0){
|
|
clicon_debug(1, "err:%d reason:%s", clicon_errno, clicon_err_reason);
|
|
goto done;
|
|
}
|
|
if (*cx == NULL)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (resbuf)
|
|
cbuf_free(resbuf);
|
|
return retval;
|
|
}
|
|
|
|
/*! Parse a cookie string and return value of cookie attribute
|
|
* @param[in] cookiestr cookie string according to rfc6265 (modified)
|
|
* @param[in] attribute cookie attribute
|
|
* @param[out] val malloced cookie value, free with free()
|
|
*/
|
|
int
|
|
get_user_cookie(char *cookiestr,
|
|
char *attribute,
|
|
char **val)
|
|
{
|
|
int retval = -1;
|
|
cvec *cvv = NULL;
|
|
char *c;
|
|
|
|
clicon_debug(1, "%s cookiestr=%s", __FUNCTION__, cookiestr);
|
|
if (str2cvec(cookiestr, ';', '=', &cvv) < 0)
|
|
goto done;
|
|
if ((c = cvec_find_str(cvv, attribute)) != NULL){
|
|
if ((*val = strdup(c)) == NULL)
|
|
goto done;
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (cvv)
|
|
cvec_free(cvv);
|
|
clicon_debug(1, "%s end %d", __FUNCTION__, retval);
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
* @param[in] r Fastcgi request handle
|
|
*/
|
|
static int
|
|
printparam(FCGX_Request *r,
|
|
char *e,
|
|
int dbgp)
|
|
{
|
|
char *p = FCGX_GetParam(e, r->envp);
|
|
|
|
if (dbgp)
|
|
clicon_debug(1, "%s = '%s'", e, p?p:"");
|
|
else
|
|
FCGX_FPrintF(r->out, "%s = '%s'\n", e, p?p:"");
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* @param[in] r Fastcgi request handle
|
|
*/
|
|
int
|
|
test(FCGX_Request *r,
|
|
int dbg)
|
|
{
|
|
printparam(r, "QUERY_STRING", dbg);
|
|
printparam(r, "REQUEST_METHOD", dbg);
|
|
printparam(r, "CONTENT_TYPE", dbg);
|
|
printparam(r, "CONTENT_LENGTH", dbg);
|
|
printparam(r, "SCRIPT_FILENAME", dbg);
|
|
printparam(r, "SCRIPT_NAME", dbg);
|
|
printparam(r, "REQUEST_URI", dbg);
|
|
printparam(r, "DOCUMENT_URI", dbg);
|
|
printparam(r, "DOCUMENT_ROOT", dbg);
|
|
printparam(r, "SERVER_PROTOCOL", dbg);
|
|
printparam(r, "GATEWAY_INTERFACE", dbg);
|
|
printparam(r, "SERVER_SOFTWARE", dbg);
|
|
printparam(r, "REMOTE_ADDR", dbg);
|
|
printparam(r, "REMOTE_PORT", dbg);
|
|
printparam(r, "SERVER_ADDR", dbg);
|
|
printparam(r, "SERVER_PORT", dbg);
|
|
printparam(r, "SERVER_NAME", dbg);
|
|
printparam(r, "HTTP_COOKIE", dbg);
|
|
printparam(r, "HTTPS", dbg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* @param[in] r Fastcgi request handle
|
|
*/
|
|
cbuf *
|
|
readdata(FCGX_Request *r)
|
|
{
|
|
int c;
|
|
cbuf *cb;
|
|
|
|
if ((cb = cbuf_new()) == NULL)
|
|
return NULL;
|
|
while ((c = FCGX_GetChar(r->in)) != -1)
|
|
cprintf(cb, "%c", c);
|
|
return cb;
|
|
}
|
|
|
|
|
|
/*! Create influxdb database
|
|
* @param[in] server_addr Name of http server. dns or ip
|
|
* @param[in] database Name of database to create
|
|
* @param[in] www_user Root admin user
|
|
* @param[in] www_passwd Password of root user
|
|
*/
|
|
int
|
|
create_database(char *server_addr,
|
|
char *database,
|
|
char *www_user,
|
|
char *www_passwd)
|
|
{
|
|
int retval = -1;
|
|
cbuf *curl = NULL;
|
|
cbuf *cdata = NULL;
|
|
|
|
if ((curl = cbuf_new()) == NULL) /* url */
|
|
goto done;
|
|
if ((cdata = cbuf_new()) == NULL) /* data */
|
|
goto done;
|
|
cprintf(curl, "http://%s:8086/db", server_addr);
|
|
cprintf(cdata, "{\"name\": \"%s\"}", database);
|
|
clicon_debug(1, "%s: %s %s", __FUNCTION__, cbuf_get(curl), cbuf_get(cdata));
|
|
if (url_post(cbuf_get(curl), www_user, www_passwd, cbuf_get(cdata), NULL, NULL) < 0)
|
|
goto done;
|
|
retval = 0;
|
|
done:
|
|
if (curl)
|
|
cbuf_free(curl);
|
|
if (cdata)
|
|
cbuf_free(cdata);
|
|
return retval;
|
|
}
|
|
|
|
/*! Create user passwd in database
|
|
* @param[in] server_addr Name of http server. dns or ip
|
|
* @param[in] database Name of database to create
|
|
* @param[in] user User to create for database (not root)
|
|
* @param[in] passwd Password of user
|
|
* @param[in] www_user Root user to create databases
|
|
* @param[in] www_passwd Password of root user
|
|
*/
|
|
int
|
|
create_db_user(char *server_addr,
|
|
char *database,
|
|
char *user,
|
|
char *password,
|
|
char *www_user,
|
|
char *www_passwd)
|
|
{
|
|
int retval = -1;
|
|
cbuf *curl = NULL;
|
|
cbuf *cdata = NULL;
|
|
|
|
if ((curl = cbuf_new()) == NULL) /* url */
|
|
goto done;
|
|
if ((cdata = cbuf_new()) == NULL) /* data */
|
|
goto done;
|
|
cprintf(curl, "http://%s:8086/db/%s/users", server_addr, database);
|
|
cprintf(cdata, "{\"name\": \"%s\", \"password\": \"%s\"}",
|
|
user, password);
|
|
if (url_post(cbuf_get(curl), www_user, www_passwd, cbuf_get(cdata), NULL, NULL) < 0)
|
|
goto done;
|
|
clicon_debug(1, "%s: %s %s", __FUNCTION__, cbuf_get(curl), cbuf_get(cdata));
|
|
cbuf_reset(curl);
|
|
cbuf_reset(cdata);
|
|
cprintf(curl, "http://%s:8086/db/%s/users/%s", server_addr, database, user);
|
|
cprintf(cdata, "{\"admin\": true}", password);
|
|
if (url_post(cbuf_get(curl), www_user, www_passwd, cbuf_get(cdata), NULL, NULL) < 0)
|
|
goto done;
|
|
clicon_debug(1, "%s: %s %s", __FUNCTION__, cbuf_get(curl), cbuf_get(cdata));
|
|
retval = 0;
|
|
done:
|
|
if (curl)
|
|
cbuf_free(curl);
|
|
if (cdata)
|
|
cbuf_free(cdata);
|
|
return retval;
|
|
}
|
|
|
|
/*! Send a curl POST request
|
|
* @retval -1 fatal error
|
|
* @retval 0 expect set but did not expected return or other non-fatal error
|
|
* @retval 1 ok
|
|
* Note: curl_easy_perform blocks
|
|
* Note: New handle is created every time, the handle can be re-used for better TCP performance
|
|
* @see same function (url_post) in grideye_curl.c
|
|
*/
|
|
int
|
|
url_post(char *url,
|
|
char *username,
|
|
char *passwd,
|
|
char *putdata,
|
|
char *expect,
|
|
char **getdata)
|
|
{
|
|
CURL *curl = NULL;
|
|
char *err;
|
|
int retval = -1;
|
|
cxobj *xr = NULL; /* reply xml */
|
|
struct curlbuf b = {0, };
|
|
CURLcode errcode;
|
|
char *output = NULL;
|
|
|
|
/* Try it with curl -X PUT -d '*/
|
|
clicon_debug(1, "%s: curl -X POST -d '%s' %s (%s:%s)",
|
|
__FUNCTION__, putdata, url, username, passwd);
|
|
/* Set up curl for doing the communication with the controller */
|
|
if ((curl = curl_easy_init()) == NULL) {
|
|
clicon_debug(1, "curl_easy_init");
|
|
goto done;
|
|
}
|
|
if ((output = curl_easy_escape(curl, putdata, 0)) == NULL){
|
|
clicon_debug(1, "curl_easy_escape");
|
|
goto done;
|
|
}
|
|
if ((err = chunk(CURL_ERROR_SIZE, __FUNCTION__)) == NULL) {
|
|
clicon_debug(1, "%s: chunk", __FUNCTION__);
|
|
goto done;
|
|
}
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
if (username)
|
|
curl_easy_setopt(curl, CURLOPT_USERNAME, username);
|
|
if (passwd)
|
|
curl_easy_setopt(curl, CURLOPT_PASSWORD, passwd);
|
|
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err);
|
|
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, putdata);
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(putdata));
|
|
if (debug)
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
|
if ((errcode = curl_easy_perform(curl)) != CURLE_OK){
|
|
clicon_debug(1, "%s: curl: %s(%d)", __FUNCTION__, err, errcode);
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
if (expect){
|
|
if (b.b_buf == NULL){
|
|
clicon_debug(1, "%s: no match", __FUNCTION__);
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
else{
|
|
clicon_debug(1, "%s: reply:%s", __FUNCTION__, b.b_buf);
|
|
if (clicon_xml_parse_string(&b.b_buf, &xr) < 0)
|
|
goto done;
|
|
if (xpath_first(xr, expect) == NULL){
|
|
clicon_debug(1, "%s: no match", __FUNCTION__);
|
|
retval = 0;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
if (getdata && b.b_buf){
|
|
*getdata = b.b_buf;
|
|
b.b_buf = NULL;
|
|
}
|
|
retval = 1;
|
|
done:
|
|
unchunk_group(__FUNCTION__);
|
|
if (output)
|
|
curl_free(output);
|
|
if (xr != NULL)
|
|
xml_free(xr);
|
|
if (b.b_buf)
|
|
free(b.b_buf);
|
|
if (curl)
|
|
curl_easy_cleanup(curl); /* cleanup */
|
|
return retval;
|
|
}
|
|
|
|
static const char Base64[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
static const char Pad64 = '=';
|
|
|
|
#if notdef
|
|
/*! return length of dst */
|
|
int
|
|
b64_encode(const char *src,
|
|
char *dst,
|
|
int dlen)
|
|
{
|
|
int i;
|
|
int s = 0;
|
|
int j = 0;
|
|
uint32_t data = 0;
|
|
int slen;
|
|
|
|
slen = strlen(src);
|
|
/*
|
|
* The encoded length will "grow" 33% in respect to the original length,
|
|
* i.e. 3 character will be 4 characters
|
|
*/
|
|
if (((slen * 7) / 5) > dlen) {
|
|
clicon_debug(1, "Destination buffer to small.\n");
|
|
return -1;
|
|
}
|
|
for (i = 0; i < slen; i++) {
|
|
data <<= 8;
|
|
data |= ((uint32_t)src[i] & 0x000000ff);
|
|
s++;
|
|
if (s == 3) {
|
|
dst[j++] = Base64[((data >> 18) & 0x3f)];
|
|
dst[j++] = Base64[((data >> 12) & 0x3f)];
|
|
dst[j++] = Base64[((data >> 6) & 0x3f)];
|
|
dst[j++] = Base64[(data & 0x3f)];
|
|
dst[j] = '\0';
|
|
data = 0;
|
|
s = 0;
|
|
}
|
|
}
|
|
switch (s) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
data <<= 4;
|
|
dst[j++] = Base64[((data >> 6) & 0x3f)];
|
|
dst[j++] = Base64[(data & 0x3f)];
|
|
dst[j++] = Pad64;
|
|
dst[j++] = Pad64;
|
|
break;
|
|
case 2:
|
|
data <<= 2;
|
|
dst[j++] = Base64[((data >> 12) & 0x3f)];
|
|
dst[j++] = Base64[((data >> 6) & 0x3f)];
|
|
dst[j++] = Base64[(data & 0x3f)];
|
|
dst[j++] = Pad64;
|
|
break;
|
|
}
|
|
dst[j] = '\0';
|
|
return j;
|
|
}
|
|
#endif
|
|
|
|
/* skips all whitespace anywhere.
|
|
converts characters, four at a time, starting at (or after)
|
|
src from base - 64 numbers into three 8 bit bytes in the target area.
|
|
it returns the number of data bytes stored at the target, or -1 on error.
|
|
*/
|
|
int
|
|
b64_decode(const char *src,
|
|
char *target,
|
|
size_t targsize)
|
|
{
|
|
int tarindex, state, ch;
|
|
char *pos;
|
|
|
|
clicon_debug(1, "%s", __FUNCTION__);
|
|
state = 0;
|
|
tarindex = 0;
|
|
|
|
while ((ch = *src++) != '\0') {
|
|
if (isspace(ch)) /* Skip whitespace anywhere. */
|
|
continue;
|
|
|
|
if (ch == Pad64)
|
|
break;
|
|
|
|
pos = strchr(Base64, ch);
|
|
if (pos == 0) /* A non-base64 character. */
|
|
return (-1);
|
|
|
|
switch (state) {
|
|
case 0:
|
|
if (target) {
|
|
if ((size_t)tarindex >= targsize)
|
|
return (-1);
|
|
target[tarindex] = (pos - Base64) << 2;
|
|
}
|
|
state = 1;
|
|
break;
|
|
case 1:
|
|
if (target) {
|
|
if ((size_t)tarindex + 1 >= targsize)
|
|
return (-1);
|
|
target[tarindex] |= (pos - Base64) >> 4;
|
|
target[tarindex+1] = ((pos - Base64) & 0x0f)
|
|
<< 4 ;
|
|
}
|
|
tarindex++;
|
|
state = 2;
|
|
break;
|
|
case 2:
|
|
if (target) {
|
|
if ((size_t)tarindex + 1 >= targsize)
|
|
return (-1);
|
|
target[tarindex] |= (pos - Base64) >> 2;
|
|
target[tarindex+1] = ((pos - Base64) & 0x03)
|
|
<< 6;
|
|
}
|
|
tarindex++;
|
|
state = 3;
|
|
break;
|
|
case 3:
|
|
if (target) {
|
|
if ((size_t)tarindex >= targsize)
|
|
return (-1);
|
|
target[tarindex] |= (pos - Base64);
|
|
}
|
|
tarindex++;
|
|
state = 0;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We are done decoding Base-64 chars. Let's see if we ended
|
|
* on a byte boundary, and/or with erroneous trailing characters.
|
|
*/
|
|
|
|
if (ch == Pad64) { /* We got a pad char. */
|
|
ch = *src++; /* Skip it, get next. */
|
|
switch (state) {
|
|
case 0: /* Invalid = in first position */
|
|
case 1: /* Invalid = in second position */
|
|
return (-1);
|
|
|
|
case 2: /* Valid, means one byte of info */
|
|
/* Skip any number of spaces. */
|
|
for ((void)NULL; ch != '\0'; ch = *src++)
|
|
if (!isspace(ch))
|
|
break;
|
|
/* Make sure there is another trailing = sign. */
|
|
if (ch != Pad64)
|
|
return (-1);
|
|
ch = *src++; /* Skip the = */
|
|
/* Fall through to "single trailing =" case. */
|
|
/* FALLTHROUGH */
|
|
|
|
case 3: /* Valid, means two bytes of info */
|
|
/*
|
|
* We know this char is an =. Is there anything but
|
|
* whitespace after it?
|
|
*/
|
|
for ((void)NULL; ch != '\0'; ch = *src++)
|
|
if (!isspace(ch))
|
|
return (-1);
|
|
|
|
/*
|
|
* Now make sure for cases 2 and 3 that the "extra"
|
|
* bits that slopped past the last full byte were
|
|
* zeros. If we don't check them, they become a
|
|
* subliminal channel.
|
|
*/
|
|
if (target && target[tarindex] != 0)
|
|
return (-1);
|
|
}
|
|
} else {
|
|
/*
|
|
* We ended by seeing the end of the string. Make sure we
|
|
* have no partial bytes lying around.
|
|
*/
|
|
if (state != 0)
|
|
return (-1);
|
|
}
|
|
|
|
return (tarindex);
|
|
}
|
|
|
|
/*! Get field from metric yang spec
|
|
* @param[in] metric Metric
|
|
* @param[in] fiels Which field in metric (eg type, units)
|
|
* @param[out] result Allocated string. Free after use
|
|
*/
|
|
static int
|
|
get_metric_spec(char *metric,
|
|
char *field,
|
|
char **result)
|
|
{
|
|
int retval = -1;
|
|
cbuf *resbuf = NULL;
|
|
char *xmlstr;
|
|
cxobj *mx;
|
|
cxobj *dx;
|
|
cxobj *sx;
|
|
char *val;
|
|
|
|
clicon_debug(1, "%s metric:%s", __FUNCTION__, metric);
|
|
*result = NULL;
|
|
if ((resbuf = cbuf_new()) == NULL)
|
|
goto done;
|
|
if (netconf_rpc(resbuf,
|
|
"<rpc><grideye><metric_spec>%s</metric_spec></grideye></rpc>]]>]]>",
|
|
metric) < 0){
|
|
clicon_debug(1, "%s error", __FUNCTION__);
|
|
goto done;
|
|
}
|
|
xmlstr = cbuf_get(resbuf);
|
|
clicon_debug(1, "xmlstr: %s", xmlstr);
|
|
if (clicon_xml_parse_string(&xmlstr, &mx) < 0){
|
|
clicon_debug(1, "err:%d reason:%s", clicon_errno, clicon_err_reason);
|
|
goto done;
|
|
}
|
|
if ((dx = xpath_first(mx, "//data")) != NULL){
|
|
if ((sx = xpath_first(dx, metric)) != NULL){
|
|
if ((val = xml_find_body(sx, field)) != NULL)
|
|
*result = strdup(val);
|
|
}
|
|
}
|
|
retval = 0;
|
|
done:
|
|
if (resbuf)
|
|
cbuf_free(resbuf);
|
|
if (mx)
|
|
xml_free(mx);
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
metric_spec_description(char *metric,
|
|
char **result)
|
|
{
|
|
return get_metric_spec(metric, "description", result);
|
|
}
|
|
|
|
int
|
|
metric_spec_units(char *metric,
|
|
char **result)
|
|
{
|
|
return get_metric_spec(metric, "units", result);
|
|
}
|