vdeplug_open: NULL or empty string are now both valid id for the standard switch
[vde.git] / vde-2 / consmgmt.c
blob8b451bd43ddcc4d0c7c83f75e90fc80ba96c178b
1 /* Copyright 2005 Renzo Davoli - VDE-2
2 * --pidfile/-p and cleanup management by Mattia Belletti (C) 2004.
3 * Licensed under the GPLv2
4 */
6 #include <config.h>
7 #include <stdio.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <syslog.h>
13 #include <stdlib.h>
14 #include <libgen.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/ioctl.h>
18 #include <sys/socket.h>
19 #include <sys/poll.h>
20 #include <sys/un.h>
21 #include <net/if.h>
22 #include <stdarg.h>
23 #define _GNU_SOURCE
24 #include <getopt.h>
26 #include <vde.h>
27 #include <port.h>
28 #include <switch.h>
29 #include <sockutils.h>
30 #include <consmgmt.h>
31 #include <qtimer.h>
32 #include <packetq.h>
34 #define MAXCMD 128
36 static struct swmodule swmi;
38 static int logok=0;
39 static char *rcfile;
40 static char *pidfile = NULL;
41 static char pidfile_path[PATH_MAX];
42 static int daemonize = 0;
43 static unsigned int console_type=-1;
44 static unsigned int mgmt_ctl=-1;
45 static unsigned int mgmt_data=-1;
46 static int mgmt_mode = 0600;
47 static char *mgmt_socket = NULL;
48 static char header[]="VDE switch V.%s\n(C) R.Davoli 2005 - GPLv2\n";
49 static char prompt[]="\nvde: ";
51 static struct comlist *clh=NULL;
53 void addcl(int ncl,struct comlist *cl)
55 register int i;
56 static struct comlist **clt=&clh;
57 for (i=0;i<ncl;i++,cl++) {
58 cl->next=NULL;
59 (*clt)=cl;
60 clt=(&cl->next);
64 void printlog(int priority, const char *format, ...)
66 va_list arg;
68 va_start (arg, format);
70 if (logok)
71 vsyslog(priority,format,arg);
72 else {
73 fprintf(stderr,"%s: ",prog);
74 vfprintf(stderr,format,arg);
75 fprintf(stderr,"\n");
77 va_end (arg);
80 void printoutc(int fd, const char *format, ...)
82 va_list arg;
84 va_start (arg, format);
85 if (fd < 0)
86 printlog(LOG_INFO,format,arg);
87 else {
88 char outbuf[MAXCMD+1];
89 vsnprintf(outbuf,MAXCMD,format,arg);
90 strcat(outbuf,"\n");
91 write(fd,outbuf,strlen(outbuf));
95 void setmgmtperm(char *path)
97 chmod(path,mgmt_mode);
100 static int help(int fd,char *arg)
102 struct comlist *p;
103 int n=strlen(arg);
104 printoutc(fd,"%-18s %-15s %s","COMMAND PATH","SYNTAX","HELP");
105 printoutc(fd,"%-18s %-15s %s","------------","--------------","------------");
106 for (p=clh;p!=NULL;p=p->next)
107 if (strncmp(p->path,arg,n) == 0)
108 printoutc(fd,"%-18s %-15s %s",p->path,p->syntax,p->help);
109 return 0;
112 static int handle_cmd(int type,int fd,char *inbuf)
114 struct comlist *p;
115 int rv=ENOSYS;
116 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
117 if (*inbuf != '\0' && *inbuf != '#') {
118 for (p=clh;p!=NULL && (p->doit==NULL || strncmp(p->path,inbuf,strlen(p->path))!=0); p=p->next)
120 if (p!=NULL)
122 inbuf += strlen(p->path);
123 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
124 if (p->type & WITHFD) {
125 printoutc(fd,"0000 DATA END WITH '.'");
126 switch(p->type & ~WITHFD){
127 case NOARG: rv=p->doit(fd); break;
128 case INTARG: rv=p->doit(fd,atoi(inbuf)); break;
129 case STRARG: rv=p->doit(fd,inbuf); break;
131 printoutc(fd,".");
132 } else {
133 switch(p->type){
134 case NOARG: rv=p->doit(); break;
135 case INTARG: rv=p->doit(atoi(inbuf)); break;
136 case STRARG: rv=p->doit(inbuf); break;
140 if (rv >= 0 && (rv > 0 || fd >= 0))
141 printoutc(fd,"1%03d %s",rv,strerror(rv));
143 return rv;
146 static int runscript(int fd,char *path)
148 FILE *f=fopen(path,"r");
149 char buf[MAXCMD];
150 if (f==NULL)
151 return ENOENT;
152 else {
153 while (fgets(buf,MAXCMD,f) != NULL) {
154 if (strlen(buf) > 1 && buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]= '\0';
155 if (fd >= 0) printoutc(fd,"vde[%s]: %s",path,buf);
156 handle_cmd(mgmt_data, fd, buf);
158 return 0;
162 void loadrcfile(void)
164 if (rcfile != NULL)
165 runscript(-1,rcfile);
166 else {
167 char path[PATH_MAX];
168 snprintf(path,PATH_MAX,"%s/.vderc",getenv("HOME"));
169 if (access(path,R_OK) == 0)
170 runscript(-1,path);
171 else {
172 if (access(STDRCFILE,R_OK) == 0)
173 runscript(-1,STDRCFILE);
178 static void handle_input(unsigned char type,int fd,int revents,int *unused)
180 char buf[MAXCMD];
181 if (type != mgmt_ctl) {
182 int n=0;
184 if (revents & POLLIN) {
185 n = read(fd, buf, sizeof(buf));
186 if(n < 0){
187 printlog(LOG_WARNING,"Reading from mgmt %s",strerror(errno));
190 if (n==0) { /*EOF*/
191 if (type == console_type) {
192 printlog(LOG_WARNING,"EOF on stdin, cleaning up and exiting");
193 exit(0);
194 } else {
195 remove_fd(fd);
197 } else {
198 int cmdout;
199 buf[n]=0;
200 if (n>0 && buf[n-1] == '\n') buf[n-1] = 0;
201 cmdout=handle_cmd(type,(type==console_type)?STDOUT_FILENO:fd,buf);
202 if (cmdout >= 0)
203 write(fd,prompt,strlen(prompt));
204 else {
205 if(type==mgmt_data) {
206 printoutc(fd,"9999 END OF SESSION");
207 remove_fd(fd);
209 if (cmdout == -2)
210 exit(0);
213 } else {/* mgmt ctl */
214 struct sockaddr addr;
215 int new;
216 socklen_t len;
218 len = sizeof(addr);
219 new = accept(fd, &addr, &len);
220 if(new < 0){
221 printlog(LOG_WARNING,"mgmt accept %s",strerror(errno));
222 return;
224 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
225 printlog(LOG_WARNING,"mgmt fcntl - setting O_NONBLOCK %s",strerror(errno));
226 close(new);
227 return;
230 add_fd(new,mgmt_data,-1);
231 snprintf(buf,MAXCMD,header,PACKAGE_VERSION);
232 write(new,buf,strlen(buf));
233 write(new,prompt,strlen(prompt));
237 static void save_pidfile()
239 if(pidfile[0] != '/')
240 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
241 else
242 strcpy(pidfile_path, pidfile);
244 int fd = open(pidfile_path,
245 O_WRONLY | O_CREAT | O_EXCL,
246 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
247 FILE *f;
249 if(fd == -1) {
250 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
251 exit(1);
254 if((f = fdopen(fd, "w")) == NULL) {
255 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
256 exit(1);
259 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
260 printlog(LOG_ERR, "Error in writing pidfile");
261 exit(1);
264 fclose(f);
267 static void cleanup(unsigned char type,int fd,int arg)
269 if (fd < 0) {
270 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
271 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
273 } else {
274 close(fd);
275 if (type == mgmt_ctl && mgmt_socket != NULL) {
276 unlink(mgmt_socket);
281 #define MGMTMODEARG 0x100
283 static struct option long_options[] = {
284 {"daemon", 0, 0, 'd'},
285 {"pidfile", 1, 0, 'p'},
286 {"rcfile", 1, 0, 'f'},
287 {"mgmt", 1, 0, 'M'},
288 {"mgmtmode", 1, 0, MGMTMODEARG}
291 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
293 static void usage(void)
295 printf(
296 "(opts from consmgmt module)\n"
297 " -d, --daemon Daemonize vde_switch once run\n"
298 " -p, --pidfile PIDFILE Write pid of daemon to PIDFILE\n"
299 " -f, --rcfile Configuration file (overrides %s and ~/.vderc)\n"
300 " -M, --mgmt SOCK path of the management UNIX socket\n"
301 " --mgmtmode MODE management UNIX socket access mode (octal)\n"
302 ,STDRCFILE);
305 static int parseopt(int c, char *optarg)
307 int outc=0;
308 switch (c) {
309 case 'd':
310 daemonize=1;
311 break;
312 case 'p':
313 pidfile=strdup(optarg);
314 break;
315 case 'f':
316 rcfile=strdup(optarg);
317 break;
318 case 'M':
319 mgmt_socket=strdup(optarg);
320 break;
321 case MGMTMODEARG:
322 sscanf(optarg,"%o",&mgmt_mode);
323 break;
324 default:
325 outc=c;
327 return outc;
330 static void init(void)
332 if (daemonize) {
333 openlog(basename(prog), LOG_PID, 0);
334 logok=1;
335 syslog(LOG_INFO,"VDE_SWITCH started");
337 /* add stdin (if tty), connect and data fds to the set of fds we wait for
338 * * input */
339 if(isatty(0) && !daemonize)
341 console_type=add_type(&swmi,0);
342 add_fd(0,console_type,-1);
345 /* saves current path in pidfile_path, because otherwise with daemonize() we
346 * * forget it */
347 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
348 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
349 exit(1);
351 strcat(pidfile_path, "/");
352 if (daemonize && daemon(0, 0)) {
353 printlog(LOG_ERR,"daemon: %s",strerror(errno));
354 exit(1);
357 /* once here, we're sure we're the true process which will continue as a
358 * * server: save PID file if needed */
359 if(pidfile) save_pidfile();
361 if(mgmt_socket != NULL) {
362 int mgmtconnfd;
363 struct sockaddr_un sun;
364 int one = 1;
366 if((mgmtconnfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
367 printlog(LOG_ERR,"mgmt socket: %s",strerror(errno));
368 return;
370 if(setsockopt(mgmtconnfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
371 sizeof(one)) < 0){
372 printlog(LOG_ERR,"mgmt setsockopt: %s",strerror(errno));
373 return;
375 if(fcntl(mgmtconnfd, F_SETFL, O_NONBLOCK) < 0){
376 printlog(LOG_ERR,"Setting O_NONBLOCK on mgmt fd: %s",strerror(errno));
377 return;
379 sun.sun_family = PF_UNIX;
380 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s",mgmt_socket);
381 if(bind(mgmtconnfd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
382 if((errno == EADDRINUSE) && still_used(&sun)) return;
383 else if(bind(mgmtconnfd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
384 printlog(LOG_ERR,"mgmt bind %s",strerror(errno));
385 return;
388 chmod(sun.sun_path,mgmt_mode);
389 if(listen(mgmtconnfd, 15) < 0){
390 printlog(LOG_ERR,"mgmt listen: %s",strerror(errno));
391 return;
393 mgmt_ctl=add_type(&swmi,0);
394 mgmt_data=add_type(&swmi,0);
395 add_fd(mgmtconnfd,mgmt_ctl,-1);
399 static int vde_logout()
401 return -1;
404 static int vde_shutdown()
406 printlog(LOG_WARNING,"Shutdown from mgmt command");
407 return -2;
410 static int showinfo(int fd)
412 printoutc(fd,header,PACKAGE_VERSION);
413 printoutc(fd,"pid %d MAC %02x:%02x:%02x:%02x:%02x:%02x uptime %d",getpid(),
414 switchmac[0], switchmac[1], switchmac[2], switchmac[3], switchmac[4], switchmac[5],
415 qtime());
416 if (mgmt_socket)
417 printoutc(fd,"mgmt %s perm 0%03o",mgmt_socket,mgmt_mode);
418 printoutc(fd,"unsent_pktq_len %d",packetq_count());
419 return 0;
422 static struct comlist cl[]={
423 {"help","[arg]","Help (limited to arg when specified)",help,STRARG | WITHFD},
424 {"logout","","logout from this mgmt terminal",vde_logout,NOARG},
425 {"shutdown","","shutdown of the switch",vde_shutdown,NOARG},
426 {"showinfo","","show switch version and info",showinfo,NOARG|WITHFD},
427 {"load","path","load a configuration script",runscript,STRARG|WITHFD},
430 void start_consmgmt(void)
432 swmi.swmname="console-mgmt";
433 swmi.swmnopts=Nlong_options;
434 swmi.swmopts=long_options;
435 swmi.usage=usage;
436 swmi.parseopt=parseopt;
437 swmi.init=init;
438 swmi.handle_input=handle_input;
439 swmi.cleanup=cleanup;
440 ADDCL(cl);
441 add_swm(&swmi);