Man page of slirpvde updated.
[vde.git] / vdetelweb / vdetelweb.c
blobfcfd0184516eb27826f8d25e968834f0459a67e1
1 /*
2 * VDETELWEB: VDE telnet and WEB interface
4 * vdetelweb.c: main
5 *
6 * Copyright 2005,2008 Virtual Square Team University of Bologna - Italy
7 * 2005 written by Renzo Davoli
8 * --pidfile/-p and cleanup management by Mattia Belletti (C) 2004
9 * (copied from vde_switch code).
10 * 2008 updated Renzo Davoli
11 * 2008 sha1sum by Marco Dalla Via
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 * $Id$
30 #include <config.h>
31 #include <stdio.h>
32 #include <signal.h>
33 #include <stdarg.h>
34 #include <syslog.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <netdb.h>
38 #include <libgen.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/select.h>
43 #include <sys/poll.h>
44 #include <sys/wait.h>
45 #include <sys/utsname.h>
46 #include <linux/un.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <string.h>
50 #include <getopt.h>
51 #include <sys/stat.h>
52 #include <fcntl.h>
53 #include "vdetelweb.h"
54 #include <lwipv6.h>
55 #include <limits.h>
57 int daemonize;
58 int telnet;
59 int web;
60 char *mgmt;
61 char *banner;
62 char *progname;
63 char *prompt;
64 int logok;
65 static char *passwd;
66 static char *pidfile = NULL;
67 static char pidfile_path[_POSIX_PATH_MAX];
68 struct stack *lwipstack;
70 #define MAXFD 16
71 #define HASH_SIZE 40
72 int npfd=0;
73 struct pollfd pfd[MAXFD];
74 voidfun fpfd[MAXFD];
75 void *status[MAXFD];
77 #define ROOTCONFFILE "/etc/vde/vdetelwebrc"
79 /* This will be prefixed by getenv("HOME") */
80 #define USERCONFFILE "/.vde/vdetelwebrc"
82 void printlog(int priority, const char *format, ...)
84 va_list arg;
86 va_start (arg, format);
88 if (logok)
89 vsyslog(priority,format,arg);
90 else {
91 fprintf(stderr,"%s: ",progname);
92 vfprintf(stderr,format,arg);
93 fprintf(stderr,"\n");
95 va_end (arg);
99 static void cleanup(void)
101 if (lwipstack)
102 lwip_stack_free(lwipstack);
103 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
104 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
108 int sha1passwdok(const char *pw) {
109 char buf[HASH_SIZE + 1];
110 int pfd_fc[2];
111 int pfd_cf[2];
112 pid_t pid;
114 pipe(pfd_fc);
115 pipe(pfd_cf);
116 pid = fork();
118 if (!pid) {
119 close(pfd_fc[1]);
120 close(pfd_cf[0]);
121 dup2(pfd_fc[0], STDIN_FILENO);
122 dup2(pfd_cf[1], STDOUT_FILENO);
124 execl("/usr/bin/sha1sum", "/usr/bin/sha1sum", NULL);
125 exit(1);
126 } else {
127 close(pfd_cf[1]);
128 close(pfd_fc[0]);
130 write(pfd_fc[1], pw, strlen(pw));
131 close(pfd_fc[1]);
132 read(pfd_cf[0], buf, sizeof(buf));
133 close(pfd_cf[0]);
135 waitpid(pid, NULL, 0);
136 return (strncmp(buf,passwd,strlen(passwd))==0);
140 static void sig_handler(int sig)
142 cleanup();
143 signal(sig, SIG_DFL);
144 kill(getpid(), sig);
147 static void setsighandlers(void)
149 /* setting signal handlers.
150 * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
151 * * ignores all the others signals which could cause termination. */
152 struct { int sig; const char *name; int ignore; } signals[] = {
153 { SIGHUP, "SIGHUP", 0 },
154 { SIGINT, "SIGINT", 0 },
155 { SIGPIPE, "SIGPIPE", 1 },
156 { SIGALRM, "SIGALRM", 1 },
157 { SIGTERM, "SIGTERM", 0 },
158 { SIGUSR1, "SIGUSR1", 1 },
159 { SIGUSR2, "SIGUSR2", 1 },
160 { SIGPOLL, "SIGPOLL", 1 },
161 { SIGPROF, "SIGPROF", 1 },
162 { SIGVTALRM, "SIGVTALRM", 1 },
163 #ifdef SIGSTKFLT
164 { SIGSTKFLT, "SIGSTKFLT", 1 },
165 #endif
166 { SIGIO, "SIGIO", 1 },
167 { SIGPWR, "SIGPWR", 1 },
168 #ifdef SIGUNUSED
169 { SIGUNUSED, "SIGUNUSED", 1 },
170 #endif
171 { 0, NULL, 0 }
174 int i;
175 for(i = 0; signals[i].sig != 0; i++)
176 if(signal(signals[i].sig,
177 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
178 perror("Setting handler");
181 static void usage(char *progname) {
182 fprintf (stderr,"Usage: %s [-w] [-t] [-d] [-n nodename] [-p pidfile] mgmt_socket\n"
183 " %s [--web] [--telnet] [--daemon] [--nodename nodename] [--pidfile pidfile] mgmt_socket\n",progname,progname);
184 exit(-1);
187 void setprompt(char *ctrl,char *nodename)
189 char buf[BUFSIZE];
190 if (nodename==NULL) {
191 struct utsname un;
192 uname(&un);
193 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",un.nodename,ctrl);
194 } else
195 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",nodename,ctrl);
196 prompt=strdup(buf);
199 int openextravdem(void)
201 struct sockaddr_un sun;
202 int fd,n;
203 char buf[BUFSIZE+1];
204 sun.sun_family=PF_UNIX;
205 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",mgmt);
206 fd=socket(PF_UNIX,SOCK_STREAM,0);
207 if (connect(fd,(struct sockaddr *)(&sun),sizeof(sun)) < 0) {
208 printlog(LOG_ERR,"Error connecting to the management socket '%s': %s", mgmt, strerror(errno));
209 return(-1);
211 if ((n=read(fd,buf,BUFSIZE))<=0) {
212 printlog(LOG_ERR,"banner %s",strerror(errno));
213 return(-1);
215 return fd;
218 int openvdem(char *mgmt,char *progname, struct netif **nif,char *nodename)
220 struct sockaddr_un sun;
221 int fd,n;
222 char buf[BUFSIZE+1],*line2,*ctrl;
223 sun.sun_family=PF_UNIX;
224 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",mgmt);
225 fd=socket(PF_UNIX,SOCK_STREAM,0);
226 if (connect(fd,(struct sockaddr *)(&sun),sizeof(sun)) < 0) {
227 printlog(LOG_ERR,"Error connecting to the management socket '%s': %s", mgmt, strerror(errno));
228 exit(-1);
230 if ((n=read(fd,buf,BUFSIZE))<=0) {
231 printlog(LOG_ERR,"Error reading banner from VDE switch: %s",strerror(errno));
232 exit(-1);
234 buf[n]=0;
235 if ((ctrl=rindex(buf,'\n')) != NULL)
236 *ctrl=0;
237 banner=strdup(buf);
238 write(fd,"ds/showinfo\n",13);
239 if ((n=read(fd,buf,BUFSIZE))<=0) {
240 printlog(LOG_ERR,"Error reading ctl socket from VDE switch: %s",strerror(errno));
241 exit(-1);
243 buf[n]=0;
244 if ((line2=index(buf,'\n')) == NULL) {
245 printlog(LOG_ERR,"Error parsing first line of ctl socket information");
246 exit(-1);
248 line2++;
249 if (strncmp(line2,"ctl dir ",8) != 0) {
250 printlog(LOG_ERR,"Error parsing ctl socket information");
251 exit(-1);
253 for(ctrl=line2+8;*ctrl!='\n' && ctrl<buf+n;ctrl++)
255 *ctrl=0;
256 ctrl=line2+8;
257 setprompt(ctrl,nodename);
258 strcat(ctrl,"[0]");
259 *nif=lwip_vdeif_add(lwipstack,ctrl);
260 if (*nif == NULL) {
261 printlog(LOG_ERR,"Cannot connect to the VDE switch");
262 exit(-1);
264 lwip_ifup(*nif);
266 return fd;
269 static void bitno2mask(unsigned char *addr,int bitno,int len)
271 int i;
272 for(i=0;i<len;i++,bitno -= 8) {
273 if (bitno >= 8)
274 addr[i]=255;
275 else if (bitno <= 0)
276 addr[i]=0;
277 else
278 addr[i]=256 - (1<<(8-bitno));
282 static void sockaddr2ip_6addr(struct ip_addr *ipaddrp,unsigned char *addr)
284 IP6_ADDR(ipaddrp,
285 (addr[0]<<8)|addr[1],
286 (addr[2]<<8)|addr[3],
287 (addr[4]<<8)|addr[5],
288 (addr[6]<<8)|addr[7],
289 (addr[8]<<8)|addr[9],
290 (addr[10]<<8)|addr[11],
291 (addr[12]<<8)|addr[13],
292 (addr[14]<<8)|addr[15]);
295 static void readip(char *arg,struct netif *nif,int af)
297 char *bit=rindex(arg,'/');
298 if (bit == 0)
299 printlog(LOG_ERR,"IP addresses must include the netmask i.e. addr/maskbits");
300 else {
301 int bitno=atoi(bit+1);
302 *bit=0;
303 struct addrinfo *res,hint;
304 struct ip_addr ipaddr,netmask;
305 int err;
306 memset(&hint,0,sizeof(hint));
307 hint.ai_family=af;
308 if ((err=getaddrinfo(arg,NULL,&hint,&res))!=0)
309 printlog(LOG_ERR,"IP address %s error %s",arg,gai_strerror(err));
310 else {
311 switch(res->ai_family) {
312 case PF_INET:
314 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
315 int addrh=ntohl(in->sin_addr.s_addr);
316 unsigned char i,addr[4];
317 for (i=0;i<4;i++,addrh>>=8)
318 addr[3-i]=addrh;
319 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
320 bitno2mask(addr,bitno,4);
321 IP64_MASKADDR(&netmask, addr[0],addr[1],addr[2],addr[3]);
322 lwip_add_addr(nif,&ipaddr,&netmask);
324 break;
325 case PF_INET6:
327 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
328 unsigned char *addr=in->sin6_addr.s6_addr;
329 sockaddr2ip_6addr(&ipaddr,addr);
330 bitno2mask(addr,bitno,16);
331 sockaddr2ip_6addr(&netmask,addr);
332 lwip_add_addr(nif,&ipaddr,&netmask);
334 break;
335 default:
336 printlog(LOG_ERR,"Unsupported Address Family: %s",arg);
338 freeaddrinfo(res);
343 static void readdefroute(char *arg,struct netif *nif,int af)
345 struct addrinfo *res,hint;
346 struct ip_addr ipaddr;
347 int err;
348 memset(&hint,0,sizeof(hint));
349 hint.ai_family=af;
350 if ((err=getaddrinfo(arg,NULL,&hint,&res))!=0)
351 printlog(LOG_ERR,"IP address %s error %s",arg,gai_strerror(err));
352 else {
353 switch(res->ai_family) {
354 case PF_INET:
356 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
357 int addrh=ntohl(in->sin_addr.s_addr);
358 unsigned char i,addr[4];
359 for (i=0;i<4;i++,addrh>>=8)
360 addr[3-i]=addrh;
361 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
362 lwip_add_route(lwipstack,IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
364 break;
365 case PF_INET6:
367 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
368 sockaddr2ip_6addr(&ipaddr,in->sin6_addr.s6_addr);
369 lwip_add_route(lwipstack,IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
371 break;
372 default:
373 printlog(LOG_ERR,"Unsupported Address Family: %s",arg);
375 freeaddrinfo(res);
379 static void readpassword(char *arg,int unused)
381 passwd=strdup(arg);
384 struct cf {
385 char *tag;
386 void (*f)();
387 int arg;
388 } cft[]= {
389 {"ip4",readip,PF_INET},
390 {"ip6",readip,PF_INET6},
391 {"ip",readip,0},
392 {"defroute4",readdefroute,PF_INET},
393 {"defroute6",readdefroute,PF_INET6},
394 {"defroute",readdefroute,0},
395 {"password",readpassword,0},
396 {NULL,NULL,0}};
398 int readconffile(char *path,struct netif *nif)
400 FILE *f;
401 char buf[BUFSIZE],*s;
402 int line = 0;
404 if (path==NULL)
405 return -1;
406 if((f=fopen(path,"r"))==NULL)
407 return -1;
408 while (fgets(buf,BUFSIZE,f) != NULL)
410 line++;
412 if ((s=rindex(buf,'\n')) != NULL)
413 *s=0;
415 for(s=buf;*s == ' ' || *s == '\t';s++);
417 if (*s != '#' && *s != '\n' && *s != '\0')
419 struct cf *scf;
420 for (scf=cft;scf->tag != NULL;scf++)
421 if(strncmp(s,scf->tag,strlen(scf->tag)) == 0)
423 s+=strlen(scf->tag);
424 for(;*s == ' ' || *s == '\t';s++);
425 if (*s == '=')
426 s++;
427 for(;*s == ' ' || *s == '\t';s++);
428 scf->f(s,nif,scf->arg);
429 break;
431 if (scf->tag == NULL)
432 printlog(LOG_ERR,"Error parsing configuration file: line %d: %s", line, buf);
435 return 0;
438 int addpfd(int fd,voidfun cb)
440 if (npfd < MAXFD) {
441 pfd[npfd].fd=fd;
442 pfd[npfd].events=POLLIN|POLLHUP;
443 pfd[npfd].revents=0;
444 fpfd[npfd]=cb;
445 npfd++;
447 return npfd-1;
450 void delpfd(int fn)
452 int i=fn;
453 for (i=fn;i<npfd-1;i++) {
454 pfd[i]=pfd[i+1];
455 fpfd[i]=fpfd[i+1];
456 status[i]=status[i+1];
458 npfd--;
461 int pfdsearch(int fd)
463 int i;
464 for (i=0;i<npfd && pfd[i].fd!=fd;i++)
466 return i;
469 int setfds(fd_set *rds, fd_set *exc)
471 int i,max=0;
472 FD_ZERO(rds);
473 FD_ZERO(exc);
474 for (i=0;i<npfd;i++) {
475 FD_SET(pfd[i].fd,rds);
476 FD_SET(pfd[i].fd,exc);
477 if (pfd[i].fd>max) max=pfd[i].fd;
479 return max+1;
482 static void save_pidfile(void)
484 if(pidfile[0] != '/')
485 strncat(pidfile_path, pidfile, _POSIX_PATH_MAX - strlen(pidfile_path));
486 else
487 strcpy(pidfile_path, pidfile);
489 int fd = open(pidfile_path,
490 O_WRONLY | O_CREAT | O_EXCL,
491 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
492 FILE *f;
494 if(fd == -1) {
495 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
496 exit(1);
499 if((f = fdopen(fd, "w")) == NULL) {
500 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
501 exit(1);
504 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
505 printlog(LOG_ERR, "Error in writing pidfile");
506 exit(1);
509 fclose(f);
512 /* this custom version of daemon(3) continue to receive stderr messages
513 * until the end of the startup phase, the foreground process terminates
514 * when stderr gets closed*/
515 static int special_daemon(void)
517 int fd;
518 int errorpipe[2];
519 char buf[256];
520 int n;
522 if (pipe(errorpipe))
523 return -1;
525 switch (fork()) {
526 case -1:
527 return (-1);
528 case 0:
529 break;
530 default:
531 close(errorpipe[1]);
532 while ((n=read(errorpipe[0],buf,128)) > 0) {
533 write(STDERR_FILENO,buf,n);
535 _exit(0);
537 close(errorpipe[0]);
539 if (setsid() == -1)
540 return (-1);
542 (void)chdir("/");
544 if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
545 (void)dup2(fd, STDIN_FILENO);
546 (void)dup2(fd, STDOUT_FILENO);
547 (void)dup2(errorpipe[1], STDERR_FILENO);
548 close(errorpipe[1]);
549 if (fd > 2)
550 (void)close (fd);
552 return 0;
555 int main(int argc, char *argv[])
557 struct netif *nif;
558 int vdefd;
559 char *conffile=NULL;
560 char *nodename=NULL;
561 int c;
563 progname=argv[0];
565 while (1) {
566 int option_index = 0;
568 static struct option long_options[] = {
569 {"daemon", 0, 0, 'd'},
570 {"mgmt", 1, 0, 'M'},
571 {"telnet", 0, 0, 't'},
572 {"web", 0, 0, 'w'},
573 {"help",0,0,'h'},
574 {"rcfile",1,0,'f'},
575 {"nodename",1,0,'n'},
576 {"pidfile", 1, 0, 'p'},
577 {0, 0, 0, 0}
579 c = getopt_long_only (argc, argv, "hdwtM:f:n:",
580 long_options, &option_index);
581 if (c == -1)
582 break;
584 switch (c) {
585 case 'M':
586 mgmt=strdup(optarg);
587 break;
588 case 'f':
589 conffile=strdup(optarg);
590 break;
591 case 'n':
592 nodename=strdup(optarg);
593 break;
594 case 't':
595 telnet=1;
596 break;
597 case 'w':
598 web=1;
599 break;
600 case 'd':
601 daemonize=1;
602 break;
603 case 'p':
604 pidfile=strdup(optarg);
605 break;
606 case 'h':
607 usage(argv[0]); //implies exit
608 break;
611 if (optind < argc && mgmt==NULL)
612 mgmt=argv[optind];
614 if (mgmt==NULL) {
615 printlog(LOG_ERR,"mgmt_socket not defined");
616 exit(-1);
618 if (telnet==0 && web==0) {
619 printlog(LOG_ERR,"at least one service option (-t -w) must be specified");
620 exit(-1);
623 atexit(cleanup);
624 setsighandlers();
626 /* saves current path in pidfile_path, because otherwise with daemonize() we
627 * forget it */
628 if(getcwd(pidfile_path, _POSIX_PATH_MAX-1) == NULL) {
629 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
630 exit(1);
632 strcat(pidfile_path, "/");
634 /* call daemon before starting the stack otherwise the stack threads
635 * does not get inherited by the forked process */
636 if (daemonize && special_daemon()) {
637 printlog(LOG_ERR,"daemon: %s",strerror(errno));
638 exit(1);
641 lwipstack=lwip_stack_new();
642 lwip_stack_set(lwipstack);
644 vdefd = openvdem(mgmt, argv[0], &nif, nodename);
646 /* If rcfile is specified, try it and nothing else */
647 if (conffile && readconffile(conffile,nif) < 0)
649 printlog(LOG_ERR, "Error reading configuration file '%s': %s", conffile, strerror(errno));
650 exit(1);
652 /* Else try default ones */
653 else if (!conffile)
655 int rv;
656 char *homedir = getenv("HOME");
657 if (homedir)
659 int len = strlen(homedir) + strlen(USERCONFFILE) + 1;
660 conffile = malloc(len);
661 snprintf(conffile, len, "%s%s", homedir, USERCONFFILE);
662 if ((rv = readconffile(conffile, nif)) >= 0)
663 free(conffile);
665 if (!homedir || rv < 0)
666 rv = readconffile(conffile = ROOTCONFFILE, nif);
668 if (rv < 0)
670 printlog(LOG_ERR, "Error reading configuration file '%s': %s", conffile, strerror(errno));
671 exit(1);
675 /* once here, we're sure we're the true process which will continue as a
676 * server: save PID file if needed */
677 if(pidfile) save_pidfile();
679 if (telnet)
680 telnet_init(vdefd);
681 if (web)
682 web_init(vdefd);
684 if (daemonize) {
685 int fd;
686 if ((fd=open("/dev/null",O_RDWR)) >= 0) {
687 close(STDERR_FILENO);
688 dup2(fd,STDERR_FILENO);
689 close(fd);
690 openlog(basename(argv[0]), LOG_PID, 0);
691 logok=1;
693 printlog(LOG_INFO,"VDETELWEB started");
696 while (1)
698 int n,m,i;
699 fd_set rds,exc;
700 int max=setfds(&rds,&exc);
701 m=lwip_select(max,&rds,NULL,&exc,NULL);
702 for(i=0; m>0 && i<max; i++) {
703 if (FD_ISSET(i,&rds) || FD_ISSET(i,&exc)) {
704 n=pfdsearch(i);
705 fpfd[n](n,i,vdefd);
706 m--;