diff --git a/src/emc/usr_intf/schedrmt.cc b/src/emc/usr_intf/schedrmt.cc index ebead0298b5..d48a3f99648 100644 --- a/src/emc/usr_intf/schedrmt.cc +++ b/src/emc/usr_intf/schedrmt.cc @@ -1,9 +1,9 @@ /******************************************************************** * Description: schedrmt.cc -* Extended telnet based scheduler interface +* Extended Telnet-based scheduler interface. * -* Derived from a work by Fred Proctor & Will Shackleford -* Further derived from work by jmkasunich +* Derived from a work by Fred Proctor & Will Shackleford. +* Further derived from work by jmkasunich. * * Author: Eric H. Johnson * License: GPL Version 2 @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -28,9 +30,7 @@ #include #include #include -#include - -#include +#include // for sessions counter #include "rcs.hh" #include "posemath.h" // PM_POSE, TO_RAD @@ -252,10 +252,9 @@ typedef struct { char progName[256];} connectionRecType; int port = 5008; -int server_sockfd, client_sockfd; -socklen_t server_len, client_len; +int server_sockfd; +socklen_t server_len; struct sockaddr_in server_address; -struct sockaddr_in client_address; bool useSockets = true; int tokenIdx; const char *delims = " \n\r\0"; @@ -263,7 +262,7 @@ int enabledConn = -1; char pwd[16] = "EMC\0"; char enablePWD[16] = "EMCTOO\0"; char serverName[24] = "EMCNETSVR\0"; -int sessions = 0; +std::atomic_int sessions = 0; int maxSessions = -1; float pollDelay = 1.0; @@ -272,8 +271,10 @@ const char *setCommands[] = { "QMODE", "QSTATUS", "AUTOTAGID", "PGMADD", "PGMBYID", "PGMBYINDEX", "PGMALL", "PRIORITYBYID", "PRIORITYBYINDEX", "DELETEBYID", "DELETEBYINDEX", "POLLRATE", ""}; +const unsigned char setCommands_max_length = 16; const char *commands[] = {"HELLO", "SET", "GET", "QUIT", "SHUTDOWN", "HELP", ""}; +const unsigned char commands_max_length = 8; struct option longopts[] = { {"port", 1, NULL, 'p'}, @@ -286,7 +287,7 @@ struct option longopts[] = { }; -static void thisQuit() +static void freeAllBuffers() { EMC_NULL emc_null_msg; @@ -312,7 +313,11 @@ static void thisQuit() delete emcCommandBuffer; emcCommandBuffer = 0; } +} +static void thisQuit() +{ + freeAllBuffers(); exit(0); } @@ -329,6 +334,7 @@ static int initSockets() return 0; } +// attached to signal SIGINT in main() static void sigQuit(int /*sig*/) { thisQuit(); @@ -346,7 +352,7 @@ static setCommandType lookupSetCommand(char *s) int temp; while (i < scUnknown) { - if (strcmp(setCommands[i], s) == 0) return i; + if (strncmp(setCommands[i], s, setCommands_max_length+1) == 0) return i; // (int)i += 1; temp = i; temp++; @@ -399,10 +405,10 @@ static int checkBinaryASCII(char *s) static queueStatusType checkMode(char *s) { - static const char *runStr = "RUN"; - static const char *stopStr = "STOP"; - static const char *pauseStr = "PAUSE"; - static const char *resumeStr = "RESUME"; + static const char * const runStr = "RUN"; + static const char * const stopStr = "STOP"; + static const char * const pauseStr = "PAUSE"; + static const char * const resumeStr = "RESUME"; if (s == NULL) return qsError; strupr(s); @@ -941,7 +947,7 @@ int commandShutdown(connectionRecType *context) { if (context->cliSock == enabledConn) { printf("Shutting down\n"); - thisQuit(); + freeAllBuffers(); return -1; } else @@ -1033,7 +1039,7 @@ static int helpQuit(connectionRecType *context) snprintf(context->outBuf, sizeof(context->outBuf), "Usage:\n\r"); rtapi_strxcat(context->outBuf, " The quit command has the server initiate a disconnect from the client,\n\r"); rtapi_strxcat(context->outBuf, " the command has no parameters and no requirements to have negotiated\n\r"); - rtapi_strxcat(context->outBuf, " a hello, or be in control."); + rtapi_strxcat(context->outBuf, " a hello, or to be in control."); sockWrite(context); return 0; } @@ -1079,7 +1085,9 @@ commandTokenType lookupToken(char *s) int temp; while (i < cmdUnknown) { - if (strcmp(commands[i], s) == 0) return i; + if (strncmp(commands[i], s, commands_max_length+1) == 0) { + return i; + } // (int)i += 1; temp = i; temp++; @@ -1093,10 +1101,10 @@ int parseCommand(connectionRecType *context) int ret = 0; char *pch; char s[64]; - static const char *helloNakStr = "HELLO NAK\r\n"; - static const char *shutdownNakStr = "SHUTDOWN NAK\r\n"; - static const char *helloAckStr = "HELLO ACK %s 1.1\r\n"; - static const char *setNakStr = "SET NAK\r\n"; + static const char * const helloNakStr = "HELLO NAK\r\n"; + static const char * const shutdownNakStr = "SHUTDOWN NAK\r\n"; + static const char * const helloAckStr = "HELLO ACK %s 1.1\r\n"; + static const char * const setNakStr = "SET NAK\r\n"; pch = strtok(context->inBuf, delims); snprintf(s, sizeof(s), helloAckStr, serverName); @@ -1143,43 +1151,31 @@ void *checkQueue(void * /*arg*/) return 0; } -void *readClient(void * /*arg*/) +void *readClient(void *arg) { + connectionRecType *context = (connectionRecType *)arg; + char str[1600]; - char buf[1600]; - unsigned int i, j; - int len; - connectionRecType *context; - - -// res = 1; - context = (connectionRecType *) malloc(sizeof(connectionRecType)); - context->cliSock = client_sockfd; - context->linked = false; - context->echo = true; - context->verbose = false; - rtapi_strxcpy(context->version, "1.0"); - rtapi_strxcpy(context->hostName, "Default"); - context->enabled = false; - context->commMode = 0; - context->commProt = 0; - context->inBuf[0] = 0; - buf[0] = 0; - + char buf[1600] = {0}; + while (1) { - len = read(context->cliSock, &str, 1600); + int len = read(context->cliSock, &str, sizeof(str)-1); if (len <= 0) goto finished; str[len] = 0; rtapi_strxcat(buf, str); - if (!memchr(str, 0x0d, strlen(str))) continue; - if (context->echo && context->linked) + if (!memchr(str, '\r', len)) continue; + if (context->echo && context->linked) { if(write(context->cliSock, buf, strlen(buf)) != (ssize_t)strlen(buf)) { fprintf(stderr, "emcrsh: write() failed: %s", strerror(errno)); } - i = 0; - j = 0; - while (i <= strlen(buf)) { + } + // Iterate over lines in buffer and parse commands + size_t buflen = strlen(buf); // avoid multiple execution of strlen within loop + for (unsigned int i=0,j=0; i <= buflen; i++) { if ((buf[i] != '\n') && (buf[i] != '\r')) { + if (j>=sizeof(context->inBuf) - 1) { + goto finished; + } context->inBuf[j] = buf[i]; j++; } @@ -1189,39 +1185,53 @@ void *readClient(void * /*arg*/) if (parseCommand(context) == -1) goto finished; j = 0; } - i++; } - buf[0] = 0; + buf[0] = 0; } finished: close(context->cliSock); free(context); - pthread_exit((void *)0); - sessions--; // FIXME: not reached + --sessions; // std::atomic_int to avoid race condition + return(NULL); // equivalent to pthread_exit() } -int sockMain() +void sockMain() { - pthread_t thrd; - int res; - while (1) { - - client_len = sizeof(client_address); - client_sockfd = accept(server_sockfd, - (struct sockaddr *)&client_address, &client_len); - if (client_sockfd < 0) exit(0); - sessions++; - if ((maxSessions == -1) || (sessions <= maxSessions)) - res = pthread_create(&thrd, NULL, readClient, (void *)NULL); - else res = -1; - if (res != 0) { - close(client_sockfd); - sessions--; + int res = -1; + struct sockaddr_in client_address; + socklen_t client_len = sizeof(client_address); + int client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len); + if (client_sockfd < 0) { + perror("sockMain: accept failed\n"); + exit(EXIT_FAILURE); } - } - return 0; + ++sessions; + if ((maxSessions == -1) || (sessions <= maxSessions)) { + connectionRecType *context = (connectionRecType *)calloc(1, sizeof(connectionRecType)); + if (context) { + // Initialize necessary context fields, rest are set to zero in calloc + context->cliSock = client_sockfd; + rtapi_strxcpy(context->hostName, "Default"); + rtapi_strxcpy(context->version, "1.0"); + context->echo = true; + pthread_t thrd; + res = pthread_create(&thrd, NULL, readClient, context); + if (pthread_detach(thrd)) { + // no errno set by pthread_detach + rcs_print_error("sockMain: error by pthread_detach - ignored\n"); + } + if (res != 0) { + free(context); + } + } + } + if (res != 0) { + close(client_sockfd); + --sessions; + } + } } static void initMain() @@ -1246,12 +1256,9 @@ static void initMain() int main(int argc, char *argv[]) { - int opt; - pthread_t updateThread; - int res; - initMain(); // process local command line args + int opt; while((opt = getopt_long(argc, argv, "e:n:p:s:w:", longopts, NULL)) != -1) { switch(opt) { case 'e': snprintf(enablePWD, sizeof(enablePWD), "%s", optarg); break; @@ -1265,17 +1272,17 @@ int main(int argc, char *argv[]) // process emc command line args if (emcGetArgs(argc, argv) != 0) { - rcs_print_error("error in argument list\n"); - exit(1); + rcs_print_error("Error in argument list\n"); + exit(EXIT_FAILURE); } // get configuration information iniLoad(emc_inifile); initSockets(); // init NML if (tryNml() != 0) { - rcs_print_error("can't connect to emc\n"); - thisQuit(); - exit(1); + rcs_print_error("Cannot connect to EMC\n"); + freeAllBuffers(); + exit(EXIT_FAILURE); } // get current serial number, and save it for restoring when we quit // so as not to interfere with real operator interface @@ -1286,9 +1293,16 @@ int main(int argc, char *argv[]) signal(SIGINT, sigQuit); schedInit(); - res = pthread_create(&updateThread, NULL, checkQueue, (void *)NULL); - if(res != 0) { perror("pthread_create"); return 1; } + pthread_t updateThread; + int res = pthread_create(&updateThread, NULL, checkQueue, (void *)NULL); + pthread_detach(updateThread); + if(res != 0) { + perror("pthread_create"); + freeAllBuffers(); + return EXIT_FAILURE; + } if (useSockets) sockMain(); - return 0; + freeAllBuffers(); + return EXIT_SUCCESS; }