node_reboot.in 17.9 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2 3 4

#
# EMULAB-COPYRIGHT
5
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
6 7 8
# All rights reserved.
#

9 10 11 12
use English;
use Getopt::Std;

#
13 14
# Reboot a node (or nodes). Will power cycle the node as a last resort.
# Use -e option to reboot all nodes in an experiment.
15
#
16 17
# Exit value is 0 if all nodes reboot okay, or the number of nodes
# could not be rebooted.
18 19 20
#
sub usage()
{
21 22
    print "Usage: node_reboot [-d] [-f] [-n] [-w] [-k] node [node ...]\n" .
	  "       node_reboot [-d] [-f] [-n] [-w] [-k] -e pid,eid\n".
23
	"Use the -d option to turn on debugging\n" .
24
	"Use the -e option to reboot all the nodes in an experiment\n" .
25
	"Use the -n option to not wait for nodes to go down\n" .
26
	"Use the -w option to to wait for nodes is come back up\n" .
27
	"Use the -k option to power cycle nodes in PXEWAIT mode\n" .
28
	"Use the -a option to reboot all free nodes\n".
29
	"Use the -f option to power cycle (and not wait for nodes to die)\n";
30 31
    exit(-1);
}
32 33
# The hidden -r option runs this in "realmode", ie don't send an event, but
# really do the work instead.
34
my  $optlist = "dfe:nwrka";
35 36 37 38 39

#
# Configure variables
#
my $TB		= "@prefix@";
40
my $CLIENT_BIN  = "@CLIENT_BINDIR@";
41
my $BOSSNODE    = "@BOSSNODE@";
42 43

#
44
# Testbed Support libraries
45
#
46 47 48
use lib "@prefix@/lib";
use libdb;
use libtestbed;
49
use event;
50
use POSIX qw(strftime);
51

Robert Ricci's avatar
Robert Ricci committed
52
my $ssh		= "$TB/bin/sshtb -n";
53
my $power	= "$TB/bin/power";
54
my $ipod	= "$TB/sbin/apod";
55
my $vnodesetup	= "$TB/sbin/vnode_setup";
56
my $bisend      = "$TB/sbin/bootinfosend";
57
my $logfile	= "$TB/log/power.log";
58 59 60 61
my $ping	= "/sbin/ping";
my %pids	= ();
my @row;
my @nodes       = ();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
62
my $debug       = 0;
63
my $force       = 0;
64
my $waitmode    = 0;
65 66
my $realmode    = 0;
my $nowait      = 0;
67
my $failed      = 0;
68
my $eidmode     = 0;
69
my $killmode    = 0;
70
my $freemode    = 0;
71
my $batchcount  = 12;
72 73
my $pid;
my $eid;
74 75 76 77 78 79

# un-taint path
$ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

# Turn off line buffering on output
Mac Newbold's avatar
Mac Newbold committed
80
$| = 1;
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
    die("Must be root! Maybe its a development version?");
}

#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
98 99 100 101
    $debug = 1;
}
if (defined($options{"f"})) {
    $force = 1;
102
}
103 104 105
if (defined($options{"k"})) {
    $killmode = 1;
}
106 107 108
if (defined($options{"w"})) {
    $waitmode = 1;
}
109 110 111
if (defined($options{"r"})) {
    $realmode = 1;
}
112 113 114
if (defined($options{"a"})) {
    $freemode = 1;
}
115 116 117
if (defined($options{"n"}) && !defined($options{"w"})) {
    $nowait = 1;
}
118 119 120 121
if (defined($options{"e"})) {
    if (@ARGV) {
	usage();
    }
122

123 124 125 126
    $eidmode = $options{"e"};
    if ($eidmode =~ /([-\w]*),([-\w]*)/) {
	$pid = $1;
	$eid = $2;
127 128
    }
    else {
129 130
	print STDOUT "Invalid argument to -e option: $eidmode\n";
	usage();
131
    }
132 133
}

