2004年05月16日

waitfor コマンド

Waitfor コマンドは Windows Server 2003 からの新しいコマンドで、シグナルを用いて複数のコンピュータの同期をとることができる。

構文1:シグナルを待つ
waitfor [/t TimeoutInSeconds] SignalName
SignalName という名のシグナルを待つ。シグナルが来るか、TimeoutInSeconds 秒経過するとこのコマンドは終了する。「/t TimeoutInSeconds」を省略したときは無期限にシグナルを待機し続ける。

構文2:シグナルを送る
waitfor [/s Computer [/u [Domain\]User [/p [Password]]]] /si SignalName
/s シグナルの送り先コンピュータ名またはIPを指定。規定値はローカルコンピュータ。
/si 送るシグナルの名前を指定。
/u 指定したユーザの権限で実行。 /p でパスワードも指定する。

これを使うと複数のバッチを同期させたり、複数のコンピュータの同期をバッチで行うことが可能である。
以前は同様のことをしたいときは、テンポラリのファイルを作成するなどして実現していたと聞く。 waitfor コマンドを使うと、ゴミも残らず、タイムアウトも指定できるなど利点が大きい。


例:
comp1 で foo1.bat, comp2 で foo2.bat を実行する。 foo2.bat の終了を foo1.bat の中で待つ。

##### comp1 / foo1.bat #####
echo comp2 の foo2.bat を待ちます。
waitfor foosignal
echo comp2 の foo2.bat は終わったようだ。

##### comp2 / foo2.bat #####
echo foo2.bat がスタートします。
////// 時間のかかる仕事 //////
waitfor /s comp1 /si foosignal

投稿者 augustus : 19:33 | コメント (0) | トラックバック

2004年04月03日

新しいユーザへ連絡すべきこと

人事異動の時期である。私の勤務先(学校)にも新しいユーザが何人も入ってきた。

職員の人数に対してPCの数が少ないので、みんなで何台かのPCを共用している。職員室LANでのユーザ名と初期パスワードを書いた紙を渡しながらいくつか説明した。

言い忘れたこともある。

パスワード管理の重要性をユーザに教育するのはなかなか大変で苦労しているadmin諸兄も多いと思う。(いいアイデアをお持ちの方は教えていただけると嬉しい。)

投稿者 augustus : 08:26 | コメント (0) | トラックバック

2004年03月06日

スタートアップスクリプトではUNC名が使えない

Windows 2000 や XP では、起動時に実行させるスタートアップスクリプトというのが使えるが、その中ではファイル名の指定にUNC名(\\server1\aaa\bbb\xxx.txt みたいな表現)が使えない。

なぜいまさら気がついたかと言うと、クライアントPCの情報をスタートアップスクリプトを使ってサーバに集めようと考えたのだが、標準出力をサーバ上のファイルにリダイレクトしようとして見事にはまってしまったのだった。スタートアップスクリプト中でリモートのファイルにアクセスしたいとき、世のシステム管理者の皆さんはどうしているのだろうかと疑問に思った。(クライアントPCのローカルのファイルを使うしかないのだろうか。)

もっと詳しく状況を書くと、各PCの ipconfig /all の出力結果を集めたいと考えており、そのためにスタートアップスクリプトを使い標準出力をサーバ上のファイルにリダイレクトしたかったのだ。
現在考えている解決策は
1)クライアントPCのローカルドライブに出力を保存させておき、適当なときに Wake up on LAN を使って電源を入れてやってから、サーバに当該ファイルを集めてくる。
2)Windows XP からのコマンドラインツールである eventcreate を使いサーバのイベントログに必要な情報を書き込ませる。
3)スタートアップスクリプトのかわりにログオンスクリプトを使う。

投稿者 augustus : 21:11 | コメント (0) | トラックバック

2004年02月22日

Wake up on LAN の実験

管理しているPCが離れたところにあったり、数が多かったりすると、電源を入れるだけでも結構大変である。多くの作業を自動化するように工夫していて、電源を入れるところだけは手動というのも悔しい。
そういうときに使えるのが Wake up on LAN。文字通り LAN 経由で、PCを起こしてくれる。

方法としては、マジックパケットと呼ばれるパケットをターゲットのPCに届けてやれば良いらしい。
http://www3.jpn.hp.com/CPO_TC/pc/doc/34602.pdf によるとマジックパケットというのは、6個の FF(16進)のあと、MACアドレスを16回以上繰り返したものだそうである。

