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>
29 #include <vdecommon.h>
32 #define HISTORYSIZE 32
36 static char **commandlist
;
38 typedef ssize_t (* ssize_fun
)();
39 ssize_fun vdehist_vderead
=read
;
40 ssize_fun vdehist_vdewrite
=write
;
41 ssize_fun vdehist_termread
=read
;
42 ssize_fun vdehist_termwrite
=write
;
44 #define HIST_COMMAND 0x0
45 #define HIST_NOCMD 0x1
46 #define HIST_PASSWDFLAG 0x80
51 unsigned char telnetprotocol
;
52 unsigned char edited
; /* the linebuf has been modified (left/right arrow)*/
53 unsigned char vindata
; /* 1 when in_data... (0000 end with .)*/
54 char lastchar
; /* for double tag*/
55 char linebuf
[BUFSIZE
]; /*line buffer from the user*/
56 int bufindex
; /*current editing position on the buf */
57 char vlinebuf
[BUFSIZE
+1]; /*line buffer from vde*/
58 int vbufindex
; /*current editing position on the buf */
59 char *history
[HISTORYSIZE
]; /*history of the previous commands*/
60 int histindex
; /* index on the history (changed with up/down arrows) */
61 int termfd
; /* fd to the terminal */
62 int mgmtfd
; /* mgmt fd to vde_switch */
65 char * nologin(char *cmd
,int len
,struct vdehiststat
*st
) {
68 char * (* vdehist_logincmd
)(char *cmd
,int len
,struct vdehiststat
*s
)
71 static int commonprefix(char *x
, char *y
,int maxlen
)
74 while (*(x
++)==*(y
++) && len
<maxlen
)
79 static void showexpand(char *linebuf
,int bufindex
, int termfd
)
83 FILE *ms
=open_memstream(&buf
,&bufsize
);
86 if (commandlist
&& bufindex
>0) {
89 if (strncmp(linebuf
,*s
,bufindex
)==0) {
99 vdehist_termwrite(termfd
,buf
,strlen(buf
));
104 static int tabexpand(char *linebuf
,int bufindex
,int maxlength
)
106 if (commandlist
&& bufindex
>0) {
107 char **s
=commandlist
;
112 if (strncmp(linebuf
,*s
,bufindex
)==0) {
118 len
=commonprefix(match
,*s
,len
);
123 int alreadymatch
=commonprefix(linebuf
,match
,len
);
124 //fprintf(stderr,"TAB %s %d -> %s %d already %d\n",linebuf,bufindex,match,len,alreadymatch);
125 if ((len
-alreadymatch
)+strlen(linebuf
) < maxlength
) {
126 memmove(linebuf
+len
,linebuf
+alreadymatch
,
127 strlen(linebuf
+alreadymatch
)+1);
128 memcpy(linebuf
+alreadymatch
,match
+alreadymatch
,len
-alreadymatch
);
129 if (nmatches
== 1 && linebuf
[len
] != ' ' && strlen(linebuf
)+1 < maxlength
) {
130 memmove(linebuf
+len
+1,linebuf
+len
,
131 strlen(linebuf
+len
)+1);
146 static int qstrcmp(const void *a
,const void *b
)
148 return strcmp(*(char * const *)a
,*(char * const *)b
);
154 char readbuf
[BUFSIZE
];
157 static char *vdehist_readln(int vdefd
,char *linebuf
,int size
,struct vh_readln
*vlb
)
161 struct pollfd wfd
={vdefd
,POLLIN
|POLLHUP
,0};
164 if (vlb
->readbufindex
==vlb
->readbufsize
) {
166 if ((vlb
->readbufsize
=read(vdefd
,vlb
->readbuf
,BUFSIZE
)) <= 0)
170 if (vlb
->readbuf
[vlb
->readbufindex
]==' ' && lastch
=='$' && vlb
->readbufindex
==vlb
->readbufsize
-1)
172 lastch
=linebuf
[i
]=vlb
->readbuf
[vlb
->readbufindex
];
173 i
++;vlb
->readbufindex
++;
174 } while (lastch
!='\n' && i
<size
-1);
179 /* create the commandlist (from the output of the help command) */
180 static void vdehist_create_commandlist(int vdefd
)
182 char linebuf
[BUFSIZE
];
183 struct vh_readln readlnbuf
={0,0};
186 char *lastcommand
=NULL
;
187 /* use a memstream to create the array.
188 add (char *) elements by fwrite */
189 FILE *ms
=open_memstream(&buf
,&bufsize
);
190 if (ms
&& vdefd
>= 0) {
191 int status
=CC_HEADER
;
192 vdehist_vdewrite(vdefd
,"help\n",5);
193 while (status
!= CC_TERM
&& vdehist_readln(vdefd
,linebuf
,BUFSIZE
,&readlnbuf
) != NULL
) {
194 if (status
== CC_HEADER
) {
195 if (strncmp(linebuf
,"------------",12) == 0)
198 if (strncmp(linebuf
,".\n",2) == 0)
202 while (*s
!=' ' && *s
!= 0)
204 *s
=0; /* take the first token */
205 /* test for menu header */
207 if (strncmp(lastcommand
,linebuf
,strlen(lastcommand
)) == 0 &&
208 linebuf
[strlen(lastcommand
)] == '/')
211 fwrite(&lastcommand
, sizeof(char *), 1, ms
);
213 lastcommand
=strdup(linebuf
);
218 fwrite(&lastcommand
, sizeof(char *), 1, ms
);
220 fwrite(&lastcommand
, sizeof(char *), 1, ms
);
222 commandlist
=(char **)buf
;
223 qsort(commandlist
,(bufsize
/ sizeof(char *))-1,sizeof(char *),qstrcmp
);
227 static void erase_line(struct vdehiststat
*st
,int prompt_too
)
230 int size
=st
->bufindex
+(prompt_too
!= 0)*strlen(prompt
);
233 FILE *ms
=open_memstream(&buf
,&bufsize
);
237 size
=strlen(st
->linebuf
)+(prompt_too
!= 0)*strlen(prompt
);
244 vdehist_termwrite(st
->termfd
,buf
,bufsize
);
249 static void redraw_line(struct vdehiststat
*st
,int prompt_too
)
252 int tail
=strlen(st
->linebuf
)-st
->bufindex
;
255 FILE *ms
=open_memstream(&buf
,&bufsize
);
258 fprintf(ms
,"%s%s",prompt
,st
->linebuf
);
260 fprintf(ms
,"%s",st
->linebuf
);
265 vdehist_termwrite(st
->termfd
,buf
,bufsize
);
270 void vdehist_mgmt_to_term(struct vdehiststat
*st
)
274 /* erase the input line */
276 /* if the communication with the manager object holds, print the output*/
277 //fprintf(stderr,"mgmt2term\n");
279 n
=vdehist_vderead(st
->mgmtfd
,buf
,BUFSIZE
);
280 //fprintf(stderr,"mgmt2term n=%d\n",n);
285 st
->vlinebuf
[(st
->vbufindex
)++]=buf
[ib
];
286 if (buf
[ib
] == '\n') {
287 st
->vlinebuf
[(st
->vbufindex
)-1]='\r';
288 st
->vlinebuf
[(st
->vbufindex
)]='\n';
289 st
->vlinebuf
[(st
->vbufindex
)+1]='\0';
292 if (st
->vlinebuf
[0]=='.' && st
->vlinebuf
[1]=='\r')
295 vdehist_termwrite(st
->termfd
,st
->vlinebuf
,(st
->vbufindex
));
297 char *message
=st
->vlinebuf
;
298 //fprintf(stderr,"MSG1 \"%s\"\n",message);
299 while (*message
!= '\0' &&
300 !(isdigit(message
[0]) &&
301 isdigit(message
[1]) &&
302 isdigit(message
[2]) &&
303 isdigit(message
[3])))
305 if (strncmp(message
,"0000",4)==0)
307 else if (isdigit(message
[1]) &&
308 isdigit(message
[2]) &&
309 isdigit(message
[3])) {
310 if(message
[0]=='1') {
312 vdehist_termwrite(st
->termfd
,message
,strlen(message
));
313 } else if (message
[0]=='3') {
315 vdehist_termwrite(st
->termfd
,"** DBG MSG: ",12);
316 vdehist_termwrite(st
->termfd
,(message
),strlen(message
));
323 n
=vdehist_vderead(st
->mgmtfd
,buf
,BUFSIZE
);
326 if (commandlist
== NULL
&& st
->mgmtfd
>= 0)
327 vdehist_create_commandlist(st
->mgmtfd
);
328 /* redraw the input line */
332 static int hist_sendcmd(struct vdehiststat
*st
)
334 char *cmd
=st
->linebuf
;
335 if (st
->status
!= HIST_COMMAND
) {
336 cmd
=vdehist_logincmd(cmd
,st
->bufindex
,st
);
337 if (commandlist
== NULL
&& st
->mgmtfd
>= 0)
338 vdehist_create_commandlist(st
->mgmtfd
);
342 while (*cmd
== ' ' || *cmd
== '\t')
344 if (strncmp(cmd
,"logout",6)==0)
348 write(st
->mgmtfd
,st
->linebuf
,st
->bufindex
);
349 if (strncmp(cmd
,"shutdown",8)==0)
352 vdehist_termwrite(st
->termfd
,"\r\n",2);
353 vdehist_termwrite(st
->termfd
,prompt
,strlen(prompt
));
355 if (commandlist
!= NULL
&&
356 (strncmp(cmd
,"plugin/add",10) == 0 ||
357 strncmp(cmd
,"plugin/del",10) == 0)) {
364 static void put_history(struct vdehiststat
*st
)
366 if(st
->history
[st
->histindex
])
367 free(st
->history
[st
->histindex
]);
368 st
->history
[st
->histindex
]=strdup(st
->linebuf
);
371 static void get_history(int change
,struct vdehiststat
*st
)
373 st
->histindex
+= change
;
374 if(st
->histindex
< 0) st
->histindex
=0;
375 if(st
->histindex
>= HISTORYSIZE
) st
->histindex
=HISTORYSIZE
-1;
376 if(st
->history
[st
->histindex
] == NULL
) (st
->histindex
)--;
377 strcpy(st
->linebuf
,st
->history
[st
->histindex
]);
378 st
->bufindex
=strlen(st
->linebuf
);
381 static void shift_history(struct vdehiststat
*st
)
383 if (st
->history
[HISTORYSIZE
-1] != NULL
)
384 free(st
->history
[HISTORYSIZE
-1]);
385 memmove(st
->history
+1,st
->history
,(HISTORYSIZE
-1)*sizeof(char *));
389 static void telnet_option_send3(int fd
,int action
,int object
)
395 vdehist_termwrite(fd
,opt
,3);
398 static int telnet_options(struct vdehiststat
*st
,unsigned char *s
)
400 register int action_n_object
;
401 if (st
->telnetprotocol
== 0) {
402 st
->telnetprotocol
=1;
404 telnet_option_send3(st
->termfd
,WILL
,TELOPT_ECHO
);
408 action_n_object
=((*s
)<<8) + (*(s
+1));
409 switch (action_n_object
) {
410 case (DO
<<8) + TELOPT_ECHO
:
411 //printf("okay echo\n");
414 case (WILL
<<8) + TELOPT_ECHO
:
415 telnet_option_send3(st
->termfd
,DONT
,TELOPT_ECHO
);
416 telnet_option_send3(st
->termfd
,WILL
,TELOPT_ECHO
);
418 case (DO
<<8) + TELOPT_SGA
:
419 //printf("do sga -> okay will sga\n");
420 telnet_option_send3(st
->termfd
,WILL
,TELOPT_SGA
);
422 case (WILL
<<8) + TELOPT_TTYPE
:
423 //printf("will tty -> dont tty\n");
424 telnet_option_send3(st
->termfd
,DONT
,TELOPT_TTYPE
);
427 //printf("not managed yet %x %x\n",*s,*(s+1));
429 telnet_option_send3(st
->termfd
,DONT
,*(s
+1));
431 telnet_option_send3(st
->termfd
,WONT
,*(s
+1));
436 int vdehist_term_to_mgmt(struct vdehiststat
*st
)
438 unsigned char buf
[BUFSIZE
];
440 n
=vdehist_termread(st
->termfd
,buf
,BUFSIZE
);
441 //printf("termto mgmt N%d %x %x %x %x\n",n,buf[0],buf[1],buf[2],buf[3]);
447 for (i
=0;i
<n
&& strlen(st
->linebuf
)<BUFSIZE
;i
++) {
448 if (buf
[i
] == 0xff && buf
[i
+1] == 0xff)
450 if(buf
[i
]==0) buf
[i
]='\n'; /*telnet encode \n as a 0 when in raw mode*/
451 if (buf
[i
] == 0xff && buf
[i
+1] != 0xff) {
452 i
+=telnet_options(st
,buf
+i
);
457 if (buf
[i
+1]=='[' && st
->status
== HIST_COMMAND
) {
460 case 'A': //fprintf(stderr,"UP\n");
465 st
->bufindex
=strlen(st
->linebuf
);
467 case 'B': //fprintf(stderr,"DOWN\n");
473 case 'C': //fprintf(stderr,"RIGHT\n");
474 if (st
->linebuf
[st
->bufindex
] != '\0') {
475 vdehist_termwrite(st
->termfd
,"\033[C",3);
479 case 'D': //fprintf(stderr,"LEFT\n");
480 if (st
->bufindex
> 0) {
481 vdehist_termwrite(st
->termfd
,"\033[D",3);
490 } else if(buf
[i
] < 0x20 && !(buf
[i
] == '\n' || buf
[i
] == '\r')) {
492 if (buf
[i
] == 4) /*ctrl D is a shortcut for UNIX people! */ {
497 case 3: /*ctrl C cleans the current buffer */
500 st
->linebuf
[(st
->bufindex
)]=0;
502 case 12: /* ctrl L redraw */
506 case 1: /* ctrl A begin of line */
511 case 5: /* ctrl E endofline */
513 st
->bufindex
=strlen(st
->linebuf
);
516 if (st
->lastchar
== '\t') {
518 showexpand(st
->linebuf
,st
->bufindex
,st
->termfd
);
522 st
->bufindex
=tabexpand(st
->linebuf
,st
->bufindex
,BUFSIZE
);
527 } else if(buf
[i
] == 0x7f) {
528 if(st
->bufindex
> 0) {
531 x
=st
->linebuf
+st
->bufindex
;
532 memmove(x
,x
+1,strlen(x
));
533 if (st
->echo
&& !(st
->status
& HIST_PASSWDFLAG
)) {
535 vdehist_termwrite(st
->termfd
,"\010\033[P",4);
537 vdehist_termwrite(st
->termfd
,"\010 \010",3);
541 if (st
->echo
&& !(st
->status
& HIST_PASSWDFLAG
)) {
542 if (st
->edited
&& buf
[i
] >= ' ')
543 vdehist_termwrite(st
->termfd
,"\033[@",3);
544 vdehist_termwrite(st
->termfd
,&(buf
[i
]),1);
546 if (buf
[i
] != '\r') {
548 if (st
->status
== HIST_COMMAND
) {
551 if (strlen(st
->linebuf
) > 0)
554 st
->bufindex
=strlen(st
->linebuf
);
555 if ((rv
=hist_sendcmd(st
)) != 0)
557 st
->bufindex
=st
->edited
=st
->histindex
=0;
558 st
->linebuf
[(st
->bufindex
)]=0;
561 x
=st
->linebuf
+st
->bufindex
;
562 memmove(x
+1,x
,strlen(x
)+1);
563 st
->linebuf
[(st
->bufindex
)++]=buf
[i
];
573 struct vdehiststat
*vdehist_new(int termfd
,int mgmtfd
) {
574 struct vdehiststat
*st
;
575 if (commandlist
== NULL
&& mgmtfd
>= 0)
576 vdehist_create_commandlist(mgmtfd
);
577 st
=malloc(sizeof(struct vdehiststat
));
581 st
->status
=HIST_NOCMD
;
583 st
->status
=HIST_COMMAND
;
585 st
->telnetprotocol
=0;
586 st
->bufindex
=st
->edited
=st
->histindex
=st
->vbufindex
=st
->vindata
=st
->lastchar
=0;
587 st
->linebuf
[(st
->bufindex
)]=0;
588 st
->vlinebuf
[(st
->vbufindex
)]=0;
591 for (i
=0;i
<HISTORYSIZE
;i
++)
597 void vdehist_free(struct vdehiststat
*st
)
601 for (i
=0;i
<HISTORYSIZE
;i
++)
603 free(st
->history
[i
]);
608 int vdehist_getstatus(struct vdehiststat
*st
)
613 void vdehist_setstatus(struct vdehiststat
*st
,int status
)
619 int vdehist_gettermfd(struct vdehiststat
*st
)
625 int vdehist_getmgmtfd(struct vdehiststat
*st
)
630 void vdehist_setmgmtfd(struct vdehiststat
*st
,int mgmtfd
)