134
# XXX Temporary, until we make event sending the default
135 136 137 138
$realmode=1;
#if ($realmode && $UID && !TBAdmin($UID)) {
#    die("*** You cannot use real mode!\n");
#}
139

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
if ($freemode) {
    #
    # Reboot all free nodes
    #
    if ($UID && !TBAdmin($UID)) {
	die("*** You not have permission to reboot all free nodes!\n");
    }

    my $query_result =
	DBQueryFatal("select n.node_id from nodes as n ".
		     "left join reserved as r on r.node_id=n.node_id ".
		     "left join node_types as nt on nt.type=n.type ".
		     "where nt.class='pc' and n.role='testnode' and ".
		     "      r.pid is NULL");

    if ($query_result->numrows == 0) {
	print STDOUT "There are no free nodes to reboot\n";
	usage();
    }
159

160 161 162 163 164 165 166 167 168
    while (@row = $query_result->fetchrow_array()) {
	push(@nodes, $row[0]);
    }
}
elsif ($eidmode) {
    #
    # If eidmode, then get the node list out of the DB instead of the command
    # line. A proper check is made later, so need to be fancy about the query.
    #
169
    my @row;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
170 171 172 173 174 175 176 177

    #
    # Verify permission to muck with this experiment.
    #
    if ($UID && !TBAdmin($UID) &&
	! TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_MODIFY)) {
	die("*** You not have permission to reboot nodes in $pid/$eid!\n");
    }
178

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    my $query_result =
	DBQueryFatal("select node_id from reserved where ".
		     "pid='$pid' and eid='$eid'");

    if ($query_result->numrows == 0) {
	print STDOUT "There are no nodes reserved in pid/eid $pid/$eid\n";
	usage();
    }
    while (@row = $query_result->fetchrow_array()) {
	push(@nodes, $row[0]);
    }
}
else {
    if (@ARGV == 0) {
	usage();
    }
195

196 197 198 199 200 201
    # Untaint the nodes.
    foreach my $node ( @ARGV ) {
	if ($node =~ /^([-\@\w]+)$/) {
	    $node = $1;
	}
	else {
Mac Newbold's avatar
Mac Newbold committed
202 203 204 205
	    die("Bad node name: $node\n");
	}
	if (!TBValidNodeName($node)) {
	    die("Node does not exist: $node\n");
206
	}
207

208 209
	push(@nodes, $node);
    }
210

Leigh B. Stoller's avatar
Leigh B. Stoller committed
211 212 213 214 215 216 217
    #
    # Verify permission to reboot these nodes.
    #
    if ($UID && !TBAdmin($UID) &&
	! TBNodeAccessCheck($UID, TB_NODEACCESS_REBOOT, @nodes)) {
	die("You do not have permission to reboot one (or more) ".
	    "of the nodes!\n");
218 219 220
    }
}

221
#
222 223 224 225
# VIRTNODE HACK: Virtual nodes are special. We can reboot jailed vnodes.
# but not old style (non-jail). Also, if we are going to reboot the physical
# node that a vnode is on, do not bother with rebooting the vnode since
# it will certainly get rebooted anyway!
226
#
227 228 229
my %realnodes = ();
my %virtnodes = ();

230
foreach my $node ( @nodes ) {
231
    my ($jailed, $plab);
232

233 234
    if (TBIsNodeVirtual($node, \$jailed, \$plab)) {
	if (! $jailed && ! $plab) {
235 236 237 238
	    print "*** Skipping old style (non-jail) virtual node $node ...\n";
	    next;
	}
	my $pnode;
239

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
	if (! TBPhysNodeID($node, \$pnode)) {
	    die("*** $0:\n".
		"    No physical node for $node!\n");
	}
	$virtnodes{$node} = $pnode;
    }
    else {
	$realnodes{$node} = $node;
    }
}
for my $node ( keys(%virtnodes) ) {
    my $pnode = $virtnodes{$node};

    if (defined($realnodes{$pnode})) {
	print "*** Dropping $node since its host ($pnode) will reboot ...\n";
	delete($virtnodes{$node});
256 257
    }
}
258
if (! keys(%realnodes) && ! keys(%virtnodes)) {
259
    print "No nodes to reboot. Exiting ...\n";
260 261 262
    exit(0);
}

263 264 265 266 267
#
# By here we've done all the preliminaries... send the event, unless we're
# in realmode.
#

268
my @sortednodes = sort(keys(%realnodes));
269 270 271 272 273 274 275 276

