Switched from dynamic library loading to standard dynamic library for lwipv6
[vde.git] / vdetelweb / vdetelweb.c
blobd0c6628bec20b9e1385cd8b979204c911e59da27
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>
56 int daemonize;
57 int telnet;
58 int web;
59 char *mgmt;
60 char *banner;
61 char *progname;
62 char *prompt;
63 int logok;
64 static char *passwd;
65 static char *pidfile = NULL;
66 static char pidfile_path[_POSIX_PATH_MAX];
67 struct stack *lwipstack;
69 #define MAXFD 16
70 #define HASH_SIZE 40
71 int npfd=0;
72 struct pollfd pfd[MAXFD];
73 voidfun fpfd[MAXFD];
74 void *status[MAXFD];
76 #define ROOTCONFFILE "/etc/vde/vdetelwebrc"
78 void printlog(int priority, const char *format, ...)
80 va_list arg;
82 va_start (arg, format);
84 if (logok)
85 vsyslog(priority,format,arg);
86 else {
87 fprintf(stderr,"%s: ",progname);
88 vfprintf(stderr,format,arg);
89 fprintf(stderr,"\n");
91 va_end (arg);
95 static void cleanup(void)
97 lwip_stack_free(lwipstack);
98 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
99 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
103 int sha1passwdok(const char *pw) {
104 char buf[HASH_SIZE + 1];
105 int pfd_fc[2];
106 int pfd_cf[2];
107 pid_t pid;
109 pipe(pfd_fc);
110 pipe(pfd_cf);
111 pid = fork();
113 if (!pid) {
114 close(pfd_fc[1]);
115 close(pfd_cf[0]);
116 dup2(pfd_fc[0], STDIN_FILENO);
117 dup2(pfd_cf[1], STDOUT_FILENO);
119 execl("/usr/bin/sha1sum", "/usr/bin/sha1sum", NULL);
120 exit(1);
121 } else {
122 close(pfd_cf[1]);
123 close(pfd_fc[0]);
125 write(pfd_fc[1], pw, strlen(pw));
126 close(pfd_fc[1]);
127 read(pfd_cf[0], buf, sizeof(buf));
128 close(pfd_cf[0]);
130 waitpid(pid, NULL, 0);
131 return (strncmp(buf,passwd,strlen(passwd))==0);
135 static void sig_handler(int sig)
137 cleanup();
138 signal(sig, SIG_DFL);
139 kill(getpid(), sig);
142 static void setsighandlers()
144 /* setting signal handlers.
145 * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
146 * * ignores all the others signals which could cause termination. */
147 struct { int sig; const char *name; int ignore; } signals[] = {
148 { SIGHUP, "SIGHUP", 0 },
149 { SIGINT, "SIGINT", 0 },
150 { SIGPIPE, "SIGPIPE", 1 },
151 { SIGALRM, "SIGALRM", 1 },
152 { SIGTERM, "SIGTERM", 0 },
153 { SIGUSR1, "SIGUSR1", 1 },
154 { SIGUSR2, "SIGUSR2", 1 },
155 { SIGPOLL, "SIGPOLL", 1 },
156 { SIGPROF, "SIGPROF", 1 },
157 { SIGVTALRM, "SIGVTALRM", 1 },
158 { SIGSTKFLT, "SIGSTKFLT", 1 },
159 { SIGIO, "SIGIO", 1 },
160 { SIGPWR, "SIGPWR", 1 },
161 { SIGUNUSED, "SIGUNUSED", 1 },
162 { 0, NULL, 0 }
165 int i;
166 for(i = 0; signals[i].sig != 0; i++)
167 if(signal(signals[i].sig,
168 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
169 perror("Setting handler");
172 static void usage(char *progname) {
173 fprintf (stderr,"Usage: %s [-w] [-t] [-d] [-n nodename] [-p pidfile] mgmt_socket\n"
174 " %s [--web] [--telnet] [--daemon] [--nodename nodename] [--pidfile pidfile] mgmt_socket\n",progname,progname);
175 exit(-1);
178 void setprompt(char *ctrl,char *nodename)
180 char buf[BUFSIZE];
181 if (nodename==NULL) {
182 struct utsname un;
183 uname(&un);
184 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",un.nodename,ctrl);
185 } else
186 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",nodename,ctrl);
187 prompt=strdup(buf);
190 int openextravdem()
192 struct sockaddr_un sun;
193 int fd,n;
194 char buf[BUFSIZE+1];
195 sun.sun_family=PF_UNIX;
196 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",mgmt);
197 fd=socket(PF_UNIX,SOCK_STREAM,0);
198 if (connect(fd,(struct sockaddr *)(&sun),sizeof(sun)) < 0) {
199 printlog(LOG_ERR,"mgmt extra connect %s",strerror(errno));
200 return(-1);
202 if ((n=read(fd,buf,BUFSIZE))<=0) {
203 printlog(LOG_ERR,"banner %s",strerror(errno));
204 return(-1);
206 return fd;
209 int openvdem(char *mgmt,char *progname, struct netif **nif,char *nodename)
211 struct sockaddr_un sun;
212 int fd,n;
213 char buf[BUFSIZE+1],*line2,*ctrl;
214 sun.sun_family=PF_UNIX;
215 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",mgmt);
216 fd=socket(PF_UNIX,SOCK_STREAM,0);
217 if (connect(fd,(struct sockaddr *)(&sun),sizeof(sun)) < 0) {
218 printlog(LOG_ERR,"mgmt connect %s",strerror(errno));
219 exit(-1);
221 if ((n=read(fd,buf,BUFSIZE))<=0) {
222 printlog(LOG_ERR,"banner %s",strerror(errno));
223 exit(-1);
225 buf[n]=0;
226 if ((ctrl=rindex(buf,'\n')) != NULL)
227 *ctrl=0;
228 banner=strdup(buf);
229 write(fd,"ds/showinfo\n",13);
230 if ((n=read(fd,buf,BUFSIZE))<=0) {
231 printlog(LOG_ERR,"read ctl socket %s",strerror(errno));
232 exit(-1);
234 buf[n]=0;
235 if ((line2=index(buf,'\n')) == NULL) {
236 printlog(LOG_ERR,"read ctl socket parse error 1");
237 exit(-1);
239 line2++;
240 if (strncmp(line2,"ctl dir ",8) != 0) {
241 printlog(LOG_ERR,"read ctl socket parse error");
242 exit(-1);
244 for(ctrl=line2+8;*ctrl!='\n' && ctrl<buf+n;ctrl++)
246 *ctrl=0;
247 ctrl=line2+8;
248 setprompt(ctrl,nodename);
249 strcat(ctrl,"[0]");
250 *nif=lwip_vdeif_add(lwipstack,ctrl);
251 if (*nif == NULL) {
252 printlog(LOG_ERR,"cannot connect to the switch");
253 exit(-1);
255 lwip_ifup(*nif);
257 return fd;
260 static void bitno2mask(unsigned char *addr,int bitno,int len)
262 int i;
263 for(i=0;i<len;i++,bitno -= 8) {
264 if (bitno >= 8)
265 addr[i]=255;
266 else if (bitno <= 0)
267 addr[i]=0;
268 else
269 addr[i]=256 - (1<<(8-bitno));
273 static void sockaddr2ip_6addr(struct ip_addr *ipaddrp,unsigned char *addr)
275 IP6_ADDR(ipaddrp,
276 (addr[0]<<8)|addr[1],
277 (addr[2]<<8)|addr[3],
278 (addr[4]<<8)|addr[5],
279 (addr[6]<<8)|addr[7],
280 (addr[8]<<8)|addr[9],
281 (addr[10]<<8)|addr[11],
282 (addr[12]<<8)|addr[13],
283 (addr[14]<<8)|addr[15]);
286 static void readip(char *arg,struct netif *nif,int af)
288 char *bit=rindex(arg,'/');
289 if (bit == 0)
290 printlog(LOG_ERR,"ip addresses must include the netmask i.e. addr/maskbits");
291 else {
292 int bitno=atoi(bit+1);
293 *bit=0;
294 struct addrinfo *res,hint;
295 struct ip_addr ipaddr,netmask;
296 int err;
297 memset(&hint,0,sizeof(hint));
298 hint.ai_family=af;
299 if ((err=getaddrinfo(arg,NULL,&hint,&res))!=0)
300 printlog(LOG_ERR,"ip address %s error %s",arg,gai_strerror(err));
301 else {
302 switch(res->ai_family) {
303 case PF_INET: {
304 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
305 int addrh=ntohl(in->sin_addr.s_addr);
306 unsigned char i,addr[4];
307 for (i=0;i<4;i++,addrh>>=8)
308 addr[3-i]=addrh;
309 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
310 bitno2mask(addr,bitno,4);
311 IP64_MASKADDR(&netmask, addr[0],addr[1],addr[2],addr[3]);
312 lwip_add_addr(nif,&ipaddr,&netmask);
314 break;
315 case PF_INET6:{
316 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
317 unsigned char *addr=in->sin6_addr.s6_addr;
318 sockaddr2ip_6addr(&ipaddr,addr);
319 bitno2mask(addr,bitno,16);
320 sockaddr2ip_6addr(&netmask,addr);
321 lwip_add_addr(nif,&ipaddr,&netmask);
323 break;
324 default:
325 printlog(LOG_ERR,"unsupported Address Family: %s",arg);
327 freeaddrinfo(res);
332 static void readdefroute(char *arg,struct netif *nif,int af)
334 struct addrinfo *res,hint;
335 struct ip_addr ipaddr;
336 int err;
337 memset(&hint,0,sizeof(hint));
338 hint.ai_family=af;
339 if ((err=getaddrinfo(arg,NULL,&hint,&res))!=0)
340 printlog(LOG_ERR,"ip address %s error %s",arg,gai_strerror(err));
341 else {
342 switch(res->ai_family) {
343 case PF_INET: {
344 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
345 int addrh=ntohl(in->sin_addr.s_addr);
346 unsigned char i,addr[4];
347 for (i=0;i<4;i++,addrh>>=8)
348 addr[3-i]=addrh;
349 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
350 lwip_add_route(lwipstack,IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
352 break;
353 case PF_INET6:{
354 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
355 sockaddr2ip_6addr(&ipaddr,in->sin6_addr.s6_addr);
356 lwip_add_route(lwipstack,IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
358 break;
359 default:
360 printlog(LOG_ERR,"unsupported Address Family: %s",arg);
362 freeaddrinfo(res);
366 static void readpassword(char *arg,int unused)
368 passwd=strdup(arg);
371 struct cf {
372 char *tag;
373 void (*f)();
374 int arg;
375 } cft[]= {
376 {"ip4",readip,PF_INET},
377 {"ip6",readip,PF_INET6},
378 {"ip",readip,0},
379 {"defroute4",readdefroute,PF_INET},
380 {"defroute6",readdefroute,PF_INET6},
381 {"defroute",readdefroute,0},
382 {"password",readpassword,0},
383 {NULL,NULL,0}};
385 int readconffile(char *path,struct netif *nif)
387 FILE *f;
388 char buf[BUFSIZE],*s;
389 if (path == NULL && geteuid() == 0)
390 path=ROOTCONFFILE;
391 if (path==NULL)
392 return -1;
393 if((f=fopen(path,"r"))==NULL)
394 return -1;
395 while (fgets(buf,BUFSIZE,f) != NULL) {
396 if ((s=rindex(buf,'\n')) != NULL)
397 *s=0;
398 for(s=buf;*s == ' ' || *s == '\t';s++)
400 if (*s != '#') {
401 struct cf *scf;
402 for (scf=cft;scf->tag != NULL;scf++)
403 if(strncmp(s,scf->tag,strlen(scf->tag)) == 0){
404 s+=strlen(scf->tag);
405 for(;*s == ' ' || *s == '\t';s++)
407 if (*s == '=') s++;
408 for(;*s == ' ' || *s == '\t';s++)
410 scf->f(s,nif,scf->arg);
411 break;
413 if (scf->tag == NULL) {
414 printlog(LOG_ERR,"rc file syntax error: %s",buf);
418 return 0;
421 int addpfd(int fd,voidfun cb)
423 if (npfd < MAXFD) {
424 pfd[npfd].fd=fd;
425 pfd[npfd].events=POLLIN|POLLHUP;
426 pfd[npfd].revents=0;
427 fpfd[npfd]=cb;
428 npfd++;
430 return npfd-1;
433 void delpfd(int fn)
435 int i=fn;
436 for (i=fn;i<npfd-1;i++) {
437 pfd[i]=pfd[i+1];
438 fpfd[i]=fpfd[i+1];
439 status[i]=status[i+1];
441 npfd--;
444 int pfdsearch(int fd)
446 int i;
447 for (i=0;i<npfd && pfd[i].fd!=fd;i++)
449 return i;
452 int setfds(fd_set *rds, fd_set *exc)
454 int i,max=0;
455 FD_ZERO(rds);
456 FD_ZERO(exc);
457 for (i=0;i<npfd;i++) {
458 FD_SET(pfd[i].fd,rds);
459 FD_SET(pfd[i].fd,exc);
460 if (pfd[i].fd>max) max=pfd[i].fd;
462 return max+1;
465 static void save_pidfile()
467 if(pidfile[0] != '/')
468 strncat(pidfile_path, pidfile, _POSIX_PATH_MAX - strlen(pidfile_path));
469 else
470 strcpy(pidfile_path, pidfile);
472 int fd = open(pidfile_path,
473 O_WRONLY | O_CREAT | O_EXCL,
474 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
475 FILE *f;
477 if(fd == -1) {
478 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
479 exit(1);
482 if((f = fdopen(fd, "w")) == NULL) {
483 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
484 exit(1);
487 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
488 printlog(LOG_ERR, "Error in writing pidfile");
489 exit(1);
492 fclose(f);
495 int main(int argc, char *argv[])
497 struct netif *nif;
498 int vdefd;
499 char *conffile=NULL;
500 char *nodename=NULL;
501 progname=argv[0];
503 int c;
504 while (1) {
505 int option_index = 0;
507 static struct option long_options[] = {
508 {"daemon", 0, 0, 'd'},
509 {"mgmt", 1, 0, 'M'},
510 {"telnet", 0, 0, 't'},
511 {"web", 0, 0, 'w'},
512 {"help",0,0,'h'},
513 {"rcfile",1,0,'f'},
514 {"nodename",1,0,'n'},
515 {"pidfile", 1, 0, 'p'},
516 {0, 0, 0, 0}
518 c = getopt_long_only (argc, argv, "hdwtM:f:n:",
519 long_options, &option_index);
520 if (c == -1)
521 break;
523 switch (c) {
524 case 'M':
525 mgmt=strdup(optarg);
526 break;
527 case 'f':
528 conffile=strdup(optarg);
529 break;
530 case 'n':
531 nodename=strdup(optarg);
532 break;
533 case 't':
534 telnet=1;
535 break;
536 case 'w':
537 web=1;
538 break;
539 case 'd':
540 daemonize=1;
541 break;
542 case 'p':
543 pidfile=strdup(optarg);
544 break;
545 case 'h':
546 usage(argv[0]); //implies exit
547 break;
550 if (optind < argc && mgmt==NULL)
551 mgmt=argv[optind];
553 if (mgmt==NULL) {
554 printlog(LOG_ERR,"mgmt_socket not defined");
555 exit(-1);
557 if (telnet==0 && web==0) {
558 printlog(LOG_ERR,"at least one service option (-t -w) must be specified");
559 exit(-1);
561 atexit(cleanup);
562 setsighandlers();
563 if (daemonize) {
564 openlog(basename(argv[0]), LOG_PID, 0);
565 logok=1;
566 syslog(LOG_INFO,"VDETELWEB started");
569 /* saves current path in pidfile_path, because otherwise with daemonize() we
570 * forget it */
571 if(getcwd(pidfile_path, _POSIX_PATH_MAX-1) == NULL) {
572 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
573 exit(1);
575 strcat(pidfile_path, "/");
577 if (daemonize && daemon(0, 1)) {
578 printlog(LOG_ERR,"daemon: %s",strerror(errno));
579 exit(1);
581 /* once here, we're sure we're the true process which will continue as a
582 * server: save PID file if needed */
583 if(pidfile) save_pidfile();
585 lwipstack=lwip_stack_new();
586 lwip_stack_set(lwipstack);
588 vdefd=openvdem(mgmt,argv[0],&nif,nodename);
589 if (readconffile(conffile,nif) < 0) {
590 printlog(LOG_ERR,"configuration file not found");
591 exit(1);
593 if (telnet)
594 telnet_init(vdefd);
595 if (web)
596 web_init(vdefd);
598 while (1)
600 int n,m,i;
601 fd_set rds,exc;
602 int max=setfds(&rds,&exc);
603 m=lwip_select(max,&rds,NULL,&exc,NULL);
604 for(i=0; m>0 && i<max; i++) {
605 if (FD_ISSET(i,&rds) || FD_ISSET(i,&exc)) {
606 n=pfdsearch(i);
607 fpfd[n](n,i,vdefd);
608 m--;