320 lines
12 KiB
C
320 lines
12 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (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.openairinterface.org/?page_id=698
|
|
*
|
|
* 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.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
/*! \file common/utils/websrv/websrv_utils.c
|
|
* \brief: utility functions for all websrv back-end sources
|
|
* \author Francois TABURET
|
|
* \date 2022
|
|
* \version 0.1
|
|
* \company NOKIA BellLabs France
|
|
* \email: francois.taburet@nokia-bell-labs.com
|
|
* \note
|
|
* \warning
|
|
*/
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include "common/utils/LOG/log.h"
|
|
#include <libgen.h>
|
|
#include <jansson.h>
|
|
#include <ulfius.h>
|
|
|
|
/* websrv_printf_t is an internal structure storing messages while processing a request */
|
|
/* The meaage is used to fill a response body */
|
|
typedef struct {
|
|
pthread_mutex_t mutex; // protect the message betwween the websrv_printf_start and websrv_print_end calls
|
|
struct _u_response *response; // the ulfius response structure, used to send the message
|
|
char *buff; // a buffer to store the message, allocated in websrv_printf_start, free in websrv_print_end
|
|
int buffsize; //
|
|
char *buffptr; // pointer to free portion of buff
|
|
bool async; // the buffer will be used to print from another thread than the callback (command pushed in a tpool queue)
|
|
} websrv_printf_t;
|
|
static websrv_printf_t websrv_printf_buff;
|
|
/*--------------------- functions for help ------------------------*/
|
|
/* possibly build a help file to be downloaded by the frontend */
|
|
void websrv_utils_build_hlpfile(char *fname)
|
|
{
|
|
char *info = NULL;
|
|
|
|
if (strstr(fname, "core.html") != NULL) {
|
|
long number_of_processors = sysconf(_SC_NPROCESSORS_ONLN);
|
|
if (number_of_processors > 0) {
|
|
char *pinfo = malloc(255);
|
|
snprintf(pinfo, 254, "Available cores on this system: 0 to %d", (int)number_of_processors - 1);
|
|
info = pinfo;
|
|
}
|
|
}
|
|
if (info != NULL) {
|
|
FILE *f = fopen(fname, "w");
|
|
if (f == NULL) {
|
|
LOG_I(UTIL, "[websrv]: Help file %s couldn't be created\n", fname);
|
|
} else {
|
|
fprintf(f, "%s", info);
|
|
fclose(f);
|
|
}
|
|
free(info);
|
|
}
|
|
}
|
|
|
|
void websrv_utils_build_hlpfiles(char *path)
|
|
{
|
|
char fname[255];
|
|
snprintf(fname, sizeof(fname), "%s/helpfiles/softmodem_show_threadsched_core.html", path);
|
|
websrv_utils_build_hlpfile(fname);
|
|
}
|
|
int websrv_utils_testhlp(char *dir, char *module, char *cmdtitle, char *coltitle)
|
|
{
|
|
char fname[255];
|
|
|
|
snprintf(fname, sizeof(fname) - 1, "%s/helpfiles/%s_%s_%s.html", dir, module, cmdtitle, coltitle);
|
|
for (int i = 0; i < strlen(fname); i++) {
|
|
if (fname[i] == ' ')
|
|
fname[i] = '_';
|
|
}
|
|
int st = access(fname, R_OK);
|
|
return ((st == 0) ? 1 : 0);
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
/* functions http request/response helpers */
|
|
/* dump a json objects */
|
|
void websrv_printjson(char *label, json_t *jsonobj, int dbglvl)
|
|
{
|
|
if (dbglvl > 0) {
|
|
char *jstr = json_dumps(jsonobj, 0);
|
|
LOG_I(UTIL, "[websrv] %s:%s\n", label, (jstr == NULL) ? "??\n" : jstr);
|
|
free(jstr);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
/* dump a request */
|
|
void websrv_dump_request(char *label, const struct _u_request *request, int dbglvl)
|
|
{
|
|
if (dbglvl > 0) {
|
|
LOG_I(UTIL, "[websrv] %s, request %s, proto %s, verb %s, path %s\n", label, request->http_url, request->http_protocol, request->http_verb, request->http_verb);
|
|
if (request->map_post_body != NULL)
|
|
for (int i = 0; i < u_map_count(request->map_post_body); i++)
|
|
LOG_I(UTIL, "[websrv] POST parameter %i %s : %s\n", i, u_map_enum_keys(request->map_post_body)[i], u_map_enum_values(request->map_post_body)[i]);
|
|
if (request->map_cookie != NULL)
|
|
for (int i = 0; i < u_map_count(request->map_cookie); i++)
|
|
LOG_I(UTIL, "[websrv] cookie variable %i %s : %s\n", i, u_map_enum_keys(request->map_cookie)[i], u_map_enum_values(request->map_cookie)[i]);
|
|
if (request->map_header != NULL)
|
|
for (int i = 0; i < u_map_count(request->map_header); i++)
|
|
LOG_I(UTIL, "[websrv] header variable %i %s : %s\n", i, u_map_enum_keys(request->map_header)[i], u_map_enum_values(request->map_header)[i]);
|
|
if (request->map_url != NULL)
|
|
for (int i = 0; i < u_map_count(request->map_url); i++)
|
|
LOG_I(UTIL, "[websrv] url variable %i %s : %s\n", i, u_map_enum_keys(request->map_url)[i], u_map_enum_values(request->map_url)[i]);
|
|
}
|
|
}
|
|
/*-----------------------------------*/
|
|
/* build a json body in a response */
|
|
void websrv_jbody(struct _u_response *response, json_t *jbody, int httpstatus, int dbglvl)
|
|
{
|
|
websrv_printjson((char *)__FUNCTION__, jbody, dbglvl);
|
|
int us = ulfius_add_header_to_response(response, "content-type", "application/json");
|
|
if (us != U_OK) {
|
|
ulfius_set_string_body_response(response, 500, "Internal server error (ulfius_add_header_to_response)");
|
|
LOG_E(UTIL, "[websrv] cannot set response header type ulfius error %d \n", us);
|
|
}
|
|
us = ulfius_set_json_body_response(response, httpstatus, jbody);
|
|
if (us != U_OK) {
|
|
ulfius_set_string_body_response(response, 500, "Internal server error (ulfius_set_json_body_response)");
|
|
LOG_E(UTIL, "[websrv] cannot set body response ulfius error %d \n", us);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*----------------------------------------------------------------*/
|
|
/* format a json string array in a response body */
|
|
int websrv_string_response(char *astring, struct _u_response *response, int httpstatus, int dbglvl)
|
|
{
|
|
json_t *jstr = json_array();
|
|
char *tokctx;
|
|
char *aline = strtok_r(astring, "\n", &tokctx);
|
|
while (aline != NULL) {
|
|
json_t *jline = json_string(aline);
|
|
json_array_append_new(jstr, jline);
|
|
aline = strtok_r(NULL, "\n", &tokctx);
|
|
}
|
|
json_t *jbody = json_pack("{s:o}", "display", jstr);
|
|
websrv_jbody(response, jbody, httpstatus, dbglvl);
|
|
return 0;
|
|
}
|
|
/*----------------------------------------------------------------------------------*/
|
|
/* set of calls to fill a buffer with a string and use this buffer in a response */
|
|
void websrv_printf_start(struct _u_response *response, int buffsize, bool async)
|
|
{
|
|
pthread_mutex_lock(&(websrv_printf_buff.mutex));
|
|
websrv_printf_buff.buff = malloc(buffsize);
|
|
websrv_printf_buff.buffptr = websrv_printf_buff.buff;
|
|
websrv_printf_buff.buffsize = buffsize;
|
|
websrv_printf_buff.response = response;
|
|
websrv_printf_buff.async = async;
|
|
}
|
|
|
|
void websrv_printf_atpos(int pos, const char *message, ...)
|
|
{
|
|
va_list va_args;
|
|
va_start(va_args, message);
|
|
|
|
websrv_printf_buff.buffptr = websrv_printf_buff.buff + pos + vsnprintf(websrv_printf_buff.buff + pos, websrv_printf_buff.buffsize - pos - 1, message, va_args);
|
|
|
|
va_end(va_args);
|
|
return;
|
|
}
|
|
|
|
void websrv_printf(const char *message, ...)
|
|
{
|
|
va_list va_args;
|
|
va_start(va_args, message);
|
|
websrv_printf_buff.buffptr += vsnprintf(websrv_printf_buff.buffptr, websrv_printf_buff.buffsize - (websrv_printf_buff.buffptr - websrv_printf_buff.buff) - 1, message, va_args);
|
|
|
|
va_end(va_args);
|
|
return;
|
|
}
|
|
|
|
void websrv_printf_end(int httpstatus, int dbglvl)
|
|
{
|
|
int count = 0;
|
|
while (__atomic_load_n(&(websrv_printf_buff.async), __ATOMIC_SEQ_CST)) {
|
|
usleep(10);
|
|
if (count == 99) {
|
|
websrv_printf("No response from server...");
|
|
break;
|
|
}
|
|
}
|
|
if (httpstatus >= 200 && httpstatus < 300) {
|
|
LOG_I(UTIL, "[websrv] %s\n", websrv_printf_buff.buff);
|
|
websrv_string_response(websrv_printf_buff.buff, websrv_printf_buff.response, httpstatus, dbglvl);
|
|
} else if (httpstatus < 1000) {
|
|
LOG_W(UTIL, "[websrv] %s\n", websrv_printf_buff.buff);
|
|
ulfius_set_binary_body_response(websrv_printf_buff.response, httpstatus, websrv_printf_buff.buff, websrv_printf_buff.buffptr - websrv_printf_buff.buff);
|
|
}
|
|
|
|
free(websrv_printf_buff.buff);
|
|
websrv_printf_buff.buff = NULL;
|
|
websrv_printf_buff.async = false;
|
|
pthread_mutex_unlock(&(websrv_printf_buff.mutex));
|
|
}
|
|
|
|
void websrv_async_printf(const char *message, ...)
|
|
{
|
|
if (__atomic_load_n(&(websrv_printf_buff.async), __ATOMIC_SEQ_CST)) {
|
|
va_list va_args;
|
|
va_start(va_args, message);
|
|
websrv_printf_buff.buffptr += vsnprintf(websrv_printf_buff.buff, websrv_printf_buff.buffsize - 1, message, va_args);
|
|
|
|
va_end(va_args);
|
|
__atomic_store_n(&(websrv_printf_buff.async), 0, __ATOMIC_SEQ_CST);
|
|
} else {
|
|
LOG_W(UTIL, "[websrv], delayed websrv_async_printf skipped\n");
|
|
}
|
|
return;
|
|
}
|
|
/* */
|
|
/*----------------------------------------------------------------------------------------------------------*/
|
|
/* callbacks and utility functions to stream a file */
|
|
char *websrv_read_file(const char *filename)
|
|
{
|
|
char *buffer = NULL;
|
|
long length;
|
|
struct stat statbuf;
|
|
|
|
FILE *f = fopen(filename, "rb");
|
|
int st = stat(filename, &statbuf);
|
|
if (f != NULL && st == 0) {
|
|
length = statbuf.st_size;
|
|
buffer = malloc(length + 1);
|
|
if (buffer != NULL) {
|
|
int rlen = fread(buffer, 1, length, f);
|
|
if (rlen != length) {
|
|
free(buffer);
|
|
LOG_E(UTIL, "[websrv] couldn't read %s_\n", filename);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
buffer[length] = '\0';
|
|
}
|
|
} else {
|
|
LOG_E(UTIL, "[websrv] error accessing %s: %s\n", filename, strerror(errno));
|
|
}
|
|
if (f != NULL)
|
|
fclose(f);
|
|
return buffer;
|
|
}
|
|
/* callbacks to send static streams */
|
|
static ssize_t callback_stream(void *cls, uint64_t pos, char *buf, size_t max)
|
|
{
|
|
if (cls != NULL) {
|
|
return fread(buf, sizeof(char), max, (FILE *)cls);
|
|
} else {
|
|
return U_STREAM_END;
|
|
}
|
|
}
|
|
|
|
static void callback_stream_free(void *cls)
|
|
{
|
|
if (cls != NULL) {
|
|
fclose((FILE *)cls);
|
|
}
|
|
}
|
|
|
|
FILE *websrv_getfile(char *filename, struct _u_response *response)
|
|
{
|
|
FILE *f = fopen(filename, "rb");
|
|
int length;
|
|
|
|
if (f) {
|
|
fseek(f, 0, SEEK_END);
|
|
length = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
LOG_I(UTIL, "[websrv] sending %d bytes from %s\n", length, filename);
|
|
} else {
|
|
LOG_E(UTIL, "[websrv] couldn't open %s\n", filename);
|
|
return NULL;
|
|
}
|
|
|
|
char *content_type = "text/html";
|
|
size_t nl = strlen(filename);
|
|
if (nl >= 3 && !strcmp(filename + nl - 3, "css"))
|
|
content_type = "text/css";
|
|
if (nl >= 2 && !strcmp(filename + nl - 2, "js"))
|
|
content_type = "text/javascript";
|
|
|
|
int ust = ulfius_add_header_to_response(response, "content-type", content_type);
|
|
if (ust != U_OK) {
|
|
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_add_header_to_response)");
|
|
LOG_E(UTIL, "[websrv] cannot set response header type ulfius error %d \n", ust);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
ust = ulfius_set_stream_response(response, 200, callback_stream, callback_stream_free, length, 1024, f);
|
|
if (ust != U_OK) {
|
|
LOG_E(UTIL, "[websrv] ulfius_set_stream_response error %d\n", ust);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
return f;
|
|
}
|
|
/* */
|