if (!$realmode) {
    EventSendFatal(host      => $BOSSNODE ,
		   objtype   => TBDB_TBEVENT_COMMAND ,
		   eventtype => TBDB_COMMAND_REBOOT ,
		   objname   => join(",",@sortednodes) );
    if (!$nowait) {
	# In here we can do some output to tell the user what's going on.
277 278 279 280 281 282
	if ($waitmode) {
	    # Wait for [SHUTDOWN,ISUP]

	} else {
	    # Wait for [SHUTDOWN]

283 284
	}
    }
285
    exit(0);
286 287
}

288
#
289 290 291
# Another shark hack. Well, perhaps not. We really don't want 50 nodes
# all rebooting at the same time, PCs *or* sharks. Lets order them
# so that the shelves are grouped together at least, and issue the reboots
Mac Newbold's avatar
Mac Newbold committed
292
# in batches.
293
#
294 295 296 297
while (@sortednodes) {
    my @batch = ();
    my $i     = 0;
    my $lastshelf = 0;
298

299
    while ($i < $batchcount && @sortednodes > 0) {
300 301 302 303 304 305 306 307 308 309 310 311 312 313
	my $node = shift(@sortednodes);
	my $shelf;
	my $unit;

	#
	# The point of this sillyness is stop at each shelf transition.
	#
	if (IsShelved($node, \$shelf, \$unit)) {
	    if ($lastshelf && $lastshelf ne $shelf) {
		unshift(@sortednodes, $node);
		last;
	    }
	    $lastshelf = $shelf;
	}
314

315 316 317 318 319 320 321 322 323
	push(@batch, $node);
	$i++;
    }

    if ($force) {
        #
        # In force mode, call the power program for the whole batch, and
	# continue on. We don't wait for them to go down or reboot.
        #
324
	info("Force mode: power cycle ".join(" ",@batch));
325
	PowerCycle(@batch);
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
	if ($?) {
	    exit ($? >> 8);
	}
    }
    else {
        #
        # Fire off a reboot process so that we can overlap them all.
        # We need the pid so we can wait for them all before preceeding.
        #
	foreach my $node ( @batch ) {
	    $mypid = RebootNode($node);
	    $pids{$node} = $mypid;
	}
    }

Mac Newbold's avatar
Mac Newbold committed
341
    #
342 343 344 345 346 347 348 349
    # If there are more nodes to go, then lets pause a bit so that we
    # do not get a flood of machines coming up all at the same exact
    # moment.
    #
    if (@sortednodes) {
	print STDOUT "Pausing to give some nodes time to reboot ...\n";
	if ($lastshelf) {
	    sleep(15);
Mac Newbold's avatar
Mac Newbold committed
350
	} else {
351
	    sleep(10);
Mac Newbold's avatar
Mac Newbold committed
352
	}
353
    }
354 355
}

356
#
357
# Wait for all the reboot children to exit before continuing.
358
#
359
my @needPowercycle = ();
360 361 362
if (! $force) {
    foreach my $node ( sort(keys(%realnodes)) ) {
	my $mypid     = $pids{$node};
363
	my $status;
364

365 366 367 368 369 370 371 372 373 374 375
	#
	# Child may have avoided the fork, and returned status directly.
	# Flip it and apply the same test.
	#
	if ($mypid <= 0) {
	    $status = -$mypid;
	}
	else {
	    waitpid($mypid, 0);
	    $status = $? >> 8;
	}
376

377 378 379 380
	if ($status == 2) {
	    # Child signaled to us that this node needs a power cycle
	    push @needPowercycle, $node;
	} elsif ($?) {
381 382 383 384 385 386 387
	    $failed++;
	    print STDERR "Reboot of node $node failed!\n";
	}
	else {
	    print STDOUT "$node rebooting ...\n";
	}
    }
388 389
}

390 391 392 393 394 395 396
#
# Power cycle nodes that couldn't be brought down any other way
#
if (@needPowercycle) {
    PowerCycle(@needPowercycle);
}

