summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoretienne2003-05-07 12:13:19 +0000
committeretienne2003-05-07 12:13:19 +0000
commit81840b81a53b005f344d6809be287026577a8928 (patch)
treefc2740e7a8f46eb28d14362b260cb9fba1e0a518
parent23abb4b87c7e40ed259dd02f653516f60e55ade4 (diff)
downloadivylaunch-81840b81a53b005f344d6809be287026577a8928.zip
ivylaunch-81840b81a53b005f344d6809be287026577a8928.tar.gz
ivylaunch-81840b81a53b005f344d6809be287026577a8928.tar.bz2
ivylaunch-81840b81a53b005f344d6809be287026577a8928.tar.xz
Initial revision
-rw-r--r--FugueConfig.pm137
-rwxr-xr-xivylaunch312
2 files changed, 449 insertions, 0 deletions
diff --git a/FugueConfig.pm b/FugueConfig.pm
new file mode 100644
index 0000000..c05d8f7
--- /dev/null
+++ b/FugueConfig.pm
@@ -0,0 +1,137 @@
+package FugueConfig;
+
+use Sys::Hostname;
+use strict;
+
+
+my $rsh = $ENV{"RSH"};
+$rsh = "rsh" unless defined $rsh;
+
+# apply cpp to a fugue config file and open resulting pipe
+sub open {
+
+ my $file = shift;
+ if (system("which cpp > /dev/null")) {
+ print "cpp command can't be located\n";
+ return undef;
+ } elsif (not -f $file) {
+ print "No such file '$file'\n";
+ return undef;
+ } elsif (not -r $file) {
+ print "'$file' is unreadable\n";
+ return undef;
+ }
+ open (FD, "cpp -traditional $file |") or return undef;
+ return \*FD;
+
+} # end open
+
+# basic parsing of a cpped fugue config file; each element of the returned
+# array describes a non-empty line, according to the following format :
+#
+# [type, host, agent_name, command, [command_options]]
+#
+# where 'type' can take one of the following values 'comment', 'global',
+# local', 'begin', and 'end'.
+# a 'comment' type will be followed by an single field
+#
+sub parse {
+
+ my $file = shift;
+ my $fd = &FugueConfig::open($file);
+ return undef unless $fd;
+ my @data;
+ my %appname;
+ my $line = 0;
+ while(<$fd>) {
+ $line++;
+ # empty lines
+ if (/^\s*$/) {
+ next;
+ # comments
+ } elsif (/^\s*\#.*$/) {
+ push(@data, ['comment', $_]);
+ } else {
+ my ($type, $host, $name, $command, @param) =
+ m/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)\#*.*$/ ;
+ #print "t=$type h=$host n=$name c=$command p=".join(' ', @param)."\n";
+ if (not defined $type) {
+ warn ("*** FugueConfig::parse WARNING : ".
+ "syntax error at line $line : \n\t=> $_\n");
+ next;
+ }
+ unless ($type eq 'global' or $type eq 'local' or
+ $type eq 'begin' or $type eq 'end') {
+ warn ("*** FugueConfig::parse WARNING : ".
+ "unknown command type '$type' at line $line. Ignored.\n");
+ next;
+ }
+ if ($appname{$name}) {
+ warn ("*** FugueConfig::parse WARNING : ".
+ "at line $line at least 2 agents have the same name '$name'; ".
+ "skipping all but the first\n");
+ next;
+ }
+ $appname{$name} = 1 if $type eq 'global' or $type eq 'local';
+ push(@data, [$type, $host, $name, $command, [@param]]);
+
+ }
+ }
+ close(1, $fd);
+ return @data;
+
+} # end parse
+
+
+# launch an named ivy agent on a given host and on a given ivy bus
+# return its pid.
+sub launchAgent {
+
+ my ($appname, $host, $command, $options, $bus) = @_;
+ my @options = @$options if ref($options) eq 'ARRAY';
+ push(@options, -b => $bus) if $bus;
+ my $command_opt = $command.' '.join(' ', @options);
+ my $pid = fork;
+ warn ("*** FugueConfig::launchAgent WARNING: Could not fork!\n"), return
+ unless defined $pid;
+
+ if ($pid) {
+ # parent
+ print "*** FugueConfig::launchAgent INFO: $host: launching $appname agent ".
+ "with \'$command_opt\' (pid=$pid)\n";
+ return ($pid);
+ } else {
+ # child
+ my $awk_command = "awk -W interactive '{print \"$appname  \", \$0;}'";
+ if ($host eq 'localhost' or $host eq hostname() ) {
+ exec "$command_opt 2>&1 | $awk_command" or
+ warn "*** FugueConfig::launchAgent WARNING: Error executing $command\n";
+ } else {
+ exec "$rsh -n $host '$command_opt' 2>&1 | $awk_command "
+ or warn "*** FugueConfig::launchAgent WARNING: Error executing ".
+ "$command on $host\n";
+ }
+ }
+
+} # end launchAgent
+
+
+# launch a given command (not an ivy agent) on a given host
+sub launchCommand {
+ my ($type, $host, $command, $options) = @_;
+ my @options = @$options if ref($options) eq 'ARRAY';
+ my $command_opt = $command.' '.join(' ', @options);
+ print "*** FugueConfig::launchCommand INFO: $host: launching command ".
+ "\'$command_opt\'\n";
+ my $awk_command = "awk -W interactive '{print \"$type  \", \$0;}'";
+ if ($host eq 'localhost' or $host eq hostname() ) {
+ $command = "$command_opt | $awk_command ";
+ } else {
+ $command = "$rsh $host '$command_opt' | $awk_command ";
+ }
+# print "$HEADER launching command $command\n";
+ system ($command);
+
+} # end launchCommand
+
+1
diff --git a/ivylaunch b/ivylaunch
new file mode 100755
index 0000000..8e470a2
--- /dev/null
+++ b/ivylaunch
@@ -0,0 +1,312 @@
+#!/usr/bin/perl -w
+#
+# IvyLaunch, a perl script to launch applications on ivy
+#
+# Copyright (C) 1997-1999
+# Centre d'Études de la Navigation Aérienne
+#
+# Main
+#
+# Authors: Michelle Jacomi <jacomi@cena.fr>
+# Johnny Accot <accot@cena.fr>
+# Modified by: Christophe Mertz Mertz@cena.fr
+# Daniel Etienne <etienne@cena.fr>
+#
+# $Id$
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU GPL General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# Or look at http://www.gnu.org/copyleft/gpl.html
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#
+# All applications that should be launched should be specified in the <project file>.
+# @Global is the list of applications that are independent of projet instances. If one
+# of these applications is not present, it is launched. @Local are the applications
+# needed for one instance of the project program. @Begin is the list of command that I
+# should execute before launching the projet applications. @End is the list of command
+# that I should execute when leaving.
+
+# signal handlers
+
+#END { Quit (); }
+
+$SIG{INT} = $SIG{KILL} = 'Quit';
+
+# where you may find the FugueConfig module
+use lib "/usr/lib/ivylaunch";
+use Ivy;
+use strict;
+use Sys::Hostname;
+use File::Basename;
+use Getopt::Long;
+use POSIX 'sys_wait_h';
+use FugueConfig;
+
+my $appliname = "ivylaunch";
+my $HEADER = "$appliname";
+
+my %OPTIONS = ("b=s" => "ivy bus");
+
+my %opt;
+my @apps;
+
+&usage unless Getopt::Long::GetOptions(\%opt, "b=s", 'help', "override") ;
+&usage if $opt{help};
+
+
+my $bus = defined $ENV{"IVYBUS"} ? $ENV{"IVYBUS"} : '127.255.255.255:2010';
+$bus = $opt{'b'} if defined $opt{'b'};
+
+
+my $file = shift @ARGV;
+&usage unless defined($file);
+
+my (%child);
+
+my %cmd = ('begin' => [], 'end' => []);
+my %agent = ('global' => [], 'local' => []);
+
+$| = 1;
+
+print "$HEADER Reading configuration file $file...\n";
+
+my ($type, @param, %ready);
+
+for (FugueConfig::parse($file)) {
+ my ($type, $host, $name, $command, $paramslist) = @$_;
+ next if $type eq 'comment';
+ if ($type eq 'begin' or $type eq 'end') {
+ push @{$cmd{$type}}, [$host, $command, $paramslist];
+ } else {
+ # add filename to ivycontrolpanel command (not destructive)
+ push(@$paramslist, $file) if ($name eq 'ivycontrolpanel');
+ push @{$agent{$type}}, [$name, $host, $command, $paramslist, $bus];
+ push (@apps, $name);
+ }
+}
+
+print "$HEADER needs ", join (",", @apps), "\n";
+
+
+Ivy->init(-loopMode => 'LOCAL',
+ -onDieFunc => [\&Terminate],
+ -appName => $appliname,
+ -ivyBus => $bus);
+
+my $ivy = Ivy->new (-appName => $appliname,
+ -ivyBus => $bus,
+ -statusFunc => \&Status,
+ -neededApp => [@apps]);
+
+print "$HEADER $appliname is being launched using bus $bus (pid=$$)...\n";
+
+# Now execute commands and launch the necessary agents
+foreach my $app (@{$cmd{'begin'}}) {
+ FugueConfig::launchCommand ('begin', @$app);
+}
+foreach my $app (@{$agent{'local'}}) {
+ my $pid = FugueConfig::launchAgent(@$app);
+ $child{$pid} = $app;
+}
+
+# if after 5 seconds the "global" agents are not detected, launch them.
+print "$HEADER Wait 5 seconds before global apps checking...\n";
+Ivy::after (5000, [\&Global]) if @{$agent{'global'}};
+
+my $host = hostname;
+
+$SIG{CHLD} = 'Child';
+
+$ivy->start;
+$ivy->mainLoop;
+
+######################################################
+
+sub Global {
+
+ print "$HEADER Now looking at global apps...\n";
+ my $n = 0;
+ foreach my $app (@{$agent{'global'}}) {
+
+ my $name = @$app[0];
+ unless (grep { $name eq $_ } (keys(%ready))) {
+ print ("$HEADER appli $name not found. Launching it.\n");
+ my $pid = FugueConfig::launchAgent(@$app);
+ $child{$pid} = $app;
+ $n++;
+ }
+ }
+ print "$HEADER $n applis launched.\n";
+}
+
+
+
+######################################################
+
+sub Child {
+
+ $SIG{CHLD} = 'Child';
+
+ if (my $pid = waitpid (-1, WNOHANG)) {
+ return if $pid == -1;
+ my $status = $? & 255;
+ print "$HEADER Warning! $child{$pid}[0]".
+ " (pid=$pid) exited with status $status.\n";
+ delete $child{$pid};
+ }
+}
+
+######################################################
+
+sub Terminate {
+ # when this function is called on an ivy die message,
+ # children don't terminate. must be explored...
+ foreach (keys %child) {
+ print "$HEADER Stopping $child{$_}[0] (pid=$_)...\n";
+ kill 15, $_;
+ }
+
+ foreach my $app (@{$cmd{'end'}}) {
+ FugueConfig::launchCommand('end',@$app);
+ }
+ print "\n$HEADER Terminated\n";
+}
+
+######################################################
+
+sub Quit {
+ print "Quit\n";
+ &Terminate();
+ $ivy->stop();
+ exit;
+}
+
+######################################################
+
+
+sub Status {
+ my ($ref_array_present, $ref_array_absent, $ref_hash_present,
+ $agent, $status, $host) = @_;
+
+ if ($status eq 'new') {
+ print ("$HEADER $agent connected from host $host.\n");
+ print ("$HEADER All agents are running.\n") unless @$ref_array_absent;
+ $ready{$agent}++;
+ }
+ elsif ($status eq 'died') {
+ print ("$HEADER $agent disconnected from host $host..\n");
+ print ("$HEADER Agents are missing.\n") if @$ref_array_absent;
+ $ready{$agent}--;
+ delete $ready{$agent} if $ready{$agent} <= 0;
+ }
+}
+
+sub usage {
+ print "Usage: $appliname [-b bus] [-override] <config_file>\n";
+ exit;
+}
+
+__END__
+
+=head1 NAME
+
+ivylaunch - a script which launches ivy agents, according to a Fugue configuration file.
+
+=head1 SYNOPSIS
+
+ivylaunch [-help] [-b bus] [-override] fugueconfigfile
+
+=head1 DESCRIPTION
+
+ivylaunch forks ivy agents described in a Fugue configuration file. ivylaunch is also an ivy agent : it reports agents connection and disconnection, and can be killed by an Ivy die message. It can be also killed by sending an interrupt signal from keyboard (SIGINT) or kill signal (SIGKILL). In both cases, ivylaunch sends a termination signals to its forked children before exiting.
+
+
+=back
+
+=head1 OPTIONS
+
+=over
+
+=item B<-b> bus
+
+Specify the ivy bus address on which ivylaunch will communicate. If this option is not provided, the value of the environment variable IVYBUS is used. If the variable is not defined, the default value is 127:2010, that is port 2010 on localhost.
+
+=item B<-override>
+
+Infer ivylaunch behavior when a B<global> agent is detected on the bus. See below for explanation.
+
+=back
+
+=head1 FUGUE CONFIGURATION FILE FORMAT
+
+Each line of a fugue configuration file should contain at least 4 mandatory fields separated by space or tabulation : the first three fields are B<type>, B<host> and B<name>. The next ones are dedicated to the B<command> and its options.
+
+=over
+
+=item B<type>
+
+the type of command should take one of the following values : begin, end, global or local. B<begin> links up with commands which are executed before launching ivy agents, B<end> with commands which are executed after agents have been killed, just before exiting. B<global> and B<local> relate to ivy agents. The B<global> tag assures the unicity of an agent : before launching a global agent, ivylaunch checks for it on the bus; if this agent is already connected, ivylaunch preserves it if the B<-override> option is false (the default value), or kills it (by sending an ivy die message) and forks a new one if -override is true. B<local> agents are launched without checking before global ones.
+
+
+=item B<host>
+
+the host where the command will be executed.
+
+=item B<name>
+
+the application name. This indicative field is used to detect double agents. Insure that this name corresponds to the real ivy name detected on the bus. This field make sense only for ivy agent; usage is to set 'none' to other commands.
+
+=item B<command>
+
+the command to execute and its options.
+
+=back
+
+Before being analysed, the configuration file is parsed by the preprocessor B<cpp>, which provides some facilities like macro expansion.
+
+=head1 EXAMPLE OF CONFIGURATION FILE
+
+ #ifndef ACC
+ #define ACC paris
+ #endif
+ #ifndef WP
+ #define WP WP1
+ #endif
+ #ifndef POSITION
+ #define POSITION --acc ACC --wp WP
+ #endif
+
+global anglo Rejeu rejeu -s 9:10 STR_ATH_01_06_26.rej
+
+global tibot ivycontrolpanel ivycontrolpanel -nocursor
+
+local lasra twinkle:ACC:WP:TC twinkle POSITION --role TC -norender
+
+local astik IvyMon ivymon
+
+=head1 BUG
+
+When ivylaunch terminates on an ivy die message, its children keep alive.
+
+=head1 SEE ALSO
+
+ivycontrolpanel(1), ivybanner(1), cpp(1)
+
+=head1 AUTHORS
+
+Daniel Etienne <etienne@cena.fr>
+
+Michelle Jacomi
+
+Johnny Accot