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 <sockutils.h>
38 static struct swmodule swmi
;
42 static char *pidfile
= NULL
;
43 static char pidfile_path
[PATH_MAX
];
44 static int daemonize
= 0;
45 static unsigned int console_type
=-1;
46 static unsigned int mgmt_ctl
=-1;
47 static unsigned int mgmt_data
=-1;
48 static int mgmt_mode
= 0600;
49 static char *mgmt_socket
= NULL
;
50 static char header
[]="VDE switch V.%s\n(C) R.Davoli 2005 - GPLv2\n";
51 static char prompt
[]="\nvde: ";
53 static struct comlist
*clh
=NULL
;
55 static int ndebugclients
=8;
56 static struct dbgcl
*dbgclh
=NULL
;
59 void addcl(int ncl
,struct comlist
*cl
)
62 static struct comlist
**clt
=&clh
;
63 for (i
=0;i
<ncl
;i
++,cl
++) {
71 void adddbgcl(int ncl
,struct dbgcl
*cl
)
74 static struct dbgcl
**clt
=&dbgclh
;
75 for (i
=0;i
<ncl
;i
++,cl
++) {
76 if ((cl
->fds
=malloc(ndebugclients
* sizeof(int))) != NULL
) {
78 for(j
=0; j
<ndebugclients
; j
++) cl
->fds
[j
]= -1;
86 void printlog(int priority
, const char *format
, ...)
90 va_start (arg
, format
);
93 vsyslog(priority
,format
,arg
);
95 fprintf(stderr
,"%s: ",prog
);
96 vfprintf(stderr
,format
,arg
);
103 void printoutc(int fd
, const char *format
, ...)
107 va_start (arg
, format
);
110 printlog(LOG_INFO
,format
,arg
);
113 char outbuf
[MAXCMD
+1];
114 vsnprintf(outbuf
,MAXCMD
,format
,arg
);
116 write(fd
,outbuf
,strlen(outbuf
));
123 void printoutc(FILE *f
, const char *format
, ...)
127 va_start (arg
, format
);
129 vfprintf(f
,format
,arg
);
132 printlog(LOG_INFO
,format
,arg
);
137 static char _dbgnl
='\n';
138 void debugout(struct dbgcl
* cl
, const char *format
, ...)
144 struct iovec iov
[]={{NULL
,0},{NULL
,0},{&_dbgnl
,1}};
146 va_start (arg
, format
);
147 iov
[0].iov_len
=asprintf(&header
,"%d %s ",3000+0,cl
->path
);
148 iov
[0].iov_base
=header
;
149 iov
[1].iov_len
=vasprintf(&msg
,format
,arg
);
153 for (i
=0; i
<ndebugclients
&& cl
->fds
[i
] >=0; i
++)
154 writev(cl
->fds
[i
],iov
,3);
160 void setmgmtperm(char *path
)
162 chmod(path
,mgmt_mode
);
165 static int help(FILE *fd
,char *arg
)
169 printoutc(fd
,"%-18s %-15s %s","COMMAND PATH","SYNTAX","HELP");
170 printoutc(fd
,"%-18s %-15s %s","------------","--------------","------------");
171 for (p
=clh
;p
!=NULL
;p
=p
->next
)
172 if (strncmp(p
->path
,arg
,n
) == 0)
173 printoutc(fd
,"%-18s %-15s %s",p
->path
,p
->syntax
,p
->help
);
177 static int handle_cmd(int type
,int fd
,char *inbuf
)
181 while (*inbuf
== ' ' || *inbuf
== '\t') inbuf
++;
182 if (*inbuf
!= '\0' && *inbuf
!= '#') {
187 f
=open_memstream(&outbuf
,&outbufsize
);
190 for (p
=clh
;p
!=NULL
&& (p
->doit
==NULL
|| strncmp(p
->path
,inbuf
,strlen(p
->path
))!=0); p
=p
->next
)
194 inbuf
+= strlen(p
->path
);
195 while (*inbuf
== ' ' || *inbuf
== '\t') inbuf
++;
196 if (p
->type
& WITHFD
) {
197 if (p
->type
& WITHFILE
) {
198 printoutc(f
,"0000 DATA END WITH '.'");
199 switch(p
->type
& ~(WITHFILE
| WITHFD
)){
200 case NOARG
: rv
=p
->doit(f
,fd
); break;
201 case INTARG
: rv
=p
->doit(f
,fd
,atoi(inbuf
)); break;
202 case STRARG
: rv
=p
->doit(f
,fd
,inbuf
); break;
206 switch(p
->type
& ~WITHFD
){
207 case NOARG
: rv
=p
->doit(fd
); break;
208 case INTARG
: rv
=p
->doit(fd
,atoi(inbuf
)); break;
209 case STRARG
: rv
=p
->doit(fd
,inbuf
); break;
212 } else if (p
->type
& WITHFILE
) {
213 printoutc(f
,"0000 DATA END WITH '.'");
214 switch(p
->type
& ~WITHFILE
){
215 case NOARG
: rv
=p
->doit(f
); break;
216 case INTARG
: rv
=p
->doit(f
,atoi(inbuf
)); break;
217 case STRARG
: rv
=p
->doit(f
,inbuf
); break;
222 case NOARG
: rv
=p
->doit(); break;
223 case INTARG
: rv
=p
->doit(atoi(inbuf
)); break;
224 case STRARG
: rv
=p
->doit(inbuf
); break;
228 if (rv
>= 0 && (rv
> 0 || fd
>= 0))
229 printoutc(f
,"1%03d %s",rv
,strerror(rv
));
232 write(fd
,outbuf
,outbufsize
);
239 static int runscript(int fd
,char *path
)
241 FILE *f
=fopen(path
,"r");
246 while (fgets(buf
,MAXCMD
,f
) != NULL
) {
247 if (strlen(buf
) > 1 && buf
[strlen(buf
)-1]=='\n') buf
[strlen(buf
)-1]= '\0';
249 char *scriptprompt
=NULL
;
250 asprintf(&scriptprompt
,"vde[%s]: %s",path
,buf
);
251 write(fd
,scriptprompt
,strlen(scriptprompt
));
254 handle_cmd(mgmt_data
, fd
, buf
);
260 void loadrcfile(void)
263 runscript(-1,rcfile
);
266 snprintf(path
,PATH_MAX
,"%s/.vderc",getenv("HOME"));
267 if (access(path
,R_OK
) == 0)
270 if (access(STDRCFILE
,R_OK
) == 0)
271 runscript(-1,STDRCFILE
);
276 static char *EOS
="9999 END OF SESSION";
277 static void handle_input(unsigned char type
,int fd
,int revents
,int *unused
)
280 if (type
!= mgmt_ctl
) {
283 if (revents
& POLLIN
) {
284 n
= read(fd
, buf
, sizeof(buf
));
286 printlog(LOG_WARNING
,"Reading from mgmt %s",strerror(errno
));
290 if (type
== console_type
) {
291 printlog(LOG_WARNING
,"EOF on stdin, cleaning up and exiting");
299 if (n
>0 && buf
[n
-1] == '\n') buf
[n
-1] = 0;
300 cmdout
=handle_cmd(type
,(type
==console_type
)?STDOUT_FILENO
:fd
,buf
);
302 write(fd
,prompt
,strlen(prompt
));
304 if(type
==mgmt_data
) {
305 write(fd
,EOS
,strlen(EOS
));
312 } else {/* mgmt ctl */
313 struct sockaddr addr
;
318 new = accept(fd
, &addr
, &len
);
320 printlog(LOG_WARNING
,"mgmt accept %s",strerror(errno
));
323 if(fcntl(new, F_SETFL
, O_NONBLOCK
) < 0){
324 printlog(LOG_WARNING
,"mgmt fcntl - setting O_NONBLOCK %s",strerror(errno
));
329 add_fd(new,mgmt_data
,-1);
330 snprintf(buf
,MAXCMD
,header
,PACKAGE_VERSION
);
331 write(new,buf
,strlen(buf
));
332 write(new,prompt
,strlen(prompt
));
336 static void save_pidfile()
338 if(pidfile
[0] != '/')
339 strncat(pidfile_path
, pidfile
, PATH_MAX
- strlen(pidfile_path
));
341 strcpy(pidfile_path
, pidfile
);
343 int fd
= open(pidfile_path
,
344 O_WRONLY
| O_CREAT
| O_EXCL
,
345 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
349 printlog(LOG_ERR
, "Error in pidfile creation: %s", strerror(errno
));
353 if((f
= fdopen(fd
, "w")) == NULL
) {
354 printlog(LOG_ERR
, "Error in FILE* construction: %s", strerror(errno
));
358 if(fprintf(f
, "%ld\n", (long int)getpid()) <= 0) {
359 printlog(LOG_ERR
, "Error in writing pidfile");
366 static void cleanup(unsigned char type
,int fd
,int arg
)
369 if((pidfile
!= NULL
) && unlink(pidfile_path
) < 0) {
370 printlog(LOG_WARNING
,"Couldn't remove pidfile '%s': %s", pidfile
, strerror(errno
));
374 if (type
== mgmt_ctl
&& mgmt_socket
!= NULL
) {
380 #define MGMTMODEARG 0x100
382 static struct option long_options
[] = {
383 {"daemon", 0, 0, 'd'},
384 {"pidfile", 1, 0, 'p'},
385 {"rcfile", 1, 0, 'f'},
387 {"mgmtmode", 1, 0, MGMTMODEARG
},
389 {"debugclients",1,0,'D'},
393 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
395 static void usage(void)
398 "(opts from consmgmt module)\n"
399 " -d, --daemon Daemonize vde_switch once run\n"
400 " -p, --pidfile PIDFILE Write pid of daemon to PIDFILE\n"
401 " -f, --rcfile Configuration file (overrides %s and ~/.vderc)\n"
402 " -M, --mgmt SOCK path of the management UNIX socket\n"
403 " --mgmtmode MODE management UNIX socket access mode (octal)\n"
405 " -D, --debugclients # number of debug clients allowed\n"
410 static int parseopt(int c
, char *optarg
)
418 pidfile
=strdup(optarg
);
421 rcfile
=strdup(optarg
);
424 mgmt_socket
=strdup(optarg
);
427 sscanf(optarg
,"%o",&mgmt_mode
);
431 ndebugclients
= atoi(optarg
);
440 static void init(void)
443 openlog(basename(prog
), LOG_PID
, 0);
445 syslog(LOG_INFO
,"VDE_SWITCH started");
447 /* add stdin (if tty), connect and data fds to the set of fds we wait for
449 if(isatty(0) && !daemonize
)
451 console_type
=add_type(&swmi
,0);
452 add_fd(0,console_type
,-1);
455 /* saves current path in pidfile_path, because otherwise with daemonize() we
457 if(getcwd(pidfile_path
, PATH_MAX
-1) == NULL
) {
458 printlog(LOG_ERR
, "getcwd: %s", strerror(errno
));
461 strcat(pidfile_path
, "/");
462 if (daemonize
&& daemon(0, 0)) {
463 printlog(LOG_ERR
,"daemon: %s",strerror(errno
));
467 /* once here, we're sure we're the true process which will continue as a
468 * * server: save PID file if needed */
469 if(pidfile
) save_pidfile();
471 if(mgmt_socket
!= NULL
) {
473 struct sockaddr_un sun
;
476 if((mgmtconnfd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0){
477 printlog(LOG_ERR
,"mgmt socket: %s",strerror(errno
));
480 if(setsockopt(mgmtconnfd
, SOL_SOCKET
, SO_REUSEADDR
, (char *) &one
,
482 printlog(LOG_ERR
,"mgmt setsockopt: %s",strerror(errno
));
485 if(fcntl(mgmtconnfd
, F_SETFL
, O_NONBLOCK
) < 0){
486 printlog(LOG_ERR
,"Setting O_NONBLOCK on mgmt fd: %s",strerror(errno
));
489 sun
.sun_family
= PF_UNIX
;
490 snprintf(sun
.sun_path
,sizeof(sun
.sun_path
),"%s",mgmt_socket
);
491 if(bind(mgmtconnfd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
492 if((errno
== EADDRINUSE
) && still_used(&sun
)) return;
493 else if(bind(mgmtconnfd
, (struct sockaddr
*) &sun
, sizeof(sun
)) < 0){
494 printlog(LOG_ERR
,"mgmt bind %s",strerror(errno
));
498 chmod(sun
.sun_path
,mgmt_mode
);
499 if(listen(mgmtconnfd
, 15) < 0){
500 printlog(LOG_ERR
,"mgmt listen: %s",strerror(errno
));
503 mgmt_ctl
=add_type(&swmi
,0);
504 mgmt_data
=add_type(&swmi
,0);
505 add_fd(mgmtconnfd
,mgmt_ctl
,-1);
509 static int vde_logout()
514 static int vde_shutdown()
516 printlog(LOG_WARNING
,"Shutdown from mgmt command");
520 static int showinfo(FILE *fd
)
522 printoutc(fd
,header
,PACKAGE_VERSION
);
523 printoutc(fd
,"pid %d MAC %02x:%02x:%02x:%02x:%02x:%02x uptime %d",getpid(),
524 switchmac
[0], switchmac
[1], switchmac
[2], switchmac
[3], switchmac
[4], switchmac
[5],
527 printoutc(fd
,"mgmt %s perm 0%03o",mgmt_socket
,mgmt_mode
);
528 printoutc(fd
,"unsent_pktq_len %d",packetq_count());
533 static int debuglist(FILE *f
,int fd
,char *arg
)
535 #define DEBUGFORMAT "%-22s %-6s %s"
539 printoutc(f
,DEBUGFORMAT
,"CATEGORY", "STATUS", "HELP");
540 printoutc(f
,DEBUGFORMAT
,"------------","------", "----");
541 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
){
542 if (strncmp(p
->path
, arg
, strlen(arg
)) == 0) {
543 for (i
=0; i
<ndebugclients
&& p
->fds
[i
] != fd
; i
++)
546 printoutc(f
, DEBUGFORMAT
, p
->path
, i
<ndebugclients
? "ON" : "OFF", p
->help
);
552 /* EINVAL -> no matches
553 * EEXIST -> all the matches already include fd
554 * EMFILE -> fd buffer averflow in at least one match
556 static int debugadd(int fd
,char *arg
) {
559 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
) {
560 if (strncmp(p
->path
, arg
, strlen(arg
)) == 0) {
562 if (rv
==EINVAL
) rv
=EEXIST
;
563 for(i
=0;i
<ndebugclients
&& (p
->fds
[i
] != -1) && (p
->fds
[i
] != fd
); i
++)
565 if (i
<ndebugclients
) {
566 if ( p
->fds
[i
] == -1 ) {
568 if (rv
!=EMFILE
) rv
=0;
577 /* EINVAL -> no matches
578 * ENOENT -> all the matches do not include fd
580 static int debugdel(int fd
,char *arg
) {
583 for (p
=dbgclh
; p
!=NULL
; p
=p
->next
){
584 if (strncmp(p
->path
, arg
, strlen(arg
)) == 0) {
586 if (rv
==EINVAL
) rv
=ENOENT
;
587 for(i
=0;i
<ndebugclients
&& (p
->fds
[i
] != -1) && (p
->fds
[i
] != fd
); i
++)
589 if (i
<ndebugclients
&& p
->fds
[i
] == fd
) {
591 for (j
=i
;j
<ndebugclients
&& p
->fds
[i
] >= 0; j
++)
593 j
--; /* the last one */
594 p
->fds
[i
]=p
->fds
[j
]; /* swap it with the deleted element*/
595 p
->fds
[j
] = -1; /* null the last one, if deleting the last one
606 static struct comlist cl
[]={
607 {"help","[arg]","Help (limited to arg when specified)",help
,STRARG
| WITHFILE
},
608 {"logout","","logout from this mgmt terminal",vde_logout
,NOARG
},
609 {"shutdown","","shutdown of the switch",vde_shutdown
,NOARG
},
610 {"showinfo","","show switch version and info",showinfo
,NOARG
|WITHFILE
},
611 {"load","path","load a configuration script",runscript
,STRARG
|WITHFD
},
613 {"debug","============","DEBUG MENU",NULL
,NOARG
},
614 {"debug/list","","list debug categories",debuglist
,STRARG
|WITHFILE
|WITHFD
},
615 {"debug/add","dbgpath","enable debug info for a given category",debugadd
,WITHFD
|STRARG
},
616 {"debug/del","dbgpath","disable debug info for a given category",debugdel
,WITHFD
|STRARG
},
620 void start_consmgmt(void)
622 swmi
.swmname
="console-mgmt";
623 swmi
.swmnopts
=Nlong_options
;
624 swmi
.swmopts
=long_options
;
626 swmi
.parseopt
=parseopt
;
628 swmi
.handle_input
=handle_input
;
629 swmi
.cleanup
=cleanup
;