397
#
398
# Now do vnodes. Do these serially for now (simple).
Mac Newbold's avatar
Mac Newbold committed
399
#
400 401
for my $node ( keys(%virtnodes) ) {
    my $pnode = $virtnodes{$node};
402

403
    if (RebootVNode($node, $pnode)) {
404
	$failed++;
405
	print STDERR "Reboot of node $node on $pnode failed!\n";
406 407
    }
    else {
408
	print STDOUT "$node on $pnode rebooting ...\n";
409 410 411
    }
}

412
if ($failed) {
413
    print STDERR "$failed real nodes could not be rebooted\n";
414 415 416 417
    exit($failed);
}

#
Mac Newbold's avatar
Mac Newbold committed
418 419
# Wait for nodes to reboot. We wait only once, no reboots.
#
420 421 422 423 424 425 426 427
if ($waitmode) {
    my $waitstart = time;

    print STDOUT "Waiting for nodes to come up ...\n";

    # Wait for events to filter through stated! If we do not wait, then we
    # could see nodes still in ISUP.
    sleep(2);
428

429 430 431 432 433 434 435 436 437
    foreach my $node ( sort(@nodes) ) {
	if (!TBNodeStateWait($node, TBDB_NODESTATE_ISUP, $waitstart, (60*6))) {
	    print STDOUT "$node is alive and well\n";
	    SetNodeBootStatus($node, NODEBOOTSTATUS_OKAY);
	    next;
	}
	SetNodeBootStatus($node, NODEBOOTSTATUS_FAILED);
	$failed++;
    }
438
}
439
print "Done. There were $failed failures to reboot.\n";
440 441 442 443 444
exit $failed;

