Compare commits

...

84 Commits

Author SHA1 Message Date
El Mghazli Yacine
51ce2fb17f setParam$ 2022-04-27 19:13:21 +02:00
El Mghazli Yacine
4ca7dafb64 setParam$ 2022-04-26 19:28:05 +02:00
El Mghazli Yacine
a3b05f0bd6 confirm button ok 2022-04-26 19:03:21 +02:00
El Mghazli Yacine
61dab4c7fd height 80% display 2022-04-26 18:56:07 +02:00
El Mghazli Yacine
afb85855a9 table OK 2022-04-26 18:55:43 +02:00
El Mghazli Yacine
eb68baaaa6 Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-04-26 16:28:34 +02:00
El Mghazli Yacine
b4e0280228 display fix 2022-04-26 16:27:35 +02:00
frtabu
16fc6028f9 fix empty array format 2022-04-26 15:27:10 +02:00
frtabu
2051e652fb fix new IRespCmd json format in backend 2022-04-26 13:06:03 +02:00
El Mghazli Yacine
6dc9440bd1 confirm dialog 2022-04-26 11:04:23 +02:00
frtabu
51f2a75bac table response implementation fix (backend) 2022-04-25 20:05:46 +02:00
El Mghazli Yacine
1fadb0c11e dynamic table WIP 2022-04-25 18:28:17 +02:00
El Mghazli Yacine
9d2b747586 Merge branch 'websrv-table' into websrv 2022-04-25 17:32:59 +02:00
frtabu
12099a910e Table Response implementation in backend 2022-04-23 01:13:36 +02:00
frtabu
9cca19e2b4 Merge branch 'websrv' of https://gitlab.eurecom.fr/oai/openairinterface5g into websrv 2022-04-22 20:01:04 +02:00
frtabu
698e7fee1f display array fix 2022-04-22 20:00:13 +02:00
El Mghazli Yacine
857094e95c Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-04-22 19:59:33 +02:00
El Mghazli Yacine
39a8aa3455 confirm dialog before sending cmd 2022-04-22 19:59:30 +02:00
frtabu
291533dd71 more commands implementation in backend 2022-04-22 19:11:17 +02:00
El Mghazli Yacine
e18753db6e "prod": "ng serve --configuration=production" 2022-04-22 17:52:29 +02:00
El Mghazli Yacine
4cfa385ae0 table display 2022-04-22 17:36:10 +02:00
El Mghazli Yacine
2761d013d7 cleanup rowCtrl 2022-04-22 17:32:27 +02:00
El Mghazli Yacine
b112ed2c6b dynamic tables 2022-04-22 17:30:41 +02:00
El Mghazli Yacine
abb59447b3 Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-04-22 15:57:37 +02:00
El Mghazli Yacine
e3d38b5cbe tables and new API with IColumn and IRow 2022-04-22 15:57:28 +02:00
frtabu
3b282d7fb9 fix json format in log response 2022-04-22 09:40:01 +02:00
frtabu
80bf03581d test table reslt with log command 2022-04-21 00:02:50 +02:00
frtabu
86f36e8098 implementation of log command 2022-04-19 19:26:16 +02:00
El Mghazli Yacine
273ce8cb49 Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-04-15 18:52:57 +02:00
El Mghazli Yacine
93126750ea details 2022-04-15 18:47:37 +02:00
frtabu
7a7b2ff48a Adapt command response structure 2022-04-15 18:04:09 +02:00
El Mghazli Yacine
dac3bc1af5 ilogs 2022-04-15 17:13:47 +02:00
frtabu
32e49924b0 implement mechanism for simple string response 2022-04-15 00:47:51 +02:00
El Mghazli Yacine
b203d4aebc openDialog 2022-04-14 19:48:16 +02:00
frtabu
dfa7579857 command processing implementation 2022-04-14 18:18:34 +02:00
frtabu
f2af6c26fa better variable type processing 2022-04-11 19:02:04 +02:00
frtabu
c4cf67f04b modify telnet server structure to support command execution from the webserver 2022-04-08 19:27:31 +02:00
frtabu
9dbcab30c8 json boolean fix 2022-04-07 14:51:35 +02:00
frtabu
24e878148e fix backend commands response body 2022-04-07 11:49:57 +02:00
frtabu
8d74ea3809 backend implementation of variable modifications 2022-04-06 19:24:12 +02:00
frtabu
a3ac83a1bf Add OPTIONS http method support, used before POST by frontend. 2022-04-06 01:04:36 +02:00
frtabu
5ef05129e1 change endpoint registration mechanism 2022-04-05 19:47:30 +02:00
El Mghazli Yacine
8de026f86e Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-04-05 19:26:15 +02:00
El Mghazli Yacine
3127898899 fixed cmds display 2022-04-05 19:25:55 +02:00
frtabu
3c6b21e9c0 More debug messages in backend 2022-04-05 19:22:38 +02:00
El Mghazli Yacine
2afae81592 fixed API variables calls 2022-04-05 18:39:46 +02:00
El Mghazli Yacine
fa03569f1f APIv2.2 2022-04-05 18:16:03 +02:00
El Mghazli Yacine
108d9a965e Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-04-05 11:52:39 +02:00
El Mghazli Yacine
6686076f06 yacine s'adapte grace a sa flaxibilité legendaire 2022-04-05 11:43:20 +02:00
Raymond Knopp
afdbca2a48 Adapt to new frontend interface 2022-04-05 11:31:34 +02:00
El Mghazli Yacine
a3b64c4a7e APIv2.2 2022-04-04 17:49:01 +02:00
El Mghazli Yacine
564bcde637 Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-04-04 16:51:31 +02:00
El Mghazli Yacine
2027d77b18 APIv2.1 2022-04-04 16:51:28 +02:00
frtabu
28f1035e27 config module fixes and websrv modif 2022-04-01 18:50:46 +02:00
El Mghazli Yacine
6e3f09a35d webserv APIv2 (frontend part) 2022-04-01 18:03:34 +02:00
El Mghazli Yacine
9ca2d78f2e working with first API version 2022-04-01 17:41:30 +02:00
frtabu
14321bfe6f fix config file generation to sip command line only options 2022-04-01 01:22:43 +02:00
frtabu
1fee616d62 config module enhancement,step 2 2022-04-01 00:47:19 +02:00
frtabu
1eb79272a1 config module enhancement, next step 2022-03-30 18:53:32 +02:00
frtabu
79b8586432 config module enhancement (dump of running config): start implem. 2022-03-29 20:06:41 +02:00
Raymond Knopp
9301d8a7f0 add websrv as optional lib and frontend as independant target in oai build 2022-03-22 19:10:42 +01:00
frtabu
8ad4c7b75c websrv backend implementation next step 2022-03-21 20:05:22 +01:00
frtabu
36e1715f18 json format sync with frontend 2022-03-18 19:46:10 +01:00
El Mghazli Yacine
310245ef78 backend env 2022-03-17 18:05:14 +01:00
El Mghazli Yacine
64e33694ef Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-03-17 17:57:56 +01:00
El Mghazli Yacine
8c355a08a7 api first test 2022-03-17 17:57:44 +01:00
frtabu
b575a95144 Merge branch 'websrv' of https://gitlab.eurecom.fr/oai/openairinterface5g into websrv 2022-03-17 16:55:07 +01:00
frtabu
c239d99f1c Tested init with front end 2022-03-17 16:53:02 +01:00
El Mghazli Yacine
fc3319c9dc display ok 2022-03-17 16:09:05 +01:00
El Mghazli Yacine
f1e87cab8f Merge branch 'websrv' of gitlab.eurecom.fr:oai/openairinterface5g into websrv 2022-03-17 11:33:30 +01:00
frtabu
e5a96b5d01 Add https support and front-end url support 2022-03-17 11:24:15 +01:00
El Mghazli Yacine
77b4022bbf rudiments ok 2022-03-17 11:21:26 +01:00
frtabu
d007295a34 Add https support and front-end url support 2022-03-16 21:03:48 +01:00
El Mghazli Yacine
6bcd3006b0 ng build OK 2022-03-16 19:55:31 +01:00
El Mghazli Yacine
7acc5e967b Merge remote-tracking branch 'origin/websrv' into websrv-front 2022-03-15 18:07:40 +01:00
El Mghazli Yacine
426b99211d updated tslib 2022-03-15 18:03:11 +01:00
frtabu
c7b2c19f0c go on websrv implem 2022-03-15 11:13:21 +01:00
frtabu
317559806a Initial implem web interface for telnet server 2022-03-14 19:53:33 +01:00
El Mghazli Yacine
51362d9c6c initial commit 2022-03-14 18:18:52 +01:00
El Mghazli Yacine
8ead6ae767 cleanup 2022-03-14 15:36:09 +01:00
El Mghazli Yacine
6e915cd64e updated angular successfully 2022-03-14 15:34:11 +01:00
El Mghazli Yacine
190f90e35d ng update 2022-03-14 15:24:03 +01:00
El Mghazli Yacine
7f19b9453e ng update 2022-03-14 14:09:35 +01:00
El Mghazli Yacine
5861a7019b ngx initial commit before upgrade 2022-03-14 14:07:22 +01:00
82 changed files with 34480 additions and 167 deletions

30
.vscode/launch.json vendored
View File

@@ -10,7 +10,8 @@
"request": "launch",
"program": "${workspaceFolder}/cmake_targets/ran_build/build/lte-softmodem",
"args": [
"-O", "../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf",
"-O",
"../ci-scripts/conf_files/rcc.band7.tm1.nfapi.conf",
"--noS1"
],
"stopAtEntry": false,
@@ -26,6 +27,31 @@
"ignoreFailures": true
}
]
},
{
"name": "Launch Chrome (4200 ng serve)",
"type": "chrome",
"request": "launch",
"url": "http://localhost:4200",
"webRoot": "${workspaceFolder}/common/utils/websrv/frontend"
},
{
"name": "Launch Chrome (localhost)",
"type": "chrome",
"request": "launch",
"url": "http://localhost",
"webRoot": "${workspaceFolder}",
"sourceMapPathOverrides": {
"webpack:/*": "${webRoot}/*",
"/./*": "${webRoot}/*",
"/src/*": "${webRoot}/*",
"/*": "*",
"/./~/*": "${webRoot}/node_modules/*"
},
"trace": true,
"runtimeArgs": [
"--remote-debugging-port=9222"
]
}
]
}
}

View File

@@ -3178,3 +3178,4 @@ ADD_CUSTOM_TARGET(oarf
)
include (${OPENAIR_DIR}/common/utils/telnetsrv/telnetsrv_CMakeLists.txt)
include (${OPENAIR_DIR}/common/utils/websrv/websrv_CMakeLists.txt)

View File

@@ -57,7 +57,7 @@ CMAKE_BUILD_TYPE="RelWithDebInfo"
CMAKE_CMD="$CMAKE"
BUILD_ECLIPSE=0
NR="False"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope websrv"
RU=0
trap handle_ctrl_c INT

View File

@@ -88,6 +88,8 @@ int processoption(paramdef_t *cfgoptions, char *value) {
config_check_valptr(cfgoptions, cfgoptions->strptr, strlen(tmpval)+1);
sprintf(*(cfgoptions->strptr), "%s",tmpval);
} else {
AssertFatal( ( strlen(*(cfgoptions->strptr)) < cfgoptions->numelt), "[CONFIG] Error: %s too long, %s value cannot exceed %i bytes\n",
cfgoptions->optname,tmpval, cfgoptions->numelt) ;
sprintf( (char *)(cfgoptions->strptr), "%s",tmpval);
}

View File

