2 * libvdehist - A library to manage history and command completion for vde mgmt protocol
3 * Copyright (C) 2006 Renzo Davoli, University of Bologna
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation version 2.1 of the License, or (at
8 * your option) any later version.
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include <arpa/telnet.h>
30 #define HISTORYSIZE 32
34 static char **commandlist
;
36 typedef ssize_t (* ssize_fun
)();
37 ssize_fun vdehist_vderead
=read
;
38 ssize_fun vdehist_vdewrite
=write
;
39 ssize_fun vdehist_termread
=read
;
40 ssize_fun vdehist_termwrite
=write
;
42 #define HIST_COMMAND 0x0
43 #define HIST_NOCMD 0x1
44 #define HIST_PASSWDFLAG 0x80
49 unsigned char telnetprotocol
;
50 unsigned char edited
; /* the linebuf has been modified (left/right arrow)*/
51 unsigned char vindata
; /* 1 when in_data... (0000 end with .)*/
52 char lastchar
; /* for double tag*/
53 char linebuf
[BUFSIZE
]; /*line buffer from the user*/
54 int bufindex
; /*current editing position on the buf */
55 char vlinebuf
[BUFSIZE
+1]; /*line buffer from vde*/
56 int vbufindex
; /*current editing position on the buf */
57 char *history
[HISTORYSIZE
]; /*history of the previous commands*/
58 int histindex
; /* index on the history (changed with up/down arrows) */
59 int termfd
; /* fd to the terminal */
60 int mgmtfd
; /* mgmt fd to vde_switch */
63 char * nologin(char *cmd
,int len
,struct vdehiststat
*st
) {
66 char * (* vdehist_logincmd
)(char *cmd
,int len
,struct vdehiststat
*s
)
69 static int commonprefix(char *x
, char *y
,int maxlen
)
72 while (*(x
++)==*(y
++) && len
<maxlen
)
77 static void showexpand(char *linebuf
,int bufindex
, int termfd
)
81 FILE *ms
=open_memstream(&buf
,&bufsize
);
84 if (commandlist
&& bufindex
>0) {
87 if (strncmp(linebuf
,*s
,bufindex
)==0) {
97 vdehist_termwrite(termfd
,buf
,strlen(buf
));
102 static int tabexpand(char *linebuf
,int bufindex
,int maxlength
)
104 if (commandlist
&& bufindex
>0) {
105 char **s
=commandlist
;
110 if (strncmp(linebuf
,*s
,bufindex
)==0) {
116 len
=commonprefix(match
,*s
,len
);
121 int alreadymatch
=commonprefix(linebuf
,match
,len
);
122 //fprintf(stderr,"TAB %s %d -> %s %d already %d\n",linebuf,bufindex,match,len,alreadymatch);
123 if ((len
-alreadymatch
)+strlen(linebuf
) < maxlength
) {
124 memmove(linebuf
+len
,linebuf
+alreadymatch
,
125 strlen(linebuf
+alreadymatch
)+1);
126 memcpy(linebuf
+alreadymatch
,match
+alreadymatch
,len
-alreadymatch
);
127 if (nmatches
== 1 && linebuf
[len
] != ' ' && strlen(linebuf
)+1 < maxlength
) {
128 memmove(linebuf
+len
+1,linebuf
+len
,
129 strlen(linebuf
+len
)+1);
143 #define MAX_KEYWORDS 128
145 static int qstrcmp(const void *a
,const void *b
)
147 return strcmp(*(char * const *)a
,*(char * const *)b
);
153 char readbuf
[BUFSIZE
];
156 static char *vdehist_readln(int vdefd
,char *linebuf
,int size
,struct vh_readln
*vlb
)
160 struct pollfd wfd
={vdefd
,POLLIN
|POLLHUP
,0};
163 if (vlb
->readbufindex
==vlb
->readbufsize
) {
165 if ((vlb
->readbufsize
=read(vdefd
,vlb
->readbuf
,BUFSIZE
)) <= 0)
169 if (vlb
->readbuf
[vlb
->readbufindex
]==' ' && lastch
=='$' && vlb
->readbufindex
==vlb
->readbufsize
-1)
171 lastch
=linebuf
[i
]=vlb
->readbuf
[vlb
->readbufindex
];
172 i
++;vlb
->readbufindex
++;
173 } while (lastch
!='\n' && i
<size
-1);
178 static void vdehist_create_commandlist(int vdefd
)
180 char linebuf
[BUFSIZE
];
181 char *localclist
[MAX_KEYWORDS
];
184 struct vh_readln readlnbuf
={0,0};
186 int status
=CC_HEADER
;
187 vdehist_vdewrite(vdefd
,"help\n",5);
188 while (status
!= CC_TERM
&& vdehist_readln(vdefd
,linebuf
,BUFSIZE
,&readlnbuf
) != NULL
) {
189 if (status
== CC_HEADER
) {
190 if (strncmp(linebuf
,"------------",12) == 0)
193 if (strncmp(linebuf
,".\n",2) == 0)
197 while (*s
!=' ' && *s
!= 0)
199 *s
=0; /* take the first token */
200 localclist
[nkeywords
]=strdup(linebuf
);
201 if (nkeywords
<MAX_KEYWORDS
) nkeywords
++;
205 while (vdehist_readln(vdefd
,linebuf
,BUFSIZE
,&readlnbuf
) != NULL
)
207 qsort(localclist
,nkeywords
,sizeof(char *),qstrcmp
);
208 for (i
=j
=0; i
<nkeywords
; i
++)
210 strncmp(localclist
[i
],localclist
[i
+1],strlen(localclist
[i
]))==0 &&
211 localclist
[i
+1][strlen(localclist
[i
])] == '/') {
212 free(localclist
[i
]); /*avoid menu*/
214 localclist
[j
]=localclist
[i
];
220 commandlist
=malloc(nkeywords
*sizeof(char *));
222 for (i
=0;i
<nkeywords
;i
++)
223 commandlist
[i
]=localclist
[i
];
226 //fprintf(stderr,"%d\n",nkeywords);
227 //fprintf(stderr,"%s %s\n",commandlist[0],commandlist[1]);
230 static void erase_line(struct vdehiststat
*st
,int prompt_too
)
233 int size
=st
->bufindex
+(prompt_too
!= 0)*strlen(prompt
);
236 FILE *ms
=open_memstream(&buf
,&bufsize
);
240 size
=strlen(st
->linebuf
)+(prompt_too
!= 0)*strlen(prompt
);
247 vdehist_termwrite(st
->termfd
,buf
,bufsize
);
252 static void redraw_line(struct vdehiststat
*st
,int prompt_too
)
255 int tail
=strlen(st
->linebuf
)-st
->bufindex
;
258 FILE *ms
=open_memstream(&buf
,&bufsize
);
261 fprintf(ms
,"%s%s",prompt
,st
->linebuf
);
263 fprintf(ms
,"%s",st
->linebuf
);
268 vdehist_termwrite(st
->termfd
,buf
,bufsize
);
273 void vdehist_mgmt_to_term(struct vdehiststat
*st
)
277 /* erase the input line */
279 /* if the communication with the manager object holds, print the output*/
280 //fprintf(stderr,"mgmt2term\n");
282 n
=vdehist_vderead(st
->mgmtfd
,buf
,BUFSIZE
);
283 //fprintf(stderr,"mgmt2term n=%d\n",n);
288 st
->vlinebuf
[(st
->vbufindex
)++]=buf
[ib
];
289 if (buf
[ib
] == '\n') {
290 st
->vlinebuf
[(st
->vbufindex
)-1]='\r';
291 st
->vlinebuf
[(st
->vbufindex
)]='\n';
292 st
->vlinebuf
[(st
->vbufindex
)+1]='\0';
295 if (st
->vlinebuf
[0]=='.' && st
->vlinebuf
[1]=='\r')
298 vdehist_termwrite(st
->termfd
,st
->vlinebuf
,(st
->vbufindex
));
300 char *message
=st
->vlinebuf
;
301 //fprintf(stderr,"MSG1 \"%s\"\n",message);
302 while (*message
!= '\0' &&
303 !(isdigit(message
[0]) &&
304 isdigit(message
[1]) &&
305 isdigit(message
[2]) &&
306 isdigit(message
[3])))
308 if (strncmp(message
,"0000",4)==0)
310 else if (isdigit(message
[1]) &&
311 isdigit(message
[2]) &&
312 isdigit(message
[3])) {
313 if(message
[0]=='1') {
315 vdehist_termwrite(st
->termfd
,message
,strlen(message
));
316 } else if (message
[0]=='3') {
318 vdehist_termwrite(st
->termfd
,"** DBG MSG: ",12);
319 vdehist_termwrite(st
->termfd
,(message
),strlen(message
));
326 n
=vdehist_vderead(st
->mgmtfd
,buf
,BUFSIZE
);
329 /* redraw the input line */
333 static int hist_sendcmd(struct vdehiststat
*st
)
335 char *cmd
=st
->linebuf
;
336 if (st
->status
!= HIST_COMMAND
) {
337 cmd
=vdehist_logincmd(cmd
,st
->bufindex
,st
);
338 if (commandlist
== NULL
&& st
->mgmtfd
>= 0)
339 vdehist_create_commandlist(st
->mgmtfd
);
343 while (*cmd
== ' ' || *cmd
== '\t')
345 if (strncmp(cmd
,"logout",6)==0)
349 write(st
->mgmtfd
,st
->linebuf
,st
->bufindex
);
350 if (strncmp(cmd
,"shutdown",8)==0)
353 vdehist_termwrite(st
->termfd
,"\r\n",2);
354 vdehist_termwrite(st
->termfd
,prompt
,strlen(prompt
));
359 static void put_history(struct vdehiststat
*st
)
361 if(st
->history
[st
->histindex
])
362 free(st
->history
[st
->histindex
]);
363 st
->history
[st
->histindex
]=strdup(st
->linebuf
);
366 static void get_history(int change
,struct vdehiststat
*st
)
368 st
->histindex
+= change
;
369 if(st
->histindex
< 0) st
->histindex
=0;
370 if(st
->histindex
>= HISTORYSIZE
) st
->histindex
=HISTORYSIZE
-1;
371 if(st
->history
[st
->histindex
] == NULL
) (st
->histindex
)--;
372 strcpy(st
->linebuf
,st
->history
[st
->histindex
]);
373 st
->bufindex
=strlen(st
->linebuf
);
376 static void shift_history(struct vdehiststat
*st
)
378 if (st
->history
[HISTORYSIZE
-1] != NULL
)
379 free(st
->history
[HISTORYSIZE
-1]);
380 memmove(st
->history
+1,st
->history
,(HISTORYSIZE
-1)*sizeof(char *));
384 static void telnet_option_send3(int fd
,int action
,int object
)
390 vdehist_termwrite(fd
,opt
,3);
393 static int telnet_options(struct vdehiststat
*st
,unsigned char *s
)
395 register int action_n_object
;
396 if (st
->telnetprotocol
== 0) {
397 st
->telnetprotocol
=1;
399 telnet_option_send3(st
->termfd
,WILL
,TELOPT_ECHO
);
403 action_n_object
=((*s
)<<8) + (*(s
+1));
404 switch (action_n_object
) {
405 case (DO
<<8) + TELOPT_ECHO
:
406 //printf("okay echo\n");
409 case (WILL
<<8) + TELOPT_ECHO
:
410 telnet_option_send3(st
->termfd
,DONT
,TELOPT_ECHO
);
411 telnet_option_send3(st
->termfd
,WILL
,TELOPT_ECHO
);
413 case (DO
<<8) + TELOPT_SGA
:
414 //printf("do sga -> okay will sga\n");
415 telnet_option_send3(st
->termfd
,WILL
,TELOPT_SGA
);
417 case (WILL
<<8) + TELOPT_TTYPE
:
418 //printf("will tty -> dont tty\n");
419 telnet_option_send3(st
->termfd
,DONT
,TELOPT_TTYPE
);
422 //printf("not managed yet %x %x\n",*s,*(s+1));
424 telnet_option_send3(st
->termfd
,DONT
,*(s
+1));
426 telnet_option_send3(st
->termfd
,WONT
,*(s
+1));
431 int vdehist_term_to_mgmt(struct vdehiststat
*st
)
433 unsigned char buf
[BUFSIZE
];
435 n
=vdehist_termread(st
->termfd
,buf
,BUFSIZE
);
436 //printf("termto mgmt N%d %x %x %x %x\n",n,buf[0],buf[1],buf[2],buf[3]);
442 for (i
=0;i
<n
&& strlen(st
->linebuf
)<BUFSIZE
;i
++) {
443 if (buf
[i
] == 0xff && buf
[i
+1] == 0xff)
445 if(buf
[i
]==0) buf
[i
]='\n'; /*telnet encode \n as a 0 when in raw mode*/
446 if (buf
[i
] == 0xff && buf
[i
+1] != 0xff) {
447 i
+=telnet_options(st
,buf
+i
);
452 if (buf
[i
+1]=='[' && st
->status
== HIST_COMMAND
) {
455 case 'A': //fprintf(stderr,"UP\n");
460 st
->bufindex
=strlen(st
->linebuf
);
462 case 'B': //fprintf(stderr,"DOWN\n");
468 case 'C': //fprintf(stderr,"RIGHT\n");
469 if (st
->linebuf
[st
->bufindex
] != '\0') {
470 vdehist_termwrite(st
->termfd
,"\033[C",3);
474 case 'D': //fprintf(stderr,"LEFT\n");
475 if (st
->bufindex
> 0) {
476 vdehist_termwrite(st
->termfd
,"\033[D",3);
485 } else if(buf
[i
] < 0x20 && !(buf
[i
] == '\n' || buf
[i
] == '\r')) {
487 if (buf
[i
] == 4) /*ctrl D is a shortcut for UNIX people! */ {
492 case 3: /*ctrl C cleans the current buffer */
495 st
->linebuf
[(st
->bufindex
)]=0;
497 case 12: /* ctrl L redraw */
501 case 1: /* ctrl A begin of line */
506 case 5: /* ctrl E endofline */
508 st
->bufindex
=strlen(st
->linebuf
);
511 if (st
->lastchar
== '\t') {
513 showexpand(st
->linebuf
,st
->bufindex
,st
->termfd
);
517 st
->bufindex
=tabexpand(st
->linebuf
,st
->bufindex
,BUFSIZE
);
522 } else if(buf
[i
] == 0x7f) {
523 if(st
->bufindex
> 0) {
526 x
=st
->linebuf
+st
->bufindex
;
527 memmove(x
,x
+1,strlen(x
));
528 if (st
->echo
&& !(st
->status
& HIST_PASSWDFLAG
)) {
530 vdehist_termwrite(st
->termfd
,"\010\033[P",4);
532 vdehist_termwrite(st
->termfd
,"\010 \010",3);
536 if (st
->echo
&& !(st
->status
& HIST_PASSWDFLAG
)) {
537 if (st
->edited
&& buf
[i
] >= ' ')
538 vdehist_termwrite(st
->termfd
,"\033[@",3);
539 vdehist_termwrite(st
->termfd
,&(buf
[i
]),1);
541 if (buf
[i
] != '\r') {
543 if (st
->status
== HIST_COMMAND
) {
546 if (strlen(st
->linebuf
) > 0)
549 st
->bufindex
=strlen(st
->linebuf
);
550 if ((rv
=hist_sendcmd(st
)) != 0)
552 st
->bufindex
=st
->edited
=st
->histindex
=0;
553 st
->linebuf
[(st
->bufindex
)]=0;
556 x
=st
->linebuf
+st
->bufindex
;
557 memmove(x
+1,x
,strlen(x
)+1);
558 st
->linebuf
[(st
->bufindex
)++]=buf
[i
];
568 struct vdehiststat
*vdehist_new(int termfd
,int mgmtfd
) {
569 struct vdehiststat
*st
;
570 if (commandlist
== NULL
&& mgmtfd
>= 0)
571 vdehist_create_commandlist(mgmtfd
);
572 st
=malloc(sizeof(struct vdehiststat
));
576 st
->status
=HIST_NOCMD
;
578 st
->status
=HIST_COMMAND
;
580 st
->telnetprotocol
=0;
581 st
->bufindex
=st
->edited
=st
->histindex
=st
->vbufindex
=st
->vindata
=st
->lastchar
=0;
582 st
->linebuf
[(st
->bufindex
)]=0;
583 st
->vlinebuf
[(st
->vbufindex
)]=0;
586 for (i
=0;i
<HISTORYSIZE
;i
++)
592 void vdehist_free(struct vdehiststat
*st
)
596 for (i
=0;i
<HISTORYSIZE
;i
++)
598 free(st
->history
[i
]);
603 int vdehist_getstatus(struct vdehiststat
*st
)
608 void vdehist_setstatus(struct vdehiststat
*st
,int status
)
614 int vdehist_gettermfd(struct vdehiststat
*st
)
620 int vdehist_getmgmtfd(struct vdehiststat
*st
)
625 void vdehist_setmgmtfd(struct vdehiststat
*st
,int mgmtfd
)