| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Citrix::SessionSet; |
|
2
|
2
|
|
|
2
|
|
1866
|
use Data::Dumper; |
|
|
2
|
|
|
|
|
10446
|
|
|
|
2
|
|
|
|
|
4746
|
|
|
3
|
|
|
|
|
|
|
#use strict; |
|
4
|
|
|
|
|
|
|
#use warnings; |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = "0.25"; |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
# TODO: Allow loading sessions from perl cache file or DB for mock testing |
|
9
|
|
|
|
|
|
|
# # http://support.citrix.com/proddocs/index.jsp?topic=/ps-unix/ps-unix-cmd-ref-commands-ctxquery.html |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Citrix::SessionSet - Query UNIX Citrix Sessions from a Citrix Farm. |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Citrix::SessionSet Allows querying: |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=over 4 |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=item 1) All sessions on farm (multiple, typically 2-8 hosts, by "farm context") |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=item 2) Sessions on a single host (by DNS hostname) |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=item 3) Sessions for an individual user (by username). |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=back |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Parses output from Citrix command line tools to Perl objects. |
|
30
|
|
|
|
|
|
|
The module tries to do its best to deal with traditional problems |
|
31
|
|
|
|
|
|
|
of sub-shell execution (command piping) and remote shelling (rsh). |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
A UNIX Citrix "application" is usually a complete Desktop environment, but may |
|
34
|
|
|
|
|
|
|
also be single app like X-Terminal, Mail Client or Word processor. |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=cut |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Use -f for short format opts -o For long fmt options |
|
42
|
|
|
|
|
|
|
# See p. 234 in Citrix Guide |
|
43
|
|
|
|
|
|
|
# d = 'DEVICE' (client) |
|
44
|
|
|
|
|
|
|
# i = 'HOST:ID' (Combo of host+sessid) |
|
45
|
|
|
|
|
|
|
# I = 'IDLE TIME' |
|
46
|
|
|
|
|
|
|
# S = STATE |
|
47
|
|
|
|
|
|
|
# u = USER |
|
48
|
|
|
|
|
|
|
# x = X display number |
|
49
|
|
|
|
|
|
|
# s = 'SERVER NAME' |
|
50
|
|
|
|
|
|
|
# l = LOGON TIME |
|
51
|
|
|
|
|
|
|
# p = APPLICATION NAME published app (APPID) |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=head1 CLASS VARIABLES |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head2 $Citrix::SessionSet::ctxcols |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
Column format string |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=head2 @Citrix::SessionSet::ctxattr |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
Citrix Session set attributes (matching the letters in $ctxcols format string). |
|
62
|
|
|
|
|
|
|
These turn into hash keys in the sessionset collection. |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head2 $Citrix::SessionSet::debug |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Class level "global" debugging level (Notice that also instance has a debug flag). Set to true to |
|
67
|
|
|
|
|
|
|
troubleshoot Citrix::SessionSet retrieval. |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=cut |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# The path of Citrix Command line utilities. |
|
73
|
|
|
|
|
|
|
# This is used as a path prefix for commands to eliminate runtime guesswork. |
|
74
|
|
|
|
|
|
|
#OLD: our $Citrix::binpath = "/opt/CTXSmf/bin"; |
|
75
|
|
|
|
|
|
|
# |
|
76
|
|
|
|
|
|
|
our $debug = 0; |
|
77
|
|
|
|
|
|
|
# Default Col layout of ctxquery |
|
78
|
|
|
|
|
|
|
our $ctxcols = "iSupd"; # t |
|
79
|
|
|
|
|
|
|
# Arributes to use in the session collection (mapping to tab output format |
|
80
|
|
|
|
|
|
|
# specifiers above). Notice col format specifiers in $ctxcols and this should match. |
|
81
|
|
|
|
|
|
|
my @ctxattr = ('HOST_SID','STATE','USERNAME','APPID','DEVICE',); #'TYPE' |
|
82
|
|
|
|
|
|
|
# OLD Unused: Legacy Default Citrix Query Timeout |
|
83
|
|
|
|
|
|
|
#our $tout = 5; |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# Provide a better alias (to use in API) |
|
86
|
|
|
|
|
|
|
*Citrix::SessionSet::usersessions = \&Citrix::SessionSet::mysess; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head1 METHODS |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head2 my $ss = Citrix::SessionSet->new($farmctx); |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
Construct a new Citrix session collection. |
|
94
|
|
|
|
|
|
|
Indicate Farm context of query by $fc (See L). |
|
95
|
|
|
|
|
|
|
Return empty session set (to be queried later) |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=cut |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub new { |
|
100
|
0
|
|
|
0
|
1
|
|
my ($class, $fc) = @_; |
|
101
|
|
|
|
|
|
|
#OLD:my $ss = []; |
|
102
|
0
|
0
|
|
|
|
|
if (!%$fc) {print("Session::new() : NO FC");return(undef);} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
103
|
0
|
|
|
|
|
|
my $ss = {'sarr' => [], 'fc' => $fc}; |
|
104
|
0
|
|
|
|
|
|
bless($ss, $class); |
|
105
|
0
|
|
|
|
|
|
return($ss); |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head2 $err = $ss->gethostsess('the-cx-host-67'); |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
Get all sessions for a single host (passed as $host) and load the sessions (adding them) into |
|
111
|
|
|
|
|
|
|
session set instance. |
|
112
|
|
|
|
|
|
|
Return 1 for errors, 0 on success. |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=cut |
|
115
|
|
|
|
|
|
|
sub gethostsess { |
|
116
|
0
|
|
|
0
|
1
|
|
my ($ss, $host) = @_; |
|
117
|
0
|
|
|
|
|
|
my $sarr = $ss->getsessions(); |
|
118
|
0
|
|
|
|
|
|
my $fh; |
|
119
|
0
|
|
|
|
|
|
my $ap = ''; |
|
120
|
0
|
|
|
|
|
|
my $fc = $ss->farmctx(); |
|
121
|
0
|
|
|
|
|
|
my $mh = $ss->getmh(); # $fc->masterhost(); |
|
122
|
0
|
|
|
|
|
|
my $ds = $fc->domainsuffix(); # OLD: {'ds'} |
|
123
|
0
|
|
|
|
|
|
my $cnt = scalar(@$sarr); |
|
124
|
0
|
|
|
|
|
|
my $usehost; # Final Host to use for query |
|
125
|
0
|
|
|
|
|
|
my $tout = $Citrix::touts->{'host'}; # 10; |
|
126
|
0
|
|
|
|
|
|
my @times = (); |
|
127
|
0
|
|
|
|
|
|
my $trace = debug($ss); |
|
128
|
0
|
0
|
0
|
|
|
|
if ($trace && $ENV{'HTTP_HOST'}) {print("");} |
|
|
0
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
#if ($host =~ /\./) {die("Expecting bare hostname (got: $host)");} |
|
130
|
0
|
0
|
|
|
|
|
if (!$host) {$usehost = $mh;$ap = ' -S';} |
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
elsif ($ds) {$usehost = "$host.$ds";} |
|
132
|
0
|
|
|
|
|
|
my $cmd = "rsh $usehost $Citrix::binpath/ctxquery -f $ctxcols $ap"; # -S |
|
133
|
|
|
|
|
|
|
# Added loading of Net::Ping to circumvent |
|
134
|
0
|
|
|
|
|
|
eval {require(Net::Ping);}; |
|
|
0
|
|
|
|
|
|
|
|
135
|
0
|
0
|
|
|
|
|
if ($@) {} # print("Dont have Net::Ping (risk hanging)"); |
|
136
|
|
|
|
|
|
|
else { |
|
137
|
0
|
|
|
|
|
|
my $p = Net::Ping->new(); |
|
138
|
0
|
0
|
|
|
|
|
if ($p->ping($usehost)) {if ($trace) {print("$usehost is alive (reachable by PING).\n");}} |
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# Reuse $tout as state variable |
|
140
|
|
|
|
|
|
|
else {$tout = 0;} |
|
141
|
0
|
|
|
|
|
|
$p->close(); |
|
142
|
0
|
0
|
|
|
|
|
if (!$tout) {$ss->{'msg'} = "$usehost NOT Alive.\n";return(1);} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
} |
|
144
|
0
|
0
|
|
|
|
|
if ($trace) {print("Launch Query: $cmd\n");$times[0] = time();} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
|
eval { |
|
146
|
|
|
|
|
|
|
local $SIG{'ALRM'} = sub { |
|
147
|
0
|
|
|
0
|
|
|
die("RSH Timeout ($usehost)\n"); |
|
148
|
0
|
|
|
|
|
|
die("Host '$usehost' was unable to return session within $tout\n"); |
|
149
|
0
|
|
|
|
|
|
}; |
|
150
|
|
|
|
|
|
|
#local $SIG{'CHLD'} = sub {die("Child ($usehost)\n");}; |
|
151
|
0
|
|
|
|
|
|
alarm($tout); |
|
152
|
0
|
0
|
|
|
|
|
if ($trace) {print("Opening Pipe ...\n");} |
|
|
0
|
|
|
|
|
|
|
|
153
|
0
|
|
|
|
|
|
my $ok = open($fh, "$cmd |"); |
|
154
|
0
|
0
|
|
|
|
|
if (!$ok) {die("Failed to open the pipe");} |
|
|
0
|
|
|
|
|
|
|
|
155
|
0
|
0
|
|
|
|
|
if ($trace) {print("Opened: '$cmd' (as $< / $>)\n");} |
|
|
0
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
}; |
|
158
|
|
|
|
|
|
|
# Enforce reset in a place which is always visited |
|
159
|
0
|
|
|
|
|
|
alarm(0); |
|
160
|
0
|
0
|
|
|
|
|
if ($trace) {$times[1] = time();print("Done Trying (Success, ",($times[1]-$times[0])," s.)\n");} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|
|
162
|
0
|
0
|
|
|
|
|
if ($trace) {print("Reset Timeout ($tout => 0)\n");} |
|
|
0
|
|
|
|
|
|
|
|
163
|
0
|
0
|
|
|
|
|
if ($@) {$ss->{'msg'} = $@;return(2);} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
164
|
0
|
0
|
|
|
|
|
if ($trace) {print("Parse Query (From: $fh)\n");} |
|
|
0
|
|
|
|
|
|
|
|
165
|
0
|
|
|
|
|
|
parse($fh, $sarr, \@ctxattr); |
|
166
|
0
|
|
|
|
|
|
my $cnt2 = scalar(@$sarr); |
|
167
|
0
|
0
|
|
|
|
|
if ($host) { |
|
168
|
0
|
|
|
|
|
|
my $cd = ($cnt2 - $cnt); |
|
169
|
0
|
|
|
|
|
|
$ss->{'stat'}->{$host}->{'cnt'} = $cd; |
|
170
|
|
|
|
|
|
|
#if (!$cd) {$ss->{'stat'}->{$host}->{'out'} = "$!";} |
|
171
|
|
|
|
|
|
|
} |
|
172
|
0
|
0
|
0
|
|
|
|
if ($trace && $ENV{'HTTP_HOST'}) {print("\n");} |
|
|
0
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
return(0); |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head2 $err = $ss->getsession('the-cx-host-67:5234'); |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Get a session identified by $hostsess string (HOST:SESSID) from session set |
|
179
|
|
|
|
|
|
|
(Involves sequential search within session set as sessions are not |
|
180
|
|
|
|
|
|
|
indexed in current version). |
|
181
|
|
|
|
|
|
|
The composite key of form "HOST:SESSID" is required, because session set may contain |
|
182
|
|
|
|
|
|
|
sessions from multiple hosts (With single farm context though). |
|
183
|
|
|
|
|
|
|
Return the single identified session (as hash) or undef if no session by SESSID |
|
184
|
|
|
|
|
|
|
is found. |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=cut |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub getsession { |
|
189
|
0
|
|
|
0
|
1
|
|
my ($ss, $hostsess) = @_; |
|
190
|
0
|
|
|
|
|
|
my $sarr = $ss->getsessions(); |
|
191
|
0
|
|
|
|
|
|
my (@s) = grep({$_->{'HOST_SID'} eq $hostsess} @$sarr); |
|
|
0
|
|
|
|
|
|
|
|
192
|
0
|
0
|
|
|
|
|
if (@s < 1) {$ss->errstr("No Sessions for $hostsess'' ");return(undef);} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
193
|
0
|
0
|
|
|
|
|
if (@s > 1) {$ss->errstr("Multiple session for Identified session '$hostsess'");return(undef);} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
#ORG:return((@s == 1) ? $s[0] : undef); |
|
195
|
0
|
|
|
|
|
|
return($s[0]); |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head2 $err = $ss->mysess('joecitrix'); |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Load sessions for single user (by username) into session set. |
|
201
|
|
|
|
|
|
|
Usually loading takes place on an empty set to have truly the sessions for individual only. |
|
202
|
|
|
|
|
|
|
This can be used to create "My Sessions" views, but this is just "Sessions for User by ID". |
|
203
|
|
|
|
|
|
|
Return 1 (and up) for errors 0, for success. |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=cut |
|
206
|
|
|
|
|
|
|
sub mysess { |
|
207
|
0
|
|
|
0
|
1
|
|
my ($ss, $userid) = @_; |
|
208
|
0
|
|
|
|
|
|
my $sarr = $ss->getsessions(); |
|
209
|
0
|
|
|
|
|
|
my $mh = $ss->getmh(); |
|
210
|
0
|
0
|
|
|
|
|
if (!$mh) {print("No Host to query from (master host for Farm)\n");return(1);} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
211
|
0
|
0
|
|
|
|
|
if (!$userid) {print("Err: No User passed for getting sessions\n");return(1);} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
212
|
0
|
|
|
|
|
|
my $cmd = "rsh $mh $Citrix::binpath/ctxquery -f $ctxcols -S user $userid"; # -S |
|
213
|
0
|
0
|
|
|
|
|
if ($ss->debug()) {print("$< / $>: $cmd \n");} |
|
|
0
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
|
my $fh; |
|
215
|
0
|
|
|
|
|
|
my $tout = $Citrix::touts->{'user'}; # 5; |
|
216
|
|
|
|
|
|
|
local $SIG{'ALRM'} = sub { |
|
217
|
|
|
|
|
|
|
# 'cx86-bh-1.bh' was not able respond within given timilimit |
|
218
|
|
|
|
|
|
|
#die("Query Timeout after $tout s. (sig: '$_[0]', masterhost '$mh')"); |
|
219
|
0
|
|
|
0
|
|
|
die("Citrix server '$mh' (master) was not able respond within given timilimit ($tout s.)"); |
|
220
|
0
|
|
|
|
|
|
}; |
|
221
|
0
|
|
|
|
|
|
alarm($tout); |
|
222
|
0
|
|
|
|
|
|
eval { |
|
223
|
0
|
|
|
|
|
|
my $ok = open($fh, "$cmd |"); |
|
224
|
0
|
0
|
|
|
|
|
if (!$ok) {die("Failed to open the pipe");} |
|
|
0
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
}; |
|
226
|
0
|
|
|
|
|
|
alarm(0); |
|
227
|
0
|
0
|
|
|
|
|
if ($@) {print("Failed: $@");return(3);} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
|
my $err = parse($fh, $sarr, \@ctxattr, 2); |
|
229
|
0
|
|
|
|
|
|
return(0); |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# Access (Set/Get) all session from session set. |
|
233
|
|
|
|
|
|
|
# Return session. |
|
234
|
|
|
|
|
|
|
sub getsessions { |
|
235
|
0
|
|
|
0
|
0
|
|
my ($ss, $set) = @_; |
|
236
|
0
|
0
|
|
|
|
|
if (defined($set)) {$ss->{'sarr'} = $set;} |
|
|
0
|
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
return($ss->{'sarr'}); |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# Get the master host of farm context related to session set. |
|
241
|
|
|
|
|
|
|
# Return master host name. |
|
242
|
|
|
|
|
|
|
sub getmh { |
|
243
|
0
|
|
|
0
|
0
|
|
my ($ss) = @_; |
|
244
|
0
|
|
|
|
|
|
$ss->{'fc'}->masterhost(); # OLD: {'mh'} |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# Deprecated. See farmctx |
|
248
|
|
|
|
|
|
|
#sub getfc { |
|
249
|
|
|
|
|
|
|
# $_[0]->{'fc'}; |
|
250
|
|
|
|
|
|
|
#} |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# Get the complete Farm context node for current session set. |
|
253
|
|
|
|
|
|
|
# Return Farm Context node. |
|
254
|
|
|
|
|
|
|
sub farmctx { |
|
255
|
0
|
0
|
|
0
|
0
|
|
if (@_ >= 2) {$_[0]->{'fc'} = $_[1];} |
|
|
0
|
|
|
|
|
|
|
|
256
|
0
|
|
|
|
|
|
$_[0]->{'fc'}; |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head2 my $cnt = $ss->count(); |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Accessor method to get the number of sessions stored in current session set. |
|
262
|
|
|
|
|
|
|
Return the (integer) count. |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=cut |
|
265
|
|
|
|
|
|
|
sub count { |
|
266
|
0
|
|
|
0
|
1
|
|
my ($ss) = @_; |
|
267
|
0
|
|
|
|
|
|
my $sarr = $ss->getsessions(); |
|
268
|
0
|
|
|
|
|
|
return(scalar(@$sarr)); |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# See getsession() |
|
272
|
|
|
|
|
|
|
sub getsessbyid { |
|
273
|
0
|
|
|
0
|
0
|
|
my ($ss, $hostsess) = @_; |
|
274
|
0
|
|
|
|
|
|
my $sarr = $ss->getsessions(); |
|
275
|
0
|
|
|
|
|
|
my (@s) = grep({$_->{'HOST_SID'} eq $hostsess} @$sarr); |
|
|
0
|
|
|
|
|
|
|
|
276
|
0
|
0
|
|
|
|
|
if (scalar(@s) == 1) {return($s[0]);} |
|
|
0
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
|
return(undef); |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Toggle debug mode on in session set collection. |
|
281
|
|
|
|
|
|
|
# This may be used in various contexts to produce more verbose output. |
|
282
|
|
|
|
|
|
|
# Also class level (non-instance) debug flag is probed to find out |
|
283
|
|
|
|
|
|
|
# the desired debug level. |
|
284
|
|
|
|
|
|
|
# As a setter this can only affect the instance level debug setting. |
|
285
|
|
|
|
|
|
|
sub debug { |
|
286
|
0
|
|
|
0
|
1
|
|
my ($ss, $lv) = @_; |
|
287
|
0
|
0
|
|
|
|
|
if (defined($lv)) {$ss->{'debug'} = $lv;} |
|
|
0
|
|
|
|
|
|
|
|
288
|
0
|
|
0
|
|
|
|
return($ss->{'debug'} || $debug); |
|
289
|
|
|
|
|
|
|
} |
|
290
|
|
|
|
|
|
|
sub errstr { |
|
291
|
0
|
|
|
0
|
0
|
|
my ($ss, $es) = @_; |
|
292
|
0
|
0
|
|
|
|
|
if (defined($es)) {$ss->{'errstr'} = $es;} |
|
|
0
|
|
|
|
|
|
|
|
293
|
0
|
|
|
|
|
|
$ss->{'errstr'}; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
# Parse output from Citrix Command pipe fiehandle $fh to $arr (which will be filled with hashes). |
|
296
|
|
|
|
|
|
|
# Space delimited fields will be parsed into attributes @$attr in hashes. |
|
297
|
|
|
|
|
|
|
# This parser is (sofar) applicable to all the possible outputs from |
|
298
|
|
|
|
|
|
|
# Citrix commands returning tabular sets. |
|
299
|
|
|
|
|
|
|
# For internal use only. Not part of exposed API. |
|
300
|
|
|
|
|
|
|
# Return 0 (indicating success) |
|
301
|
|
|
|
|
|
|
sub parse { |
|
302
|
0
|
|
|
0
|
0
|
|
my ($fh, $arr, $attr, $scnt) = @_; |
|
303
|
0
|
0
|
|
|
|
|
if (!$scnt) {$scnt = 0;} # To keep warnings silent |
|
|
0
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# Discard heading line |
|
305
|
0
|
|
|
|
|
|
<$fh>; |
|
306
|
0
|
0
|
|
|
|
|
if ($scnt > 1) {for (2..$scnt) {<$fh>}} |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
|
my $i = 0; |
|
308
|
0
|
|
|
|
|
|
my $spcnt = scalar(@$attr); |
|
309
|
|
|
|
|
|
|
# Consider error message from Citrix server |
|
310
|
0
|
|
|
|
|
|
my $ere = qr/Session\s+info/; # not available |
|
311
|
0
|
|
|
|
|
|
while (<$fh>) { |
|
312
|
|
|
|
|
|
|
# Check early |
|
313
|
0
|
0
|
|
|
|
|
if (/$ere/) {last;} |
|
|
0
|
|
|
|
|
|
|
|
314
|
0
|
|
|
|
|
|
chomp(); |
|
315
|
0
|
|
|
|
|
|
s/^\s+//; |
|
316
|
0
|
0
|
|
|
|
|
if (!$_) {next;} |
|
|
0
|
|
|
|
|
|
|
|
317
|
0
|
|
|
|
|
|
my @a = split(/\s+/, $_, $spcnt); |
|
318
|
0
|
|
|
|
|
|
my %h; |
|
319
|
0
|
|
|
|
|
|
@h{@$attr} = @a; |
|
320
|
|
|
|
|
|
|
# Separate this to a parser hook ? |
|
321
|
0
|
|
|
|
|
|
@h{'HOST', 'SID'} = split(/:/, $h{'HOST_SID'}); |
|
322
|
0
|
|
|
|
|
|
$h{'APPID'} =~ s/^#//; |
|
323
|
|
|
|
|
|
|
# Never care about STATE=listen,conn - Not here |
|
324
|
0
|
|
|
|
|
|
push(@$arr, \%h); |
|
325
|
0
|
|
|
|
|
|
$i++; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
0
|
|
|
|
|
|
close($fh); |
|
328
|
0
|
|
|
|
|
|
return(0); |
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# Internal: Extract Host Statistics Information from a session set (total number, |
|
333
|
|
|
|
|
|
|
# distributions of various session states). |
|
334
|
|
|
|
|
|
|
# Return Host Statistics (Hash of hashes). |
|
335
|
|
|
|
|
|
|
sub hin { |
|
336
|
0
|
|
|
0
|
0
|
|
my ($ss) = @_; |
|
337
|
0
|
|
|
|
|
|
my $sarr = $ss->getsessions(); |
|
338
|
0
|
|
|
|
|
|
my %hosts; |
|
339
|
0
|
|
|
|
|
|
map({ |
|
340
|
0
|
|
|
|
|
|
$hosts{$_->{'HOST'}}->{'tot'}++; |
|
341
|
0
|
|
|
|
|
|
$hosts{$_->{'HOST'}}->{$_->{'STATE'}}++; |
|
342
|
|
|
|
|
|
|
} @$sarr); |
|
343
|
|
|
|
|
|
|
# Update Names into stats |
|
344
|
0
|
|
|
|
|
|
for (keys(%hosts)) {$hosts{$_}->{'host'} = $_;} |
|
|
0
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
|
return(\%hosts); |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
1; |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
#__END__ |
|
351
|
|
|
|
|
|
|
# =head2 NOTES |
|
352
|
|
|
|
|
|
|
# |