+
+sub send_rcmd_reply
+{
+ my $self = shift;
+ my $tonode = shift;
+ my $fromnode = shift;
+ my $user = shift;
+ while (@_) {
+ my $line = shift;
+ $line =~ s/\s*$//;
+ Log('rcmd', 'out', $fromnode, $line, $user);
+ if ($self->is_clx) {
+ $self->send(pc85($main::mycall, $fromnode, $user, "$main::mycall:$line"));
+ } else {
+ $self->send(pc35($main::mycall, $fromnode, "$main::mycall:$line"));
+ }
+ }
+}
+
+# Punt off a long running command into a separate process - this will be caused by an rcmd from outside
+#
+# This is called from commands to run some potentially long running
+# function. The process forks and then runs the function and returns
+# the result back to the cmd.
+#
+# NOTE: this merely forks the current process and then runs the cmd in that (current) context.
+# IT DOES NOT START UP SOME NEW PROGRAM AND RELIES ON THE FACT THAT IT IS RUNNING DXSPIDER
+# THE CURRENT CONTEXT!!
+#
+# call: $self->spawn_cmd($original_cmd_line, \<function>, [cb => sub{...}], [prefix => "cmd> "], [progress => 0|1], [args => [...]]);
+sub spawn_cmd
+{
+ my $self = shift;
+ my $line = shift;
+ my $cmdref = shift;
+ my $call = $self->{call};
+ my %args = @_;
+ my @out;
+
+ my $cb = delete $args{cb};
+ my $prefix = delete $args{prefix};
+ my $progress = delete $args{progress};
+ my $args = delete $args{args} || [];
+ my $t0 = [gettimeofday];
+
+ # remembered from process_cmd when spawn_cmd was called thru DXCommandmode::run_cmd which was called by process_rcmd
+ my $fromnode = $self->{_rcmd_fromnode};
+ my $user = $self->{_rcmd_user};
+
+ no strict 'refs';
+
+ my $fc = Mojo::IOLoop::Subprocess->new;
+
+ # just behave normally if something has set the "one-shot" _nospawn in the channel
+ if ($self->{_nospawn}) {
+ eval { @out = $cmdref->(@$args); };
+ if ($@) {
+ DXDebug::dbgprintring(25);
+ push @out, DXDebug::shortmess($@);
+ }
+ return @out;
+ }
+
+ # $fc->serializer(\&encode_json);
+# $fc->deserializer(\&decode_json);
+ $fc->run(
+ sub {
+ my $subpro = shift;
+ if (isdbg('progress')) {
+ my $s = "line: $line";
+ $s .= ", args: " . join(', ', @$args) if $args && @$args;
+ dbg($s);
+ }
+ eval { @out = $cmdref->(@$args); };
+ if ($@) {
+ DXDebug::dbgprintring(25);
+ push @out, DXDebug::shortmess($@);
+ }
+ return @out;
+ },
+# $args,
+ sub {
+ my ($fc, $err, @res) = @_;
+ my $self = DXChannel::get($call);
+ return unless $self;
+
+ if ($err) {
+ my $s = "DXCommand::spawn_cmd: call $call error $err";
+ dbg($s) if isdbg('chan');
+ if ($fromnode && $user) {
+ $self->send_rcmd_reply($main::mycall, $fromnode, $user, $s);
+ } else {
+ $self->send($s);
+ }
+ return;
+ }
+ if ($cb) {
+ # transform output if required
+ @res = $cb->($self, @res);
+ }
+ if (@res) {
+ if ($fromnode && $user) {
+ $self->send_rcmd_reply($main::mycall, $fromnode, $user, @res);
+ } else {
+ $self->send(@res);
+ }
+ }
+ diffms("rcmd from $user on $call", $line, $t0, scalar @res) if isdbg('progress');
+ });
+
+ return @out;
+}
+