#
# Reboot a node in a child process. Return the pid to the parent so
# that it can wait on all the children later.
Mac Newbold's avatar
Mac Newbold committed
445
#
446
sub RebootNode {
447
    my ($pc) = @_;
448
    my ($status, $syspid, $mypid, $didipod, $nodestate);
449 450 451

    print STDOUT "Rebooting $pc ...\n";

Mac Newbold's avatar
Mac Newbold committed
452 453 454
    # Report some activity into last_ext_act
    TBActivityReport($pc);

455 456 457 458 459 460 461 462 463 464 465 466
    #
    # Is the node in PXEWAIT? If so we want to wake it up so that it can
    # query bootinfo and do what it is supposed to do, without a real reboot.
    # We send the initial wakeup from here, but let stated deal with it
    # failing (timeout) and resending it. That means we could be called
    # with the node in PXEWAKEUP, so send it another wakeup. The point is that
    # stated is controlling the timeouts. Eventually stated gives up and uses
    # the -k option to force a power cycle.
    #
    if (! TBGetNodeEventState($pc, \$nodestate)) {
	info("$pc has no event state: power cycle");
	print STDERR "$pc has no event state Power cycling ...\n" if $debug;
467 468
	# Signal to the called that the node needs to be power cycled
	return -2;
469 470 471 472 473 474
    }
    if ($nodestate eq TBDB_NODESTATE_PXEWAIT() ||
	$nodestate eq TBDB_NODESTATE_PXEWAKEUP()) {
	#
	# In killmode, we do not want to bother with sending a wakeup event.
	# Just do the power cycle. This is used to unstick a machine that
475
	# is in waitmode, but not responding to the wakeups.
476 477 478 479 480
	#
	if ($killmode) {
	    info("$pc: in $nodestate: but power cycling in killmode");
	    print STDERR "$pc: in $nodestate: but power cycling in killmode\n"
		if $debug;
481 482
	    # Signal to the caller that the node needs to be power cycled
	    return -2;
483
	}
484

485 486 487 488
	#
	# The aux program sends the event to stated ...
	#
	my $optarg = ($debug ? "-dd" : "");
489

490 491 492 493 494
	print STDERR "$pc: in $nodestate: sending wakeup command.\n" if $debug;
	system("$bisend $optarg -q $pc");
	if ($?) {
	    info("$pc: PXEWAKEUP failed ... power cycle");
	    print STDERR "$pc: PXEWAKEUP failed ... power cycle.\n" if $debug;
495 496
	    # Signal to the caller that the node needs to be power cycled
	    return -2;
497
	}
498 499 500 501 502 503
	return 0;
    }

    #
    # Do the rest in a child process. After the fork, we do the ping test
    # before we reconnect to the DB. This cuts down on the flurry of connects
504
    # by a bunch of children (ping will take a moment to run).
505 506 507 508
    #
    $mypid = fork();
    if ($mypid) {
	return $mypid;
509 510
    }

511 512 513 514
    #
    # See if the machine is pingable. If its not pingable, then we just
    # power cycle the machine rather than wait for ssh to time out.
    #
515
    if (! DoesPing($pc)) {
516
	info("$pc appears dead: power cycle");
517
	print STDERR "$pc appears to be dead. Power cycling ...\n" if $debug;
518 519
	# Signal to the parent that the node needs to be power cycled
	exit(2);
520
    }
521
    TBdbfork();
522 523 524

    #
    # Machine is pingable at least. Try to reboot it gracefully,
Mac Newbold's avatar
Mac Newbold committed
525
    # or power cycle anyway if that does not work.
526
    #
527
    print STDERR "Trying ssh reboot of $pc ...\n" if $debug;
528

529 530 531 532 533
    #
    # Must change our real UID to root so that ssh will work. We save the old
    # UID so that we can restore it after we finish the ssh
    #
    my $oldUID = $UID;
534
#    print STDERR "Saved UID: $oldUID\n" if $debug;
535
    $UID = 0;
536

537 538 539 540
    #
    # Run an ssh command in a child process, protected by an alarm to
    # ensure that the ssh is not hung up forever if the machine is in
    # some funky state.
Mac Newbold's avatar
Mac Newbold committed
541
    #
542
    $syspid = fork();
543

544 545
    if ($syspid) {
	local $SIG{ALRM} = sub { kill("TERM", $syspid); };
546
	alarm 20;
547 548 549 550 551 552 553
	waitpid($syspid, 0);
	alarm 0;

	#
	# The ssh can return non-zero exit status, but still have worked.
	# FreeBSD for example.
	#
554
	print STDERR "reboot of $pc returned $?.\n" if $debug;
555

556
	#
557 558
	# If either ssh is not running or it timed out,
	# send it a ping of death.
Mac Newbold's avatar
Mac Newbold committed
559
	#
560 561 562 563 564 565
	if ($? == 256 || $? == 15) {
	    if ($? == 256) {
		print STDERR "$pc is not running sshd.\n" if $debug;
	    } else {
		print STDERR "$pc is wedged.\n" if $debug;
	    }
566
	    info("$pc: ssh reboot failed ... sending ipod");
567 568
	    print STDERR "Trying Ping-of-Death on $pc ...\n" if $debug;

569
	    system("$ipod $pc");
570
	    $didipod = 1;
571
	} else {
572
	    info("$pc: ssh reboot ($?)");
573
	    $didipod = 0;
574 575 576
	}
    }
    else {
577
	exec("$ssh -host $pc /sbin/reboot");
578 579 580
	exit(0);
    }

581
    #
Mac Newbold's avatar
Mac Newbold committed
582
    # Restore the old UID so that scripts run from this point on get the
583 584 585
    # user's real UID
    #
    $UID = $oldUID;
586
#    print STDERR "Restored UID: $UID\n" if $debug;
587

588 589 590 591 592 593
    #
    # Okay, before we power cycle lets really make sure. We wait a while
    # for it to stop responding to pings, and if it never goes silent,
    # punch the power button.
    #
    if (WaitTillDead($pc) == 0) {
594 595
	my $state = TBDB_NODESTATE_SHUTDOWN;
	TBSetNodeEventState($pc,$state);
596 597
	exit(0);
    }
598

599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
    #
    # Haven't yet tried an ipod, try that and wait again.
    # This further slows down reboot but is probably worth it
    # since this should be a rare case (reboot says it worked but
    # node doesn't reboot) and is vital if the nodes have no
    # power cycle capability to fall back on.
    #
    if (! $didipod) {
	info("$pc: reboot failed ... sending ipod");
	$UID = 0;
	system("$ipod $pc");
	$UID = $oldUID;
	if (WaitTillDead($pc) == 0) {
	    my $state = TBDB_NODESTATE_SHUTDOWN;
	    TBSetNodeEventState($pc,$state);
	    exit(0);
	}
    }

618
    info("$pc: ipod failed ... power cycle");
619
    print STDERR "$pc is still running. Power cycling ...\n" if $debug;
620
    exit(2);
621 622
}

