diff --git a/hostconfig/hostconfig b/hostconfig/hostconfig new file mode 100755 index 0000000..752fb09 --- /dev/null +++ b/hostconfig/hostconfig @@ -0,0 +1,141 @@ +#! /usr/bin/perl +use strict; +use warnings; +use Getopt::Long; + +my %TAGS; # ( host => { tag=>1, ... }, ... ) +my %options; + +$0 =~ /get-hostconfig/ and warn "$0: deprecated. Please use hostconfig\n"; + +sub read_hostconfig { + open my $in,'<','/etc/hostconfig' or die "/etc/hostconfig: $!\n"; + while (<$in>) { + s/#.*//; + chomp; + my ($host,$attr,$value)=split ' ',$_,3; + $host or next; + $attr or next; + $attr ne 'tag' and $TAGS{$host}->{$attr}=2; + $attr eq 'tag' or next; + $TAGS{$host}||={}; + my @tag=split ' ',$value; + for my $tag (@tag) { + my ($neg,$tag)=$tag=~/^(!?)(.+)/; + $neg and next; + $TAGS{$host}->{$tag}=1; + } + } +} + + +sub evalue_expression { + + my ($hostname,$expression)=@_; + + exists $TAGS{$hostname} or warn "$0: hostname $hostname : no info\n"; + my $tags=$TAGS{$hostname}; + + my @l = grep /\S/,split /([&\|\(\)!]|\s+)/,$expression; + + $options{'debug'} and warn join(' ',map("'$_'",@l)),"\n"; + for (@l) { + s/\s*(.+?)\s*/$1/; + if (/^[a-z][a-z0-9$_-]*$/i) { + $_=exists $tags->{$_} ? 1 : 0; + } + } + $options{'debug'} and warn join(' ',map("'$_'",@l)),"\n"; + + my $result=eval join(' ',@l); + $@ and die "$0: syntax error in expression: $expression\n"; + $options{'debug'} and warn "result: ".($result ? 'true' : 'false'),"\n"; + + return $result; +} + +sub populate_node { + my ($hostname)=@_; + my $DIR='/node/tags'; + -d $DIR and system "rm -rf $DIR" and exit 1; + mkdir $DIR or die "$DIR: $!\n"; + my $tags=$TAGS{$hostname}; + for (keys %$tags) { + open my $touch,'>',"$DIR/$_"; + } +} + +sub list { + my ($expression)=@_; + my @ret; + for my $hostname (sort keys %TAGS) { + if (evalue_expression($hostname,$expression)) { + push @ret,$hostname; + } + } + return @ret; +} + + +sub USAGE { + <<"EOF"; + + usage: $0 [options] - show tags + $0 [options] expression... - evaluate and set exit status + $0 --list [options] expression... - enumerate hosts for which expression is true + + $0 [options] --populate-node - (re-)create /node/tags/ + + options: + + --hostname host : evaluate for another host + --debug : guess what this does + + expressions: + + boolean terms: tags from hostconfig + unary operator: '!' + binary operators: '&','|' + precedence: '(',')' + + expression example: + + if $0 distmaster ;then echo "lala";fi + if $0 'mx64 & desktop' ;then echo "lala";fi + if $0 'testing | (mx64b & !desktop)' ;then echo "lala";fi + +EOF +} + +my $hostname; + +GetOptions +( + 'hostname=s' => \$options{'hostname'}, + 'debug' => \$options{'debug'}, + 'populate-node' => \$options{'populate-node'}, + 'list' => \$options{'list'}, + +) or die USAGE; + +chomp($hostname = $options{'hostname'} ? $options{'hostname'} : `uname -n`);$hostname=~s/\..*//; +read_hostconfig(); + +my $expression = join ' ',@ARGV; + +if ($options{'populate-node'}) { + populate_node($hostname); +} elsif ($options{'list'}) { + @ARGV or die USAGE; + print join(' ',list($expression)),"\n"; +} else { + if (@ARGV) { + exit (evalue_expression($hostname,$expression) ? 0 : 1); + } else { + exists $TAGS{$hostname} or warn "$0: hostname $hostname : no info\n"; + my $tags=$TAGS{$hostname}; + print join (' ',sort keys %$tags),"\n"; + } +} + + diff --git a/install.sh b/install.sh index fcbf487..2a7d242 100755 --- a/install.sh +++ b/install.sh @@ -129,4 +129,5 @@ install_exec setuid/setuid "$DESTDIR$usr_sbindir/s install_exec uvpn/uvpn "$DESTDIR$usr_bindir/uvpn" install_exec mxmount/mxmount "$DESTDIR$usr_bindir/mxmount" install_data mxmount/mxmount.service "$DESTDIR$systemdunitdir/mxmount.service" +install_exec hostconfig/hostconfig "$DESTDIR$usr_sbindir/hostconfig" exit