several bugfixes. deamon mode okay. needs 20051230 cvs for lwipv6 lib.
[vde.git] / vdetelweb / vdetelweb.c
blobc2abd857d2a099199012742c3ab0df2495effcef
1 /*
2 * VDETELWEB: VDE telnet and WEB interface
4 * vdetelweb.c: main
5 *
6 * Copyright 2005 Renzo Davoli University of Bologna - Italy
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 * $Id$
25 #include <stdio.h>
26 #include <signal.h>
27 #include <stdarg.h>
28 #include <syslog.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/select.h>
34 #include <sys/poll.h>
35 #include <sys/utsname.h>
36 #include <linux/un.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <string.h>
40 #include <getopt.h>
41 #include "vdetelweb.h"
42 #include <lwipv6.h>
44 int daemonize;
45 int telnet;
46 int web;
47 char *mgmt;
48 char *banner;
49 char *progname;
50 char *prompt;
51 int logok;
52 char *passwd;
54 #define MAXFD 16
55 int npfd=0;
56 struct pollfd pfd[MAXFD];
57 typedef int (*intfun)();
58 intfun fpfd[MAXFD];
59 void *status[MAXFD];
61 #define ROOTCONFFILE "/etc/vde/vdetelwebrc"
63 void printlog(int priority, const char *format, ...)
65 va_list arg;
67 va_start (arg, format);
69 if (logok)
70 vsyslog(priority,format,arg);
71 else {
72 fprintf(stderr,"%s: ",progname);
73 vfprintf(stderr,format,arg);
74 fprintf(stderr,"\n");
76 va_end (arg);
80 static void cleanup(void)
84 static void sig_handler(int sig)
86 cleanup();
87 signal(sig, SIG_DFL);
88 exit(0);
91 static void setsighandlers()
93 /* setting signal handlers.
94 * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
95 * * ignores all the others signals which could cause termination. */
96 struct { int sig; const char *name; int ignore; } signals[] = {
97 { SIGHUP, "SIGHUP", 0 },
98 { SIGINT, "SIGINT", 0 },
99 { SIGPIPE, "SIGPIPE", 1 },
100 { SIGALRM, "SIGALRM", 1 },
101 { SIGTERM, "SIGTERM", 0 },
102 { SIGUSR1, "SIGUSR1", 1 },
103 { SIGUSR2, "SIGUSR2", 1 },
104 { SIGPOLL, "SIGPOLL", 1 },
105 { SIGPROF, "SIGPROF", 1 },
106 { SIGVTALRM, "SIGVTALRM", 1 },
107 { SIGSTKFLT, "SIGSTKFLT", 1 },
108 { SIGIO, "SIGIO", 1 },
109 { SIGPWR, "SIGPWR", 1 },
110 { SIGUNUSED, "SIGUNUSED", 1 },
111 { 0, NULL, 0 }
114 int i;
115 for(i = 0; signals[i].sig != 0; i++)
116 if(signal(signals[i].sig,
117 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
118 perror("Setting handler");
121 static void usage(char *progname) {
122 fprintf (stderr,"Usage: %s [-w] [-t] [-d] [-n nodename] mgmt_socket\n"
123 " %s [--web] [--telnet] [--daemon] [--nodename nodename] mgmt_socket\n",progname,progname);
124 exit(-1);
127 void setprompt(char *ctrl,char *nodename)
129 char buf[BUFSIZE];
130 if (nodename==NULL) {
131 struct utsname un;
132 uname(&un);
133 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",un.nodename,ctrl);
134 } else
135 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",nodename,ctrl);
136 prompt=strdup(buf);
139 int openvdem(char *mgmt,char *progname, struct netif **nif,char *nodename)
141 struct sockaddr_un sun;
142 int fd,n;
143 char buf[BUFSIZE+1],*line2,*ctrl;
144 sun.sun_family=PF_UNIX;
145 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",mgmt);
146 fd=socket(PF_UNIX,SOCK_STREAM,0);
147 if (connect(fd,(struct sockaddr *)(&sun),sizeof(sun)) < 0) {
148 printlog(LOG_ERR,"mgmt connect %s",strerror(errno));
149 exit(-1);
151 if ((n=read(fd,buf,BUFSIZE))<=0) {
152 printlog(LOG_ERR,"banner %s",strerror(errno));
153 exit(-1);
155 buf[n]=0;
156 if ((ctrl=rindex(buf,'\n')) != NULL)
157 *ctrl=0;
158 banner=strdup(buf);
159 write(fd,"ds/showinfo\n",13);
160 if ((n=read(fd,buf,BUFSIZE))<=0) {
161 printlog(LOG_ERR,"read ctl socket %s",strerror(errno));
162 exit(-1);
164 buf[n]=0;
165 if ((line2=index(buf,'\n')) == NULL) {
166 printlog(LOG_ERR,"read ctl socket parse error 1");
167 exit(-1);
169 line2++;
170 if (strncmp(line2,"ctl dir ",8) != 0) {
171 printlog(LOG_ERR,"read ctl socket parse error");
172 exit(-1);
174 for(ctrl=line2+8;*ctrl!='\n' && ctrl<buf+n;ctrl++)
176 *ctrl=0;
177 ctrl=line2+8;
178 setprompt(ctrl,nodename);
179 strcat(ctrl,"[0]");
180 *nif=lwip_vdeif_add(ctrl);
181 if (*nif == NULL) {
182 printlog(LOG_ERR,"cannot connect to the switch");
183 exit(-1);
185 lwip_ifup(*nif);
187 return fd;
190 static void bitno2mask(unsigned char *addr,int bitno,int len)
192 int i;
193 for(i=0;i<len;i++,bitno -= 8) {
194 if (bitno >= 8)
195 addr[i]=255;
196 else if (bitno <= 0)
197 addr[i]=0;
198 else
199 addr[i]=256 - (1<<(8-bitno));
203 static void sockaddr2ip_6addr(struct ip_addr *ipaddrp,unsigned char *addr)
205 IP6_ADDR(ipaddrp,
206 (addr[0]<<8)|addr[1],
207 (addr[2]<<8)|addr[3],
208 (addr[4]<<8)|addr[5],
209 (addr[6]<<8)|addr[7],
210 (addr[8]<<8)|addr[9],
211 (addr[10]<<8)|addr[11],
212 (addr[12]<<8)|addr[13],
213 (addr[14]<<8)|addr[15]);
216 static void readip(char *arg,struct netif *nif,int af)
218 char *bit=rindex(arg,'/');
219 if (bit == 0)
220 printlog(LOG_ERR,"ip addresses must include the netmask i.e. addr/maskbits");
221 else {
222 int bitno=atoi(bit+1);
223 *bit=0;
224 struct addrinfo *res,hint;
225 struct ip_addr ipaddr,netmask;
226 int err;
227 memset(&hint,0,sizeof(hint));
228 hint.ai_family=af;
229 if (err=getaddrinfo(arg,NULL,&hint,&res))
230 printlog(LOG_ERR,"ip address %s error %s",arg,gai_strerror(err));
231 else {
232 switch(res->ai_family) {
233 case PF_INET: {
234 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
235 int addrh=ntohl(in->sin_addr.s_addr);
236 unsigned char i,addr[4];
237 for (i=0;i<4;i++,addrh>>=8)
238 addr[3-i]=addrh;
239 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
240 bitno2mask(addr,bitno,4);
241 IP64_MASKADDR(&netmask, addr[0],addr[1],addr[2],addr[3]);
242 lwip_add_addr(nif,&ipaddr,&netmask);
244 break;
245 case PF_INET6:{
246 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
247 char i;
248 unsigned char *addr=in->sin6_addr.s6_addr;
249 sockaddr2ip_6addr(&ipaddr,addr);
250 bitno2mask(addr,bitno,16);
251 sockaddr2ip_6addr(&netmask,addr);
252 lwip_add_addr(nif,&ipaddr,&netmask);
254 break;
255 default:
256 printlog(LOG_ERR,"unsupported Address Family: %s",arg);
258 freeaddrinfo(res);
263 static void readdefroute(char *arg,struct netif *nif,int af)
265 struct addrinfo *res,hint;
266 struct ip_addr ipaddr,netmask;
267 int err;
268 memset(&hint,0,sizeof(hint));
269 hint.ai_family=af;
270 if (err=getaddrinfo(arg,NULL,&hint,&res))
271 printlog(LOG_ERR,"ip address %s error %s",arg,gai_strerror(err));
272 else {
273 switch(res->ai_family) {
274 case PF_INET: {
275 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
276 int addrh=ntohl(in->sin_addr.s_addr);
277 unsigned char i,addr[4];
278 for (i=0;i<4;i++,addrh>>=8)
279 addr[3-i]=addrh;
280 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
281 lwip_add_route(IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
283 break;
284 case PF_INET6:{
285 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
286 char i;
287 sockaddr2ip_6addr(&ipaddr,in->sin6_addr.s6_addr);
288 lwip_add_route(IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
290 break;
291 default:
292 printlog(LOG_ERR,"unsupported Address Family: %s",arg);
294 freeaddrinfo(res);
298 static void readpassword(char *arg,int unused)
300 passwd=strdup(arg);
303 struct cf {
304 char *tag;
305 void (*f)();
306 int arg;
307 } cft[]= {
308 {"ip4",readip,PF_INET},
309 {"ip6",readip,PF_INET6},
310 {"ip",readip,0},
311 {"defroute4",readdefroute,PF_INET},
312 {"defroute6",readdefroute,PF_INET6},
313 {"defroute",readdefroute,0},
314 {"password",readpassword,0},
315 {NULL,NULL,0}};
317 int readconffile(char *path,struct netif *nif)
319 FILE *f;
320 char buf[BUFSIZE],*s;
321 if (path == NULL && geteuid() == 0)
322 path=ROOTCONFFILE;
323 if (path==NULL)
324 return -1;
325 if((f=fopen(path,"r"))==NULL)
326 return -1;
327 while (fgets(buf,BUFSIZE,f) != NULL) {
328 if ((s=rindex(buf,'\n')) != NULL)
329 *s=0;
330 for(s=buf;*s == ' ' || *s == '\t';s++)
332 if (*s != '#') {
333 struct cf *scf;
334 for (scf=cft;scf->tag != NULL;scf++)
335 if(strncmp(s,scf->tag,strlen(scf->tag)) == 0){
336 s+=strlen(scf->tag);
337 for(;*s == ' ' || *s == '\t';s++)
339 if (*s == '=') s++;
340 for(;*s == ' ' || *s == '\t';s++)
342 scf->f(s,nif,scf->arg);
343 break;
345 if (scf->tag == NULL) {
346 printlog(LOG_ERR,"rc file syntax error: %s",buf);
350 return 0;
353 int addpfd(int fd,intfun cb)
355 if (npfd < MAXFD) {
356 pfd[npfd].fd=fd;
357 pfd[npfd].events=POLLIN|POLLHUP;
358 pfd[npfd].revents=0;
359 fpfd[npfd]=cb;
360 npfd++;
362 return npfd-1;
365 void delpfd(int fn)
367 int i=fn;
368 for (i=fn;i<npfd-1;i++) {
369 pfd[i]=pfd[i+1];
370 fpfd[i]=fpfd[i+1];
371 status[i]=status[i+1];
373 npfd--;
376 int pfdsearch(int fd)
378 int i;
379 for (i=0;i<npfd && pfd[i].fd!=fd;i++)
381 return i;
384 int setfds(fd_set *rds, fd_set *exc)
386 int i,max=0;
387 FD_ZERO(rds);
388 FD_ZERO(exc);
389 for (i=0;i<npfd;i++) {
390 FD_SET(pfd[i].fd,rds);
391 FD_SET(pfd[i].fd,exc);
392 if (pfd[i].fd>max) max=pfd[i].fd;
394 return max+1;
397 int main(int argc, char *argv[])
399 struct netif *nif;
400 int vdefd;
401 char *conffile=NULL;
402 char *nodename=NULL;
403 progname=argv[0];
405 int c;
406 while (1) {
407 int option_index = 0;
409 static struct option long_options[] = {
410 {"daemon", 0, 0, 'd'},
411 {"mgmt", 1, 0, 'M'},
412 {"telnet", 0, 0, 't'},
413 {"web", 0, 0, 'w'},
414 {"help",0,0,'h'},
415 {"rcfile",1,0,'f'},
416 {"nodename",1,0,'n'},
417 {0, 0, 0, 0}
419 c = getopt_long_only (argc, argv, "hdwtM:f:n:",
420 long_options, &option_index);
421 if (c == -1)
422 break;
424 switch (c) {
425 case 'M':
426 mgmt=strdup(optarg);
427 break;
428 case 'f':
429 conffile=strdup(optarg);
430 break;
431 case 'n':
432 nodename=strdup(optarg);
433 break;
434 case 't':
435 telnet=1;
436 break;
437 case 'w':
438 web=1;
439 break;
440 case 'd':
441 daemonize=1;
442 break;
443 case 'h':
444 usage(argv[0]); //implies exit
445 break;
448 if (optind < argc && mgmt==NULL)
449 mgmt=argv[optind];
451 if (mgmt==NULL) {
452 printlog(LOG_ERR,"mgmt_socket not defined");
453 exit(-1);
455 if (telnet==0 && web==0) {
456 printlog(LOG_ERR,"at least one service option (-t -w) must be specified");
457 exit(-1);
459 setsighandlers();
460 if (daemonize && daemon(0, 1)) {
461 printlog(LOG_ERR,"daemon: %s",strerror(errno));
462 exit(1);
464 LOADLWIPV6DL;
465 vdefd=openvdem(mgmt,argv[0],&nif,nodename);
466 if (readconffile(conffile,nif) < 0) {
467 printlog(LOG_ERR,"configuration file not found");
468 exit(1);
470 if (telnet)
471 telnet_init(vdefd);
472 if (web)
473 web_init(vdefd);
475 while (1)
477 int n,m,i;
478 fd_set rds,exc;
479 int max=setfds(&rds,&exc);
480 m=lwip_select(max,&rds,NULL,&exc,NULL);
481 for(i=0; m>0 && i<max; i++) {
482 if (FD_ISSET(i,&rds) || FD_ISSET(i,&exc)) {
483 n=pfdsearch(i);
484 fpfd[n](n,i,vdefd);
485 m--;