summaryrefslogtreecommitdiffstats
path: root/contrib/check_ms_spooler.pl
blob: cf2464af92adbe40306b43cb21ae8843245cf4b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
#!/usr/bin/perl -w

# $Id$

# $Log$
# Revision 1.1  2002/07/16 00:04:42  stanleyhopcroft
# Primitive and in need of refinement test of MS spooler (with smbclient)
#
# Revision 2.5  2002-02-13 07:36:08+11  anwsmh
# Correct 'apostrophe' disaster.
# Apostrophes in plugin output cause Netsaint notification commands
# ( sh echo 'yada $PLUGINOUTPUT$ ..') to fail, usually mysteriously
# eg notify OK works but notify CRITICAL does not.
# Replace '$var' in print "output" with \"$var\".
#
# Revision 2.4  2001-11-21 21:36:05+11  anwsmh
# Minor corrections
#  . replace 'die' by print .. exit $ERRORS{CRITICAL}
#  . change concluding message to list the queues (sorted) if there are no enqueued docs.
#
# Revision 2.3  2001-11-20 11:00:58+11  anwsmh
# Major corrections.
#  1. to sub AUTOLOAD: coderef parms must be @_ (ie the parm when the new sub is called)
#  2. to processing of queue report (no inspection of $last_line; entire $queue_report is
#     checked for errors)
#  3. cosmetic and debug changes in many places.
#
# Revision 2.2  2001-11-17 23:30:34+11  anwsmh
# After adapting two different queue reports resulting from
# different name resolution methods.
#
# Revision 2.1  2001-11-17 13:21:54+11  anwsmh
# Adapt to Netsaint ('use utils, Getopt::Long, and standard switch processing).
# Fix many peculiarities.
#


use strict ;

use Getopt::Long ;
use utils ;

use vars qw($opt_H $opt_s $opt_W $opt_u $opt_p $opt_w $opt_c $debug);
use vars '$AUTOLOAD' ;
use utils qw($TIMEOUT %ERRORS &print_revision &support &usage);

my $PROGNAME = 'check_ms_spooler' ;

sub print_help ();
sub print_usage ();
sub help ();
sub version ();

delete @ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

use constant SMBCLIENT_PATH	=> '/usr/local/samba/bin/smbclient' ;
use constant MAX_QUEUES_TO_CHECK => 20 ;		# So that the check doesn't take longer than $TIMEOUT

use constant SMBCLIENT_SVC	=> sub { return `${\SMBCLIENT_PATH} -L //$_[0] -U $_[1]%$_[2]` } ;
use constant SMBCLIENT_QUEUE	=> sub { return `${\SMBCLIENT_PATH} //$_[0]/$_[1] -U $_[2]%$_[3] -c 'queue; quit' 2>/dev/null` } ;

							# The queue results depend on the name resolution method.

							# Forcing 'wins' or 'bcat' name resolution makes the queue results the
							# same for all spoolers (those that are resolved with WINS have an extra line
							# 'Got a positive name query response from <ip address of WINS> ..)
							# but would fail if there is no WINS and when miscreant spoolers
							# don't respond to broadcasts.

use constant MIN		=> sub { my $min = $_[0] ; foreach (@_) { $min = $_ if $_ <= $min; } return $min ; } ;

$SIG{"ALRM"} = sub { die "Alarm clock restart" } ;

Getopt::Long::Configure('bundling', 'no_ignore_case');
GetOptions
        ("V|version"     => \&version,
         "h|help"        => \&help,
         "d|debug"       => \$debug,
         "p|password=s"  => \$opt_p,
         "u|username=s"  => \$opt_u,
         "H|hostname=s"  => \$opt_H);



($opt_H) || usage("MS Spooler name not specified\n");
my $spooler = $1 if $opt_H =~ m#(\w+)# ;		# MS host names allow __any__ characters (more than \w)
($spooler) || usage("Invalid MS spooler name: $opt_H\n");

($opt_u) || ($opt_u = 'guest');
my $user = $1 if $opt_u =~ m#(\w+)# ;
($user) || usage("Invalid user: $opt_u\n");

($opt_p) || ($opt_p = 'guest');
my $pass = $1 if ($opt_p =~ /(.*)/);
($pass) || usage("Invalid password: $opt_p\n");

my ($printer, $queue, @queues, $ms_spooler_status, @results, %junk) ;
my (@fault_messages, @queue_contents, @services, @prandom_queue_indices) ;
my ($queue_contents, $number_of_queues, $state, $queue_report) ;

$state = "getting service list (${\SMBCLIENT_PATH} -L $spooler -U $user%$pass) from spooler\n" ;

eval {
  alarm($TIMEOUT) ;
  @services = SMBCLIENT_SVC->( $spooler, $user, $pass ) ;
} ;
alarm(0) ;

