/* * 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/telnetsrv/telnetsrv.c * \brief: implementation of a telnet server * \author Francois TABURET * \date 2017 * \version 0.1 * \company NOKIA BellLabs France * \email: francois.taburet@nokia-bell-labs.com * \note * \warning */ #define _GNU_SOURCE #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <pthread.h> #include "telnetsrv.h" #include <string.h> #include <stdarg.h> #include <unistd.h> #include <fcntl.h> #include <dlfcn.h> #include <sys/time.h> #include <sys/resource.h> #include "common/utils/load_module_shlib.h" #include "common/config/config_userapi.h" #include "common/utils/threadPool/thread-pool.h" #include "executables/softmodem-common.h" #include <readline/history.h> #include "telnetsrv_phycmd.h" #include "telnetsrv_proccmd.h" static char *telnet_defstatmod[] = {"softmodem","phy","loader","measur"}; static telnetsrv_params_t telnetparams; #define TELNETSRV_LISTENADDR 0 #define TELNETSRV_LISTENPORT 1 #define TELNETSRV_PRIORITY 2 #define TELNETSRV_DEBUG 3 #define TELNETSRV_LOOPC 4 #define TELNETSRV_LOOPD 5 #define TELNETSRV_HISFILE 6 #define TELNETSRV_HISSIZE 7 #define TELNETSRV_PHYBSIZE 8 #define TELNETSRV_STATICMOD 9 #define TELNETSRV_SHRMOD 10 paramdef_t telnetoptions[] = { /*--------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /* configuration parameters for telnet utility */ /* optname helpstr paramflags XXXptr defXXXval type numelt */ /*--------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ {"listenaddr", "<listen ip address>\n", 0, uptr:&telnetparams.listenaddr, defstrval:"0.0.0.0", TYPE_IPV4ADDR, 0 }, {"listenport", "<local port>\n", 0, uptr:&(telnetparams.listenport), defuintval:9090, TYPE_UINT, 0 }, {"priority", "<scheduling policy (0-99)\n", 0, iptr:&telnetparams.priority, defuintval:0, TYPE_INT, 0 }, {"debug", "<debug level>\n", 0, uptr:NULL, defuintval:0, TYPE_UINT, 0 }, {"loopcount", "<loop command iterations>\n", 0, uptr:&(telnetparams.loopcount), defuintval:10, TYPE_UINT, 0 }, {"loopdelay", "<loop command delay (ms)>\n", 0, uptr:&(telnetparams.loopdelay), defuintval:5000, TYPE_UINT, 0 }, {"histfile", "<history file name>\n", PARAMFLAG_NOFREE, strptr:&(telnetparams.histfile), defstrval:"oaitelnet.history", TYPE_STRING, 0 }, {"histsize", "<history sizes>\n", 0, iptr:&(telnetparams.histsize), defuintval:50, TYPE_INT, 0 }, {"phypbsize", "<phy dump buff size (bytes)>\n",0, uptr:&(telnetparams.phyprntbuff_size),defuintval:65000, TYPE_UINT, 0 }, {"staticmod", "<static modules selection>\n", 0, strlistptr:NULL, defstrlistval:telnet_defstatmod,TYPE_STRINGLIST,(sizeof(telnet_defstatmod)/sizeof(char *))}, {"shrmod", "<dynamic modules selection>\n", 0, strlistptr:NULL, defstrlistval:NULL,TYPE_STRINGLIST,0 } }; int get_phybsize(void) { return telnetparams.phyprntbuff_size; }; int add_telnetcmd(char *modulename,telnetshell_vardef_t *var, telnetshell_cmddef_t *cmd ); int setoutput(char *buff, int debug, telnet_printfunc_t prnt); int setparam(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} }; telnetshell_cmddef_t telnet_cmdarray[] = { {"redirlog","[here,file,off]",setoutput}, {"param","[prio]",setparam}, {"history","[list,reset]",history_cmd}, {"","",NULL}, }; void client_printf(const char *message, ...) { va_list va_args; va_start(va_args, message); if (telnetparams.new_socket > 0) { vsnprintf(telnetparams.msgbuff,sizeof(telnetparams.msgbuff)-1,message, va_args); send(telnetparams.new_socket,telnetparams.msgbuff, strlen(telnetparams.msgbuff), MSG_NOSIGNAL); } else { vprintf(message, va_args); } va_end(va_args); return ; } #define NICE_MAX 19 #define NICE_MIN -20 void set_sched(pthread_t tid, int pid, int priority) { int rt; struct sched_param schedp; int policy; char strpolicy[10]; //sched_get_priority_max(SCHED_FIFO) if (priority < NICE_MIN) { policy=SCHED_FIFO; sprintf(strpolicy,"%s","fifo"); schedp.sched_priority= NICE_MIN - priority ; if ( (schedp.sched_priority < sched_get_priority_min(SCHED_FIFO)) || (schedp.sched_priority > sched_get_priority_max(SCHED_FIFO)) ) { client_printf("Error: %i invalid prio, should be %i to %i, \n", priority, NICE_MIN -sched_get_priority_min(SCHED_FIFO), NICE_MIN - sched_get_priority_max(SCHED_FIFO) ); } } else if (priority > NICE_MAX) { policy=SCHED_IDLE; sprintf(strpolicy,"%s","idle"); schedp.sched_priority=0; } else { policy=SCHED_OTHER; sprintf(strpolicy,"%s","other"); schedp.sched_priority=0; } if( tid != 0) { rt = pthread_setschedparam(tid, policy, &schedp); } else if(pid > 0) { rt = sched_setscheduler( pid, policy,&schedp); } else { rt= -1; client_printf("Error: no pid or tid specified\n"); } if (rt != 0) { client_printf("Error %i: %s modifying sched param to %s:%i, \n", errno,strerror(errno),strpolicy,schedp.sched_priority); } else { client_printf("policy set to %s, priority %i\n",strpolicy,schedp.sched_priority); if ( policy==SCHED_OTHER) { rt = getpriority(PRIO_PROCESS,tid); if (rt != -1) { rt = setpriority(PRIO_PROCESS,tid,priority); if (rt < 0) { client_printf("Error %i: %s trying to set nice value of thread %u to %i\n", errno,strerror(errno),tid,priority); } } else { client_printf("Error %i: %s trying to get nice value of thread %u \n", errno,strerror(errno),tid); } } } if ( policy == SCHED_OTHER) { if ( tid > 0 && tid != pthread_self()) { client_printf("setting nice value using a thread id not implemented....\n"); } else if (pid > 0) { errno=0; rt = setpriority(PRIO_PROCESS,pid,priority); if (rt != 0) { client_printf("Error %i: %s calling setpriority, \n",errno,strerror(errno)); } else { client_printf("nice value set to %i\n",priority); } } } } void set_affinity(pthread_t tid, int pid, int coreid) { cpu_set_t cpuset; int rt; CPU_ZERO(&cpuset); CPU_SET(coreid, &cpuset); if (tid > 0) { rt = pthread_setaffinity_np((pthread_t)tid, sizeof(cpu_set_t), &cpuset); } else if (pid > 0) { rt = sched_setaffinity((pid_t)pid, sizeof(cpu_set_t), &cpuset); } else { rt= -1; } if (rt != 0) { client_printf("Error %i: %s calling , xxx_setaffinity...\n",errno,strerror(errno)); } else { client_printf("thread %i affinity set to %i\n",(pid==0)?(int)tid:pid,coreid); } } /*------------------------------------------------------------------------------------*/ /* function implementing telnet server specific commands, parameters of the telnet_cmdarray table */ void redirstd(char *newfname,telnet_printfunc_t prnt ) { FILE *fd; fd=freopen(newfname, "w", stdout); if (fd == NULL) { prnt("ERROR: stdout redir to %s error %s",strerror(errno)); } fd=freopen(newfname, "w", stderr); if (fd == NULL) { prnt("ERROR: stderr redir to %s error %s",strerror(errno)); } } int setoutput(char *buff, int debug, telnet_printfunc_t prnt) { char cmds[TELNET_MAX_MSGLENGTH/TELNET_CMD_MAXSIZE][TELNET_CMD_MAXSIZE]; char *logfname; char stdout_str[64]; #define LOGFILE "logfile.log" memset(cmds,0,sizeof(cmds)); sscanf(buff,"%9s %32s %9s %9s %9s", cmds[0],cmds[1],cmds[2],cmds[3],cmds[4] ); if (strncasecmp(cmds[0],"here",4) == 0) { fflush(stdout); sprintf(stdout_str,"/proc/%i/fd/%i",getpid(),telnetparams.new_socket); dup2(telnetparams.new_socket,fileno(stdout)); // freopen(stdout_str, "w", stdout); // freopen(stdout_str, "w", stderr); dup2(telnetparams.new_socket,fileno(stderr)); prnt("Log output redirected to this terminal (%s)\n",stdout_str); } if (strncasecmp(cmds[0],"file",4) == 0) { if (cmds[1][0] == 0) logfname=LOGFILE; else logfname=cmds[1]; fflush(stdout); redirstd(logfname,prnt); } if (strncasecmp(cmds[0],"off",3) == 0) { fflush(stdout); redirstd("/dev/tty",prnt); } return CMDSTATUS_FOUND; } /* setoutput */ 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)); sscanf(buff,"%9s %9s %9s %9s %9s", cmds[0],cmds[1],cmds[2],cmds[3],cmds[4] ); if (strncasecmp(cmds[0],"prio",4) == 0) { int prio; prio=(int)strtol(cmds[1],NULL,0); if (errno == ERANGE) return CMDSTATUS_VARNOTFOUND; telnetparams.priority = prio; set_sched(pthread_self(),0,prio); return CMDSTATUS_FOUND; } if (strncasecmp(cmds[0],"aff",3) == 0) { int aff; aff=(int)strtol(cmds[1],NULL,0); if (errno == ERANGE) return CMDSTATUS_VARNOTFOUND; set_affinity(pthread_self(),0,aff); return CMDSTATUS_FOUND; } return CMDSTATUS_NOTFOUND; } /* setparam */ int history_cmd(char *buff, int debug, telnet_printfunc_t prnt) { char cmds[TELNET_MAX_MSGLENGTH/TELNET_CMD_MAXSIZE][TELNET_CMD_MAXSIZE]; memset(cmds,0,sizeof(cmds)); sscanf(buff,"%9s %9s %9s %9s %9s", cmds[0],cmds[1],cmds[2],cmds[3],cmds[4] ); if (cmds[0] == NULL) return CMDSTATUS_VARNOTFOUND; if (strncasecmp(cmds[0],"list",4) == 0) { HIST_ENTRY **hist = history_list(); if (hist) { for (int i = 0; hist[i]; i++) { prnt ("%d: %s\n", i + history_base, hist[i]->line); } } return CMDSTATUS_FOUND; } if (strncasecmp(cmds[0],"reset",5) == 0) { clear_history(); write_history(telnetparams.histfile); return CMDSTATUS_FOUND; } return CMDSTATUS_NOTFOUND; } /* history_cmd */ /*-------------------------------------------------------------------------------------------------------*/ /* generic commands available for all modules loaded by the server */ int setgetvar(int moduleindex,char getorset,char *params) { int n,i; char varname[TELNET_CMD_MAXSIZE]; char *varval=NULL; memset(varname,0,sizeof(varname)); n = sscanf(params,"%9s %ms",varname,&varval); for ( i=0 ; telnetparams.CmdParsers[moduleindex].var[i].varvalptr != NULL ; i++) { if ( strncasecmp(telnetparams.CmdParsers[moduleindex].var[i].varname,varname,strlen(telnetparams.CmdParsers[moduleindex].var[i].varname)) == 0) { 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; } } if (n > 1 && (getorset == 's' || getorset == 'S')) { client_printf("%s, %s set to \n", telnetparams.CmdParsers[moduleindex].module, telnetparams.CmdParsers[moduleindex].var[i].varname); 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; } } } } if (n>1 && varval != NULL) { free(varval); } return CMDSTATUS_VARNOTFOUND; } /*----------------------------------------------------------------------------------------------------*/ char *get_time(char *buff,int bufflen) { struct tm tmstruct; time_t now = time (0); strftime (buff, bufflen, "%Y-%m-%d %H:%M:%S.000", localtime_r(&now,&tmstruct)); return buff; } int process_command(char *buf) { int i,j,k; char modulename[TELNET_CMD_MAXSIZE]; char cmd[TELNET_CMD_MAXSIZE]; char cmdb[TELNET_MAX_MSGLENGTH]; char *bufbck; int rt; memset(modulename,0,sizeof(modulename)); memset(cmd,0,sizeof(cmd)); memset(cmdb,0,sizeof(cmdb)); if (strncasecmp(buf,"ex",2) == 0) return CMDSTATUS_EXIT; if (strncasecmp(buf,"help",4) == 0) { for (i=0; telnetparams.CmdParsers[i].var != NULL && telnetparams.CmdParsers[i].cmd != NULL; i++) { client_printf(" module %i = %s:\n",i,telnetparams.CmdParsers[i].module); for(j=0; telnetparams.CmdParsers[i].var[j].varvalptr != NULL ; j++) { client_printf(" %s [get set] %s <value>\n", telnetparams.CmdParsers[i].module, telnetparams.CmdParsers[i].var[j].varname); } for(j=0; telnetparams.CmdParsers[i].cmd[j].cmdfunc != NULL ; j++) { client_printf(" %s %s %s\n", telnetparams.CmdParsers[i].module,telnetparams.CmdParsers[i].cmd[j].cmdname, telnetparams.CmdParsers[i].cmd[j].helpstr); } } return CMDSTATUS_FOUND; } memset(modulename,0,sizeof(modulename)); memset(cmd,0,sizeof(cmd)); memset(cmdb,0,sizeof(cmdb)); bufbck=strdup(buf); rt=CMDSTATUS_NOTFOUND; j = sscanf(buf,"%19s %19s %[^\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); 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)) { if (strncasecmp(cmd,"getall",7) == 0 ) { for(j=0; telnetparams.CmdParsers[i].var[j].varvalptr != NULL ; j++) { setgetvar(i,'g',telnetparams.CmdParsers[i].var[j].varname); } rt= CMDSTATUS_FOUND; } else if (strcasecmp(cmd,"get") == 0 || strcasecmp(cmd,"set") == 0) { rt= setgetvar(i,cmd[0],cmdb); } 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].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); pushNotifiedFIFO(telnetparams.CmdParsers[i].cmd[k].qptr, msg); } else { telnetparams.CmdParsers[i].cmd[k].cmdfunc(cmdb, telnetparams.telnetdbg, client_printf); } rt= CMDSTATUS_FOUND; } } /* for k */ }/* else */ }/* strncmp: module name test */ else if (strncasecmp(modulename,"loop",4) == 0 ) { int f = fcntl(telnetparams.new_socket,F_GETFL); int f1=fcntl (telnetparams.new_socket, F_SETFL, O_NONBLOCK | f); if (f<0 || f1 <0) { client_printf( " Loop won't be cancelable: %s\n",strerror(errno) ); } for(int lc=0; lc<telnetparams.loopcount; lc++) { char dummybuff[20]; char tbuff[64]; client_printf(CSI "1J" CSI "1;10H " STDFMT "%s %i/%i\n", get_time(tbuff,sizeof(tbuff)),lc,telnetparams.loopcount ); process_command(bufbck+strlen("loop")+1); errno=0; int rs = read(telnetparams.new_socket,dummybuff,sizeof(dummybuff)); if (telnetparams.telnetdbg > 0) client_printf("Received \"%s\" status %d, errno %s while running loop\n",dummybuff,rs,strerror(errno)); if ( errno != EAGAIN && errno != EWOULDBLOCK) { client_printf( STDFMT " Loop canceled, iteration %i/%i\n",lc,telnetparams.loopcount ); lc=telnetparams.loopcount; break; } usleep(telnetparams.loopdelay * 1000); } fcntl (telnetparams.new_socket, F_SETFL, f); rt= CMDSTATUS_FOUND; } /* loop */ } /* for i */ free(bufbck); return rt; } void run_telnetsrv(void) { int sock; struct sockaddr_in name; char buf[TELNET_MAX_MSGLENGTH]; struct sockaddr cli_addr; unsigned int cli_len = sizeof(cli_addr); int readc, filled; int status; int optval = 1; char prompt[sizeof(TELNET_PROMPT_PREFIX)+10]; pthread_setname_np(pthread_self(), "telnet"); set_sched(pthread_self(),0,telnetparams.priority); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) fprintf(stderr,"[TELNETSRV] Error %s on socket call\n",strerror(errno)); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval); name.sin_family = AF_INET; if (telnetparams.listenaddr == 0) name.sin_addr.s_addr = INADDR_ANY; else name.sin_addr.s_addr = telnetparams.listenaddr; name.sin_port = htons((unsigned short)(telnetparams.listenport)); if(bind(sock, (void *) &name, sizeof(name))) fprintf(stderr,"[TELNETSRV] Error %s on bind call\n",strerror(errno)); if(listen(sock, 1) == -1) fprintf(stderr,"[TELNETSRV] Error %s on listen call\n",strerror(errno)); using_history(); int plen=sprintf(prompt,"%s_%s> ",TELNET_PROMPT_PREFIX,get_softmodem_function(NULL)); printf("\nInitializing telnet server...\n"); while( (telnetparams.new_socket = accept(sock, &cli_addr, &cli_len)) ) { printf("[TELNETSRV] Telnet client connected....\n"); read_history(telnetparams.histfile); stifle_history(telnetparams.histsize); if(telnetparams.new_socket < 0) fprintf(stderr,"[TELNETSRV] Error %s on accept call\n",strerror(errno)); while(telnetparams.new_socket>0) { filled = 0; memset(buf,0,sizeof(buf)); while(filled < ( TELNET_MAX_MSGLENGTH-1)) { readc = recv(telnetparams.new_socket, buf+filled, TELNET_MAX_MSGLENGTH-filled-1, 0); if(!readc) break; filled += readc; if(buf[filled-1] == '\n') { buf[filled-1] = 0; break; } } if(!readc) { printf ("[TELNETSRV] Telnet Client disconnected.\n"); break; } if (telnetparams.telnetdbg > 0) printf("[TELNETSRV] Command received: readc %i filled %i \"%s\"\n", readc, filled,buf); if (buf[0] == '!') { if (buf[1] == '!') { sprintf(buf,"%s","telnet history list"); } else { HIST_ENTRY *hisentry = history_get(strtol(buf+1,NULL,0)); if (hisentry) { char msg[TELNET_MAX_MSGLENGTH + plen +10]; sprintf(buf,"%s",hisentry->line); sprintf(msg,"%s %s\n",prompt, hisentry->line); send(telnetparams.new_socket, msg, strlen(msg), MSG_NOSIGNAL); } } } if (strlen(buf) > 2 ) { status=process_command(buf); } else status=CMDSTATUS_NOCMD; if (status != CMDSTATUS_EXIT) { if (status == CMDSTATUS_NOTFOUND) { char msg[TELNET_MAX_MSGLENGTH + 50]; sprintf(msg,"Error: \n %s\n is not a softmodem command\n",buf); send(telnetparams.new_socket, msg, strlen(msg), MSG_NOSIGNAL); } else if (status == CMDSTATUS_FOUND) { add_history(buf); } send(telnetparams.new_socket, prompt, strlen(prompt), MSG_NOSIGNAL); } else { printf ("[TELNETSRV] Closing telnet connection...\n"); break; } } write_history(telnetparams.histfile); clear_history(); close(telnetparams.new_socket); printf ("[TELNETSRV] Telnet server waitting for connection...\n"); } close(sock); return; } void poll_telnetcmdq(void *qid, void *arg) { notifiedFIFO_elt_t *msg = pollNotifiedFIFO((notifiedFIFO_t *)qid); if (msg != NULL) { telnetsrv_qmsg_t *msgdata=NotifiedFifoData(msg); msgdata->cmdfunc(msgdata->cmdbuff,msgdata->debug,msgdata->prnt,arg); free(msgdata->cmdbuff); delNotifiedFIFO_elt(msg); } } /*------------------------------------------------------------------------------------------------*/ /* load the commands delivered with the telnet server * * * */ void exec_moduleinit(char *modname) { void (*fptr)(void); char initfunc[TELNET_CMD_MAXSIZE+9]; if (strlen(modname) > TELNET_CMD_MAXSIZE) { fprintf(stderr,"[TELNETSRV] module %s not loaded, name exceeds the %i size limit\n", modname, TELNET_CMD_MAXSIZE); return; } sprintf(initfunc,"add_%s_cmds",modname); fptr = dlsym(RTLD_DEFAULT,initfunc); if ( fptr != NULL) { fptr(); } else { fprintf(stderr,"[TELNETSRV] couldn't find %s for module %s \n",initfunc,modname); } } int add_embeddedmodules(void) { int ret=0; for(int i=0; i<telnetoptions[TELNETSRV_STATICMOD].numelt; i++) { ret++; exec_moduleinit(telnetoptions[TELNETSRV_STATICMOD].strlistptr[i]); } return ret; } int add_sharedmodules(void) { char initfunc[TELNET_CMD_MAXSIZE+9]; void (*fptr)(void); int ret=0; for(int i=0; i<telnetoptions[TELNETSRV_SHRMOD].numelt; i++) { sprintf(initfunc,"add_%s_cmds",telnetoptions[TELNETSRV_SHRMOD].strlistptr[i]); fptr = dlsym(RTLD_DEFAULT,initfunc); if ( fptr != NULL) { fptr(); ret++; } else { fprintf(stderr,"[TELNETSRV] couldn't find %s for module %s \n",initfunc,telnetoptions[TELNETSRV_STATICMOD].strlistptr[i]); } } return ret; } /* autoinit functions is called by the loader when the telnet shared library is dynamically loaded */ int telnetsrv_autoinit(void) { memset(&telnetparams,0,sizeof(telnetparams)); config_get( telnetoptions,sizeof(telnetoptions)/sizeof(paramdef_t),"telnetsrv"); /* possibly load a exec specific shared lib */ char *execfunc=get_softmodem_function(NULL); char libname[64]; sprintf(libname,"telnetsrv_%s",execfunc); load_module_shlib(libname,NULL,0,NULL); if(pthread_create(&telnetparams.telnet_pthread,NULL, (void *(*)(void *))run_telnetsrv, NULL) != 0) { fprintf(stderr,"[TELNETSRV] Error %s on pthread_create call\n",strerror(errno)); return -1; } add_telnetcmd("telnet", telnet_vardef, telnet_cmdarray); add_embeddedmodules(); return 0; } /*---------------------------------------------------------------------------------------------*/ /* add_telnetcmd is used to add a set of commands to the telnet server. A module calls this * function at init time. the telnet server is delivered with a set of commands which * will be loaded or not depending on the telnet section of the config file */ int add_telnetcmd(char *modulename, telnetshell_vardef_t *var, telnetshell_cmddef_t *cmd) { int i; notifiedFIFO_t *afifo=NULL; if( modulename == NULL || var == NULL || cmd == NULL) { fprintf(stderr,"[TELNETSRV] Telnet server, add_telnetcmd: invalid parameters\n"); return -1; } for (i=0; i<TELNET_MAXCMD ; i++) { if (telnetparams.CmdParsers[i].var == NULL) { strncpy(telnetparams.CmdParsers[i].module,modulename,sizeof(telnetparams.CmdParsers[i].module)-1); telnetparams.CmdParsers[i].cmd = cmd; telnetparams.CmdParsers[i].var = var; if (cmd->cmdflags & TELNETSRV_CMDFLAG_PUSHINTPOOLQ) { if (afifo == NULL) { afifo = malloc(sizeof(notifiedFIFO_t)); initNotifiedFIFO(afifo); } cmd->qptr = afifo; } printf("[TELNETSRV] Telnet server: module %i = %s added to shell\n", i,telnetparams.CmdParsers[i].module); break; } } return 0; } /* function which will be called by the shared lib loader, to check shared lib version against main exec version. version mismatch no considered as fatal (interfaces not supposed to change) */ int telnetsrv_checkbuildver(char *mainexec_buildversion, char **shlib_buildversion) { #ifndef PACKAGE_VERSION #define PACKAGE_VERSION "standalone built: " __DATE__ __TIME__ #endif *shlib_buildversion = PACKAGE_VERSION; if (strcmp(mainexec_buildversion, *shlib_buildversion) != 0) { fprintf(stderr,"[TELNETSRV] shared lib version %s, doesn't match main version %s, compatibility should be checked\n", mainexec_buildversion,*shlib_buildversion); } return 0; } int telnetsrv_getfarray(loader_shlibfunc_t **farray) { *farray=malloc(sizeof(loader_shlibfunc_t)*2); (*farray)[0].fname=TELNET_ADDCMD_FNAME; (*farray)[0].fptr=(int (*)(void) )add_telnetcmd; (*farray)[1].fname=TELNET_POLLCMDQ_FNAME; (*farray)[1].fptr=(int (*)(void) )poll_telnetcmdq; return ( 2); }