version change
[vde.git] / vde-2 / consmgmt.c
blobae133d80edf6a4afe85f4e07f0fbcd133a9744ce
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 <port.h>
27 #include <switch.h>
28 #include <sockutils.h>
29 #include <consmgmt.h>
30 #include <qtimer.h>
32 #define MAXCMD 128
34 static struct swmodule swmi;
36 static int logok=0;
37 static char *rcfile;
38 static char *pidfile = NULL;
39 static char pidfile_path[_POSIX_PATH_MAX];
40 static int daemonize = 0;
41 static unsigned int console_type=-1;
42 static unsigned int mgmt_ctl=-1;
43 static unsigned int mgmt_data=-1;
44 static int mgmt_mode = 0600;
45 static char *mgmt_socket = NULL;
46 static char header[]="VDE switch V.%s\n(C) R.Davoli 2005 - GPLv2\n";
47 static char prompt[]="\nvde: ";
49 static struct comlist *clh=NULL;
51 void addcl(int ncl,struct comlist *cl)
53 register int i;
54 static struct comlist **clt=&clh;
55 for (i=0;i<ncl;i++,cl++) {
56 cl->next=NULL;
57 (*clt)=cl;
58 clt=(&cl->next);
62 void printlog(int priority, const char *format, ...)
64 va_list arg;
66 va_start (arg, format);
68 if (logok)
69 vsyslog(priority,format,arg);
70 else {
71 fprintf(stderr,"%s: ",prog);
72 vfprintf(stderr,format,arg);
73 fprintf(stderr,"\n");
75 va_end (arg);
78 void printoutc(int fd, const char *format, ...)
80 va_list arg;
82 va_start (arg, format);
83 if (fd < 0)
84 printlog(LOG_INFO,format,arg);
85 else {
86 char outbuf[MAXCMD+1];
87 vsnprintf(outbuf,MAXCMD,format,arg);
88 strcat(outbuf,"\n");
89 write(fd,outbuf,strlen(outbuf));
93 void setmgmtperm(char *path)
95 chmod(path,mgmt_mode);
98 static int help(int fd,char *arg)
100 struct comlist *p;
101 int n=strlen(arg);
102 printoutc(fd,"%-18s %-15s %s","COMMAND PATH","SYNTAX","HELP");
103 printoutc(fd,"%-18s %-15s %s","------------","--------------","------------");
104 for (p=clh;p!=NULL;p=p->next)
105 if (strncmp(p->path,arg,n) == 0)
106 printoutc(fd,"%-18s %-15s %s",p->path,p->syntax,p->help);
107 return 0;
110 static int handle_cmd(int type,int fd,char *inbuf)
112 struct comlist *p;
113 int rv=ENOSYS;
114 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
115 if (*inbuf != '\0' && *inbuf != '#') {
116 for (p=clh;p!=NULL && (p->doit==NULL || strncmp(p->path,inbuf,strlen(p->path))!=0); p=p->next)
118 if (p!=NULL)
120 inbuf += strlen(p->path);
121 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
122 if (p->type & WITHFD) {
123 printoutc(fd,"0000 DATA END WITH '.'");
124 switch(p->type & ~WITHFD){
125 case NOARG: rv=p->doit(fd); break;
126 case INTARG: rv=p->doit(fd,atoi(inbuf)); break;
127 case STRARG: rv=p->doit(fd,inbuf); break;
129 printoutc(fd,".");
130 } else {
131 switch(p->type){
132 case NOARG: rv=p->doit(); break;
133 case INTARG: rv=p->doit(atoi(inbuf)); break;
134 case STRARG: rv=p->doit(inbuf); break;
138 if (rv >= 0 && (rv > 0 || fd >= 0))
139 printoutc(fd,"1%03d %s",rv,strerror(rv));
141 return rv;
144 static int runscript(int fd,char *path)
146 FILE *f=fopen(path,"r");
147 char buf[MAXCMD];
148 if (f==NULL)
149 return ENOENT;
150 else {
151 while (fgets(buf,MAXCMD,f) != NULL) {
152 if (strlen(buf) > 1 && buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]= '\0';
153 if (fd >= 0) printoutc(fd,"vde[%s]: %s",path,buf);
154 handle_cmd(mgmt_data, fd, buf);
156 return 0;
160 void loadrcfile(void)
162 if (rcfile != NULL)
163 runscript(-1,rcfile);
164 else {
165 char path[PATH_MAX];
166 snprintf(path,PATH_MAX,"%s/.vderc",getenv("HOME"));
167 if (access(path,R_OK) == 0)
168 runscript(-1,path);
169 else {
170 if (access(STDRCFILE,R_OK) == 0)
171 runscript(-1,STDRCFILE);
176 static void handle_input(unsigned char type,int fd,int revents,int *unused)
178 char buf[MAXCMD];
179 if (type != mgmt_ctl) {
180 int n=0;
182 if (revents & POLLIN) {
183 n = read(fd, buf, sizeof(buf));
184 if(n < 0){
185 printlog(LOG_WARNING,"Reading from mgmt %s",strerror(errno));
188 if (n==0) { /*EOF*/
189 if (type == console_type) {
190 printlog(LOG_WARNING,"EOF on stdin, cleaning up and exiting");
191 exit(0);
192 } else {
193 remove_fd(fd);
195 } else {
196 int cmdout;
197 buf[n]=0;
198 if (n>0 && buf[n-1] == '\n') buf[n-1] = 0;
199 cmdout=handle_cmd(type,(type==console_type)?STDOUT_FILENO:fd,buf);
200 if (cmdout >= 0)
201 write(fd,prompt,strlen(prompt));
202 else {
203 if(type==mgmt_data) {
204 printoutc(fd,"9999 END OF SESSION");
205 remove_fd(fd);
207 if (cmdout == -2)
208 exit(0);
211 } else {/* mgmt ctl */
212 struct sockaddr addr;
213 int new;
214 socklen_t len;
216 len = sizeof(addr);
217 new = accept(fd, &addr, &len);
218 if(new < 0){
219 printlog(LOG_WARNING,"mgmt accept %s",strerror(errno));
220 return;
222 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
223 printlog(LOG_WARNING,"mgmt fcntl - setting O_NONBLOCK %s",strerror(errno));
224 close(new);
225 return;
228 add_fd(new,mgmt_data,-1);
229 snprintf(buf,MAXCMD,header,PACKAGE_VERSION);
230 write(new,buf,strlen(buf));
231 write(new,prompt,strlen(prompt));
235 static void save_pidfile()
237 if(pidfile[0] != '/')
238 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
239 else
240 strcpy(pidfile_path, pidfile);
242 int fd = open(pidfile_path,
243 O_WRONLY | O_CREAT | O_EXCL,
244 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
245 FILE *f;
247 if(fd == -1) {
248 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
249 exit(1);
252 if((f = fdopen(fd, "w")) == NULL) {
253 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
254 exit(1);
257 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
258 printlog(LOG_ERR, "Error in writing pidfile");
259 exit(1);
262 fclose(f);
265 static void cleanup(unsigned char type,int fd,int arg)
267 if (fd < 0) {
268 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
269 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
271 } else {
272 close(fd);
273 if (type == mgmt_ctl && mgmt_socket != NULL) {
274 unlink(mgmt_socket);
279 #define MGMTMODEARG 0x100
281 static struct option long_options[] = {
282 {"daemon", 0, 0, 'd'},
283 {"pidfile", 1, 0, 'p'},
284 {"rcfile", 1, 0, 'f'},
285 {"mgmt", 1, 0, 'M'},
286 {"mgmtmode", 1, 0, MGMTMODEARG}
289 #define Nlong_options (sizeof(long_options)/sizeof(struct option));
291 static void usage(void)
293 printf(
294 "(opts from consmgmt module)\n"
295 " -d, --daemon Daemonize vde_switch once run\n"
296 " -p, --pidfile PIDFILE Write pid of daemon to PIDFILE\n"
297 " -f, --rcfile Configuration file (overrides %s and ~/.vderc)\n"
298 " -M, --mgmt SOCK path of the management UNIX socket\n"
299 " --mgmtmode MODE management UNIX socket access mode (octal)\n"
300 ,STDRCFILE);
303 static int parseopt(int c, char *optarg)
305 int outc=0;
306 switch (c) {
307 case 'd':
308 daemonize=1;
309 break;
310 case 'p':
311 pidfile=strdup(optarg);
312 break;
313 case 'f':
314 rcfile=strdup(optarg);
315 break;
316 case 'M':
317 mgmt_socket=strdup(optarg);
318 break;
319 case MGMTMODEARG:
320 sscanf(optarg,"%o",&mgmt_mode);
321 break;
322 default:
323 outc=c;
325 return outc;
328 static void init(void)
330 if (daemonize) {
331 openlog(basename(prog), LOG_PID, 0);
332 logok=1;
333 syslog(LOG_INFO,"VDE_SWITCH started");
335 /* add stdin (if tty), connect and data fds to the set of fds we wait for
336 * * input */
337 if(isatty(0) && !daemonize)
339 console_type=add_type(&swmi,0);
340 add_fd(0,console_type,-1);
343 /* saves current path in pidfile_path, because otherwise with daemonize() we
344 * * forget it */
345 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
346 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
347 exit(1);
349 strcat(pidfile_path, "/");
350 if (daemonize && daemon(0, 1)) {
351 printlog(LOG_ERR,"daemon: %s",strerror(errno));
352 exit(1);
355 /* once here, we're sure we're the true process which will continue as a
356 * * server: save PID file if needed */
357 if(pidfile) save_pidfile();
359 if(mgmt_socket != NULL) {
360 int mgmtconnfd;
361 struct sockaddr_un sun;
362 int one = 1;
364 if((mgmtconnfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
365 printlog(LOG_ERR,"mgmt socket: %s",strerror(errno));
366 return;
368 if(setsockopt(mgmtconnfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
369 sizeof(one)) < 0){
370 printlog(LOG_ERR,"mgmt setsockopt: %s",strerror(errno));
371 return;
373 if(fcntl(mgmtconnfd, F_SETFL, O_NONBLOCK) < 0){
374 printlog(LOG_ERR,"Setting O_NONBLOCK on mgmt fd: %s",strerror(errno));
375 return;
377 sun.sun_family = PF_UNIX;
378 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s",mgmt_socket);
379 if(bind(mgmtconnfd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
380 if((errno == EADDRINUSE) && still_used(&sun)) return;
381 else if(bind(mgmtconnfd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
382 printlog(LOG_ERR,"mgmt bind %s",strerror(errno));
383 return;
386 chmod(sun.sun_path,mgmt_mode);
387 if(listen(mgmtconnfd, 15) < 0){
388 printlog(LOG_ERR,"mgmt listen: %s",strerror(errno));
389 return;
391 mgmt_ctl=add_type(&swmi,0);
392 mgmt_data=add_type(&swmi,0);
393 add_fd(mgmtconnfd,mgmt_ctl,-1);
397 static int vde_logout()
399 return -1;
402 static int vde_shutdown()
404 printlog(LOG_WARNING,"Shutdown from mgmt command");
405 return -2;
408 static int showinfo(int fd)
410 printoutc(fd,header,PACKAGE_VERSION);
411 printoutc(fd,"pid %d MAC %02x:%02x:%02x:%02x:%02x:%02x uptime %d",getpid(),
412 switchmac[0], switchmac[1], switchmac[2], switchmac[3], switchmac[4], switchmac[5],
413 qtime());
414 if (mgmt_socket)
415 printoutc(fd,"mgmt %s perm 0%03o",mgmt_socket,mgmt_mode);
416 return 0;
419 static struct comlist cl[]={
420 {"help","[arg]","Help (limited to arg when specified)",help,STRARG | WITHFD},
421 {"logout","","logout from this mgmt terminal",vde_logout,NOARG},
422 {"shutdown","","shutdown of the switch",vde_shutdown,NOARG},
423 {"showinfo","","show switch version and info",showinfo,NOARG|WITHFD},
424 {"load","path","load a configuration script",runscript,STRARG|WITHFD},
427 void start_consmgmt(void)
429 swmi.swmname="console-mgmt";
430 swmi.swmnopts=Nlong_options;
431 swmi.swmopts=long_options;
432 swmi.usage=usage;
433 swmi.parseopt=parseopt;
434 swmi.init=init;
435 swmi.handle_input=handle_input;
436 swmi.cleanup=cleanup;
437 ADDCL(cl);
438 add_swm(&swmi);