Perl によるスクリプトがhttp://adlib.rsch.tuis.ac.jp/~akira/unix/にあった。

自分用に書き直したのが、以下のスクリプト。複数のPCをターゲットにするよう書きかえるのも簡単である。(MAC address と Broadcast IP address を書き換えてから試してみてほしい。)
use Socket;
#
# wake up on LAN
#

# ターゲットの MAC address と Broadcast IP address
$ret=wakeup_on_lan("00-11-22-33-44-55", "10.255.255.255");
print $ret if $ret;

#
# wakeup_on_lan(MACアドレス, ブロードキャストアドレス)
# エラーのときはエラーの内容を示す文字列を返す。
# エラーでないときは空文字列を返す。
sub wakeup_on_lan{ 
    my($mac_address, $broadcast_address)=@_;
    my($macstr, $remote_port,$proto,$iaddr,$paddr,$pac);
    # MAC address decode
    $mac_address=~s/-//g;
    $macstr=pack("H12", $mac_address);
    # Magic Packet create
    $pac = "\xff\xff\xff\xff\xff\xff";
    for($i=0; $i<20;$i++) {
        $pac .=$macstr;
    }
    # open socket
    $remote_port="7";
    $proto=getprotobyname('udp');
    socket(S, PF_INET, SOCK_DGRAM, $proto) 
        or return "ERROR at open socket: $!";
    setsockopt(S, SOL_SOCKET, SO_BROADCAST,1) 
        or return "ERROR at sockopt: $!";
    # send Magic Packet
    $iaddr=inet_aton($broadcast_address) 
        or return "ERROR at get broadcast address: $!";
    $paddr=sockaddr_in($remote_port,$iaddr) 
        or return "ERROR at make broadcast: $!";
    send(S,$pac,0,$paddr) 
        or return "ERROR at send broadcast:$!";
    return("");
}

マジックパケットの送り先が1台なのにブロードキャストを使うには理由がある。 届きさえすれば、ブロードキャストでなくてもよいのだが、arp テーブルがクリアされたりしていると届かない。ブロードキャストを使えば、そういうことは気にしなくて良くなる。
UDP の port 7 を使っているが、実験してみると port 番号は適当で良いようだ。

ターゲットの MAC アドレスが必要になるが、電源が入っているときに ping を飛ばしてから arp コマンドを使って、arp -a とやれば簡単に調べられる。

がんばれば、LAN だけでなくインターネット経由で Wake up on LAN を行うこともできるらしい。
http://bb.watch.impress.co.jp/column/shimizu/2003/09/16/

投稿者 augustus : 05:54 | コメント (0)

2004年02月21日

Perl の Net::Ping モジュール

ping といえばネットワーク的に繋がっているかどうかを確かめるための定番ツールだが、Net::Ping モジュールはそれと同様の機能をもつ Perl のモジュールである。

例えば、 192.168.0.1 に ping を飛ばして返事があれば、何かの処理をする例はこんな感じ。
use Net::Ping;
$host="192.168.0.1";
$timeout=0.1;
$p = Net::Ping->new("icmp");
if $p->ping($host, $timeout){
	print "$host is alive.\n";
}
$p->close();


もうちょっと複雑にしてみる。次は
192.168.0.11 から 192.168.0.40 の範囲に ping を飛ばしてから arp コマンドによって mac address を取得するスクリプト。
use Net::Ping;
$timeout=0.1;
$p = Net::Ping->new("icmp");
@host=map("192.168.0.$_", 11..41);
for $host (@host){
    $p->ping($host, $timeout);
}
$p->close();
open(CMD, "arp -a|") or die "cannot exec arp";
print grep($_!~/invalid/, <CMD>);
close(CMD);

投稿者 augustus : 12:05 | コメント (0) | トラックバック

2004年02月14日

フォルダリダイレクトに必要なNTFSアクセス権

1月に学校のシステムの大幅入替があって、Windows NT 4.0 から Windows 2003 Server + XP professional に変えている。セットアップ等は業者さんがやってくれているのだが、まだいろいろ不具合が残っている。

昨日何気なくクライアントPCのイベントログを覗いてみると、Application Data フォルダーのリダイレクトに失敗したというエラーの山。
試行錯誤の結果分かったのだが、Application Data フォルダーの該当ユーザのアクセス権がフルコントロールでないとエラーになるようだ。フォルダリダイレクトのためには共有のアクセス権にフルコントロールが必要なことは知っていたのだが、NTFSのアクセス権もフルコントロールが必要だったとは知らなかった。
各ユーザの Application Data フォルダーのアクセス権を cacls.exe を使って書換えるスクリプトを用意すれば解決するだろう。

