# foreachindomain.pl V0.9
#
# do something for every machine in your domain
#
# (C) 1998 by Alexander Frink
#
# the Resource Kit tools BROWSTAT, NETSVC and SOON must be in your path
# you must be Administrator on all machines in the domain
#
# adjust what has to be done in sub job {} below
# the demo-job does the following:
#       - start the schedule service on the remote machine (if not already
#         running)
#       - synchronize time with the remote machine (needed for the SOON
#         command)
#       - connect x: to \\machine\c$
#       - copy c:\scripts\testjob.bat to x:\temp (i.e. c:\temp on the remote
#         machine)
#       - schedule testjob.bat within 5 seconds on the remote machine
#       - wait 10 seconds for the job to complete
#       - type the output of x:\temp\testjob.log on the screen
#       - copy x:\temp\testjob.log to c:\scripts\testjob.log.machine
#       - disconnect x:
#       - stop the schedule service if it was started 
#
# to do:
#       - modify the variables $WaitTime to $RemotePath (not $RemoteShare)
#         below
#       - write a batch file (e.g. dir c:\)
#       - adjust all lines if (!($res =~ /something/)) { } if you do not use 
#         an English NT version (or comment these lines out)
#       - test your job on a single machine (see 'to test your job on a
#         single machine...' below)
#
# run this script with 'perl foreachindomain.pl 2>&1 > foreach.log'
# to examine the output

# name of the job to execute (without path)
$JobFile="testjob.bat";

# name of a log file which receives the output of the job (without path)
$JobLogFile="testjob.log";

# time in seconds to wait for the job to complete (if you do not wait long
# enough, the job will still finish, but the log file is incomplete)
$WaitTime=10;

# where the job is found and output will be copied to
$LocalPath="c:\\scripts";

# an unused drive letter which is used to connect the remote machine (without :)
$LocalNetDrive="x";

# the remote drive letter where the script will execute (without :)
$RemoteDrive="c";

# path where the script will be executed remotely (without drive letter)
$RemotePath="\\temp";

# remote share, MUST EXIST! (if not, check HKEY_LOCAL_MACHINE\
# System\CurrentControlSet\Services\LanmanServer\Parameters for a
# value AutoShareServer/Wks resp., or create it with RMTSHARE in job{}
$RemoteShare="$RemoteDrive\$";

sub job {
    # the machine name including \\ is passed to $machine,
    # without \\ to $machine_short

    my ($machine, $machine_short)=@_;
    my ($ScheduleStartedManually,$res);

    print "processing $machine_short\n";

    # start the scheduler service for AT/SOON commands if necessary
    $ScheduleStartedManually=StartSchedule($machine);
    if ($ScheduleStartedManually==2) {
        print "Cannot start Schedule, exiting for this machine.\n";
        return;
    }

    # synchronize the time for the SOON command
    $res=`net time $machine /set /y`;
    if (!($res =~ /successfully/)) {
        print "Cannot sync time, exiting for this machine.\n";
        return;
    }

    # connect drive c: to copy the command to execute
    # make sure the network drive letter is unused
    system "net use $LocalNetDrive: /del 2> NUL:";
    $res=`net use $LocalNetDrive: $machine\\$RemoteShare`;
    if (!($res =~ /successfully/)) {
        print "Cannot connect to $machine\\$RemoteShare, exiting for this machine: $res\n";
        return;
    }
    $res=`copy $LocalPath\\$JobFile $LocalNetDrive:$RemotePath`;
    if (!($res =~ /1 file\(s\) copied/)) {
        print "Cannot copy job to $LocalNetDrive:$RemotePath, exiting for this machine: $res.\n";
        return;
    }

    # execute your command
    $res=`soon $machine 5 \"cmd /c \"\" $RemoteDrive:$RemotePath\\$JobFile 2>&1 > $RemoteDrive:$RemotePath\\$JobLogFile \"\"\"`;
    if (!($res =~ /Added a new job/)) {
        print "Cannot schedule job, exiting for this machine: $res.\n";
        return;
    }

    # wait several seconds, then show the result and save it
    sleep $WaitTime;
    system "type $LocalNetDrive:$RemotePath\\$JobLogFile";
    $res=`copy $LocalNetDrive:$RemotePath\\$JobLogFile $LocalPath\\$JobLogFile.$machine_short`;
    if (!($res =~ /1 file\(s\) copied/)) {
        print "Cannot copy logfile back, exiting for this machine: $res.\n";
        return;
    }

    # disconnect net drive
    system "net use $LocalNetDrive: /del 2>&1 > NUL:";

    # stop the schedule service if started manually
    StopSchedule($machine,$ScheduleStartedManually);
}    

# to test your job on a single machine, uncomment the next two lines
#job("\\\\bibserver","bibserver");
#exit(0);

# determine the network transport
# 3rd line of BROWSTAT STATUS looks like
#    Status for domain YOURDOMAIN on transport \Device\NetBT_NE20001
# transport would be NETBT_NE20001
open IN,"browstat status |" or die "can't open BROWSTAT STATUS output";
for ($i=0; $i<3; $i++) {
    $_=<IN>;
}
if (/\\Device\\(.*)$/) {
    $transport=$1;
    print "transport is $transport.\n";
} else {
    die "can't determine your transport";
}
close IN;

# pipe the output of 'browstat' as input to the perl script
open IN,"browstat view $transport |" or die "can't open BROWSTAT VIEW output";

# skip first 6 lines
for ($i=0; $i<6; $i++) {
    $_=<IN>;
}

# next lines contain the machines in your domain (plus other information)
# machine name may not contain spaces
while (<IN>) {
    if (/^(\\\\([^ ]*)) /) {
        $machine=$1;
        $machine_short=$2;
    } else {
        die "output doesn't match \\\\machine + space";
    }    
    job($machine,$machine_short);
}

close IN;

sub StartSchedule {
    my ($machine)=@_;
    my ($ScheduleRunning,$ScheduleStartedManually,$error,$res);
 
    $error=0;
    $ScheduleStartedManually=0;
    $ScheduleRunning=0;
    while ((!$ScheduleRunning)&&(!$error)) {
        $res=`netsvc schedule $machine /query`;
        if ($res =~ /running/) {
            # Schedule is running, nothing to do
            print "Schedule is running.\n";
            $ScheduleRunning=1;
        } elsif ($res =~ /stopped/) {
            # Schedule is stopped, manually start it
            print "Schedule is stopped, trying to start\n";
            $res=`netsvc schedule $machine /start`;
            if (!($res =~ /running/ or $res =~ /pending/)) {
                print "cannot start Schedule service: $res\n";
                $error=1;
            } else {
                $ScheduleStartedManually=1;
                sleep 3; # wait 3 seconds, try again
            }
        } elsif ($res =~ /pending/) {
            print "Schedule is pending, trying again\n";
            sleep 3; # wait 3 seconds, try again
        } else {
            print "cannot evaluate Schedule status: $res\n";
            $error=1;
        }
    }
    if ($error) {
        return 2;
    }
    return $ScheduleStartedManually;
}

sub StopSchedule {
    my ($machine,$ScheduleStartedManually)=@_;

    if ($ScheduleStartedManually) {
        system "netsvc schedule $machine /stop";
    }
}


