tagging vde-2 version 2.3.2
[vde.git] / 2.3.2 / src / vde_autolink.c
blob09c1f454789fdd92935cef92501ab081b8a6fad0
1 /*
2 * Copyright (C) 2007 - Luca Bigliardi
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License
5 * as published by the Free Software Foundation; either version 2
6 * of the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <libgen.h>
26 #include <limits.h>
27 #include <string.h>
28 #include <time.h>
29 #include <sys/wait.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <syslog.h>
33 #include <stdarg.h>
34 #include <sys/types.h>
35 #include <signal.h>
37 #include <config.h>
38 #include <vde.h>
39 #include <vdecommon.h>
40 #include <libvdemgmt.h>
42 #define STDRCFILE "/etc/vde2/vde_autolink.rc"
44 #define MAXCONS 4
45 #define MGMTMODEARG 129
46 #define MAXPORTS 256
48 #define FSTPDBG_PADD "fstp/+"
49 #define FSTPDBG_PDEL "fstp/-"
50 #define FSTPDBG_STAT "fstp/status"
52 #define MAXCMD 128
53 #define BUFSIZE 1024
55 #define CHANGEWIRETIME 8
56 #define SLEEPWIRETIME 30
57 #define SCHED_TRY 60
58 #define SCHED_LONGTRY 120
59 #define SCHED_CHECK 30
61 #define ST_DISCARD 0
62 #define ST_ACTIVE 1
64 char *progname = NULL;
65 char *mgmt = NULL;
66 int mgmtmode = 0700;
67 char *vdeswitch = NULL;
68 char *switchmgmt = NULL;
69 int daemonize = 0;
70 char *rcfile = NULL;
71 char *pidfile = NULL;
72 char pidfile_path[PATH_MAX];
73 struct pollfd pfd[MAXCONS];
74 int logok=0;
75 struct vdemgmt* vdemgmt=NULL;
76 int polltimeout=-1;
78 static int runscript(int fd,char *path);
80 static char prompt[]="\nVDEal$ ";
81 static char header[]="\nVDE autolink V.%s\n(C) L.Bigliardi 2007 - GPLv2\n";
83 static char *myport = "$myport";
84 static char *mysock = "$mysock";
85 static char *myhost = "$remotehost";
87 struct wire {
88 char *type;
89 char *cmd;
90 struct wire *next;
93 struct alwire {
94 char *type;
95 char *cmd;
96 time_t try;
97 time_t oldtry;
98 struct alwire *next;
101 struct autolink {
102 char *name; /* alink name */
103 char **hosts; /* list of remote hosts */
104 unsigned int portno; /* number of switch port */
105 int enabled; /* flag for active */
106 int state; /* link status */
107 int connhost; /* current remote host to connect to */
108 struct alwire *connwire; /* current type of wire we try to use */
109 int wirepid; /* pid of wire, -1 if no up */
110 struct alwire **wires; /* list of wire types to use */
111 struct autolink *next;
114 static struct wire *av_wires = NULL;
115 static struct autolink *alinks = NULL;
117 struct job{
118 void (*f)(struct autolink *al);
119 time_t time;
120 struct autolink *al;
121 struct job *n;
124 static struct job *jq = NULL;
126 /* Generic utils (from vde framework) */
127 void printlog(int priority, const char *format, ...)
129 va_list arg;
131 va_start (arg, format);
133 if (logok)
134 vsyslog(priority,format,arg);
135 else {
136 fprintf(stderr,"%s: ",progname);
137 vfprintf(stderr,format,arg);
138 fprintf(stderr,"\n");
140 va_end (arg);
143 void printoutc(int fd, const char *format, ...)
145 va_list arg;
146 char outbuf[MAXCMD+1];
148 va_start (arg, format);
149 vsnprintf(outbuf,MAXCMD,format,arg);
150 strcat(outbuf,"\n");
151 write(fd,outbuf,strlen(outbuf));
154 void port_dispose(int p);
156 static void cleanup(void)
159 int tmppid;
160 struct autolink *curlink = alinks;
162 /* kill every link */
163 while(curlink){
164 port_dispose(curlink->portno);
165 if ( (tmppid = curlink->wirepid) != -1) {
166 curlink->wirepid = -1;
167 kill(tmppid, SIGQUIT);
169 curlink = curlink->next;
172 /* close management connections */
173 if (mgmt)
174 unlink(mgmt);
175 if (vdemgmt) {
176 vdemgmt_asyncunreg(vdemgmt, FSTPDBG_PADD);
177 vdemgmt_asyncunreg(vdemgmt, FSTPDBG_PDEL);
178 vdemgmt_asyncunreg(vdemgmt, FSTPDBG_STAT);
179 vdemgmt_close(vdemgmt);
183 static void sig_handler(int sig)
186 /*fprintf(stderr,"Caught signal %d, cleaning up and exiting", sig);*/
187 cleanup();
188 signal(sig, SIG_DFL);
189 if (sig == SIGTERM)
190 _exit(0);
191 else
192 kill(getpid(), sig);
195 struct autolink *find_alink_pid(int pid);
197 static void catch_zombies(int signo)
199 int status;
200 struct autolink *a;
202 if( (a=find_alink_pid(wait(&status))) )
203 a->wirepid = -1;
206 static void setsighandlers()
208 /* setting signal handlers.
209 * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
210 * ignores all the others signals which could cause termination. */
211 struct { int sig; const char *name; int ignore; } signals[] = {
212 { SIGHUP, "SIGHUP", 0 },
213 { SIGINT, "SIGINT", 0 },
214 { SIGPIPE, "SIGPIPE", 1 },
215 { SIGALRM, "SIGALRM", 1 },
216 { SIGTERM, "SIGTERM", 0 },
217 { SIGUSR1, "SIGUSR1", 1 },
218 { SIGUSR2, "SIGUSR2", 1 },
219 { SIGPROF, "SIGPROF", 1 },
220 { SIGVTALRM, "SIGVTALRM", 1 },
221 #ifdef VDE_LINUX
222 { SIGPOLL, "SIGPOLL", 1 },
223 #ifdef SIGSTKFLT
224 { SIGSTKFLT, "SIGSTKFLT", 1 },
225 #endif
226 { SIGIO, "SIGIO", 1 },
227 { SIGPWR, "SIGPWR", 1 },
228 #ifdef SIGUNUSED
229 { SIGUNUSED, "SIGUNUSED", 1 },
230 #endif
231 #endif
232 #ifdef VDE_DARWIN
233 { SIGXCPU, "SIGXCPU", 1 },
234 { SIGXFSZ, "SIGXFSZ", 1 },
235 #endif
236 { 0, NULL, 0 }
239 int i;
240 for(i = 0; signals[i].sig != 0; i++)
241 if(signal(signals[i].sig, signals[i].ignore ? SIG_IGN :
242 sig_handler) < 0)
243 printlog(LOG_ERR,"Setting handler for %s: %s",
244 signals[i].name, strerror(errno));
246 signal(SIGCHLD,catch_zombies);
249 /* Autolink Utils */
250 struct wire *find_wire(char *type)
252 struct wire *curwire = av_wires;
253 while(curwire){
254 if(!strcmp(curwire->type, type))
255 return curwire;
256 curwire = curwire->next;
258 return NULL;
261 struct alwire *find_alwire(char *type, struct autolink *alink)
263 struct alwire *curalwire = alink->wires[0];
264 /* each wires[i] has same types */
265 while(curalwire){
266 if(!strcmp(curalwire->type, type))
267 return curalwire;
268 curalwire = curalwire->next;
270 return NULL;
273 struct autolink *find_alink_port(int port)
275 struct autolink *curlink = alinks;
276 while(curlink){
277 if( curlink->portno == port )
278 return curlink;
279 curlink = curlink->next;
281 return NULL;
284 struct autolink *find_alink_pid(int pid)
286 struct autolink *curlink = alinks;
287 while(curlink){
288 if(curlink->wirepid == pid )
289 return curlink;
290 curlink = curlink->next;
292 return NULL;
295 struct autolink *find_alink(char *name)
297 struct autolink *curlink = alinks;
298 while(curlink){
299 if(!strcmp(curlink->name, name))
300 return curlink;
301 curlink = curlink->next;
303 return NULL;
306 struct autolink *alink_exists(struct autolink *al)
308 struct autolink *c = alinks;
309 while(c){
310 if (c == al)
311 return c;
312 c = c->next;
314 return NULL;
317 int port_reserve(void)
320 int p; char cmd[strlen("port/create")+5];
322 for(p=1; p <= MAXPORTS ; p++){
323 sprintf(cmd, "port/create %d", p);
324 if(!vdemgmt_sendcmd(vdemgmt, cmd, NULL))
325 return p;
327 return -1;
330 void port_dispose(int p)
333 char cmd[strlen("port/remove")+5];
334 sprintf(cmd, "port/remove %d", p);
335 vdemgmt_sendcmd(vdemgmt, cmd, NULL);
339 char *strrplc(char **s, char *old, char *new)
341 /* create new string (free old) replacing old with new */
343 char *limit, *new_s, *old_s;
344 int slen, oldlen, newlen, headlen, diff, taillen = 0;
346 old_s = *s;
348 slen=strlen(old_s); oldlen=strlen(old); newlen=strlen(new);
350 limit = strstr(old_s, old);
351 if ( limit == NULL )
352 return NULL;
354 headlen = (int)(limit - old_s);
355 diff = newlen - oldlen;
356 taillen = slen - ( headlen + oldlen );
358 if( (new_s=(char *)malloc(slen+diff+1)) == NULL)
359 return NULL;
361 snprintf(new_s, headlen+1, "%s", old_s);
362 snprintf(new_s+headlen, newlen+1, "%s", new);
363 snprintf(new_s+headlen+newlen, taillen+1, "%s", old_s+headlen+oldlen);
365 *s = new_s;
366 return new_s;
369 void alink_connect(struct autolink *link)
372 char *token, *dupcmd, **myargv = NULL;
373 int count=0, s[2], sdata=1;
375 if(!link->connwire){
376 printlog(LOG_ERR, "alink_connect null connwire");
377 exit(1);
380 printlog(LOG_NOTICE,"[%s] connecting wire: %s to %s", link->name,
381 link->connwire->type, link->hosts[link->connhost]);
383 for( dupcmd=strdup(link->connwire->cmd) ; ; dupcmd=NULL){
384 token = strtok(dupcmd, " ");
385 myargv=realloc(myargv, (count+1)*sizeof(char *));
386 if(!myargv) exit(1);
387 myargv[count]=token;
388 if( !token ) break;
389 count++;
392 if( socketpair(AF_UNIX, SOCK_STREAM, 0, s) ) exit(1);
394 if( (link->wirepid = fork()) == 0 ){
395 /* parent goes first, otherwise pid may be lost */
396 read(s[1],&sdata,sizeof(int));
397 close(s[0]); close(s[1]);
398 execvp(myargv[0], myargv);
399 /* TODO: handle return from execvp */
400 } else {
401 write(s[0],&sdata,sizeof(int));
402 close(s[0]); close(s[1]);
407 void insert_job(void (*f)(struct autolink *al), struct autolink *al, int gap)
409 struct job *j=jq, *pj=jq, *nj; time_t now;
411 /* remove other jobs for same alink, if any */
412 while(j){
413 if (al == j->al) {
414 if (jq == j) jq=j->n;
415 else pj->n=j->n;
416 free(j);
418 pj = j;
419 j = j->n;
422 /* insert job, ordered by time */
423 if ((nj=(struct job *)malloc(sizeof(struct job))) == NULL){
424 printlog(LOG_ERR, "%s, cannot alloc new job", __FUNCTION__);
425 exit(-1);
427 time(&now);
428 nj->f=f; nj->time=now+gap; nj->al=al; nj->n=NULL;
429 if(jq == NULL){
430 jq = nj;
431 return;
433 j = pj = jq;
434 while(j){
435 if (j->time > nj->time){
436 if (jq == j){
437 jq = nj;
438 jq->n = j;
440 else {
441 pj->n = nj;
442 nj->n = j;
444 return;
446 pj = j;
447 j = j->n;
453 struct job *extract_job()
455 struct job *j = jq;
457 jq=jq->n;
458 return j;
461 /* Async functions and handlers */
462 void alink_try(struct autolink *al);
464 void alink_check(struct autolink *al)
466 if (al->state != ST_ACTIVE){
467 printlog(LOG_NOTICE, "[%s] check failed, scheduled new wire connection", al->name);
468 kill(al->wirepid, SIGQUIT);
469 insert_job(alink_try, al, SCHED_TRY);
471 else
472 printlog(LOG_NOTICE, "[%s] check passed", al->name);
475 void alink_try(struct autolink *al)
478 time_t now;
480 time(&now);
482 /* change wire if died too fast,
483 * try hosts in round robin */
484 if(al->connwire->try > (now - CHANGEWIRETIME)){
485 if(!al->connwire->next){
486 al->connhost++;
487 if( al->hosts[al->connhost] == NULL )
488 al->connhost = 0;
489 al->connwire = al->wires[al->connhost];
490 } else {
491 al->connwire = al->connwire->next;
493 printlog(LOG_NOTICE, "[%s] try next wire: %s (%s)", al->name,
494 al->connwire->type, al->hosts[al->connhost]);
495 /* suspend autolink if cycled too fast */
496 if(al->connwire->oldtry > (now - SLEEPWIRETIME)){
497 printlog(LOG_NOTICE, "[%s], go suspend", al->name);
498 insert_job(alink_try, al, SCHED_LONGTRY);
499 return;
503 al->connwire->oldtry = al->connwire->try;
504 al->connwire->try = now;
506 alink_connect(al);
508 insert_job(alink_check, al, SCHED_CHECK);
511 void ah_padd(const char *event, int tag, const char *data)
514 int port; char *s;
515 struct autolink *al;
517 for( s = (char *)data ; *s != ' ' ; s++);
518 s++;
519 port=atoi(s);
521 al = find_alink_port(port);
522 if (!al || !al->enabled)
523 return;
524 printlog(LOG_NOTICE, "[%s] received %s for port %d", al->name, event, port);
526 if (al->state == ST_DISCARD){
527 al->state = ST_ACTIVE;
528 printlog(LOG_NOTICE, "[%s] state change, discard -> active", al->name);
532 void ah_pdel(const char *event, int tag, const char *data)
535 int port; char *s;
536 struct autolink *al;
538 for( s = (char *)data ; *s != ' ' ; s++);
539 s++;
540 port=atoi(s);
542 al = find_alink_port(port);
543 if (!al || !al->enabled)
544 return;
545 printlog(LOG_NOTICE, "[%s] received %s for port %d", al->name, event, port);
547 if (al->state == ST_ACTIVE){
548 al->state = ST_DISCARD;
549 printlog(LOG_NOTICE, "[%s] state change, active -> discard", al->name);
550 if(al->wirepid != -1)
551 kill(al->wirepid, SIGQUIT);
552 printlog(LOG_NOTICE, "[%s] scheduled new wire connection");
553 insert_job(alink_try, al, SCHED_TRY);
557 void ah_state(const char *event, int tag, const char *data)
559 int port; char *s;
560 struct autolink *al;
562 for( s = (char *)data ; *s != ' ' ; s++);
563 s++;
564 port=atoi(s);
566 al = find_alink_port(port);
567 if (!al || !al->enabled)
568 return;
569 printlog(LOG_NOTICE, "[%s] received %s for port %d", al->name, event, port);
571 if (strstr(data, "learning+forwarding") && (al->state == ST_DISCARD)){
572 al->state = ST_ACTIVE;
573 printlog(LOG_NOTICE, "[%s] state change, discard -> active", al->name);
574 return;
576 if (strstr(data, "discarding") && (al->state == ST_ACTIVE)){
577 al->state = ST_DISCARD;
578 printlog(LOG_NOTICE, "[%s] state change, active -> discard", al->name);
579 return;
583 /* MGMT functions */
584 int jobsqueue(int fd, char *arg)
586 time_t now; struct job *j;
588 if(!jq){
589 printoutc(fd, "jobs queue is empty");
590 return 0;
592 time(&now);
593 j = jq;
594 while (j){
595 printoutc(fd, "TIME: %d, ACTION: %s, LINK: %s", j->time - now,
596 (j->f == alink_try) ? "try " : "check", j->al->name);
597 j = j->n;
599 printoutc(fd, "");
600 return 0;
603 int alinklinkonoff(int fd, char *arg)
606 char *endname, *name;
607 int namelen, vallen, value;
608 struct autolink *curlink;
610 /* check if we have name and type */
611 endname = strstr(arg, " ");
612 namelen = (int)(endname - arg);
613 if( namelen <= 0 ) return EINVAL;
615 vallen = (int)(arg+strlen(arg) - (endname+1));
616 if( vallen <= 0 ) return EINVAL;
618 if( sscanf(endname+1, "%i", &value) != 1)
619 return EINVAL;
621 /* pick autolink and wire */
622 if( (name = (char *)malloc(namelen+1) ) == NULL ) exit(1);
623 snprintf(name, namelen+1, "%s", arg);
625 curlink = find_alink(name);
626 free(name);
627 if(!curlink) return ENXIO;
629 if(value){
630 if(!curlink->wires) return ENXIO;
631 if(curlink->enabled) return 0;
632 curlink->enabled = 1;
633 curlink->state = ST_DISCARD;
634 curlink->connwire=curlink->wires[0];
635 alink_try(curlink);
637 else {
638 if(!curlink->enabled) return 0;
639 curlink->enabled = 0;
640 kill(curlink->wirepid, SIGQUIT);
641 curlink->connwire = NULL;
643 return 0;
646 int alinkdeltypelink(int fd, char *arg)
649 char *endname, *name, *type;
650 int namelen, typelen, i;
651 struct autolink *curlink;
652 struct alwire *curalwire, *prevalwire;
654 /* check if we have name and type */
655 endname = strstr(arg, " ");
656 namelen = (int)(endname - arg);
657 if( namelen <= 0 ) return EINVAL;
659 typelen = strlen(arg) - namelen -1;
660 if( typelen <= 0 ) return EINVAL;
662 /* pick autolink */
663 if( (name = (char *)malloc(namelen+1) ) == NULL ) exit(1);
664 snprintf(name, namelen+1, "%s", arg);
666 curlink = find_alink(name);
667 free(name);
668 if(!curlink) return ENXIO;
669 if(curlink->enabled) return EINVAL; /* avoid RC */
671 if(!curlink->wires[0]) return EINVAL; /* no wires! */
673 /* delete alwire */
674 if( (type = (char *)malloc(typelen+1) ) == NULL ) exit(1);
675 snprintf(type, typelen+1, "%s", endname+1);
677 for( i = 0 ; curlink->hosts[i] != NULL ; i++){
678 curalwire = prevalwire = curlink->wires[i];
680 while(curalwire){
681 if(!strcmp(curalwire->type, type)){
682 if(curalwire == curlink->wires[i]){
683 curlink->wires[i] = curalwire->next;
685 else {
686 prevalwire->next = curalwire->next;
688 free(curalwire->type);
689 free(curalwire->cmd);
690 free(curalwire);
691 free(type);
692 return 0;
694 prevalwire = curalwire;
695 curalwire = curalwire->next;
699 free(type);
700 return EINVAL;
703 int alinkaddtypelink(int fd, char *arg)
706 char *endname, *name, *type, portbuf[42];
707 int namelen, typelen, i;
708 struct autolink *curlink;
709 struct wire *wire;
710 struct alwire *alwire;
712 /* check if we have name and type */
713 endname = strstr(arg, " ");
714 namelen = (int)(endname - arg);
715 if( namelen <= 0 ) return EINVAL;
717 typelen = strlen(arg) - namelen -1;
718 if( typelen <= 0 ) return EINVAL;
720 /* pick autolink and wire */
721 if( (name = (char *)malloc(namelen+1) ) == NULL ) exit(1);
722 snprintf(name, namelen+1, "%s", arg);
724 curlink = find_alink(name);
725 free(name);
726 if(!curlink) return ENXIO;
727 if(curlink->enabled) return EINVAL; /* avoid RC */
729 if( (type = (char *)malloc(typelen+1) ) == NULL ) exit(1);
730 snprintf(type, typelen+1, "%s", endname+1);
732 wire = find_wire(type);
733 free(type);
734 if(!wire) return ENXIO;
736 /* only one wire type for each autolink */
737 alwire = find_alwire(wire->type, curlink);
738 if(alwire) return EINVAL;
740 /* alloc alwires */
741 for( i = 0 ; curlink->hosts[i] != NULL ; i++ ){
742 if(!curlink->wires[i]){
743 if( (curlink->wires[i] = (struct alwire *)
744 malloc(sizeof(struct alwire))) == NULL )
745 exit(1);
746 alwire = curlink->wires[i];
748 else {
749 alwire = curlink->wires[i];
750 while(alwire->next)
751 alwire = alwire->next;
752 if( (alwire->next=(struct alwire *)
753 malloc(sizeof(struct alwire))) == NULL )
754 exit(1);
755 alwire = alwire->next;
758 /* set port, sock and remotehost in alwire command */
759 if( (alwire->cmd = (char *)malloc(strlen(wire->cmd)+1)) == NULL)
760 exit(1);
762 strcpy(alwire->cmd, wire->cmd);
763 sprintf(portbuf, "%u", curlink->portno);
764 strrplc(&(alwire->cmd), myport, portbuf);
765 strrplc(&(alwire->cmd), mysock, vdeswitch);
766 strrplc(&(alwire->cmd), myhost, curlink->hosts[i]);
768 /* fill rest of alwire struct */
769 if( (alwire->type = (char *)
770 malloc(strlen(wire->type)+1)) == NULL)
771 exit(1);
773 strcpy(alwire->type, wire->type);
774 alwire->try = 0;
775 alwire->oldtry = 0;
776 alwire->next = NULL;
779 return 0;
782 int alinkdellink(int fd, char *arg)
785 struct autolink *curlink, *prevlink;
786 struct alwire *curalwire, *prevalwire;
787 int i;
789 if(!alinks) return EINVAL;
791 prevlink = curlink = alinks;
792 while(curlink){
793 if(!strcmp(curlink->name, arg)){
794 if(curlink->enabled) return EINVAL; /* avoid RC */
795 if(curlink == alinks){
796 alinks = curlink->next;
798 else {
799 prevlink->next = curlink->next;
801 port_dispose(curlink->portno);
802 free(curlink->name);
803 /* remove hosts and alwires */
804 for ( i = 0 ; curlink->hosts[i] != NULL ; i++){
805 free(curlink->hosts[i]);
806 curalwire = curlink->wires[i];
807 while(curalwire){
808 prevalwire = curalwire;
809 curalwire = curalwire->next;
810 free(prevalwire);
813 free(curlink->hosts);
814 free(curlink->wires);
815 free(curlink);
816 return 0;
818 prevlink = curlink;
819 curlink = curlink->next;
821 return EINVAL;
824 int alinkaddlink(int fd, char *arg)
827 char *name, *endname = NULL, *tmphosts, *token;
828 int namelen, hostlen, i, j;
829 struct autolink *curlink;
831 /* check if we have name and remotehost */
832 endname = strstr(arg, " ");
833 namelen = (int)(endname - arg);
834 if( namelen <= 0 ) return EINVAL;
836 hostlen = strlen(arg) - namelen -1;
837 if( hostlen <= 0 ) return EINVAL;
839 /* alloc and set name */
840 if( (name = (char *)malloc(namelen+1) ) == NULL ) exit(1);
841 snprintf(name, namelen+1, "%s", arg);
843 /* check for duplicate */
844 if( find_alink(name) ){
845 free(name); return EINVAL;
848 /* alloc autolink */
849 if(alinks == NULL){
850 alinks = (struct autolink *)malloc(sizeof(struct autolink));
851 if(alinks == NULL) exit(1);
852 curlink = alinks;
853 } else {
854 curlink = alinks;
855 while(curlink->next)
856 curlink = curlink->next;
857 curlink->next = (struct autolink *)
858 malloc(sizeof(struct autolink));
859 if(curlink->next == NULL) exit(1);
860 curlink = curlink->next;
862 curlink->name = name;
864 /* reserve a port on switch */
865 if( (curlink->portno = port_reserve()) < 0 ){
866 free(curlink->name);
867 free(curlink);
868 if(alinks == curlink) alinks = NULL;
869 return ENXIO;
872 /* alloc and set remote host array (null terminated) */
873 i=0;
874 curlink->hosts=NULL;
875 for( tmphosts=strdup(endname+1) ; ; tmphosts=NULL){
876 token = strtok(tmphosts, " ");
877 curlink->hosts=realloc(curlink->hosts, (i+1)*sizeof(char *));
878 if(!curlink->hosts) exit(1);
879 curlink->hosts[i]=token;
880 if( !token ) break;
881 i++;
883 /* alloc wires array */
884 if( (curlink->wires = malloc(i*sizeof(char *))) == NULL ) exit(1);
885 for( j = 0 ; j < i ; j++)
886 curlink->wires[j] = NULL;
888 curlink->enabled = 0;
889 curlink->state = 0;
890 curlink->connhost = 0;
891 curlink->connwire = NULL;
892 curlink->next = NULL;
894 return 0;
897 int alinkrunninglinks(int fd, char *arg)
899 struct autolink *curlink;
900 time_t now;
902 if(!alinks) return 0;
903 time(&now);
904 curlink = alinks;
905 while (curlink){
906 if( curlink->enabled && (curlink->wirepid != -1) &&
907 ( curlink->state == ST_ACTIVE ) &&
908 (curlink->connwire->try <
909 now - CHANGEWIRETIME) ) {
910 /* show only stable connections */
911 printoutc(fd, "NAME = %s , RHOST = %s , WIRE = %s ,"
912 " PID: %d", curlink->name,
913 curlink->hosts[curlink->connhost],
914 curlink->connwire->type,
915 curlink->wirepid);
916 printoutc(fd, "");
918 curlink = curlink->next;
920 return 0;
923 int alinkshowlinks(int fd, char *arg)
925 struct autolink *curlink;
926 struct alwire *curalwire = NULL;
927 int i ;
929 if(!alinks){
930 printoutc(fd, "no autolink defined");
931 return 0;
933 curlink = alinks;
934 while (curlink){
935 printoutc(fd, "NAME = %s (PORT: %d%s)", curlink->name,
936 curlink->portno,
937 (curlink->enabled?" - ACTIVE":""));
938 for(i = 0 ; curlink->hosts[i] != NULL ; i++){
939 printoutc(fd, "RHOST: %s", curlink->hosts[i]);
940 if(curlink->wires[i]){
941 printoutc(fd, "WIRES:");
942 curalwire = curlink->wires[i];
944 while(curalwire){
945 printoutc(fd,"%s: %s\n", curalwire->type,
946 curalwire->cmd);
947 curalwire = curalwire->next;
950 printoutc(fd, "");
951 curlink = curlink->next;
953 return 0;
956 int alinkdelwire(int fd, char* arg)
958 struct wire *curwire, *prevwire;
960 if(!av_wires) return EINVAL;
962 prevwire = curwire = av_wires;
963 while(curwire){
964 if(!strcmp(curwire->type, arg)){
965 if(curwire == av_wires){
966 av_wires = curwire->next;
968 else {
969 prevwire->next = curwire->next;
971 free(curwire->type);
972 free(curwire->cmd);
973 free(curwire);
974 return 0;
976 prevwire = curwire;
977 curwire = curwire->next;
979 return EINVAL;
982 int alinkaddwire(int fd, char* arg)
985 struct wire *curwire = NULL;
986 char *type = NULL;
987 int typelen = 0;
988 int cmdlen = 0;
990 /* check if we have type and command */
991 char *endtype = strstr(arg, " ");
992 typelen = (int)(endtype - arg);
993 if( typelen <= 0 ) return EINVAL;
994 cmdlen = strlen(arg) - typelen -1;
995 if( cmdlen <= 0 ) return EINVAL;
997 /* alloc and set type */
998 if( (type = (char *)malloc(typelen+1) ) == NULL ) exit(1);
999 snprintf(type, typelen+1, "%s", arg);
1001 /* check for duplicate */
1002 if( find_wire(type) ){
1003 free(type); return EINVAL;
1005 /* alloc wire */
1006 if(av_wires == NULL){
1007 av_wires = (struct wire *)malloc(sizeof(struct wire));
1008 if(av_wires == NULL) exit(1);
1009 curwire = av_wires;
1010 } else {
1011 curwire = av_wires;
1012 while(curwire->next)
1013 curwire = curwire->next;
1014 curwire->next = (struct wire *)malloc(sizeof(struct wire));
1015 if(curwire->next == NULL) exit(1);
1016 curwire = curwire->next;
1019 curwire->next = NULL;
1020 curwire->type = type;
1022 /* alloc and set command */
1023 if( (curwire->cmd = (char *)malloc(cmdlen+1) ) == NULL )
1024 exit(1);
1025 snprintf(curwire->cmd, cmdlen+1, "%s", endtype+1);
1027 /* check variables */
1028 if( !strstr(curwire->cmd, myport) || !strstr(curwire->cmd, mysock) ||
1029 !strstr(curwire->cmd, myhost) ){
1030 free(curwire->type); free(curwire->cmd);
1031 free(curwire);
1032 if(av_wires == curwire) av_wires = NULL;
1033 return EINVAL;
1036 return 0;
1039 int alinkshowwires(int fd, char *arg)
1041 struct wire *curwire;
1042 if(!av_wires){
1043 printoutc(fd, "no wire defined");
1044 return 0;
1046 curwire = av_wires;
1047 while (curwire){
1048 printoutc(fd, "TYPE = %s\nCMD = %s\n", curwire->type,
1049 curwire->cmd);
1050 curwire = curwire->next;
1052 return 0;
1055 int alinkshutdown(int fd, char *arg)
1057 printlog(LOG_WARNING,"Shutdown from mgmt command");
1058 exit(0);
1061 int alinkhelp(int fd, char *arg)
1064 printoutc(fd, "help: print a summary of mgmt commands");
1065 printoutc(fd, "shutdown: terminate");
1066 printoutc(fd, "runscript: load a config file [args: PATH]");
1067 printoutc(fd, "showwires: list inserted wires");
1068 printoutc(fd, "addwire: add a type of wire, with variables [args: TYPE CMD]");
1069 printoutc(fd, "delwire: delete a type of wire [args: TYPE]");
1070 printoutc(fd, "showlinks: list inserted autolinks");
1071 printoutc(fd, "runninglinks: print running links");
1072 printoutc(fd, "addlink: add an autolink [args: NAME REMOTEHOSTS]");
1073 printoutc(fd, "dellink: delete an autolink [args: NAME]");
1074 printoutc(fd, "addtypelink: add a type of wire to named link [args: NAME TYPE]");
1075 printoutc(fd, "deltypelink: delete a type of wire from named link [args: NAME TYPE]");
1076 printoutc(fd, "linkonoff: activate/deactivate autolink [args: NAME 1/0]");
1077 printoutc(fd, "jobsqueue: print status of job queue");
1079 return 0;
1082 struct comlist {
1083 char *tag;
1084 int (*fun)(int fd,char *arg);
1085 } cl[]={
1086 {"help",alinkhelp},
1087 {"shutdown", alinkshutdown},
1088 {"showwires", alinkshowwires},
1089 {"addwire", alinkaddwire},
1090 {"delwire", alinkdelwire},
1092 {"showlinks", alinkshowlinks},
1093 {"runninglinks", alinkrunninglinks},
1094 {"addlink", alinkaddlink},
1095 {"dellink", alinkdellink},
1097 {"addtypelink", alinkaddtypelink},
1098 {"deltypelink", alinkdeltypelink},
1099 {"linkonoff", alinklinkonoff},
1100 {"runscript", runscript},
1102 {"jobsqueue", jobsqueue},
1105 #define NCL sizeof(cl)/sizeof(struct comlist)
1107 static int handle_cmd(int fd,char *inbuf)
1109 int rv=ENOSYS;
1110 int i;
1111 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
1112 if (*inbuf != '\0' && *inbuf != '#') {
1113 for (i=0; i<NCL &&
1114 strncmp(cl[i].tag,inbuf,strlen(cl[i].tag))!=0; i++)
1116 if (i<NCL) {
1117 inbuf += strlen(cl[i].tag);
1118 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
1119 printoutc(fd,"0000 DATA END WITH '.'");
1120 rv=cl[i].fun(fd,inbuf);
1121 printoutc(fd,".");
1123 if (rv == 0) {
1124 printoutc(fd,"1000 Success");
1125 } else {
1126 printoutc(fd,"1%03d %s",rv,strerror(rv));
1128 return rv;
1130 return rv;
1133 static int mgmtcommand(int fd)
1135 char buf[MAXCMD+1];
1136 int n,rv;
1137 n = read(fd, buf, MAXCMD);
1138 if (n<0) {
1139 printlog(LOG_ERR,"read from mgmt %s", strerror(errno));
1140 return 0;
1142 else if (n==0)
1143 return -1;
1144 else {
1145 buf[n]=0;
1146 if (n>0 && buf[n-1] == '\n')
1147 buf[n-1] = 0;
1148 rv=handle_cmd(fd,buf);
1149 if (rv>=0)
1150 write(fd,prompt,strlen(prompt));
1151 return rv;
1155 static int newmgmtconn(int fd,struct pollfd *pfd,int nfds)
1157 int new;
1158 unsigned int len;
1159 char buf[MAXCMD];
1160 struct sockaddr addr;
1161 new = accept(fd, &addr, &len);
1162 if(new < 0){
1163 printlog(LOG_ERR,"mgmt accept %s",strerror(errno));
1164 return nfds;
1166 if (nfds < MAXCONS) {
1168 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
1169 printlog(LOG_WARNING, "mgmt fcntl - setting "
1170 "O_NONBLOCK %s",strerror(errno));
1171 close(new);
1172 return nfds;
1175 pfd[nfds].fd=new;
1176 pfd[nfds].events=POLLIN | POLLHUP;
1177 pfd[nfds].revents=0;
1179 snprintf(buf,MAXCMD,header,PACKAGE_VERSION);
1180 write(new,buf,strlen(buf));
1181 write(new,prompt,strlen(prompt));
1182 return ++nfds;
1183 } else {
1184 printlog(LOG_ERR,"too many mgmt connections");
1185 close (new);
1186 return nfds;
1190 static int delmgmtconn(int i,struct pollfd *pfd,int nfds)
1192 if (i<nfds) {
1193 close(pfd[i].fd);
1194 memmove(pfd+i,pfd+i+1,sizeof (struct pollfd) * (nfds-i-1));
1195 nfds--;
1197 return nfds;
1200 static int openmgmt(char *mgmt)
1202 int mgmtconnfd;
1203 struct sockaddr_un sun;
1204 int one = 1;
1206 if((mgmtconnfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
1207 fprintf(stderr,"%s: mgmt socket: %s",progname,strerror(errno));
1208 exit(1);
1210 if(setsockopt(mgmtconnfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
1211 sizeof(one)) < 0){
1212 fprintf(stderr,"%s: mgmt setsockopt: %s",progname,
1213 strerror(errno));
1214 exit(1);
1216 if(fcntl(mgmtconnfd, F_SETFL, O_NONBLOCK) < 0){
1217 fprintf(stderr,"%s: Setting O_NONBLOCK on mgmt fd: %s",
1218 progname,strerror(errno));
1219 exit(1);
1221 sun.sun_family = PF_UNIX;
1222 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s",mgmt);
1223 if(bind(mgmtconnfd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
1224 fprintf(stderr,"%s: mgmt bind %s",progname,strerror(errno));
1225 exit(1);
1227 chmod(sun.sun_path,mgmtmode);
1228 if(listen(mgmtconnfd, 15) < 0){
1229 fprintf(stderr,"%s: mgmt listen: %s",progname,strerror(errno));
1230 exit(1);
1232 return mgmtconnfd;
1235 static int runscript(int fd,char *path)
1237 FILE *f=fopen(path,"r");
1238 char buf[MAXCMD];
1239 if (f==NULL)
1240 return ENOENT;
1241 else {
1242 while (fgets(buf,MAXCMD,f) != NULL) {
1243 if (strlen(buf) > 1 && buf[strlen(buf)-1]=='\n')
1244 buf[strlen(buf)-1]= '\0';
1245 if (fd >= 0) printoutc(fd,"vde_autolink[%s]: %s",
1246 path,buf);
1247 handle_cmd(fd, buf);
1249 return 0;
1253 static void loadrcfile(void)
1255 if (rcfile != NULL)
1256 runscript(-1,rcfile);
1257 else {
1258 char path[PATH_MAX];
1259 snprintf(path,PATH_MAX,"%s/.vde2/vde_autolink.rc",getenv("HOME"));
1260 if (access(path,R_OK) == 0)
1261 runscript(-1,path);
1262 else {
1263 if (access(STDRCFILE,R_OK) == 0)
1264 runscript(-1,STDRCFILE);
1269 static void save_pidfile()
1271 if(pidfile[0] != '/')
1272 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
1273 else
1274 strcpy(pidfile_path, pidfile);
1276 int fd = open(pidfile_path,
1277 O_WRONLY | O_CREAT | O_EXCL,
1278 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1279 FILE *f;
1281 if(fd == -1) {
1282 printlog(LOG_ERR, "Error in pidfile creation: %s",
1283 strerror(errno));
1284 exit(1);
1287 if((f = fdopen(fd, "w")) == NULL) {
1288 printlog(LOG_ERR, "Error in FILE* construction: %s",
1289 strerror(errno));
1290 exit(1);
1293 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
1294 printlog(LOG_ERR, "Error in writing pidfile");
1295 exit(1);
1298 fclose(f);
1301 static void usage(void)
1303 printf(
1304 " -h, --help Display this help\n"
1305 " -f, --rcfile Configuration file (overrides %s and ~/.vde_autolinkrc)\n"
1306 " -d, --daemon Daemonize vde_autolink once run\n"
1307 " -p, --pidfile PIDFILE Write pid of daemon to PIDFILE\n"
1308 " -M, --mgmt SOCK Path of the management UNIX socket\n"
1309 " --mgmtmode MODE Management UNIX socket access mode (octal)\n"
1310 " -s, --sock [*] Attach to this vde_switch socket\n"
1311 " -S, --switchmgmt [*] Attach to this vde_switch management socket\n"
1312 " [*] == Required option!\n"
1313 ,STDRCFILE);
1316 int main(int argc,char **argv)
1319 int npfd=0, option_index;
1320 int mgmtfd, mgmtindex=-1, vdemgindex=-1, consoleindex=-1;
1321 struct job *j; time_t now;
1323 static struct option long_options[] = {
1324 {"help", 0, 0, 'h'},
1325 {"rcfile", 1, 0, 'f'},
1326 {"daemon", 0, 0, 'd'},
1327 {"pidfile", 1, 0, 'p'},
1328 {"mgmt", 1, 0, 'M'},
1329 {"mgmtmode", 1, 0, MGMTMODEARG},
1330 {"sock", 1, 0, 's'},
1331 {"switchmgmt", 1, 0, 'S'},
1333 progname=basename(argv[0]);
1335 setsighandlers();
1336 atexit(cleanup);
1338 while(1) {
1339 int c;
1340 c = GETOPT_LONG (argc, argv, "hf:dp:M:s:S:",
1341 long_options, &option_index);
1342 if (c<0)
1343 break;
1344 switch (c) {
1345 case 'h':
1346 usage();
1347 break;
1348 case 'f':
1349 rcfile=strdup(optarg);
1350 break;
1351 case 'd':
1352 daemonize=1;
1353 break;
1354 case 'p':
1355 pidfile=strdup(optarg);
1356 break;
1357 case 'M':
1358 mgmt=strdup(optarg);
1359 break;
1360 case MGMTMODEARG:
1361 sscanf(optarg,"%o",&mgmtmode);
1362 break;
1363 case 's':
1364 vdeswitch=strdup(optarg);
1365 break;
1366 case 'S':
1367 switchmgmt=strdup(optarg);
1368 break;
1369 default:
1370 usage();
1371 break;
1375 if (optind < argc)
1376 usage();
1378 if( !vdeswitch || !switchmgmt )
1379 usage();
1381 if (daemonize){
1382 openlog(basename(progname), LOG_PID, 0);
1383 logok=1;
1384 syslog(LOG_INFO,"VDE_AUTOLINK started");
1387 if(isatty(0) && !daemonize){
1388 consoleindex=npfd;
1389 pfd[consoleindex].fd=0;
1390 pfd[consoleindex].events=POLLIN | POLLHUP;
1391 pfd[consoleindex].revents=0;
1392 npfd++;
1395 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
1396 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
1397 exit(1);
1399 strcat(pidfile_path, "/");
1400 if (daemonize && daemon(0, 1)) {
1401 printlog(LOG_ERR,"daemon: %s",strerror(errno));
1402 exit(1);
1404 if(pidfile) save_pidfile();
1406 if( (vdemgmt=vdemgmt_open(switchmgmt)) == NULL ){
1407 printlog(LOG_ERR, "cannot open %s\n", switchmgmt);
1408 return -1;
1410 vdemgindex=npfd;
1411 pfd[vdemgindex].fd=vdemgmt_getfd(vdemgmt);
1412 pfd[vdemgindex].events=POLLIN | POLLHUP;
1413 pfd[vdemgindex].revents=0;
1414 npfd++;
1416 if( vdemgmt_asyncreg(vdemgmt, FSTPDBG_PADD, ah_padd)
1417 || vdemgmt_asyncreg(vdemgmt, FSTPDBG_PDEL, ah_pdel)
1418 || vdemgmt_asyncreg(vdemgmt, FSTPDBG_STAT, ah_state) ){
1419 printlog(LOG_ERR, "cannot register async handler on switch");
1420 return -1;
1423 if(mgmt){
1424 mgmtfd=openmgmt(mgmt);
1425 mgmtindex=npfd;
1426 pfd[mgmtindex].fd=mgmtfd;
1427 pfd[mgmtindex].events=POLLIN | POLLHUP;
1428 pfd[mgmtindex].revents=0;
1429 npfd++;
1432 loadrcfile();
1434 while(1){
1436 poll(pfd,npfd,polltimeout);
1438 /* Handle async output from switch */
1439 if(pfd[vdemgindex].revents & POLLHUP){
1440 printlog(LOG_ERR, "switch closed connection, exiting");
1441 exit(1);
1443 if( pfd[vdemgindex].revents & POLLIN )
1444 vdemgmt_asyncrecv(vdemgmt);
1446 /* Handle console connections and commands */
1447 if(consoleindex >= 0 &&
1448 ( pfd[consoleindex].revents & POLLHUP ||
1449 (pfd[consoleindex].revents & POLLIN &&
1450 mgmtcommand(pfd[consoleindex].fd)<0) ) )
1451 exit(0);
1453 if (mgmt && (pfd[mgmtindex].revents != 0))
1454 npfd=newmgmtconn(pfd[mgmtindex].fd,pfd,npfd);
1456 if (mgmt && (npfd > mgmtindex+1)) {
1457 register int i;
1458 for (i=mgmtindex+1;i<npfd;i++) {
1459 if( (pfd[i].revents & POLLHUP) ||
1460 ((pfd[i].revents & POLLIN) &&
1461 (mgmtcommand(pfd[i].fd) < 0)) )
1462 npfd=delmgmtconn(i,pfd,npfd);
1466 /* Run scheduled jobs and compute new timeout for poll */
1467 time(&now);
1468 while ( jq && (now > jq->time) ){
1469 j=extract_job();
1470 if (alink_exists(j->al) && j->al->enabled)
1471 j->f(j->al);
1472 free(j);
1474 polltimeout = jq ? jq->time - now : -1 ;