if ($@ and $@ !~ /Alarm clock restart/) {
  print "Failed. $PROGNAME failed $state. Got \"$@\"\n" ;
  exit $ERRORS{"CRITICAL"} ;
}

if ($@ and $@ =~ /Alarm clock restart/) {
  print "Failed. $PROGNAME timed out $state. Got \"@services\"\n" ;
  exit $ERRORS{"CRITICAL"} ;
}

# tsitc> /usr/local/samba/bin/smbclient //ipaprint1/tt03 -U blah%blah -P -c 'queue; quit'
# Added interface ip=10.0.100.252 bcast=10.255.255.255 nmask=255.0.0.0
# Connection to ipaprint1 failed

# tsitc> /usr/local/samba/bin/smbclient -L sna_spl1 -U blah%blah | & more
# Added interface ip=10.0.100.252 bcast=10.255.255.255 nmask=255.0.0.0
# Got a positive name query response from 10.0.100.29 ( 10.0.6.20 )
# session setup failed: ERRDOS - ERRnoaccess (Access denied.)

if ( grep /Connection to $spooler failed|ERR/, @services ) {
  print "Failed. $PROGNAME failed $state. Got \"@services\"\n" ;
  # print "Failed. Request for services list to $spooler failed. Got \"@services\"\n" ;
  exit $ERRORS{"CRITICAL"} ;
}

# tsitc# /usr/local/samba/bin/smbclient -L ipaprint -U blah%blah
# Added interface ip=10.0.100.252 bcast=10.255.255.255 nmask=255.0.0.0
# Domain=[IPAUSTRALIA] OS=[Windows NT 4.0] Server=[NT LAN Manager 4.0]
# 
#         Sharename      Type      Comment
#         ---------      ----      -------
#         TH02           Printer   TH02
#         ADMIN$         Disk      Remote Admin
#         IPC$           IPC       Remote IPC
#         S431           Printer   S431
#         S402           Printer   S402
#         S401           Printer   S401
#         C$             Disk      Default share
#         BW01           Printer   BW01
#         BW02           Printer   BW02
#         TL11           Printer   TL11
#         TL07           Printer   TL07
#         S225           Printer   Discovery South - 2nd Floor - HP CLJ4500
#         S224           Printer   S224
#         S223           Printer   Discovery South 2nd Floor Trademarks Training
#         S222           Printer   S222
#         S203           Printer   S203
#         S202           Printer   S202

my @printers = map  { my @junk = split; $junk[0] }
	       grep { my @junk = split; defined $junk[1] and $junk[1] eq 'Printer' } @services ;
						# don't check IPC$, ADMIN$ etc.

$ms_spooler_status = 0 ;
$number_of_queues = MIN->(MAX_QUEUES_TO_CHECK, (scalar(@services) >> 3) + 1) ;

$state = "checking queues on $spooler" ;

