286 lines
8.4 KiB
C
286 lines
8.4 KiB
C
/*
|
|
*
|
|
***** BEGIN LICENSE BLOCK *****
|
|
|
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
|
Copyright (C) 2017-2019 Olof Hagsand
|
|
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC (Netgate)
|
|
|
|
This file is part of CLIXON.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
the GNU General Public License Version 3 or later (the "GPL"),
|
|
in which case the provisions of the GPL are applicable instead
|
|
of those above. If you wish to allow use of your version of this file only
|
|
under the terms of the GPL, and not to allow others to
|
|
use your version of this file under the terms of Apache License version 2,
|
|
indicate your decision by deleting the provisions above and replace them with
|
|
the notice and other provisions required by the GPL. If you do not delete
|
|
the provisions above, a recipient may use your version of this file under
|
|
the terms of any one of the Apache License version 2 or the GPL.
|
|
|
|
***** END LICENSE BLOCK *****
|
|
|
|
* Stream restconf support functions.
|
|
* (Original in grideye)
|
|
* Example: clixon_util_stream -u http://localhost/streams/EXAMPLE -s 2018-10-21T19:22:16
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "clixon_config.h" /* generated by config & autoconf */
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <syslog.h>
|
|
#include <signal.h>
|
|
#include <curl/curl.h>
|
|
|
|
/* cligen */
|
|
#include <cligen/cligen.h>
|
|
|
|
/* clixon */
|
|
#include "clixon/clixon.h"
|
|
|
|
/*
|
|
* Types (curl)
|
|
*/
|
|
struct curlbuf{
|
|
size_t b_len;
|
|
char *b_buf;
|
|
};
|
|
|
|
/*
|
|
* For the asynchronous case. I think we must handle the case where of many of these
|
|
* come in before we can handle them in the upper-level polling routine.
|
|
* realloc. Therefore, we append new data to the userdata buffer.
|
|
*/
|
|
static size_t
|
|
curl_get_cb(void *ptr,
|
|
size_t size,
|
|
size_t nmemb,
|
|
void *userdata)
|
|
{
|
|
struct curlbuf *buf = (struct curlbuf *)userdata;
|
|
int len;
|
|
|
|
len = size*nmemb;
|
|
if ((buf->b_buf = realloc(buf->b_buf, buf->b_len+len+1)) == NULL)
|
|
return 0;
|
|
memcpy(buf->b_buf+buf->b_len, ptr, len);
|
|
buf->b_len += len;
|
|
buf->b_buf[buf->b_len] = '\0';
|
|
// fprintf(stderr, "%s\n", buf->b_buf);
|
|
return len;
|
|
}
|
|
|
|
/*! Given an URL and data to post, do a (curl) get request with data.
|
|
*
|
|
* If getdata is set, return the (malloced) data (which should be freed).
|
|
*
|
|
* @param[in] start 'start-time' parameter that will be URL-encoded
|
|
* @retval 1 ok
|
|
* @retval -1 fatal error
|
|
*
|
|
* @note curl_easy_perform blocks
|
|
* @note New handle is created every time, the handle can be re-used for
|
|
* better TCP performance
|
|
*/
|
|
int
|
|
stream_url_get(char *url,
|
|
char *start,
|
|
char *stop,
|
|
int timeout,
|
|
char **getdata)
|
|
{
|
|
int retval = -1;
|
|
CURL *curl;
|
|
char *err = NULL;
|
|
char *encoded = NULL;
|
|
struct curlbuf cb = {0, };
|
|
cbuf *cbf = NULL;
|
|
struct curl_slist *list = NULL;
|
|
int ret;
|
|
|
|
clixon_debug(CLIXON_DBG_DEFAULT, "%s: curl -G %s start-time=%s stop-time=%s",
|
|
__FUNCTION__, url, start?start:"", stop?stop:"");
|
|
/* Set up curl for doing the communication with the controller */
|
|
if ((curl = curl_easy_init()) == NULL) {
|
|
clicon_err(OE_PLUGIN, errno, "curl_easy_init");
|
|
goto done;
|
|
}
|
|
if ((cbf = cbuf_new()) == NULL)
|
|
goto done;
|
|
|
|
if ((err = malloc(CURL_ERROR_SIZE)) == NULL) {
|
|
clixon_debug(CLIXON_DBG_DEFAULT, "%s: malloc", __FUNCTION__);
|
|
goto done;
|
|
}
|
|
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
|
/* HEADERS */
|
|
list = curl_slist_append(list, "Accept: text/event-stream");
|
|
// list = curl_slist_append(list, "Cache-Control: no-cache");
|
|
// list = curl_slist_append(list, "Connection: keep-alive");
|
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
|
/* specify URL to get */
|
|
cprintf(cbf, "%s", url);
|
|
if (strlen(start)||strlen(stop))
|
|
cprintf(cbf, "?");
|
|
if (strlen(start)){
|
|
if ((encoded = curl_easy_escape(curl, start, 0)) == NULL)
|
|
goto done;
|
|
cprintf(cbf, "start-time=%s", encoded);
|
|
curl_free(encoded);
|
|
encoded = NULL;
|
|
}
|
|
if (strlen(stop)){
|
|
if (strlen(start))
|
|
cprintf(cbf, "&");
|
|
if ((encoded = curl_easy_escape(curl, stop, 0)) == NULL)
|
|
goto done;
|
|
cprintf(cbf, "stop-time=%s", encoded);
|
|
curl_free(encoded);
|
|
encoded = NULL;
|
|
}
|
|
clixon_debug(CLIXON_DBG_DEFAULT, "url: %s\n", cbuf_get(cbf));
|
|
curl_easy_setopt(curl, CURLOPT_URL, cbuf_get(cbf));
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_get_cb);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &cb);
|
|
|
|
/* some servers don't like requests that are made without a user-agent
|
|
field, so we provide one */
|
|
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err);
|
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
|
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
|
ret = curl_easy_perform(curl);
|
|
if (ret != CURLE_OPERATION_TIMEDOUT && ret != CURLE_OK){
|
|
fprintf(stderr, "curl: %s %d", err, ret);
|
|
goto done;
|
|
}
|
|
if (getdata && cb.b_buf){
|
|
*getdata = cb.b_buf;
|
|
cb.b_buf = NULL;
|
|
}
|
|
retval = 1;
|
|
done:
|
|
if (cbf)
|
|
cbuf_free(cbf);
|
|
if (err)
|
|
free(err);
|
|
if (encoded)
|
|
curl_free(encoded);
|
|
if (cb.b_buf)
|
|
free(cb.b_buf);
|
|
if (curl)
|
|
curl_easy_cleanup(curl); /* cleanup */
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
usage(char *argv0)
|
|
{
|
|
fprintf(stderr, "usage:%s <options>*\n"
|
|
"where options are:\n"
|
|
"\t-h\t\tHelp\n"
|
|
"\t-D <level>\tDebug level\n"
|
|
"\t-u <url>\tURL (mandatory)\n"
|
|
"\t-s <start>\tStart-time (format: 2018-10-21T19:22:16 OR +/-<x>s\n"
|
|
"\t-e <end>\tStop-time (same format as start)\n"
|
|
"\t-t <timeout>\tTimeout (default: 10)\n"
|
|
, argv0);
|
|
|
|
exit(0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
cbuf *cb = cbuf_new();
|
|
char *url = NULL;
|
|
char *getdata = NULL;
|
|
int timeout = 10;
|
|
char start[28] = {0,}; /* strlen = 0 */
|
|
char stop[28] = {0,};
|
|
int c;
|
|
char *argv0 = argv[0];
|
|
struct timeval now;
|
|
int dbg = 0;
|
|
|
|
clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR);
|
|
gettimeofday(&now, NULL);
|
|
optind = 1;
|
|
opterr = 0;
|
|
while ((c = getopt(argc, argv, "hDu:s:e:t:")) != -1)
|
|
switch (c) {
|
|
case 'h':
|
|
usage(argv0);
|
|
break;
|
|
case 'D':
|
|
dbg = 1;
|
|
break;
|
|
case 'u': /* URL */
|
|
url = optarg;
|
|
break;
|
|
case 's': /* start-time */
|
|
if (*optarg == '+' || *optarg == '-'){
|
|
struct timeval t = now;
|
|
t.tv_sec += atoi(optarg);
|
|
if (time2str(&t, start, sizeof(start)) < 0)
|
|
goto done;
|
|
}
|
|
else
|
|
strcpy(start, optarg);
|
|
break;
|
|
case 'e': /* stop-time */
|
|
if (*optarg == '+' || *optarg == '-'){
|
|
struct timeval t = now;
|
|
t.tv_sec += atoi(optarg);
|
|
if (time2str(&t, stop, sizeof(stop)) < 0)
|
|
goto done;
|
|
}
|
|
else
|
|
strcpy(stop, optarg);
|
|
break;
|
|
case 't': /* timeout */
|
|
timeout = atoi(optarg);
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
break;
|
|
}
|
|
clixon_debug_init(dbg, NULL);
|
|
if (url == NULL)
|
|
usage(argv[0]);
|
|
curl_global_init(0);
|
|
if (stream_url_get(url, start, stop, timeout, &getdata) < 0)
|
|
goto done;
|
|
if (getdata)
|
|
fprintf(stdout, "%s", getdata);
|
|
fflush(stdout);
|
|
done:
|
|
curl_global_cleanup();
|
|
if (getdata)
|
|
free(getdata);
|
|
if (cb)
|
|
cbuf_free(cb);
|
|
return 0;
|
|
}
|
|
|
|
|