Avoid segmentation fault if no tap name given (thanks to Filippo Giunchedi)
[vde.git] / vde-2 / consmgmt.c
blobc93e41154e78cd57b0732d878c1eef1de75496b8
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
5 */
7 #define _GNU_SOURCE
8 #include <config.h>
9 #include <stdio.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <syslog.h>
15 #include <stdlib.h>
16 #include <libgen.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/ioctl.h>
20 #include <sys/socket.h>
21 #include <sys/poll.h>
22 #include <sys/un.h>
23 #include <sys/uio.h>
24 #include <net/if.h>
25 #include <stdarg.h>
26 #include <getopt.h>
28 #include <vde.h>
29 #include <port.h>
30 #include <switch.h>
31 #include <sockutils.h>
32 #include <consmgmt.h>
33 #include <qtimer.h>
34 #include <packetq.h>
36 #define MAXCMD 128
38 static struct swmodule swmi;
40 static int logok=0;
41 static char *rcfile;
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;
54 #ifdef DEBUGOPT
55 static int ndebugclients=8;
56 static struct dbgcl *dbgclh=NULL;
57 #endif
59 void addcl(int ncl,struct comlist *cl)
61 register int i;
62 static struct comlist **clt=&clh;
63 for (i=0;i<ncl;i++,cl++) {
64 cl->next=NULL;
65 (*clt)=cl;
66 clt=(&cl->next);
70 #ifdef DEBUGOPT
71 void adddbgcl(int ncl,struct dbgcl *cl)
73 int i, j;
74 static struct dbgcl **clt=&dbgclh;
75 for (i=0;i<ncl;i++,cl++) {
76 if ((cl->fds=malloc(ndebugclients * sizeof(int))) != NULL) {
77 cl->next=NULL;
78 for(j=0; j<ndebugclients; j++) cl->fds[j]= -1;
79 (*clt)=cl;
80 clt=(&cl->next);
84 #endif
86 void printlog(int priority, const char *format, ...)
88 va_list arg;
90 va_start (arg, format);
92 if (logok)
93 vsyslog(priority,format,arg);
94 else {
95 fprintf(stderr,"%s: ",prog);
96 vfprintf(stderr,format,arg);
97 fprintf(stderr,"\n");
99 va_end (arg);
102 #if 0
103 void printoutc(int fd, const char *format, ...)
105 va_list arg;
107 va_start (arg, format);
108 #if 0
109 if (fd < 0)
110 printlog(LOG_INFO,format,arg);
111 else {
112 #endif
113 char outbuf[MAXCMD+1];
114 vsnprintf(outbuf,MAXCMD,format,arg);
115 strcat(outbuf,"\n");
116 write(fd,outbuf,strlen(outbuf));
117 #if 0
119 #endif
121 #endif
123 void printoutc(FILE *f, const char *format, ...)
125 va_list arg;
127 va_start (arg, format);
128 if (f) {
129 vfprintf(f,format,arg);
130 fprintf(f,"\n");
131 } else
132 printlog(LOG_INFO,format,arg);
133 va_end(arg);
136 #ifdef DEBUGOPT
137 static char _dbgnl='\n';
138 void debugout(struct dbgcl* cl, const char *format, ...)
140 va_list arg;
141 char *msg;
142 int i;
143 char *header;
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);
150 iov[1].iov_base=msg;
151 va_end (arg);
153 for (i=0; i<ndebugclients && cl->fds[i] >=0; i++)
154 writev(cl->fds[i],iov,3);
155 free(header);
156 free(msg);
158 #endif
160 void setmgmtperm(char *path)
162 chmod(path,mgmt_mode);
165 static int help(FILE *fd,char *arg)
167 struct comlist *p;
168 int n=strlen(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);
174 return 0;
177 static int handle_cmd(int type,int fd,char *inbuf)
179 struct comlist *p;
180 int rv=ENOSYS;
181 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
182 if (*inbuf != '\0' && *inbuf != '#') {
183 char *outbuf;
184 size_t outbufsize;
185 FILE *f;
186 if (fd >= 0)
187 f=open_memstream(&outbuf,&outbufsize);
188 else
189 f=NULL;
190 for (p=clh;p!=NULL && (p->doit==NULL || strncmp(p->path,inbuf,strlen(p->path))!=0); p=p->next)
192 if (p!=NULL)
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;
204 printoutc(f,".");
205 } else {
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;
219 printoutc(f,".");
220 } else {
221 switch(p->type){
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));
230 if (f) {
231 fclose(f);
232 write(fd,outbuf,outbufsize);
233 free(outbuf);
236 return rv;
239 static int runscript(int fd,char *path)
241 FILE *f=fopen(path,"r");
242 char buf[MAXCMD];
243 if (f==NULL)
244 return ENOENT;
245 else {
246 while (fgets(buf,MAXCMD,f) != NULL) {
247 if (strlen(buf) > 1 && buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]= '\0';
248 if (fd >= 0) {
249 char *scriptprompt=NULL;
250 asprintf(&scriptprompt,"vde[%s]: %s",path,buf);
251 write(fd,scriptprompt,strlen(scriptprompt));
252 free(scriptprompt);
254 handle_cmd(mgmt_data, fd, buf);
256 return 0;
260 void loadrcfile(void)
262 if (rcfile != NULL)
263 runscript(-1,rcfile);
264 else {
265 char path[PATH_MAX];
266 snprintf(path,PATH_MAX,"%s/.vderc",getenv("HOME"));
267 if (access(path,R_OK) == 0)
268 runscript(-1,path);
269 else {
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)
279 char buf[MAXCMD];
280 if (type != mgmt_ctl) {
281 int n=0;
283 if (revents & POLLIN) {
284 n = read(fd, buf, sizeof(buf));
285 if(n < 0){
286 printlog(LOG_WARNING,"Reading from mgmt %s",strerror(errno));
289 if (n==0) { /*EOF*/
290 if (type == console_type) {
291 printlog(LOG_WARNING,"EOF on stdin, cleaning up and exiting");
292 exit(0);
293 } else {
294 remove_fd(fd);
296 } else {
297 int cmdout;
298 buf[n]=0;
299 if (n>0 && buf[n-1] == '\n') buf[n-1] = 0;
300 cmdout=handle_cmd(type,(type==console_type)?STDOUT_FILENO:fd,buf);
301 if (cmdout >= 0)
302 write(fd,prompt,strlen(prompt));
303 else {
304 if(type==mgmt_data) {
305 write(fd,EOS,strlen(EOS));
306 remove_fd(fd);
308 if (cmdout == -2)
309 exit(0);
312 } else {/* mgmt ctl */
313 struct sockaddr addr;
314 int new;
315 socklen_t len;
317 len = sizeof(addr);
318 new = accept(fd, &addr, &len);
319 if(new < 0){
320 printlog(LOG_WARNING,"mgmt accept %s",strerror(errno));
321 return;
323 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
324 printlog(LOG_WARNING,"mgmt fcntl - setting O_NONBLOCK %s",strerror(errno));
325 close(new);
326 return;
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));
340 else
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);
346 FILE *f;
348 if(fd == -1) {
349 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
350 exit(1);
353 if((f = fdopen(fd, "w")) == NULL) {
354 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
355 exit(1);
358 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
359 printlog(LOG_ERR, "Error in writing pidfile");
360 exit(1);
363 fclose(f);
366 static void cleanup(unsigned char type,int fd,int arg)
368 if (fd < 0) {
369 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
370 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
372 } else {
373 close(fd);
374 if (type == mgmt_ctl && mgmt_socket != NULL) {
375 unlink(mgmt_socket);
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'},
386 {"mgmt", 1, 0, 'M'},
387 {"mgmtmode", 1, 0, MGMTMODEARG},
388 #ifdef DEBUGOPT
389 {"debugclients",1,0,'D'},
390 #endif
393 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
395 static void usage(void)
397 printf(
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"
404 #ifdef DEBUGOPT
405 " -D, --debugclients # number of debug clients allowed\n"
406 #endif
407 ,STDRCFILE);
410 static int parseopt(int c, char *optarg)
412 int outc=0;
413 switch (c) {
414 case 'd':
415 daemonize=1;
416 break;
417 case 'p':
418 pidfile=strdup(optarg);
419 break;
420 case 'f':
421 rcfile=strdup(optarg);
422 break;
423 case 'M':
424 mgmt_socket=strdup(optarg);
425 break;
426 case MGMTMODEARG:
427 sscanf(optarg,"%o",&mgmt_mode);
428 break;
429 #ifdef DEBUGOPT
430 case 'D':
431 ndebugclients = atoi(optarg);
432 break;
433 #endif
434 default:
435 outc=c;
437 return outc;
440 static void init(void)
442 if (daemonize) {
443 openlog(basename(prog), LOG_PID, 0);
444 logok=1;
445 syslog(LOG_INFO,"VDE_SWITCH started");
447 /* add stdin (if tty), connect and data fds to the set of fds we wait for
448 * * input */
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
456 * * forget it */
457 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
458 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
459 exit(1);
461 strcat(pidfile_path, "/");
462 if (daemonize && daemon(0, 0)) {
463 printlog(LOG_ERR,"daemon: %s",strerror(errno));
464 exit(1);
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) {
472 int mgmtconnfd;
473 struct sockaddr_un sun;
474 int one = 1;
476 if((mgmtconnfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
477 printlog(LOG_ERR,"mgmt socket: %s",strerror(errno));
478 return;
480 if(setsockopt(mgmtconnfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
481 sizeof(one)) < 0){
482 printlog(LOG_ERR,"mgmt setsockopt: %s",strerror(errno));
483 return;
485 if(fcntl(mgmtconnfd, F_SETFL, O_NONBLOCK) < 0){
486 printlog(LOG_ERR,"Setting O_NONBLOCK on mgmt fd: %s",strerror(errno));
487 return;
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));
495 return;
498 chmod(sun.sun_path,mgmt_mode);
499 if(listen(mgmtconnfd, 15) < 0){
500 printlog(LOG_ERR,"mgmt listen: %s",strerror(errno));
501 return;
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()
511 return -1;
514 static int vde_shutdown()
516 printlog(LOG_WARNING,"Shutdown from mgmt command");
517 return -2;
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],
525 qtime());
526 if (mgmt_socket)
527 printoutc(fd,"mgmt %s perm 0%03o",mgmt_socket,mgmt_mode);
528 printoutc(fd,"unsent_pktq_len %d",packetq_count());
529 return 0;
532 #ifdef DEBUGOPT
533 static int debuglist(FILE *f,int fd,char *arg)
535 #define DEBUGFORMAT "%-22s %-6s %s"
536 struct dbgcl *p;
537 int i;
538 int rv=ENOENT;
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++)
545 rv=0;
546 printoutc(f, DEBUGFORMAT, p->path, i<ndebugclients ? "ON" : "OFF", p->help);
549 return rv;
552 /* EINVAL -> no matches
553 * EEXIST -> all the matches already include fd
554 * EMFILE -> fd buffer averflow in at least one match
555 * 0 otherwise */
556 static int debugadd(int fd,char *arg) {
557 struct dbgcl *p;
558 int rv=EINVAL;
559 for (p=dbgclh; p!=NULL; p=p->next) {
560 if (strncmp(p->path, arg, strlen(arg)) == 0) {
561 int i;
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 ) {
567 p->fds[i] = fd;
568 if (rv!=EMFILE) rv=0;
570 } else
571 rv=EMFILE;
574 return rv;
577 /* EINVAL -> no matches
578 * ENOENT -> all the matches do not include fd
579 * 0 otherwise */
580 static int debugdel(int fd,char *arg) {
581 struct dbgcl *p;
582 int rv=EINVAL;
583 for (p=dbgclh; p!=NULL; p=p->next){
584 if (strncmp(p->path, arg, strlen(arg)) == 0) {
585 int i;
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) {
590 int j;
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
596 null itself */
597 rv=0;
601 return rv;
604 #endif
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},
612 #ifdef DEBUGOPT
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},
617 #endif
620 void start_consmgmt(void)
622 swmi.swmname="console-mgmt";
623 swmi.swmnopts=Nlong_options;
624 swmi.swmopts=long_options;
625 swmi.usage=usage;
626 swmi.parseopt=parseopt;
627 swmi.init=init;
628 swmi.handle_input=handle_input;
629 swmi.cleanup=cleanup;
630 ADDCL(cl);
631 add_swm(&swmi);