pidfile and cleanup mgmt for startup/shutdown scripts
[vde.git] / vdetelweb / vdetelweb.c
blob1fb23cb568dda642c3fd7338a168af03826efe8e
1 /*
2 * VDETELWEB: VDE telnet and WEB interface
4 * vdetelweb.c: main
5 *
6 * Copyright 2005 Renzo Davoli University of Bologna - Italy
7 * --pidfile/-p and cleanup management by Mattia Belletti (C) 2004
8 * (copied from vde_switch code).
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 * $Id$
27 #include <stdio.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <syslog.h>
31 #include <errno.h>
32 #include <netdb.h>
33 #include <libgen.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/select.h>
37 #include <sys/poll.h>
38 #include <sys/utsname.h>
39 #include <linux/un.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <string.h>
43 #include <getopt.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #include "vdetelweb.h"
47 #include <lwipv6.h>
49 int daemonize;
50 int telnet;
51 int web;
52 char *mgmt;
53 char *banner;
54 char *progname;
55 char *prompt;
56 int logok;
57 char *passwd;
58 static char *pidfile = NULL;
59 static char pidfile_path[_POSIX_PATH_MAX];
61 #define MAXFD 16
62 int npfd=0;
63 struct pollfd pfd[MAXFD];
64 typedef int (*intfun)();
65 intfun fpfd[MAXFD];
66 void *status[MAXFD];
68 #define ROOTCONFFILE "/etc/vde/vdetelwebrc"
70 void printlog(int priority, const char *format, ...)
72 va_list arg;
74 va_start (arg, format);
76 if (logok)
77 vsyslog(priority,format,arg);
78 else {
79 fprintf(stderr,"%s: ",progname);
80 vfprintf(stderr,format,arg);
81 fprintf(stderr,"\n");
83 va_end (arg);
87 static void cleanup(void)
89 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
90 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
94 static void sig_handler(int sig)
96 cleanup();
97 signal(sig, SIG_DFL);
98 kill(getpid(), sig);
101 static void setsighandlers()
103 /* setting signal handlers.
104 * * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
105 * * ignores all the others signals which could cause termination. */
106 struct { int sig; const char *name; int ignore; } signals[] = {
107 { SIGHUP, "SIGHUP", 0 },
108 { SIGINT, "SIGINT", 0 },
109 { SIGPIPE, "SIGPIPE", 1 },
110 { SIGALRM, "SIGALRM", 1 },
111 { SIGTERM, "SIGTERM", 0 },
112 { SIGUSR1, "SIGUSR1", 1 },
113 { SIGUSR2, "SIGUSR2", 1 },
114 { SIGPOLL, "SIGPOLL", 1 },
115 { SIGPROF, "SIGPROF", 1 },
116 { SIGVTALRM, "SIGVTALRM", 1 },
117 { SIGSTKFLT, "SIGSTKFLT", 1 },
118 { SIGIO, "SIGIO", 1 },
119 { SIGPWR, "SIGPWR", 1 },
120 { SIGUNUSED, "SIGUNUSED", 1 },
121 { 0, NULL, 0 }
124 int i;
125 for(i = 0; signals[i].sig != 0; i++)
126 if(signal(signals[i].sig,
127 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
128 perror("Setting handler");
131 static void usage(char *progname) {
132 fprintf (stderr,"Usage: %s [-w] [-t] [-d] [-n nodename] [-p pidfile] mgmt_socket\n"
133 " %s [--web] [--telnet] [--daemon] [--nodename nodename] [--pidfile pidfile] mgmt_socket\n",progname,progname);
134 exit(-1);
137 void setprompt(char *ctrl,char *nodename)
139 char buf[BUFSIZE];
140 if (nodename==NULL) {
141 struct utsname un;
142 uname(&un);
143 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",un.nodename,ctrl);
144 } else
145 snprintf(buf,BUFSIZE,"VDE2@%s[%s]: ",nodename,ctrl);
146 prompt=strdup(buf);
149 int openvdem(char *mgmt,char *progname, struct netif **nif,char *nodename)
151 struct sockaddr_un sun;
152 int fd,n;
153 char buf[BUFSIZE+1],*line2,*ctrl;
154 sun.sun_family=PF_UNIX;
155 snprintf(sun.sun_path,UNIX_PATH_MAX,"%s",mgmt);
156 fd=socket(PF_UNIX,SOCK_STREAM,0);
157 if (connect(fd,(struct sockaddr *)(&sun),sizeof(sun)) < 0) {
158 printlog(LOG_ERR,"mgmt connect %s",strerror(errno));
159 exit(-1);
161 if ((n=read(fd,buf,BUFSIZE))<=0) {
162 printlog(LOG_ERR,"banner %s",strerror(errno));
163 exit(-1);
165 buf[n]=0;
166 if ((ctrl=rindex(buf,'\n')) != NULL)
167 *ctrl=0;
168 banner=strdup(buf);
169 write(fd,"ds/showinfo\n",13);
170 if ((n=read(fd,buf,BUFSIZE))<=0) {
171 printlog(LOG_ERR,"read ctl socket %s",strerror(errno));
172 exit(-1);
174 buf[n]=0;
175 if ((line2=index(buf,'\n')) == NULL) {
176 printlog(LOG_ERR,"read ctl socket parse error 1");
177 exit(-1);
179 line2++;
180 if (strncmp(line2,"ctl dir ",8) != 0) {
181 printlog(LOG_ERR,"read ctl socket parse error");
182 exit(-1);
184 for(ctrl=line2+8;*ctrl!='\n' && ctrl<buf+n;ctrl++)
186 *ctrl=0;
187 ctrl=line2+8;
188 setprompt(ctrl,nodename);
189 strcat(ctrl,"[0]");
190 *nif=lwip_vdeif_add(ctrl);
191 if (*nif == NULL) {
192 printlog(LOG_ERR,"cannot connect to the switch");
193 exit(-1);
195 lwip_ifup(*nif);
197 return fd;
200 static void bitno2mask(unsigned char *addr,int bitno,int len)
202 int i;
203 for(i=0;i<len;i++,bitno -= 8) {
204 if (bitno >= 8)
205 addr[i]=255;
206 else if (bitno <= 0)
207 addr[i]=0;
208 else
209 addr[i]=256 - (1<<(8-bitno));
213 static void sockaddr2ip_6addr(struct ip_addr *ipaddrp,unsigned char *addr)
215 IP6_ADDR(ipaddrp,
216 (addr[0]<<8)|addr[1],
217 (addr[2]<<8)|addr[3],
218 (addr[4]<<8)|addr[5],
219 (addr[6]<<8)|addr[7],
220 (addr[8]<<8)|addr[9],
221 (addr[10]<<8)|addr[11],
222 (addr[12]<<8)|addr[13],
223 (addr[14]<<8)|addr[15]);
226 static void readip(char *arg,struct netif *nif,int af)
228 char *bit=rindex(arg,'/');
229 if (bit == 0)
230 printlog(LOG_ERR,"ip addresses must include the netmask i.e. addr/maskbits");
231 else {
232 int bitno=atoi(bit+1);
233 *bit=0;
234 struct addrinfo *res,hint;
235 struct ip_addr ipaddr,netmask;
236 int err;
237 memset(&hint,0,sizeof(hint));
238 hint.ai_family=af;
239 if (err=getaddrinfo(arg,NULL,&hint,&res))
240 printlog(LOG_ERR,"ip address %s error %s",arg,gai_strerror(err));
241 else {
242 switch(res->ai_family) {
243 case PF_INET: {
244 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
245 int addrh=ntohl(in->sin_addr.s_addr);
246 unsigned char i,addr[4];
247 for (i=0;i<4;i++,addrh>>=8)
248 addr[3-i]=addrh;
249 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
250 bitno2mask(addr,bitno,4);
251 IP64_MASKADDR(&netmask, addr[0],addr[1],addr[2],addr[3]);
252 lwip_add_addr(nif,&ipaddr,&netmask);
254 break;
255 case PF_INET6:{
256 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
257 char i;
258 unsigned char *addr=in->sin6_addr.s6_addr;
259 sockaddr2ip_6addr(&ipaddr,addr);
260 bitno2mask(addr,bitno,16);
261 sockaddr2ip_6addr(&netmask,addr);
262 lwip_add_addr(nif,&ipaddr,&netmask);
264 break;
265 default:
266 printlog(LOG_ERR,"unsupported Address Family: %s",arg);
268 freeaddrinfo(res);
273 static void readdefroute(char *arg,struct netif *nif,int af)
275 struct addrinfo *res,hint;
276 struct ip_addr ipaddr,netmask;
277 int err;
278 memset(&hint,0,sizeof(hint));
279 hint.ai_family=af;
280 if (err=getaddrinfo(arg,NULL,&hint,&res))
281 printlog(LOG_ERR,"ip address %s error %s",arg,gai_strerror(err));
282 else {
283 switch(res->ai_family) {
284 case PF_INET: {
285 struct sockaddr_in *in=(struct sockaddr_in *)res->ai_addr;
286 int addrh=ntohl(in->sin_addr.s_addr);
287 unsigned char i,addr[4];
288 for (i=0;i<4;i++,addrh>>=8)
289 addr[3-i]=addrh;
290 IP64_ADDR(&ipaddr, addr[0],addr[1],addr[2],addr[3]);
291 lwip_add_route(IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
293 break;
294 case PF_INET6:{
295 struct sockaddr_in6 *in=(struct sockaddr_in6 *)res->ai_addr;
296 char i;
297 sockaddr2ip_6addr(&ipaddr,in->sin6_addr.s6_addr);
298 lwip_add_route(IP_ADDR_ANY,IP_ADDR_ANY,&ipaddr,nif,0);
300 break;
301 default:
302 printlog(LOG_ERR,"unsupported Address Family: %s",arg);
304 freeaddrinfo(res);
308 static void readpassword(char *arg,int unused)
310 passwd=strdup(arg);
313 struct cf {
314 char *tag;
315 void (*f)();
316 int arg;
317 } cft[]= {
318 {"ip4",readip,PF_INET},
319 {"ip6",readip,PF_INET6},
320 {"ip",readip,0},
321 {"defroute4",readdefroute,PF_INET},
322 {"defroute6",readdefroute,PF_INET6},
323 {"defroute",readdefroute,0},
324 {"password",readpassword,0},
325 {NULL,NULL,0}};
327 int readconffile(char *path,struct netif *nif)
329 FILE *f;
330 char buf[BUFSIZE],*s;
331 if (path == NULL && geteuid() == 0)
332 path=ROOTCONFFILE;
333 if (path==NULL)
334 return -1;
335 if((f=fopen(path,"r"))==NULL)
336 return -1;
337 while (fgets(buf,BUFSIZE,f) != NULL) {
338 if ((s=rindex(buf,'\n')) != NULL)
339 *s=0;
340 for(s=buf;*s == ' ' || *s == '\t';s++)
342 if (*s != '#') {
343 struct cf *scf;
344 for (scf=cft;scf->tag != NULL;scf++)
345 if(strncmp(s,scf->tag,strlen(scf->tag)) == 0){
346 s+=strlen(scf->tag);
347 for(;*s == ' ' || *s == '\t';s++)
349 if (*s == '=') s++;
350 for(;*s == ' ' || *s == '\t';s++)
352 scf->f(s,nif,scf->arg);
353 break;
355 if (scf->tag == NULL) {
356 printlog(LOG_ERR,"rc file syntax error: %s",buf);
360 return 0;
363 int addpfd(int fd,intfun cb)
365 if (npfd < MAXFD) {
366 pfd[npfd].fd=fd;
367 pfd[npfd].events=POLLIN|POLLHUP;
368 pfd[npfd].revents=0;
369 fpfd[npfd]=cb;
370 npfd++;
372 return npfd-1;
375 void delpfd(int fn)
377 int i=fn;
378 for (i=fn;i<npfd-1;i++) {
379 pfd[i]=pfd[i+1];
380 fpfd[i]=fpfd[i+1];
381 status[i]=status[i+1];
383 npfd--;
386 int pfdsearch(int fd)
388 int i;
389 for (i=0;i<npfd && pfd[i].fd!=fd;i++)
391 return i;
394 int setfds(fd_set *rds, fd_set *exc)
396 int i,max=0;
397 FD_ZERO(rds);
398 FD_ZERO(exc);
399 for (i=0;i<npfd;i++) {
400 FD_SET(pfd[i].fd,rds);
401 FD_SET(pfd[i].fd,exc);
402 if (pfd[i].fd>max) max=pfd[i].fd;
404 return max+1;
407 static void save_pidfile()
409 if(pidfile[0] != '/')
410 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
411 else
412 strcpy(pidfile_path, pidfile);
414 int fd = open(pidfile_path,
415 O_WRONLY | O_CREAT | O_EXCL,
416 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
417 FILE *f;
419 if(fd == -1) {
420 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
421 exit(1);
424 if((f = fdopen(fd, "w")) == NULL) {
425 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
426 exit(1);
429 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
430 printlog(LOG_ERR, "Error in writing pidfile");
431 exit(1);
434 fclose(f);
437 int main(int argc, char *argv[])
439 struct netif *nif;
440 int vdefd;
441 char *conffile=NULL;
442 char *nodename=NULL;
443 progname=argv[0];
445 int c;
446 while (1) {
447 int option_index = 0;
449 static struct option long_options[] = {
450 {"daemon", 0, 0, 'd'},
451 {"mgmt", 1, 0, 'M'},
452 {"telnet", 0, 0, 't'},
453 {"web", 0, 0, 'w'},
454 {"help",0,0,'h'},
455 {"rcfile",1,0,'f'},
456 {"nodename",1,0,'n'},
457 {"pidfile", 1, 0, 'p'},
458 {0, 0, 0, 0}
460 c = getopt_long_only (argc, argv, "hdwtM:f:n:",
461 long_options, &option_index);
462 if (c == -1)
463 break;
465 switch (c) {
466 case 'M':
467 mgmt=strdup(optarg);
468 break;
469 case 'f':
470 conffile=strdup(optarg);
471 break;
472 case 'n':
473 nodename=strdup(optarg);
474 break;
475 case 't':
476 telnet=1;
477 break;
478 case 'w':
479 web=1;
480 break;
481 case 'd':
482 daemonize=1;
483 break;
484 case 'p':
485 pidfile=strdup(optarg);
486 break;
487 case 'h':
488 usage(argv[0]); //implies exit
489 break;
492 if (optind < argc && mgmt==NULL)
493 mgmt=argv[optind];
495 if (mgmt==NULL) {
496 printlog(LOG_ERR,"mgmt_socket not defined");
497 exit(-1);
499 if (telnet==0 && web==0) {
500 printlog(LOG_ERR,"at least one service option (-t -w) must be specified");
501 exit(-1);
503 atexit(cleanup);
504 setsighandlers();
505 if (daemonize) {
506 openlog(basename(argv[0]), LOG_PID, 0);
507 logok=1;
508 syslog(LOG_INFO,"VDETELWEB started");
511 /* saves current path in pidfile_path, because otherwise with daemonize() we
512 * forget it */
513 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
514 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
515 exit(1);
517 strcat(pidfile_path, "/");
519 if (daemonize && daemon(0, 1)) {
520 printlog(LOG_ERR,"daemon: %s",strerror(errno));
521 exit(1);
523 /* once here, we're sure we're the true process which will continue as a
524 * server: save PID file if needed */
525 if(pidfile) save_pidfile();
527 LOADLWIPV6DL;
528 vdefd=openvdem(mgmt,argv[0],&nif,nodename);
529 if (readconffile(conffile,nif) < 0) {
530 printlog(LOG_ERR,"configuration file not found");
531 exit(1);
533 if (telnet)
534 telnet_init(vdefd);
535 if (web)
536 web_init(vdefd);
538 while (1)
540 int n,m,i;
541 fd_set rds,exc;
542 int max=setfds(&rds,&exc);
543 m=lwip_select(max,&rds,NULL,&exc,NULL);
544 for(i=0; m>0 && i<max; i++) {
545 if (FD_ISSET(i,&rds) || FD_ISSET(i,&exc)) {
546 n=pfdsearch(i);
547 fpfd[n](n,i,vdefd);
548 m--;