1 /* Copyright 2005,2006,2007 Renzo Davoli - VDE-2
2 * 2007 co-authors Ludovico Gardenghi, Filippo Giunchedi, Luca Bigliardi
3 * --pidfile/-p and cleanup management by Mattia Belletti (C) 2004.
4 * Licensed under the GPLv2
17 #include <sys/types.h>
19 #include <sys/ioctl.h>
20 #include <sys/socket.h>
31 #include <vdecommon.h>
33 #include "../vde_switch/switch.h"
34 #include "sockutils.h"
39 static struct swmodule swmi
;
40 extern time_t starting_time
;
44 static char *pidfile
= NULL
;
45 static char pidfile_path
[PATH_MAX
];
46 static int daemonize
= 0;
47 static unsigned int console_type
=-1;
48 static unsigned int mgmt_ctl
=-1;
49 static unsigned int mgmt_data
=-1;
50 static int mgmt_mode
= 0600;
51 static char *mgmt_socket
= NULL
;
52 static char header
[]="KVDE switch V.%s\n(C) Virtual Square Team (coord. R. Davoli) 2005,...,2008 - GPLv2\n";
53 static char prompt
[]="\nvde$ ";
55 static struct comlist
*clh
=NULL
;
56 static struct comlist
**clt
=&clh
;
59 static struct dbgcl
*dbgclh
=NULL
;
60 static struct dbgcl
**dbgclt
=&dbgclh
;
61 #define MGMTPORTNEW (dl)
62 #define MGMTPORTDEL (dl+1)
63 static struct dbgcl dl
[]= {
64 {"mgmt/+",NULL
,D_MGMT
|D_PLUS
},
65 {"mgmt/-",NULL
,D_MGMT
|D_MINUS
}
69 static struct plugin
*pluginh
=NULL
;
70 static struct plugin
**plugint
=&pluginh
;
73 void addcl(int ncl
,struct comlist
*cl
)
76 for (i
=0;i
<ncl
;i
++,cl
++) {
83 void delcl(int ncl
,struct comlist
*cl
)
86 for (i
=0;i
<ncl
;i
++,cl
++) {
87 struct comlist
**p
=&clh
;
100 void adddbgcl(int ncl
,struct dbgcl
*cl
)
103 for (i
=0;i
<ncl
;i
++,cl
++) {
110 void deldbgcl(int ncl
,struct dbgcl
*cl
)
113 for (i
=0;i
<ncl
;i
++,cl
++) {
114 struct dbgcl
**p
=&dbgclh
;
117 if (cl
->fds
) free(cl
->fds
);
118 if (cl
->fun
) free(cl
->fun
);
130 void addplugin(struct plugin
*cl
)
137 void delplugin(struct plugin
*cl
)
139 struct plugin
**p
=&pluginh
;
151 void printlog(int priority
, const char *format
, ...)
155 va_start (arg
, format
);
158 vsyslog(priority
,format
,arg
);
160 fprintf(stderr
,"%s: ",prog
);
161 vfprintf(stderr
,format
,arg
);
162 fprintf(stderr
,"\n");
168 void printoutc(int fd
, const char *format
, ...)
172 va_start (arg
, format
);
175 printlog(LOG_INFO
,format
,arg
);
178 char outbuf
[MAXCMD
+1];
179 vsnprintf(outbuf
,MAXCMD
,format
,arg
);
181 write(fd
,outbuf
,strlen(outbuf
));
188 void printoutc(FILE *f
, const char *format
, ...)
192 va_start (arg
, format
);
194 vfprintf(f
,format
,arg
);
197 printlog(LOG_INFO
,format
,arg
);
202 static char _dbgnl
='\n';
203 void debugout(struct dbgcl
* cl
, const char *format
, ...)
209 struct iovec iov
[]={{NULL
,0},{NULL
,0},{&_dbgnl
,1}};
211 va_start (arg
, format
);
212 iov
[0].iov_len
=asprintf(&header
,"3%03o %s ",cl
->tag
& 0777,cl
->path
);
213 iov
[0].iov_base
=header
;
214 iov
[1].iov_len
=vasprintf(&msg
,format
,arg
);
218 for (i
=0; i
<cl
->nfds
; i
++)
219 writev(cl
->fds
[i
],iov
,3);
224 void eventout(struct dbgcl
* cl
, ...)
228 for (i
=0; i
<cl
->nfun
; i
++) {
230 (cl
->fun
[i
])(cl
,arg
);
235 int packetfilter(struct dbgcl
* cl
, ...)
241 (void) va_arg(arg
,int); /*port*/
242 (void) va_arg(arg
,char *); /*buf*/
245 for (i
=0; i
<cl
->nfun
&& len
>0; i
++) {
247 int rv
=(cl
->fun
[i
])(cl
,arg
);
259 void setmgmtperm(char *path
)
261 chmod(path
,mgmt_mode
);
264 static int help(FILE *fd
,char *arg
)
268 printoutc(fd
,"%-18s %-15s %s","COMMAND PATH","SYNTAX","HELP");
269 printoutc(fd
,"%-18s %-15s %s","------------","--------------","------------");
270 for (p
=clh
;p
!=NULL
;p
=p
->next
)
271 if (strncmp(p
->path
,arg
,n
) == 0)
272 printoutc(fd
,"%-18s %-15s %s",p
->path
,p
->syntax
,p
->help
);
276 static int handle_cmd(int type
,int fd
,char *inbuf
)
280 while (*inbuf
== ' ' || *inbuf
== '\t') inbuf
++;
281 if (*inbuf
!= '\0' && *inbuf
!= '#') {
286 f
=open_memstream(&outbuf
,&outbufsize
);
289 for (p
=clh
;p
!=NULL
&& (p
->doit
==NULL
|| strncmp(p
->path
,inbuf
,strlen(p
->path
))!=0); p
=p
->next
)
293 inbuf
+= strlen(p
->path
);
294 while (*inbuf
== ' ' || *inbuf
== '\t') inbuf
++;
295 if (p
->type
& WITHFD
) {
296 if (p
->type
& WITHFILE
) {
297 printoutc(f
,"0000 DATA END WITH '.'");
298 switch(p
->type
& ~(WITHFILE
| WITHFD
)){
299 case NOARG
: rv
=p
->doit(f
,fd
); break;
300 case INTARG
: rv
=p
->doit(f
,fd
,atoi(inbuf
)); break;
301 case STRARG
: rv
=p
->doit(f
,fd
,inbuf
); break;
305 switch(p
->type
& ~WITHFD
){
306 case NOARG
: rv
=p
->doit(fd
); break;
307 case INTARG
: rv
=p
->doit(fd
,atoi(inbuf
)); break;
308 case STRARG
: rv
=p
->doit(fd
,inbuf
); break;
311 } else if (p
->type
& WITHFILE
) {
312 printoutc(f
,"0000 DATA END WITH '.'");
313 switch(p
->type
& ~WITHFILE
){
314 case NOARG
: rv
=p
->doit(f
); break;
315 case INTARG
: rv
=p
->doit(f
,atoi(inbuf
)); break;
316 case STRARG
: rv
=p
->doit(f
,inbuf
); break;
321 case NOARG
: rv
=p
->doit(); break;
322 case INTARG
: rv
=p
->doit(atoi(inbuf
)); break;
323 case STRARG
: rv
=p
->doit(inbuf
); break;
327 if (rv
>= 0 && (rv
> 0 || fd
>= 0))
328 printoutc(f
,"1%03d %s",rv
,strerror(rv
));
331 write(fd
,outbuf
,outbufsize
);
338 static int runscript(int fd
,char *path
)
340 FILE *f
=fopen(path
,"r");
345 while (fgets(buf
,MAXCMD
,f
) != NULL
) {
346 if (strlen(buf
) > 1 && buf
[strlen(buf
)-1]=='\n') buf
[strlen(buf
)-1]= '\0';
348 char *scriptprompt
=NULL
;
349 asprintf(&scriptprompt
,"vde[%s]: %s",path
,buf
);
350 write(fd
,scriptprompt
,strlen(scriptprompt
));
353 handle_cmd(mgmt_data
, fd
, buf
);
359 void loadrcfile(void)
362 runscript(-1,rcfile
);
365 snprintf(path
,PATH_MAX
,"%s/.vde2/vde_switch.rc",getenv("HOME"));
366 if (access(path
,R_OK
) == 0)
369 if (access(STDRCFILE
,R_OK
) == 0)
370 runscript(-1,STDRCFILE
);
375 void mgmtnewfd(int new)
378 if(fcntl(new, F_SETFL
, O_NONBLOCK
) < 0){
379 printlog(LOG_WARNING
,"mgmt fcntl - setting O_NONBLOCK %s",strerror(errno
));
384 add_fd(new,mgmt_data
,NULL
);
385 EVENTOUT(MGMTPORTNEW
,new);
386 snprintf(buf
,MAXCMD
,header
,PACKAGE_VERSION
);
387 write(new,buf
,strlen(buf
));
388 write(new,prompt
,strlen(prompt
));
392 static int debugdel(int fd
,char *arg
);
394 static char *EOS
="9999 END OF SESSION";
395 static void handle_io(unsigned char type
,int fd
,int revents
,void *unused
)
398 if (type
!= mgmt_ctl
) {
401 if (revents
& POLLIN
) {
402 n
= read(fd
, buf
, sizeof(buf
));
404 printlog(LOG_WARNING
,"Reading from mgmt %s",strerror(errno
));
408 if (type
== console_type
) {
409 printlog(LOG_WARNING
,"EOF on stdin, cleaning up and exiting");
420 if (n
>0 && buf
[n
-1] == '\n') buf
[n
-1] = 0;
421 cmdout
=handle_cmd(type
,(type
==console_type
)?STDOUT_FILENO
:fd
,buf
);
423 write(fd
,prompt
,strlen(prompt
));
425 if(type
==mgmt_data
) {
426 write(fd
,EOS
,strlen(EOS
));
428 EVENTOUT(MGMTPORTDEL
,fd
);
437 } else {/* mgmt ctl */
438 struct sockaddr addr
;
443 new = accept(fd
, &addr
, &len
);
445 printlog(LOG_WARNING
,"mgmt accept %s",strerror(errno
));
448 if(fcntl(new, F_SETFL
, O_NONBLOCK
) < 0){
449 printlog(LOG_WARNING
,"mgmt fcntl - setting O_NONBLOCK %s",strerror(errno
));
454 add_fd(new,mgmt_data
,NULL
);
455 EVENTOUT(MGMTPORTNEW
,new);
456 snprintf(buf
,MAXCMD
,header
,PACKAGE_VERSION
);
457 write(new,buf
,strlen(buf
));
458 write(new,prompt
,strlen(prompt
));
462 static void save_pidfile()
464 if(pidfile
[0] != '/')
465 strncat(pidfile_path
, pidfile
, PATH_MAX
- strlen(pidfile_path
) - 1);
467 strncpy(pidfile_path
, pidfile
, PATH_MAX
- 1);
469 int fd
= open(pidfile_path
,
470 O_WRONLY
| O_CREAT
| O_EXCL
,
471 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
475 printlog(LOG_ERR
, "Error in pidfile creation: %s", strerror(errno
));
479 if((f
= fdopen(fd
, "w")) == NULL
) {
480 printlog(LOG_ERR
, "Error in FILE* construction: %s", strerror(errno
));
484 if(fprintf(f
, "%ld\n", (long int)getpid()) <= 0) {
485 printlog(LOG_ERR
, "Error in writing pidfile");
492 static void cleanup(unsigned char type
,int fd
,void *unused
)
495 if((pidfile
!= NULL
) && unlink(pidfile_path
) < 0) {
496 printlog(LOG_WARNING
,"Couldn't remove pidfile '%s': %s", pidfile
, strerror(errno
));
500 if (type
== mgmt_ctl
&& mgmt_socket
!= NULL
) {
506 #define MGMTMODEARG 0x100
508 static struct option long_options
[] = {
509 {"daemon", 0, 0, 'd'},
510 {"pidfile", 1, 0, 'p'},
511 {"rcfile", 1, 0, 'f'},
513 {"mgmtmode", 1, 0, MGMTMODEARG
},
515 {"debugclients",1,0,'D'},
519 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
521 static void usage(void)
524 "(opts from consmgmt module)\n"
525 " -d, --daemon Daemonize vde_switch once run\n"
526 " -p, --pidfile PIDFILE Write pid of daemon to PIDFILE\n"
527 " -f, --rcfile Configuration file (overrides %s and ~/.vderc)\n"
528 " -M, --mgmt SOCK path of the management UNIX socket\n"
529 " --mgmtmode MODE management UNIX socket access mode (octal)\n"
531 " -D, --debugclients # number of debug clients allowed\n"
536 static int parseopt(int c
, char *optarg
)
544 pidfile
=strdup(optarg
);
547 rcfile
=strdup(optarg
);
550 mgmt_socket
=strdup(optarg
);
553 sscanf(optarg
,"%o",&mgmt_mode
);
561 static void init(void)
564 openlog(basename(prog
), LOG_PID
, 0);
566 syslog(LOG_INFO
,"VDE_SWITCH started");
568 /* add stdin (if tty), connect and data fds to the set of fds we wait for
572 console_type
=add_type(&swmi
,0);
573 add_fd(0,console_type
,NULL
);
576 /* saves current path in pidfile_path, because otherwise with daemonize() we
578 if(getcwd(pidfile_path
, PATH_MAX
-2) == NULL
) {
579 printlog(LOG_ERR
, "getcwd: %s", strerror(errno
));
582 strcat(pidfile_path
, "/");
583 if (daemonize
&& daemon(0, 0)) {
584 printlog(LOG_ERR
,"daemon: %s",strerror(errno
));
588 /* once here, we're sure we're the true process which will continue as a
589 * * server: save PID file if needed */
590 if(pidfile
) save_pidfile();
592 if(mgmt_socket
!= NULL
) {
594 struct sockaddr_un sun
;
597 if((mgmtconnfd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0){
598 printlog(LOG_ERR
,"mgmt socket: %s",strerror(errno
));
601 if(setsockopt(mgmtconnfd
, SOL_SOCKET
, SO_REUSEADDR
, (char *) &one
,
603 printlog(LOG_ERR
,"mgmt setsockopt: %s",strerror(errno
));
606 if(fcntl(mgmtconnfd
, F_SETFL
, O_NONBLOCK
) < 0){
607 printlog(LOG_ERR
,"Setting O_NONBLOCK on mgmt fd: %s",strerror(errno
));
610 sun
.sun_family
= PF_UNIX
;
611 snprintf(sun
.sun_path
,sizeof(sun
.sun_path
),"%s",mgmt_socket
);
612 if(bind(mgmtconnfd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
613 if((errno
== EADDRINUSE
) && still_used(&sun
)) return;
614 else if(bind(mgmtconnfd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
615 printlog(LOG_ERR
,"mgmt bind %s",strerror(errno
));
619 chmod(sun
.sun_path
,mgmt_mode
);
620 if(listen(mgmtconnfd
, 15) < 0){
621 printlog(LOG_ERR
,"mgmt listen: %s",strerror(errno
));
624 mgmt_ctl
=add_type(&swmi
,0);
625 mgmt_data
=add_type(&swmi
,0);
626 add_fd(mgmtconnfd
,mgmt_ctl
,NULL
);
630 static int vde_logout()
635 static int vde_shutdown()
637 printlog(LOG_WARNING
,"Shutdown from mgmt command");
641 static int showinfo(FILE *fd
)
643 printoutc(fd
,header
,PACKAGE_VERSION
);
644 printoutc(fd
,"pid %d MAC %02x:%02x:%02x:%02x:%02x:%02x uptime %d",getpid(),
645 switchmac
[0], switchmac
[1], switchmac
[2], switchmac
[3], switchmac
[4], switchmac
[5],
646 time(NULL
)-starting_time
);
648 printoutc(fd
,"mgmt %s perm 0%03o",mgmt_socket
,mgmt_mode
);
653 static int debuglist(FILE *f
,int fd
,char *path
)
655 #define DEBUGFORMAT1 "%-22s %-3s %-6s %s"
656 #define DEBUGFORMAT2 "%-22s %03o %-6s %s"
660 printoutc(f
,DEBUGFORMAT1
,"CATEGORY", "TAG", "STATUS", "HELP");
661 printoutc(f
,DEBUGFORMAT1
,"------------","---","------", "----");
662 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
){
663 if (p
->help
&& strncmp(p
->path
, path
, strlen(path
)) == 0) {
664 for (i
=0; i
<p
->nfds
&& p
->fds
[i
] != fd
; i
++)
667 printoutc(f
, DEBUGFORMAT2
, p
->path
, p
->tag
&0777, i
<p
->nfds
? "ON" : "OFF", p
->help
);
673 /* EINVAL -> no matches
674 * EEXIST -> all the matches already include fd
675 * ENOMEM -> fd buffer realloc failed
677 static int debugadd(int fd
,char *path
) {
680 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
) {
681 if (p
->help
&& strncmp(p
->path
, path
, strlen(path
)) == 0) {
683 if (rv
==EINVAL
) rv
=EEXIST
;
684 for(i
=0;i
<p
->nfds
&& (p
->fds
[i
] != fd
); i
++)
688 int newsize
=p
->maxfds
+DBGCLSTEP
;
689 p
->fds
=realloc(p
->fds
,newsize
*sizeof(int));
694 if (rv
!= ENOMEM
) rv
=0;
700 if (rv
!= ENOMEM
) rv
=0;
708 /* EINVAL -> no matches
709 * ENOENT -> all the matches do not include fd
711 static int debugdel(int fd
,char *path
) {
714 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
){
715 if (strncmp(p
->path
, path
, strlen(path
)) == 0) {
717 if (rv
==EINVAL
) rv
=ENOENT
;
718 for(i
=0;i
<p
->nfds
&& (p
->fds
[i
] != fd
); i
++)
721 p
->nfds
--; /* the last one */
722 p
->fds
[i
]=p
->fds
[p
->nfds
]; /* swap it with the deleted element*/
730 int eventadd(int (*fun
)(),char *path
,void *arg
) {
733 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
) {
734 if (strncmp(p
->path
, path
, strlen(path
)) == 0) {
736 if (rv
==EINVAL
) rv
=EEXIST
;
737 for(i
=0;i
<p
->nfun
&& (p
->fun
[i
] != fun
); i
++)
741 int newsize
=p
->maxfun
+DBGCLSTEP
;
742 p
->fun
=realloc(p
->fun
,newsize
*sizeof(int));
743 p
->funarg
=realloc(p
->funarg
,newsize
*sizeof(void *));
744 if (p
->fun
&& p
->funarg
) {
749 if (rv
!= ENOMEM
) rv
=0;
755 if (rv
!= ENOMEM
) rv
=0;
763 /* EINVAL -> no matches
764 * ENOENT -> all the matches do not include fun
766 int eventdel(int (*fun
)(),char *path
,void *arg
) {
769 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
){
770 if (strncmp(p
->path
, path
, strlen(path
)) == 0) {
772 if (rv
==EINVAL
) rv
=ENOENT
;
773 for(i
=0;i
<p
->nfun
&& (p
->fun
[i
] != fun
) && (p
->funarg
[i
] != arg
); i
++)
776 p
->nfun
--; /* the last one */
777 p
->fun
[i
]=p
->fun
[p
->nfun
]; /* swap it with the deleted element*/
788 static int pluginlist(FILE *f
,char *arg
)
790 #define PLUGINFMT "%-22s %s"
793 printoutc(f
,PLUGINFMT
,"NAME", "HELP");
794 printoutc(f
,PLUGINFMT
,"------------","----");
795 for (p
=pluginh
; p
!=NULL
; p
=p
->next
){
796 if (strncmp(p
->name
, arg
, strlen(arg
)) == 0) {
797 printoutc(f
,PLUGINFMT
,p
->name
,p
->help
);
804 static int pluginadd(char *arg
) {
808 if ((handle
=dlopen(arg
,RTLD_LAZY
)) != NULL
) {
809 if ((p
=(struct plugin
*) dlsym(handle
,"vde_plugin_data")) != NULL
) {
810 if (p
->handle
!= NULL
) { /* this dyn library is already loaded*/
825 static int plugindel(char *arg
) {
827 struct plugin
**p
=&pluginh
;
829 if (strncmp((*p
)->name
, arg
, strlen(arg
)) == 0
830 && ((*p
)->handle
!= NULL
)) {
831 struct plugin
*this=*p
;
833 dlclose(this->handle
);
842 static struct comlist cl
[]={
843 {"help","[arg]","Help (limited to arg when specified)",help
,STRARG
| WITHFILE
},
844 {"logout","","logout from this mgmt terminal",vde_logout
,NOARG
},
845 {"shutdown","","shutdown of the switch",vde_shutdown
,NOARG
},
846 {"showinfo","","show switch version and info",showinfo
,NOARG
|WITHFILE
},
847 {"load","path","load a configuration script",runscript
,STRARG
|WITHFD
},
849 {"debug","============","DEBUG MENU",NULL
,NOARG
},
850 {"debug/list","","list debug categories",debuglist
,STRARG
|WITHFILE
|WITHFD
},
851 {"debug/add","dbgpath","enable debug info for a given category",debugadd
,WITHFD
|STRARG
},
852 {"debug/del","dbgpath","disable debug info for a given category",debugdel
,WITHFD
|STRARG
},
855 {"plugin","============","PLUGINS MENU",NULL
,NOARG
},
856 {"plugin/list","","list plugins",pluginlist
,STRARG
|WITHFILE
},
857 {"plugin/add","library","load a plugin",pluginadd
,STRARG
},
858 {"plugin/del","name","unload a plugin",plugindel
,STRARG
},
862 void start_consmgmt(void)
864 swmi
.swmname
="console-mgmt";
865 swmi
.swmnopts
=Nlong_options
;
866 swmi
.swmopts
=long_options
;
868 swmi
.parseopt
=parseopt
;
870 swmi
.handle_io
=handle_io
;
871 swmi
.cleanup
=cleanup
;