家に帰ってから調べてみるとマイクロソフトのページにちゃんと書いてあった。

イベントログにはこれに関連した気持ち悪い警告が残っている。これも上述のマイクロソフトのページに書いてあることで解決すればいいなあ。

投稿者 augustus : 08:51 | コメント (2) | トラックバック

2004年01月31日

プロセスの実行の防止

多くの学校では、生徒がフロッピーディスクにまずいプログラムを入れてきて実行することがあると思う。フロッピーディスクだから、NTFSのアクセス権でなんとかできる問題ではない。
Microsoft の TechNet にプロセスの実行の防止という記事があった。WSHからWMIを使って、プロセスが起動するたびにその名前をチェックして条件に合えばプロセスを kill する。

それを perl に移してみたのが以下のもの(Windows XP 用) 。
A:ドライブと E:ドライブの実行ファイルが起動されるとそのプロセスが kill される。フロッピーディスクだけでなく、CD-ROMドライブや、ユーザのホームディレクトリからの実行を防止するように改造するのは簡単だろう。
use Win32::OLE;
$strComputer=".";
$wmi = Win32::OLE->GetObject(
  "WinMgmts:{impersonationLevel=impersonate}!//".
  "$strComputer\\root\\cimv2"
) or die;
$colMonitoredProc = $wmi->ExecNotificationQuery(
  "select * from __instancecreationevent within 1".
  " where TargetInstance isa 'Win32_Process'"
);
while(1){
  $objLatestProcess = $colMonitoredProc->{NextEvent};
  $proc_name=$objLatestProcess->TargetInstance->Name();
  if($proc_name=~/a:/ or $proc_name=~/e:/){
    $objLatestProcess->TargetInstance->Terminate();
  }
}

上記のスクリプトだとコマンドプロンプトが開いてしまうので、別のプロセスからウィンドウを開かないようにして起動するのが使いやすいかも。
use Win32;
use Win32::OLE;
use Win32::Process;

if($ARGV[0] eq "-eXecute"){
  shift;
  procchk(@ARGV);
}else{
  my $path=Win32::ExpandEnvironmentStrings("%PATH%");
  ($perl_path)=grep(/perl/i, split(/;/,$path));
  $perl_path=~s/\\$//;
  $perl_path.="\\perl.exe";
  $perl_path="" if(! -e $perl_path);
  $script_path=Win32::GetFullPathName($0);
  
  ($strComputer, @inhibit)=@ARGV;
  $strComputer ||= ".";
  @inhibit = qw/a:\ z:\ e:\ install/ if(! @inhibit);
  Win32::Process::Create($ProcessObj, $perl_path,
    $perl_path.' "'.$script_path.
    "\" -eXecute $strComputer @inhibit",
    0, NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW, "."
  )|| die ErrorReport();
}

sub ErrorReport{
  die;
}

sub procchk{
  my ($strComputer, @inhibit)=@_;
  return if $inhibit[0] eq "";
  grep(s/\\/\\\\/g,@inhibit);
  $check_str=join(" or ",map("\$_[0]=~/$_/i",@inhibit));
  eval("\$check_name = sub {$check_str}");

  $wmi = Win32::OLE->GetObject(
    "WinMgmts:{impersonationLevel=impersonate}!//"."
    $strComputer\\root\\cimv2"
  );
  $colMonitoredProc = $wmi->ExecNotificationQuery(
    "select * from __instancecreationevent within 1 ".
    "where TargetInstance isa 'Win32_Process'"
  );
  while(1){
    $objLatestProcess = $colMonitoredProc->{NextEvent};
    $proc_name=
      $objLatestProcess->TargetInstance->Name();
    if($check_name->($proc_name)){
      $objLatestProcess->TargetInstance->Terminate();
    }
  }
}

投稿者 augustus : 21:29 | コメント (0) | トラックバック

2004年01月28日

条件に合うユーザを一括でグループに加える

Windows 2003 server で条件に合うユーザを一括であるグループに加えたいという場面に昨日遭遇した。
具体的には abc.local(仮名) というドメインに所属している ログオンIDが zt で始まるユーザを全て seito(仮名)というグループに加えるという仕事。


条件に合うユーザを捜すには dsquery user が使える。
dsquery user -samid zt*

