File Coverage

blib/lib/XAS/Lib/WS/RemoteShell.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package XAS::Lib::WS::RemoteShell;
2              
3             our $VERSION = '0.02';
4              
5 1     1   923 use XAS::Lib::XML;
  0            
  0            
6             use XAS::Class
7             version => $VERSION,
8             base => 'XAS::Lib::WS::Base',
9             utils => ':validation dotid',
10             codec => 'base64',
11             accessors => 'created command_id shell_id stderr stdout exitcode clixml',
12             ;
13              
14             # ----------------------------------------------------------------------
15             # Public Methods
16             # ----------------------------------------------------------------------
17              
18             sub create {
19             my $self = shift;
20              
21             my $uuid = $self->uuid->create_str;
22             my $xml = $self->_create_xml($uuid);
23              
24             $self->log->debug(sprintf('create: uuid - %s', $uuid));
25              
26             $self->_make_call($xml);
27              
28             return $self->_create_response($uuid);
29              
30             }
31              
32             sub command {
33             my $self = shift;
34             my ($command) = validate_params(\@_, [1]);
35              
36             my $uuid = $self->uuid->create_str;
37             my $xml = $self->_command_xml($uuid, $command);
38              
39             $self->{'stdout'} = '';
40             $self->{'stderr'} = '';
41             $self->{'exitcode'} = -1;
42              
43             $self->log->debug(sprintf('command: uuid - %s', $uuid));
44              
45             $self->_make_call($xml);
46             $self->_command_response($uuid);
47              
48             }
49              
50             sub delete {
51             my $self = shift;
52              
53             my $uuid = $self->uuid->create_str;
54             my $xml = $self->_delete_xml($uuid);
55              
56             $self->_make_call($xml);
57              
58             return $self->_delete_response($uuid);
59              
60             }
61              
62             sub destroy {
63             my $self = shift;
64              
65             if ($self->created) {
66              
67             $self->delete();
68              
69             $self->{'created'} = 0;
70              
71             }
72              
73             }
74              
75             sub receive {
76             my $self = shift;
77              
78             my $running;
79             my $uuid = $self->uuid->create_str;
80             my $xml = $self->_receive_xml($uuid);
81              
82             do {
83              
84             $self->_make_call($xml);
85             $running = $self->_receive_response($uuid);
86              
87             } while ($running);
88              
89             }
90              
91             sub send {
92             my $self = shift;
93             my ($buffer, $eot) = validate_params(\@_, [
94             1,
95             { optional => 1, default => 1 },
96             ]);
97              
98             my $uuid = $self->uuid->create_str;
99             my $xml = $self->_send_xml($uuid, $buffer, $eot);
100              
101             $self->_make_call($xml);
102              
103             return $self->_send_response($uuid);
104              
105             }
106              
107             sub signal {
108             my $self = shift;
109              
110             my $uuid = $self->uuid->create_str;
111             my $xml = $self->_signal_xml($uuid);
112              
113             $self->_make_call($xml);
114              
115             return $self->_signal_response($uuid);
116              
117             }
118              
119             sub check_exitcode {
120             my $self = shift;
121              
122             my $caller = (caller(1))[3];
123             my $errmsg = $self->stdout;
124              
125             if ($self->exitcode > 0) {
126              
127             if ($self->stderr =~ /CLIXML/) { # Powershell error response
128              
129             $self->_parse_clixml();
130             $errmsg = $self->clixml->doc->toString();
131              
132             } else {
133              
134             $errmsg = $self->stderr;
135              
136             }
137              
138             $self->throw_msg(
139             dotid($self->class) . sprintf('.%s.badrc', $caller),
140             'ws_badrc',
141             $self->exitcode,
142             $errmsg
143             );
144              
145             }
146              
147             }
148              
149             # ----------------------------------------------------------------------
150             # Private Methods
151             # ----------------------------------------------------------------------
152              
153             sub _check_command_id {
154             my $self = shift;
155             my $uuid = shift;
156              
157             $self->log->debug(sprintf('check_command_id: %s = %s', $uuid, $self->command_id));
158              
159             unless ($uuid eq $self->command_id) {
160              
161             $self->throw_msg(
162             dotid($self->class) . '.check_command_id.wrongid',
163             'ws_wrongid'
164             );
165              
166             }
167              
168             }
169              
170             sub _parse_clixml {
171             my $self = shift;
172              
173             my $xml = $self->stderr;
174              
175             $xml =~ s/\#< CLIXML//g;
176             $xml =~ s/_x000D_//g;
177             $xml =~ s/_x000A_//g;
178              
179             $self->clixml->load($xml);
180              
181             }
182              
183             sub _create_response {
184             my $self = shift;
185             my $uuid = shift;
186              
187             my $xpath;
188             my $stat = 0;
189              
190             $self->_check_relates_to($uuid);
191              
192             if ($self->xml->get_item('//x:ResourceCreated')) {
193              
194             if (my $item = $self->xml->get_item('//rsp:ShellId')) {
195              
196             $self->{'shell_id'} = $item;
197             $self->{'created'} = 1;
198              
199             $self->log->debug(sprintf('create_response: shell_id = %s', $self->shell_id));
200              
201             $stat = 1;
202              
203             } else {
204              
205             $self->throw_msg(
206             dotid($self->class) . '._create_response.shell_id',
207             'ws_noshellid',
208             );
209              
210             }
211              
212             } else {
213              
214             $self->throw_msg(
215             dotid($self->class) . '._create_response.shell_id',
216             'ws_noresource',
217             );
218              
219             }
220              
221             return $stat;
222              
223             }
224              
225             sub _command_response {
226             my $self = shift;
227             my $uuid = shift;
228              
229             my $xpath = '//rsp:CommandId';
230              
231             $self->_check_relates_to($uuid);
232              
233             if (my $id = $self->xml->get_item($xpath)) {
234              
235             $self->{'command_id'} = $id;
236              
237             } else {
238              
239             $self->throw_msg(
240             dotid($self->class) . '._command_reponse.command_id',
241             'ws_nocmdid',
242             );
243              
244             }
245              
246             }
247              
248             sub _receive_response {
249             my $self = shift;
250             my $uuid = shift;
251              
252             my $running = 1;
253             my $xpath = '//rsp:ReceiveResponse';
254              
255             $self->_check_relates_to($uuid);
256              
257             my $elements = $self->xml->get_items($xpath);
258              
259             foreach my $element (@$elements) {
260              
261             if ($element->nodeName =~ /Stream/) {
262              
263             my $name = $element->getAttribute('Name');
264             my $id = $element->getAttribute('CommandId');
265              
266             $self->_check_command_id($id);
267              
268             if ($name =~ /stdout/) {
269              
270             if (my $stuff = $element->textContent) {
271              
272             $self->{'stdout'} .= decode($stuff);
273              
274             }
275              
276             } elsif ($name =~ /stderr/) {
277              
278             if (my $stuff = $element->textContent) {
279              
280             $self->{'stderr'} .= decode($stuff);
281              
282             }
283              
284             }
285              
286             } elsif ($element->nodeName =~ /CommandState/) {
287              
288             my $state = $element->getAttribute('State');
289             my $id = $element->getAttribute('CommandId');
290              
291             $self->_check_command_id($id);
292              
293             $running = ($state =~ /Running/ || 0);
294              
295             if (my $children = $element->childNodes) {
296              
297             foreach my $child (@$children) {
298              
299             if ($child->nodeName =~ /ExitCode/) {
300              
301             $self->{'exitcode'} = $child->textContent;
302              
303             }
304              
305             }
306              
307             }
308              
309             }
310              
311             }
312              
313             return $running;
314              
315             }
316              
317             sub _send_response {
318             my $self = shift;
319             my $uuid = shift;
320              
321             my $stat = 0;
322             my $xpath = '//rsp:SendResponse';
323              
324             $self->_check_relates_to($uuid);
325              
326             $stat = 1 if ($self->xml->get_items($xpath));
327              
328             return $stat;
329              
330             }
331              
332             sub _signal_response {
333             my $self = shift;
334             my $uuid = shift;
335              
336             my $stat = 0;
337             my $xpath = '//rsp:SignalResponse';
338              
339             $self->_check_relates_to($uuid);
340              
341             $stat = 1 if ($self->xml->get_item($xpath));
342              
343             return $stat;
344              
345             }
346              
347             sub _delete_response {
348             my $self = shift;
349             my $uuid = shift;
350              
351             my $stat = 0;
352             my $xpath = '//rsp:DeleteResponse';
353              
354             $self->_check_relates_to($uuid);
355              
356             $stat = 1 if ($self->xml->get_item($xpath));
357              
358             }
359              
360             sub init {
361             my $class = shift;
362              
363             my $self = $class->SUPER::init(@_);
364              
365             $self->{'created'} = 0;
366             $self->{'clixml'} = XAS::Lib::XML->new();
367              
368             return $self;
369              
370             }
371              
372             # ----------------------------------------------------------------------
373             # XML boilerplate - we're using heredoc for simplcity
374             #
375             # XML for ws-manage RemoteShell was taken from
376             # http://msdn.microsoft.com/en-us/library/cc251731.aspx
377             # ----------------------------------------------------------------------
378              
379             sub _command_xml {
380             my $self = shift;
381             my $uuid = shift;
382             my $command = shift;
383              
384             my $url = $self->url;
385             my $timeout = $self->timeout;
386             my $shell_id = $self->shell_id;
387              
388             my $xml = <<"XML";
389             <?xml version="1.0" encoding="UTF-8"?>
390             <s:Envelope
391             xmlns:s="http://www.w3.org/2003/05/soap-envelope"
392             xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
393             xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
394             <s:Header>
395             <a:To>
396             $url
397             </a:To>
398             <w:ResourceURI s:mustUnderstand="true">
399             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
400             </w:ResourceURI>
401             <a:ReplyTo>
402             <a:Address s:mustUnderstand="true">
403             http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
404             </a:Address>
405             </a:ReplyTo>
406             <a:Action s:mustUnderstand="true">
407             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command
408             </a:Action>
409             <w:MaxEnvelopeSize s:mustUnderstand="true">
410             512000
411             </w:MaxEnvelopeSize>
412             <a:MessageID>
413             uuid:$uuid
414             </a:MessageID>
415             <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
416             <w:SelectorSet>
417             <w:Selector Name="ShellId">
418             $shell_id
419             </w:Selector>
420             </w:SelectorSet>
421             <w:OptionSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
422             <w:Option Name="WINRS_CONSOLEMODE_STDIN">TRUE</w:Option>
423             <w:Option Name="WINRS_SKIP_CMD_SHELL">FALSE</w:Option>
424             </w:OptionSet>
425             <w:OperationTimeout>PT$timeout.000S</w:OperationTimeout>
426             </s:Header>
427             <s:Body>
428             <rsp:CommandLine xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell">
429             <rsp:Command>
430             &quot;$command&quot;
431             </rsp:Command>
432             </rsp:CommandLine>
433             </s:Body>
434             </s:Envelope>
435             XML
436              
437             return $xml;
438              
439             }
440              
441             sub _create_xml {
442             my $self = shift;
443             my $uuid = shift;
444              
445             my $url = $self->url;
446             my $timeout = $self->timeout;
447              
448             my $xml = <<"XML";
449             <?xml version="1.0" encoding="UTF-8"?>
450             <s:Envelope
451             xmlns:s="http://www.w3.org/2003/05/soap-envelope"
452             xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
453             xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
454             <s:Header>
455             <a:To>
456             $url
457             </a:To>
458             <w:ResourceURI s:mustUnderstand="true">
459             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
460             </w:ResourceURI>
461             <a:ReplyTo>
462             <a:Address s:mustUnderstand="true">
463             http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
464             </a:Address>
465             </a:ReplyTo>
466             <a:Action s:mustUnderstand="true">
467             http://schemas.xmlsoap.org/ws/2004/09/transfer/Create
468             </a:Action>
469             <w:MaxEnvelopeSize s:mustUnderstand="true">
470             512000
471             </w:MaxEnvelopeSize>
472             <a:MessageID>
473             uuid:$uuid
474             </a:MessageID>
475             <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
476             <w:OptionSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
477             <w:Option Name="WINRS_NOPROFILE">TRUE</w:Option>
478             <w:Option Name="WINRS_CODEPAGE">437</w:Option>
479             </w:OptionSet>
480             <w:OperationTimeout>
481             PT$timeout.000S
482             </w:OperationTimeout>
483             </s:Header>
484             <s:Body>
485             <rsp:Shell xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell">
486             <rsp:InputStreams>stdin</rsp:InputStreams>
487             <rsp:OutputStreams>stdout stderr</rsp:OutputStreams>
488             </rsp:Shell>
489             </s:Body>
490             </s:Envelope>
491             XML
492              
493             return $xml;
494              
495             }
496              
497             sub _delete_xml {
498             my $self = shift;
499             my $uuid = shift;
500              
501             my $url = $self->url;
502             my $timeout = $self->timeout;
503             my $shell_id = $self->shell_id;
504              
505             my $xml = <<"XML";
506             <?xml version="1.0" encoding="UTF-8"?>
507             <s:Envelope
508             xmlns:s="http://www.w3.org/2003/05/soap-envelope"
509             xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
510             xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
511             <s:Header>
512             <a:To>
513             $url
514             </a:To>
515             <a:ReplyTo>
516             <a:Address s:mustUnderstand="true">
517             http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
518             </a:Address>
519             </a:ReplyTo>
520             <a:Action s:mustUnderstand="true">
521             http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete
522             </a:Action>
523             <w:MaxEnvelopeSize s:mustUnderstand="true">
524             512000
525             </w:MaxEnvelopeSize>
526             <a:MessageID>
527             uuid:$uuid
528             </a:MessageID>
529             <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
530             <w:ResourceURI s:mustUnderstand="true">
531             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
532             </w:ResourceURI>
533             <w:SelectorSet>
534             <w:Selector Name="ShellId">
535             $shell_id
536             </w:Selector>
537             </w:SelectorSet>
538             <w:OperationTimeout>PT$timeout.000S</w:OperationTimeout>
539             </s:Header>
540             <s:Body></s:Body>
541             </s:Envelope>
542             XML
543              
544             return $xml;
545              
546             }
547              
548             sub _receive_xml {
549             my $self = shift;
550             my $uuid = shift;
551              
552             my $url = $self->url;
553             my $timeout = $self->timeout;
554             my $shell_id = $self->shell_id;
555             my $command_id = $self->command_id;
556              
557             my $xml = <<"XML";
558             <?xml version="1.0" encoding="UTF-8"?>
559             <s:Envelope
560             xmlns:s="http://www.w3.org/2003/05/soap-envelope"
561             xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
562             xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
563             <s:Header>
564             <a:To>
565             $url
566             </a:To>
567             <a:ReplyTo>
568             <a:Address s:mustUnderstand="true">
569             http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
570             </a:Address>
571             </a:ReplyTo>
572             <a:Action s:mustUnderstand="true">
573             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive
574             </a:Action>
575             <w:MaxEnvelopeSize s:mustUnderstand="true">
576             512000
577             </w:MaxEnvelopeSize>
578             <a:MessageID>
579             uuid:$uuid
580             </a:MessageID>
581             <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
582             <w:ResourceURI s:mustUnderstand="true">
583             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
584             </w:ResourceURI>
585             <w:SelectorSet>
586             <w:Selector Name="ShellId">
587             $shell_id
588             </w:Selector>
589             </w:SelectorSet>
590             <w:OperationTimeout>PT$timeout.000S</w:OperationTimeout>
591             </s:Header>
592             <s:Body>
593             <rsp:Receive
594             xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell"
595             SequenceId="0">
596             <rsp:DesiredStream CommandId="$command_id">
597             stdout stderr
598             </rsp:DesiredStream>
599             </rsp:Receive>
600             </s:Body>
601             </s:Envelope>
602             XML
603              
604             return $xml;
605              
606             }
607              
608             sub _send_xml {
609             my $self = shift;
610             my $uuid = shift;
611             my $buffer = shift;
612             my $eot = shift;
613              
614             my $url = $self->url;
615             my $timeout = $self->timeout;
616             my $shell_id = $self->shell_id;
617             my $command_id = $self->command_id;
618              
619             $buffer = encode($buffer);
620              
621             my $xml = <<"XML";
622             <?xml version="1.0" encoding="UTF-8"?>
623             <s:Envelope
624             xmlns:s="http://www.w3.org/2003/05/soap-envelope"
625             xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
626             xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
627             <s:Header>
628             <a:To>
629             $url
630             </a:To>
631             <a:ReplyTo>
632             <a:Address s:mustUnderstand="true">
633             http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
634             </a:Address>
635             </a:ReplyTo>
636             <a:Action s:mustUnderstand="true">
637             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send
638             </a:Action>
639             <w:MaxEnvelopeSize s:mustUnderstand="true">
640             153600
641             </w:MaxEnvelopeSize>
642             <a:MessageID>
643             uuid:$uuid
644             </a:MessageID>
645             <w:Locale xml:lang="en-US" s:mustUnderstand="false" />
646             <w:ResourceURI>
647             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
648             </w:ResourceURI s:mustUnderstand="true">
649             <w:SelectorSet>
650             <w:Selector Name="ShellId">
651             $shell_id
652             </w:Selector>
653             </w:SelectorSet>
654             <w:OperationTimeout>PT$timeout.000S</w:OperationTimeout>
655             </s:Header>
656             <s:Body>
657             <rsp:Send xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell">
658             <rsp:Stream
659             xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell"
660             Name="stdin" CommandId="$command_id">
661             $buffer
662             </rsp:Stream>
663             </rsp:Send>
664             </s:Body>
665             </s:Envelope>
666             XML
667              
668             return $xml;
669              
670             }
671              
672             sub _signal_xml {
673             my $self = shift;
674             my $uuid = shift;
675              
676             my $url = $self->url;
677             my $timeout = $self->timeout;
678             my $shell_id = $self->shell_id;
679             my $command_id = $self->command_id;
680              
681             my $xml = <<"XML";
682             <?xml version="1.0" encoding="UTF-8"?>
683             <s:Envelope
684             xmlns:s="http://www.w3.org/2003/05/soap-envelope"
685             xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
686             xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">
687             <s:Header>
688             <a:To>
689             $url
690             </a:To>
691             <a:ReplyTo>
692             <a:Address s:mustUnderstand="true">
693             http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
694             </a:Address>
695             </a:ReplyTo>
696             <a:Action s:mustUnderstand="true">
697             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal
698             </a:Action>
699             <w:MaxEnvelopeSize s:mustUnderstand="true">
700             512000
701             </w:MaxEnvelopeSize>
702             <a:MessageID>
703             uuid:$uuid
704             </a:MessageID>
705             <w:Locale xml:lang="en-US" s:mustUnderstand="false"/>
706             <w:ResourceURI s:mustUnderstand="true">
707             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd
708             </w:ResourceURI>
709             <w:SelectorSet>
710             <w:Selector Name="ShellId">
711             $shell_id
712             </w:Selector>
713             </w:SelectorSet>
714             <w:OperationTimeout>PT$timeout.000S</w:OperationTimeout>
715             </s:Header>
716             <s:Body>
717             <rsp:Signal
718             xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell"
719             CommandId="$command_id">
720             <rsp:Code>
721             http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate
722             </rsp:Code>
723             </rsp:Signal>
724             </s:Body>
725             </s:Envelope>
726             XML
727              
728             return $xml;
729              
730             }
731              
732              
733             1;
734              
735             __END__
736            
737             =head1 NAME
738              
739             XAS::Lib::WS::RemoteShell - A class for the XAS environment
740              
741             =head1 SYNOPSIS
742              
743             use XAS::Lib::WS::RemoteShell;
744              
745             my $wsman = XAS::Lib::WS::RemoteShell->new(
746             -username => 'Administrator',
747             -password => 'password',
748             -url => 'http://localhost:5985/wsman',
749             );
750              
751             # this appears to be the sequence that winrs uses.
752              
753             if ($wsman->create()) {
754            
755             $wsman->command('dir');
756             $wsman->receive();
757             $wsman->signal();
758             $wsman->delete();
759            
760             }
761              
762             printf("%s", $wsman->stdout);
763             printf("exit code: %s\n", $wsman->exitcode);
764              
765             =head1 DESCRIPTION
766              
767             This package implements a client for the RemoteShell feature of WS-Manage.
768             Getting the RemoteShell feature working under Windows is not easy. The
769             reasons for these problems may be hidden in a Knowledge Base article on
770             MSDN. These problems are mostly related to authentication and quirks of the
771             winrm server.
772              
773             On Windows 2013 R2 the "Windows Remote Management Server" needs to be
774             configured as follows:
775              
776             From a powershell console.
777              
778             ps> cd WSman:\localhost\
779             ps> cd Client
780             ps> set-item AllowUnencrypted $true
781             ps> set-item TrustedHosts "*"
782             ps> dir
783             ps> cd ..\Service
784             ps> set-item AllowUnencrypted $true
785             ps> cd Auth
786             ps> set-item Basic $true
787             ps> dir
788             ps> cd ..
789             ps> dir
790             ps> cd ..
791              
792             Other versions of Windows are done similarly. This will allow connections
793             from a none Windows box. These connections will be in clear text.
794             B<This should not be used on the public internet>.
795              
796             This configuration will allow for an unencrypted HTTP connection with BASIC
797             Authentication to a local user account, on the target box. You may wish to
798             use the local Administrator account.
799              
800             The usage of SSL for security will require additional configuration. Which
801             is not documented well. By default, Windows doesn't listen on port 5986.
802              
803             Using a Domain account requires kerberos authentication. I have not gotten this
804             to work with RemoteShell. It may require additional configuration for that
805             to work. But this configuration is not documented. Hence, the current usage.
806             Once again, refer to that mythical Knowledge Base articule on MSDN.
807              
808             The Linux version (L<OpenWSMAN v2.2.3|https://openwsman.github.io/>) doesn't
809             implement the RemoteShell feature.
810              
811             =head1 METHODS
812              
813             =head2 new
814              
815             This class inherits from L<XAS::Lib::WS::Base|XAS::Lib::WS::Base> and takes
816             the same parameters. The parameters:
817              
818             -keep_alive
819             -auth_method
820              
821             Have been defaulted to approbriate values.
822              
823             =head2 create
824              
825             This method makes the initial connection to the server and creates a remote
826             shell. It returns TRUE if it was successful.
827              
828             =head2 command($command)
829              
830             This method will send a command to the server to be executed by the shell.
831              
832             =over 4
833              
834             =item B<$command>
835              
836             The command string to send.
837              
838             =back
839              
840             =head2 send
841              
842             This method will send a buffer to the remote shell.
843              
844             =head2 receive
845              
846             This method will receive output from the remote shell. This will include
847             output for STDOUT and STDERR. The exit code will also be retrieved from the
848             command.
849              
850             =head2 signal
851              
852             This method will send a "terminate" signal to the remote shell.
853              
854             =head2 delete
855              
856             This method will delete the remote shell.
857              
858             =head2 stdout
859              
860             This method returns the output from STDOUT.
861              
862             =head2 stderr
863              
864             This method returns the output from STDERR.
865              
866             =head2 exitcode
867              
868             This method returns the exit code.
869              
870             =head2 check_exitcode
871              
872             This method will check the exit code. If the code is greater then 0 it will
873             try to parse the stderr stream looking for a reason. This method throws
874             an exception with the exit code and the parsed stderr.
875              
876             =head1 SEE ALSO
877              
878             =over 4
879              
880             =item L<XAS::Lib::WS::Base|XAS::Lib::WS::Base>
881              
882             =item L<XAS|XAS>
883              
884             =back
885              
886             =head1 AUTHOR
887              
888             Kevin L. Esteb, E<lt>kevin@kesteb.usE<gt>
889              
890             =head1 COPYRIGHT AND LICENSE
891              
892             Copyright (c) 2012-2016 Kevin L. Esteb
893              
894             This is free software; you can redistribute it and/or modify it under
895             the terms of the Artistic License 2.0. For details, see the full text
896             of the license at http://www.perlfoundation.org/artistic_license_2_0.
897              
898             =cut