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
18 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
32 #include <vdecommon.h>
36 #include "sockutils.h"
43 static struct swmodule swmi
;
47 static char *pidfile
= NULL
;
48 static char pidfile_path
[PATH_MAX
];
49 static int daemonize
= 0;
50 static unsigned int console_type
=-1;
51 static unsigned int mgmt_ctl
=-1;
52 static unsigned int mgmt_data
=-1;
53 static int mgmt_mode
= 0600;
54 static gid_t mgmt_group
= -1;
56 static char *mgmt_socket
= NULL
;
57 static char header
[]="VDE switch V.%s\n(C) Virtual Square Team (coord. R. Davoli) 2005,2006,2007 - GPLv2\n";
58 static char prompt
[]="\nvde$ ";
60 static struct comlist
*clh
=NULL
;
61 static struct comlist
**clt
=&clh
;
64 static struct dbgcl
*dbgclh
=NULL
;
65 static struct dbgcl
**dbgclt
=&dbgclh
;
66 #define MGMTPORTNEW (dl)
67 #define MGMTPORTDEL (dl+1)
68 #define MGMTSIGHUP (dl+2)
69 static struct dbgcl dl
[]= {
70 {"mgmt/+",NULL
,D_MGMT
|D_PLUS
},
71 {"mgmt/-",NULL
,D_MGMT
|D_MINUS
},
72 {"sig/hup",NULL
,D_SIG
|D_HUP
}
76 static struct plugin
*pluginh
=NULL
;
77 static struct plugin
**plugint
=&pluginh
;
80 void addcl(int ncl
,struct comlist
*cl
)
83 for (i
=0;i
<ncl
;i
++,cl
++) {
90 void delcl(int ncl
,struct comlist
*cl
)
93 for (i
=0;i
<ncl
;i
++,cl
++) {
94 register struct comlist
**p
=&clh
;
107 void adddbgcl(int ncl
,struct dbgcl
*cl
)
110 for (i
=0;i
<ncl
;i
++,cl
++) {
117 void deldbgcl(int ncl
,struct dbgcl
*cl
)
120 for (i
=0;i
<ncl
;i
++,cl
++) {
121 register struct dbgcl
**p
=&dbgclh
;
124 if (cl
->fds
) free(cl
->fds
);
125 if (cl
->fun
) free(cl
->fun
);
137 void addplugin(struct plugin
*cl
)
144 void delplugin(struct plugin
*cl
)
146 register struct plugin
**p
=plugint
=&pluginh
;
158 void printlog(int priority
, const char *format
, ...)
162 va_start (arg
, format
);
165 vsyslog(priority
,format
,arg
);
167 fprintf(stderr
,"%s: ",prog
);
168 vfprintf(stderr
,format
,arg
);
169 fprintf(stderr
,"\n");
174 void printoutc(FILE *f
, const char *format
, ...)
178 va_start (arg
, format
);
180 vfprintf(f
,format
,arg
);
183 printlog(LOG_INFO
,format
,arg
);
188 static char _dbgnl
='\n';
189 void debugout(struct dbgcl
* cl
, const char *format
, ...)
195 struct iovec iov
[]={{NULL
,0},{NULL
,0},{&_dbgnl
,1}};
197 va_start (arg
, format
);
198 iov
[0].iov_len
=asprintf(&header
,"3%03o %s ",cl
->tag
& 0777,cl
->path
);
199 iov
[0].iov_base
=header
;
200 iov
[1].iov_len
=vasprintf(&msg
,format
,arg
);
204 for (i
=0; i
<cl
->nfds
; i
++)
205 writev(cl
->fds
[i
],iov
,3);
210 void eventout(struct dbgcl
* cl
, ...)
214 for (i
=0; i
<cl
->nfun
; i
++) {
216 (cl
->fun
[i
])(cl
,cl
->funarg
[i
],arg
);
221 int packetfilter(struct dbgcl
* cl
, ...)
227 (void) va_arg(arg
,int); /*port*/
228 (void) va_arg(arg
,char *); /*buf*/
231 for (i
=0; i
<cl
->nfun
&& len
>0; i
++) {
233 int rv
=(cl
->fun
[i
])(cl
,cl
->funarg
[i
],arg
);
245 void setmgmtperm(char *path
)
247 chmod(path
,mgmt_mode
);
248 chown(path
, -1, mgmt_group
);
251 static int help(FILE *fd
,char *arg
)
255 printoutc(fd
,"%-18s %-15s %s","COMMAND PATH","SYNTAX","HELP");
256 printoutc(fd
,"%-18s %-15s %s","------------","--------------","------------");
257 for (p
=clh
;p
!=NULL
;p
=p
->next
)
258 if (strncmp(p
->path
,arg
,n
) == 0)
259 printoutc(fd
,"%-18s %-15s %s",p
->path
,p
->syntax
,p
->help
);
263 static int handle_cmd(int type
,int fd
,char *inbuf
)
267 while (*inbuf
== ' ' || *inbuf
== '\t') inbuf
++;
268 if (*inbuf
!= '\0' && *inbuf
!= '#') {
271 FILE *f
=open_memstream(&outbuf
,&outbufsize
);
272 for (p
=clh
;p
!=NULL
&& (p
->doit
==NULL
|| strncmp(p
->path
,inbuf
,strlen(p
->path
))!=0); p
=p
->next
)
276 inbuf
+= strlen(p
->path
);
277 while (*inbuf
== ' ' || *inbuf
== '\t') inbuf
++;
278 if (p
->type
& WITHFD
) {
280 if (p
->type
& WITHFILE
) {
281 printoutc(f
,"0000 DATA END WITH '.'");
282 switch(p
->type
& ~(WITHFILE
| WITHFD
)){
283 case NOARG
: rv
=p
->doit(f
,fd
); break;
284 case INTARG
: rv
=p
->doit(f
,fd
,atoi(inbuf
)); break;
285 case STRARG
: rv
=p
->doit(f
,fd
,inbuf
); break;
289 switch(p
->type
& ~WITHFD
){
290 case NOARG
: rv
=p
->doit(fd
); break;
291 case INTARG
: rv
=p
->doit(fd
,atoi(inbuf
)); break;
292 case STRARG
: rv
=p
->doit(fd
,inbuf
); break;
297 } else if (p
->type
& WITHFILE
) {
298 printoutc(f
,"0000 DATA END WITH '.'");
299 switch(p
->type
& ~WITHFILE
){
300 case NOARG
: rv
=p
->doit(f
); break;
301 case INTARG
: rv
=p
->doit(f
,atoi(inbuf
)); break;
302 case STRARG
: rv
=p
->doit(f
,inbuf
); break;
307 case NOARG
: rv
=p
->doit(); break;
308 case INTARG
: rv
=p
->doit(atoi(inbuf
)); break;
309 case STRARG
: rv
=p
->doit(inbuf
); break;
314 printoutc(f
,"1000 Success");
316 printoutc(f
,"1%03d %s",rv
,strerror(rv
));
320 write(fd
,outbuf
,outbufsize
);
326 static int runscript(int fd
,char *path
)
328 FILE *f
=fopen(path
,"r");
333 while (fgets(buf
,MAXCMD
,f
) != NULL
) {
334 if (strlen(buf
) > 1 && buf
[strlen(buf
)-1]=='\n') buf
[strlen(buf
)-1]= '\0';
336 char *scriptprompt
=NULL
;
337 asprintf(&scriptprompt
,"vde[%s]: %s\n",path
,buf
);
338 write(fd
,scriptprompt
,strlen(scriptprompt
));
341 handle_cmd(mgmt_data
, fd
, buf
);
348 void loadrcfile(void)
351 runscript(-1,rcfile
);
354 snprintf(path
,PATH_MAX
,"%s/.vde2/vde_switch.rc",getenv("HOME"));
355 if (access(path
,R_OK
) == 0)
358 if (access(STDRCFILE
,R_OK
) == 0)
359 runscript(-1,STDRCFILE
);
364 void mgmtnewfd(int new)
367 if(fcntl(new, F_SETFL
, O_NONBLOCK
) < 0){
368 printlog(LOG_WARNING
,"mgmt fcntl - setting O_NONBLOCK %s",strerror(errno
));
373 add_fd(new,mgmt_data
,NULL
);
374 EVENTOUT(MGMTPORTNEW
,new);
375 snprintf(buf
,MAXCMD
,header
,PACKAGE_VERSION
);
376 write(new,buf
,strlen(buf
));
377 write(new,prompt
,strlen(prompt
));
381 static int debugdel(int fd
,char *arg
);
383 static char *EOS
="9999 END OF SESSION";
384 static void handle_io(unsigned char type
,int fd
,int revents
,void *private_data
)
387 if (type
!= mgmt_ctl
) {
390 if (revents
& POLLIN
) {
391 n
= read(fd
, buf
, sizeof(buf
));
393 printlog(LOG_WARNING
,"Reading from mgmt %s",strerror(errno
));
397 if (n
==0) { /*EOF || POLLHUP*/
398 if (type
== console_type
) {
399 printlog(LOG_WARNING
,"EOF on stdin, cleaning up and exiting");
410 if (n
>0 && buf
[n
-1] == '\n') buf
[n
-1] = 0;
411 cmdout
=handle_cmd(type
,(type
==console_type
)?STDOUT_FILENO
:fd
,buf
);
413 write(fd
,prompt
,strlen(prompt
));
415 if(type
==mgmt_data
) {
416 write(fd
,EOS
,strlen(EOS
));
418 EVENTOUT(MGMTPORTDEL
,fd
);
427 } else {/* mgmt ctl */
428 struct sockaddr addr
;
433 new = accept(fd
, &addr
, &len
);
435 printlog(LOG_WARNING
,"mgmt accept %s",strerror(errno
));
438 if(fcntl(new, F_SETFL
, O_NONBLOCK
) < 0){
439 printlog(LOG_WARNING
,"mgmt fcntl - setting O_NONBLOCK %s",strerror(errno
));
444 add_fd(new,mgmt_data
,NULL
);
445 EVENTOUT(MGMTPORTNEW
,new);
446 snprintf(buf
,MAXCMD
,header
,PACKAGE_VERSION
);
447 write(new,buf
,strlen(buf
));
448 write(new,prompt
,strlen(prompt
));
452 static void save_pidfile()
454 if(pidfile
[0] != '/')
455 strncat(pidfile_path
, pidfile
, PATH_MAX
- strlen(pidfile_path
));
457 strcpy(pidfile_path
, pidfile
);
459 int fd
= open(pidfile_path
,
460 O_WRONLY
| O_CREAT
| O_EXCL
,
461 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
465 printlog(LOG_ERR
, "Error in pidfile creation: %s", strerror(errno
));
469 if((f
= fdopen(fd
, "w")) == NULL
) {
470 printlog(LOG_ERR
, "Error in FILE* construction: %s", strerror(errno
));
474 if(fprintf(f
, "%ld\n", (long int)getpid()) <= 0) {
475 printlog(LOG_ERR
, "Error in writing pidfile");
482 static void cleanup(unsigned char type
,int fd
,void *private_data
)
485 if((pidfile
!= NULL
) && unlink(pidfile_path
) < 0) {
486 printlog(LOG_WARNING
,"Couldn't remove pidfile '%s': %s", pidfile
, strerror(errno
));
490 if (type
== mgmt_ctl
&& mgmt_socket
!= NULL
) {
496 #define MGMTMODEARG 0x100
497 #define MGMTGROUPARG 0x101
499 static struct option long_options
[] = {
500 {"daemon", 0, 0, 'd'},
501 {"pidfile", 1, 0, 'p'},
502 {"rcfile", 1, 0, 'f'},
504 {"mgmtmode", 1, 0, MGMTMODEARG
},
505 {"mgmtgroup", 1, 0, MGMTGROUPARG
},
507 {"debugclients",1,0,'D'},
511 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
513 static void usage(void)
516 "(opts from consmgmt module)\n"
517 " -d, --daemon Daemonize vde_switch once run\n"
518 " -p, --pidfile PIDFILE Write pid of daemon to PIDFILE\n"
519 " -f, --rcfile Configuration file (overrides %s and ~/.vderc)\n"
520 " -M, --mgmt SOCK path of the management UNIX socket\n"
521 " --mgmtmode MODE management UNIX socket access mode (octal)\n"
522 " --mgmtgroup GROUP management UNIX socket group name\n"
524 " -D, --debugclients # number of debug clients allowed\n"
529 static int parseopt(int c
, char *optarg
)
538 pidfile
=strdup(optarg
);
541 rcfile
=strdup(optarg
);
544 mgmt_socket
=strdup(optarg
);
547 sscanf(optarg
,"%o",&mgmt_mode
);
550 if (!(grp
= getgrnam(optarg
)))
552 fprintf(stderr
, "No such group '%s'\n", optarg
);
555 mgmt_group
= grp
->gr_gid
;
564 static void init(void)
567 openlog(basename(prog
), LOG_PID
, 0);
569 syslog(LOG_INFO
,"VDE_SWITCH started");
571 /* add stdin (if tty), connect and data fds to the set of fds we wait for
573 if(isatty(0) && !daemonize
)
575 console_type
=add_type(&swmi
,0);
576 add_fd(0,console_type
,NULL
);
579 /* saves current path in pidfile_path, because otherwise with daemonize() we
581 if(getcwd(pidfile_path
, PATH_MAX
-1) == NULL
) {
582 printlog(LOG_ERR
, "getcwd: %s", strerror(errno
));
585 strcat(pidfile_path
, "/");
586 if (daemonize
&& daemon(0, 0)) {
587 printlog(LOG_ERR
,"daemon: %s",strerror(errno
));
591 /* once here, we're sure we're the true process which will continue as a
592 * * server: save PID file if needed */
593 if(pidfile
) save_pidfile();
595 if(mgmt_socket
!= NULL
) {
597 struct sockaddr_un sun
;
600 if((mgmtconnfd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0){
601 printlog(LOG_ERR
,"mgmt socket: %s",strerror(errno
));
604 if(setsockopt(mgmtconnfd
, SOL_SOCKET
, SO_REUSEADDR
, (char *) &one
,
606 printlog(LOG_ERR
,"mgmt setsockopt: %s",strerror(errno
));
609 if(fcntl(mgmtconnfd
, F_SETFL
, O_NONBLOCK
) < 0){
610 printlog(LOG_ERR
,"Setting O_NONBLOCK on mgmt fd: %s",strerror(errno
));
613 sun
.sun_family
= PF_UNIX
;
614 snprintf(sun
.sun_path
,sizeof(sun
.sun_path
),"%s",mgmt_socket
);
615 if(bind(mgmtconnfd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
616 if((errno
== EADDRINUSE
) && still_used(&sun
)) return;
617 else if(bind(mgmtconnfd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
618 printlog(LOG_ERR
,"mgmt bind %s",strerror(errno
));
622 setmgmtperm(sun
.sun_path
);
623 if(listen(mgmtconnfd
, 15) < 0){
624 printlog(LOG_ERR
,"mgmt listen: %s",strerror(errno
));
627 mgmt_ctl
=add_type(&swmi
,0);
628 mgmt_data
=add_type(&swmi
,0);
629 add_fd(mgmtconnfd
,mgmt_ctl
,NULL
);
633 static int vde_logout()
638 static int vde_shutdown()
640 printlog(LOG_WARNING
,"Shutdown from mgmt command");
644 static int showinfo(FILE *fd
)
646 printoutc(fd
,header
,PACKAGE_VERSION
);
647 printoutc(fd
,"pid %d MAC %02x:%02x:%02x:%02x:%02x:%02x uptime %d",getpid(),
648 switchmac
[0], switchmac
[1], switchmac
[2], switchmac
[3], switchmac
[4], switchmac
[5],
651 printoutc(fd
,"mgmt %s perm 0%03o",mgmt_socket
,mgmt_mode
);
656 static int debuglist(FILE *f
,int fd
,char *path
)
658 #define DEBUGFORMAT1 "%-22s %-3s %-6s %s"
659 #define DEBUGFORMAT2 "%-22s %03o %-6s %s"
663 printoutc(f
,DEBUGFORMAT1
,"CATEGORY", "TAG", "STATUS", "HELP");
664 printoutc(f
,DEBUGFORMAT1
,"------------","---","------", "----");
665 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
){
666 if (p
->help
&& strncmp(p
->path
, path
, strlen(path
)) == 0) {
667 for (i
=0; i
<p
->nfds
&& p
->fds
[i
] != fd
; i
++)
670 printoutc(f
, DEBUGFORMAT2
, p
->path
, p
->tag
&0777, i
<p
->nfds
? "ON" : "OFF", p
->help
);
676 /* EINVAL -> no matches
677 * EEXIST -> all the matches already include fd
678 * ENOMEM -> fd buffer realloc failed
680 static int debugadd(int fd
,char *path
) {
683 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
) {
684 if (p
->help
&& strncmp(p
->path
, path
, strlen(path
)) == 0) {
686 if (rv
==EINVAL
) rv
=EEXIST
;
687 for(i
=0;i
<p
->nfds
&& (p
->fds
[i
] != fd
); i
++)
691 int newsize
=p
->maxfds
+DBGCLSTEP
;
692 p
->fds
=realloc(p
->fds
,newsize
*sizeof(int));
697 if (rv
!= ENOMEM
) rv
=0;
703 if (rv
!= ENOMEM
) rv
=0;
711 /* EINVAL -> no matches
712 * ENOENT -> all the matches do not include fd
714 static int debugdel(int fd
,char *path
) {
717 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
){
718 if (strncmp(p
->path
, path
, strlen(path
)) == 0) {
720 if (rv
==EINVAL
) rv
=ENOENT
;
721 for(i
=0;i
<p
->nfds
&& (p
->fds
[i
] != fd
); i
++)
724 p
->nfds
--; /* the last one */
725 p
->fds
[i
]=p
->fds
[p
->nfds
]; /* swap it with the deleted element*/
733 int eventadd(int (*fun
)(),char *path
,void *arg
) {
736 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
) {
737 if (strncmp(p
->path
, path
, strlen(path
)) == 0) {
739 if (rv
==EINVAL
) rv
=EEXIST
;
740 for(i
=0;i
<p
->nfun
&& (p
->fun
[i
] != fun
); i
++)
744 int newsize
=p
->maxfun
+DBGCLSTEP
;
745 p
->fun
=realloc(p
->fun
,newsize
*sizeof(int));
746 p
->funarg
=realloc(p
->funarg
,newsize
*sizeof(void *));
747 if (p
->fun
&& p
->funarg
) {
752 if (rv
!= ENOMEM
) rv
=0;
758 if (rv
!= ENOMEM
) rv
=0;
766 /* EINVAL -> no matches
767 * ENOENT -> all the matches do not include fun
769 int eventdel(int (*fun
)(),char *path
,void *arg
) {
772 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
){
773 if (strncmp(p
->path
, path
, strlen(path
)) == 0) {
775 if (rv
==EINVAL
) rv
=ENOENT
;
776 for(i
=0;i
<p
->nfun
&& (p
->fun
[i
] != fun
) && (p
->funarg
[i
] != arg
); i
++)
779 p
->nfun
--; /* the last one */
780 p
->fun
[i
]=p
->fun
[p
->nfun
]; /* swap it with the deleted element*/
791 static int pluginlist(FILE *f
,char *arg
)
793 #define PLUGINFMT "%-22s %s"
796 printoutc(f
,PLUGINFMT
,"NAME", "HELP");
797 printoutc(f
,PLUGINFMT
,"------------","----");
798 for (p
=pluginh
; p
!=NULL
; p
=p
->next
){
799 if (strncmp(p
->name
, arg
, strlen(arg
)) == 0) {
800 printoutc(f
,PLUGINFMT
,p
->name
,p
->help
);
807 /* This will be prefixed with getent("$HOME") */
808 #define USER_PLUGINS_DIR "/.vde2/plugins"
811 # define MAX(a, b) ((a) > (b) ? (a) : (b))
814 * Try to dlopen a plugin trying different names and locations:
815 * (code from view-os by Gardenghi)
818 * 2) dlopen(modname.so)
819 * 3) dlopen(user_umview_plugin_directory/modname)
820 * 4) dlopen(user_umview_plugin_directory/modname.so)
821 * 5) dlopen(global_umview_plugin_directory/modname)
822 * 6) dlopen(global_umview_plugin_directory/modname.so)
826 #define TRY_DLOPEN(fmt...) \
828 snprintf(testpath, tplen, fmt); \
829 if ((handle = dlopen(testpath, flag))) \
836 void *plugin_dlopen(const char *modname
, int flag
)
841 char *homedir
= getenv("HOME");
846 if ((handle
= dlopen(modname
, flag
)))
849 /* If there is no home directory, use CWD */
853 tplen
= strlen(modname
) +
854 strlen(MODULES_EXT
) + 2 + // + 1 is for a '/' and + 1 for \0
855 MAX(strlen(PLUGINS_DIR
),
856 strlen(homedir
) + strlen(USER_PLUGINS_DIR
));
858 testpath
= malloc(tplen
);
860 TRY_DLOPEN("%s%s", modname
, MODULES_EXT
);
861 TRY_DLOPEN("%s%s/%s", homedir
, USER_PLUGINS_DIR
, modname
);
862 TRY_DLOPEN("%s%s/%s%s", homedir
, USER_PLUGINS_DIR
, modname
, MODULES_EXT
);
863 TRY_DLOPEN("%s%s", PLUGINS_DIR
, modname
);
864 TRY_DLOPEN("%s/%s%s", PLUGINS_DIR
, modname
, MODULES_EXT
);
872 static int pluginadd(char *arg
) {
876 if ((handle
=plugin_dlopen(arg
,RTLD_LAZY
)) != NULL
) {
877 if ((p
=(struct plugin
*) dlsym(handle
,"vde_plugin_data")) != NULL
) {
878 if (p
->handle
!= NULL
) { /* this dyn library is already loaded*/
893 static int plugindel(char *arg
) {
894 struct plugin
**p
=&pluginh
;
897 if (strncmp((*p
)->name
, arg
, strlen(arg
)) == 0
898 && ((*p
)->handle
!= NULL
)) {
899 struct plugin
*this=*p
;
912 static struct comlist cl
[]={
913 {"help","[arg]","Help (limited to arg when specified)",help
,STRARG
| WITHFILE
},
914 {"logout","","logout from this mgmt terminal",vde_logout
,NOARG
},
915 {"shutdown","","shutdown of the switch",vde_shutdown
,NOARG
},
916 {"showinfo","","show switch version and info",showinfo
,NOARG
|WITHFILE
},
917 {"load","path","load a configuration script",runscript
,STRARG
|WITHFD
},
919 {"debug","============","DEBUG MENU",NULL
,NOARG
},
920 {"debug/list","","list debug categories",debuglist
,STRARG
|WITHFILE
|WITHFD
},
921 {"debug/add","dbgpath","enable debug info for a given category",debugadd
,WITHFD
|STRARG
},
922 {"debug/del","dbgpath","disable debug info for a given category",debugdel
,WITHFD
|STRARG
},
925 {"plugin","============","PLUGINS MENU",NULL
,NOARG
},
926 {"plugin/list","","list plugins",pluginlist
,STRARG
|WITHFILE
},
927 {"plugin/add","library","load a plugin",pluginadd
,STRARG
},
928 {"plugin/del","name","unload a plugin",plugindel
,STRARG
},
932 static void sighupmgmt(int signo
)
934 EVENTOUT(MGMTSIGHUP
, signo
);
937 void start_consmgmt(void)
939 swmi
.swmname
="console-mgmt";
940 swmi
.swmnopts
=Nlong_options
;
941 swmi
.swmopts
=long_options
;
943 swmi
.parseopt
=parseopt
;
945 swmi
.handle_io
=handle_io
;
946 swmi
.cleanup
=cleanup
;
953 signal(SIGHUP
,sighupmgmt
);