/* a simple commandline client for sics and sea Markus Zolliker Mar. 2003 first version Aug. 2005 visible difference between client for SICS and sea */ #include #include #include #include #include #include #include #include #include #include "term.h" #include "myc_err.h" #include "myc_str.h" #include "instr_hosts.h" typedef enum { NORMAL, SPY, CLIENT, NMODE } Mode; #define MAXMSG 8192 static char *clcname="six", *servername="sics"; static int clclen=3; static char host[128]; static char instr[32]; static int sock[2]; /* main socket and spy socket */ static int level=0; /* undefined level */ static int deflevel=0; static int remember=1; static int hidecom=1; static char user1[32]=""; static char pswd1[32]=""; static char user2[32]=""; static char pswd2[32]=""; static char status[MAXMSG]="Busy"; static int busy[2]; static int port; static char msg0[MAXMSG]; static char msg1[MAXMSG]; /* contains messages for main and spy */ static int pos0 = 0; static int pos1 = 0; static char *prefix[NMODE+1]={"", "| ", "# ", ""}; static int newline[NMODE+1]={1,1,1,1}; static char *sim=""; static char *pw=NULL; static char *us=""; void Put(Mode mode, char *text) { char chr; while (*text) { if (newline[mode]) { fputs("\033[0m", stdout); fputs(prefix[mode], stdout); newline[mode] = 0; if (strncmp(text, "ERROR:", 6) == 0) { fputs("\033[31;1m", stdout); } } chr = *text; text++; fputc(chr, stdout); if (chr == '\r' || chr == '\n') { newline[mode] = 1; } } fflush(stdout); } void PutC(char *text) { Put(CLIENT, text); } void PutClear(void) { Put(NMODE, ""); } void UsageNote(void) { PutC(" (for usage type: "); PutC(clcname); PutC(" help)\n"); } void Usage(int cmds_only) { if (!cmds_only) { PutC(" \n "); PutC(clcname); PutC( " commandline options:\n" " -x login as spy\n" " -u login as user\n" " -m login as manager\n" " help show this help text\n" " -a or a ask always for username/password, forget passwords\n" " -c or c use background color instead of # and |\n" " -s or s simulation mode (on some instruments)\n" " connect to the server for instr\n" " -h connect to a server on a different host\n" " -p connect to a server on a different port\n" " -n do only a minimal login (no check of instrument)\n" " no option login with default privilege\n" ); } PutC( "\n" " Special commands treated by "); PutC(clcname); PutC(" (these are no "); PutC(servername); PutC(" commands!)\n" "\n" " quit return to unix prompt\n" " exit return to unix prompt\n" " stop interrupt driving\n" " "); PutC(clcname); PutC(" help show this help text\n" " "); PutC(clcname); PutC(" def stop define a key for stop command\n" " "); PutC(clcname); PutC(" save save stop key and connection (host/port/access)\n" " + increase privilege\n" " - decrease privilege\n" "\n" " The "); PutC(servername); PutC(" status is shown, if it is not 'Eager to execute commands'.\n" " A shown status does not prohibit to enter commands.\n" "\n" " When "); PutC(servername); PutC(" is busy, a vertical bar '|' is shown at the left of the line.\n" " You may then enter more commands, but only under Spy privilege.\n" " All messages from the client are prefixed with a hash symbol '#'.\n" "\n" " Markus Zolliker, Aug. 2005\n" "\n" ); Put(-1, ""); /* clear colors */ } int CocCreateSockAdr( struct sockaddr_in *sockaddrPtr, /* Socket address */ const char *host, /* Host. NULL implies INADDR_ANY */ int port) /* Port number */ { struct hostent *hostent; /* Host database entry */ struct in_addr addr; /* For 64/32 bit madness */ (void) memset((char *) sockaddrPtr, '\0', sizeof(struct sockaddr_in)); sockaddrPtr->sin_family = AF_INET; sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF)); if (host == NULL || host[0]=='\0') { addr.s_addr = INADDR_ANY; } else { hostent = gethostbyname(host); if (hostent != NULL) { memcpy((char *) &addr, (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); } else { addr.s_addr = inet_addr(host); if (addr.s_addr == (unsigned long)-1) { return -1; /* error */ } } } /* * There is a rumor that this assignment may require care on * some 64 bit machines. */ sockaddrPtr->sin_addr.s_addr = addr.s_addr; return 0; } char* readWrite(int tmo, int skip, char *find) { int lbuf, go, n, shortTmo, l, socket; Mode idx; char *msg, *p, *q; char chr; static char result[MAXMSG]; int hide; static int laststat=0, laststatlen=0; /* * read from registered sockets and write to stdout until a key pressed, * a timeout occurred (tmo [msec]), or a TRANSACTIONFINISHED message * received. Handle also status and TRANSACTIONSTART messages. * * skip=0: skip response that does not match * skip=1: skip all responses * skip=2: skip all responses, quit on match * * if the response starts with the contents of find * the second part of the line is returned * if the response contain the contents of find, but not at the beginning * "1" is returned * if the text is not found, or find=NULL, an empty line is returned */ n=0; go=0; shortTmo = 0; str_copy(result, ""); socket=term_wait_socket(0, tmo); while (socket > 0) { idx = (socket == sock[1]); ERR_SI(lbuf=recv(socket, &chr, 1, 0)); if (lbuf==0) return "0"; while (lbuf>0) { if (chr == '\n') break; if (idx) { if (chr != '\r' && pos1 < MAXMSG-1) { /* cut lines longer than MAXMSG */ msg1[pos1]=chr; pos1++; } } else { if (chr != '\r' && pos0 < MAXMSG-1) { /* cut lines longer than MAXMSG */ msg0[pos0]=chr; pos0++; } } socket=term_wait_socket(0, tmo); if (socket <= 0) break; idx = (socket == sock[1]); ERR_SI(lbuf=recv(socket, &chr, 1, 0)); } hide=0; if (idx) { /* spy mode */ msg1[pos1] = '\0'; pos1 = 0; msg = msg1; if (!busy[1]) { /* skip spy messages if not busy (they will be doubled) */ hide = 1; } } else { msg0[pos0] = '\0'; pos0 = 0; msg = msg0; } /* printf("[%s]\n", msg); */ if (strncmp(msg, "TRANSACTIONSTART", 16) == 0) { busy[idx] = 1; hide = 1; } else if (strncmp(msg, "TRANSACTIONFINISHED", 19) == 0) { busy[idx] = 0; if (skip != 2) go = 1; shortTmo = 1; hide = 1; } else if (idx == 0 && strncmp(msg, "status = ", 9) == 0) { strcpy(status, msg+9); l = strlen(msg); if (status[0] == 'E') { } else if (laststat!=status[0] || laststatlen != l) { laststat = status[0]; laststatlen = l; } hide = 1; } if (idx == 0 && find != NULL) { p=strstr(msg, find); if (p!=NULL) { /* copy message to result */ if (p==msg) { str_copy(result, msg+strlen(find)); q=strchr(result,'\r'); if (q != NULL) *q='\0'; } else { str_copy(result, "1"); } hide=1; if (skip == 2) go = 1; } } if (hidecom && (skip || hide)) { /* skip text */ n+=strlen(msg); } else { term_clear(); Put(idx, msg); if (chr == '\n') { Put(idx, "\033[K\n"); } } if (go) break; socket = term_wait_socket(0, shortTmo?100:tmo); shortTmo = 0; } fflush(stdout); if (result==NULL) laststat=0; return result; OnError: return NULL; } void PrintCmd(char *buf, int mode) { Put(mode, "\r\033[K\n"); Put(mode, "\033[1;34m>> "); Put(mode, buf); Put(mode, "\033[K\033[0m\n"); } int sendCmd(int socket, char *cmd) { int l; char buf[256]; snprintf(buf, sizeof buf, "fulltransact %s\n", cmd); ERR_SI(l=send(socket, buf, strlen(buf), 0)); return l; OnError: return -1; } int scramble(char *buf) { int i, n, cnt, chr; int x; /* Scrambles a string. Twice scramble gives original. It does never convert a plain char to a ctrl char and all standard ascii-codes to special chars and vice versa. returns the number of special chars on output */ n=strlen(buf); x=23; cnt=0; for (i=0; i 0) scramble(buf); } void putscrambled(char *buf, FILE *fil) { char cvt[256]; str_copy(cvt, buf); scramble(cvt); fprintf(fil, "%s\n", cvt); } int setrights(int gotolevel) { char *p; char prefhead[128], buf[128]; FILE *fil; int ask; static char user[32]=""; static char pswd[32]=""; int savepw; if (pw == NULL) pw=pswd; str_copy(prefhead, clcname); str_append(prefhead, "."); str_append(prefhead, instr); if (remember) { fil=term_open_pref(0, prefhead, "r"); if (fil != NULL) { term_fgets(buf, sizeof(buf), fil); getscrambled(user1, sizeof(user1), fil); getscrambled(pswd1, sizeof(pswd1), fil); getscrambled(user2, sizeof(user2), fil); getscrambled(pswd2, sizeof(pswd2), fil); fclose(fil); } if (deflevel==0) { deflevel=buf[0]-'0'; if (deflevel<1 || deflevel>3) deflevel=2; } } else { deflevel=2; } if (gotolevel == 0) gotolevel = deflevel; if (deflevel == 0) deflevel = 3; if (level != gotolevel) { if (gotolevel==1) { if (user1[0]=='\0') { if (strcmp(clcname, "six") == 0) { str_copy(user1, "lnsmanager"); } else { str_copy(user2, servername); str_append(user2, "manager"); } } us=user1; pw=pswd1; } else if (gotolevel==2) { if (user2[0]=='\0') { if (strcmp(clcname, "six") == 0) { str_copy(user2, instr); str_lowcase(user2, user2); str_append(user2, "user"); } else { str_copy(user2, servername); str_append(user2, "user"); } } us=user2; pw=pswd2; } else if (gotolevel==3) { us="Spy"; pw="007"; } ask=1; savepw = 1; if (us[0]!='\0' && pw[0]!='\0' && remember) { sprintf(buf, "config rights %s %s", us, pw); ERR_I(sendCmd(sock[0], buf)); ERR_P(p=readWrite(12000,0,"Acknowledged")); PutC("."); if (*p=='\0') { if (0==strcmp(us, user1)) { user1[0]='\0'; pswd1[0]='\0'; } if (0==strcmp(us, user2)) { user2[0]='\0'; pswd2[0]='\0'; } } else { ask=0; savepw = 0; } } if (ask) { PutC("\r"); PutC(servername); PutC(" username"); if (us[0]!='\0') { PutC(" ["); PutC(us); PutC("]"); } PutC(": "); term_fgets(user, sizeof(user), stdin); newline[CLIENT]=1; if (0==strcmp(user, "quit")) return 1; if (0==strcmp(user, "exit")) return 1; if (user[0]=='\0') { str_copy(user, us); } PutC("password: "); term_fgets(pswd, sizeof(pswd), stdin); newline[CLIENT]=1; if (0==strcmp(pswd, "quit")) return 1; if (0==strcmp(pswd, "exit")) return 1; if (pswd[0]!='\0') { sprintf(buf, "config rights %s %s", user, pswd); ERR_I(sendCmd(sock[0], buf)); ERR_P(p=readWrite(12000,0,"Acknowledged")); if (*p=='\0') { /* no success */ gotolevel=3; if (0==strcmp(user, user1)) { user1[0]='\0'; pswd1[0]='\0'; } if (0==strcmp(user, user2)) { user2[0]='\0'; pswd2[0]='\0'; } pw=NULL; } else { /* success */ if (0==strcmp(user, user1)) { str_copy(pswd1, pswd); } if (0==strcmp(user, user2)) { str_copy(pswd2, pswd); } us=user; pw=pswd; } } else { gotolevel=3; } } level=3; ERR_I(sendCmd(sock[0], "config myrights")); ERR_P(p=readWrite(12000,1,"UserRights = ")); PutC("."); if (*p!='\0') { level=*p-'0'; } if (level==3) { us="Spy"; } } if (pw!=NULL && savepw) { if (level==1) { str_copy(user1, us); str_copy(pswd1, pw); if (0==strcmp(user1, user2)) { user2[0]='\0'; pswd2[0]='\0'; } } else if (level==2) { str_copy(user2, us); str_copy(pswd2, pw); if (0==strcmp(user2, user1)) { user1[0]='\0'; pswd1[0]='\0'; } } } fil=term_open_pref(0, prefhead, "w"); if (fil!=NULL) { fprintf(fil, "%d\n", deflevel); if (remember) { putscrambled(user1, fil); putscrambled(pswd1, fil); putscrambled(user2, fil); putscrambled(pswd2, fil); } fclose(fil); } return 0; OnError: return -1; } int SavePrefs(void) { char prefhead[128], name[128]; FILE *fil; ERR_SI(gethostname(name, sizeof name)); /* get ip name of this host */ str_copy(prefhead, clcname); str_append(prefhead, "_"); str_append(prefhead, sim); str_append(prefhead, name); ERR_SP(fil=term_open_pref(0, prefhead, "w")); fprintf(fil, "keys\n"); term_save_keys(fil, PutC); fclose(fil); return 0; OnError: return -1; } int LoadPrefs(void) { char prefhead[128], name[128], line[128]; FILE *fil; ERR_SI(gethostname(name, sizeof name)); /* get ip name of this host */ str_copy(prefhead, clcname); str_append(prefhead, "_"); str_append(prefhead, sim); str_append(prefhead, name); fil=term_open_pref(0, prefhead, "r"); if (fil) { while (term_fgets(line, sizeof(line), fil)) { if (0 == strcmp(line, "keys")) { term_load_keys(fil, PutC); } } fclose(fil); } return 0; OnError: return -1; } int Connect(void) { int sock; struct sockaddr_in sadr; char *p; ERR_I(CocCreateSockAdr(&sadr, host, port)); ERR_SI(sock=socket(AF_INET, SOCK_STREAM, 0)); term_reg_socket(sock); ERR_SI(connect(sock, (struct sockaddr *)&sadr, sizeof(sadr))); ERR_SI(send(sock, "Spy 007\n", 8, 0)); ERR_P(p=readWrite(12000,2,"Login O")); if (*p!='K') { /* looking for the 'K' of 'Login OK' */ PutC("rejected\n"); return 0; } return sock; OnError: return -1; } int main (int argc, char *argv[]) { struct hostent *ent; int iret, pos; fd_set mask; int i, j, gotolevel, sicslogin; int home; int savehist = 0; char buf[512], lbuf[16]; char stdPrompt[128]="", prompt[256]=""; char *p; char *bar; char *pnam[4]={"0", "MANAGER", "user", "spy"}; char *hostArg; char *subcmd; char *service; atexit(term_off); port=-1; home=0; sicslogin=1; deflevel=0; gotolevel=0; hostArg=NULL; sock[1]=0; /* do not yet connect 2nd connection */ for (i=1; i=argc) { PutC("missing host"); UsageNote(); return 0; } hostArg = argv[i]; } else if (0==strcmp(argv[i], "-p")) { i++; if (i>=argc) { PutC("missing port"); UsageNote(); return 0; } port=atoi(argv[i]); if (port == 0) { PutC("illegal port"); UsageNote(); return 0; } } else if (0==strcmp(argv[i], "-n")) { sicslogin=0; } else { if (strlen(argv[i])>=32) { PutC("argument too long"); UsageNote(); return 0; } else if (argv[i][0] == '-') { PutC("unknown option: "); PutC(argv[i]); UsageNote(); return 0; } else if (argv[i][1] == '\0' && isdigit(argv[i][0])) { port = atoi(argv[i]); } else { hostArg = argv[i]; } } } if (*sim != '\0') { service = "simsics"; } else { service = servername; } home = InstrHost(service, hostArg, instr, sizeof instr , host, sizeof host, &port); ent = gethostbyname(host); if (!ent) { if (hostArg == NULL || hostArg[0] <= ' ') { snprintf(host, sizeof host, "%s", getenv("HOST")); hostArg = host; } PutC(service); PutC(" on "); PutC(hostArg); PutC(" not found in InstrumentHostList\n"); p=getenv("InstrumentHostList"); if (p) { PutC("("); PutC(p); PutC(")\n"); } return 0; } snprintf(host, sizeof host, "%s", ent->h_name); LoadPrefs(); PutC( "---------------------------------------------------\n"); if (strcmp(clcname, "six") == 0) { PutC("six, a fast sics commandline client (doc: six help)\n"); if (port == -1) { if (*sim == '\0') { port = 2911; } else { port = 2927; } home = 1; } } else if (strcmp(clcname, "seacmd") == 0) { PutC("seacmd, a sea commandline client (doc: seacmd help)\n"); if (port == -1) { port = 8641; } else if (port > 0 && port < 9) { port += 8640; } home = 1; } else { PutC("graphcmd, a graph commandline client (doc: graphcmd help)\n"); if (port == -1) { port = 8741; } else if (port > 0 && port < 9) { port += 8740; } home = 1; } PutC( "---------------------------------------------------\n"); PutC("."); ERR_I(sock[0]=Connect()); PutC("."); if (sock[0] == 0) return 0; if (sicslogin) { ERR_I(sendCmd(sock[0], "Instrument")); ERR_P(p=readWrite(12000,0,"Instrument = ")); PutC("."); str_copy(instr, p); if (*instr=='\0') { PutC("can not detect instrument\n"); return 0; } str_lowcase(instr,instr); p=strchr(instr,' '); if (p!=NULL) *p='\0'; if (0==strcmp(instr,"SANS-II")) { str_copy(instr, "SANS2"); } if (!home) { deflevel = 3; gotolevel = 3; } ERR_I(i=setrights(gotolevel)); PutC("."); PutC("\rlogged in to "); PutC(servername); PutC(" as "); PutC(pnam[level]); PutC(" on "); PutC(instr); PutC("\n"); sprintf(stdPrompt, "%s[%s] ", clcname, instr); ERR_I(sendCmd(sock[0], "status interest")); ERR_P(readWrite(12000,0,"OK")); ERR_I(sendCmd(sock[0], "status")); ERR_P(readWrite(12000,0,NULL)); term_read_hist(clcname, instr); } else { sprintf(stdPrompt, "%s[%s] ", clcname, host); status[0]='E'; status[1]='\0'; term_read_hist(clcname, host); } iret=1; buf[0]='\0'; pos=0; savehist = 1; while (1) { if (busy[0]) { bar = prefix[SPY]; } else { bar = prefix[NORMAL]; } if (status[0] == 'E') { /* Eager to ... */ sprintf(prompt, "%s%s", bar, stdPrompt); } else { sprintf(prompt, "%s%s(%s) ", bar, stdPrompt, status); } FD_ZERO(&mask); FD_SET(sock[0], &mask); iret=term_get_line(buf, sizeof(buf)-2, &pos, prompt, &mask); if (iret==STDIN_FILENO) { /* input line terminated */ str_lowcase(lbuf, buf); if (0==strcmp(lbuf,"quit") || 0==strcmp(lbuf,"exit")) { PrintCmd(buf, CLIENT); break; } p=""; if (0==strcmp(lbuf,"stop")) { PrintCmd(buf, CLIENT); ERR_SI(send(sock[0], "INT1712 3\n", 10, 0)); buf[0]='\0'; } else if (0==strcmp(buf, "-") || 0==strcmp(buf, "--") || 0==strcmp(buf, "+") || 0==strcmp(buf, "++")) { PrintCmd(buf, CLIENT); if (level == 0) { gotolevel = 3; } else { gotolevel = level; } if (buf[0]=='-') { if (gotolevel<3) gotolevel++; } else { if (gotolevel>1) gotolevel--; } if (strlen(buf)==2) { deflevel=gotolevel; } term_off(); j = level; ERR_I(i=setrights(gotolevel)); /* level might be changed */ if (j!=level) { PutC("\rswitched to "); } else { PutC("\rremain at "); } PutC(pnam[level]); PutC(" privilege\n"); buf[0]='\0'; } else if (0==strncmp(buf, "help ", 5) && 0==strcmp(buf+5, clcname)) { PrintCmd(buf, CLIENT); Usage(1); buf[0]='\0'; } else if (0==strncmp(buf, clcname, clclen) && buf[clclen] == ' ') { subcmd = buf + clclen; while (*subcmd <= ' ' && *subcmd != 0) { subcmd++; } if (0==strcmp(subcmd, "help")) { PrintCmd(buf, CLIENT); Usage(0); buf[0]='\0'; } else if (0==strncmp(subcmd, "def ", 4)) { PrintCmd(buf, CLIENT); term_define_key(subcmd+4, 0); buf[0]='\0'; } else if (0==strcmp(subcmd, "def")) { PrintCmd(buf, CLIENT); term_define_key("", 0); buf[0]='\0'; } else if (0==strcmp(subcmd, "save")) { PrintCmd(buf, CLIENT); PutC("defaults for next calls to "); PutC(clcname); PutC(":\n"); ERR_I(SavePrefs()); if (level == 0) { gotolevel = 3; } else { gotolevel = level; } deflevel = gotolevel; ERR_I(setrights(gotolevel)); PutC(" login as "); PutC(us); PutC(" with "); PutC(pnam[level]); PutC(" privilege\n"); buf[0]='\0'; } else { PrintCmd(buf, CLIENT); PutC("ERROR: unknown command\n"); buf[0]='\0'; } } else if (buf[0] == '\0') { /* empty command */ PrintCmd(buf, CLIENT); ERR_P(p=readWrite(200,0,NULL)); /* just wait a little */ } if (buf[0] != '\0') { if (busy[0]) { if (!sock[1]) { ERR_I(sock[1] = Connect()); if (!sock[1]) { PutC("spy connection rejected\n"); } } if (busy[1]) { /* print in red bold */ PrintCmd(buf, CLIENT); PutC("\033[1;31mBUSY (already 2 commands pending)\033[0m\n"); buf[0]='\0'; } else if (sock[1]) { PrintCmd(buf, SPY); ERR_I(sendCmd(sock[1], buf)); } else { PrintCmd(buf, CLIENT); buf[0]='\0'; } } else { PrintCmd(buf, NORMAL); ERR_I(sendCmd(sock[0], buf)); } } if (buf[0] != '\0') { ERR_P(p=readWrite(500,0,NULL)); } buf[0]='\0'; pos=0; } else if (iret>0) { /* something arrived from sockets */ if (iret == sock[0] || iret == sock[1]) { ERR_P(p=readWrite(500,0,NULL)); } } else if (iret==-2) { goto SaveHist; } else if (iret<0) { ERR_MSG("term_get_line failed"); } if (strcmp(p, "0") == 0) { term_clear(); PutC("\nconnection lost\n"); goto SaveHist; } } if (savehist) term_save_hist(1); /* save history without last line */ goto Bye; OnError: ErrShow("end"); SaveHist: if (savehist) term_save_hist(0); /* save history with last line */ Bye: term_off(); if (stdPrompt[0]) { PutC("\nexit "); PutC(stdPrompt); PutC("\n"); PutClear(); /* clear colors */ } return 0; }