Vde_plug sets automagically the minimum permissions
[vde.git] / vde-2 / src / lib / libvdehist.c
blob078fbce7345efe6fdf78e170b9641c31581d35a7
1 /*
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
20 #define _GNU_SOURCE
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <poll.h>
27 #include <arpa/telnet.h>
29 #define BUFSIZE 1024
30 #define HISTORYSIZE 32
32 extern char *prompt;
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
46 struct vdehiststat {
47 unsigned char status;
48 unsigned char echo;
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) {
64 return NULL;
66 char * (* vdehist_logincmd)(char *cmd,int len,struct vdehiststat *s)
67 =nologin;
69 static int commonprefix(char *x, char *y,int maxlen)
71 int len=0;
72 while (*(x++)==*(y++) && len<maxlen)
73 len++;
74 return len;
77 static void showexpand(char *linebuf,int bufindex, int termfd)
79 char *buf;
80 size_t bufsize;
81 FILE *ms=open_memstream(&buf,&bufsize);
82 int nmatches=0;
83 if (ms) {
84 if (commandlist && bufindex>0) {
85 char **s=commandlist;
86 while (*s) {
87 if (strncmp(linebuf,*s,bufindex)==0) {
88 nmatches++;
89 fprintf(ms,"%s ",*s);
91 s++;
93 fprintf(ms,"\r\n");
95 fclose(ms);
96 if (nmatches > 1)
97 vdehist_termwrite(termfd,buf,strlen(buf));
98 free(buf);
102 static int tabexpand(char *linebuf,int bufindex,int maxlength)
104 if (commandlist && bufindex>0) {
105 char **s=commandlist;
106 int nmatches=0;
107 int len=0;
108 char *match=NULL;
109 while (*s) {
110 if (strncmp(linebuf,*s,bufindex)==0) {
111 nmatches++;
112 if (nmatches == 1) {
113 match=*s;
114 len=strlen(match);
115 } else
116 len=commonprefix(match,*s,len);
118 s++;
120 if (len > 0) {
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);
130 linebuf[len]=' ';
131 len++;
133 bufindex=len;
137 return bufindex;
140 #define CC_HEADER 0
141 #define CC_BODY 1
142 #define CC_TERM 2
143 #define MAX_KEYWORDS 128
145 static int qstrcmp(const void *a,const void *b)
147 return strcmp(*(char * const *)a,*(char * const *)b);
150 struct vh_readln {
151 int readbufsize;
152 int readbufindex;
153 char readbuf[BUFSIZE];
156 static char *vdehist_readln(int vdefd,char *linebuf,int size,struct vh_readln *vlb)
158 int i;
159 char lastch=' ';
160 struct pollfd wfd={vdefd,POLLIN|POLLHUP,0};
161 i=0;
162 do {
163 if (vlb->readbufindex==vlb->readbufsize) {
164 poll(&wfd,1,-1);
165 if ((vlb->readbufsize=read(vdefd,vlb->readbuf,BUFSIZE)) <= 0)
166 return NULL;
167 vlb->readbufindex=0;
169 if (vlb->readbuf[vlb->readbufindex]==' ' && lastch=='$' && vlb->readbufindex==vlb->readbufsize-1)
170 return NULL;
171 lastch=linebuf[i]=vlb->readbuf[vlb->readbufindex];
172 i++;vlb->readbufindex++;
173 } while (lastch!='\n' && i<size-1);
174 linebuf[i]=0;
175 return linebuf;
178 static void vdehist_create_commandlist(int vdefd)
180 char linebuf[BUFSIZE];
181 char *localclist[MAX_KEYWORDS];
182 int nkeywords=0;
183 int i,j;
184 struct vh_readln readlnbuf={0,0};
185 if (vdefd >= 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)
191 status=CC_BODY;
192 } else {
193 if (strncmp(linebuf,".\n",2) == 0)
194 status=CC_TERM;
195 else {
196 char *s=linebuf;
197 while (*s!=' ' && *s != 0)
198 s++;
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++)
209 if (i<nkeywords-1 &&
210 strncmp(localclist[i],localclist[i+1],strlen(localclist[i]))==0 &&
211 localclist[i+1][strlen(localclist[i])] == '/') {
212 free(localclist[i]); /*avoid menu*/
213 } else {
214 localclist[j]=localclist[i];
215 j++;
217 nkeywords=j;
219 nkeywords++;
220 commandlist=malloc(nkeywords*sizeof(char *));
221 if (commandlist) {
222 for (i=0;i<nkeywords;i++)
223 commandlist[i]=localclist[i];
224 commandlist[i]=NULL;
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)
232 int j;
233 int size=st->bufindex+(prompt_too != 0)*strlen(prompt);
234 char *buf;
235 size_t bufsize;
236 FILE *ms=open_memstream(&buf,&bufsize);
237 if (ms) {
238 for (j=0;j<size;j++)
239 fputc('\010',ms);
240 size=strlen(st->linebuf)+(prompt_too != 0)*strlen(prompt);
241 for (j=0;j<size;j++)
242 fputc(' ',ms);
243 for (j=0;j<size;j++)
244 fputc('\010',ms);
245 fclose(ms);
246 if (buf)
247 vdehist_termwrite(st->termfd,buf,bufsize);
248 free(buf);
252 static void redraw_line(struct vdehiststat *st,int prompt_too)
254 int j;
255 int tail=strlen(st->linebuf)-st->bufindex;
256 char *buf;
257 size_t bufsize;
258 FILE *ms=open_memstream(&buf,&bufsize);
259 if (ms) {
260 if (prompt_too)
261 fprintf(ms,"%s%s",prompt,st->linebuf);
262 else
263 fprintf(ms,"%s",st->linebuf);
264 for (j=0;j<tail;j++)
265 fputc('\010',ms);
266 fclose(ms);
267 if (buf)
268 vdehist_termwrite(st->termfd,buf,bufsize);
269 free(buf);
273 void vdehist_mgmt_to_term(struct vdehiststat *st)
275 char buf[BUFSIZE+1];
276 int n=0,ib=0;
277 /* erase the input line */
278 erase_line(st,1);
279 /* if the communication with the manager object holds, print the output*/
280 //fprintf(stderr,"mgmt2term\n");
281 if (st->mgmtfd) {
282 n=vdehist_vderead(st->mgmtfd,buf,BUFSIZE);
283 //fprintf(stderr,"mgmt2term n=%d\n",n);
284 buf[n]=0;
285 while (n>0) {
286 for(ib=0;ib<n;ib++)
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';
293 (st->vbufindex)++;
294 if (st->vindata) {
295 if (st->vlinebuf[0]=='.' && st->vlinebuf[1]=='\r')
296 st->vindata=0;
297 else
298 vdehist_termwrite(st->termfd,st->vlinebuf,(st->vbufindex));
299 } else {
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])))
307 message++;
308 if (strncmp(message,"0000",4)==0)
309 st->vindata=1;
310 else if (isdigit(message[1]) &&
311 isdigit(message[2]) &&
312 isdigit(message[3])) {
313 if(message[0]=='1') {
314 message+=5;
315 vdehist_termwrite(st->termfd,message,strlen(message));
316 } else if (message[0]=='3') {
317 message+=5;
318 vdehist_termwrite(st->termfd,"** DBG MSG: ",12);
319 vdehist_termwrite(st->termfd,(message),strlen(message));
323 (st->vbufindex)=0;
326 n=vdehist_vderead(st->mgmtfd,buf,BUFSIZE);
329 /* redraw the input line */
330 redraw_line(st,1);
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);
340 if (cmd==NULL)
341 return 0;
343 while (*cmd == ' ' || *cmd == '\t')
344 cmd++;
345 if (strncmp(cmd,"logout",6)==0)
346 return 1;
347 else {
348 if (*cmd != 0) {
349 write(st->mgmtfd,st->linebuf,st->bufindex);
350 if (strncmp(cmd,"shutdown",8)==0)
351 return 2;
353 vdehist_termwrite(st->termfd,"\r\n",2);
354 vdehist_termwrite(st->termfd,prompt,strlen(prompt));
356 return 0;
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 *));
381 st->history[0]=NULL;
384 static void telnet_option_send3(int fd,int action,int object)
386 char opt[3];
387 opt[0]=0xff;
388 opt[1]=action;
389 opt[2]=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;
398 st->echo=0;
399 telnet_option_send3(st->termfd,WILL,TELOPT_ECHO);
401 int skip=2;
402 s++;
403 action_n_object=((*s)<<8) + (*(s+1));
404 switch (action_n_object) {
405 case (DO<<8) + TELOPT_ECHO:
406 //printf("okay echo\n");
407 st->echo=1;
408 break;
409 case (WILL<<8) + TELOPT_ECHO:
410 telnet_option_send3(st->termfd,DONT,TELOPT_ECHO);
411 telnet_option_send3(st->termfd,WILL,TELOPT_ECHO);
412 break;
413 case (DO<<8) + TELOPT_SGA:
414 //printf("do sga -> okay will sga\n");
415 telnet_option_send3(st->termfd,WILL,TELOPT_SGA);
416 break;
417 case (WILL<<8) + TELOPT_TTYPE:
418 //printf("will tty -> dont tty\n");
419 telnet_option_send3(st->termfd,DONT,TELOPT_TTYPE);
420 break;
421 default:
422 //printf("not managed yet %x %x\n",*s,*(s+1));
423 if (*s == WILL)
424 telnet_option_send3(st->termfd,DONT,*(s+1));
425 else if (*s == DO)
426 telnet_option_send3(st->termfd,WONT,*(s+1));
428 return skip;
431 int vdehist_term_to_mgmt(struct vdehiststat *st)
433 unsigned char buf[BUFSIZE];
434 int n,i,rv=0;
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]);
437 if (n==0)
438 return 1;
439 else if (n<0)
440 return n;
441 else {
442 for (i=0;i<n && strlen(st->linebuf)<BUFSIZE;i++) {
443 if (buf[i] == 0xff && buf[i+1] == 0xff)
444 i++;
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);
448 } else
450 if(buf[i] == 0x1b) {
451 /* ESCAPE! */
452 if (buf[i+1]=='[' && st->status == HIST_COMMAND) {
453 st->edited=1;
454 switch (buf[i+2]) {
455 case 'A': //fprintf(stderr,"UP\n");
456 erase_line(st,0);
457 put_history(st);
458 get_history(1,st);
459 redraw_line(st,0);
460 st->bufindex=strlen(st->linebuf);
461 break;
462 case 'B': //fprintf(stderr,"DOWN\n");
463 erase_line(st,0);
464 put_history(st);
465 get_history(-1,st);
466 redraw_line(st,0);
467 break;
468 case 'C': //fprintf(stderr,"RIGHT\n");
469 if (st->linebuf[st->bufindex] != '\0') {
470 vdehist_termwrite(st->termfd,"\033[C",3);
471 (st->bufindex)++;
473 break;
474 case 'D': //fprintf(stderr,"LEFT\n");
475 if (st->bufindex > 0) {
476 vdehist_termwrite(st->termfd,"\033[D",3);
477 (st->bufindex)--;
479 break;
481 i+=3;
483 else
484 i+=2;/* ignored */
485 } else if(buf[i] < 0x20 && !(buf[i] == '\n' || buf[i] == '\r')) {
486 /*ctrl*/
487 if (buf[i] == 4) /*ctrl D is a shortcut for UNIX people! */ {
488 rv=1;
489 break;
491 switch (buf[i]) {
492 case 3: /*ctrl C cleans the current buffer */
493 erase_line(st,0);
494 st->bufindex=0;
495 st->linebuf[(st->bufindex)]=0;
496 break;
497 case 12: /* ctrl L redraw */
498 erase_line(st,1);
499 redraw_line(st,1);
500 break;
501 case 1: /* ctrl A begin of line */
502 erase_line(st,0);
503 st->bufindex=0;
504 redraw_line(st,0);
505 break;
506 case 5: /* ctrl E endofline */
507 erase_line(st,0);
508 st->bufindex=strlen(st->linebuf);
509 redraw_line(st,0);
510 case '\t': /* tab */
511 if (st->lastchar== '\t') {
512 erase_line(st,1);
513 showexpand(st->linebuf,st->bufindex,st->termfd);
514 redraw_line(st,1);
515 } else {
516 erase_line(st,0);
517 st->bufindex=tabexpand(st->linebuf,st->bufindex,BUFSIZE);
518 redraw_line(st,0);
520 break;
522 } else if(buf[i] == 0x7f) {
523 if(st->bufindex > 0) {
524 char *x;
525 (st->bufindex)--;
526 x=st->linebuf+st->bufindex;
527 memmove(x,x+1,strlen(x));
528 if (st->echo && !(st->status & HIST_PASSWDFLAG)) {
529 if (st->edited)
530 vdehist_termwrite(st->termfd,"\010\033[P",4);
531 else
532 vdehist_termwrite(st->termfd,"\010 \010",3);
535 } else {
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') {
542 if (buf[i]=='\n') {
543 if (st->status == HIST_COMMAND) {
544 st->histindex=0;
545 put_history(st);
546 if (strlen(st->linebuf) > 0)
547 shift_history(st);
549 st->bufindex=strlen(st->linebuf);
550 if ((rv=hist_sendcmd(st)) != 0)
551 break;
552 st->bufindex=st->edited=st->histindex=0;
553 st->linebuf[(st->bufindex)]=0;
554 } else {
555 char *x;
556 x=st->linebuf+st->bufindex;
557 memmove(x+1,x,strlen(x)+1);
558 st->linebuf[(st->bufindex)++]=buf[i];
562 st->lastchar=buf[i];
565 return rv;
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));
573 if (st) {
574 int i;
575 if (mgmtfd < 0)
576 st->status=HIST_NOCMD;
577 else
578 st->status=HIST_COMMAND;
579 st->echo=1;
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;
584 st->termfd=termfd;
585 st->mgmtfd=mgmtfd;
586 for (i=0;i<HISTORYSIZE;i++)
587 st->history[i]=0;
589 return st;
592 void vdehist_free(struct vdehiststat *st)
594 if (st) {
595 int i;
596 for (i=0;i<HISTORYSIZE;i++)
597 if(st->history[i])
598 free(st->history[i]);
599 free(st);
603 int vdehist_getstatus(struct vdehiststat *st)
605 return st->status;
608 void vdehist_setstatus(struct vdehiststat *st,int status)
610 st->status=status;
614 int vdehist_gettermfd(struct vdehiststat *st)
616 return st->termfd;
620 int vdehist_getmgmtfd(struct vdehiststat *st)
622 return st->mgmtfd;
625 void vdehist_setmgmtfd(struct vdehiststat *st,int mgmtfd)
627 st->mgmtfd=mgmtfd;