AF_IPN is no longer protocol #34 (assigned to AF_ISDN).
[vde.git] / vde-2 / src / vde_autolink.c
blob4b7300f3f7b409c1db2bb7fea1be2046a5d12caf
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 kill(getpid(), sig);
192 struct autolink *find_alink_pid(int pid);
194 static void catch_zombies(int signo)
196 int status;
197 struct autolink *a;
199 if( (a=find_alink_pid(wait(&status))) )
200 a->wirepid = -1;
203 static void setsighandlers()
205 /* setting signal handlers.
206 * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
207 * ignores all the others signals which could cause termination. */
208 struct { int sig; const char *name; int ignore; } signals[] = {
209 { SIGHUP, "SIGHUP", 0 },
210 { SIGINT, "SIGINT", 0 },
211 { SIGPIPE, "SIGPIPE", 1 },
212 { SIGALRM, "SIGALRM", 1 },
213 { SIGTERM, "SIGTERM", 0 },
214 { SIGUSR1, "SIGUSR1", 1 },
215 { SIGUSR2, "SIGUSR2", 1 },
216 { SIGPROF, "SIGPROF", 1 },
217 { SIGVTALRM, "SIGVTALRM", 1 },
218 #ifdef VDE_LINUX
219 { SIGPOLL, "SIGPOLL", 1 },
220 #ifdef SIGSTKFLT
221 { SIGSTKFLT, "SIGSTKFLT", 1 },
222 #endif
223 { SIGIO, "SIGIO", 1 },
224 { SIGPWR, "SIGPWR", 1 },
225 #ifdef SIGUNUSED
226 { SIGUNUSED, "SIGUNUSED", 1 },
227 #endif
228 #endif
229 #ifdef VDE_DARWIN
230 { SIGXCPU, "SIGXCPU", 1 },
231 { SIGXFSZ, "SIGXFSZ", 1 },
232 #endif
233 { 0, NULL, 0 }
236 int i;
237 for(i = 0; signals[i].sig != 0; i++)
238 if(signal(signals[i].sig, signals[i].ignore ? SIG_IGN :
239 sig_handler) < 0)
240 printlog(LOG_ERR,"Setting handler for %s: %s",
241 signals[i].name, strerror(errno));
243 signal(SIGCHLD,catch_zombies);
246 /* Autolink Utils */
247 struct wire *find_wire(char *type)
249 struct wire *curwire = av_wires;
250 while(curwire){
251 if(!strcmp(curwire->type, type))
252 return curwire;
253 curwire = curwire->next;
255 return NULL;
258 struct alwire *find_alwire(char *type, struct autolink *alink)
260 struct alwire *curalwire = alink->wires[0];
261 /* each wires[i] has same types */
262 while(curalwire){
263 if(!strcmp(curalwire->type, type))
264 return curalwire;
265 curalwire = curalwire->next;
267 return NULL;
270 struct autolink *find_alink_port(int port)
272 struct autolink *curlink = alinks;
273 while(curlink){
274 if( curlink->portno == port )
275 return curlink;
276 curlink = curlink->next;
278 return NULL;
281 struct autolink *find_alink_pid(int pid)
283 struct autolink *curlink = alinks;
284 while(curlink){
285 if(curlink->wirepid == pid )
286 return curlink;
287 curlink = curlink->next;
289 return NULL;
292 struct autolink *find_alink(char *name)
294 struct autolink *curlink = alinks;
295 while(curlink){
296 if(!strcmp(curlink->name, name))
297 return curlink;
298 curlink = curlink->next;
300 return NULL;
303 struct autolink *alink_exists(struct autolink *al)
305 struct autolink *c = alinks;
306 while(c){
307 if (c == al)
308 return c;
309 c = c->next;
311 return NULL;
314 int port_reserve(void)
317 int p; char cmd[strlen("port/create")+5];
319 for(p=1; p <= MAXPORTS ; p++){
320 sprintf(cmd, "port/create %d", p);
321 if(!vdemgmt_sendcmd(vdemgmt, cmd, NULL))
322 return p;
324 return -1;
327 void port_dispose(int p)
330 char cmd[strlen("port/remove")+5];
331 sprintf(cmd, "port/remove %d", p);
332 vdemgmt_sendcmd(vdemgmt, cmd, NULL);
336 char *strrplc(char **s, char *old, char *new)
338 /* create new string (free old) replacing old with new */
340 char *limit, *new_s, *old_s;
341 int slen, oldlen, newlen, headlen, diff, taillen = 0;
343 old_s = *s;
345 slen=strlen(old_s); oldlen=strlen(old); newlen=strlen(new);
347 limit = strstr(old_s, old);
348 if ( limit == NULL )
349 return NULL;
351 headlen = (int)limit - (int)old_s;
352 diff = newlen - oldlen;
353 taillen = slen - ( headlen + oldlen );
355 if( (new_s=(char *)malloc(slen+diff+1)) == NULL)
356 return NULL;
358 snprintf(new_s, headlen+1, "%s", old_s);
359 snprintf(new_s+headlen, newlen+1, "%s", new);
360 snprintf(new_s+headlen+newlen, taillen+1, "%s", old_s+headlen+oldlen);
362 *s = new_s;
363 return new_s;
366 void alink_connect(struct autolink *link)
369 char *token, *dupcmd, **myargv = NULL;
370 int count=0, s[2], sdata=1;
372 if(!link->connwire){
373 printlog(LOG_ERR, "alink_connect null connwire");
374 exit(1);
377 printlog(LOG_NOTICE,"[%s] connecting wire: %s to %s", link->name,
378 link->connwire->type, link->hosts[link->connhost]);
380 for( dupcmd=strdup(link->connwire->cmd) ; ; dupcmd=NULL){
381 token = strtok(dupcmd, " ");
382 myargv=realloc(myargv, (count+1)*sizeof(char *));
383 if(!myargv) exit(1);
384 myargv[count]=token;
385 if( !token ) break;
386 count++;
389 if( socketpair(AF_UNIX, SOCK_STREAM, 0, s) ) exit(1);
391 if( (link->wirepid = fork()) == 0 ){
392 /* parent goes first, otherwise pid may be lost */
393 read(s[1],&sdata,sizeof(int));
394 close(s[0]); close(s[1]);
395 execvp(myargv[0], myargv);
396 /* TODO: handle return from execvp */
397 } else {
398 write(s[0],&sdata,sizeof(int));
399 close(s[0]); close(s[1]);
404 void insert_job(void (*f)(struct autolink *al), struct autolink *al, int gap)
406 struct job *j=jq, *pj=jq, *nj; time_t now;
408 /* remove other jobs for same alink, if any */
409 while(j){
410 if (al == j->al) {
411 if (jq == j) jq=j->n;
412 else pj->n=j->n;
413 free(j);
415 pj = j;
416 j = j->n;
419 /* insert job, ordered by time */
420 if ((nj=(struct job *)malloc(sizeof(struct job))) == NULL){
421 printlog(LOG_ERR, "%s, cannot alloc new job", __FUNCTION__);
422 exit(-1);
424 time(&now);
425 nj->f=f; nj->time=now+gap; nj->al=al; nj->n=NULL;
426 if(jq == NULL){
427 jq = nj;
428 return;
430 j = pj = jq;
431 while(j){
432 if (j->time > nj->time){
433 if (jq == j){
434 jq = nj;
435 jq->n = j;
437 else {
438 pj->n = nj;
439 nj->n = j;
441 return;
443 pj = j;
444 j = j->n;
450 struct job *extract_job()
452 struct job *j = jq;
454 jq=jq->n;
455 return j;
458 /* Async functions and handlers */
459 void alink_try(struct autolink *al);
461 void alink_check(struct autolink *al)
463 if (al->state != ST_ACTIVE){
464 printlog(LOG_NOTICE, "[%s] check failed, scheduled new wire connection", al->name);
465 kill(al->wirepid, SIGQUIT);
466 insert_job(alink_try, al, SCHED_TRY);
468 else
469 printlog(LOG_NOTICE, "[%s] check passed", al->name);
472 void alink_try(struct autolink *al)
475 time_t now;
477 time(&now);
479 /* change wire if died too fast,
480 * try hosts in round robin */
481 if(al->connwire->try > (now - CHANGEWIRETIME)){
482 if(!al->connwire->next){
483 al->connhost++;
484 if( al->hosts[al->connhost] == NULL )
485 al->connhost = 0;
486 al->connwire = al->wires[al->connhost];
487 } else {
488 al->connwire = al->connwire->next;
490 printlog(LOG_NOTICE, "[%s] try next wire: %s (%s)", al->name,
491 al->connwire->type, al->hosts[al->connhost]);
492 /* suspend autolink if cycled too fast */
493 if(al->connwire->oldtry > (now - SLEEPWIRETIME)){
494 printlog(LOG_NOTICE, "[%s], go suspend", al->name);
495 insert_job(alink_try, al, SCHED_LONGTRY);
496 return;
500 al->connwire->oldtry = al->connwire->try;
501 al->connwire->try = now;
503 alink_connect(al);
505 insert_job(alink_check, al, SCHED_CHECK);
508 void ah_padd(const char *event, int tag, const char *data)
511 int port; char *s;
512 struct autolink *al;
514 for( s = (char *)data ; *s != ' ' ; s++);
515 s++;
516 port=atoi(s);
518 al = find_alink_port(port);
519 if (!al || !al->enabled)
520 return;
521 printlog(LOG_NOTICE, "[%s] received %s for port %d", al->name, event, port);
523 if (al->state == ST_DISCARD){
524 al->state = ST_ACTIVE;
525 printlog(LOG_NOTICE, "[%s] state change, discard -> active", al->name);
529 void ah_pdel(const char *event, int tag, const char *data)
532 int port; char *s;
533 struct autolink *al;
535 for( s = (char *)data ; *s != ' ' ; s++);
536 s++;
537 port=atoi(s);
539 al = find_alink_port(port);
540 if (!al || !al->enabled)
541 return;
542 printlog(LOG_NOTICE, "[%s] received %s for port %d", al->name, event, port);
544 if (al->state == ST_ACTIVE){
545 al->state = ST_DISCARD;
546 printlog(LOG_NOTICE, "[%s] state change, active -> discard", al->name);
547 if(al->wirepid != -1)
548 kill(al->wirepid, SIGQUIT);
549 printlog(LOG_NOTICE, "[%s] scheduled new wire connection");
550 insert_job(alink_try, al, SCHED_TRY);
554 void ah_state(const char *event, int tag, const char *data)
556 int port; char *s;
557 struct autolink *al;
559 for( s = (char *)data ; *s != ' ' ; s++);
560 s++;
561 port=atoi(s);
563 al = find_alink_port(port);
564 if (!al || !al->enabled)
565 return;
566 printlog(LOG_NOTICE, "[%s] received %s for port %d", al->name, event, port);
568 if (strstr(data, "learning+forwarding") && (al->state == ST_DISCARD)){
569 al->state = ST_ACTIVE;
570 printlog(LOG_NOTICE, "[%s] state change, discard -> active", al->name);
571 return;
573 if (strstr(data, "discarding") && (al->state == ST_ACTIVE)){
574 al->state = ST_DISCARD;
575 printlog(LOG_NOTICE, "[%s] state change, active -> discard", al->name);
576 return;
580 /* MGMT functions */
581 int jobsqueue(int fd, char *arg)
583 time_t now; struct job *j;
585 if(!jq){
586 printoutc(fd, "jobs queue is empty");
587 return 0;
589 time(&now);
590 j = jq;
591 while (j){
592 printoutc(fd, "TIME: %d, ACTION: %s, LINK: %s", j->time - now,
593 (j->f == alink_try) ? "try " : "check", j->al->name);
594 j = j->n;
596 printoutc(fd, "");
597 return 0;
600 int alinklinkonoff(int fd, char *arg)
603 char *endname, *name;
604 int namelen, vallen, value;
605 struct autolink *curlink;
607 /* check if we have name and type */
608 endname = strstr(arg, " ");
609 namelen = (int)endname - (int)arg;
610 if( namelen <= 0 ) return EINVAL;
612 vallen = ((int)arg+strlen(arg)) - ((int)endname+1);
613 if( vallen <= 0 ) return EINVAL;
615 if( sscanf(endname+1, "%i", &value) != 1)
616 return EINVAL;
618 /* pick autolink and wire */
619 if( (name = (char *)malloc(namelen+1) ) == NULL ) exit(1);
620 snprintf(name, namelen+1, "%s", arg);
622 curlink = find_alink(name);
623 free(name);
624 if(!curlink) return ENXIO;
626 if(value){
627 if(!curlink->wires) return ENXIO;
628 if(curlink->enabled) return 0;
629 curlink->enabled = 1;
630 curlink->state = ST_DISCARD;
631 curlink->connwire=curlink->wires[0];
632 alink_try(curlink);
634 else {
635 if(!curlink->enabled) return 0;
636 curlink->enabled = 0;
637 kill(curlink->wirepid, SIGQUIT);
638 curlink->connwire = NULL;
640 return 0;
643 int alinkdeltypelink(int fd, char *arg)
646 char *endname, *name, *type;
647 int namelen, typelen, i;
648 struct autolink *curlink;
649 struct alwire *curalwire, *prevalwire;
651 /* check if we have name and type */
652 endname = strstr(arg, " ");
653 namelen = (int)endname - (int)arg;
654 if( namelen <= 0 ) return EINVAL;
656 typelen = strlen(arg) - namelen -1;
657 if( typelen <= 0 ) return EINVAL;
659 /* pick autolink */
660 if( (name = (char *)malloc(namelen+1) ) == NULL ) exit(1);
661 snprintf(name, namelen+1, "%s", arg);
663 curlink = find_alink(name);
664 free(name);
665 if(!curlink) return ENXIO;
666 if(curlink->enabled) return EINVAL; /* avoid RC */
668 if(!curlink->wires[0]) return EINVAL; /* no wires! */
670 /* delete alwire */
671 if( (type = (char *)malloc(typelen+1) ) == NULL ) exit(1);
672 snprintf(type, typelen+1, "%s", endname+1);
674 for( i = 0 ; curlink->hosts[i] != NULL ; i++){
675 curalwire = prevalwire = curlink->wires[i];
677 while(curalwire){
678 if(!strcmp(curalwire->type, type)){
679 if(curalwire == curlink->wires[i]){
680 curlink->wires[i] = curalwire->next;
682 else {
683 prevalwire->next = curalwire->next;
685 free(curalwire->type);
686 free(curalwire->cmd);
687 free(curalwire);
688 free(type);
689 return 0;
691 prevalwire = curalwire;
692 curalwire = curalwire->next;
696 free(type);
697 return EINVAL;
700 int alinkaddtypelink(int fd, char *arg)
703 char *endname, *name, *type, portbuf[42];
704 int namelen, typelen, i;
705 struct autolink *curlink;
706 struct wire *wire;
707 struct alwire *alwire;
709 /* check if we have name and type */
710 endname = strstr(arg, " ");
711 namelen = (int)endname - (int)arg;
712 if( namelen <= 0 ) return EINVAL;
714 typelen = strlen(arg) - namelen -1;
715 if( typelen <= 0 ) return EINVAL;
717 /* pick autolink and wire */
718 if( (name = (char *)malloc(namelen+1) ) == NULL ) exit(1);
719 snprintf(name, namelen+1, "%s", arg);
721 curlink = find_alink(name);
722 free(name);
723 if(!curlink) return ENXIO;
724 if(curlink->enabled) return EINVAL; /* avoid RC */
726 if( (type = (char *)malloc(typelen+1) ) == NULL ) exit(1);
727 snprintf(type, typelen+1, "%s", endname+1);
729 wire = find_wire(type);
730 free(type);
731 if(!wire) return ENXIO;
733 /* only one wire type for each autolink */
734 alwire = find_alwire(wire->type, curlink);
735 if(alwire) return EINVAL;
737 /* alloc alwires */
738 for( i = 0 ; curlink->hosts[i] != NULL ; i++ ){
739 if(!curlink->wires[i]){
740 if( (curlink->wires[i] = (struct alwire *)
741 malloc(sizeof(struct alwire))) == NULL )
742 exit(1);
743 alwire = curlink->wires[i];
745 else {
746 alwire = curlink->wires[i];
747 while(alwire->next)
748 alwire = alwire->next;
749 if( (alwire->next=(struct alwire *)
750 malloc(sizeof(struct alwire))) == NULL )
751 exit(1);
752 alwire = alwire->next;
755 /* set port, sock and remotehost in alwire command */
756 if( (alwire->cmd = (char *)malloc(strlen(wire->cmd)+1)) == NULL)
757 exit(1);
759 strcpy(alwire->cmd, wire->cmd);
760 sprintf(portbuf, "%u", curlink->portno);
761 strrplc(&(alwire->cmd), myport, portbuf);
762 strrplc(&(alwire->cmd), mysock, vdeswitch);
763 strrplc(&(alwire->cmd), myhost, curlink->hosts[i]);
765 /* fill rest of alwire struct */
766 if( (alwire->type = (char *)
767 malloc(strlen(wire->type)+1)) == NULL)
768 exit(1);
770 strcpy(alwire->type, wire->type);
771 alwire->try = 0;
772 alwire->oldtry = 0;
773 alwire->next = NULL;
776 return 0;
779 int alinkdellink(int fd, char *arg)
782 struct autolink *curlink, *prevlink;
783 struct alwire *curalwire, *prevalwire;
784 int i;
786 if(!alinks) return EINVAL;
788 prevlink = curlink = alinks;
789 while(curlink){
790 if(!strcmp(curlink->name, arg)){
791 if(curlink->enabled) return EINVAL; /* avoid RC */
792 if(curlink == alinks){
793 alinks = curlink->next;
795 else {
796 prevlink->next = curlink->next;
798 port_dispose(curlink->portno);
799 free(curlink->name);
800 /* remove hosts and alwires */
801 for ( i = 0 ; curlink->hosts[i] != NULL ; i++){
802 free(curlink->hosts[i]);
803 curalwire = curlink->wires[i];
804 while(curalwire){
805 prevalwire = curalwire;
806 curalwire = curalwire->next;
807 free(prevalwire);
810 free(curlink->hosts);
811 free(curlink->wires);
812 free(curlink);
813 return 0;
815 prevlink = curlink;
816 curlink = curlink->next;
818 return EINVAL;
821 int alinkaddlink(int fd, char *arg)
824 char *name, *endname = NULL, *tmphosts, *token;
825 int namelen, hostlen, i, j;
826 struct autolink *curlink;
828 /* check if we have name and remotehost */
829 endname = strstr(arg, " ");
830 namelen = (int)endname - (int)arg;
831 if( namelen <= 0 ) return EINVAL;
833 hostlen = strlen(arg) - namelen -1;
834 if( hostlen <= 0 ) return EINVAL;
836 /* alloc and set name */
837 if( (name = (char *)malloc(namelen+1) ) == NULL ) exit(1);
838 snprintf(name, namelen+1, "%s", arg);
840 /* check for duplicate */
841 if( find_alink(name) ){
842 free(name); return EINVAL;
845 /* alloc autolink */
846 if(alinks == NULL){
847 alinks = (struct autolink *)malloc(sizeof(struct autolink));
848 if(alinks == NULL) exit(1);
849 curlink = alinks;
850 } else {
851 curlink = alinks;
852 while(curlink->next)
853 curlink = curlink->next;
854 curlink->next = (struct autolink *)
855 malloc(sizeof(struct autolink));
856 if(curlink->next == NULL) exit(1);
857 curlink = curlink->next;
859 curlink->name = name;
861 /* reserve a port on switch */
862 if( (curlink->portno = port_reserve()) < 0 ){
863 free(curlink->name);
864 free(curlink);
865 if(alinks == curlink) alinks = NULL;
866 return ENXIO;
869 /* alloc and set remote host array (null terminated) */
870 i=0;
871 curlink->hosts=NULL;
872 for( tmphosts=strdup(endname+1) ; ; tmphosts=NULL){
873 token = strtok(tmphosts, " ");
874 curlink->hosts=realloc(curlink->hosts, (i+1)*sizeof(char *));
875 if(!curlink->hosts) exit(1);
876 curlink->hosts[i]=token;
877 if( !token ) break;
878 i++;
880 /* alloc wires array */
881 if( (curlink->wires = malloc(i*sizeof(char *))) == NULL ) exit(1);
882 for( j = 0 ; j < i ; j++)
883 curlink->wires[j] = NULL;
885 curlink->enabled = 0;
886 curlink->state = 0;
887 curlink->connhost = 0;
888 curlink->connwire = NULL;
889 curlink->next = NULL;
891 return 0;
894 int alinkrunninglinks(int fd, char *arg)
896 struct autolink *curlink;
897 time_t now;
899 if(!alinks) return 0;
900 time(&now);
901 curlink = alinks;
902 while (curlink){
903 if( curlink->enabled && (curlink->wirepid != -1) &&
904 ( curlink->state == ST_ACTIVE ) &&
905 (curlink->connwire->try <
906 now - CHANGEWIRETIME) ) {
907 /* show only stable connections */
908 printoutc(fd, "NAME = %s , RHOST = %s , WIRE = %s ,"
909 " PID: %d", curlink->name,
910 curlink->hosts[curlink->connhost],
911 curlink->connwire->type,
912 curlink->wirepid);
913 printoutc(fd, "");
915 curlink = curlink->next;
917 return 0;
920 int alinkshowlinks(int fd, char *arg)
922 struct autolink *curlink;
923 struct alwire *curalwire = NULL;
924 int i ;
926 if(!alinks){
927 printoutc(fd, "no autolink defined");
928 return 0;
930 curlink = alinks;
931 while (curlink){
932 printoutc(fd, "NAME = %s (PORT: %d%s)", curlink->name,
933 curlink->portno,
934 (curlink->enabled?" - ACTIVE":""));
935 for(i = 0 ; curlink->hosts[i] != NULL ; i++){
936 printoutc(fd, "RHOST: %s", curlink->hosts[i]);
937 if(curlink->wires[i]){
938 printoutc(fd, "WIRES:");
939 curalwire = curlink->wires[i];
941 while(curalwire){
942 printoutc(fd,"%s: %s\n", curalwire->type,
943 curalwire->cmd);
944 curalwire = curalwire->next;
947 printoutc(fd, "");
948 curlink = curlink->next;
950 return 0;
953 int alinkdelwire(int fd, char* arg)
955 struct wire *curwire, *prevwire;
957 if(!av_wires) return EINVAL;
959 prevwire = curwire = av_wires;
960 while(curwire){
961 if(!strcmp(curwire->type, arg)){
962 if(curwire == av_wires){
963 av_wires = curwire->next;
965 else {
966 prevwire->next = curwire->next;
968 free(curwire->type);
969 free(curwire->cmd);
970 free(curwire);
971 return 0;
973 prevwire = curwire;
974 curwire = curwire->next;
976 return EINVAL;
979 int alinkaddwire(int fd, char* arg)
982 struct wire *curwire = NULL;
983 char *type = NULL;
984 int typelen = 0;
985 int cmdlen = 0;
987 /* check if we have type and command */
988 char *endtype = strstr(arg, " ");
989 typelen = (int)endtype - (int)arg;
990 if( typelen <= 0 ) return EINVAL;
991 cmdlen = strlen(arg) - typelen -1;
992 if( cmdlen <= 0 ) return EINVAL;
994 /* alloc and set type */
995 if( (type = (char *)malloc(typelen+1) ) == NULL ) exit(1);
996 snprintf(type, typelen+1, "%s", arg);
998 /* check for duplicate */
999 if( find_wire(type) ){
1000 free(type); return EINVAL;
1002 /* alloc wire */
1003 if(av_wires == NULL){
1004 av_wires = (struct wire *)malloc(sizeof(struct wire));
1005 if(av_wires == NULL) exit(1);
1006 curwire = av_wires;
1007 } else {
1008 curwire = av_wires;
1009 while(curwire->next)
1010 curwire = curwire->next;
1011 curwire->next = (struct wire *)malloc(sizeof(struct wire));
1012 if(curwire->next == NULL) exit(1);
1013 curwire = curwire->next;
1016 curwire->next = NULL;
1017 curwire->type = type;
1019 /* alloc and set command */
1020 if( (curwire->cmd = (char *)malloc(cmdlen+1) ) == NULL )
1021 exit(1);
1022 snprintf(curwire->cmd, cmdlen+1, "%s", endtype+1);
1024 /* check variables */
1025 if( !strstr(curwire->cmd, myport) || !strstr(curwire->cmd, mysock) ||
1026 !strstr(curwire->cmd, myhost) ){
1027 free(curwire->type); free(curwire->cmd);
1028 free(curwire);
1029 if(av_wires == curwire) av_wires = NULL;
1030 return EINVAL;
1033 return 0;
1036 int alinkshowwires(int fd, char *arg)
1038 struct wire *curwire;
1039 if(!av_wires){
1040 printoutc(fd, "no wire defined");
1041 return 0;
1043 curwire = av_wires;
1044 while (curwire){
1045 printoutc(fd, "TYPE = %s\nCMD = %s\n", curwire->type,
1046 curwire->cmd);
1047 curwire = curwire->next;
1049 return 0;
1052 int alinkshutdown(int fd, char *arg)
1054 printlog(LOG_WARNING,"Shutdown from mgmt command");
1055 exit(0);
1058 int alinkhelp(int fd, char *arg)
1061 printoutc(fd, "help: print a summary of mgmt commands");
1062 printoutc(fd, "shutdown: terminate");
1063 printoutc(fd, "runscript: load a config file [args: PATH]");
1064 printoutc(fd, "showwires: list inserted wires");
1065 printoutc(fd, "addwire: add a type of wire, with variables [args: TYPE CMD]");
1066 printoutc(fd, "delwire: delete a type of wire [args: TYPE]");
1067 printoutc(fd, "showlinks: list inserted autolinks");
1068 printoutc(fd, "runninglinks: print running links");
1069 printoutc(fd, "addlink: add an autolink [args: NAME REMOTEHOSTS]");
1070 printoutc(fd, "dellink: delete an autolink [args: NAME]");
1071 printoutc(fd, "addtypelink: add a type of wire to named link [args: NAME TYPE]");
1072 printoutc(fd, "deltypelink: delete a type of wire from named link [args: NAME TYPE]");
1073 printoutc(fd, "linkonoff: activate/deactivate autolink [args: NAME 1/0]");
1074 printoutc(fd, "jobsqueue: print status of job queue");
1076 return 0;
1079 struct comlist {
1080 char *tag;
1081 int (*fun)(int fd,char *arg);
1082 } cl[]={
1083 {"help",alinkhelp},
1084 {"shutdown", alinkshutdown},
1085 {"showwires", alinkshowwires},
1086 {"addwire", alinkaddwire},
1087 {"delwire", alinkdelwire},
1089 {"showlinks", alinkshowlinks},
1090 {"runninglinks", alinkrunninglinks},
1091 {"addlink", alinkaddlink},
1092 {"dellink", alinkdellink},
1094 {"addtypelink", alinkaddtypelink},
1095 {"deltypelink", alinkdeltypelink},
1096 {"linkonoff", alinklinkonoff},
1097 {"runscript", runscript},
1099 {"jobsqueue", jobsqueue},
1102 #define NCL sizeof(cl)/sizeof(struct comlist)
1104 static int handle_cmd(int fd,char *inbuf)
1106 int rv=ENOSYS;
1107 int i;
1108 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
1109 if (*inbuf != '\0' && *inbuf != '#') {
1110 for (i=0; i<NCL &&
1111 strncmp(cl[i].tag,inbuf,strlen(cl[i].tag))!=0; i++)
1113 if (i<NCL) {
1114 inbuf += strlen(cl[i].tag);
1115 while (*inbuf == ' ' || *inbuf == '\t') inbuf++;
1116 printoutc(fd,"0000 DATA END WITH '.'");
1117 rv=cl[i].fun(fd,inbuf);
1118 printoutc(fd,".");
1120 printoutc(fd,"1%03d %s",rv,strerror(rv));
1121 return rv;
1123 return rv;
1126 static int mgmtcommand(int fd)
1128 char buf[MAXCMD+1];
1129 int n,rv;
1130 n = read(fd, buf, MAXCMD);
1131 if (n<0) {
1132 printlog(LOG_ERR,"read from mgmt %s", strerror(errno));
1133 return 0;
1135 else if (n==0)
1136 return -1;
1137 else {
1138 buf[n]=0;
1139 if (n>0 && buf[n-1] == '\n')
1140 buf[n-1] = 0;
1141 rv=handle_cmd(fd,buf);
1142 if (rv>=0)
1143 write(fd,prompt,strlen(prompt));
1144 return rv;
1148 static int newmgmtconn(int fd,struct pollfd *pfd,int nfds)
1150 int new;
1151 unsigned int len;
1152 char buf[MAXCMD];
1153 struct sockaddr addr;
1154 new = accept(fd, &addr, &len);
1155 if(new < 0){
1156 printlog(LOG_ERR,"mgmt accept %s",strerror(errno));
1157 return nfds;
1159 if (nfds < MAXCONS) {
1161 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
1162 printlog(LOG_WARNING, "mgmt fcntl - setting "
1163 "O_NONBLOCK %s",strerror(errno));
1164 close(new);
1165 return nfds;
1168 pfd[nfds].fd=new;
1169 pfd[nfds].events=POLLIN | POLLHUP;
1170 pfd[nfds].revents=0;
1172 snprintf(buf,MAXCMD,header,PACKAGE_VERSION);
1173 write(new,buf,strlen(buf));
1174 write(new,prompt,strlen(prompt));
1175 return ++nfds;
1176 } else {
1177 printlog(LOG_ERR,"too many mgmt connections");
1178 close (new);
1179 return nfds;
1183 static int delmgmtconn(int i,struct pollfd *pfd,int nfds)
1185 if (i<nfds) {
1186 close(pfd[i].fd);
1187 memmove(pfd+i,pfd+i+1,sizeof (struct pollfd) * (nfds-i-1));
1188 nfds--;
1190 return nfds;
1193 static int openmgmt(char *mgmt)
1195 int mgmtconnfd;
1196 struct sockaddr_un sun;
1197 int one = 1;
1199 if((mgmtconnfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
1200 fprintf(stderr,"%s: mgmt socket: %s",progname,strerror(errno));
1201 exit(1);
1203 if(setsockopt(mgmtconnfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
1204 sizeof(one)) < 0){
1205 fprintf(stderr,"%s: mgmt setsockopt: %s",progname,
1206 strerror(errno));
1207 exit(1);
1209 if(fcntl(mgmtconnfd, F_SETFL, O_NONBLOCK) < 0){
1210 fprintf(stderr,"%s: Setting O_NONBLOCK on mgmt fd: %s",
1211 progname,strerror(errno));
1212 exit(1);
1214 sun.sun_family = PF_UNIX;
1215 snprintf(sun.sun_path,sizeof(sun.sun_path),"%s",mgmt);
1216 if(bind(mgmtconnfd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
1217 fprintf(stderr,"%s: mgmt bind %s",progname,strerror(errno));
1218 exit(1);
1220 chmod(sun.sun_path,mgmtmode);
1221 if(listen(mgmtconnfd, 15) < 0){
1222 fprintf(stderr,"%s: mgmt listen: %s",progname,strerror(errno));
1223 exit(1);
1225 return mgmtconnfd;
1228 static int runscript(int fd,char *path)
1230 FILE *f=fopen(path,"r");
1231 char buf[MAXCMD];
1232 if (f==NULL)
1233 return ENOENT;
1234 else {
1235 while (fgets(buf,MAXCMD,f) != NULL) {
1236 if (strlen(buf) > 1 && buf[strlen(buf)-1]=='\n')
1237 buf[strlen(buf)-1]= '\0';
1238 if (fd >= 0) printoutc(fd,"vde_autolink[%s]: %s",
1239 path,buf);
1240 handle_cmd(fd, buf);
1242 return 0;
1246 static void loadrcfile(void)
1248 if (rcfile != NULL)
1249 runscript(-1,rcfile);
1250 else {
1251 char path[PATH_MAX];
1252 snprintf(path,PATH_MAX,"%s/.vde2/vde_autolink.rc",getenv("HOME"));
1253 if (access(path,R_OK) == 0)
1254 runscript(-1,path);
1255 else {
1256 if (access(STDRCFILE,R_OK) == 0)
1257 runscript(-1,STDRCFILE);
1262 static void save_pidfile()
1264 if(pidfile[0] != '/')
1265 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
1266 else
1267 strcpy(pidfile_path, pidfile);
1269 int fd = open(pidfile_path,
1270 O_WRONLY | O_CREAT | O_EXCL,
1271 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1272 FILE *f;
1274 if(fd == -1) {
1275 printlog(LOG_ERR, "Error in pidfile creation: %s",
1276 strerror(errno));
1277 exit(1);
1280 if((f = fdopen(fd, "w")) == NULL) {
1281 printlog(LOG_ERR, "Error in FILE* construction: %s",
1282 strerror(errno));
1283 exit(1);
1286 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
1287 printlog(LOG_ERR, "Error in writing pidfile");
1288 exit(1);
1291 fclose(f);
1294 static void usage(void)
1296 printf(
1297 " -h, --help Display this help\n"
1298 " -f, --rcfile Configuration file (overrides %s and ~/.vde_autolinkrc)\n"
1299 " -d, --daemon Daemonize vde_autolink once run\n"
1300 " -p, --pidfile PIDFILE Write pid of daemon to PIDFILE\n"
1301 " -M, --mgmt SOCK Path of the management UNIX socket\n"
1302 " --mgmtmode MODE Management UNIX socket access mode (octal)\n"
1303 " -s, --sock [*] Attach to this vde_switch socket\n"
1304 " -S, --switchmgmt [*] Attach to this vde_switch management socket\n"
1305 " [*] == Required option!\n"
1306 ,STDRCFILE);
1309 int main(int argc,char **argv)
1312 int n, npfd=0, option_index;
1313 int mgmtfd, mgmtindex=-1, vdemgindex=-1, consoleindex=-1;
1314 struct job *j; time_t now;
1316 static struct option long_options[] = {
1317 {"help", 0, 0, 'h'},
1318 {"rcfile", 1, 0, 'f'},
1319 {"daemon", 0, 0, 'd'},
1320 {"pidfile", 1, 0, 'p'},
1321 {"mgmt", 1, 0, 'M'},
1322 {"mgmtmode", 1, 0, MGMTMODEARG},
1323 {"sock", 1, 0, 's'},
1324 {"switchmgmt", 1, 0, 'S'},
1326 progname=basename(argv[0]);
1328 setsighandlers();
1329 atexit(cleanup);
1331 while(1) {
1332 int c;
1333 c = GETOPT_LONG (argc, argv, "hf:dp:M:s:S:",
1334 long_options, &option_index);
1335 if (c<0)
1336 break;
1337 switch (c) {
1338 case 'h':
1339 usage();
1340 break;
1341 case 'f':
1342 rcfile=strdup(optarg);
1343 break;
1344 case 'd':
1345 daemonize=1;
1346 break;
1347 case 'p':
1348 pidfile=strdup(optarg);
1349 break;
1350 case 'M':
1351 mgmt=strdup(optarg);
1352 break;
1353 case MGMTMODEARG:
1354 sscanf(optarg,"%o",&mgmtmode);
1355 break;
1356 case 's':
1357 vdeswitch=strdup(optarg);
1358 break;
1359 case 'S':
1360 switchmgmt=strdup(optarg);
1361 break;
1362 default:
1363 usage();
1364 break;
1368 if (optind < argc)
1369 usage();
1371 if( !vdeswitch || !switchmgmt )
1372 usage();
1374 if (daemonize){
1375 openlog(basename(progname), LOG_PID, 0);
1376 logok=1;
1377 syslog(LOG_INFO,"VDE_AUTOLINK started");
1380 if(isatty(0) && !daemonize){
1381 consoleindex=npfd;
1382 pfd[consoleindex].fd=0;
1383 pfd[consoleindex].events=POLLIN | POLLHUP;
1384 pfd[consoleindex].revents=0;
1385 npfd++;
1388 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
1389 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
1390 exit(1);
1392 strcat(pidfile_path, "/");
1393 if (daemonize && daemon(0, 1)) {
1394 printlog(LOG_ERR,"daemon: %s",strerror(errno));
1395 exit(1);
1397 if(pidfile) save_pidfile();
1399 if( (vdemgmt=vdemgmt_open(switchmgmt)) == NULL ){
1400 printlog(LOG_ERR, "cannot open %s\n", switchmgmt);
1401 return -1;
1403 vdemgindex=npfd;
1404 pfd[vdemgindex].fd=vdemgmt_getfd(vdemgmt);
1405 pfd[vdemgindex].events=POLLIN | POLLHUP;
1406 pfd[vdemgindex].revents=0;
1407 npfd++;
1409 if( vdemgmt_asyncreg(vdemgmt, FSTPDBG_PADD, ah_padd)
1410 || vdemgmt_asyncreg(vdemgmt, FSTPDBG_PDEL, ah_pdel)
1411 || vdemgmt_asyncreg(vdemgmt, FSTPDBG_STAT, ah_state) ){
1412 printlog(LOG_ERR, "cannot register async handler on switch");
1413 return -1;
1416 if(mgmt){
1417 mgmtfd=openmgmt(mgmt);
1418 mgmtindex=npfd;
1419 pfd[mgmtindex].fd=mgmtfd;
1420 pfd[mgmtindex].events=POLLIN | POLLHUP;
1421 pfd[mgmtindex].revents=0;
1422 npfd++;
1425 loadrcfile();
1427 while(1){
1429 n=poll(pfd,npfd,polltimeout);
1431 /* Handle async output from switch */
1432 if(pfd[vdemgindex].revents & POLLHUP){
1433 printlog(LOG_ERR, "switch closed connection, exiting");
1434 exit(1);
1436 if( pfd[vdemgindex].revents & POLLIN )
1437 vdemgmt_asyncrecv(vdemgmt);
1439 /* Handle console connections and commands */
1440 if(consoleindex >= 0 &&
1441 ( pfd[consoleindex].revents & POLLHUP ||
1442 (pfd[consoleindex].revents & POLLIN &&
1443 mgmtcommand(pfd[consoleindex].fd)<0) ) )
1444 exit(0);
1446 if (mgmt && (pfd[mgmtindex].revents != 0))
1447 npfd=newmgmtconn(pfd[mgmtindex].fd,pfd,npfd);
1449 if (mgmt && (npfd > mgmtindex+1)) {
1450 register int i;
1451 for (i=mgmtindex+1;i<npfd;i++) {
1452 if( (pfd[i].revents & POLLHUP) ||
1453 ((pfd[i].revents & POLLIN) &&
1454 (mgmtcommand(pfd[i].fd) < 0)) )
1455 npfd=delmgmtconn(i,pfd,npfd);
1459 /* Run scheduled jobs and compute new timeout for poll */
1460 time(&now);
1461 while ( jq && (now > jq->time) ){
1462 j=extract_job();
1463 if (alink_exists(j->al) && j->al->enabled)
1464 j->f(j->al);
1465 free(j);
1467 polltimeout = jq ? jq->time - now : -1 ;