3 # This module impliments the user facing command mode for a dx cluster
5 # Copyright (c) 1998 Dirk Koopman G1TLH
10 package DXCommandmode;
21 use vars qw( %Cache $last_dir_mtime @cmd);
23 $last_dir_mtime = 0; # the last time one of the cmd dirs was modified
24 @cmd = undef; # a list of commands+path pairs (in alphabetical order)
26 # this is how a a connection starts, you get a hello message and the motd with
27 # possibly some other messages asking you to set various things up if you are
28 # new (or nearly new and slacking) user.
32 my ($self, $line) = @_;
33 my $user = $self->{user};
34 my $call = $self->{call};
35 my $name = $user->{name};
37 $self->{name} = $name ? $name : $call;
38 $self->msg('l2',$self->{name});
39 $self->send_file($main::motd) if (-e $main::motd);
40 $self->msg('pr', $call);
41 $self->state('prompt'); # a bit of room for further expansion, passwords etc
42 $self->{priv} = $user->priv;
43 $self->{priv} = 0 if $line =~ /^(ax|te)/; # set the connection priv to 0 - can be upgraded later
44 $self->{consort} = $line; # save the connection type
48 # This is the normal command prompt driver
53 my $user = $self->{user};
54 my $call = $self->{call};
58 $cmdline =~ s|//|/|og;
60 # split the command line up into parts, the first part is the command
61 my ($cmd, $args) = $cmdline =~ /^([\w\/]+)\s*(.*)/o;
65 # first expand out the entry to a command
68 my @ans = $self->eval_file($main::localcmd, $cmd, $args);
69 @ans = $self->eval_file($main::cmd, $cmd, $args) if !$ans[0];
72 $self->send(@ans) if @ans > 0;
76 $self->msg('e2', @ans);
85 # send a prompt only if we are in a prompt state
86 $self->prompt() if $self->{state} =~ /^prompt/o;
90 # This is called from inside the main cluster processing loop and is used
91 # for despatching commands that are doing some long processing job
99 # finish up a user context
107 # short cut to output a prompt
113 my $call = $self->{call};
114 DXChannel::msg($self, 'pr', $call);
118 # search for the command in the cache of short->long form commands
123 my $short_cmd = shift;
124 return $short_cmd; # just return it for now
128 # the persistant execution of things from the command directories
131 # This allows perl programs to call functions dynamically
133 # This has been nicked directly from the perlembed pages
136 #require Devel::Symdump;
138 sub valid_package_name {
140 $string =~ s/([^A-Za-z0-9\/])/sprintf("_%2x",unpack("C",$1))/eg;
142 #second pass only for words starting with a digit
143 $string =~ s|/(\d)|sprintf("/_%2x",unpack("C",$1))|eg;
145 #Dress it up as a real package name
147 return "Emb_" . $string;
150 #borrowed from Safe.pm
156 $pkg = "DXChannel::$pkg\::"; # expand to full symbol table name
157 ($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
159 my $stem_symtab = *{$stem}{HASH};
161 delete $stem_symtab->{$leaf};
168 my $package = valid_package_name($cmdname);
169 my $filename = "$path/$cmdname.pl";
170 my $mtime = -M $filename;
172 # return if we can't find it
173 return (0, DXM::msg('e1')) if !defined $mtime;
175 if(defined $Cache{$package}{mtime} && $Cache{$package}{mtime } <= $mtime) {
176 #we have compiled this subroutine already,
177 #it has not been updated on disk, nothing left to do
178 #print STDERR "already compiled $package->handler\n";
182 if (!open FH, $filename) {
183 return (0, "Syserr: can't open '$filename' $!");
189 #wrap the code into a subroutine inside our unique package
190 my $eval = qq{package DXChannel; sub $package { $sub; }};
192 my @list = split /\n/, $eval;
195 dbg('eval', $_, "\n");
198 #print "eval $eval\n";
200 #hide our variables within this block
201 my($filename,$mtime,$package,$sub);
205 delete_package($package);
206 return (0, "Syserr: Eval err $@ on $package");
209 #cache it unless we're cleaning out each time
210 $Cache{$package}{mtime} = $mtime;
214 my $c = qq{ \@r = \$self->$package(\@_); };
215 dbg('eval', "cluster cmd = $c\n");
218 delete_package($package);
219 return (0, "Syserr: Eval err $@ on cached $package");
222 #take a look if you want
223 #print Devel::Symdump->rnew($package)->as_string, $/;