Linux >= 3.9 compatibility issue fixed: hlist_for_each_entry has 3 args now
[vde.git] / vdetelweb / vdetelweb.c
blobf7a720c9324d9346283f0859d0ff406c885082b6
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>
56 #include <mhash.h>
58 int daemonize;
59 int telnet;
60 int web;
61 char *mgmt;
62 char *banner;
63 char *progname;
64 char *prompt;
65 int logok;
66 static char *passwd;
67 static char *pidfile = NULL;
68 static char pidfile_path[_POSIX_PATH_MAX];
69 struct stack *lwipstack;
71 #define MAXFD 16
72 #define HASH_SIZE 40
73 int npfd=0;
74 struct pollfd pfd[MAXFD];
75 voidfun fpfd[MAXFD];
76 void *status[MAXFD];
78 #define ROOTCONFFILE "/etc/vde/vdetelwebrc"
80 /* This will be prefixed by getenv("HOME") */
81 #define USERCONFFILE "/.vde/vdetelwebrc"
83 void printlog(int priority, const char *format, ...)
85 va_list arg;
87 va_start (arg, format);
89 if (logok)
90 vsyslog(priority,format,arg);
91 else {
92 fprintf(stderr,"%s: ",progname);
93 vfprintf(stderr,format,arg);
94 fprintf(stderr,"\n");
96 va_end (arg);
100 static void cleanup(void)
102 if (lwipstack)
103 lwip_stack_free(lwipstack);
104 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
105 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
109 static char hex[]="0123456789abcdef";
110 int sha1passwdok(const char *pw) {
111 unsigned char out[mhash_get_block_size(MHASH_SHA1)];
112 char outstr[mhash_get_block_size(MHASH_SHA1)*2+1];
113 int i;
114 MHASH td;
115 td=mhash_init(MHASH_SHA1);
116 mhash(td, pw, strlen(pw));
117 mhash_deinit(td, out);
118 for (i=0; i<mhash_get_block_size(MHASH_SHA1); i++) {
119 outstr[2*i]=hex[out[i] >> 4];
120 outstr[2*i+1]=hex[out[i] & 0xf];
122 outstr[2*i]=0;
123 return (memcmp(outstr,passwd,mhash_get_block_size(MHASH_SHA1))==0);
126 static void sig_handler(int sig)
128 cleanup();
129 signal(sig, SIG_DFL);
130 kill(getpid(), sig);
133 static void setsighandlers(void)
135 /* setting signal handlers.
136 * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
137 * * ignores all the others signals which could cause termination. */
138 struct { int sig; const char *name; int ignore; } signals[] = {
139 { SIGHUP, "SIGHUP", 0 },
140 { SIGINT, "SIGINT", 0 },
141 { SIGPIPE, "SIGPIPE", 1 },
142 { SIGALRM, "SIGALRM", 1 },
143 { SIGTERM, "SIGTERM", 0 },
144 { SIGUSR1, "SIGUSR1", 1 },
145 { SIGUSR2, "SIGUSR2", 1 },
146 { SIGPOLL, "SIGPOLL", 1 },
147 { SIGPROF, "SIGPROF", 1 },
148 { SIGVTALRM, "SIGVTALRM", 1 },
149 #ifdef SIGSTKFLT
150 { SIGSTKFLT, "SIGSTKFLT", 1 },
151 #endif
152 { SIGIO, "SIGIO", 1 },
153 { SIGPWR, "SIGPWR", 1 },
154 #ifdef SIGUNUSED
155 { SIGUNUSED, "SIGUNUSED", 1 },
156 #endif
157 { 0, NULL, 0 }
160 int i;
161 for(i = 0; signals[i].sig != 0; i++)
162 if(signal(signals[i].sig,
163 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
164 perror("Setting handler");
167 static void usage(char *progname) {
168 fprintf (stderr,"Usage: %s [-w] [-t] [-d] [-n nodename] [-p pidfile] mgmt_socket\n"
169 " %s [--web] [--telnet] [--daemon] [--nodename nodename] [--pidfile pidfile] mgmt_socket\n",progname,progname);
170 exit(-1);
173 void setprompt(char *ctrl,char *nodename)
175 char buf[BUFSIZE];
176 if (nodename==NULL) {
177 struct utsname un;
178 uname(&un);
179 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",un.nodename,ctrl);
180 } else
181 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",nodename,ctrl);
182 prompt=strdup(buf);
185 int openextravdem(void)
187 struct sockaddr_un sun;
188 int fd,n;
189 char buf[BUFSIZE+1];
190 sun.sun_family=PF_UNIX;
191 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",mgmt);
192 fd=socket(PF_UNIX,SOCK_STREAM,0);
193 if (connect(fd,(struct sockaddr *)(&sun),sizeof(sun)) < 0) {
194 printlog(LOG_ERR,"Error connecting to the management socket '%s': %s", mgmt, strerror(errno));
195 return(-1);
197 if ((n=read(fd,buf,BUFSIZE))<=0) {
198 printlog(LOG_ERR,"banner %s",strerror(errno));
199 return(-1);
201 return fd;
204 int openvdem(char *mgmt,char *progname, struct netif **nif,char *nodename)
206 struct sockaddr_un sun;
207 int fd,n;
208 char buf[BUFSIZE+1],*line2,*ctrl;
209 sun.sun_family=PF_UNIX;
210 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",mgmt);
211 fd=socket(PF_UNIX,SOCK_STREAM,0);
212 if (connect(fd,(struct sockaddr *)(&sun),sizeof(sun)) < 0) {
213 printlog(LOG_ERR,"Error connecting to the management socket '%s': %s", mgmt, strerror(errno));
214 exit(-1);
216 if ((n=read(fd,buf,BUFSIZE))<=0) {
217 printlog(LOG_ERR,"Error reading banner from VDE switch: %s",strerror(errno));
218 exit(-1);
220 buf[n]=0;
221 if ((ctrl=rindex(buf,'\n')) != NULL)
222 *ctrl=0;
223 banner=strdup(buf);
224 write(fd,"ds/showinfo\n",13);
225 if ((n=read(fd,buf,BUFSIZE))<=0) {
226 printlog(LOG_ERR,"Error reading ctl socket from VDE switch: %s",strerror(errno));
227 exit(-1);
229 buf[n]=0;
230 if ((line2=index(buf,'\n')) == NULL) {
231 printlog(LOG_ERR,"Error parsing first line of ctl socket information");
232 exit(-1);
234 line2++;
235 if (strncmp(line2,"ctl dir ",8) != 0) {
236 printlog(LOG_ERR,"Error parsing ctl socket information");
237 exit(-1);
239 for(ctrl=line2+8;*ctrl!='\n' && ctrl<buf+n;ctrl++)
241 *ctrl=0;
242 ctrl=line2+8;
243 setprompt(ctrl,nodename);
244 strcat(ctrl,"[0]");
245 *nif=lwip_vdeif_add(lwipstack,ctrl);
246 if (*nif == NULL) {
247 printlog(LOG_ERR,"Cannot connect to the VDE switch");
248 exit(-1);
250 lwip_ifup(*nif);
252 return fd;
255 static void bitno2mask(unsigned char *addr,int bitno,int len)
257 int i;
258 for(i=0;i<len;i++,bitno -= 8) {
259 if (bitno >= 8)
260 addr[i]=255;
261 else if (bitno <= 0)
262 addr[i]=0;
263 else
264 addr[i]=256 - (1<<(8-bitno));
268 static void sockaddr2ip_6addr(struct ip_addr *ipaddrp,unsigned char *addr)
270 IP6_ADDR(ipaddrp,
271 (addr[0]<<8)|addr[1],
272 (addr[2]<<8)|addr[3],
273 (addr[4]<<8)|addr[5],
274 (addr[6]<<8)|addr[7],
275 (addr[8]<<8)|addr[9],
276 (addr[10]<<8)|addr[11],
277 (addr[12]<<8)|addr[13],
278 (addr[14]<<8)|addr[15]);
281 static void readip(char *arg,struct netif *nif,int af)
283 char *bit=rindex(arg,'/');
284 if (bit == 0)
285 printlog(LOG_ERR,"IP addresses must include the netmask i.e. addr/maskbits");
286 else {
287 int bitno=atoi(bit+1);
288 *bit=0;
289 struct addrinfo *res,hint;
290 struct ip_addr ipaddr,netmask;
291 int err;
292 memset(&hint,0,sizeof(hint));
293 hint.ai_family=af;
294 if ((err=getaddrinfo(arg,NULL,&hint,&res))!=0)
295 printlog(LOG_ERR,"IP address %s error %s",arg,gai_strerror(err));
296 else {
297 switch(res->ai_family) {
298 case PF_INET:
300 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
301 int addrh=ntohl(in->sin_addr.s_addr);
302 unsigned char i,addr[4];
303 for (i=0;i<4;i++,addrh>>=8)
304 addr[3-i]=addrh;
305 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
306 bitno2mask(addr,bitno,4);
307 IP64_MASKADDR(&netmask, addr[0],addr[1],addr[2],addr[3]);
308 lwip_add_addr(nif,&ipaddr,&netmask);
310 break;
311 case PF_INET6:
313 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
314 unsigned char *addr=in->sin6_addr.s6_addr;
315 sockaddr2ip_6addr(&ipaddr,addr);
316 bitno2mask(addr,bitno,16);
317 sockaddr2ip_6addr(&netmask,addr);
318 lwip_add_addr(nif,&ipaddr,&netmask);
320 break;
321 default:
322 printlog(LOG_ERR,"Unsupported Address Family: %s",arg);
324 freeaddrinfo(res);
329 static void readdefroute(char *arg,struct netif *nif,int af)
331 struct addrinfo *res,hint;
332 struct ip_addr ipaddr;
333 int err;
334 memset(&hint,0,sizeof(hint));
335 hint.ai_family=af;
336 if ((err=getaddrinfo(arg,NULL,&hint,&res))!=0)
337 printlog(LOG_ERR,"IP address %s error %s",arg,gai_strerror(err));
338 else {
339 switch(res->ai_family) {
340 case PF_INET:
342 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
343 int addrh=ntohl(in->sin_addr.s_addr);
344 unsigned char i,addr[4];
345 for (i=0;i<4;i++,addrh>>=8)
346 addr[3-i]=addrh;
347 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
348 lwip_add_route(lwipstack,IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
350 break;
351 case PF_INET6:
353 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
354 sockaddr2ip_6addr(&ipaddr,in->sin6_addr.s6_addr);
355 lwip_add_route(lwipstack,IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
357 break;
358 default:
359 printlog(LOG_ERR,"Unsupported Address Family: %s",arg);
361 freeaddrinfo(res);
365 static void readpassword(char *arg,int unused)
367 passwd=strdup(arg);
370 struct cf {
371 char *tag;
372 void (*f)();
373 int arg;
374 } cft[]= {
375 {"ip4",readip,PF_INET},
376 {"ip6",readip,PF_INET6},
377 {"ip",readip,0},
378 {"defroute4",readdefroute,PF_INET},
379 {"defroute6",readdefroute,PF_INET6},
380 {"defroute",readdefroute,0},
381 {"password",readpassword,0},
382 {NULL,NULL,0}};
384 int readconffile(char *path,struct netif *nif)
386 FILE *f;
387 char buf[BUFSIZE],*s;
388 int line = 0;
390 if (path==NULL)
391 return -1;
392 if((f=fopen(path,"r"))==NULL)
393 return -1;
394 while (fgets(buf,BUFSIZE,f) != NULL)
396 line++;
398 if ((s=rindex(buf,'\n')) != NULL)
399 *s=0;
401 for(s=buf;*s == ' ' || *s == '\t';s++);
403 if (*s != '#' && *s != '\n' && *s != '\0')
405 struct cf *scf;
406 for (scf=cft;scf->tag != NULL;scf++)
407 if(strncmp(s,scf->tag,strlen(scf->tag)) == 0)
409 s+=strlen(scf->tag);
410 for(;*s == ' ' || *s == '\t';s++);
411 if (*s == '=')
412 s++;
413 for(;*s == ' ' || *s == '\t';s++);
414 scf->f(s,nif,scf->arg);
415 break;
417 if (scf->tag == NULL)
418 printlog(LOG_ERR,"Error parsing configuration file: line %d: %s", line, buf);
421 return 0;
424 int addpfd(int fd,voidfun cb)
426 if (npfd < MAXFD) {
427 pfd[npfd].fd=fd;
428 pfd[npfd].events=POLLIN|POLLHUP;
429 pfd[npfd].revents=0;
430 fpfd[npfd]=cb;
431 npfd++;
433 return npfd-1;
436 void delpfd(int fn)
438 int i=fn;
439 for (i=fn;i<npfd-1;i++) {
440 pfd[i]=pfd[i+1];
441 fpfd[i]=fpfd[i+1];
442 status[i]=status[i+1];
444 npfd--;
447 int pfdsearch(int fd)
449 int i;
450 for (i=0;i<npfd && pfd[i].fd!=fd;i++)
452 return i;
455 #if 0
456 int setfds(fd_set *rds, fd_set *exc)
458 int i,max=0;
459 FD_ZERO(rds);
460 FD_ZERO(exc);
461 for (i=0;i<npfd;i++) {
462 FD_SET(pfd[i].fd,rds);
463 FD_SET(pfd[i].fd,exc);
464 if (pfd[i].fd>max) max=pfd[i].fd;
466 return max+1;
468 #endif
470 static void save_pidfile(void)
472 if(pidfile[0] != '/')
473 strncat(pidfile_path, pidfile, _POSIX_PATH_MAX - strlen(pidfile_path));
474 else
475 strcpy(pidfile_path, pidfile);
477 int fd = open(pidfile_path,
478 O_WRONLY | O_CREAT | O_EXCL,
479 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
480 FILE *f;
482 if(fd == -1) {
483 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
484 exit(1);
487 if((f = fdopen(fd, "w")) == NULL) {
488 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
489 exit(1);
492 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
493 printlog(LOG_ERR, "Error in writing pidfile");
494 exit(1);
497 fclose(f);
500 /* this custom version of daemon(3) continue to receive stderr messages
501 * until the end of the startup phase, the foreground process terminates
502 * when stderr gets closed*/
503 static int special_daemon(void)
505 int fd;
506 int errorpipe[2];
507 char buf[256];
508 int n;
510 if (pipe(errorpipe))
511 return -1;
513 switch (fork()) {
514 case -1:
515 return (-1);
516 case 0:
517 break;
518 default:
519 close(errorpipe[1]);
520 while ((n=read(errorpipe[0],buf,128)) > 0) {
521 write(STDERR_FILENO,buf,n);
523 _exit(0);
525 close(errorpipe[0]);
527 if (setsid() == -1)
528 return (-1);
530 (void)chdir("/");
532 if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
533 (void)dup2(fd, STDIN_FILENO);
534 (void)dup2(fd, STDOUT_FILENO);
535 (void)dup2(errorpipe[1], STDERR_FILENO);
536 close(errorpipe[1]);
537 if (fd > 2)
538 (void)close (fd);
540 return 0;
543 int main(int argc, char *argv[])
545 struct netif *nif;
546 int vdefd;
547 char *conffile=NULL;
548 char *nodename=NULL;
549 int c;
551 progname=argv[0];
553 while (1) {
554 int option_index = 0;
556 static struct option long_options[] = {
557 {"daemon", 0, 0, 'd'},
558 {"mgmt", 1, 0, 'M'},
559 {"telnet", 0, 0, 't'},
560 {"web", 0, 0, 'w'},
561 {"help",0,0,'h'},
562 {"rcfile",1,0,'f'},
563 {"nodename",1,0,'n'},
564 {"pidfile", 1, 0, 'p'},
565 {0, 0, 0, 0}
567 c = getopt_long_only (argc, argv, "hdwtM:f:n:",
568 long_options, &option_index);
569 if (c == -1)
570 break;
572 switch (c) {
573 case 'M':
574 mgmt=strdup(optarg);
575 break;
576 case 'f':
577 conffile=strdup(optarg);
578 break;
579 case 'n':
580 nodename=strdup(optarg);
581 break;
582 case 't':
583 telnet=1;
584 break;
585 case 'w':
586 web=1;
587 break;
588 case 'd':
589 daemonize=1;
590 break;
591 case 'p':
592 pidfile=strdup(optarg);
593 break;
594 case 'h':
595 usage(argv[0]); //implies exit
596 break;
599 if (optind < argc && mgmt==NULL)
600 mgmt=argv[optind];
602 if (mgmt==NULL) {
603 printlog(LOG_ERR,"mgmt_socket not defined");
604 exit(-1);
606 if (telnet==0 && web==0) {
607 printlog(LOG_ERR,"at least one service option (-t -w) must be specified");
608 exit(-1);
611 atexit(cleanup);
612 setsighandlers();
614 /* saves current path in pidfile_path, because otherwise with daemonize() we
615 * forget it */
616 if(getcwd(pidfile_path, _POSIX_PATH_MAX-1) == NULL) {
617 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
618 exit(1);
620 strcat(pidfile_path, "/");
622 /* call daemon before starting the stack otherwise the stack threads
623 * does not get inherited by the forked process */
624 if (daemonize && special_daemon()) {
625 printlog(LOG_ERR,"daemon: %s",strerror(errno));
626 exit(1);
629 lwipstack=lwip_stack_new();
630 lwip_stack_set(lwipstack);
632 vdefd = openvdem(mgmt, argv[0], &nif, nodename);
634 /* If rcfile is specified, try it and nothing else */
635 if (conffile && readconffile(conffile,nif) < 0)
637 printlog(LOG_ERR, "Error reading configuration file '%s': %s", conffile, strerror(errno));
638 exit(1);
640 /* Else try default ones */
641 else if (!conffile)
643 int rv;
644 char *homedir = getenv("HOME");
645 if (homedir)
647 int len = strlen(homedir) + strlen(USERCONFFILE) + 1;
648 conffile = malloc(len);
649 snprintf(conffile, len, "%s%s", homedir, USERCONFFILE);
650 if ((rv = readconffile(conffile, nif)) >= 0)
651 free(conffile);
653 if (!homedir || rv < 0)
654 rv = readconffile(conffile = ROOTCONFFILE, nif);
656 if (rv < 0)
658 printlog(LOG_ERR, "Error reading configuration file '%s': %s", conffile, strerror(errno));
659 exit(1);
663 /* once here, we're sure we're the true process which will continue as a
664 * server: save PID file if needed */
665 if(pidfile) save_pidfile();
667 if (telnet)
668 telnet_init(vdefd);
669 if (web)
670 web_init(vdefd);
672 if (daemonize) {
673 int fd;
674 if ((fd=open("/dev/null",O_RDWR)) >= 0) {
675 close(STDERR_FILENO);
676 dup2(fd,STDERR_FILENO);
677 close(fd);
678 openlog(basename(argv[0]), LOG_PID, 0);
679 logok=1;
681 printlog(LOG_INFO,"VDETELWEB started");
684 while (1)
686 int i;
687 int m=lwip_poll(pfd,npfd,-1);
688 for (i=0;i<npfd && m>0;i++) {
689 if (pfd[i].revents) {
690 m--;
691 fpfd[i](i,pfd[i].fd,vdefd);