@@ -88,10 +88,21 @@ int load_config_sharedlib(configmodule_interface_t *cfgptr) {
st = -1;
}
if ( cfgptr->rtflags & CONFIG_SAVERUNCFG ) {
sprintf (fname,"config_%s_set",cfgptr->cfgmode);
cfgptr->set = dlsym(lib_handle,fname);
if (cfgptr->set == NULL ) {
printf("[CONFIG] %s %d no function %s for config mode %s\n",
__FILE__, __LINE__,fname, cfgptr->cfgmode);
st = -1;
}
}
sprintf (fname,"config_%s_end",cfgptr->cfgmode);
cfgptr->end = dlsym(lib_handle,fname);
if (cfgptr->getlist == NULL ) {
if (cfgptr->end == NULL ) {
printf("[CONFIG] %s %d no function %s for config mode %s\n",
__FILE__, __LINE__,fname, cfgptr->cfgmode);
}
@@ -186,6 +197,7 @@ configmodule_interface_t *load_configmodule(int argc,
uint32_t initflags)
{
char *cfgparam=NULL;
char *modeparams=NULL;
char *cfgmode=NULL;
char *strtokctx=NULL;
@@ -193,7 +205,8 @@ configmodule_interface_t *load_configmodule(int argc,
uint32_t tmpflags=0;
int i;
int OoptIdx=-1;
int OWoptIdx=-1;
printf("CMDLINE: ");
for (int i=0; i<argc; i++)
printf("\"%s\" ", argv[i]);
@@ -208,7 +221,11 @@ configmodule_interface_t *load_configmodule(int argc,
cfgparam = argv[i+1];
OoptIdx=i;
}
char *OWopt = strstr(argv[i],"OW");
if ( OWopt == argv[i]+2) {
OWoptIdx=i;
}
if ( strstr(argv[i], "help_config") != NULL ) {
config_printhelp(Config_Params,CONFIG_PARAMLENGTH(Config_Params),CONFIG_SECTIONNAME);
exit(0);
@@ -258,6 +275,11 @@ configmodule_interface_t *load_configmodule(int argc,
/* argv[0] is the exec name, always Ok */
cfgptr->argv_info[0] |= CONFIG_CMDLINEOPT_PROCESSED;
/* When reuested _(_--OW or rtflag is 5), a file with config parameters, as defined after all processing, will be created */
if (OWoptIdx >= 0) {
cfgptr->argv_info[OWoptIdx] |= CONFIG_CMDLINEOPT_PROCESSED;
cfgptr->rtflags |= CONFIG_SAVERUNCFG ;
}
/* when OoptIdx is >0, -O option has been detected at position OoptIdx
* we must memorize arv[OoptIdx is Ok */
if (OoptIdx >= 0) {
@@ -294,14 +316,18 @@ configmodule_interface_t *load_configmodule(int argc,
printf("%s ",cfgptr->cfgP[i]);
}
printf(", debug flags: 0x%08x\n",cfgptr->rtflags);
if (cfgptr->rtflags & CONFIG_PRINTPARAMS) {
cfgptr->status = malloc(sizeof(configmodule_status_t));
}
if (strstr(cfgparam,CONFIG_CMDLINEONLY) == NULL) {
i=load_config_sharedlib(cfgptr);
if (i == 0) {
printf("[CONFIG] config module %s loaded\n",cfgmode);
Config_Params[CONFIGPARAM_DEBUGFLAGS_IDX].uptr=&(cfgptr->rtflags);
int idx = config_paramidx_fromname(Config_Params, CONFIG_PARAMLENGTH(Config_Params), CONFIGP_DEBUGFLAGS);
Config_Params[idx].uptr=&(cfgptr->rtflags);
idx = config_paramidx_fromname(Config_Params, CONFIG_PARAMLENGTH(Config_Params), CONFIGP_TMPDIR);
Config_Params[idx].strptr=&(cfgptr->tmpdir);
config_get(Config_Params,CONFIG_PARAMLENGTH(Config_Params), CONFIG_SECTIONNAME );
} else {
fprintf(stderr,"[CONFIG] %s %d config module \"%s\" couldn't be loaded\n", __FILE__, __LINE__,cfgmode);
@@ -313,7 +339,10 @@ configmodule_interface_t *load_configmodule(int argc,
cfgptr->getlist = config_cmdlineonly_getlist;
cfgptr->end = (configmodule_endfunc_t)nooptfunc;
}
printf("[CONFIG] debug flags: 0x%08x\n",cfgptr->rtflags);
if (modeparams != NULL) free(modeparams);
if (cfgmode != NULL) free(cfgmode);
@@ -330,6 +359,14 @@ configmodule_interface_t *load_configmodule(int argc,
/* free memory allocated when reading parameters */
/* config module could be initialized again after this call */
void end_configmodule(void) {
if ( cfgptr->status && (cfgptr->rtflags & CONFIG_SAVERUNCFG)) {
printf_params("[CONFIG] Runtime params creation status: %i null values, %i errors, %i empty list or array, %i successfull \n",
cfgptr->status->num_err_nullvalue,
cfgptr->status->num_err_write,
cfgptr->status->emptyla,
cfgptr->status->num_write);
}
if (cfgptr != NULL) {
if (cfgptr->end != NULL) {
printf ("[CONFIG] calling config module end function...\n");
@@ -355,7 +392,7 @@ void end_configmodule(void) {
void free_configmodule(void) {
if (cfgptr != NULL) {
end_configmodule();
if( cfgptr->status != NULL) free(cfgptr->status);
if( cfgptr->cfgmode != NULL) free(cfgptr->cfgmode);
printf ("[CONFIG] free %i config parameter pointers\n",cfgptr->num_cfgP);

View File

@@ -54,6 +54,7 @@
#define CONFIG_DEBUGPTR (1<<1) // print memory allocation/free debug messages
#define CONFIG_DEBUGCMDLINE (1<<2) // print command line processing messages
#define CONFIG_NOABORTONCHKF (1<<4) // disable abort execution when parameter checking function fails
#define CONFIG_SAVERUNCFG (1<<5) // create config file with running values, as after cfg file + command line processing
#define CONFIG_NOEXITONHELP (1<<19) // do not exit after printing help
#define CONFIG_HELP (1<<20) // print help message
#define CONFIG_ABORT (1<<21) // config failed,abort execution
@@ -61,7 +62,23 @@
typedef int(*configmodule_initfunc_t)(char *cfgP[],int numP);
typedef int(*configmodule_getfunc_t)(paramdef_t *,int numparams, char *prefix);
typedef int(*configmodule_getlistfunc_t)(paramlist_def_t *, paramdef_t *,int numparams, char *prefix);
typedef int(*configmodule_setfunc_t)(paramdef_t *cfgoptions, int numoptions, char *prefix );
typedef void(*configmodule_endfunc_t)(void);
typedef struct configmodule_status {
int num_paramgroups;
char ** paramgroups_names;
int num_err_nullvalue;
int emptyla;
union {
int num_err_read;
int num_err_write;
};
union {
int num_read;
int num_write;
} ;
}configmodule_status_t;
typedef struct configmodule_interface {
int argc;
char **argv;
@@ -72,11 +89,14 @@ typedef struct configmodule_interface {
configmodule_initfunc_t init;
configmodule_getfunc_t get;
configmodule_getlistfunc_t getlist;
configmodule_setfunc_t set;
configmodule_endfunc_t end;
uint32_t numptrs;
uint32_t rtflags;
char *ptrs[CONFIG_MAX_ALLOCATEDPTRS];
bool ptrsAllocated[CONFIG_MAX_ALLOCATEDPTRS];
char *tmpdir;
configmodule_status_t *status; // allocated in debug mode only
} configmodule_interface_t;
#ifdef CONFIG_LOADCONFIG_MAIN
@@ -89,16 +109,19 @@ static char config_helpstr [] = "\n lte-softmodem -O [config mode]<:dbgl[debugfl
incp parameter can be used to define the include path used for config files (@include directive)\n \
defaults is set to the path of the main config file.\n";
#define CONFIG_SECTIONNAME "config"
#define CONFIGPARAM_DEBUGFLAGS_IDX 0
#define CONFIG_HELP_TMPDIR "<Repository to create temporary file>"
#define CONFIG_SECTIONNAME "config"
#define CONFIGP_DEBUGFLAGS "debugflags"
#define CONFIGP_TMPDIR "tmpdir"
static paramdef_t Config_Params[] = {
/*-----------------------------------------------------------------------------------------------------------------------*/
/* config parameters for config module */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-----------------------------------------------------------------------------------------------------------------------*/
{"debugflags", config_helpstr, 0, uptr:NULL, defintval:0, TYPE_MASK, 0},
/*--------------------------------------------------------------------------------------------------------------------------*/
/* config parameters for config module */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*--------------------------------------------------------------------------------------------------------------------------*/
{CONFIGP_DEBUGFLAGS, config_helpstr, 0, uptr:NULL, defintval:0, TYPE_MASK, 0},
{CONFIGP_TMPDIR, CONFIG_HELP_TMPDIR,PARAMFLAG_NOFREE, strptr:NULL, defstrval:"/tmp" ,TYPE_STRING, 0},
};
#else

View File

@@ -46,6 +46,7 @@
#define PARAMFLAG_NOFREE (1 << 3) // don't free parameter in end function
#define PARAMFLAG_BOOL (1 << 4) // integer param can be 0 or 1
#define PARAMFLAG_CMDLINE_NOPREFIXENABLED (1 << 5) // on the command line, allow a parameter to be specified without the prefix
#define PARAMFLAG_CMDLINEONLY (1 << 6) // this parameter cannot be specified in config file
/* Flags used by config modules to return info to calling modules and/or to for internal usage*/
#define PARAMFLAG_MALLOCINCONFIG (1 << 15) // parameter allocated in config module

View File

@@ -237,6 +237,9 @@ int config_get(paramdef_t *params, int numparams, char *prefix) {
if (ret >= 0) {
config_process_cmdline(params, numparams, prefix);
if ( cfgif->rtflags & CONFIG_SAVERUNCFG ) {
config_get_if()->set(params, numparams, prefix);
}
config_execcheck(params, numparams, prefix);
}
@@ -271,9 +274,11 @@ int config_getlist(paramlist_def_t *ParamList, paramdef_t *params, int numparams
char cfgpath[MAX_OPTNAME_SIZE*2 + 6]; /* prefix.listname.[listindex] */
for (int i = 0; i < ParamList->numelt; ++i) {
// TODO config_process_cmdline?
sprintf(cfgpath, "%s.[%i]", newprefix, i);
config_process_cmdline(ParamList->paramarray[i],numparams,cfgpath);
if ( config_get_if()->rtflags & CONFIG_SAVERUNCFG ) {
config_get_if()->set(ParamList->paramarray[i], numparams,cfgpath );
}
config_execcheck(ParamList->paramarray[i], numparams, cfgpath);
}

View File

@@ -34,8 +34,11 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <time.h>
#include <libgen.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "config_libconfig.h"
#include "config_libconfig_private.h"
@@ -87,8 +90,222 @@ int read_intarray(paramdef_t *cfgoptions,config_setting_t *setting, char *cfgpat
return cfgoptions->numelt;
}
int config_libconfig_setparams(paramdef_t *cfgoptions, int numoptions, config_setting_t * asetting, char * prefix) {
int status;
int errors=0;
int notused=0;
int emptyla=0;
#define LIBCONFIG_NOTUSED_PARAMS "Not used? (NULL value ptr)"
char *secprefix=(prefix==NULL)?"":prefix;
for(int i=0; i<numoptions; i++) {
if (cfgoptions[i].paramflags & PARAMFLAG_CMDLINEONLY) {
continue;
printf_params("[LIBCONFIG] setting %s.%s skipped (command line only) \n",secprefix,cfgoptions[i].optname);
}
status=CONFIG_FALSE;
config_setting_t * psetting;
char *spath=malloc(((prefix==NULL)?0:strlen(prefix))+strlen(cfgoptions[i].optname)+10);
sprintf(spath,"%s%s%s",secprefix, (prefix==NULL)?"":".", cfgoptions[i].optname);
psetting = config_lookup(&(libconfig_privdata.runtcfg),spath);
free(spath);
if (psetting != NULL) {
printf_params("[LIBCONFIG] setting %s.%s already created \n",secprefix,cfgoptions[i].optname);
continue;
}
switch(cfgoptions[i].type) {
case TYPE_STRING:
psetting =config_setting_add(asetting,cfgoptions[i].optname,CONFIG_TYPE_STRING);
if (psetting!= NULL) {
char *value;
if (cfgoptions[i].strptr == NULL) {
notused++;
value=LIBCONFIG_NOTUSED_PARAMS;
} else {
if (cfgoptions[i].numelt == 0 ) {
value = *(cfgoptions[i].strptr);
} else {
value = (char *)(cfgoptions[i].strptr);
}
}
status=config_setting_set_string(psetting, value);
}
break;
case TYPE_STRINGLIST:
psetting =config_setting_add(asetting,cfgoptions[i].optname,CONFIG_TYPE_LIST);
if (psetting!= NULL)
{
if(cfgoptions[i].numelt <= 0) {
emptyla++;
printf_params("[LIBCONFIG], no element in list %s\n",cfgoptions[i].optname);
status=CONFIG_TRUE;
}
for (int j=0; j<cfgoptions[i].numelt ; j++) {
config_setting_t *elemsetting=config_setting_set_string_elem(psetting, -1,cfgoptions[i].strptr[j] );
if (elemsetting == NULL) {
fprintf(stderr,"[LIBCONFIG] Error: Creating list %s element %i value %s\n",cfgoptions[i].optname,j,cfgoptions[i].strptr[j]);
break;
} else
status=CONFIG_TRUE;
}
}
break;
case TYPE_UINT8:
case TYPE_INT8:
case TYPE_UINT16:
case TYPE_INT16:
case TYPE_UINT32:
case TYPE_INT32:
case TYPE_MASK:
psetting =config_setting_add(asetting,cfgoptions[i].optname,CONFIG_TYPE_INT);
if (psetting!= NULL)
status=config_setting_set_int(psetting, (int)*(cfgoptions[i].iptr) );
break;
case TYPE_UINT64:
case TYPE_INT64:
psetting =config_setting_add(asetting,cfgoptions[i].optname,CONFIG_TYPE_INT64);
if (psetting!= NULL)
status=config_setting_set_int64(psetting, (int64_t)*(cfgoptions[i].i64ptr) );
break;
case TYPE_UINTARRAY:
case TYPE_INTARRAY:
psetting =config_setting_add(asetting,cfgoptions[i].optname,CONFIG_TYPE_ARRAY);
if (psetting!= NULL) {
if(cfgoptions[i].numelt <= 0) {
emptyla++;
printf_params("[LIBCONFIG], no element in array %s.%s\n",secprefix,cfgoptions[i].optname);
status=CONFIG_TRUE;
}
for (int j=0; j<cfgoptions[i].numelt ; j++) {
config_setting_t *elemsetting=config_setting_set_int_elem(psetting, -1,(int)(cfgoptions[i].iptr[j]) );
if (elemsetting == NULL) {
fprintf(stderr,"[LIBCONFIG] Error: Creating array %s.%s, at index %i value %i\n",secprefix,cfgoptions[i].optname,j,(int)(cfgoptions[i].iptr[j]));
break;
} else
status=CONFIG_TRUE;
}
}
break;
case TYPE_DOUBLE:
psetting =config_setting_add(asetting,cfgoptions[i].optname,CONFIG_TYPE_FLOAT);
if (psetting!= NULL)
status=config_setting_set_float(psetting, (double)*(cfgoptions[i].dblptr) );
break;
case TYPE_IPV4ADDR:
psetting =config_setting_add(asetting,cfgoptions[i].optname,CONFIG_TYPE_STRING);
if (psetting!= NULL) {
char ipstr[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, cfgoptions[i].uptr, ipstr, INET_ADDRSTRLEN) == NULL) {
notused++;
sprintf(ipstr,"undef");
} else {
status=config_setting_set_string(psetting, ipstr);
}
}
break;
case TYPE_LIST:
break;
default:
fprintf(stderr,"[LIBCONFIG] %s.%s type %i not supported\n",secprefix,cfgoptions[i].optname ,cfgoptions[i].type);
status=CONFIG_FALSE;
break;
} /* switch on param type */
if (status != CONFIG_TRUE) {
errors++;
fprintf(stderr,"[LIBCONFIG] Error creating setting %i: %s.%s type %i\n",i,secprefix,cfgoptions[i].optname ,cfgoptions[i].type);
}
}
printf_params("[LIBCONFIG], in group \"%s\" %i settings \n",secprefix,numoptions);
if (notused > 0)
printf_params("[LIBCONFIG], ..... %i settings with NULL value pointer\n",notused);
if (errors >0)
fprintf(stderr,"[LIBCONFIG] .....%i settings creation errors \n",errors);
if (emptyla >0)
fprintf(stderr,"[LIBCONFIG] .....%i empty lists or arrays settings \n",emptyla);
if ( cfgptr->status ) {
cfgptr->status->num_err_nullvalue += notused;
cfgptr->status->num_err_write += errors;
cfgptr->status->num_write += numoptions;
cfgptr->status->emptyla += emptyla;
}
return errors;
}
int config_libconfig_set(paramdef_t *cfgoptions, int numoptions, char *prefix ) {
char *tokctx1=NULL;
int listidx=-1;
char *prefixbck=NULL;
char *prefix_elem1=NULL;
config_setting_t * asetting = config_root_setting (&(libconfig_privdata.runtcfg));
if (prefix != NULL) {
prefixbck=strdup(prefix);
prefix_elem1 = strtok_r(prefixbck, ".", &tokctx1);
}
printf_params("[LIBCONFIG], processing prefix %s\n",(prefix==NULL)?"NULL":prefix);
/* parse the prefix , possibly creating groups, lists and list elements */
while (prefix_elem1 != NULL) {
int n1=strlen(prefix_elem1);
char *prefix_elem2 = prefix_elem1+n1+1 ;
printf_params("[LIBCONFIG], processing elem1 %s elem2 %s\n",prefix_elem1,prefix_elem2);
/* prefix (the path to the parameter name) may contain groups and elements from a list, which are specified with [] */
if (prefix_elem2[0] != '[') { // not a list
config_setting_t *tmpset = config_setting_lookup(asetting,prefix_elem1);
if (tmpset == NULL)
asetting = config_setting_add (asetting, prefix_elem1, CONFIG_TYPE_GROUP);
else
asetting = tmpset;
printf_params("[LIBCONFIG], creating or looking for group %s: %s\n",prefix_elem1,(asetting==NULL)?"NOK":"OK");
} else { // a list
listidx=(int)strtol(prefix_elem2+1,NULL,10);
if (errno == EINVAL || errno == ERANGE) {
printf_params("[LIBCONFIG], Error %s looking for list index in %s \n",strerror(errno), prefix_elem2);
break;
}
config_setting_t *tmpset = config_setting_lookup(asetting,prefix_elem1);
if (tmpset == NULL)
asetting = config_setting_add (asetting, prefix_elem1, CONFIG_TYPE_LIST);
else
asetting = tmpset;
printf_params("[LIBCONFIG], creating or looking for list %s: %s\n",prefix_elem1,(asetting==NULL)?"NOK":"OK");
if (asetting != NULL) {
tmpset = config_setting_get_elem (asetting, listidx);
if (tmpset == NULL)
asetting = config_setting_add (asetting, NULL, CONFIG_TYPE_GROUP);
else
asetting = tmpset;
printf_params("[LIBCONFIG], creating or looking for list element %i: %s\n",listidx,(asetting==NULL)?"NOK":"OK");
}
prefix_elem1 = strtok_r(NULL, ".", &tokctx1); //skip the [x] elements we already took care of it
if (prefix_elem1 == NULL) {
fprintf(stderr, "[LICONFIG] End of parsing %s \n",prefix);
break;
}
}
if (asetting == NULL) {
fprintf(stderr,"[LIBCONFIG] Error creating setting %s %s %i\n",prefix_elem1,prefix_elem2,listidx);
}
prefix_elem1 = strtok_r(NULL, ".", &tokctx1);
}
free(prefixbck);
if (asetting != NULL) {
config_libconfig_setparams(cfgoptions, numoptions,asetting,prefix);
printf_params("[LIBCONFIG] %i settings added in group %s\n",numoptions,(prefix==NULL)?"":prefix);
return 0;
} else {
fprintf(stderr,"[LIBCONFIG] Error parsing %s params, skipped...\n",(prefix==NULL)?"":prefix);
return -1;
}
}
int config_libconfig_get(paramdef_t *cfgoptions,int numoptions, char *prefix ) {
@@ -376,6 +593,12 @@ int config_libconfig_init(char *cfgP[], int numP) {
free(tmppath);
return -1;
}
/* possibly init a libconfig struct for saving really used params */
if( cfgptr->rtflags & CONFIG_SAVERUNCFG ) {
config_init ( &(libconfig_privdata.runtcfg));
// config_set_options (&(libconfig_privdata.runtcfg), CONFIG_OPTION_ALLOW_OVERRIDES, CONFIG_TRUE);
}
free(tmppath);
return 0;
}
@@ -383,7 +606,25 @@ int config_libconfig_init(char *cfgP[], int numP) {
void config_libconfig_end(void ) {
config_destroy(&(libconfig_privdata.cfg));
if( cfgptr->rtflags & CONFIG_SAVERUNCFG ) {
char *fname=strdup(libconfig_privdata.configfile);
int newcfgflen = strlen( libconfig_privdata.configfile) + strlen(cfgptr->tmpdir) + 20;
char *newcfgf = malloc( newcfgflen);
time_t t = time(NULL);
struct tm tm = *localtime(&t);
snprintf (newcfgf,newcfgflen, "%s/%s-run%d_%02d_%02d_%02d%02d",cfgptr->tmpdir,basename(fname),
tm.tm_year + 1900,tm.tm_mon + 1,tm.tm_mday, tm.tm_hour, tm.tm_min);
if ( config_write_file (&(libconfig_privdata.runtcfg) , newcfgf) != CONFIG_TRUE) {
fprintf(stderr,"[LIBCONFIG] %s %d file %s - line %d: %s\n",__FILE__, __LINE__,
newcfgf, config_error_line(&(libconfig_privdata.runtcfg)),
config_error_text(&(libconfig_privdata.runtcfg)));
} else {
printf("[LIBCONFIG] file %s created successfully\n", newcfgf);
}
config_destroy(&(libconfig_privdata.runtcfg));
free(fname);
free(newcfgf);
}
if ( libconfig_privdata.configfile != NULL ) {
free(libconfig_privdata.configfile);
libconfig_privdata.configfile=NULL;

View File

@@ -40,12 +40,11 @@ extern "C"
#include "common/config/config_paramdesc.h"
typedef struct libconfig_privatedata {
char *configfile;
config_t cfg;
config_t runtcfg;
} libconfig_privatedata_t;
#ifdef __cplusplus

View File

@@ -302,8 +302,8 @@ void log_getconfig(log_t *g_log)
for (int i=MIN_LOG_COMPONENTS; i < MAX_LOG_PREDEF_COMPONENTS; i++) {
if(g_log->log_component[i].name == NULL) {
g_log->log_component[i].name = malloc(16);
sprintf((char *)g_log->log_component[i].name,"comp%i?",i);
g_log->log_component[i].name = calloc(1,16);
snprintf((char *)g_log->log_component[i].name,15,"undefcomp%i",i);
logparams_logfile[i].paramflags = PARAMFLAG_DONOTREAD;
logparams_level[i].paramflags = PARAMFLAG_DONOTREAD;
}

View File

@@ -90,25 +90,28 @@ int get_phybsize(void) {
};
int add_telnetcmd(char *modulename,telnetshell_vardef_t *var, telnetshell_cmddef_t *cmd );
int setoutput(char *buff, int debug, telnet_printfunc_t prnt);
int wsetoutput(char *buff, int debug, telnet_printfunc_t prnt, ... );
int setparam(char *buff, int debug, telnet_printfunc_t prnt);
int wsetparam(char *buff, int debug, telnet_printfunc_t prnt, ... );
int history_cmd(char *buff, int debug, telnet_printfunc_t prnt);
telnetshell_vardef_t telnet_vardef[] = {
{"debug",TELNET_VARTYPE_INT32,&telnetparams.telnetdbg},
{"prio",TELNET_VARTYPE_INT32,&telnetparams.priority},
{"loopc",TELNET_VARTYPE_INT32,&telnetparams.loopcount},
{"loopd",TELNET_VARTYPE_INT32,&telnetparams.loopdelay},
{"phypb",TELNET_VARTYPE_INT32,&telnetparams.phyprntbuff_size},
{"hsize",TELNET_VARTYPE_INT32,&telnetparams.histsize},
{"hfile",TELNET_VARTYPE_STRING,&telnetparams.histfile},
{"",0,NULL}
{"debug",TELNET_VARTYPE_INT32,0,&telnetparams.telnetdbg},
{"prio",TELNET_VARTYPE_INT32,0,&telnetparams.priority},
{"loopc",TELNET_VARTYPE_INT32,0,&telnetparams.loopcount},
{"loopd",TELNET_VARTYPE_INT32,0,&telnetparams.loopdelay},
{"phypb",TELNET_VARTYPE_INT32,0,&telnetparams.phyprntbuff_size},
{"hsize",TELNET_VARTYPE_INT32,0,&telnetparams.histsize},
{"hfile",TELNET_VARTYPE_STRING,TELNET_CHECKVAL_RDONLY,&telnetparams.histfile},
{"",0,0,NULL}
};
telnetshell_cmddef_t telnet_cmdarray[] = {
{"redirlog","[here,file,off]",setoutput},
{"param","[prio]",setparam},
{"history","[list,reset]",history_cmd},
{"","",NULL},
{"redirlog","[here,file,off]",setoutput,{wsetoutput},0,NULL},
{"param","[prio]",setparam,{wsetparam},0,NULL},
{"history","[list,reset]",history_cmd,{NULL},TELNETSRV_CMDFLAG_TELNETONLY,NULL},
{"","",NULL,{NULL},0,NULL},
};
@@ -245,6 +248,11 @@ void redirstd(char *newfname,telnet_printfunc_t prnt ) {
prnt("ERROR: stderr redir to %s error %s",strerror(errno));
}
}
int wsetoutput(char *buffer, int debug, telnet_printfunc_t prnt, ... ) {
return 0;
}
int setoutput(char *buff, int debug, telnet_printfunc_t prnt) {
char cmds[TELNET_MAX_MSGLENGTH/TELNET_CMD_MAXSIZE][TELNET_CMD_MAXSIZE];
char *logfname;
@@ -281,6 +289,10 @@ int setoutput(char *buff, int debug, telnet_printfunc_t prnt) {
return CMDSTATUS_FOUND;
} /* setoutput */
int wsetparam(char *buff, int debug, telnet_printfunc_t prnt, ...) {
return 0;
}
int setparam(char *buff, int debug, telnet_printfunc_t prnt) {
char cmds[TELNET_MAX_MSGLENGTH/TELNET_CMD_MAXSIZE][TELNET_CMD_MAXSIZE];
memset(cmds,0,sizeof(cmds));
@@ -344,6 +356,98 @@ int history_cmd(char *buff, int debug, telnet_printfunc_t prnt) {
/*
generic commands available for all modules loaded by the server
*/
char *telnet_getvarvalue(telnetshell_vardef_t *var, int varindex) {
char *val;
switch(var[varindex].vartype) {
case TELNET_VARTYPE_INT32:
val=malloc(64);
snprintf(val,64,"%i",*(int32_t *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_INT64:
val=malloc(128);
snprintf(val,128,"%lli",(long long)*(int64_t *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_INT16:
val=malloc(16);
snprintf(val,16,"%hi",*(short *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_INT8:
val=malloc(16);
snprintf(val,16,"%i",(int)(*(int8_t *)(var[varindex].varvalptr)));
break;
case TELNET_VARTYPE_UINT:
val=malloc(64);
snprintf(val,64,"%u",*(unsigned int *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_DOUBLE:
val=malloc(32);
snprintf(val,32,"%g\n",*(double *)(var[varindex].varvalptr));
break;
case TELNET_VARTYPE_STRING:
val = strdup(*(char **)(var[varindex].varvalptr));
break;
default:
val=malloc(64);
snprintf(val,64,"ERR:var %i unknown type",varindex);
break;
}
return val;
}
int telnet_setvarvalue(telnetshell_vardef_t *var,char *strval, telnet_printfunc_t prnt ) {
int st=0;
switch(var->vartype) {
case TELNET_VARTYPE_INT32:
*(int *)(var->varvalptr) = (int)strtol(strval,NULL,0);
if (prnt != NULL)
prnt("%i\n",*(int *)(var->varvalptr));
break;
case TELNET_VARTYPE_INT16:
*(short *)(var->varvalptr) = (short)strtol(strval,NULL,0);
if (prnt != NULL)
prnt("%hi\n",*(short *)(var->varvalptr));
break;
case TELNET_VARTYPE_INT8:
*(char *)(var->varvalptr) = (char)strtol(strval,NULL,0);
if (prnt != NULL)
prnt("%i\n",*(int *)(var->varvalptr));
break;
case TELNET_VARTYPE_UINT:
*(unsigned int *)(var->varvalptr) = (unsigned int)strtol(strval,NULL,0);
if (prnt != NULL)
prnt("%u\n",*(unsigned int *)(var->varvalptr));
break;
case TELNET_VARTYPE_DOUBLE:
*(double *)(var->varvalptr) = strtod(strval,NULL);
if (prnt != NULL)
prnt("%g\n",*(double *)(var->varvalptr));
break;
case TELNET_VARTYPE_STRING:
sprintf(*(char **)(var->varvalptr),"%s", strval);
if (prnt != NULL)
prnt("\"%s\"\n",*(char **)(var->varvalptr));
break;
default:
if (prnt != NULL)
prnt("unknown type\n");
st=-1;
break;
}
return st;
}
int setgetvar(int moduleindex,char getorset,char *params) {
int n,i;
@@ -357,81 +461,15 @@ int setgetvar(int moduleindex,char getorset,char *params) {
if (n > 0 && (getorset == 'g' || getorset == 'G')) {
client_printf("%s, %s = ", telnetparams.CmdParsers[moduleindex].module,
telnetparams.CmdParsers[moduleindex].var[i].varname );
switch(telnetparams.CmdParsers[moduleindex].var[i].vartype) {
case TELNET_VARTYPE_INT32:
client_printf("%i\n",*(int32_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_INT64:
client_printf("%lli\n",*(int64_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_INT16:
client_printf("%hi\n",*(short *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_INT8:
client_printf("%i\n",(int)(*(int8_t *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr)));
break;
case TELNET_VARTYPE_UINT:
client_printf("%u\n",*(unsigned int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_DOUBLE:
client_printf("%g\n",*(double *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_STRING:
client_printf("\"%s\"\n",*(char **)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
default:
client_printf("unknown type\n");
break;
}
char *strval=telnet_getvarvalue(telnetparams.CmdParsers[moduleindex].var, i);
client_printf("%s\n",strval);
free(strval);
}
if (n > 1 && (getorset == 's' || getorset == 'S')) {
client_printf("%s, %s set to \n", telnetparams.CmdParsers[moduleindex].module,
telnetparams.CmdParsers[moduleindex].var[i].varname);
telnet_setvarvalue(&(telnetparams.CmdParsers[moduleindex].var[i]),varval, client_printf);
switch(telnetparams.CmdParsers[moduleindex].var[i].vartype) {
case TELNET_VARTYPE_INT32:
*(int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = (int)strtol(varval,NULL,0);
client_printf("%i\n",*(int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_INT16:
*(short *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = (short)strtol(varval,NULL,0);
client_printf("%hi\n",*(short *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_INT8:
*(char *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = (char)strtol(varval,NULL,0);
client_printf("%i\n",*(int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_UINT:
*(unsigned int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = (unsigned int)strtol(varval,NULL,0);
client_printf("%u\n",*(unsigned int *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_DOUBLE:
*(double *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr) = strtod(varval,NULL);
client_printf("%g\n",*(double *)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
case TELNET_VARTYPE_STRING:
sprintf(*(char **)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr),"%s", varval);
client_printf("\"%s\"\n",*(char **)(telnetparams.CmdParsers[moduleindex].var[i].varvalptr));
break;
default:
client_printf("unknown type\n");
break;
}
}
}
}
@@ -474,6 +512,8 @@ int process_command(char *buf) {
}
for(j=0; telnetparams.CmdParsers[i].cmd[j].cmdfunc != NULL ; j++) {
if ( telnetparams.CmdParsers[i].cmd[j].cmdflags & TELNETSRV_CMDFLAG_WEBSRVONLY )
continue;
client_printf(" %s %s %s\n",
telnetparams.CmdParsers[i].module,telnetparams.CmdParsers[i].cmd[j].cmdname,
telnetparams.CmdParsers[i].cmd[j].helpstr);
@@ -489,7 +529,7 @@ int process_command(char *buf) {
j = sscanf(buf,"%19s %19s %m[^\t\n]",modulename,cmd,&cmdb);
if (telnetparams.telnetdbg > 0)
printf("process_command: %i words, module=%s cmd=%s, parameters= %s\n",j,modulename,cmd,cmdb);
printf("process_command: %i words, module=%s cmd=%s, parameters= %s\n",j,modulename,cmd,(cmdb==NULL)?"":cmdb);
for (i=0; j>=2 && telnetparams.CmdParsers[i].var != NULL && telnetparams.CmdParsers[i].cmd != NULL; i++) {
if ( (strncasecmp(telnetparams.CmdParsers[i].module,modulename,strlen(telnetparams.CmdParsers[i].module)) == 0)) {
@@ -504,13 +544,16 @@ int process_command(char *buf) {
} else {
for (k=0 ; telnetparams.CmdParsers[i].cmd[k].cmdfunc != NULL ; k++) {
if (strncasecmp(cmd, telnetparams.CmdParsers[i].cmd[k].cmdname,sizeof(telnetparams.CmdParsers[i].cmd[k].cmdname)) == 0) {
if (telnetparams.CmdParsers[i].cmd[k].cmdflags & TELNETSRV_CMDFLAG_WEBSRVONLY)
continue;
if (telnetparams.CmdParsers[i].cmd[k].qptr != NULL) {
notifiedFIFO_elt_t *msg =newNotifiedFIFO_elt(sizeof(telnetsrv_qmsg_t),0,NULL,NULL);
telnetsrv_qmsg_t *cmddata=NotifiedFifoData(msg);
cmddata->cmdfunc=(qcmdfunc_t)telnetparams.CmdParsers[i].cmd[k].cmdfunc;
cmddata->prnt=client_printf;
cmddata->debug=telnetparams.telnetdbg;
cmddata->cmdbuff=strdup(cmdb);
if (cmdb != NULL)
cmddata->cmdbuff=strdup(cmdb);
pushNotifiedFIFO(telnetparams.CmdParsers[i].cmd[k].qptr, msg);
} else {
telnetparams.CmdParsers[i].cmd[k].cmdfunc(cmdb, telnetparams.telnetdbg, client_printf);
@@ -901,3 +944,7 @@ int telnetsrv_getfarray(loader_shlibfunc_t **farray) {
(*farray)[1].fptr=(int (*)(void) )poll_telnetcmdq;
return ( 2);
}
/* for webserver interface, needs access to some telnet server paramaters */
telnetsrv_params_t *get_telnetsrv_params(void) {
return &telnetparams;
}

View File

@@ -48,19 +48,52 @@
#define CMDSTATUS_VARNOTFOUND 3
#define CMDSTATUS_NOTFOUND 4
/* definitions to store 2 dim table, used to store command results before */
/* displaying them either on console or web page */
#define TELNET_MAXLINE_NUM 100
#define TELNET_MAXCOL_NUM 5
typedef struct col {
char coltitle[TELNET_CMD_MAXSIZE];
unsigned int coltype;
} acol_t;
typedef struct line {
char *val[TELNET_MAXCOL_NUM];
} aline_t;
typedef struct webdatadef {
char tblname[TELNET_HELPSTR_SIZE];
int numlines;
int numcols;
acol_t columns[TELNET_MAXCOL_NUM];
aline_t lines[TELNET_MAXLINE_NUM];
} webdatadef_t;
/*----------------------------------------------------------------------------*/
/* structure to be used when adding a module to the telnet server */
/* This is the second parameter of the add_telnetcmd function, which can be used */
/* to add a set of new command to the telnet server shell */
typedef void(*telnet_printfunc_t)(const char* format, ...);
typedef int(*cmdfunc_t)(char*, int, telnet_printfunc_t prnt);
typedef int(*webfunc_t)(char *cmdbuff, int debug, telnet_printfunc_t prnt, ... );
typedef int(*webfunc_getdata_t)(char *cmdbuff, int debug, void *data);
typedef int(*qcmdfunc_t)(char*, int, telnet_printfunc_t prnt,void *arg);
#define TELNETSRV_CMDFLAG_PUSHINTPOOLQ (1<<0) // ask the telnet server to push the command in a thread pool queue
#define TELNETSRV_CMDFLAG_PUSHINTPOOLQ (1<<0) // ask the telnet server to push the command in a thread pool queue
#define TELNETSRV_CMDFLAG_GETWEBDATA (1<<1) // When called from web server, use the getdata variant of the function
#define TELNETSRV_CMDFLAG_TELNETONLY (1<<2) // Only for telnet client connections
#define TELNETSRV_CMDFLAG_WEBSRVONLY (1<<3) // Only for web server connections
#define TELNETSRV_CMDFLAG_CONFEXEC (1<<4) // Ask for confirm before exec
#define TELNETSRV_CMDFLAG_GETWEBTBLDATA (1<<8) // When called from web server, use the get table data variant of the function
#define TELNETSRV_CMDFLAG_PRINTWEBTBLDATA (1<<9) // Print as a single column table
typedef struct cmddef {
char cmdname[TELNET_CMD_MAXSIZE];
char helpstr[TELNET_HELPSTR_SIZE];
cmdfunc_t cmdfunc;
cmdfunc_t cmdfunc;
union {
webfunc_t webfunc;
webfunc_getdata_t webfunc_getdata;
};
unsigned int cmdflags;
void *qptr;
} telnetshell_cmddef_t;
@@ -78,6 +111,8 @@ typedef struct telnetsrv_qmsg {
/*structure to be used when adding a module to the telnet server */
/* This is the first parameter of the add_telnetcmd function, which can be used */
/* to add a set of new variables which can be got/set from the telnet server shell */
/* var type: bits 0-3, type value (1 to 15)
* type modifier, bits 4-31 */
#define TELNET_VARTYPE_INT32 1
#define TELNET_VARTYPE_INT16 2
#define TELNET_VARTYPE_INT64 3
@@ -85,10 +120,15 @@ typedef struct telnetsrv_qmsg {
#define TELNET_VARTYPE_DOUBLE 5
#define TELNET_VARTYPE_INT8 6
#define TELNET_VARTYPE_UINT 7
#define TELNET_CHECKVAL_RDONLY 16
#define TELNET_CHECKVAL_BOOL 32
typedef struct variabledef {
char varname[TELNET_CMD_MAXSIZE];
char vartype;
char checkval;
void *varvalptr;
} telnetshell_vardef_t;
@@ -155,4 +195,9 @@ void set_sched(pthread_t tid, int pid,int priority);
void set_affinity(pthread_t tid, int pid, int coreid);
extern int get_phybsize(void);
#endif
#ifdef WEBSERVERCODE
extern telnetsrv_params_t *get_telnetsrv_params(void);
extern char *telnet_getvarvalue(telnetshell_vardef_t *var, int varindex);
extern int telnet_setvarvalue(telnetshell_vardef_t *var,char *strval, telnet_printfunc_t prnt ) ;
#endif
#endif

View File

@@ -30,6 +30,6 @@ endforeach()
install(TARGETS telnetsrv DESTINATION bin)
if (EXISTS "${OPENAIR_BUILD_DIR}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_BUILD_DIR}/ran_build/build")
install(TARGETS telnetsrv DESTINATION ${OPENAIR_BUILD_DIR}/ran_build/build)
endif (EXISTS "${OPENAIR_BUILD_DIR}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_BUILD_DIR}/ran_build/build")
if (EXISTS "${OPENAIR_CMAKE}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_CMAKE}/ran_build/build")
install(TARGETS telnetsrv DESTINATION ${OPENAIR_CMAKE}/ran_build/build)
endif (EXISTS "${OPENAIR_CMAKE}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_CMAKE}/ran_build/build")

View File

@@ -110,9 +110,8 @@ int dump_phyvars(char *buf, int debug, telnet_printfunc_t prnt) {
telnetshell_cmddef_t phy_cmdarray[] = {
{"disp","[phycnt,uedump,uestat UE<x>]", dump_phyvars},
{"","",NULL},
{"disp","[phycnt,uedump,uestat UE<x>]", dump_phyvars,{NULL},0,NULL},
{"","",NULL,{NULL},0,NULL},
};

View File

@@ -42,24 +42,29 @@
int loader_show_cmd(char *buff, int debug, telnet_printfunc_t prnt);
telnetshell_cmddef_t loader_cmdarray[] = {
{"show","[params,modules]",loader_show_cmd},
{"","",NULL},
{"show","[params,modules]",loader_show_cmd,{(webfunc_t)loader_show_cmd},0,NULL},
{"","",NULL,{NULL},0,NULL},
};
/*-------------------------------------------------------------------------------------*/
int loader_show_cmd(char *buff, int debug, telnet_printfunc_t prnt)
{
if (buff == NULL) {
prnt("ERROR wrong loader SHOW command...\n");
return 0;
}
if (debug > 0)
prnt( "loader_show_cmd received %s\n",buff);
if (strcasestr(buff,"params") != NULL) {
prnt( "loader parameters:\n");
prnt( " Main executable build version: \"%s\"\n", loader_data.mainexec_buildversion);
prnt( " Default shared lib path: \"%s\"\n", loader_data.shlibpath);
prnt( " Max number of shared lib : %i\n", loader_data.maxshlibs);
}
else if (strcasestr(buff,"modules") != NULL) {
else if (strcasestr(buff,"modules") != NULL || buff[0]==0 || strcasestr(buff,"show") != NULL) {
prnt( "%i shared lib have been dynamicaly loaded by the oai loader\n", loader_data.numshlibs);
for (int i=0 ; i<loader_data.numshlibs ; i++) {
prnt( " Module %i: %s\n", i,loader_data.shlibs[i].name);

View File

@@ -39,11 +39,11 @@
telnetshell_vardef_t loader_globalvardef[] = {
{"mainversion",TELNET_VARTYPE_STRING,&(loader_data.mainexec_buildversion)},
{"defpath",TELNET_VARTYPE_STRING,&(loader_data.shlibpath)},
{"maxshlibs",TELNET_VARTYPE_INT32,&(loader_data.maxshlibs)},
{"numshlibs",TELNET_VARTYPE_INT32,&(loader_data.numshlibs)},
{"",0,NULL}
{"mainversion",TELNET_VARTYPE_STRING,TELNET_CHECKVAL_RDONLY, &(loader_data.mainexec_buildversion)},
{"defpath",TELNET_VARTYPE_STRING,TELNET_CHECKVAL_RDONLY,&(loader_data.shlibpath)},
{"maxshlibs",TELNET_VARTYPE_INT32,TELNET_CHECKVAL_RDONLY,&(loader_data.maxshlibs)},
{"numshlibs",TELNET_VARTYPE_INT32,TELNET_CHECKVAL_RDONLY,&(loader_data.numshlibs)},
{"",0,0,NULL}
};
telnetshell_vardef_t *loader_modulesvardef;

View File

@@ -92,7 +92,7 @@ telnetshell_cmddef_t measur_cmdarray[] = {
};
telnetshell_vardef_t measur_vardef[] = {
{"",0,NULL}
{"",0,0,NULL}
};
/* function to be implemented in any telnetsrv_xxx_measurements.c sources
to allow common measurment code to access measurments data */

View File

@@ -53,7 +53,7 @@ telnetshell_vardef_t phy_vardef[] = {
//{"txshift",TELNET_VARTYPE_INT32,NULL},
//{"rachemin",TELNET_VARTYPE_INT32,NULL},
//{"rachdmax",TELNET_VARTYPE_INT32,NULL},
{"",0,NULL}
{"",0,0,NULL}
};
#else

View File

@@ -139,7 +139,9 @@ char toksep[2];
prnt("%s\n",prntline);
} /*decode_procstat */
void read_statfile(char *fname,int debug, telnet_printfunc_t prnt)
void read_statfile(char *fname,int debug, telnet_printfunc_t prnt, void *data)
{
FILE *procfile;
char arecord[1024];
@@ -169,9 +171,9 @@ struct dirent *entry;
prnt(" id name state USRmod KRNmod prio nice vsize proc pol \n\n");
prnt("\n id name state USRmod KRNmod prio nice vsize proc pol \n\n");
snprintf(aname, sizeof(aname), "/proc/%d/stat", getpid());
read_statfile(aname,debug,prnt);
read_statfile(aname,debug,prnt,NULL);
prnt("\n");
snprintf(aname, sizeof(aname), "/proc/%d/task", getpid());
proc_dir = opendir(aname);
@@ -183,18 +185,86 @@ struct dirent *entry;
while ((entry = readdir(proc_dir)) != NULL)
{
if(entry->d_name[0] == '.')
continue;
snprintf(aname, sizeof(aname), "/proc/%d/task/%.*s/stat", getpid(),(int)(sizeof(aname)-24),entry->d_name);
read_statfile(aname,debug,prnt);
if(entry->d_name[0] != '.')
{
snprintf(aname, sizeof(aname), "/proc/%d/task/%.*s/stat", getpid(),(int)(sizeof(aname)-24),entry->d_name);
read_statfile(aname,debug,prnt,NULL);
}
} /* while entry != NULL */
closedir(proc_dir);
} /* print_threads */
int proccmd_websrv_getdata(char *cmdbuff, int debug, void *data) {
if (strcasestr(cmdbuff,"loglvl") != NULL) {
webdatadef_t *logsdata = ( webdatadef_t *) data;
logsdata->numcols=4;
logsdata->numlines=0;
snprintf(logsdata->columns[0].coltitle,TELNET_CMD_MAXSIZE,"component");
logsdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(logsdata->columns[1].coltitle,TELNET_CMD_MAXSIZE,"level");
logsdata->columns[1].coltype = TELNET_VARTYPE_STRING;
snprintf(logsdata->columns[2].coltitle,TELNET_CMD_MAXSIZE,"enabled");
logsdata->columns[2].coltype = TELNET_CHECKVAL_BOOL;
snprintf(logsdata->columns[3].coltitle,TELNET_CMD_MAXSIZE,"output");
logsdata->columns[3].coltype = TELNET_VARTYPE_STRING;
for (int i=MIN_LOG_COMPONENTS; i < MAX_LOG_COMPONENTS; i++) {
if (g_log->log_component[i].name != NULL) {
logsdata->numlines++;
logsdata->lines[i].val[0]= (char *)(g_log->log_component[i].name);
logsdata->lines[i].val[1]=map_int_to_str(log_level_names,(g_log->log_component[i].level>=0)?g_log->log_component[i].level:g_log->log_component[i].savedlevel);
logsdata->lines[i].val[2]=(g_log->log_component[i].level>=0)?"true":"false";
logsdata->lines[i].val[3]=(g_log->log_component[i].filelog>0)?g_log->log_component[i].filelog_name:"stdout";
}
}
}
if (strcasestr(cmdbuff,"dbgopt") != NULL) {
webdatadef_t *logsdata = ( webdatadef_t *) data;
logsdata->numcols=3;
logsdata->numlines=0;
snprintf(logsdata->columns[0].coltitle,TELNET_CMD_MAXSIZE,"module");
logsdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(logsdata->columns[1].coltitle,TELNET_CMD_MAXSIZE,"debug");
logsdata->columns[1].coltype = TELNET_CHECKVAL_BOOL;
snprintf(logsdata->columns[2].coltitle,TELNET_CMD_MAXSIZE,"dump");
logsdata->columns[2].coltype = TELNET_CHECKVAL_BOOL;
for (int i=0; log_maskmap[i].name != NULL ; i++) {
logsdata->numlines++;
logsdata->lines[i].val[0]= log_maskmap[i].name;
logsdata->lines[i].val[1]= (g_log->debug_mask & log_maskmap[i].value)?"true":"false";
logsdata->lines[i].val[2]=(g_log->dump_mask & log_maskmap[i].value)?"true":"false";
}
}
if (strcasestr(cmdbuff,"logopt") != NULL) {
webdatadef_t *logsdata = ( webdatadef_t *) data;
logsdata->numcols=2;
logsdata->numlines=0;
snprintf(logsdata->columns[0].coltitle,TELNET_CMD_MAXSIZE,"option");
logsdata->columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
snprintf(logsdata->columns[1].coltitle,TELNET_CMD_MAXSIZE,"enabled");
logsdata->columns[1].coltype = TELNET_CHECKVAL_BOOL;
for (int i=0; log_options[i].name != NULL; i++) {
logsdata->numlines++;
logsdata->lines[i].val[0]=log_options[i].name;
logsdata->lines[i].val[1]=(g_log->flag & log_options[i].value)?"true":"false";
}
}
return 0;
}
int proccmd_show(char *buf, int debug, telnet_printfunc_t prnt)
{
if (buf == NULL) {
prnt("ERROR wrong softmodem SHOW command...\n");
return 0;
}
if (debug > 0)
prnt(" proccmd_show received %s\n",buf);
if (strcasestr(buf,"thread") != NULL) {
@@ -270,6 +340,10 @@ int bv1,bv2;
int res;
char sv1[64];
if (buf == NULL) {
prnt("ERROR wrong thread command...\n");
return 0;
}
bv1=0;
bv2=0;
sv1[0]=0;
@@ -284,7 +358,7 @@ char sv1[64];
prnt(" proccmd_thread: %i params = %i,%s,%i\n",res,bv1,sv1,bv2);
if(res != 3)
{
prnt("softmodem thread needs 3 params, %i received\n",res);
print_threads(buf, debug, prnt);
return 0;
}
@@ -307,7 +381,7 @@ int proccmd_exit(char *buf, int debug, telnet_printfunc_t prnt)
{
if (debug > 0)
prnt("process module received %s\n",buf);
end_configmodule();
exit_fun("telnet server received exit command\n");
return 0;
}

View File

@@ -45,8 +45,9 @@ extern int proccmd_show(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_thread(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_exit(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_log(char *buf, int debug, telnet_printfunc_t prnt);
extern int proccmd_websrv_getdata(char *cmdbuff, int debug, void *data);
telnetshell_vardef_t proc_vardef[] = {
{"",0,NULL}
{"",0,0,NULL}
};
#define PROCCMD_LOG_HELP_STRING " log sub commands: \n\
show: display current log configuration \n\
@@ -73,10 +74,14 @@ telnetshell_vardef_t proc_vardef[] = {
telnetshell_cmddef_t proc_cmdarray[] = {
{"show","loglvl|thread|config", proccmd_show},
{"log","(enter help for details)", proccmd_log},
{"thread","(enter help for details)", proccmd_thread},
{"exit","", proccmd_exit},
{"show","loglvl|thread|config", proccmd_show,{(webfunc_t)proccmd_websrv_getdata},TELNETSRV_CMDFLAG_TELNETONLY,NULL},
{"log","(enter help for details)", proccmd_log, {NULL},TELNETSRV_CMDFLAG_TELNETONLY,NULL},
{"show loglvl","", proccmd_log,{(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY|TELNETSRV_CMDFLAG_GETWEBTBLDATA,NULL},
{"show logopt","", proccmd_log,{(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY|TELNETSRV_CMDFLAG_GETWEBTBLDATA,NULL},
{"show dbgopt","", proccmd_log,{(webfunc_t)proccmd_websrv_getdata}, TELNETSRV_CMDFLAG_WEBSRVONLY|TELNETSRV_CMDFLAG_GETWEBTBLDATA,NULL},
{"show config","", proccmd_show,{(webfunc_t)proccmd_show}, TELNETSRV_CMDFLAG_WEBSRVONLY,NULL},
{"show thread","", proccmd_thread,{(webfunc_t)proccmd_show},TELNETSRV_CMDFLAG_WEBSRVONLY|TELNETSRV_CMDFLAG_PRINTWEBTBLDATA,NULL},
{"exit","", proccmd_exit,{NULL},TELNETSRV_CMDFLAG_CONFEXEC,NULL},
{"","",NULL},
};
#else

View File

@@ -0,0 +1,7 @@
The oai embedded telnet server is an optional monitoring and debugging tool. It provides a simple Command Line Interface to the oai softmem. New commands can easily be added by developers to the telnet server.
* [Using the telnet server](telnetusage.md)
* [Adding commands to the oai telnet server](telnetaddcmd.md)
* [telnet server architecture ](telnetarch.md)
[oai Wikis home](https://gitlab.eurecom.fr/oai/openairinterface5g/wikis/home)

View File

@@ -0,0 +1,31 @@
# telnet server principles
The oai telnet server is implemented in shared libraries to be loaded by the [oai shared library loader](loader). `libtelnetsrv.so ` contains the code common to all oai softmodem executables, where `libtelnetsrv_<app>.so` contains the code specific to the executable identified by `app`.
| `<app>` | executable |
| :-----: | :---------------: |
| enb | `lte-softmodem` |
| gnb | `nr-softmodem` |
| 4Gue | `lte-uesoftmodem` |
| 5Gue | `nr-uesoftmodem` |
The implementation includes a `telnetsrv_autoinit` function which is automatically called at load time, starts the telnet server and registers a first set of commands, which are delivered with the server (telnet, softmodem, loader). it also uses the `get_softmodem_function` call to retrieve the running executable id and possibly loads the corresponding `libtelnetsrv_<app>.so`library.
Currently the telnet server only supports one user connection. The same dedicated thread is used to wait for a user connection and process the input received from this connection.
The telnet server provides an API which can be used by any oai component to add new CLI commands to the server. A pre-defined command can be used to get or set a list of variables.
# telnet server source files
telnet server source files are located in [common/utils/telnetsrv](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/telnetsrv)
1. [telnetsrv.c](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/telnetsrv/telnetsrv.c) contains the telnet server implementation, including the implementation of the `telnet` CLI command. This implementation is compatible with all softmodem executables and is in charge of loading any additional `libtelnetsrv_<app> .so` containing code specific to the running executables.
1. [telnetsrv.h](https://gitlab.eurecom.fr/oai/openairinterface5g/tree/develop/common/utils/telnetsrv/telnetsrv.h) is the telnet server include file containing both private and public data type definitions. It also contains API prototypes for functions that are used to register a new command in the server.
1. `telnetsrv_<XXX\>.c`: implementation of \<XXX\> CLI command which are delivered with the telnet server and are common to all softmodem executables.
1. `telnetsrv_<XXX\>.h`: include file for the implementation of XXX CLI command. Usually included only in the corresponding `.c`file
1. `telnetsrv_<app>_<XXX>.c`: implementation of \<XXX\> CLI command specific to the executable identified by \<app\>.These sources are used to create `libtelnetsrv_<app>.so` at build time.
1. [telnetsrv_CMakeLists.txt](https://gitlab.eurecom.fr/oai/openairinterface5g/blob/develop/common/utils/telnetsrv/telnetsrv_CMakeLists.txt): CMakelists file containing the cmake instructions to build the telnet server. this file is included in the [global oai CMakelists](https://gitlab.eurecom.fr/oai/openairinterface5g/blob/develop/cmake_targets/CMakeLists.txt).
[oai telnet server home](telnetsrv.md)

View File

@@ -0,0 +1,40 @@
# starting the softmodem with the telnet server
By default the embedded telnet server, which is implemented in a shared library, is not built. It can be built after compiling the softmodem executable using the `build_oai` script:
```bash
cd \<oai repository\>/openairinterface5g
source oaienv
cd cmake_targets
./build_oai --build-lib telnetsrv
```
This will create the `libtelnetsrv.so` and `libtelnetsrv_<app> file in the `targets/bin` and `cmake_targets/ran_build/build` sub directories of the oai repository. <app> can be "enb", "gnb", "4GUE" or "5GUE", each library containing functions specific to a given executable.
When starting the softmodem, you must specify the **_\-\-telnetsrv_** option to load and start the telnet server. The telnet server is loaded via the [oai shared library loader](loader).
# using the Command Line Interface
By default the telnet server listen on all the ip addresses configured on the system and on port 9090. This behavior can be changed using the `listenaddr` and `listenport` parameters.
The telnet server includes a basic help, listing available commands and some commands also provide a specific detailed help sub-command.
Below are examples of telnet sessions:
* [getting help](telnethelp.md)
* [using the history](telnethist.md)
* [using the get and set commands](telnetgetset.md)
* [using the loop command](telnetloop.md)
* [loader command](telnetloader.md)
* [log command](telnetlog.md)
* [measur command](telnetmeasur.md)
# telnet server parameters
The telnet server is using the [oai configuration module](Config/Rtusage). Telnet parameters must be specified in the `telnetsrv` section. Some parameters can be modified via the telnet telnet server command, as specified in the last column of the following table.
| name | type | default | description | dynamic |
|:---:|:---:|:---:|:----|:----:|
| `listenaddr` | `ipV4 address, ascii format` | "0.0.0.0" | local address the server is listening on| N |
| `listenport` | `integer` | 9090 | port number the server is listening on | N |
| `loopcount` | `integer` | 10 | number of iterations for the loop command | Y |
| `loopdelay` | `integer` | 5000 | delay (in ms) between 2 loop command iterations | Y |
| `histfile` | `character string` | "oaitelnet.history" | file used for command history persistency | Y |
| `histfsize` | `integer` | 50 | maximum number of commands saved in the history | Y |
[oai telnet server home](telnetsrv.md)

View File

@@ -0,0 +1,17 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.

View File

@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@@ -0,0 +1,50 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"common/utils/websrv/frontend/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}

49
common/utils/websrv/frontend/.gitignore vendored Normal file
View File

@@ -0,0 +1,49 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
/artifacts
/builds
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
config.toml

View File

@@ -0,0 +1,31 @@
stages:
- install
- build
variables:
TRION_CLI_VERSION: 12.2.4
#
#jobs
#
install:
stage: install
image: trion/ng-cli:${TRION_CLI_VERSION}
script:
- npm ci
only:
changes:
- package-lock.json
build:
stage: build
extends: .install
image: trion/ng-cli-karma:${TRION_CLI_VERSION}
script:
- npm run lint
- npm run build
artifacts:
expire_in: 1 day
paths:
- dist/softmodem-ngx

View File

@@ -0,0 +1 @@
# SoftModemNgx

View File

@@ -0,0 +1,107 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"cli": {
"analytics": false,
"defaultCollection": "@angular-eslint/schematics"
},
"version": 1,
"newProjectRoot": "projects",
"projects": {
"softmodemngx": {
"projectType": "application",
"schematics": {
"@schematics/angular:application": {
"strict": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/softmodem-ngx",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
"src/styles.css"
],
"scripts": [],
"allowedCommonJsDependencies": [
"@firebase/auth",
"firebase"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "softmodemngx:build:production"
},
"development": {
"browserTarget": "softmodemngx:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "softmodemngx:build"
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}
},
"defaultProject": "softmodemngx"
}

View File

@@ -0,0 +1,40 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: ['--headless', '--no-sandbox']
}
},
directConnect: true,
SELENIUM_PROMISE_MANAGER: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function () { }
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};

View File

@@ -0,0 +1,37 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function () { }
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};

View File

@@ -0,0 +1,23 @@
import { browser, logging } from 'protractor';
import { AppPage } from './app.po';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
// it('should display welcome message', async () => {
// await page.navigateTo();
// expect(await page.getTitleText()).toEqual('example app is running!');
// });
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
async navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl);
}
async getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText();
}
}

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": ["jasmine", "node"]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
{
"name": "softmodemngx",
"version": "2.0.0",
"scripts": {
"lint": "ng lint",
"build": "ng build",
"dev": "ng serve --configuration=development",
"prod": "ng serve --configuration=production"
},
"private": true,
"dependencies": {
"@angular/animations": "~12.2.4",
"@angular/cdk": "~12.2.4",
"@angular/common": "~12.2.4",
"@angular/compiler": "~12.2.4",
"@angular/core": "~12.2.4",
"@angular/flex-layout": "^12.0.0-beta.34",
"@angular/forms": "~12.2.4",
"@angular/material": "^12.2.4",
"@angular/platform-browser": "~12.2.4",
"@angular/platform-browser-dynamic": "~12.2.4",
"@angular/router": "~12.2.4",
"moment": "^2.29.1",
"rxjs": "^6.6.6",
"tslib": "^2.0.0",
"zone.js": "^0.11.4"
},
"devDependencies": {
"@angular-devkit/architect": "^0.1200.0",
"@angular-devkit/build-angular": "~12.2.4",
"@angular-eslint/builder": "12.1.0",
"@angular-eslint/eslint-plugin": "12.1.0",
"@angular-eslint/eslint-plugin-template": "12.1.0",
"@angular-eslint/schematics": "12.5.0",
"@angular-eslint/template-parser": "12.1.0",
"@angular/cli": "~12.2.4",
"@angular/compiler-cli": "~12.2.4",
"@angular/language-service": "^12.2.4",
"@types/node": "^12.20.23",
"@typescript-eslint/eslint-plugin": "4.23.0",
"@typescript-eslint/parser": "4.23.0",
"eslint": "^7.26.0",
"fs-extra": "^10.0.0",
"fuzzy": "^0.1.3",
"husky": "^5.1.3",
"inquirer": "^6.2.2",
"inquirer-autocomplete-prompt": "^1.0.1",
"jasmine-core": "~3.7.1",
"jasmine-spec-reporter": "~5.0.0",
"jsonc-parser": "^3.0.0",
"open": "^7.0.3",
"ts-node": "^8.10.2",
"tslint-angular": "^3.0.3",
"typescript": "~4.2.4"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged && ng lint && ng test",
"pre-push": "ng build --aot true"
}
}
}

View File

@@ -0,0 +1,85 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';
import { Param, ParamFC } from '../controls/param.control';
export interface IVariable {
name: string;
value: string;
type: IArgType;
modifiable: boolean; //set command ?
}
export enum ILogLvl {
error = "error",
warn = "warn",
analysis = "analysis",
info = "info",
debug = "debug",
trace = "trace"
}
export enum ILogOutput {
stdout = "stdout",
telnet = "telnet",
web = "web",
file = "/tmp/<component>.log",
}
export enum IArgType {
boolean = "boolean",
list = "list",
range = "range",
number = "number",
string = "string"
}
export interface ICommand {
name: string;
confirm?: string;
}
export interface IColumn { //should use IVariable ?
name: string;
type: IArgType;
modifiable: boolean; //set command ?
}
export type IRow = string[]
export interface ITable {
columns: IColumn[];
rows: IRow[];
}
export interface IResp {
display: string[],
table?: ITable
}
const route = '/oaisoftmodem';
@Injectable({
providedIn: 'root',
})
export class CommandsApi {
constructor(private httpClient: HttpClient) { }
public readVariables$ = (moduleName?: string) => this.httpClient.get<IVariable[]>(environment.backend + route + '/' + (moduleName ? ('/' + moduleName) : "") + '/variables/');
public readCommands$ = (moduleName?: string) => this.httpClient.get<ICommand[]>(environment.backend + route + '/' + (moduleName ? ('/' + moduleName) : "") + '/commands/');
public runCommand$ = (command: ICommand, moduleName: string) => this.httpClient.post<IResp>(environment.backend + route + '/' + moduleName + '/commands/', command);
public setVariable$ = (variable: IVariable, moduleName?: string) => this.httpClient.post<IResp>(environment.backend + route + (moduleName ? ('/' + moduleName) : "") + '/variables/', variable);
public setParam$ = (param: Param) => this.httpClient.post<IResp>(environment.backend + route + '/' + param.moduleName + '/set',
{
cmdName: param.cmdName,
colName: param.col.name,
rawIndex: param.rawIndex,
value: param.value
}
);
}

View File

@@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CommandsComponent } from './components/commands/commands.component';
const routes: Routes = [
{ path: '', redirectTo: '/commands', pathMatch: 'full' },
{ path: 'commands', component: CommandsComponent },
{ path: '**', redirectTo: '' },
];
@NgModule({
imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
exports: [RouterModule],
})
export class AppRoutingModule { }

View File

@@ -0,0 +1 @@
<app-commands></app-commands>

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = 'softmodem-ngx';
}

View File

@@ -0,0 +1,78 @@
import { ClipboardModule } from '@angular/cdk/clipboard/';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatChipsModule } from '@angular/material/chips';
import { MatNativeDateModule } from '@angular/material/core';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTableModule } from '@angular/material/table';
import { MatToolbarModule } from '@angular/material/toolbar';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CommandsApi } from './api/commands.api';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CommandsComponent } from './components/commands/commands.component';
import { ConfirmDialogComponent } from './components/confirm/confirm.component';
import { ErrorDialogComponent } from './components/error-dialog/error-dialog.component';
import { InterceptorProviders } from './interceptors/interceptors';
import { LoadingService } from './services/loading.service';
@NgModule({
declarations: [
AppComponent,
ErrorDialogComponent,
CommandsComponent,
ConfirmDialogComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
HttpClientModule,
MatFormFieldModule,
MatInputModule,
ReactiveFormsModule,
// MatChipsModule,
MatProgressSpinnerModule,
MatButtonModule,
FlexLayoutModule,
MatToolbarModule,
// MatListModule,
MatTableModule,
MatSelectModule,
MatDialogModule,
MatSnackBarModule,
MatSlideToggleModule,
MatGridListModule,
MatCardModule,
// MatMenuModule,
],
providers: [
// services
LoadingService,
// api
CommandsApi,
// interceptors
InterceptorProviders,
],
bootstrap: [AppComponent],
entryComponents: [ErrorDialogComponent],
})
export class AppModule { }

View File

@@ -0,0 +1,34 @@
mat-form-field {
width: 50%;
}
.status {
color: green;
font-weight: 800;
}
.ng-invalid .status {
color: red;
}
.grid-container {
margin: 20px;
}
.dashboard-card {
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
}
.more-button {
position: absolute;
top: 5px;
right: 10px;
}
.dashboard-card-content {
text-align: start;
}

View File

@@ -0,0 +1,121 @@
<div class="grid-container">
<mat-grid-list cols="2" rowHeight="200px">
<mat-grid-tile [colspan]="1" [rowspan]="2">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>Softmodem variables</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div *ngIf="vars$ | async as vars">
<div *ngFor="let variable of vars">
<mat-form-field>
<mat-label>{{ variable.nameFC.value }}</mat-label>
<input input matInput [formControl]="variable.valueFC" />
</mat-form-field>
<button mat-raised-button color="primary" [disabled]="!variable.modifiableFC.value"
(click)="onVarSubmit(variable)">
set
</button>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile [colspan]="1" [rowspan]="2">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>Softmodem commands</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div *ngIf="modules$ | async as modules" fxLaypout="row">
<mat-form-field>
<mat-label>Module</mat-label>
<mat-select (selectionChange)="onModuleSelect($event.value)">
<mat-option *ngFor="let module of modules" [value]="module"> {{ module.nameFC.value }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div *ngIf="modulecmds$ | async as cmds">
<div *ngIf="cmds.length">
<div *ngFor="let cmd of cmds" fxLayoutGap="10px" fxLayoutAlign="start start">
<button mat-raised-button color="primary" (click)="onCmdSubmit(cmd)"> {{cmd.nameFC.value}} </button>
</div>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile *ngIf="modulevars$ | async as modulevars" [colspan]="1" [rowspan]="6">
<mat-card *ngIf="modulevars.length" class="dashboard-card">
<mat-card-header>
<mat-card-title>{{ selectedModule!.nameFC.value }} variables</mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<div *ngFor="let variable of modulevars">
<mat-form-field>
<mat-label>{{ variable.nameFC.value }}</mat-label>
<input input matInput [formControl]="variable.valueFC" />
</mat-form-field>
<button mat-raised-button color="primary" [disabled]="!variable.modifiableFC.value"
(click)="onModuleVarsubmit(variable)">
set
</button>
</div>
</mat-card-content>
</mat-card>
</mat-grid-tile>
<mat-grid-tile *ngIf="selectedCmd" [colspan]="1" [rowspan]="10">
<mat-card class="dashboard-card">
<mat-card-header>
<mat-card-title>{{ selectedModule!.nameFC.value }} {{ selectedCmd.name }} </mat-card-title>
</mat-card-header>
<mat-card-content class="dashboard-card-content">
<mat-table mat-table [dataSource]="rows$" multiTemplateDataRows class="mat-elevation-z8">
<!-- For anyone who has set the multiTemplateDataRows property of mat-table to true,
you can't use index. Instead you have use either dataIndex or renderIndex.
See https://github.com/angular/material2/issues/12793 -->
<div *ngFor="let col of columns; index as colIndex" matColumnDef="{{col.name}}">
<mat-header-cell *matHeaderCellDef>
<div>
<h4>{{ col.name }}</h4>
</div>
</mat-header-cell>
<mat-cell *matCellDef="let row; dataIndex as rowIndex">
<div *ngIf="col.type === IOptionType.boolean; then toggle else default"> </div>
<!-- <button mat-raised-button color="primary" (click)="onParamSubmit(row)"> set </button> -->
<ng-template #toggle>
<mat-slide-toggle [formControl]="row[colIndex]" [checked]="row[colIndex].value"
[disabled]="!col.modifiable" (change)="onParamSubmit(row)">
</mat-slide-toggle>
</ng-template>
<ng-template #default>
<mat-form-field>
<input matInput [formControl]="row[colIndex]" [disabled]="!col.modifiable" />
</mat-form-field>
</ng-template>
</mat-cell>
</div>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns" (keydown.enter)="onParamSubmit(row)"></mat-row>
</mat-table>
</mat-card-content>
</mat-card>
</mat-grid-tile>
</mat-grid-list>
</div>

View File

@@ -0,0 +1,121 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs/internal/Observable';
import { of } from 'rxjs/internal/observable/of';
import { map } from 'rxjs/internal/operators/map';
import { mergeMap } from 'rxjs/internal/operators/mergeMap';
import { filter } from 'rxjs/operators';
import { CommandsApi, IArgType, IColumn, ICommand } from 'src/app/api/commands.api';
import { CmdCtrl } from 'src/app/controls/cmd.control';
import { Param, ParamFC } from 'src/app/controls/param.control';
import { VarCtrl } from 'src/app/controls/var.control';
import { DialogService } from 'src/app/services/dialog.service';
import { LoadingService } from 'src/app/services/loading.service';
@Component({
selector: 'app-commands',
templateUrl: './commands.component.html',
styleUrls: ['./commands.component.css'],
})
export class CommandsComponent {
IOptionType = IArgType;
vars$: Observable<VarCtrl[]>
modules$: Observable<CmdCtrl[]>
selectedModule?: CmdCtrl
selectedCmd?: ICommand
modulevars$?: Observable<VarCtrl[]>
modulecmds$?: Observable<CmdCtrl[]>
//table columns
displayedColumns: string[] = []
rows$: Observable<ParamFC[][]> = new Observable<ParamFC[][]>()
columns: IColumn[] = []
constructor(
public commandsApi: CommandsApi,
public loadingService: LoadingService,
public dialogService: DialogService
) {
this.vars$ = this.commandsApi.readVariables$().pipe(
map((vars) => vars.map(ivar => new VarCtrl(ivar)))
);
this.modules$ = this.commandsApi.readCommands$().pipe(
map((cmds) => cmds.map(icmd => new CmdCtrl(icmd)))
);
}
onModuleSelect(control: CmdCtrl) {
this.selectedModule = control
this.modulecmds$ = this.commandsApi.readCommands$(`${control.nameFC.value}`).pipe(
map(icmds => icmds.map(icmd => new CmdCtrl(icmd)))
)
this.modulevars$ = this.commandsApi.readVariables$(`${control.nameFC.value}`).pipe(
map(ivars => ivars.map(ivar => new VarCtrl(ivar))),
)
}
onVarSubmit(control: VarCtrl) {
this.commandsApi.setVariable$(control.api()).subscribe();
}
onModuleVarsubmit(control: VarCtrl) {
this.commandsApi.setVariable$(control.api(), `${this.selectedModule!.nameFC.value}`)
.pipe(
map(resp => this.dialogService.openRespDialog(resp))
).subscribe();
}
onCmdSubmit(control: CmdCtrl) {
this.selectedCmd = control.api()
const obs = control.confirm
? this.dialogService.openConfirmDialog(control.confirm).pipe(
filter(confirmed => confirmed)
)
: of(null)
this.rows$ = obs.pipe(
mergeMap(() => this.commandsApi.runCommand$(control.api(), `${this.selectedModule!.nameFC.value}`)),
mergeMap(resp => {
if (resp.display[0]) return this.dialogService.openRespDialog(resp, 'cmd ' + control.nameFC.value + ' response:')
else return of(resp)
}),
map(resp => {
this.columns = resp.table!.columns
this.displayedColumns = this.columns.map(col => col.name)
let rows = [];
for (let i = 0; i < resp.table!.rows.length; i++) {
let row: Param[] = []
for (let j = 0; j < this.columns.length; j++) {
row[j] = new Param(
resp.table!.rows[i][j],
this.columns[j],
j,
this.selectedModule!.nameFC.value,
this.selectedCmd!.name
)
}
rows[i] = row.map(param => new ParamFC(param))
}
return rows
})
);
}
onParamSubmit(row: ParamFC[]) {
row.map(paramFC => this.commandsApi.setParam$(paramFC.api()).subscribe());
}
}

View File

@@ -0,0 +1,5 @@
<h3>{{ data.title }}</h3>
<div fxLayoutGap="10px" mat-dialog-actions fxLayout="row" fxLayoutAlign="center start">
<button mat-button [mat-dialog-close]="true" cdkFocusInitial>Ok</button>
<button mat-button (click)="onNoClick()">Cancel</button>
</div>

View File

@@ -0,0 +1,21 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-confirm',
templateUrl: './confirm.component.html',
styleUrls: ['./confirm.component.css']
})
export class ConfirmDialogComponent {
constructor(
public dialogRef: MatDialogRef<void>,
@Inject(MAT_DIALOG_DATA) public data: any) {
}
onNoClick() {
this.dialogRef.close();
}
}

View File

@@ -0,0 +1,4 @@
<div fxLayout="column" fxLayoutAlign="center center">
<h3>{{ data.title }}</h3>
<pre [innerHTML]="data.body"></pre>
</div>

View File

@@ -0,0 +1,14 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-error-dialog',
templateUrl: './error-dialog.component.html',
styleUrls: ['./error-dialog.component.css'],
})
export class ErrorDialogComponent {
JSON = JSON
title = 'Angular-Interceptor';
constructor(@Inject(MAT_DIALOG_DATA) public data: any) {
}
}

View File

@@ -0,0 +1,36 @@
import { FormControl, FormGroup } from '@angular/forms';
import { ICommand } from '../api/commands.api';
const enum CmdFCN {
name = 'name',
confirm = 'confirm'
}
export class CmdCtrl extends FormGroup {
confirm?: string
constructor(cmd: ICommand) {
super({});
this.addControl(CmdFCN.name, new FormControl(cmd.name));
this.confirm = cmd.confirm
}
api() {
const doc: ICommand = {
name: this.nameFC.value
};
return doc;
}
get nameFC() {
return this.get(CmdFCN.name) as FormControl;
}
set nameFC(fc: FormControl) {
this.setControl(CmdFCN.name, fc);
}
}

View File

@@ -0,0 +1,25 @@
import { FormControl } from '@angular/forms';
import { IArgType, IColumn } from '../api/commands.api';
export class Param {
constructor(
public value: string,
public col: IColumn,
public rawIndex: number,
public moduleName: string,
public cmdName: string) { }
}
export class ParamFC extends FormControl {
constructor(public param: Param) {
super(param.value);
}
api() {
this.param.value = this.value
return this.param
}
}

View File

@@ -0,0 +1,67 @@
import { FormControl, FormGroup } from '@angular/forms';
import { IVariable } from '../api/commands.api';
const enum VariablesFCN {
name = 'name',
value = "value",
type = "type",
modifiable = "modifiable"
}
export class VarCtrl extends FormGroup {
constructor(ivar: IVariable) {
super({});
this.addControl(VariablesFCN.name, new FormControl(ivar.name));
this.addControl(VariablesFCN.value, new FormControl(ivar.value));
this.addControl(VariablesFCN.type, new FormControl(ivar.type));
this.addControl(VariablesFCN.modifiable, new FormControl(ivar.modifiable));
}
api() {
const doc: IVariable = {
name: this.nameFC.value,
value: String(this.valueFC.value), //FIXME
type: this.typeFC.value,
modifiable: this.modifiableFC.value
};
return doc;
}
get nameFC() {
return this.get(VariablesFCN.name) as FormControl;
}
set nameFC(control: FormControl) {
this.setControl(VariablesFCN.name, control);
}
get valueFC() {
return this.get(VariablesFCN.value) as FormControl;
}
set valueFC(control: FormControl) {
this.setControl(VariablesFCN.value, control);
}
get typeFC() {
return this.get(VariablesFCN.type) as FormControl;
}
set typeFC(control: FormControl) {
this.setControl(VariablesFCN.type, control);
}
get modifiableFC() {
return this.get(VariablesFCN.modifiable) as FormControl;
}
set modifiableFC(control: FormControl) {
this.setControl(VariablesFCN.modifiable, control);
}
}

View File

@@ -0,0 +1,55 @@
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { DialogService as DialogService } from '../services/dialog.service';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(public dialogService: DialogService) { }
intercept(request: HttpRequest<unknown>, next: HttpHandler) {
return next.handle(request).pipe(
// tap((event: HttpEvent<any>) => {
// if (event instanceof HttpResponse) {
// switch (event.status) {
// case 200:
// case 201:
// this.log(GREEN, request.method + ' ' + event.status + ' Success');
// this.dialogService.openSnackBar(request.method + ' ' + event.status + ' Success');
// break;
// default:
// break;
// }
// // return throwError(event.body);
// }
// }),
catchError((error: HttpErrorResponse) => {
switch (error.status) {
case 400:
case 403:
case 501:
case 500:
this.log(YELLOW, request.method + ' ' + error.status + ' Error: ' + error.error);
this.dialogService.openErrorDialog(error);
break;
default:
break;
}
return throwError(error.error);
}),
);
}
private log = (color: string, txt: string) => console.log(color, '[API] ' + txt);
}
const RED = '\x1b[31m%s\x1b[0m';
const GREEN = '\x1b[32m%s\x1b[0m';
const YELLOW = '\x1b[33m%s\x1b[0m';
const MAGENTA = '\x1b[35m%s\x1b[0m';
const CYAN = '\x1b[36m%s%s\x1b[0m';

View File

@@ -0,0 +1,8 @@
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { ErrorInterceptor } from './error.interceptor';
import { SpinnerInterceptor } from './spinner.interceptor';
export const InterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: SpinnerInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
];

View File

@@ -0,0 +1,29 @@
import { finalize } from 'rxjs/operators';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { LoadingService } from '../services/loading.service';
@Injectable()
export class SpinnerInterceptor implements HttpInterceptor {
activeRequests = 0;
constructor(private loadingService: LoadingService) { }
intercept(request: HttpRequest<unknown>, next: HttpHandler) {
if (this.activeRequests === 0) {
this.loadingService.startLoading();
}
this.activeRequests++;
return next.handle(request).pipe(
finalize(() => {
this.activeRequests--;
if (this.activeRequests === 0) {
this.loadingService.stopLoading();
}
}),
);
}
}

View File

@@ -0,0 +1,86 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Observable } from 'rxjs/internal/Observable';
import { of } from 'rxjs/internal/observable/of';
import { tap } from 'rxjs/internal/operators/tap';
import { IResp } from '../api/commands.api';
import { ConfirmDialogComponent } from '../components/confirm/confirm.component';
import { ErrorDialogComponent } from '../components/error-dialog/error-dialog.component';
@Injectable({
providedIn: 'root',
})
export class DialogService {
public isDialogOpen = false;
constructor(
private _dialog: MatDialog,
private _snackBar: MatSnackBar,
) { }
openErrorDialog(error: HttpErrorResponse): Observable<any> {
if (this.isDialogOpen) {
return of(undefined);
}
this.isDialogOpen = true;
return this._dialog.open(ErrorDialogComponent, {
width: '900px',
data: {
title: error.status + ' Error',
body: error.error,
},
}).afterClosed()
.pipe(tap(() => this.isDialogOpen = false));
}
openRespDialog(resp: IResp, title?: string): Observable<IResp> {
if (this.isDialogOpen || !resp.display.length) {
return of(resp);
}
this.isDialogOpen = true;
const dialogRef = this._dialog.open(ErrorDialogComponent, {
width: '900px',
height: '80%',
data: {
title: title,
body: resp.display!.join("</p><p>")
},
});
dialogRef.afterClosed().subscribe((_) => {
console.log('The dialog was closed');
this.isDialogOpen = false;
});
return of(resp)
}
openSnackBar(title: string): void {
this._snackBar.open(title, undefined, {
duration: 500,
horizontalPosition: 'center',
verticalPosition: 'bottom',
});
}
openConfirmDialog(question: string) {
if (this.isDialogOpen) {
return of(undefined);
}
this.isDialogOpen = true;
return this._dialog.open(ConfirmDialogComponent, {
width: '300px',
data: { title: question }
})
.afterClosed()
.pipe(tap(() => this.isDialogOpen = false));
}
}

View File

@@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class LoadingService {
isLoading$ = new Subject<boolean>();
constructor() { }
startLoading() {
this.isLoading$.next(true);
}
stopLoading() {
this.isLoading$.next(false);
}
}

View File

@@ -0,0 +1,8 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: true,
backend: 'http://localhost:8090' //FIXME
};

View File

@@ -0,0 +1,19 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
backend: 'http://192.168.1.67:8090'
// backend: 'http://10.133.10.152:8090'
// backend: 'http://10.130.163.206:8090'
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>SoftmodemNgx</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
</head>
<body class="mat-typography">
<app-root></app-root>
</body>
</html>

View File

@@ -0,0 +1,13 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.error(err));

View File

@@ -0,0 +1,65 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@@ -0,0 +1,10 @@
/* You can add global styles to this file, and also import other style files */
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: Roboto, 'Helvetica Neue', sans-serif;
}

View File

@@ -0,0 +1,23 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(
path: string,
deep?: boolean,
filter?: RegExp,
): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": ["src/main.ts", "src/polyfills.ts"],
"include": ["src/**/*.d.ts"]
}

View File

@@ -0,0 +1,30 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": ["jasmine"]
},
"files": ["src/test.ts", "src/polyfills.ts"],
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}

View File

@@ -0,0 +1,805 @@
/*
* 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.c
* \brief: implementation of web API
* \author Francois TABURET
* \date 2022
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#include <libgen.h>
#include <jansson.h>
#include <ulfius.h>
#include "common/config/config_userapi.h"
#include "common/utils/LOG/log.h"
#include "common/utils/websrv/websrv.h"
#include "executables/softmodem-common.h"
#define WEBSERVERCODE
#include "common/utils/telnetsrv/telnetsrv.h"
static websrv_params_t websrvparams;
static websrv_printf_t websrv_printf_buff;
paramdef_t websrvoptions[] = {
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* configuration parameters for telnet utility */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
{"listenaddr", "<listen ip address>\n", 0, uptr:&websrvparams.listenaddr, defstrval:"0.0.0.0", TYPE_IPV4ADDR, 0 },
{"listenport", "<local port>\n", 0, uptr:&(websrvparams.listenport), defuintval:8090, TYPE_UINT, 0 },
{"priority", "<scheduling policy (0-99)\n", 0, iptr:&websrvparams.priority, defuintval:0, TYPE_INT, 0 },
{"debug", "<debug level>\n", 0, uptr:&websrvparams.dbglvl, defuintval:0, TYPE_UINT, 0 },
{"url", "<server url>\n", 0, strptr:&websrvparams.url, defstrval:"index.html", TYPE_STRING, 0 },
{"cert", "<cert file>\n", 0, strptr:&websrvparams.certfile, defstrval:NULL, TYPE_STRING, 0 },
{"key", "<key file>\n", 0, strptr:&websrvparams.keyfile, defstrval:NULL, TYPE_STRING, 0 },
};
int websrv_add_endpoint( char **http_method, int num_method, const char * url_prefix,const char * url_format,
int (* callback_function[])(const struct _u_request * request,
struct _u_response * response,
void * user_data),
void * user_data) ;
void register_module_endpoints(cmdparser_t *module) ;
void websrv_printjson(char * label, json_t *jsonobj){
char *jstr = json_dumps(jsonobj,0);
LOG_I(UTIL,"[websrv] %s:%s\n", label, (jstr==NULL)?"??\n":jstr);
free(jstr);
}
void websrv_gettbldata_response(struct _u_response * response,webdatadef_t * wdata) ;
/*-----------------------------------*/
/* build a json body in a response */
void websrv_jbody( struct _u_response * response, json_t *jbody) {
websrv_printjson( (char *)__FUNCTION__ , jbody);
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, 200, 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) {
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);
return 0;
}
/* */
void websrv_printf_start(struct _u_response * response, int buffsize ) {
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;
}
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 ) {
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) ;
} else {
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;
pthread_mutex_unlock(&(websrv_printf_buff.mutex));
}
/* buid a response via a webdatadef_t structure containing one string column */
void websrv_printf_tbl_end(int httpstatus ) {
webdatadef_t pdata;
char *tokctx;
pdata.numcols=1;
pdata.numlines=0;
pdata.columns[0].coltype = TELNET_VARTYPE_STRING | TELNET_CHECKVAL_RDONLY;
pdata.columns[0].coltitle[0]=0;
for ( char *alineptr=strtok_r(websrv_printf_buff.buff,"\n",&tokctx); alineptr != NULL ; alineptr=strtok_r(NULL,"\n",&tokctx) ) {
pdata.lines[pdata.numlines].val[0]=alineptr;
pdata.numlines++;
}
websrv_gettbldata_response(websrv_printf_buff.response,&pdata);
free(websrv_printf_buff.buff);
websrv_printf_buff.buff=NULL;
pthread_mutex_unlock(&(websrv_printf_buff.mutex));
}
/*--------------------------------------------------------------------------------------------------*/
/* format a json response from a result table returned from a call to a telnet server command */
void websrv_getdata_response(struct _u_response * response,webdatadef_t * wdata) {
json_t *jdata = json_array();
for (int i=0; i<wdata->numlines ; i++) {
json_t *kv=json_object();
for (int j=0; j<wdata->numcols; j++) {
json_t *jval;
if(wdata->columns[j].coltype & TELNET_CHECKVAL_BOOL)
jval=json_boolean(wdata->lines[i].val[j]);
else if (wdata->columns[j].coltype & TELNET_VARTYPE_STRING)
jval=json_string(wdata->lines[i].val[j]);
// else if (wdata->columns[j].coltype == TELNET_VARTYPE_DOUBLE)
// jval=json_real((double)(wdata->lines[i].val[j]));
else
jval=json_integer((long)(wdata->lines[i].val[j]));
json_object_set_new(kv, wdata->columns[j].coltitle, jval);
}
json_array_append_new(jdata,kv);
}
json_t *jbody=json_pack("{s:[o],s:o}","display",json_null(),"logs",jdata);
websrv_jbody(response,jbody);
}
/*--------------------------------------------------------------------------------------------------*/
/* format a json response from a result table returned from a call to a telnet server command */
void websrv_gettbldata_response(struct _u_response * response,webdatadef_t * wdata) {
json_t *jcols = json_array();
json_t *jdata = json_array();
char *coltype;
for (int i=0; i<wdata->numcols; i++) {
if(wdata->columns[i].coltype & TELNET_CHECKVAL_BOOL)
coltype="boolean";
else if (wdata->columns[i].coltype & TELNET_VARTYPE_STRING)
coltype="string";
else
coltype="number";
json_t *acol=json_pack("{s:s,s:s,s:b}","name",wdata->columns[i].coltitle,"type",coltype,
"modifiable",( wdata->columns[i].coltype & TELNET_CHECKVAL_RDONLY)?0:1 );
json_array_append_new(jcols,acol);
}
for (int i=0; i<wdata->numlines ; i++) {
json_t *jval;
json_t *jline=json_array();
for (int j=0; j<wdata->numcols; j++) {
if(wdata->columns[j].coltype & TELNET_CHECKVAL_BOOL)
jval=json_string(wdata->lines[i].val[j]);
else if (wdata->columns[j].coltype & TELNET_VARTYPE_STRING)
jval=json_string(wdata->lines[i].val[j]);
// else if (wdata->columns[j].coltype == TELNET_VARTYPE_DOUBLE)
// jval=json_real((double)(wdata->lines[i].val[j]));
else
jval=json_integer((long)(wdata->lines[i].val[j]));
json_array_append_new(jline,jval);
}
json_array_append_new(jdata,jline);
}
json_t *jbody=json_pack("{s:[],s:{s:o,s:o}}","display","table","columns",jcols,"rows",jdata);
websrv_jbody(response,jbody);
}
/*----------------------------------------------------------------------------------------------------------*/
/* callbacks and utility functions to stream a file */
char * websrv_read_file(const char * filename) {
char * buffer = NULL;
long length;
FILE * f = fopen (filename, "rb");
if (f != NULL) {
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
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);
return NULL;
}
buffer[length] = '\0';
}
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";
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;
}
/*------------------------------------------------------------------------------------------------------------------------*/
/* callback processing main ((initial) url (<address>/<websrvparams.url> */
int websrv_callback_get_mainurl(const struct _u_request * request, struct _u_response * response, void * user_data) {
LOG_I(UTIL,"[websrv] Requested file is: %s\n",request->http_url);
FILE *f = websrv_getfile(websrvparams.url,response) ;
if (f == NULL)
return U_CALLBACK_ERROR;
return U_CALLBACK_CONTINUE;
}
/* default callback tries to find a file in the web server repo (path exctracted from <websrvparams.url>) and if found streams it */
int websrv_callback_default (const struct _u_request * request, struct _u_response * response, void * user_data) {
LOG_I(UTIL,"[websrv] Requested file is: %s %s\n",request->http_verb,request->http_url);
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] );
char *tmpurl = strdup(websrvparams.url);
char *srvdir = dirname(tmpurl);
if (srvdir==NULL) {
LOG_E(UTIL,"[websrv] Cannot extract dir name from %s requested file is %s\n",websrvparams.url,request->http_url);
return U_CALLBACK_ERROR;
}
char *fpath = malloc( strlen(request->http_url)+strlen(srvdir)+2);
sprintf(fpath,"%s/%s",srvdir, request->http_url);
FILE *f = websrv_getfile(fpath,response) ;
free(fpath);
free(tmpurl);
if (f == NULL)
return U_CALLBACK_ERROR;
return U_CALLBACK_CONTINUE;
}
/* callback processing url (<address>/oaisoftmodem/:module/variables or <address>/oaisoftmodem/:module/commands) */
int websrv_callback_newmodule(const struct _u_request * request, struct _u_response * response, void * user_data) {
LOG_I(UTIL,"[websrv] callback_newmodule received %s %s\n",request->http_verb,request->http_url);
if (user_data == NULL) {
ulfius_set_string_body_response(response, 500, "Cannot access oai softmodem data");
LOG_E(UTIL,"[websrv] callback_newmodule: user-data is NULL");
return U_CALLBACK_COMPLETE;
}
telnetsrv_params_t *telnetparams = (telnetsrv_params_t *)user_data;
for (int i=0; i<u_map_count(request->map_url) ; i++) {
LOG_I(UTIL,"[websrv] url element %i %s : %s\n",i,u_map_enum_keys(request->map_url)[i], u_map_enum_values(request->map_url)[i] );
if ( strcmp(u_map_enum_keys(request->map_url)[i],"module") == 0) {
for (int j=0; telnetparams->CmdParsers[j].cmd != NULL; j++) {
/* found the module in the telnet server module array, it was likely not registered at init time */
if (strcmp(telnetparams->CmdParsers[j].module, u_map_enum_values(request->map_url)[i]) == 0) {
register_module_endpoints( &(telnetparams->CmdParsers[j]) );
return U_CALLBACK_CONTINUE;
}
} /* for j */
}
} /* for i */
ulfius_set_string_body_response(response, 500, "Request for an unknown module");
return U_CALLBACK_COMPLETE;
}
/* callback processing url (<address>/oaisoftmodem/module/variables or <address>/oaisoftmodem/module/commands), options method */
int websrv_callback_okset_softmodem_cmdvar(const struct _u_request * request, struct _u_response * response, void * user_data) {
LOG_I(UTIL,"[websrv] : callback_okset_softmodem_cmdvar received %s %s\n",request->http_verb,request->http_url);
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] );
int us=ulfius_add_header_to_response(response,"Access-Control-Request-Method" ,"POST");
if (us != 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",us);
}
us=ulfius_add_header_to_response(response,"Access-Control-Allow-Headers", "content-type");
us=ulfius_set_empty_body_response(response, 200);
if (us != U_OK){
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_set_empty_body_response)");
LOG_E(UTIL,"[websrv] cannot set empty body response ulfius error %d \n",us);
}
return U_CALLBACK_COMPLETE;
}
int websrv_callback_set_softmodemvar(const struct _u_request * request, struct _u_response * response, void * user_data) {
LOG_I(UTIL,"[websrv] : callback_set_softmodemvar received %s %s\n",request->http_verb,request->http_url);
websrv_printf_start(response,256);
json_error_t jserr;
json_t* jsbody = ulfius_get_json_body_request (request, &jserr);
int httpstatus=404;
if (jsbody == NULL) {
websrv_printf("cannot find json body in %s %s\n",request->http_url, jserr.text );
httpstatus=400;
} else {
websrv_printjson("callback_set_softmodemvar: ",jsbody);
if (user_data == NULL) {
httpstatus=500;
websrv_printf("%s: NULL user data",request->http_url);
} else {
cmdparser_t * modulestruct = (cmdparser_t *)user_data;
json_t *J=json_object_get(jsbody, "name");
const char *vname=json_string_value(J);
for ( telnetshell_vardef_t *var = modulestruct->var; var->varvalptr!= NULL ;var++) {
if (strncmp(var->varname,vname,TELNET_CMD_MAXSIZE) == 0){
J=json_object_get(jsbody, "value");
if(J!=NULL) {
if (json_is_string(J)) {
const char *vval=json_string_value(J);
websrv_printf("var %s set to ",var->varname);
int st=telnet_setvarvalue(var,(char *)vval, websrv_printf );
if (st>=0) {
httpstatus=200;
} else {
httpstatus=500;
}
} else if (json_is_integer(J)) {
json_int_t i = json_integer_value(J);
switch(var->vartype) {
case TELNET_VARTYPE_INT64:
*(int64_t *)var->varvalptr=(int64_t)i;
break;
case TELNET_VARTYPE_INT32:
*(int32_t *)var->varvalptr=(int32_t)i;
break;
case TELNET_VARTYPE_INT16:
*(int16_t *)var->varvalptr=(int16_t)i;
break;
case TELNET_VARTYPE_INT8:
*(int8_t *)var->varvalptr=(int8_t)i;
break;
case TELNET_VARTYPE_UINT:
*(unsigned int *)var->varvalptr=(unsigned int)i;
break;
default:
httpstatus=500;
websrv_printf(" Cannot set var %s, integer type mismatch\n",vname );
break;
}
} else if (json_is_real(J)) {
double lf = json_real_value(J);
if(var->vartype==TELNET_VARTYPE_DOUBLE) {
*(double *)var->varvalptr = lf;
httpstatus=200;
websrv_printf(" Var %s, set to %g\n",vname, *(double *)var->varvalptr );
} else {
httpstatus=500;
websrv_printf(" Cannot set var %s, real type mismatch\n",vname );
}
}
} else {
httpstatus=500;
websrv_printf("Cannot set var %s, json object is NULL\n",vname );
}
break;
}
}//for
}//user_data
} //sbody
websrv_printf_end(httpstatus);
return U_CALLBACK_COMPLETE;
}
/* callback processing module url (<address>/oaisoftmodem/module/commands), post method */
int websrv_processwebfunc(struct _u_response * response, cmdparser_t * modulestruct ,telnetshell_cmddef_t *cmd) {
LOG_I(UTIL,"[websrv] : executing command %s %s\n",modulestruct->module,cmd->cmdname);
if ( cmd->cmdflags & TELNETSRV_CMDFLAG_GETWEBDATA ) {
webdatadef_t wdata;
memset(&wdata,0,sizeof(wdata));
cmd->webfunc_getdata(cmd->cmdname,websrvparams.dbglvl,(webdatadef_t *)&wdata);
websrv_getdata_response(response,&wdata);
} else if (cmd->cmdflags & TELNETSRV_CMDFLAG_GETWEBTBLDATA) {
webdatadef_t wdata;
memset(&wdata,0,sizeof(wdata));
cmd->webfunc_getdata(cmd->cmdname,websrvparams.dbglvl,(webdatadef_t *)&wdata);
websrv_gettbldata_response(response,&wdata);
} else {
websrv_printf_start(response,16384);
char *sptr=index(cmd->cmdname,' ');
cmd->cmdfunc((sptr==NULL)?cmd->cmdname:sptr,websrvparams.dbglvl,websrv_printf);
if (cmd->cmdflags & TELNETSRV_CMDFLAG_PRINTWEBTBLDATA)
websrv_printf_tbl_end(200);
else
websrv_printf_end(200);
}
return 200;
}
int websrv_callback_exec_softmodemcmd(const struct _u_request * request, struct _u_response * response, void * user_data) {
LOG_I(UTIL,"[websrv] : callback_exec_softmodemcmd received %s %s\n",request->http_verb,request->http_url);
if (user_data == NULL) {
ulfius_set_string_body_response(response, 500, "Cannot access oai softmodem data");
LOG_E(UTIL,"[websrv] callback_exec_softmodemcmd: user-data is NULL");
return U_CALLBACK_COMPLETE;
}
cmdparser_t * modulestruct = (cmdparser_t *)user_data;
json_t* jsbody = ulfius_get_json_body_request (request, NULL);
int httpstatus=404;
char *msg="";
if (jsbody == NULL) {
msg="Unproperly formatted request body";
httpstatus=400;
} else {
websrv_printjson("callback_exec_softmodemcmd: ",jsbody);
json_t *J=json_object_get(jsbody, "name");
const char *vname = json_string_value(J);
if (vname == NULL ) {
msg="No command name in request body";
LOG_E(UTIL,"[websrv] command name not found in body\n");
httpstatus=400;
} else {
httpstatus=501;
msg="Unknown command in request body";
for ( telnetshell_cmddef_t *cmd = modulestruct->cmd ; cmd->cmdfunc != NULL ;cmd++) {
if ( strcmp(cmd->cmdname,vname) == 0 && (( cmd->cmdflags & TELNETSRV_CMDFLAG_TELNETONLY) == 0) ){
httpstatus=websrv_processwebfunc(response,modulestruct,cmd);
break;
}
}//for
}
} //sbody
if (httpstatus >= 300)
ulfius_set_string_body_response(response, httpstatus, msg);
return U_CALLBACK_COMPLETE;
}
/* callback processing module url (<address>/oaisoftmodem/module/variables), get method*/
int websrv_callback_get_softmodemvar(const struct _u_request * request, struct _u_response * response, void * user_data) {
LOG_I(UTIL,"[websrv] : callback_get_softmodemvar received %s %s module %s\n",request->http_verb,request->http_url,
(user_data==NULL)?"NULL":((cmdparser_t *)user_data)->module);
if (user_data == NULL) {
ulfius_set_string_body_response(response, 500, "No variables defined for this module");
return U_CALLBACK_COMPLETE;
}
cmdparser_t * modulestruct = (cmdparser_t *)user_data;
LOG_I(UTIL,"[websrv] received %s variables request\n", modulestruct->module);
json_t *modulevars = json_array();
for(int j=0; modulestruct->var[j].varvalptr != NULL ; j++) {
char*strval=telnet_getvarvalue(modulestruct->var, j);
int modifiable=1;
if (modulestruct->var[j].checkval & TELNET_CHECKVAL_RDONLY)
modifiable=0;
json_t *oneaction;
switch(modulestruct->var[j].vartype) {
case TELNET_VARTYPE_DOUBLE:
oneaction =json_pack("{s:s,s:s,s:g,s:b}","type","number","name",modulestruct->var[j].varname,"value",*(double *)(modulestruct->var[j].varvalptr),"modifiable",modifiable);
case TELNET_VARTYPE_INT32:
case TELNET_VARTYPE_INT16:
case TELNET_VARTYPE_INT8:
case TELNET_VARTYPE_UINT:
oneaction =json_pack("{s:s,s:s,s:i,s:b}","type","number","name",modulestruct->var[j].varname,"value",(int)(*(int *)(modulestruct->var[j].varvalptr)),"modifiable",modifiable);
break;
case TELNET_VARTYPE_INT64:
oneaction =json_pack("{s:s,s:s,s:lli,s:b}","type","number","name",modulestruct->var[j].varname,"value",(int64_t)(*(int64_t *)(modulestruct->var[j].varvalptr)),"modifiable",modifiable);
break;
case TELNET_VARTYPE_STRING:
oneaction =json_pack("{s:s,s:s,s:s,s:b}","type","string","name",modulestruct->var[j].varname,"value",strval,"modifiable",modifiable);
break;
default:
oneaction =json_pack("{s:s,s:s,s:s,s:b}","type","???","name",modulestruct->var[j].varname,"value","???","modifiable",modifiable);
break;
}
if (oneaction==NULL) {
LOG_E(UTIL,"[websrv] cannot encode oneaction %s/%s\n",modulestruct->module,modulestruct->var[j].varname);
} else {
websrv_printjson("oneaction",oneaction);
}
free(strval);
json_array_append(modulevars , oneaction);
}
if (json_array_size(modulevars) == 0) {
LOG_I(UTIL,"[websrv] no defined variables for %s\n",modulestruct->module);
} else {
websrv_printjson("modulevars",modulevars);
}
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, 200, modulevars);
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 U_CALLBACK_COMPLETE;
}
/* callback processing module url (<address>/oaisoftmodem/module/commands)*/
int websrv_callback_get_softmodemcmd(const struct _u_request * request, struct _u_response * response, void * user_data) {
LOG_I(UTIL,"[websrv] : callback_get_softmodemcmd received %s %s module %s\n",request->http_verb,request->http_url,
(user_data==NULL)?"NULL":((cmdparser_t *)user_data)->module);
if (user_data == NULL) {
ulfius_set_string_body_response(response, 500, "No commands defined for this module");
return U_CALLBACK_COMPLETE;
}
cmdparser_t *modulestruct = (cmdparser_t *)user_data;
LOG_I(UTIL,"[websrv] received %s commands request\n", modulestruct->module);
json_t *modulesubcom = json_array();
for(int j=0; modulestruct->cmd[j].cmdfunc != NULL ; j++) {
if(strcasecmp("help",modulestruct->cmd[j].cmdname) == 0 || ( modulestruct->cmd[j].cmdflags & TELNETSRV_CMDFLAG_TELNETONLY ) ) {
continue;
}
json_t *acmd;
if (modulestruct->cmd[j].cmdflags & TELNETSRV_CMDFLAG_CONFEXEC) {
char confstr[256];
snprintf(confstr,sizeof(confstr),"Execute %s ?",modulestruct->cmd[j].cmdname);
acmd =json_pack( "{s:s,s:s}", "name",modulestruct->cmd[j].cmdname,"confirm", confstr);
} else {
acmd =json_pack( "{s:s}", "name",modulestruct->cmd[j].cmdname);
}
json_array_append(modulesubcom , acmd);
}
if (modulesubcom==NULL) {
LOG_E(UTIL,"[websrv] cannot encode modulesubcom response for %s\n",modulestruct->module);
} else {
websrv_printjson("modulesubcom",modulesubcom);
}
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, 200, modulesubcom);
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 U_CALLBACK_COMPLETE;
}
int websrv_callback_get_softmodemmodules(const struct _u_request * request, struct _u_response * response, void * user_data) {
telnetsrv_params_t *telnetparams= get_telnetsrv_params();
json_t *cmdnames = json_array();
for (int i=0; telnetparams->CmdParsers[i].var != NULL && telnetparams->CmdParsers[i].cmd != NULL; i++) {
json_t *acmd =json_pack( "{s:s}", "name",telnetparams->CmdParsers[i].module);
json_array_append(cmdnames, acmd);
}
int us=ulfius_add_header_to_response(response,"content-type" ,"application/json");
if (us != U_OK){
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL,"[websrv] cannot set modules response header type ulfius error %d \n",us);
}
us=ulfius_set_json_body_response(response, 200, cmdnames);
if (us != U_OK){
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_set_json_body_response)");
LOG_E(UTIL,"[websrv] cannot set modules body response ulfius error %d \n",us);
} else {
websrv_printjson("cmdnames",cmdnames);
}
// ulfius_set_string_body_response(response, 200, cfgfile);
return U_CALLBACK_COMPLETE;
}
/* callback processing initial url (<address>/oaisoftmodem)*/
int websrv_callback_get_softmodemstatus(const struct _u_request * request, struct _u_response * response, void * user_data) {
char *cfgfile=CONFIG_GETCONFFILE ;
char *execfunc=get_softmodem_function(NULL);
char *strtype="string";
json_t *modulevars = json_array();
json_t *body1=json_pack("{s:s,s:s,s:s,s:b}","name","config_file", "value",cfgfile, "type",strtype,"modifiable",0);
if (body1==NULL) {
LOG_E(UTIL,"[websrv] cannot encode status body1 response\n");
} else {
websrv_printjson("status body1",body1);
}
json_t *body2=json_pack("{s:s,s:s,s:s,s:b}","name","exec_function", "value",execfunc, "type", strtype, "modifiable",0);
if (body2==NULL) {
LOG_E(UTIL,"[websrv] cannot encode status body1 response\n");
} else {
websrv_printjson("status body2",body2);
}
json_array_append(modulevars , body1);
json_array_append(modulevars , body2);
int us=ulfius_add_header_to_response(response,"content-type" ,"application/json");
if (us != U_OK){
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_add_header_to_response)");
LOG_E(UTIL,"[websrv] cannot set status response header type ulfius error %d \n",us);
}
us=ulfius_set_json_body_response(response, 200, modulevars);
if (us != U_OK){
ulfius_set_string_body_response(response, 501, "Internal server error (ulfius_set_json_body_response)");
LOG_E(UTIL,"[websrv] cannot set status body response ulfius error %d \n",us);
}
// ulfius_set_string_body_response(response, 200, cfgfile);
return U_CALLBACK_COMPLETE;
}
int websrv_add_endpoint( char **http_method, int num_method, const char * url_prefix,const char * url_format,
int (* callback_function[])(const struct _u_request * request,
struct _u_response * response,
void * user_data),
void * user_data) {
int status;
int j=0;
int priority = (user_data == NULL)?10:0;
for (int i=0; i<num_method; i++) {
status=ulfius_add_endpoint_by_val(&(websrvparams.instance),http_method[i],url_prefix,url_format,priority,callback_function[i],user_data);
if (status != U_OK) {
LOG_E(UTIL,"[websrv] cannot add endpoint %s %s/%s\n",http_method[i],url_prefix,url_format);
} else {
j++;
LOG_I(UTIL,"[websrv] endpoint %s %s/%s added\n",http_method[i],url_prefix,url_format);
}
}
return j;
}
/* add endpoints for a module, as defined in cmdparser_t telnet server structure */
void register_module_endpoints(cmdparser_t *module) {
int (* callback_functions_var[3])(const struct _u_request * request,
struct _u_response * response,
void * user_data) ={websrv_callback_get_softmodemvar,websrv_callback_okset_softmodem_cmdvar,websrv_callback_set_softmodemvar};
char *http_methods[3]={"GET","OPTIONS","POST"};
int (* callback_functions_cmd[3])(const struct _u_request * request,
struct _u_response * response,
void * user_data) ={websrv_callback_get_softmodemcmd,websrv_callback_okset_softmodem_cmdvar,websrv_callback_exec_softmodemcmd};
char prefixurl[TELNET_CMD_MAXSIZE+20];
snprintf(prefixurl,TELNET_CMD_MAXSIZE+19,"oaisoftmodem/%s",module->module);
LOG_I(UTIL,"[websrv] add endpoints %s/[variables or commands] \n",prefixurl);
websrv_add_endpoint(http_methods,3,prefixurl,"commands" ,callback_functions_cmd , module );
websrv_add_endpoint(http_methods,3,prefixurl,"variables" ,callback_functions_var, module);
}
void* websrv_autoinit() {
int ret;
telnetsrv_params_t *telnetparams= get_telnetsrv_params();
memset(&websrvparams,0,sizeof(websrvparams));
config_get( websrvoptions,sizeof(websrvoptions)/sizeof(paramdef_t),"websrv");
if (ulfius_init_instance(&(websrvparams.instance), websrvparams.listenport, NULL, NULL) != U_OK) {
LOG_W(UTIL, "[websrv] Error,cannot init websrv\n");
return(NULL);
}
u_map_put(websrvparams.instance.default_headers, "Access-Control-Allow-Origin", "*");
// Maximum body size sent by the client is 1 Kb
websrvparams.instance.max_post_body_size = 1024;
// Endpoint list declaration
//1: load the frontend code: files contained in the websrvparams.url directory
ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", websrvparams.url, NULL, 0, &websrv_callback_get_mainurl, NULL);
//2: build the first page, when receiving the "oaisoftmodem" url
// ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "variables", 0, &websrv_callback_get_softmodemstatus, NULL);
ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "commands", 0, &websrv_callback_get_softmodemmodules, NULL);
//3 default_endpoint declaration, it tries to open the file with the url name as specified in the request.It looks for the file
ulfius_set_default_endpoint(&(websrvparams.instance), &websrv_callback_default, NULL);
// endpoints
int (* callback_functions_var[3])(const struct _u_request * request,
struct _u_response * response,
void * user_data) ={websrv_callback_get_softmodemstatus,websrv_callback_okset_softmodem_cmdvar,websrv_callback_set_softmodemvar};
char *http_methods[3]={"GET","OPTIONS","POST"};
websrv_add_endpoint(http_methods,3,"oaisoftmodem","variables" ,callback_functions_var,NULL);
for (int i=0; telnetparams->CmdParsers[i].cmd != NULL; i++) {
register_module_endpoints( &(telnetparams->CmdParsers[i]) );
}
ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "@module/commands", 10, websrv_callback_newmodule, telnetparams);
ulfius_add_endpoint_by_val(&(websrvparams.instance), "GET", "oaisoftmodem", "@module/variables", 10, websrv_callback_newmodule, telnetparams);
// Start the framework
ret=U_ERROR;
if (websrvparams.keyfile!=NULL && websrvparams.certfile!=NULL) {
char * key_pem = websrv_read_file(websrvparams.keyfile);
char * cert_pem = websrv_read_file(websrvparams.certfile);
if ( key_pem == NULL && cert_pem != NULL) {
ret = ulfius_start_secure_framework(&(websrvparams.instance), key_pem, cert_pem);
free(key_pem);
free(cert_pem);
} else {
LOG_E(UTIL,"[websrv] Unable to load key %s and cert %s_\n",websrvparams.keyfile,websrvparams.certfile);
}
} else {
ret = ulfius_start_framework(&(websrvparams.instance));
}
if (ret == U_OK) {
LOG_I(UTIL, "[websrv] Web server started on port %d", websrvparams.instance.port);
} else {
LOG_W(UTIL,"[websrv] Error starting web server on port %d\n",websrvparams.instance.port);
}
return &(websrvparams.instance);
}
void websrv_end(void *webinst) {
ulfius_stop_framework((struct _u_instance *)webinst);
ulfius_clean_instance((struct _u_instance *)webinst);
return;
}

View File

@@ -0,0 +1,65 @@
/*
* 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.h
* \brief: implementation of web API
* \author Francois TABURET
* \date 2022
* \version 0.1
* \company NOKIA BellLabs France
* \email: francois.taburet@nokia-bell-labs.com
* \note
* \warning
*/
#ifndef WEBSRV_H
#define WEBSRV_H
#define WEBSRV_MODNAME "websrv"
#define WEBSRV_PORT 8090
/* 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
} websrv_printf_t;
/* websrv_params_t is an internal structure storing all the current parameters and */
/* global variables used by the web server */
typedef struct {
struct _u_instance instance; // ulfius (web server) instance
unsigned int dbglvl; // debug level of the server
int priority; // server running priority
unsigned int listenport; // ip port the telnet server is listening on
unsigned int listenaddr; // ip address the telnet server is listening on
unsigned int listenstdin; // enable command input from stdin
char *url; // url for the main (initial) web page
char *certfile; // cert file
char *keyfile; // key file
} websrv_params_t;
#endif

View File

@@ -0,0 +1,47 @@
if ( "${OPENAIR_CMAKE}" STREQUAL "")
message( FATAL_ERROR "oai Environment variables not set")
endif ( "${OPENAIR_CMAKE}" STREQUAL "")
set(WEBSRVROOT ${OPENAIR_DIR}/common/utils/websrv )
# websrv dependencies
#set( ULFIUS "")
find_library(ULFIUS NAMES "libulfius.so" REQUIRED NO_CACHE)
if ("${ULFIUS}" STREQUAL "")
message( FATAL_ERROR "ulfius library (https://github.com/babelouest/ulfius) not found, install libulfius-dev (ubuntu) ")
endif("${ULFIUS}" STREQUAL "")
#set (NPM "")
find_program(NPM NAMES npm NO_CACHE)
if ("${NPM}" STREQUAL "")
message( ERROR " npm is not installed, frontend won't be built. Possibly install npm, package is available for ubuntu and fedora")
endif("${NPM}" STREQUAL "")
# build the backend ( the embedded web server)
set(WEBSRV_SOURCE
${WEBSRVROOT}/websrv.c
)
add_library(websrv MODULE ${WEBSRV_SOURCE} )
target_link_libraries(websrv PRIVATE ulfius jansson)
# build the frontend ( loaded from web server by browsers)
add_custom_target (
websrvfront_installjsdep
WORKING_DIRECTORY ${WEBSRVROOT}/frontend
COMMAND npm install
DEPENDS ${WEBSRVROOT}/frontend/package-lock.json
)
add_custom_target (
websrvfront
WORKING_DIRECTORY ${WEBSRVROOT}/frontend
COMMAND npm run build
DEPENDS websrvfront_installjsdep
)
#install built files, required at exec time
install(TARGETS websrv DESTINATION bin)
if (EXISTS "${OPENAIR_CMAKE}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_CMAKE}/ran_build/build")
add_custom_command(TARGET websrvfront
POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${WEBSRVROOT}/frontend/dist/softmodem-ngx ${OPENAIR_CMAKE}/ran_build/build/websrv
COMMAND ${CMAKE_COMMAND} -E copy_directory ${WEBSRVROOT}/frontend/dist/softmodem-ngx ${OPENAIR_TARGETS}/bin/websrv
COMMENT "Moving frontend files to:\n ${OPENAIR_CMAKE}/ran_build/build/websrv\n ${OPENAIR_TARGETS}/bin/websrv" )
endif (EXISTS "${OPENAIR_CMAKE}/ran_build/build" AND IS_DIRECTORY "${OPENAIR_CMAKE}/ran_build/build")

View File

@@ -1494,7 +1494,8 @@ int start_rf(RU_t *ru) {
}
int stop_rf(RU_t *ru) {
ru->rfdevice.trx_end_func(&ru->rfdevice);
if (ru->rfdevice.trx_end_func )
ru->rfdevice.trx_end_func(&ru->rfdevice);
return 0;
}

View File

@@ -88,6 +88,7 @@ void get_common_options(uint32_t execmask) {
uint32_t online_log_messages=0;
uint32_t glog_level=0 ;
uint32_t start_telnetsrv = 0, start_telnetclt = 0;
uint32_t start_websrv = 0;
uint32_t noS1 = 0, nokrnmod = 1, nonbiot = 0;
uint32_t rfsim = 0, basicsim = 0, do_forms = 0;
char *logmem_filename = NULL;
@@ -117,6 +118,10 @@ void get_common_options(uint32_t execmask) {
set_softmodem_optmask(SOFTMODEM_TELNETCLT_BIT);
}
if (start_telnetsrv) {
load_module_shlib("websrv",NULL,0,NULL);
}
if (logmem_filename != NULL && strlen(logmem_filename) > 0) {
log_mem_filename = &logmem_filename[0];
log_mem_flag = 1;
@@ -197,6 +202,7 @@ void signal_handler(int sig) {
softmodem_printresources(sig,(telnet_printfunc_t)printf);
if (sig != SOFTMODEM_RTSIGNAL) {
printf("Linux signal %s...\n",strsignal(sig));
end_configmodule();
exit_function(__FILE__, __FUNCTION__, __LINE__,"softmodem starting exit procedure\n");
}
}

View File

@@ -170,18 +170,20 @@ extern int usrp_tx_thread;
#define CONFIG_HLP_FLOG "Enable online log \n"
#define CONFIG_HLP_LOGL "Set the global log level, valid options: (4:trace, 3:debug, 2:info, 1:warn, (0:error))\n"
#define CONFIG_HLP_TELN "Start embedded telnet server \n"
#define CONFIG_HLP_WEB "Start embedded web server \n"
#define CONFIG_FLOG_OPT "R"
#define CONFIG_LOGL_OPT "g"
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
/* command line parameters for LOG utility */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*-------------------------------------------------------------------------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/* command line parameters for LOG utility */
/* optname helpstr paramflags XXXptr defXXXval type numelt */
/*------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#define CMDLINE_LOGPARAMS_DESC { \
{CONFIG_FLOG_OPT , CONFIG_HLP_FLOG, 0, uptr:&online_log_messages, defintval:1, TYPE_INT, 0}, \
{CONFIG_LOGL_OPT , CONFIG_HLP_LOGL, 0, uptr:&glog_level, defintval:0, TYPE_UINT, 0}, \
{"telnetsrv", CONFIG_HLP_TELN, PARAMFLAG_BOOL, uptr:&start_telnetsrv, defintval:0, TYPE_UINT, 0}, \
{"log-mem", NULL, 0, strptr:(char **)&logmem_filename, defstrval:NULL, TYPE_STRING, 0}, \
{"telnetclt", NULL, 0, uptr:&start_telnetclt, defstrval:NULL, TYPE_UINT, 0}, \
{CONFIG_FLOG_OPT , CONFIG_HLP_FLOG, 0, uptr:&online_log_messages, defintval:1, TYPE_INT, 0}, \
{CONFIG_LOGL_OPT , CONFIG_HLP_LOGL, 0, uptr:&glog_level, defintval:0, TYPE_UINT, 0}, \
{"telnetsrv", CONFIG_HLP_TELN, PARAMFLAG_BOOL|PARAMFLAG_CMDLINEONLY, uptr:&start_telnetsrv, defintval:0, TYPE_UINT, 0}, \
{"websrv", CONFIG_HLP_WEB, PARAMFLAG_BOOL|PARAMFLAG_CMDLINEONLY, uptr:&start_websrv, defintval:0, TYPE_UINT, 0}, \
{"log-mem", NULL, 0, strptr:(char **)&logmem_filename, defstrval:NULL, TYPE_STRING, 0}, \
{"telnetclt", NULL, 0, uptr:&start_telnetclt, defstrval:NULL, TYPE_UINT, 0}, \
}

View File

@@ -41,12 +41,12 @@
static int coding_setmod_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static telnetshell_cmddef_t coding_cmdarray[] = {
{"mode","[sse,avx2,stdc,none]",coding_setmod_cmd},
{"","",NULL},
{"mode","[sse,avx2,stdc,none]",coding_setmod_cmd,{NULL},0,NULL},
{"","",NULL,{NULL},0,NULL},
};
telnetshell_vardef_t coding_vardef[] = {
{"maxiter",TELNET_VARTYPE_INT32,&max_turbo_iterations},
{"",0,NULL}
{"maxiter",TELNET_VARTYPE_INT32,0,&max_turbo_iterations},
{"",0,0,NULL}
};
/* PHY/defs.h contains MODE_DECODE_XXX macros, following table must match */
static char *modedesc[] = {"none","sse","C","avx2"};

View File

@@ -48,14 +48,16 @@ static int channelmod_show_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static int channelmod_modify_cmd(char *buff, int debug, telnet_printfunc_t prnt);
static int channelmod_print_help(char *buff, int debug, telnet_printfunc_t prnt);
static telnetshell_cmddef_t channelmod_cmdarray[] = {
{"help","",channelmod_print_help},
{"show","<predef,current>",channelmod_show_cmd},
{"modify","<channelid> <param> <value>",channelmod_modify_cmd},
{"","",NULL},
{"help","",channelmod_print_help,{NULL},0,NULL},
{"show","<predef,current>",channelmod_show_cmd,{NULL},TELNETSRV_CMDFLAG_TELNETONLY,NULL},
{"show predef","",channelmod_show_cmd,{NULL},TELNETSRV_CMDFLAG_WEBSRVONLY,NULL},
{"show current","",channelmod_show_cmd,{NULL},TELNETSRV_CMDFLAG_WEBSRVONLY,NULL},
{"modify","<channelid> <param> <value>",channelmod_modify_cmd,{NULL},TELNETSRV_CMDFLAG_TELNETONLY,NULL},
{"","",NULL,{NULL},0,NULL},
};
static telnetshell_vardef_t channelmod_vardef[] = {
{"",0,NULL}
{"",0,0,NULL}
};
static double snr_dB=25;
@@ -1931,10 +1933,15 @@ static void display_channelmodel(channel_desc_t *cd,int debug, telnet_printfunc_
}
}
static int channelmod_show_cmd(char *buff, int debug, telnet_printfunc_t prnt) {
char *subcmd=NULL;
int s = sscanf(buff,"%ms\n",&subcmd);
int s;
if (buff == NULL) {
subcmd = strdup(""); //enforce help display
s=2;
} else {
s = sscanf(buff,"%ms\n",&subcmd);
}
if (s>0) {
if ( strcmp(subcmd,"predef") == 0) {

View File

@@ -91,12 +91,12 @@
static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg);
static telnetshell_cmddef_t rfsimu_cmdarray[] = {
{"setmodel","<model name> <model type>",(cmdfunc_t)rfsimu_setchanmod_cmd,TELNETSRV_CMDFLAG_PUSHINTPOOLQ},
{"","",NULL},
{"setmodel","<model name> <model type>",(cmdfunc_t)rfsimu_setchanmod_cmd,{NULL},TELNETSRV_CMDFLAG_PUSHINTPOOLQ,NULL},
{"","",NULL,{NULL},0,NULL},
};
static telnetshell_vardef_t rfsimu_vardef[] = {
{"",0,NULL}
{"",0,0,NULL}
};
pthread_mutex_t Sockmutex;
@@ -316,6 +316,15 @@ static void rfsimulator_readconfig(rfsimulator_state_t *rfsimulator) {
static int rfsimu_setchanmod_cmd(char *buff, int debug, telnet_printfunc_t prnt, void *arg) {
char *modelname=NULL;
char *modeltype=NULL;
rfsimulator_state_t *t = (rfsimulator_state_t *)arg;
if(t->channelmod==false) {
prnt("ERROR channel modelisation disabled...\n");
return 0;
}
if (buff == NULL) {
prnt("ERROR wrong rfsimu setchannelmod command...\n");
return 0;
}
if (debug)
prnt("rfsimu_setchanmod_cmd buffer \"%s\"\n",buff);
int s = sscanf(buff,"%m[^ ] %ms\n",&modelname, &modeltype);