/* Copyright (C) 2003 GFRN systems This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The latest version of this program may be found at http://CQiNet.sourceforge.net $Log: tbdcmd.c,v $ Revision 1.7 2007/07/02 13:43:35 wb6ymh Corrected C99 C declaration mixed with code in tbdcmd.c that's not accepted by pre C89 compilers. This is the first time I've run into code gcc 4.x liked that other compilers don't, it's usually the other way around. Revision 1.6 2007/06/29 14:30:22 wb6ymh 1. Modified code to use 5199 by default instead of 5198 when involked as tbdchat, tlbchat, or chat. 2. Modified all hard coded references to "tbdcmd" to use the current program alias. 3. Added code to suppress stations lists received from conference bridges. 4. Added -S command line switch to disable station list suppression logic. Revision 1.5 2006/08/21 16:32:33 wb6ymh Added code to set rlim_max to RLIM_INFINITY. Fixes ages old "Unable to enable core dumps" warning which occured on recent versions of the Linux kernel. Revision 1.4 2005/01/09 00:00:30 wb6ymh 1. Added -t option to enable display of a timestamp before each line of chat text. 2. Fix typo and add "warning" to error message about failure to enable core (not "code") dumps. Apparently Fedora has changed something as this always fails with "Invalid argument". 3. Modified new tbdcmd misfeature that suppressed "error" codes of zero to only suppress them when tbdcmd is run interactively. 4. Corrected bug caused by uninitialized select timeout structure in tbdcmd. This bug only occurred on a few systems, thanks to ke4tte for providing access to one where it did so I could debug it. Revision 1.3 2004/12/01 14:30:27 wb6ymh 1. Don't watch stdin unless it's a tty! 2. Don't suppress "error" codes of zero unless we're running interactively. Revision 1.2 2004/11/29 00:38:50 wb6ymh 1. Added timeout to avoid hanging if thebridge doesn't respond. 2. Added support for text chat. Revision 1.1 2003/08/16 14:27:27 wb6ymh Initial import: command line utility to send commands to thebridge. */ #include "common.h" #ifndef _WIN32 // FreeBSD, Linux, etc.. #include #ifdef HAVE_UNISTD_H #include #endif #ifdef STDC_HEADERS #include #endif #ifdef TIME_WITH_SYS_TIME #include #include #else #ifdef HAVE_SYS_TIME_H #include #else #include #endif #endif #include #ifdef HAVE_STRINGS_H #include #endif #include #include #include #include #include #include #else // Windoze #include #include #include #include #include #endif #include "ilink.h" #include "tbd.h" #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE !FALSE #endif #define RESP_TO 10 // wait for 10 seconds from responses from tbd typedef union { struct sockaddr s; struct sockaddr_in i; #define ADDR i.sin_addr.s_addr #define PORT i.sin_port } IPAdrUnion; #ifdef _WIN32 // Windoze WSADATA wsad; #endif int bQuiet = 0; int bSlient = 0; int bTimeStamp = 0; int bAllowStationList = FALSE; int Port = ILINK_RTP_PORT; #ifndef HAVE_GETOPT // globals needed by getopt int optind = 1; int optopt; char *optarg; int getopt(int argc,char **argv,char *opts); #endif char *TimeT2String(time_t time); int IsStationList(char *Line); void Usage(char *Command) { printf("Usage: %s [-p] [-q] [-s] [-S] [-t] [commands] \n",Command); printf("\t-p - Specify port (default %d).\n",Port); printf("\t-q - Quiet, numeric result code only.\n"); printf("\t-s - Slient, suppress banner.\n"); printf("\t-S - Allow conference bridge's station list (default is suppress).\n"); printf("\t-t - Add timestamps to text chat messages.\n"); } int main(int argc, char* argv[]) { int Ret = 0; int Option; SOCKET MySocket; IPAdrUnion HisAdr; char Line[1024]; char *cp; int BytesRxed; int BytesSent; int FirstArg = 1; int ArgCount = argc; int WritePoint; int Wrote; int i; fd_set ReadFDset; struct timeval SelTO; int Selret; int bResponseWait = FALSE; int bDisplayPrompt = TRUE; int bInteractive = FALSE; time_t TimeNow; time_t TimeCmdSent; int bChat = FALSE; char *Command = strrchr(argv[0],PATH_SEP); #ifndef _WIN32 struct rlimit rlim; // On some systems core dumps are disabled by default, // enable them so we can debug this thing ! rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; if(setrlimit(RLIMIT_CORE, &rlim)) { perror("Warning - Unable to enable core dumps: "); } #else /* Initialize Winsock*/ if(WSAStartup(0x0101, &wsad) != 0) { printf("Fatal error: unable to initialize Winsock\n"); exit(3); } #endif if(Command != NULL) { Command++; } else { Command = argv[0]; } if(strcmp(Command,"tbdchat") == 0 || strcmp(Command,"tlbchat") == 0 || strcmp(Command,"chat") == 0) { Port = ILINK_RTCP_PORT; bChat = TRUE; } while((Option = getopt(argc, argv, "p:qtsS")) != -1 && !Ret) { FirstArg++; ArgCount--; switch(Option) { case 'p': // port if(sscanf(optarg,"%d",&Port) != 1 || Port <= 0 || Port > 0xffff) { printf("%s: Error invalid port number \"%s\"\n",Command,optarg); exit(3); } break; case 'q': // quiet bQuiet = TRUE; break; case 's': // slient bSlient = TRUE; break; case 'S': // Allow station list bAllowStationList = TRUE; break; case 't': // Timestamps bTimeStamp = TRUE; break; case '?': Usage(Command); exit(3); } } if(ArgCount == 1) { bInteractive = TRUE; } if(!bQuiet && !bSlient && !Ret) { printf("%s Version " VERSION " compiled "__DATE__ " " __TIME__"\n", Command); } if(ArgCount > 1) { WritePoint = 0; for(i = 0; i < ArgCount -1 ; i++) { Wrote = snprintf(&Line[WritePoint],sizeof(Line)-WritePoint-1,"%s%s", WritePoint == 0 ? "" : " ",argv[FirstArg+i]); if(Wrote > 0 && WritePoint + Wrote < sizeof(Line)) { WritePoint += Wrote; } else { // Buffer overflow break; } } Line[WritePoint] = 0; } else { Line[0] = 0; } if((MySocket = socket(AF_INET,SOCK_DGRAM,0)) == SOCKET_ERROR) { printf("Socket() failed.\n"); } else { HisAdr.i.sin_family = AF_INET; HisAdr.PORT = htons((u_short) Port); HisAdr.ADDR = inet_addr("127.0.0.1"); do { if(bInteractive && bDisplayPrompt && Line[0] == 0) { bDisplayPrompt = FALSE; printf("%s> ",bChat ? "chat" : "tbd"); fflush(stdout); } if(strlen(Line) > 0) { BytesSent = sendto(MySocket,Line,strlen(Line)+1,0, &HisAdr.s,sizeof(HisAdr)); if(BytesSent == SOCKET_ERROR) { Ret = ERROR_CODE; printf("Error: sendto() failed (%d)\n",Ret); break; } Line[0] = 0; bResponseWait = TRUE; time(&TimeCmdSent); } FD_ZERO(&ReadFDset); FD_SET(MySocket,&ReadFDset); #ifdef _WIN32 // Windoze can't select on standard in 'cuz it ain't so standard // so wake up every 100 milliseconds to poll the keyboard SelTO.tv_sec = 0; SelTO.tv_usec = 100000; Selret = select(FD_SETSIZE,&ReadFDset,NULL,NULL,&SelTO); #else if(bInteractive) { FD_SET((SOCKET) fileno(stdin),&ReadFDset); } if(bResponseWait) { // Wait a second for the response SelTO.tv_sec = 1; SelTO.tv_usec = 0; Selret = select(FD_SETSIZE,&ReadFDset,NULL,NULL,&SelTO); } else { Selret = select(FD_SETSIZE,&ReadFDset,NULL,NULL,NULL); } #endif #ifndef _WIN32 if(FD_ISSET(fileno(stdin),&ReadFDset)) #else if(kbhit()) #endif { if(fgets(Line,sizeof(Line),stdin) == NULL) { break; } if((cp = strchr(Line,'\n')) != NULL) { *cp = 0; } if(strlen(Line) == 0) { // exit on empty line break; } bDisplayPrompt = TRUE; } if(FD_ISSET(MySocket,&ReadFDset)) { BytesRxed = recv(MySocket,Line,sizeof(Line)-1,0); if(BytesRxed == SOCKET_ERROR) { Ret = ERROR_CODE; printf("Error: recv() failed (%d)\n",Ret); break; } Line[BytesRxed] = 0; Ret = atoi(Line); if(Ret != TBD_CHAT_TEXT) { bResponseWait = FALSE; } if(bQuiet) { if((cp = strchr(Line,'\n')) != NULL) { // Only want a single line, truncate the output cp[1] = 0; } } if(bInteractive) { // Interactive mode, overwrite the prompt printf("\r \r"); } // Remove any extra line feeds at the end of the line. Thebridge's // output contains a single , but text chat messages have two. if((cp = strrchr(Line,'\n')) != NULL && cp[-1] == '\n') { // Only want a single linefeed, remove the second one *cp = 0; } time(&TimeNow); if(Ret == TBD_CHAT_TEXT && bTimeStamp) { printf("%s: ",TimeT2String(TimeNow)); } if(Ret == TBD_CHAT_TEXT_SENT && bInteractive) { // Interactive mode and this is just an ack for a message we // sent, don't display it } else if(!bAllowStationList && bInteractive && IsStationList(Line)) { // Suppress station list. } else if(bInteractive && (Ret == 0 || Ret == TBD_CHAT_TEXT) && (cp = strchr(Line,'\n')) != NULL) { // return code is not interesting, don't display it printf("%s",&cp[1]); } else { printf("%s",Line); } Line[0] = 0; bDisplayPrompt = TRUE; } if(bResponseWait && (TimeNow - TimeCmdSent) >= RESP_TO) { // Timeout waiting for a response from tbd Ret = TBD_ERR_TIMEOUT; printf("%d\n",Ret); break; } } while(bInteractive); } return Ret; } char *TimeT2String(time_t time) { struct tm *pTm; static char TimeString[] = "mm/dd hh:mm"; if(time != 0) { pTm = localtime(&time); if(pTm != NULL) { snprintf(TimeString,sizeof(TimeString),"%2d/%02d %2d:%02d", pTm->tm_mon + 1,pTm->tm_mday,pTm->tm_hour,pTm->tm_min); } else { return ""; } } else { return ""; } return TimeString; } // If we have ">CONF " on the first line then assume this is a station list int IsStationList(char *Line) { char *cp; int Ret = FALSE; if((cp = strchr(Line,'\n')) != NULL && (cp = strchr(cp+1,'\n')) != NULL) { *cp = 0; if(strstr(Line,">CONF ") != NULL) { Ret = TRUE; } *cp = '\n'; } return Ret; } #ifndef HAVE_GETOPT /* * getopt a wonderful little function that handles the command line. * available courtesy of AT&T. */ int getopt(int argc,char **argv,char *opts) { static int sp = 1; register int c; register char *cp; if(sp == 1) if(optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') return(EOF); else if(strcmp(argv[optind], "--") == 0) { optind++; return(EOF); } optopt = c = argv[optind][sp]; if(c == ':' || (cp=strchr(opts, c)) == NULL) { printf("%s: illegal option -- ", argv[0],c); if(argv[optind][++sp] == '\0') { optind++; sp = 1; } return('?'); } if(*++cp == ':') { if(argv[optind][sp+1] != '\0') optarg = &argv[optind++][sp+1]; else if(++optind >= argc) { printf("%s: option requires an argument -- ", argv[0],c); sp = 1; return('?'); } else optarg = argv[optind++]; sp = 1; } else { if(argv[optind][++sp] == '\0') { sp = 1; optind++; } optarg = NULL; } return(c); } #endif