グループに加えるには dsmod group が使える
dsmod group "CN=seito,CN=Users,OU=abc,OU=local" -addmbr メンバーの識別名
(メンバーの識別名を省略すると標準入力から読み込まれる)

以上を組み合わせて完成品は
dsquery user -samid zt* | dsmod group "CN=teiji,CN=Users,OU=abc,OU=local" -addmbr
(一行で入力)

本校の環境では ログオンID が zt から始まるユーザは全て teiji グループに所属しているので、「特定のグループのメンバーを全て、別のあるグループに所属させる」という方針でもうまく行くと思う。
dsquery group -samid teiji | dsqet group -members | dsmod group "CN=seito,CN=Users,OU=abc,OU=local" -addmbr
(一行で入力)

詳しくは ディレクトリ サービスのコマンド ライン ツール などを参照されたい。

投稿者 augustus : 06:24 | コメント (0) | トラックバック

2004年01月21日

cacls.exe が力不足のとき

NTFS のアクセス権をコマンドラインから変更するツールとして、Windows NT, 2000, XP, 2003 server などでは標準で cacls.exe がある。ところが、この cacls.exe はアクセス権の種類として、フルコントロール、変更、書込、読込しか与えることができず、細かい設定ができない。

もう少し細かく設定したいときには
unicacls.exe
xcacls.exe を使うとよい。

<<< unicacls の使用例 >>>
XXX という名のフォルダーにseitoグループはファイルを作成でき、自分の作ったファイルは変更可能で、他人の作ったファイルの中身は覗けないという設定
unicacls XXX /T /E /G seito:rwx- "Creator Owner":C system:F administrators:F
要は、フォルダーとサブフォルダーに対して書込の権利を与えて、ファイルに対しては権限を与えない。そのままだと、自分で作ったファイルも編集できないので Creator Owner グループを使うことを考えた。

投稿者 augustus : 02:55 | コメント (0) | トラックバック

2004年01月18日

Explorer のオプション

エクスプローラのオプションを調べようとして、
explorer /?
とやってみると、
パス '/?'は存在しないか、ディレクトリではありません。
と答えられてしまった。しかたがないので、googleさんに尋ねて調べたのが、以下の結果。

書式:
EXPLORER.EXE [/N] [/E] [, /ROOT, <オブジェクト>]
[, /SELECT, <サブ オブジェクト>]

オプション:
/N   新しいウィンドウでマイコンピュータ風の表示
/E   エクスプローラ風(左側にフォルダをツリー表示)。
/ROOT   開くウィンドウのルートを指定。
/SELECT   指定したオブジェクトを選択した状態で開く。

例:
explorer /e,/root,c:\
explorer /e,/select,"C:\program files\accessories"

実験してみると、select と root を両方指定したときは挙動がおかしいようだ。

参照:
マイクロソフト サポート技術情報 - 130510
W95:エクスプローラ起動時に使用できるオプション スイッチ

投稿者 augustus : 18:10 | コメント (0) | トラックバック

2004年01月15日

NTFSアクセス権も含めてファイルをコピーする方法

NTFS上でアクセス権も含めてそのままファイルやフォルダをコピーするにはどうしたらよいのだろうか。このTipsのキーワードは「xcopy」コマンドに「/O」オプションを付けて実行することだ。

NTFSアクセス権も含めてファイルをコピーする方法
Windows 2000 ヒント集

日経Windows pro windows 2003 server 特集

投稿者 augustus : 18:09 | コメント (0) | トラックバック

Windows 2000リソースキットのSubinacl.exe

某メーリングリストでファイルの所有権を変更する方法が質問されていた。それに対する答えが subinacl.

マイクロソフト サポート技術情報 - 265360
コマンド ラインを使用して複数のサブディレクトリのアクセス許可を編集する


##### Subinacl.exe ユーティリティを使用してサブディレクトリの所有権を変更する例
subinacl /subdirectories c:\winnt\profiles\*.* /setowner=administrator

投稿者 augustus : 18:02 | コメント (1) | トラックバック

dsquery

Windows 2003 server からの新しいコマンド。
例えば、ドメインのユーザを列挙したいとき、 dsquery user とすると DN名が出力される。
うちの学校のドメインでは、
dsquery user -desc *2-01-01* |dsget user とやると 2年1組1番の生徒のユーザ情報を検索できる。
これは便利だ。


Microsoft TechNet - Dsquery

投稿者 augustus : 16:14 | コメント (0) | トラックバック