623
#
Mac Newbold's avatar
Mac Newbold committed
624 625
# Reboot a vnode in a child process, and wait for it.
#
626 627 628 629 630 631 632 633 634 635
sub RebootVNode($$) {
    my ($vnode, $pnode) = @_;
    my $syspid;

    print STDOUT "Rebooting $vnode on $pnode ...\n";

    #
    # Run an ssh command in a child process, protected by an alarm to
    # ensure that the ssh is not hung up forever if the machine is in
    # some funky state.
Mac Newbold's avatar
Mac Newbold committed
636
    #
637 638 639 640 641 642 643 644 645 646 647 648 649 650
    $syspid = fork();

    if ($syspid) {
	local $SIG{ALRM} = sub { kill("TERM", $syspid); };
	alarm 20;
	waitpid($syspid, 0);
	alarm 0;
	my $exitstatus = $?;

	#
	# The ssh can return non-zero exit status, but still have worked.
	# FreeBSD for example.
	#
	print STDERR "reboot of $vnode returned $exitstatus.\n" if $debug;
651

652 653
	#
	# Look for setup failure, reported back through ssh.
Mac Newbold's avatar
Mac Newbold committed
654
	#
655 656 657 658 659 660 661 662 663 664 665 666 667 668
	if ($exitstatus) {
	    if ($exitstatus == 256) {
		print STDERR "$pnode is not running sshd.\n" if $debug;
	    }
	    elsif ($exitstatus == 15) {
		print STDERR "$pnode is wedged.\n" if $debug;
	    }
	}
	return($exitstatus);
    }
    #
    # Must change our real UID to root so that ssh will work.
    #
    $UID = 0;
669

670
    exec("$ssh -host $vnode $CLIENT_BIN/vnodesetup -r -j $vnode");
671 672 673
    exit(0);
}

674 675 676 677
#
# Power cycle a PC using the testbed power program.
#
sub PowerCycle {
678 679 680
    my @pcs = @_;

    my $pcstring = join(" ",@pcs);
681

682
    system("$power cycle $pcstring");
683 684 685 686 687
    return $? >> 8;
}

#
# Wait until a machine stops returning ping packets.
Mac Newbold's avatar
Mac Newbold committed
688
#
689
sub WaitTillDead {
690
    my ($pc) = @_;
691 692

    print STDERR "Waiting for $pc to die off\n" if $debug;
693

694 695 696 697 698
    #
    # Sigh, a long ping results in the script waiting until all the
    # packets are sent from all the pings, before it will exit. So,
    # loop doing a bunch of shorter pings.
    #
699 700 701
    for ($i = 0; $i < 30; $i++) {
	if (! DoesPing($pc)) {
	    print STDERR "$pc is rebooting.\n" if $debug;
702 703 704 705 706 707 708
	    return 0;
	}
    }
    print STDERR "$pc is still alive.\n" if $debug;
    return 1;
}

709 710
#
# Returns 1 if host is responding to pings, 0 otherwise
711
# This routine is NOT allowed to do any DB queries!
712 713
#
sub DoesPing {
714 715 716
    my ($pc) = @_;
    my $status;
    my $saveuid;
717 718 719 720 721 722 723 724

    $saveuid = $UID;
    $UID = 0;
    system("$ping -q -i 0.25 -c 8 -t 2 $pc >/dev/null 2>&1");
    $UID = $saveuid;
    $status = $? >> 8;

    #
725
    # Ping returns 0 if any packets are returned. Returns 2 if pingable
726 727
    # but no packets are returned. Other non-zero error codes indicate
    # other problems.  Any non-zero return indicates "not pingable" to us.
Mac Newbold's avatar
Mac Newbold committed
728
    #
729 730 731 732 733 734
    print STDERR "$ping $pc returned $status\n" if $debug;
    if ($status) {
	return 0;
    }
    return 1;
}
735

736

737 738 739 740 741 742 743 744
sub info($) {
    my $message = shift;
    # Print out log entries like this:
    # Sep 20 09:36:00 $message
    open(LOG,">> $logfile");
    print LOG strftime("%b %e %H:%M:%S",localtime)." $message\n";
    close(LOG);
}
745