eval {
						# foreach queues to check
						#   generate a pseudo-random int in 0 .. $#printers
						#   drop it if the index has already been generated ;

  %junk = () ;
  @prandom_queue_indices = grep { ! $junk{$_}++ } 
                           map  { int( rand($#printers) ) } ( 1 .. $number_of_queues ) ;

  @queues = @printers[@prandom_queue_indices] ;

  # @queues = @printers[ map { int( rand($#printers) ) } ( 1 .. $number_of_queues ) ] ;

  alarm($TIMEOUT) ;

  @queue_contents = @fault_messages = () ;

  foreach $printer (sort @queues) {

    # Expect 3 lines from a queue report.
    # If queue is empty, last line is null otherwise
    # it will contain a queue report or an SMB error
    
    # Empty Queue.
    # Added interface ip=10.0.100.252 bcast=10.255.255.255 nmask=255.0.0.0
    # Domain=[IPAUSTRALIA] OS=[Windows NT 4.0] Server=[NT LAN Manager 4.0]

    # Queue command from a spooler with a DNS name.  
    # Added interface ip=10.0.100.252 bcast=10.255.255.255 nmask=255.0.0.0
    # Domain=[IPAUSTRALIA] OS=[Windows NT 4.0] Server=[NT LAN Manager 4.0]
    # 65       16307        Microsoft Word - Servicesweoffer2.doc
    # 68       10410        Microsoft Word - Servicesweoffer.doc
    # 143      24997        Microsoft Word - Miss Samantha Anne Craig.doc
    # 182      15635        Microsoft Word - services we provide.doc
  
    # Error.
    # Added interface ip=10.0.100.252 bcast=10.255.255.255 nmask=255.0.0.0
    # Domain=[IPAUSTRALIA] OS=[Windows NT 4.0] Server=[NT LAN Manager 4.0]
    # tree connect failed: ERRDOS - ERRnosuchshare (You specified an invalid share name)
  
    # Can't connect error.
    # Added interface ip=10.0.100.252 bcast=10.255.255.255 nmask=255.0.0.0
    # Connection to sna_spl2 failed
  
  
    # Empty Queue from a spooler with no DNS name, NetBIOS name resolved by WINS.
    # Added interface ip=10.0.100.252 bcast=10.255.255.255 nmask=255.0.0.0
    # Got a positive name query response from 10.0.100.29 ( 10.0.6.20 )
    # Domain=[SNA_PRINT] OS=[Windows NT 4.0] Server=[NT LAN Manager 4.0]
  
    # There are 3 lines of output from smbclient for those spoolers whose names are
    # resolved by WINS (because those names are NetBIOS and therefore not in DNS);
    # 4 lines for errors or enqueued jobs
  
    print STDERR "${\SMBCLIENT_PATH} //$spooler/$printer -U $user%$pass -c 'queue; quit' ==>\n" if $debug ;
  
    @results = SMBCLIENT_QUEUE->( $spooler, $printer, $user, $pass ) ;

    print STDERR "\"@results\"\n" if $debug ;
    
    # set $ms_spooler_status somehow
  
    chomp( @results ) ;
    $queue_report = queue_report->(@results) ;
    print STDERR '$queue_report for $printer ', "$printer: \"$queue_report\"\n\n" if $debug ;
  
    if ( defined $queue_report and ($queue_report !~ /ERR/ && $queue_report !~ /failed/) ) {
      $ms_spooler_status = 1 ;
      push @queue_contents, "$printer: $queue_report" if $queue_report ;
    } else {
      push @fault_messages, "$printer: $queue_report" ;
    }
  }
  alarm(0) ;
} ;

if ($@ and $@ !~ /Alarm clock restart/) {
  print "Failed. $PROGNAME failed at $state. Got \"$@\"\n" ;
  exit $ERRORS{"CRITCAL"} ;
}

if ($@ and $@ =~ /Alarm clock restart/) {
  my $i ;
  foreach (@queues) { $i++ ; last if $_ eq $printer } 
  print "Failed. Timed out connecting to $printer ($i of $number_of_queues) on //$spooler after $TIMEOUT secs. Got \"@fault_messages\"\n" ;
  exit $ERRORS{"CRITICAL"} ;
}
  
if (! $ms_spooler_status) {
  print "Failed. Couldn't connect to @queues on //$spooler as user $user. Got \"@fault_messages\"\n" ;
  exit $ERRORS{"CRITICAL"} ;
}

$queue_contents = ( @queue_contents != 0 ? join(" ", (@queue_contents == 1 ? "Queue" : "Queues"), @queue_contents) :
				           "All Queues empty" ) ;
  
print "Ok. Connected to ", $queue_contents =~ /empty$/ ? "@{[sort @queues]}" : scalar @queues, " queues on //$spooler. $queue_contents\n" ;
exit $ERRORS{"OK"} ;

sub print_usage () {
	print "Usage: $PROGNAME -H <spooler> -u <user> -p <password>\n";
}

sub print_help () {
	print_revision($PROGNAME,'$Revision$ ');
	print "Copyright (c) 2001 Karl DeBisschop/S Hopcroft

Perl Check MS Spooler plugin for NetSaint. Display a subset of the queues on an SMB (Samba or MS) print spooler.

";
	print_usage();
	print '
-H, --hostname=STRING
   NetBIOS name of the SMB Print spooler (Either Samba or MS spooler)
-u, --user=STRING
   Username to log in to server. (Default: "guest")
-p, --password=STRING
   Password to log in to server. (Default: "guest")
-d, --debug
   Debugging output.
-h, --help
   This stuff.

';
	support();
}

sub version () {
	print_revision($PROGNAME,'$Revision$ ');
	exit $ERRORS{'OK'};
}

sub help () {
	print_help();
	exit $ERRORS{'OK'};
}

sub AUTOLOAD {

  my @queue_rep = @_ ;

  # 'Object Oriented Perl', D Conway, p 95

  no strict 'refs' ;

  if ( $AUTOLOAD =~ /.*::queue_report/ ) {

    if ( grep /Got a positive name query response from/, @queue_rep ){
      *{$AUTOLOAD} = sub { return join ' ', splice(@_, 3) } ;
      return join '', splice(@queue_rep, 3) ;
    } else {
      *{$AUTOLOAD} = sub { return join ' ',splice(@_, 2) } ;
      return join '', splice(@queue_rep, 2) ;
    }
  } else {
    die "No such subroutine: $AUTOLOAD" ;
  }
}