[PS1]「シェル・ワンライナー 160 本ノック」第1章を Powershell でやってみた

2023年2月25日

 「PowerShellワンライナーで解く『シェル・ワンライナー160本ノック』第1章」という記事をみたので、つられてやってみました(ぇ

 書籍情報はここ。問題自体は書籍を参照してください。
 問題に使われているデータはこちらで提供されています。

 また、このページではワンライナーを「改行がないこと」「複文としないこと(";" で区切らないこと)」とします。(一般的な定義がどうだかは知りません)
 そのうえで、個人的にワンライナーは好きではないので、以下の解は基本的にワンライナーで書いていません(汗 普通に改行やインデントを使用しています。

  1. 問1

     正規表現で Match させた例。

    gc .\files.txt |
    ? {
        $_ -match "\.exe$"
    }
    

     拡張子を求めて比較した例。

    gc .\files.txt |
    ? {
        [System.IO.Path]::GetExtension($_) -eq ".exe"
    }
    
  2. 問2

    Add-Type -AssemblyName System.Drawing
    gci *.jpg |
    % {
        $image = [System.Drawing.Image]::FromFile($_.fullname)
        $newname = [System.IO.Path]::ChangeExtension($_.fullname, "png")
        $image.Save($newname, [System.Drawing.Imaging.ImageFormat]::Png)
    }
    

     下記は、上記の変数部分を展開し、ワンライナーにしてみた例です。(";" を使わない例/改行は読みにくくなるので削っていませんが、削っても動きます。ただし、Add-Type の行はどうもならないのでここだけは ";" で区切っています。)
     問11 の例を除いて、このページの解答例は、同様の手法(変数部分の展開や;区切り、文字列の連結など)でワンライナーにすることができます。

    Add-Type -AssemblyName System.Drawing;
    gci *.jpg |
    % {
        [System.Drawing.Image]::FromFile($_.fullname).Save([System.IO.Path]::ChangeExtension($_.fullname, "png"), [System.Drawing.Imaging.ImageFormat]::Png)
    }
    
  3. 問3

     書式を "0000" とした例。

    gci |
    % {
        $newname = "{0:0000}" -f [int]$_.Name
        ren $_ $newname
    }
    

     書式を "D4"(桁数を数値で指定)とした例。

    gci |
    % {
        $newname = "{0:D4}" -f [int]$_.Name
        ren $_ $newname
    }
    
  4. 問4
    gci |
    ? {
        [int](gc -raw $_) -eq 10
    } | 
    % {
        del $_
    }
    
  5. 問5
    gc .\ntp.conf | 
    % {
        if ($_ -match "^pool (.+ )") {
            $Matches[1]
        }
    } 
    
  6. 問6

     スペースの数を書式で指定した例。

    @(5..1) |
    % {
        "{0,$_}" -f "x"
    }
    
  7. 問7

     1.1 や 1.08 は二進小数(= 浮動小数点型)では無限小数となるので、Decimal を使用しています。

     あと、-split した配列を "," 演算子を使用して配列のままパイプに流しています。

    (
        gc .\kakeibo.txt | 
        % {
            ,($_ -split " ")
        } |
        % {
            if ($_[1] -match "\*" -or $_[0] -lt "20191001") {
                [decimal]$_[2] * 1.08d
            } else {
                [decimal]$_[2] * 1.1d
            }
        } |
        Measure -Sum
    ).Sum
    
  8. 問8

     時刻文字列が年と時の間に ":" が入っているので、[DateTime]::ParseExact を使用し、時刻の書式を指定しています。

     ちなみに夜の 12 時 (AM12 時)、昼の 12 時 (PM12 時)はともに午前です(ぇ
     参考「質問4-1)正午は午前12時?それとも、午後12時?

    gc .\access.log |
    % {
        if ($_ -match "\[(.+)\]$") {
    
            $date = [DateTime]::ParseExact($Matches[1],"dd/MMM/yyyy:HH:mm:ss zzz",[System.Globalization.CultureInfo]::InvariantCulture)
    
            if ($date.Hour -le 12) {
                "午前"
            } else {
                "午後"
            }
        }
    } |
    group |
    select Name, Count
    
  9. 問9

     時刻文字列が年と時の間に ":" が入っていない一般的な形式なので、Get-Date -Date を使用し、時刻の書式は指定していません。

    gc .\log_range.log |
    ? {
        if ($_ -match "\[(.+)\]") {
            $from = Get-Date -Date "2016/12/24 21:00:00"
            $to = Get-Date -Date "2016/12/25 4:00:00"
            $date = Get-Date -Date $Matches[1]
    
            ($from -le $date) -and ($date -lt $to)
        }
    }
    
  10. 問10
    gc .\headings.md |
    % {
        if ($_ -match "# (.+)$") {
            $Matches[1]
            "==="
    
        } elseif ($_ -match "## (.+)$") {
            $Matches[1]
            "---"
    
        } else {
            $_
    
        } 
    }
    
  11. 問11

     この例は唯一ワンライナーに書き換えることができません。

    $names = @{};
    $names["すず"] = "鈴木";
    $names["さと"] = "佐藤";
    $names["やま"] = "山本";
    
    $gijiroku = gc .\gijiroku.txt;
    
    for ($i = 0; $i -lt $gijiroku.Length; $i += 3)
    {
        $name = $gijiroku[$i]
        $text = $gijiroku[$i + 1]
        "{0}: {1}`n" -f $names[$name], $text
    }
    

     ワンライナーにこだわるなら以下のようにも書けますし、ある意味上記よりこちらのほうがシンプルではあります。
     しかし個人的に読解性/メンテ性の観点から好きではありません。

    gc -Raw .\gijiroku.txt |
    % {$_ -replace "(^|`r?`n)さと`r?`n","`n佐藤:"} |
    % {$_ -replace "(^|`r?`n)すず`r?`n","`n鈴木:"} |
    % {$_ -replace "(^|`r?`n)やま`r?`n","`n山本:"}
    

[CiscoIOS] VLAN (6) VLAN ルーティング

2021年6月17日

 VLAN セグメントをつなぎます。(L3 スイッチ)

  1. まず VLAN に IP アドレスを与えます。

     今回は VLAN ルーティングを ESW1 で行うことにします。よって作業は ESW1 で行います。
     ESW2 では作業しません。

    ESW1#conf t
    
    ESW1(config)#interface vlan 10
    ESW1(config-if)#ip address 192.168.1.254 255.255.255.0
    ESW1(config-if)#exit
    
    ESW1(config)#interface vlan 20
    ESW1(config-if)#ip address 192.168.2.254 255.255.255.0
    ESW1(config-if)#exit
    
    ESW1(config)#interface vlan 30
    ESW1(config-if)#ip address 192.168.3.254 255.255.255.0
    ESW1(config-if)#exit
    ESW1(config)#exit
    

     VLAN に IP アドレスが設定されたかを確認します。

    ESW1#show ip interface brief
    Interface                  IP-Address      OK? Method Status                Protocol
    FastEthernet0/0            unassigned      YES NVRAM  administratively down down    
    FastEthernet0/1            unassigned      YES NVRAM  administratively down down    
    FastEthernet1/0            unassigned      YES unset  up                    up      
    FastEthernet1/1            unassigned      YES unset  up                    up      
    FastEthernet1/2            unassigned      YES unset  up                    down    
    FastEthernet1/3            unassigned      YES unset  up                    down    
    FastEthernet1/4            unassigned      YES unset  up                    up      
    FastEthernet1/5            unassigned      YES unset  up                    up      
    FastEthernet1/6            unassigned      YES unset  up                    down    
    FastEthernet1/7            unassigned      YES unset  up                    down    
    FastEthernet1/8            unassigned      YES unset  up                    down    
    FastEthernet1/9            unassigned      YES unset  up                    down    
    FastEthernet1/10           unassigned      YES unset  up                    down    
    FastEthernet1/11           unassigned      YES unset  up                    down    
    FastEthernet1/12           unassigned      YES unset  up                    down    
    FastEthernet1/13           unassigned      YES unset  up                    down    
    FastEthernet1/14           unassigned      YES unset  up                    down    
    FastEthernet1/15           unassigned      YES unset  up                    up      
    Vlan1                      unassigned      YES NVRAM  administratively down down    
    Vlan10                     192.168.1.254   YES NVRAM  up                    up
    Vlan20                     192.168.2.254   YES NVRAM  up                    up
    Vlan30                     192.168.3.254   YES NVRAM  up                    up
    
  2. 次にルーティングを有効にします。

     まず、show ip route コマンドを実行し、ルーティングが無効であることを確認します。

    ESW1#show ip route
    Default gateway is not set
    
    Host               Gateway           Last Use    Total Uses  Interface
    ICMP redirect cache is empty
    

     ip routing コマンドを実行し、ルーティングを有効にします。

    ESW1#conf t
    ESW1(config)#ip routing
    ESW1(config)#exit
    

     再度 show ip route コマンドを実行し、ルーティングが有効になったことを確認します。
     特に、VLAN10~30 が「directly connected」となっていることを確認します。

    ESW1#show ip route
    Codes: C - connected, S - static, R - RIP, M - mobile, B - BGP
           D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
           N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
           E1 - OSPF external type 1, E2 - OSPF external type 2
           i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
           ia - IS-IS inter area, * - candidate default, U - per-user static route
           o - ODR, P - periodic downloaded static route
    
    Gateway of last resort is not set
    
    C    192.168.1.0/24 is directly connected, Vlan10
    C    192.168.2.0/24 is directly connected, Vlan20
    C    192.168.3.0/24 is directly connected, Vlan30
    
  3. 端末にデフォルトゲートウェイを設定します。

     端末にデフォルトゲートウェイを設定します。
     また併せて、テストのために変更していたサブネットマスクを正しい値(255.255.255.0)に修正します。

    VLAN 端末 IP Subnet Mask Default Gateway
    VLAN10 PC11 192.168.1.11 255.255.255.0 192.168.1.254
    PC12 192.168.1.12
    PC13 192.168.1.13
    VLAN20 PC21 192.168.1.21 192.168.2.254
    PC22 192.168.1.22
    VLAN20 PC31 192.168.1.31 192.168.3.254
    PC32 192.168.1.32

     端末に VPCS を使用している場合は以下のような感じで設定します。

    PC11> ip 192.168.1.11 255.255.255.0 192.168.1.254
    Checking for duplicate address...
    PC1 : 192.168.1.11 255.255.255.0 gateway 192.168.1.254
    
    PC11> show ip
    
    NAME        : PC11[1]
    IP/MASK     : 192.168.1.11/24
    GATEWAY     : 192.168.1.254
    DNS         :
    MAC         : 00:50:79:66:68:00
    LPORT       : 10034
    RHOST:PORT  : 127.0.0.1:10035
    MTU:        : 1500
    
    PC11> save
    Saving startup configuration to startup.vpc
    .  done
    
    PC11>
    
  4. 疎通の確認

     各サブネット間で ping が通ることを確認します。
     (緑色のハッチは前回からの差分です。)

    PC11 PC12 PC13 PC21 PC22 PC31 PC32
    PC11
    PC12
    PC13
    PC21
    PC22
    PC31
    PC32

[CiscoIOS] VLAN (5) VLAN トランクポートの設定

2021年6月17日

 スイッチ間の VLAN を接続します。具体的には ESW1 と ESW2 の VLAN10 をつなぐためにトランクポートを設定します。
 以下では Fa15 ポートをトランクポートにしています。
 ESW2 でも同様の設定を行います。

ESW1#configure terminal
ESW1(config)#interface fa1/15
ESW1(config-if)#switchport
ESW1(config-if)#switchport mode trunk
ESW1(config-if)#exit
ESW1(config)#exit

ESW1#show interfaces status

Port    Name               Status       Vlan       Duplex Speed Type
Fa1/0                      connected    10           full     100 10/100BaseTX
Fa1/1                      connected    10           auto    auto 10/100BaseTX
Fa1/2                      notconnect   10           auto    auto 10/100BaseTX
Fa1/3                      notconnect   10           auto    auto 10/100BaseTX
Fa1/4                      connected    20           full     100 10/100BaseTX
Fa1/5                      connected    20           full     100 10/100BaseTX
Fa1/6                      notconnect   20           auto    auto 10/100BaseTX
Fa1/7                      notconnect   20           auto    auto 10/100BaseTX
Fa1/8                      notconnect   1            auto    auto 10/100BaseTX
Fa1/9                      notconnect   1            auto    auto 10/100BaseTX
Fa1/10                     notconnect   1            auto    auto 10/100BaseTX
Fa1/11                     notconnect   1            auto    auto 10/100BaseTX
Fa1/12                     notconnect   1            auto    auto 10/100BaseTX
Fa1/13                     notconnect   1            auto    auto 10/100BaseTX
Fa1/14                     notconnect   1            auto    auto 10/100BaseTX
Fa1/15                     connected    trunk        full     100 10/100BaseTX

 これで、ESW1 と ESW2 の Fa15 ポートを接続することで、ESW1 側の VLAN 10 の端末と ESW2 側の VLAN10 の端末が疎通します。

PC11 PC12 PC13 PC21 PC22 PC31 PC32
PC11 × × × ×
PC12 × × × ×
PC13 × × × ×
PC21 × × × × ×
PC22 × × × × ×
PC31 × × × × ×
PC32 × × × × ×

 ただ、上記の設定だと VLAN 10 以外の通信も ESW1 と ESW2 の間で通信が発生しています。
 それを防ぐには switchport trunk allowed vlan でトランクポートに転送する VLAN ID を指定すれば OK なのですが、単純に転送したい VLAN ID だけ指定するとエラーになります。

ESW1(config-if)#switchport trunk allowed vlan 10,20,30
Command rejected: Bad VLAN allowed list. You have to include all default vlans, e.g. 1-2,1002-1005.

 上記は「デフォルト VLAN を含めろ。例えば 1 と 2 と 1002 から 1005 を設定しろ。」と怒られているわけですが、これは VLAN 1 が default VLAN、1002~1005 はよくわからないけど default VLAN と同様に Catalyst がデフォルトで作成している VLAN なので排除できない、ということのようです。(詳細はググって他のサイトで確認してください)
 なので、例えば VLAN 10 だけ転送したい(VLAN 20 と 30 は転送したくない)場合は以下のように書きます。

ESW1(config-if)#switchport trunk allowed vlan 1,1002-1005,10

[CiscoIOS] VLAN (4) VLAN アクセスポートの設定

2021年6月17日

 ポート VLAN の設定を行います。

  1. まず最初に ESW1 のポートの状態を確認します。

     これから VLAN の設定を行う予定のポート(fa1/0 ~ fa1/7)が default に割当たっていることを確認します。

    ESW1#show vlan-switch
    
    VLAN Name                             Status    Ports
    ---- -------------------------------- --------- -------------------------------
    1    default                          active    Fa1/0, Fa1/1, Fa1/2, Fa1/3
                                                    Fa1/4, Fa1/5, Fa1/6, Fa1/7
                                                    Fa1/8, Fa1/9, Fa1/10, Fa1/11
                                                    Fa1/12, Fa1/13, Fa1/14, Fa1/15
    1002 fddi-default                     act/unsup 
    1003 token-ring-default               act/unsup 
    1004 fddinet-default                  act/unsup 
    1005 trnet-default                    act/unsup 
    
    VLAN Type  SAID       MTU   Parent RingNo BridgeNo Stp  BrdgMode Trans1 Trans2
    ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
    1    enet  100001     1500  -      -      -        -    -        1002   1003
    1002 fddi  101002     1500  -      -      -        -    -        1      1003
    1003 tr    101003     1500  1005   0      -        -    srb      1      1002
    1004 fdnet 101004     1500  -      -      1        ibm  -        0      0   
    1005 trnet 101005     1500  -      -      1        ibm  -        0      0   
    
    
  2. VLAN ID の設定を行います。

     VLAN ID 10, 20, 30 を設定します。

    ESW1#configure terminal
    
    ESW1(config)#vlan 10
    ESW1(config-vlan)#exit
    ESW1(config)#vlan 20
    ESW1(config-vlan)#exit
    ESW1(config)#vlan 30
    ESW1(config-vlan)#exit
    ESW1(config)#exit
    

     VLAN ID が作成できたかを確認します。

    ESW1#show vlan-switch
    
    VLAN Name                             Status    Ports
    ---- -------------------------------- --------- -------------------------------
    1    default                          active    Fa1/0, Fa1/1, Fa1/2, Fa1/3
                                                    Fa1/4, Fa1/5, Fa1/6, Fa1/7
                                                    Fa1/8, Fa1/9, Fa1/10, Fa1/11
                                                    Fa1/12, Fa1/13, Fa1/14, Fa1/15
    10   VLAN0010                         active   
    20   VLAN0020                         active   
    30   VLAN0030                         active   
    1002 fddi-default                     act/unsup 
    1003 token-ring-default               act/unsup 
    1004 fddinet-default                  act/unsup 
    1005 trnet-default                    act/unsup 
    
    VLAN Type  SAID       MTU   Parent RingNo BridgeNo Stp  BrdgMode Trans1 Trans2
    ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
    1    enet  100001     1500  -      -      -        -    -        1002   1003
    10   enet  100010     1500  -      -      -        -    -        0      0   
    20   enet  100020     1500  -      -      -        -    -        0      0   
    30   enet  100030     1500  -      -      -        -    -        0      0   
    1002 fddi  101002     1500  -      -      -        -    -        1      1003
    1003 tr    101003     1500  1005   0      -        -    srb      1      1002
    1004 fdnet 101004     1500  -      -      1        ibm  -        0      0   
              
    VLAN Type  SAID       MTU   Parent RingNo BridgeNo Stp  BrdgMode Trans1 Trans2
    ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
    1005 trnet 101005     1500  -      -      1        ibm  -        0      0   
    
  3. アクセスポートの設定を行います。

     ポート fa1/0 ~ fa1/3 を VLAN ID 10 に、fa1/4 ~ fa1/7 を VLAN ID 20 に設定します。

    ESW1#configure terminal
    
    ESW1(config)#interface range fa1/0 - 3
    ESW1(config-if-range)#switchport
    ESW1(config-if-range)#switchport mode access 
    ESW1(config-if-range)#switchport access vlan 10
    ESW1(config-if-range)#exit
    
    ESW1(config)#interface range fa1/4 - 7
    ESW1(config-if-range)#switchport
    ESW1(config-if-range)#switchport mode access
    ESW1(config-if-range)#switchport access vlan 20
    ESW1(config-if-range)#exit
    
    ESW1(config)#exit
    

     アクセスポートが設定できたことを確認します。

    ESW1#show vlan-switch
    
    VLAN Name                             Status    Ports
    ---- -------------------------------- --------- -------------------------------
    1    default                          active    Fa1/8, Fa1/9, Fa1/10, Fa1/11
                                                    Fa1/12, Fa1/13, Fa1/14, Fa1/15
    10   VLAN0010                         active    Fa1/0, Fa1/1, Fa1/2, Fa1/3
    20   VLAN0020                         active    Fa1/4, Fa1/5, Fa1/6, Fa1/7
    30   VLAN0030                         active    
    1002 fddi-default                     act/unsup 
    1003 token-ring-default               act/unsup 
    1004 fddinet-default                  act/unsup 
    1005 trnet-default                    act/unsup 
    
    VLAN Type  SAID       MTU   Parent RingNo BridgeNo Stp  BrdgMode Trans1 Trans2
    ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
    1    enet  100001     1500  -      -      -        -    -        1002   1003
    10   enet  100010     1500  -      -      -        -    -        0      0   
    20   enet  100020     1500  -      -      -        -    -        0      0   
    30   enet  100030     1500  -      -      -        -    -        0      0   
    1002 fddi  101002     1500  -      -      -        -    -        1      1003
    1003 tr    101003     1500  1005   0      -        -    srb      1      1002
    1004 fdnet 101004     1500  -      -      1        ibm  -        0      0   
    1005 trnet 101005     1500  -      -      1        ibm  -        0      0   
    
    ESW1#show interfaces status 
    
    Port    Name               Status       Vlan       Duplex Speed Type
    Fa1/0                      notconnect   10           auto    auto 10/100BaseTX
    Fa1/1                      notconnect   10           auto    auto 10/100BaseTX
    Fa1/2                      notconnect   10           auto    auto 10/100BaseTX
    Fa1/3                      notconnect   10           auto    auto 10/100BaseTX
    Fa1/4                      notconnect   20           auto    auto 10/100BaseTX
    Fa1/5                      notconnect   20           auto    auto 10/100BaseTX
    Fa1/6                      notconnect   20           auto    auto 10/100BaseTX
    Fa1/7                      notconnect   20           auto    auto 10/100BaseTX
    Fa1/8                      notconnect   1            auto    auto 10/100BaseTX
    Fa1/9                      notconnect   1            auto    auto 10/100BaseTX
    Fa1/10                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/11                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/12                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/13                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/14                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/15                     notconnect   1            auto    auto 10/100BaseTX
    ESW1#
    
  4. 引き続き ESW2 への設定を行います。

     ESW2 のアクセスポートは fa1/0 ~ fa1/3 を VLAN ID 10 に、fa1/4 ~ fa1/7 を VLAN ID 30 に設定します。

    ESW2#conf t
    
    ESW2(config)#vlan 10
    ESW2(config-vlan)#exit
    ESW2(config)#vlan 20
    ESW2(config-vlan)#exit
    ESW2(config)#vlan 30
    ESW2(config-vlan)#exit
    
    ESW2(config)#interface range fa1/0 - 3
    ESW2(config-if-range)#switchport
    ESW2(config-if-range)#switchport mode access
    ESW2(config-if-range)#switchport access vlan 10
    ESW2(config-if-range)#exit
    
    ESW2(config)#interface range fa1/4 - 7
    ESW2(config-if-range)#switchport
    ESW2(config-if-range)#switchport mode access
    ESW2(config-if-range)#switchport access vlan 30
    ESW2(config-if-range)#exit
    
    ESW2(config)#exit
    

     設定の確認をします。

    ESW2#show vlan-switch
    
    VLAN Name                             Status    Ports
    ---- -------------------------------- --------- -------------------------------
    1    default                          active    Fa1/8, Fa1/9, Fa1/10, Fa1/11
                                                    Fa1/12, Fa1/13, Fa1/14, Fa1/15
    10   VLAN0010                         active    Fa1/0, Fa1/1, Fa1/2, Fa1/3
    20   VLAN0020                         active
    30   VLAN0030                         active    Fa1/4, Fa1/5, Fa1/6, Fa1/7
    1002 fddi-default                     act/unsup
    1003 token-ring-default               act/unsup
    1004 fddinet-default                  act/unsup
    1005 trnet-default                    act/unsup
    
    VLAN Type  SAID       MTU   Parent RingNo BridgeNo Stp  BrdgMode Trans1 Trans2
    ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
    1    enet  100001     1500  -      -      -        -    -        1002   1003
    10   enet  100010     1500  -      -      -        -    -        0      0
    20   enet  100020     1500  -      -      -        -    -        0      0
    30   enet  100030     1500  -      -      -        -    -        0      0
    1002 fddi  101002     1500  -      -      -        -    -        1      1003
    1003 tr    101003     1500  1005   0      -        -    srb      1      1002
    1004 fdnet 101004     1500  -      -      1        ibm  -        0      0
    1005 trnet 101005     1500  -      -      1        ibm  -        0      0
    
    ESW2#show interfaces status
    
    Port    Name               Status       Vlan       Duplex Speed Type
    Fa1/0                      connected    10           full     100 10/100BaseTX
    Fa1/1                      notconnect   10           auto    auto 10/100BaseTX
    Fa1/2                      notconnect   10           auto    auto 10/100BaseTX
    Fa1/3                      notconnect   10           auto    auto 10/100BaseTX
    Fa1/4                      connected    30           full     100 10/100BaseTX
    Fa1/5                      connected    30           full     100 10/100BaseTX
    Fa1/6                      notconnect   30           auto    auto 10/100BaseTX
    Fa1/7                      notconnect   30           auto    auto 10/100BaseTX
    Fa1/8                      notconnect   1            auto    auto 10/100BaseTX
    Fa1/9                      notconnect   1            auto    auto 10/100BaseTX
    Fa1/10                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/11                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/12                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/13                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/14                     notconnect   1            auto    auto 10/100BaseTX
    Fa1/15                     connected    1            full     100 10/100BaseTX
    
  5. 疎通確認を行います。

     PC11 から PC12 への ping は通るはずです。同様に PC21 から PC22 への ping も通ります。
     しかし PC11 から PC21 への ping は通らないことを確認します。

     同様に PC31 から PC32 への ping は通りますが、PC31 から PC21, PC31 から PC13 への ping は通りません。
     PC11 から PC13 への ping も、まだこの段階では通らないことを確認します。

     総当たりでは以下のようになるはずです。

    PC11 PC12 PC13 PC21 PC22 PC31 PC32
    PC11 × × × × ×
    PC12 × × × × ×
    PC13 × × × × × ×
    PC21 × × × × ×
    PC22 × × × × ×
    PC31 × × × × ×
    PC32 × × × × ×

[CiscoIOS] VLAN (3) 準備 VTP モードの変更

2021年6月16日

 Cisco のスイッチには VTP (VLAN Trunking Protocol) という、要するに VLAN の設定を行うと他のスイッチにもその設定を自動で反映してくれるという便利機能があります。

 しかしこの手の機能はいやな予感しかしないので、無効にします(汗
 まず、現在の設定を確認します。

ESW1#show vtp status

VTP Version                     : 2
Configuration Revision          : 0
Maximum VLANs supported locally : 68
Number of existing VLANs        : 5
VTP Operating Mode              : Server
VTP Domain Name                 : 
VTP Pruning Mode                : Disabled
VTP V2 Mode                     : Disabled
VTP Traps Generation            : Disabled
MD5 digest                      : 0xBF 0x86 0x94 0x45 0xFC 0xDF 0xB5 0x70 
Configuration last modified by 0.0.0.0 at 0-0-00 00:00:00
Local updater ID is 0.0.0.0 (no valid interface found)

 transparent mode でない場合、transparent mode(=無効)に変更します。

ESW1#configure terminal

ESW1(config)#vtp mode transparent
Setting device to VTP TRANSPARENT mode.

ESW1(config)#do show vtp status
VTP Version                     : 2
Configuration Revision          : 0
Maximum VLANs supported locally : 68
Number of existing VLANs        : 5
VTP Operating Mode              : Transparent
VTP Domain Name                 : 
VTP Pruning Mode                : Disabled
VTP V2 Mode                     : Disabled
VTP Traps Generation            : Disabled
MD5 digest                      : 0xBF 0x86 0x94 0x45 0xFC 0xDF 0xB5 0x70 
Configuration last modified by 0.0.0.0 at 0-0-00 00:00:00
ESW1(config)#

[CiscoIOS] VLAN (2) ポートの確認

2021年6月16日

 まず、ESW1, ESW2 はなにも設定していない状態でも普通のスイッチとして動作するので、PC11 から PC12 への ping は通るはずです。それを確認してみます。

PC11> ping 192.168.0.12 -c 1
84 bytes from 192.168.0.12 icmp_seq=1 ttl=64 time=0.601 ms

 ping が通った場合、ESW1 に接続し interface status を確認すると、接続ポートが connected になっているのが確認できます。

ESW1#show interfaces status

Port    Name               Status       Vlan       Duplex Speed Type
Fa1/0                      connected    1            full     100 10/100BaseTX
Fa1/1                      connected    1            full     100 10/100BaseTX
Fa1/2                      notconnect   1            auto    auto 10/100BaseTX
Fa1/3                      notconnect   1            auto    auto 10/100BaseTX
(表示略)

 もし ping が通らない場合、ESW1 の port の設定が disable になっている可能性があります。

ESW1#show interfaces status

Port    Name               Status       Vlan       Duplex Speed Type
Fa1/0                      disabled     1            full     100 10/100BaseTX
Fa1/1                      disabled     1            full     100 10/100BaseTX
Fa1/2                      disabled     1            auto    auto 10/100BaseTX
Fa1/3                      disabled     1            auto    auto 10/100BaseTX

 その場合は、当該ポートに no shutdown を設定します。
 以下はその例ですが、面倒なので fa1/0 ~ fa1/15 の 16 ポートをまとめて no shutdown にしています。

ESW1#configure terminal
Enter configuration commands, one per line.  End with CNTL/Z.

ESW1(config)#interface range fa1/0 - 15
ESW1(config-if-range)#no shutdown
ESW1(config-if-range)#exit

(表示略)

ESW1(config)#do show interfaces status

Port    Name               Status       Vlan       Duplex Speed Type
Fa1/0                      connected    1            full     100 10/100BaseTX
Fa1/1                      connected    1            full     100 10/100BaseTX
Fa1/2                      notconnect   1            auto    auto 10/100BaseTX
Fa1/3                      notconnect   1            auto    auto 10/100BaseTX

(表示略)

 すべての端末間で ping が飛ぶことが確認できれば OK です。

[CiscoIOS] VLAN (1) 概要と準備

2021年6月16日

 Cisco IOS で VLAN を設定し、検証してみたいと思います。

 まず、作りたいネットワークを下図とします。
 スイッチが 2 つに VLAN が 3 つの構成です。線上の番号はスイッチのポート番号です。
 最終的には、VLAN 間を通信できるのは、PC11 と PC21, PC21 と PC31 だけとし、他は VLAN 内の端末しか通信できないようにしたいと考えています。
 ルーターの絵になっているところはルーターではなく L3 スイッチを想定しています。

 実機がないので検証できない、という場合は GNS3 という IOS エミュレーターを使って検証ができます。ただ、IOS イメージが必要で、これの入手が鬼門です(汗
 ちなみに下図は、GNS で上図のネットワークを構築した例です。緑の「▶」ボタンを押すとエミュレーションを開始します。

 ネットワーク上の各端末(PC11~PC32)の IP アドレスは、まずは以下の通りとします。サブネットマスクが 255.255.0.0 としている点に注意してください。これは検証の都合でそうしていて、最終的には 255.255.255.0 に直します。

VLAN 端末 IP Subnet Mask
VLAN10 PC11 192.168.1.11 255.255.0.0
PC12 192.168.1.12 255.255.0.0
PC13 192.168.1.13 255.255.0.0
VLAN20 PC21 192.168.1.21 255.255.0.0
PC22 192.168.1.22 255.255.0.0
VLAN20 PC31 192.168.1.31 255.255.0.0
PC32 192.168.1.32 255.255.0.0

 GNS で検証する場合、端末は VPCS を配置するのが軽量でよいらしいです。
 VPCS を配置した直後は IP アドレスが設定されていないので、VPCS のコンソールに接続して IP アドレスの設定を行います。設定は ip コマンドで、確認は show ip コマンドで行います。設定後は save コマンドで設定を保存します。

PC11> ip 192.168.0.11 255.255.255.0
Checking for duplicate address...
PC1 : 192.168.0.11 255.255.0.0

PC11> show ip

NAME        : PC11[1]
IP/MASK     : 192.168.0.11/16
GATEWAY     : 255.255.0.0
DNS         : 
MAC         : 00:50:79:66:68:01
LPORT       : 10042
RHOST:PORT  : 127.0.0.1:10043
MTU:        : 1500

PC11> save
Saving startup configuration to startup.vpc
.  done

 また、GNS3 の説明はここではしないので、必要であればググって他をあたってください(汗

[環境] ドライバーのインストールには成功するが、コード 39 のエラーで動作しない場合の対策

2021年6月15日

 Windows 10 でドライバーをインストールするとコード 39 のエラーが発生することがあります。

 この場合、グループポリシー(gpedit.msc)の「コンピューターの構成」→「管理用テンプレート」→「システム」→「Device Guard」の「仮想化ペースのセキュリティ」を確認して、「未構成」か「有効」になっている場合、一旦「無効」にすると動作することがあります。

 一度認識したら「有効」または「未構成」に戻してもエラーにはならないと思うので、「無効」のままにしておくのが気味が悪いようなら設定を元に戻しておくとよいです。

[遺産対策] RS-MIDI (3)

2021年6月13日

 [遺産対策] RS-MIDI のコメントに「マルチポート(16ch/ポート)に対応したhairless-midiserialのfork」があると紹介されたので試してみました。

 https://github.com/tyan0/hairless-midiserial/wiki の一番下にダウンロードリンクがあるので、そこから Windows 版をダウンロードして試してみました。

 32ch のデータも普通に鳴りました!

 注意点としては、Multiport のチェックボックスにチェックを入れるのを忘れないことと、通信設定(file → Preferences)の設定を入れるのを忘れないことでしょうか。後者は気づきにくいので注意です。(最初通信速度がデフォルトの 75 bps とかになっていて、それで鳴らなかった・・・)

 当然のことながら、MIDI In と Serial port の設定、および Loop MIDI 側の設定も忘れずにします。

 個人的にはこれを見つける前に「[遺産対策] RS-MIDI (2)」でブリッジ作ってしまったので、それで用は足りているのですが、こちらは MIDI ポートが 2 port でなく 6 port まで対応していますし、動作プラットフォームも Windows だけでなく Mac や Linux にも対応しています。Windows 版も .net framework 依存ではなさそうなので、将来を含めて長く使える可能性が高い気がします。

[遺産対策] RS-MIDI (2)

2021年1月14日


 loopMIDI と Hairless MIDI<->Serial Bridge を使えば、RSC-15AT ケーブルを使って SC-88 のコンピューター端子経由で音を鳴らすことができる、のは先の記事の通りですが。

 一方、SC-88 系に限定すると RSC-15AT ケーブル一本で A part 16 ch と B part 16 ch 合計 32ch を同時に鳴らせたわけですが、Hairless MIDI<->Serial Bridge はあくまで MIDI ポートとシリアルポートの 1:1 のブリッジなので A part 16 ch しか鳴らすことができません。Hairless MIDI<->Serial Bridge は SC-88 の演奏を目的としたものではないので当然の動作ですが、この点はちょっと残念ですね。

 ということで、RSC-15AT ケーブル経由で SC-88 2 part 32 ch を鳴らすことができる MIDI - Serial ブリッジ「RSC15toSC88」を作ってみました。Hairless MIDI<->Serial Bridge の代わりに、この RSC15toSC88 を使用します。

 使用方法は以下の通り。

  1. loopMIDI で仮想 MIDI ポートをもう一つ作成します。

     下図のように 2 port となるようにします。

  2. MIDI プレイヤーの出力先に、作成した MIDI ポートを追加します。

     TMIDI Player の場合、以下のように ポート A と ポート B に loopMIDI のポートを登録します。

  3. RSC15toSC88 をダウンロードし、インストールします。

     https://github.com/ooltcloud/RSC15toSC88/releases/latest から RSC15toSC88.zip をダウンロードし、適当にフォルダに展開します。その後 RSC15toSC88.exe を実行します。

  4. RSC15toSC88 の設定をします。

     MIDI In 側に loopMIDI で作成した仮想 MIDI ポートを MIDI プレイヤーの Port A,B と対応するように設定します。
     シリアルポートは接続機器につながっているポートを選択します。(Hairless MIDI<->Serial Bridge と同じです。)

  5. RSC15toSC88 の Start ボタンを押します。
  6. MIDI プレイヤーで MIDI データを演奏します。

 上記で 32ch の MIDI データが演奏できるようになります。
 もっとも今更こんなの使って演奏したいニーズがあるとも思えませんが・・・(汗

[遺産対策] RS-MIDI

2021年1月12日

 かつて、[遺産対策] USB-MIDI 変換ケーブル で「RSC-15AT なるシリアル接続のコンピューターケーブルなるものがあって、Roland SC-88 以降の 32ch 出力にも 1本のケーブルで演奏可能、とかできたんですが、これの x64 用のドライバが出てないんですね。」などと書いたのですが。

 それは現在でも変化はないようですが、しかし loopMIDI という仮想 MIDI ポートと Hairless MIDI<->Serial Bridge という MIDI - Serial ブリッジを使うと、USB Serial 変換ケーブル経由で SC-88 等の Computer ポートへリダイレクトできることが分かったので以下のその手順をメモ。

 また、今回の手順は MIDI データの再生を目的とし、接続機器からの MIDI In は取り扱わないことを前提としています。対象は Windows 10 x64 です。

  1. loopMIDI をダウンロードし、インストールします。

     実行バイナリーを http://www.tobias-erichsen.de/software/loopmidi.html からダウンロードします。

     インストールはダウンロードした zip の中に「loopMIDISetup.exe」があるので、それを実行します。
     インストールは指示されるままデフォルトで進めていけばよいと思います。

  2. loopMIDI で仮想 MIDI ポートを作成します。

     Loop MIDI を起動し、ウインドウ左下の「+」ボタンを押すと仮想 MIDI ポートが作成されます。

  3. Hairless MIDI<->Serial Bridge をダウンロードし、インストールします。

     実行バイナリーを https://projectgus.github.io/hairless-midiserial/ からダウンロードします。

     インストールはダウンロードした zip を解凍すれば OK です。解凍されたファイルの hairless-midiserial.exe を実行します。

  4. Hairless MIDI<->Serial Bridge の設定をします。

     起動後、メニューの「file → Preferences」を選択します。
     以下の画面キャプチャのように設定します。(38400bps / 8bit / None / 1bit / None)

     また、接続機器側の通信速度も 38400 bps に設定します。(例えば接続機器が SC-88 の場合は背面のスイッチを PC-2 に設定します。)

  5. シリアルポートと MIDI ポートを選択します。

     シリアルポートは接続機器につながっているポートを選択します。機器との接続は USB - Serial 変換ケーブル(と RSC-15AT 等の接続機器に対応したシリアルケーブル)で OK です。

     MIDI ポートは MIDI In 側に loopMIDI で作成した仮想 MIDI ポートを設定します。
     MIDI Out 側は Not Connected のままで OK です。(今回の例は MIDI データの再生が目的なので。)

  6. MIDI プレイヤーの出力先を、loopMIDI で作成した仮想 MIDI ポートに設定します。

    TMIDI Player の場合、メニューの「オプション → MIDI設定」のポートタブのポート A に、loopMIDI で設定した仮想 MIDI ポートを設定します。

[環境設定] BSOD 発生時の自動再起動の抑止

2021年1月2日

 起動時に BSOD (Stop) 発生するけど、自動再起動されてしまって読めなくて困る。と言った場合、以下のレジストリを 0 にすることで自動再起動を抑止することができます。

\HKLM\SYSTEM\ControlSet001\Control\CrashControl\AutoReboot

 レジストリの変更の仕方は「[環境設定] P2V などで STOP 0x7B が発生した場合の対処方法」を参考にしてください。

[環境設定] P2V などで STOP 0x7B が発生した場合の対処方法

2020年12月27日

 先日、Windows 7 系の OS を (P2)I2V してみた際に STOP 0x7B に遭遇したので、その解決方法について。
 結論としては、レジストリ \HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\intelide の Start 値が 3 になっているので、それを 0 に変更すると修復できます。(多分)

 手順は以下の通り。

  1. 前提

     今回の例は仮想マシンがターゲットです。そのため、仮想マシンの起動ディスク(修復対象の OS がインストールされているディスク)が IDE 接続となっていることを確認してください。(AHCI や SCSI ではありません)

  2. 次に、起動できない原因が STOP 0x7B であることを確認します。
  3. 再起動し、スタートアップ修復を選択します。

     スタートアップ修復が起動され、修復の完了を待ちます。当然ですがスタートアップ修復をしても修復はされません。(なので修復自体は待たずにキャンセルしてショートカットしても問題ありません。)
     修復が完了または中断し「スタートアップ修復は問題を検出できませんでした」なり「この問題を自動的に修復できません」なりのダイアログに変わったら、「システム復元とサポートの詳細オプションの表示」を選択します。

     選択後、IME の選択やログオンの要求があればその指示に従って進めます。

  4. システム回復オプションのダイアログが表示されたら「コマンドプロンプト」を選択します。
  5. コマンドプロンプトで「regedit」と入力し、レジストリエディタを起動します。
  6. レジストリハイブを読み込むために「HKEY_LOCAL_MACHINE」または「HKEY_USERS」を選択します。

     起動したレジストリエディタにインストール OS のレジストリが表示されているように見えますが実は違います。
     これはスタートアップ修復で起動している OS のレジストリなので、これを操作してもインストール OS(= STOP 0x7B を発生されている OS)に影響を与えることができません。
     そのため、インストール OS のレジストリのファイルを読み込みます。これを「ハイプの読み込み」といいます。

  7. 「ファイル」→「ハイプの読み込み」を選択します。
  8. 現在のディレクトリを確認します。【重要】

     カレントドライブが X: になっていることを確認します。
     X: はスタートアップ修復用 の OS のドライブなので、インストールしている Windows のドライブと異なる点に注意します。
     X: にもレジストリファイル(X:\Windows\System32\Config\SYSTEM)がありますが、このドライブのレジストリファイルは操作しません。

  9. OS がインストールされているドライブの \Windows\System32\Config をポイントします。

     今回の場合だと、D: が OS のドライブだったので、D:\Windows\System32\Config を選択します。

  10. 「SYSTEM」という名のファイルを選択します。
  11. キー名を入力します。

     キー名自体に意味はないので、どのような名前にしても良いです。
     個人的には「aaaaaaaaaaaaaaaaaaa」のような無意味かつ長い名前にしたほうが、どこにハイブをマウントしたのかが分かりやすいのでオススメです。

  12. マウントしたハイブ以下の ControlSet001\Services をポイントします。
  13. intelide の start 値を変更します。

     intelide の start 値が 3 となっているはずなので、これを 0 にします。

  14. ControlSet002 が存在する場合、ControlSet001 と同様に変更します。
  15. 終了したらレジストリエディタ等を閉じて行き、再起動します。

     うまくいけば 0x7B が回避され、Windows が起動するはずです。

  16. 上記で回復できなかった場合。

     Fix: BSOD Error 0x0000007B on Boot on Windows 7 and Server 2008 R2 によれば、各サービスの Start 値は以下のようです。
     Start 値の意味は 0 が Boot, 3 が手動です。(残りの値は、1:システム, 2:自動, 4:無効 です。)

    Service 仮想 物理(SATA) 物理(RAID)
    aliide 3 3 3
    amdide 3 3 3
    atapi 0 0 0
    cmdide 3 3 3
    iastorv 3 3 3
    intelide 0 3 3
    LSI_SAS 0 3 3
    msahci 3 0 0
    pciide 3 0 3
    viaide 3 3 3

     各サービスの Start 値が上表と同じ値になるように変更します。(今回は仮想マシンがターゲットなので、上表の「仮想」の列と同じ値になるようにします。)
     変更後、再起動して STOP 0x7B が回避できることを確認します。(未確認)

  17. 応用

     たとえば BIOS(UEFI)で IDE(レガシー)から AHCI に変更した後に STOP 0x7B で起動できないようなケースでは、上記の msahci の Start 値を 3 から 0 に変更することで起動が可能になるかもしれません。(未確認)

[Excel] Volume Licence 版の単体 Excel 2019 のダウンロードとインストール

2020年10月2日

 Office 2019 ですが、iso ファイルも msi ファイルも提供されなくなりました。面倒な・・・(汗
 Volume ライセンスで購入した Office をインストールするには構成ツールを使え、と。面倒な・・・(汗
 UX 低下していませんか?(しろめ

 ということでインストールの手順のメモです。

 以下の例は Office ではなく単体の Excel をインストールするケースです。またネットワークの負荷を考えてダウンロードとインストールは別工程にしています。

  1. Office 展開ツールをダウンロード

     Office Deployment Tool のサイトでOffice 展開ツールをダウンロードします。

  2. ダウンロードファイルの解凍

     ダウンロードしたファイルを実行し解凍します。ここでは、Z:\Excel 以下に解凍したことにします。
     Z:\Excel に以下の 4 ファイルが作成されます。
     今回は setup.exe のみあればよく、xml ファイルは要らないので xml ファイルは削除します。

    configuration-Office2019Enterprise.xml
    configuration-Office365-x64.xml
    configuration-Office365-x86.xml
    setup.exe
    
  3. 構成ファイルの作成

     以下の内容で configuration-Excel2019.xml を作成します。

    <Configuration>
    
      <Add OfficeClientEdition="32" Channel="PerpetualVL2019">
        <Product ID="Excel2019Volume">
          <Language ID="ja-jp" />
        </Product>
      </Add>
    
    </Configuration>
    

     要素の意味は Microsoft のサイトで確認してください。(上記は 32 bit 版の日本語を指定しています)

  4. ダウンロード

     次にコマンドプロンプトで以下を実行します。
     実行前に、カレントディレクトリを Z:\Excel にしておきます。

    SETUP /download .\configuration-Excel2019.xml
    

     すると Z:\Excel に Office というフォルダが作成され、ファイルのダウンロードが始まります。

  5. インストールポイントの作成

     もしダウンロードした PC 以外にインストールしたい場合は、Z:\Excel 以下を DVD に焼くか、USB メモリにコピーするかします。

  6. インストール

     管理者権限のコマンドプロンプトで以下を入力します。
     実行前にインストールポイントをカレントディレクトリにしておきます。

    SETUP /configure .\configuration-Excel2019.xml
    
  7. インストール後

     今回作った構成ファイルはプロダクトキーの設定はしていないので、Excel を起動してからプロダクトキーを入力します。
     あらかじめプロダクトキーを構成ファイルに設定したい場合は、構成ファイル(configuration-Excel2019.xml)に PIDKEY 要素を設定することで行えます。詳しくは「Office 2019 の展開 (IT 担当者向け)」を参照してください。(xml の要素が自動翻訳されて中途半端に日本語になっているので、必要に応じて英文に切り替えて読んでください)

[文字] 等幅フォント考 2020

2020年8月19日

 いつも困る等幅フォント問題。とりあえず現時点の状況を確認してみました。

 検証対象は Windows 10 ver 1809, macOS 10.15 Catalina, Android 8, iPad OS 13 で、ブラウザはいずれも Google Chrome(Version 84 前後)です。

  • 和文

     ここでいう和文とは、主に過去 ShiftJIS を構成していた JIS X 0201 と JIS X 0208 のコードポイントの集合を指します。したがって、それらコードに含まれる英数字および記号類も和文の範囲です。

    1. Windows

       Windows の場合、最近だと 'biz UDGothic' が標準で搭載されています。それ以前だと 'MS Gothic' が定番です。serif 系の 'biz UDMincho', 'MS Mincho' もあります。
       いわゆる、JIS X 0208 の範囲は全角幅です。Unicode で追加されたラテン拡張領域の文字などは半角幅になります。
       罫線は全角 (半角 2 文字) 単位です。

      font-family: 'biz UDGothic', 'MS Gothic', Monospace;
      +-+-+-+-+-+-+-+-+-+
      |a|1|ij|ǽ|ʤ|×|α|||
      +-+-+-+-+-+-+-+-+-+
      ┌─┬─┬─┬─┬─┐
      │△│漢│字│か│な│
      │カナ│Ai│01│£ij│ǽʤ│
      └─┴─┴─┴─┴─┘
      

    2. macOS

       macOS の場合、'Osaka-Mono' 一択です。(多分)
       'MS Gothic' 等と同じく「全角文字 = 半角 2 文字」です。
       ただ、ラテン拡張の文字、および発音記号(IPA 拡張文字)は Menlo やヒラギノなどにフォールバックするので等幅ではなくなります。

      font-family: 'Osaka-Mono', Monospace;
      +-+-+-+-+-+-+-+-+-+
      |a|1|ij|ǽ|ʤ|×|α|||
      +-+-+-+-+-+-+-+-+-+
      ┌─┬─┬─┬─┬─┐
      │△│漢│字│か│な│
      │カナ│Ai│01│£ij│ǽʤ│
      └─┴─┴─┴─┴─┘
      
    3. Android / iOS (iPadOS)

       Android と iOS は和文の等幅フォントをもっていないので、和文の等幅表示はできません。(多分)

      font-family: Monospace;
      +-+-+-+-+-+-+-+-+-+
      |a|1|ij|ǽ|ʤ|×|α|||
      +-+-+-+-+-+-+-+-+-+
      ┌─┬─┬─┬─┬─┐
      │△│漢│字│か│な│
      │カナ│Ai│01│£ij│ǽʤ│
      └─┴─┴─┴─┴─┘
      
    4. Web Font

       結局のところ、等幅フォントを使いたい場合に OS 搭載フォントだけで勝負する、というのは 2020 年現在はほぼ無理筋です。
       そうすると Web Font に頼らざるをえないのですが、和文フォントのファイルサイズは MB 単位になるため、わずかな表示のためにこのファイルサイズを運用するのは現実的でありません。ページで使用する文字のグリフのみが登録されたフォントファイルを作ってファイルサイズを減らす方法もありますが、個人 blog でわざわざそこまで頑張るのも現実的ではありません(汗

       そこで Illusion という補助フォントを使うと便利です。3 書体あり、それぞれファイルサイズは 60 KB 程度で軽量です。
       このフォントはかなや漢字は持っていないので、OS 搭載のフォントと組み合わせて使います。

       以下は 'Illusion-N-Regular' を使った例で、記号や罫線を半角幅で表示します。

      font-family: 'Illusion-N-Regular', 'biz UDGothic', 'Osaka-Mono', 'MS Gothic', Monospace;
      +-+-+-+-+-+-+-+-+-+
      |a|1|ij|ǽ|ʤ|×|α|||
      +-+-+-+-+-+-+-+-+-+
      ┌─┬─┬─┬─┬─┐
      │△│漢│字│か│な│
      │カナ│Ai│01│£ij│ǽʤ│
      └─┴─┴─┴─┴─┘
      

       以下は 'Illusion-Z-Regular' を使った例で、記号や罫線を全角幅で表示します。

      font-family: 'Illusion-Z-Regular', 'biz UDGothic', 'Osaka-Mono', 'MS Gothic', Monospace;
      +-+-+-+-+-+-+-+-+-+
      |a|1|ij|ǽ|ʤ|×|α|||
      +-+-+-+-+-+-+-+-+-+
      ┌─┬─┬─┬─┬─┐
      │△│漢│字│か│な│
      │カナ│Ai│01│£ij│ǽʤ│
      └─┴─┴─┴─┴─┘
      

       Windows は 'biz UDGothic' または 'MS Gothic' 単体でも等幅表示が維持できますが、このフォントと組み合わせても等幅表示が維持できます。
       macOS の場合は組み合わせる 'Osaka-Mono' がラテン拡張のグリフを持っていないので等幅表示でなくなります。Android と iOS も同様に Illusion が持つ文字以外の欧文の文字は等幅表示でなくなります。しかし和文領域(JIS X 0201 + JIS X 0208)の範囲では等幅表示が維持できます。

  • 欧文

     ここでいう欧文とは、Unicode の基本多言語面(BMP)において、ラテン文字, ギリシャ文字, 記号類を主とする欧州圏の文字集合を指します。絵文字(✉など)や非欧州圏(アジア圏等)のアラビア文字やヘブライ文字は、ここでは含みません。

    1. Windows / macOS / Android / iOS (iPadOS) 共通

       欧文フォントの場合、serif であることが許容できれば 'Courier New' が無難かと思います。
       sans-serif だと Windows は 'Consolas', macOS と iOS は 'Menlo', Android は monospace みたいな感じで面倒です。
       罫線は英字 1 文字単位ですが、和文(全角文字)は混ぜられません。

      font-family: 'Courier New', Monospace;
      +-+-+-+-+-+-+-+-+-+
      |a|1|ij|ǽ|ʤ|×|α|||
      +-+-+-+-+-+-+-+-+-+
      ┌─┬─┬─┬─┬─┐
      │△│漢│字│か│な│
      │カナ│Ai│01│£ij│ǽʤ│
      └─┴─┴─┴─┴─┘
      
      font-family: 'Consolas', 'Menlo', Monospace;
      +-+-+-+-+-+-+-+-+-+
      |a|1|ij|ǽ|ʤ|×|α|||
      +-+-+-+-+-+-+-+-+-+
      ┌─┬─┬─┬─┬─┐
      │△│漢│字│か│な│
      │カナ│Ai│01│£ij│ǽʤ│
      └─┴─┴─┴─┴─┘
      

       また、Android に至っては欧文等幅フォントも十分なグリフを持っていません。たとえば 'Courier New' で割り当たるフォント(Serif-Monospace / 'Cutive Mono')は '○'(記号類)や 'α'(ギリシャ文字)などのグリフを持っていません。Monospace で割り当たる 'Roboto Mono')は 'α' や 'ǽ'(ラテン拡張)はありますが '○' はやはり持っていないので記号類は和文フォントの文字が表示されたりします。

    2. Web Font

       罫線や記号類を混ぜたい場合、Android では対応するグリフがないので web font に頼るほか方法がありません。(多分)
       ここでは DejaVuLGCSansMono というフォントを使ってみました。
       フォントのファイルサイズはおよそ 170 KB とそこそこ大きいのですが、大きめの画像程度だと思えば運用できないファイルサイズではないと思います。

      font-family: 'DejaVuLGCSansMono';
      +-+-+-+-+-+-+-+-+-+
      |a|1|ij|ǽ|ʤ|×|α|||
      +-+-+-+-+-+-+-+-+-+
      ┌─┬─┬─┬─┬─┐
      │△│漢│字│か│な│
      │カナ│Ai│01│£ij│ǽʤ│
      └─┴─┴─┴─┴─┘
      

       上記ではわかりにくいですが、欧文だけなら罫線もズレません。

      ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
      |a|1|ij|ǽ|ʤ|×|α|∫|○|£|
      └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
      

 しかしこれだけクロスプラットフォームが当然の世界でも、フォントひとつでここまで苦労しなくてはならないとは…_| ̄|○

[EdgeOS] 設定の表示

2020年8月16日

 現在の設定を見たい場合、show configuration で表示させることができます。

ubnt@ubnt:~$ show configuration

interfaces {
    ethernet eth0 {
        address xxx.xxx.xxx.xxx/xx
        duplex auto
        speed auto
    }

~以下略~

 なんですが、ツリー表示ではなく実際に投入できるコマンドの形式で欲しいことがあります。
 その場合は、/opt/vyatta/sbin/vyatta-config-gen-sets.pl を使用することによってコマンド形式で表示させることができます。引数は config ファイルなので /config/config.boot を指定します。

ubnt@ubnt:~$ /opt/vyatta/sbin/vyatta-config-gen-sets.pl /config/config.boot

set firewall all-ping 'enable'
set firewall broadcast-ping 'disable'
set firewall ipv6-receive-redirects 'disable'
set firewall ipv6-src-route 'disable'
set firewall ip-src-route 'disable'
set firewall log-martians 'enable'

~以下略~

[EdgeOS] Edgerouter ER-X で BB ルーターっぽいものを作る (3)

2020年8月16日

 今回はファイアウォールの設定について考えてみます。
 前回、以下のような設定がありました。

set interfaces ethernet eth1 firewall out   name 'routing_to_wan'
set interfaces ethernet eth1 firewall in    name 'wan_to_routing'
set interfaces ethernet eth1 firewall local name 'wan_to_local'

 eth1 のファイアウォールに Ruleset を割り付けているわけですが、out, in, local というキーワードが出てきています。これは一体何なのか?
 図にすると以下のような感じです。

 out はルーターから外へ出ていく通信、in は外からルーターへ入ってくる通信です。

 local も外からルーターへ入ってくる通信です。in と local の違いは in は宛先がルーター以外の通信(中継する通信)、local は宛先がルーター自身が宛先(eth0 や eth1 のアドレスが宛先)の通信です。
 local も in もルーターに流入する通信なので in にファイアウォールを書けば OK、と思っているとルーター宛て通信が素通しになるので注意が必要です。例えば in にファイアウォールを設定し SSH 通信を遮断したとしても、local に対して設定していない場合、ルーター宛ての SSH の通信は遮断されません。
 したがってルーター宛ての通信のファイアウォールを local に忘れずに行うようにします。

 ER-X の全ポートを図にすると以下のような感じです。青い線が in/out の経路、赤い線が local の経路です。

[EdgeOS] Edgerouter ER-X で BB ルーターっぽいものを作る (2)

2020年8月16日

 BB ルーターを作るための設定を行ってみます。

 要件は以下とします。

  1. LAN 側を eth0, WAN 側を eth1 とします。
  2. WAN 側は DHCP とします。(PPPoE にすれば直接 ISP につながる BB ルーターにできますが、今回は WAN 側からの Firewall 確認などが難しくなるのでしません。)
  3. NAPT を設定します。
  4. WAN 側からは別の BB ルーターを経由してインターネットに出られるようにします。

 上記要件を図にすると以下のようになります。Edge Router の部分が今回設定するルーターです。

 結論としては、設定は以下のようになりました。

# Set Address
set interfaces ethernet eth0 address '192.168.1.1/24'
set interfaces ethernet eth1 address 'dhcp'

# Set DNS Forwarding 
set service dns forwarding listen-on 'eth0'
set service dns forwarding dhcp eth1

# Set NAPT
set service nat rule 5000 outbound-interface 'eth1'
set service nat rule 5000 type 'masquerade'

# Make Firewall Ruleset and Set Default Action
set firewall name  routing_to_wan  default-action 'accept'  # Routing Process -> WAN  
set firewall name  wan_to_routing  default-action 'drop'    # WAN -> Routing Process
set firewall name  wan_to_local    default-action 'drop'    # WAN -> Local Services

# Set Firewall Rule: WAN -> Local Services  
set firewall name  wan_to_local  rule 100 action accept
set firewall name  wan_to_local  rule 100 state established enable
set firewall name  wan_to_local  rule 100 state related enable

# Set Firewall Rule: WAN -> Routing Process
set firewall name  wan_to_routing  rule 100 action accept
set firewall name  wan_to_routing  rule 100 state established enable
set firewall name  wan_to_routing  rule 100 state related enable

# Assign Firewall Ruleset to WAN Interface 
set interfaces ethernet eth1 firewall out   name 'routing_to_wan'
set interfaces ethernet eth1 firewall in    name 'wan_to_routing'
set interfaces ethernet eth1 firewall local name 'wan_to_local'

 設定の手順とその意味は以下。

  1. Teraterm などで Edge router に SSH で接続します。

     最近は Windows 10 でも SSH コマンドがあったりするので、それを使うのも良いかもしれません。
     コマンドプロンプトで以下のように入力すると接続できます。

    C\>ssh ubnt@192.168.1.1
    
  2. configure と入力し、設定モードに移行します。

     configure と入力すると、プロンプトが $ (=操作モード) から # (=設定モード) に変化します。

    ubnt@ubnt:~$ configure
    [edit]
    ubnt@ubnt#
    
  3. インターフェースアドレスの設定をします。

     以下の設定はデフォルトの設定と同じなので「The specified configuration node already exists」と警告されます。デフォルトから変わらないなら入力の意味はありませんが、明示してこのアドレスにしたという意図を残す意味で入力しておきます。

    set interfaces ethernet eth0 address '192.168.1.1/24'
    set interfaces ethernet eth1 address 'dhcp'
    
  4. DNS フォワーディングの設定をします。

     LAN 側から参照する DNS アドレスをルーターにするために、DNS フォワーディングの設定をします。

    set service dns forwarding listen-on 'eth0'
    set service dns forwarding dhcp eth1
    
  5. NAPT の設定をします。

     outbound-interface に WAN 側のインターフェース (=eth1) を割り当てます。
     また NAPT とするため、type 'masquerade'、を指定します。
     ちなみにここのルール番号は 5000~10000 以下で設定する必要があるらしいです。

    set service nat rule 5000 outbound-interface 'eth1'
    set service nat rule 5000 type 'masquerade'
    
  6. Firewall のルールセットとデフォルトアクションを設定します。

     routing_to_wan (LAN またはルーター本体から WAN 方向) はすべて許可 (=accept) とします。
     wan_to_routing (WAN から LAN 方向) と wan_to_local (WAN からルーター本体宛て) はすべて破棄 (=drop) とします。

    set firewall name  routing_to_wan  default-action 'accept' 
    set firewall name  wan_to_routing  default-action 'drop'
    set firewall name  wan_to_local    default-action 'drop'
    
  7. wan_to_local (WAN からルーター宛て) のルールを設定します。

     すでに接続中のセッションについての通信(LAN またはルーター本体から WAN 方向に接続した通信に関連する通信)のみ許可するようにします。

    set firewall name  wan_to_local  rule 100 action accept
    set firewall name  wan_to_local  rule 100 state established enable
    set firewall name  wan_to_local  rule 100 state related enable
    
  8. wan_to_routing (WAN から LAN 方向) のルールを設定します。

     wan_to_local と同様にします。

    set firewall name  wan_to_routing  rule 100 action accept
    set firewall name  wan_to_routing  rule 100 state established enable
    set firewall name  wan_to_routing  rule 100 state related enable
    
  9. WAN 側のインターフェース (=eth1) にルールセットを割り当てます。
    set interfaces ethernet eth1 firewall out   name 'routing_to_wan'
    set interfaces ethernet eth1 firewall in    name 'wan_to_routing'
    set interfaces ethernet eth1 firewall local name 'wan_to_local'
    
  10. commit と入力し設定を有効にします。
    commit
    
  11. 動作を確認し問題がなければ save を入力し設定を保存します。
    save
    

[EdgeOS] Edgerouter ER-X で BB ルーターっぽいものを作る (1)

2020年8月16日

 Edgerouter X (ER-X) で BB (BroadBand) ルーターっぽいものを作ってみます。
 ER-X ってのは以下の写真のような 5 port のイーサネットスイッチ並みの大きさのルーターです。amazon とかで 10000 円前後で購入できます。(2020 年現在)

 BB ルーター設定を行う前に、まずルーターの初期化をしておきます。(余計な設定が残っていると設定流した後に意図した動作とならない恐れがあるため)

  1. edge router の web console にアクセスしログインします。
  2. 画面下の「System」を押下します。
  3. System ペインの下のほうに「Reset to Default」というボタンがあるので、それを押下します。
  4. 「Reset to Default」を押下します。
  5. 「Yes I'm Sure」を押下します。
  6. 初期化を開始します。
  7. しばらく経つと「lost connection」ダイアログが表示されるので、「Try Again」を押下します。
    ただし元のアクセス先が 192.168.1.1 以外だといつまでたっても繋がらないはずなので、その場合は web ブラウザに 192.168.1.1 を入力して接続先を変えてください。
  8. 「Reload」ボタンがでたら、30秒ごとくらいに押下します。
  9. ログイン画面が出れば初期化完了です。

 念のため、初期化直後の設定を見てみると以下のような感じでした。

ubnt@ubnt:~$ show configuration
interfaces {
    ethernet eth0 {
        address 192.168.1.1/24
        duplex auto
        speed auto
    }
    ethernet eth1 {
        address dhcp
        duplex auto
        speed auto
    }
    ethernet eth2 {
        duplex auto
        speed auto
    }
    ethernet eth3 {
        duplex auto
        speed auto
    }
    ethernet eth4 {
        duplex auto
        poe {
            output off
        }
        speed auto
    }
    loopback lo {
    }
    switch switch0 {
        mtu 1500
    }
}
service {
    gui {
        http-port 80
        https-port 443
        older-ciphers enable
    }
    ssh {
        port 22
        protocol-version v2
    }
}
system {
    host-name ubnt
    login {
        user ubnt {
            authentication {
                encrypted-password ****************
            }
            level admin
        }
    }
    ntp {
        server 0.ubnt.pool.ntp.org {
        }
        server 1.ubnt.pool.ntp.org {
        }
        server 2.ubnt.pool.ntp.org {
        }
        server 3.ubnt.pool.ntp.org {
        }
    }
    syslog {
        global {
            facility all {
                level notice
            }
            facility protocols {
                level debug
            }
        }
    }
    time-zone UTC
}
ubnt@ubnt:~$

[文字] 欧文フォント使用時の記号文字の大きさ(3) 等幅フォント

2020年8月13日

 Courier New のような欧文系の等幅フォントで表示すると以下のように見えます。(多分)

aʤǽα×

 'a' と '○' が等幅で表示されています。
 (android 系はダメっぽいです。ダメな場合は「こらちの画像」を参照ください。)

 が、かつてよく見ていた等幅フォントはこうではありません。こんな感じです。

 つまり和文系のフォントでは、漢字やひらがなは正方形、英数は正方形を 1/2 に縦割りした長方形、で埋めたものを「等幅」と呼んでいます。(多分)
 ちなみに前者の文字を全角文字、後者の文字を半角文字、と呼びます。「全角」という呼び方は、もとは組版/印刷業界の用語のようです。
 (http://ja.wikipedia.org/wiki/全角と半角

 うまく表示できていれば、以下では 'a' が '○' の半分の幅で表示されているはずです。

ai12#%nmβ34

 漢字が全角文字なのは当然としても、なぜ '○' が全角文字扱いかというと、'○' は JIS X 0208 の文字だからです。(多分)
 かつて ShiftJIS が全盛だった時代は、全角とは JIS X 0208 の文字であり、半角とは JIS X 0201 の文字でした。前者は 2 バイトコードの規格であり、後者は 1 バイトコードの規格です。このため、2 バイトコードの文字は全角、1 バイトコードの文字は半角として扱うのが自然でした。
 ただ、今となっては符号化長で文字の幅が決まるのはおかしいですし、欧文も Unicode の登場でマルチバイトの時代となり '○' や '→' といった記号が使用できるようになりました。このような経緯から、同じ文字でも和文系のフォントと欧文系のフォントで幅が異なる状況が生じています。

 また、欧文系の等幅フォントは縦横比が 1:1、または 2:1 ではありません。つまり正方形を基本にしていません。ここも大きな違いです。

 したがって現在、等幅フォントは大別して欧文系のものと和文系のものと二つあり、単に等幅フォントと言われた場合、どちらを指しているのかを気にする必要があります。

 また、和文の等幅フォントが英数字以外(JIS X 0201 の文字以外)は全角幅かというとそういうわけではありません。たとえば 'ʤ' などの発音記号は MS ゴシックでは半角幅で表示されます。別のフォントでは漢字は全角幅だが記号類は欧文系にあわせて半角幅、というものもあります。したがって「マルチバイト文字 = 全角」という意識は捨てたほうが無難です。
 さらに最近は、和文でも、半角幅 3 文字 = 全角幅 2 文字、といった変則的な文字幅のフォントも登場していますから今後は、半角幅 2 文字 = 全角幅 1 文字、であることが当然という意識も捨てておいた方がトラブルは少なそうです。そのうち、全角文字 = 正方形、という関係も崩れるかもしれません。(過去、プリンターへの出力でそういう感じの実装がありました。)
 等幅フォントを選択する際は、使用する文字の幅が意図したものかどうかを逐一確認しておく必要がありそうです。

 このあたりの事情を加味して、CSS の font-family も monospace 一択ではなく、欧文の monospace なのか和文の monospace なのか、ついでに その等幅フォントが serif なのか sansserif なのかの指定もできるようになってくれるといいですね(汗

[文字] 欧文フォント使用時の記号文字の大きさ(2)

2020年8月13日

 欧文系のフォントが本当に高さ(x-height)で調整されているのかを確認してみました。

 Arial Unicode MS で表示すると以下のようになります。(下図は MS-Word での表示)

 記号('○' とか '×')だけみると半角幅にあわせたデザインのように見えますが、横長になる文字(上記だと"ʤ"と"ǽ")は半角幅ではありません。

 とはいえ、Arial Unicode MS の場合、ちょっと記号が上寄りに配置されている感じですが、Lucida Sans Unicode だと、バランスよく中央に配置されているのが分かります。

 これらから、x-height にあわせてデザインされているといえると思います。

 また、等幅フォントの Courier New にすると、以下のような感じとなります。

 ばっちり等幅ですね。

[文字] 欧文フォント使用時の記号文字の大きさ(1)

2020年8月13日

 フォントによって '○' など記号の大きさが異なることがあります。

 和文系のフォントと欧文系(?)のフォントを比較した場合、英字や漢字の大きさはほぼ同じ大きさに見えますが、'○' などの記号の大きさがかなり違って見えることがあります。

 原因は、和文系フォントは全角の領域(=ほぼ行の高さ)に配置しているのに対して、欧文系フォントは x-height という高さを基準に配置しているからです。
 x-height はどこの高さかというと、英語罫線のノートの真ん中のところ('m' とか 'o' とか 'x' が書かれている領域)です。

 以前、「○が小さいのはなぜ?(未解決)」 で「欧文フォント幅(日本的には半角に近い)で統一されている」と書きました。が、それは誤りで実際には「幅」ではなく「高さ」で配置されているようです。

 また、欧文フォントの構成要素の詳細については、「http://ja.wikipedia.org/wiki/書体#欧文書体の各構成要素」が詳しいです。

[環境] RDP の COM リダイレクトポートの確認

2020年8月2日

 リモートデスクトップでの COM リダイレクトについて、確認方法のメモ。
 change port を使用します。

  • サーバー側のマッピング

     サーバー側の COM のマッピングは以下だったとします。

    C>change port
    AUX = \DosDevices\COM1
    COM3 = \Device\VCP0
    
  • クライアント側のマッピング

     クライアント側の COM のマッピングは以下だったとします。

    C>change port
    AUX = \DosDevices\COM1
    COM1 = \Device\Serial0
    COM2 = \Device\StnSerial0
    COM3 = \Device\StnSerial1
    
  • リモートデスクトップ接続時のマッピング

     リモートデスクトップで接続すると以下のようになります。
     つまりポート番号が重なる場合はクライアント側が優先されます。

    C>change port
    AUX = \DosDevices\COM1
    COM1 = \Device\RdpDrPort\;COM1:2\tsclient\COM1
    COM2 = \Device\RdpDrPort\;COM2:2\tsclient\COM2
    COM3 = \Device\RdpDrPort\;COM3:2\tsclient\COM3
    
  • 重なっているデバイスを削除

     重なったポートを削除すると、ローカルの(サーバー側の)ポートが現れます。

    C>change port /D COM3
    
    C>change port
    AUX = \DosDevices\COM1
    COM1 = \Device\RdpDrPort\;COM1:2\tsclient\COM1
    COM2 = \Device\RdpDrPort\;COM2:2\tsclient\COM2
    COM3 = \Device\VCP0    ← ローカルのデバイスが表示される
    
  • 重なっているデバイスをずらしたい

     重なったポートをずらしたい場合、コピーを作ったうえで、重なったポートを削除します。
     以下はリダイレクトされている COM3 を COM31 にコピーした後に、COM3 を削除してサーバー側の COM ポートを有効にしています。

    C>change port COM31=COM3
    
    C>change port
    AUX = \DosDevices\COM1
    COM1 = \Device\RdpDrPort\;COM1:2\tsclient\COM1
    COM2 = \Device\RdpDrPort\;COM2:2\tsclient\COM2
    COM3 = \Device\RdpDrPort\;COM3:2\tsclient\COM3
    COM31 = \Device\RdpDrPort\;COM3:2\tsclient\COM3    ← リダイレクトの COM3 がコピーされる
    
    C>change port /D COM3
    
    C>change port
    AUX = \DosDevices\COM1
    COM1 = \Device\RdpDrPort\;COM1:2\tsclient\COM1
    COM2 = \Device\RdpDrPort\;COM2:2\tsclient\COM2
    COM3 = \Device\VCP0    ← ローカルのデバイスが表示される
    COM31 = \Device\RdpDrPort\;COM3:2\tsclient\COM3
    

     単純に move する方法は知らないので、もし方法があるなら教えてください(汗
     もっとも重ならないようにサーバー側の COM ポート番号をあらかじめ重ならないであろう番号にずらしておくのが正解だと思います。COM の番号は 256 まであるので、例えばサーバー側を 200 番台にしておければクライアント側と重なることはまずありません。

[VB.NET][C#] イベント構文

2020年7月31日

 私は母語が BASIC でして、だからなのか VB のほうをよく使っていて、ゆえに C# を書こうとすると戸惑うことがあり、event も書き方をよく忘れるのでメモ(汗

 以下はほぼ等価なイベントを使ったサンプルコード。結果だけ見れば、なんでこんな迂遠なことを、なんだけどもサンプルっとことで(汗

  • VB.NET
    Module Program
        Private WithEvents _obj As Class1
    
        Sub Main(args As String())
            _obj = New Class1
            _obj.Method01()
        End Sub
    
        Private Sub _obj_Event01(message As String, value As Integer) Handles _obj.Event01
            Console.WriteLine("{0} {1}", message, value)
        End Sub
    End Module
    
    Class Class1
        Public Event Event01(message As String, value As Integer)
    
        Public Sub Method01()
            RaiseEvent Event01("Hello1", 1)
        End Sub
    End Class
    
  • C#
    class Program
    {
        private static Class1 _obj;
    
        static void Main(string[] args)
        {
            _obj = new Class1();
            _obj.Event01 += _obj_Event01;
    
            _obj.Method01();
        }
    
        public static void _obj_Event01(string s, int i)
        {
            Console.WriteLine("{0} {1}", s, i);
        }
    }
    
    class Class1
    {
        public event Action<string, int> Event01;
    
        public void Method01()
        {
            this.Event01("Helo", 1);
        }
    }
    

 個人的には VB のほうの構文が好きでして、それは以下のような理由です。

  • WithEvents キーワードでイベントを起こすオブジェクトだというのが明示できる点
  • Handles キーワードでイベントで呼ばれるメソッドだということが明示される点
  • RaiseEvent でイベントを実行していることが明示される点
  • event 用の delegate を別に定義しなくてよい点
  • IDE の機能でイベントメソッドを自動作成してくれる

 C# のが好きでない理由はその逆ですね。

  • イベントを起こすオブジェクトとそうでないオブジェクトとの差がない
  • イベントで呼ばれるメソッドとそうでないメソッドが、メソッドを見ただけではわからない
    (もちろんイベントを登録しているところでは解るけど場所が離れているので・・・)
  • イベントの実行とメソッドの呼び出しの差がない
  • event 用の delegate を別に定義する必要がある。最近は Action を使えるけど、そうすると引数名が自由にならない。
  • IDE の機能でイベントは自動的に作成くれなさそう

 もっとも、まだ C# を使いこなせていないからそう思うだけなのかもしれません(汗

[Oracle] Timespan 型に相当するデータ型

2020年7月29日

 知りませんでしたが、Oracle にも時間型(期間型), つまり C# などでいうところの Timespan 型に相当するデータ型があるようです。

 ただ C# の Timespan 型と違い、年月の部分と日時分秒の部分とに分けて、それぞれに型が用意されているようです。前者が interval year to month 型、後者が interval day to second 型です。

 試してみます。

SQL> create table TIMESPAN (
  2      YM    interval year to month,
  3      DHMS  interval day to second
  4  );

表が作成されました。

SQL> insert into TIMESPAN values ('+99-11', '99 23:59:59.999999');

1行が作成されました。

SQL> select * from TIMESPAN;

YM      DHMS
------- --------------------
+99-11  +99 23:59:59.999999

 リテラル書式を使えば select 文内で計算も可能です。

SQL> select (interval '01 02:03:04' day to second) + (interval '05:06' hour to minute) from dual;

(INTERVAL'0102:03:04'DAYTOSECOND)+(INTERVAL'05:06'HOURTOMINUTE)
----------------------------------------------------------------
+000000001 07:09:04.000000000

 C# や VB.NET でアクセスするときは、day to second 型は Timespan 型, year to month 型は Int64 型に対応するようです。Int64 型ってなんかモヤっとしますが・・・(汗

[Oracle] [C#] Timestamp with time zone 型へのアクセス方法

2020年7月28日

 VB.NET や C# から Timestamp with time zone 型へのアクセス方法について検討してみます。

 まずは公式ドキュメントを参照してみます。
 「OracleDataReaderオブジェクトからのデータの取得」をみると「表3-10 .NETタイプのアクセッサ」に Timestamp with time zone はタイムゾーン情報のない System.DateTime に対応していて、タイムゾーン情報のある System.DateTimeOffset とは対応しないことが読み取れます。
 また、「表3-11 ODP.NETタイプのアクセッサ」をみると、ODP.NET の型としては OracleTimeStampTZ 型が用意されていることがわかります。OracleTimeStampTZ 型については「OracleTimeStampTZ 構造」に詳細が記載されています。(おそらく Structure の直訳が「構造」なのでそうなっているだけで、日本語としては「OracleTimeStampTZ 構造体」が正しい気がする・・・)

 ここまでで予想できることは、.net のタイムゾーン情報のある日付型である DateTimeOffset 型と、Oracle の Timestamp with time zone 型, OracleTimeStampTZ 型の間には相互変換ができる仕組みがなさそうだ、ということです。

 ということで試してみます。

 以下のような表があるとします。
 TSTZ 列は Timestamp with time zone 型です。

SQL> select ID, TSTZ from TIME;

ID TSTZ
-- -------------------------------
 1 15-01-06 00:29:53.006000 +09:00
 2 15-01-05 10:29:53.488000 -05:00

 これを普通に取り出してみます。以下の例では Dapper を使用して select しています。

// using Oracle.ManagedDataAccess.Types;
// using Oracle.ManagedDataAccess.Client;
// using Dapper;

var connStr = "User Id = scott; password = tiger; data source = DBHOST/ORACLE_SID";
var conn = new OracleConnection(connStr);

var results = conn.Query("select ID, TSTZ from TIME order by ID");
foreach (var result in results)
{
    Console.WriteLine("{0}: {1:yyyy/MM/dd hh:mm.ss.fffffff}", result.ID, result.TSTZ);
}

 結果は以下です。タイムゾーン情報が抜け落ちた現地時間が DateTime 型で戻ってきます。
 DateTime 型ではなく DateTimeOffset 型で取得してくれたらよかったのですがそうではないようです。公式ドキュメント通りですね(汗
 したがって情報が欠落してしまうので Dapper でアクセスすることができません。(誰かよい方法を知っていたら教えてください。-_-;)

1: 2015/01/06 12:29.53.0060000
2: 2015/01/05 10:29.53.4880000

 そこで普通に OracleCommand を使用して OracleTimeStampTZ 型経由で取得を試みます。
 こちらも OracleTimeStampTZ 型ではなく DateTimeOffset 型で取得してくれたらよいのですが、GetDateTimeOffset メソッドはありません。これも公式ドキュメント通りです(汗

var connStr = "User Id = scott; password = tiger; data source = DBHOST/ORACLE_SID";
var conn = new OracleConnection(connStr);
conn.Open();

var cmd = new OracleCommand("select ID, TSTZ from TIME order by ID", conn);
var reader = cmd.ExecuteReader();

while (reader.Read())
{
    // 取得
    var ID = reader.GetInt32(0);
    var tstz = reader.GetOracleTimeStampTZ(1);

    Console.WriteLine("{0}: {1:yyyy/MM/dd}", ID, tstz);
}

conn.Close();

 しかし OracleTimeStampTZ 型は使い勝手がよくないです。
 理由のひとつは、OracleTimeStampTZ 型は Oracle 独自の型なので、この型をいろんなところで持ちまわすと、例えば将来データベースを変更するようなことが起こると大変なことになりそうです。
 もう一つの理由は、Console.WriteLine() などで書式指定した場合に、意図通りにならない点です。
 上記サンプルコードは書式として「yyyy/MM/dd」を指定していますが、出力結果は意図したものと異なっています。

1: 01/06/2015 00:29:53.006 +09:00
2: 01/05/2015 10:29:53.488 -05:00

 以上のようなことから、できるだけ .net 標準の DateTime 型または DateTimeOffset 型を使いたい。
 ということで、OracleTimeStampTZ 型と DateTimeOffset 型を相互に変換する拡張メソッドを用意することにしました。
 ただし実装はかなり力技です(汗 # 特に Replace("+", "") あたり・・・(汗

public static class OracleTsTzExtensions
{
    // OracleTimeStampTZ → DateTimeOffset
    public static DateTimeOffset ToDateTimeOffset(this OracleTimeStampTZ value)
    {
        return new DateTimeOffset((DateTime)value, TimeSpan.Parse(value.TimeZone.Replace("+", "")));
    }

    // DateTimeOffset → OracleTimeStampTZ
    public static OracleTimeStampTZ ToTimeStampTZ(this DateTimeOffset value)
    {
        return new OracleTimeStampTZ(value.DateTime, value.Offset.ToString());
    }
}

 OracleTimeStampTZ 型を DateTimeOffset 型に変換する拡張メソッドを使用すると、以下のように書けます。

var connStr = "User Id = scott; password = tiger; data source = DBHOST/ORACLE_SID";
var conn = new OracleConnection(connStr);
conn.Open();

var cmd = new OracleCommand("select ID, TSTZ from TIME order by ID", conn);
var reader = cmd.ExecuteReader();

while (reader.Read())
{
    // 取得
    var ID = reader.GetInt32(0);
    var tstz = reader.GetOracleTimeStampTZ(1);

    // DateTimeOffset 型に変更
    DateTimeOffset dt = tstz.ToDateTimeOffset();

    Console.WriteLine("{0}: {1:yyyy/MM/dd HH:mm.ss.fffffff K}", ID, dt);

}

conn.Close();

 結果は以下の通り。

1: 2015/01/06 00:29.53.0060000 +09:00
2: 2015/01/05 10:29.53.4880000 -05:00

 select ではなく、insert や update などをするときには DateTimeOffset 型から OracleTimeStampTZ 型に変換する拡張メソッドを使います。

var connStr = "User Id = scott; password = tiger; data source = DBHOST/ORACLE_SID";
var conn = new OracleConnection(connStr);
conn.Open();

var dt = DateTimeOffset.Parse("2020/7/28 15:00:23.1234567 -07:00");
OracleCommand cmd = new OracleCommand("update TIME set TSTZ = :tz where ID = 1", conn);

OracleTimeStampTZ tstz = dt.ToTimeStampTZ();
cmd.Parameters.Add("tz", tstz);

cmd.ExecuteNonQuery();

conn.Close();

 結果は以下です。

SQL> select ID, TSTZ from TIME where ID=1;

ID TSTZ
-- -------------------------------
 1 20-07-28 15:00:23.123457 -07:00

 面倒ですね(汗
 その点、Timestamp with local time zone 型のほうは、タイムゾーン情報は持たないので DateTime 型でアクセスでき、お手軽ではあります。もっとも Timestamp with time zone とは機能や役割が違うので比較できる話ではないのですが。

 上記は検討が浅いのでもっとよい対処法があるかもしれません。あればだれが教えてください(汗

[Oracle] データベースのタイムゾーン

2020年7月28日

 異なるタイムゾーンにいるそれぞれの Oracle Database に接続されるプログラム間で、時刻をやり取りするにはどうすればよいか?について調べてみました。

  1. 前提となるシステム

     前提となるシステム構成は下図のような感じです。

  2. 検討前の対策案
    1. データベースサーバーのタイムゾーン(=DBTIMEZONE)を UTC にして、ローカルタイムはアプリケーション側で対処する。
    2. Oracle がもつ何かしらの時刻システムを利用する。
    3. 時刻型は使用せず文字列型で頑張る。(外道?)
  3. 検討後の個人的な結論
    • DBTIMEZONE の設定は変えない。
    • Timestamp with local time zone 型が扱いやすそう。
    • Date 型や Timestamp 型は使わないほうがよさそう。
  4. 結論に至るまでの考察。
    1. Date 型や Timestamp 型について。
      • Date 型や Timestamp 型は、そもそもタイムゾーン情報を持ち合わせていないので、その記録時刻がどのタイムゾーンに属するのかを判定することができない。
      • 同様に、よく使われる sysdate, systimestamp が戻す型は Date 型, Timestamp 型であるためタイムゾーンを持たない。
      • sysdate, systimestamp が戻す時刻は database のマシンの locale 情報に基づいている。このため ORA_SDTZ の設定(*1) を替えても、あくまで database が動作している PC の timezone での時刻が戻される。

      (*1) 環境変数 ORA_SDTZ にタイムゾーンを指定することで、セッション(=クライアント)のタイムゾーンを変えることができる。

    2. Timestamp with time zone 型について。
      • タイムゾーン情報を記録する。
      • 記録されたタイムゾーンは insert したセッションのタイムゾーンが記録される。このため UTC で同じ時刻であっても違う時刻として記録される。たとえば 09:00 +09:00 と 19:00 -05:00 はともに UTC 0:00 だが違うものとして扱われる。
      • 記録したデータを、他のタイムゾーンから読みだした場合、記録時のタイムゾーンの表記で読みだされる。+09:00 のセッションから -05:00 で記録したデータを読みだした場合、09:00 ではなく 19:00 -05:00 が得られる。
    3. Timestamp with local time zone 型について。
      • データベースのタイムゾーンに変換して記録される。
      • このため、insert したセッションのタイムゾーンに関わりなく、読みだし時にそのクライアント側のタイムゾーンの時刻に変換される。
  5. ここまでの要点
    • DATE型 / TIMESTAMP型はタイムゾーン情報が失われる。
    • Timestamp with time zone 型は Insert したクライアントのタイムゾーン情報も記録される。
    • Timestamp with local time zone 型は読み出し時にクライアントのタイムゾーンに変換されて表示される。
    • SYSDATE / SYSTIMESTAMP はクライアントのタイムゾーンが何であれ、常にデータベースサーバーのタイムゾーンの値で記録され、かつタイムゾーン情報は失われる。
  6. 挙動の確認

    1. 準備とか

      • テスト用のテーブルを作成する
        create table TIME (
        ID       number(2),
        DT       date,
        TS       timestamp,
        TSL      timestamp with local time zone,
        TSTZ     timestamp with time zone
        );
        
      • 表示を見易くするための設定など
        ALTER SESSION SET NLS_DATE_FORMAT = 'RR-MM-DD HH24:MI:SS';
        set TAB off
        set LINESIZE 300
        col SYSTIMESTAMP for A32
        col CURRENT_TIMESTAMP for A32
        col TS   for A25
        col TSL  for A25
        col TSTZ for A32
        col SESSIONTIMEZONE for A17
        
    2. それぞれのクライアントでデータベースタイムゾーン/セッションタイムゾーンを確認
      1. +09:00 側クライアントで確認
        SQL> select DBTIMEZONE,SESSIONTIMEZONE from dual;
        
        DBTIMEZONE   SESSIONTIMEZONE
        ------------ -----------------
        +00:00       +09:00
        
        SQL> select SYSDATE, CURRENT_DATE, SYSTIMESTAMP, CURRENT_TIMESTAMP from DUAL;
        
        SYSDATE           CURRENT_DATE      SYSTIMESTAMP                     CURRENT_TIMESTAMP
        ----------------- ----------------- -------------------------------- --------------------------------
        15-01-06 00:35:01 15-01-06 00:35:01 15-01-06 00:35:01.865000 +09:00  15-01-06 00:35:01.865000 +09:00
        
      2. -05:00 側クライアントで確認

         SYSDATE, SYSTIMESTAMP はクライアント側のタイムゾーンではなく、Database 側のタイムゾーンの時刻になっていることに注目します。

        SQL> select DBTIMEZONE,SESSIONTIMEZONE from dual;
        
        DBTIMEZONE   SESSIONTIMEZONE
        ------------ -----------------
        +00:00       -05:00
        
        SQL> select SYSDATE, CURRENT_DATE, SYSTIMESTAMP, CURRENT_TIMESTAMP from DUAL;
        
        SYSDATE           CURRENT_DATE      SYSTIMESTAMP                     CURRENT_TIMESTAMP
        ----------------- ----------------- -------------------------------- --------------------------------
        15-01-06 00:19:46 15-01-05 10:19:46 15-01-06 00:19:46.649000 +09:00  15-01-05 10:19:46.649000 -05:00
        
    3. CURRENT_TIMESTAMP を INSERT

       TSTZ 列は UTC としては同一時刻でも表記が異なる点に注目します。TSL 列はクライアント側のローカル時刻として表示されます。DT 列と TS 列は Insert したクライアントのローカル時刻で表示されますが、どのクライアントから Insert したかの情報(=タイムゾーン情報)は記録されていないため、時系列,発生順にソートすることができない記録になっていることに注目します。

      1. +09:00 側クライアントで操作
        SQL> insert into TIME values (1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
        SQL> commit;
        
      2. -05:00 側クライアントで操作
        SQL> insert into TIME values (2, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
        SQL> commit;
        
      3. +09:00 側クライアントで確認
        SQL> select * from TIME;
        
        ID DT                TS                        TSL                       TSTZ
        -- ----------------- ------------------------- ------------------------- --------------------------------
         1 15-01-06 00:29:53 15-01-06 00:29:53.006000  15-01-06 00:29:53.006000  15-01-06 00:29:53.006000 +09:00
         2 15-01-05 10:29:53 15-01-05 10:29:53.488000  15-01-06 00:29:53.488000  15-01-05 10:29:53.488000 -05:00
        
      4. -05:00 側クライアントで確認
        SQL> select * from TIME;
        
        ID DT                TS                        TSL                       TSTZ
        -- ----------------- ------------------------- ------------------------- --------------------------------
         1 15-01-06 00:29:53 15-01-06 00:29:53.006000  15-01-05 10:29:53.006000  15-01-06 00:29:53.006000 +09:00
         2 15-01-05 10:29:53 15-01-05 10:29:53.488000  15-01-05 10:29:53.488000  15-01-05 10:29:53.488000 -05:00
        
    4. SYSTIMESTAMP を INSERT

       TSL 列はローカル時刻が表示されます。DT 列と TS 列は常に +09:00 (=DBTIMEZONE) で表示されます。TSTZ 列は SYSTIMESTAMP のタイムゾーン(=+9:00)で表示されます。

      1. +09:00 側クライアントで操作
        SQL> insert into TIME values (3, SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
        SQL> commit;
        
      2. -05:00 側クライアントで操作
        SQL> insert into TIME values (4, SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
        SQL> commit;
        
      3. +09:00 側クライアントで確認
        SQL> select TIME.*, CURRENT_TIMESTAMP from TIME;
        
        ID DT                TS                        TSL                       TSTZ                             CURRENT_TIMESTAMP
        -- ----------------- ------------------------- ------------------------- -------------------------------- --------------------------------
         3 15-01-06 00:45:17 15-01-06 00:45:17.809000  15-01-06 00:45:17.809000  15-01-06 00:45:17.809000 +09:00  15-01-06 00:54:34.766000 +09:00
         4 15-01-06 00:45:18 15-01-06 00:45:18.349000  15-01-06 00:45:18.349000  15-01-06 00:45:18.349000 +09:00  15-01-06 00:54:34.766000 +09:00
        
      4. -05:00 側クライアントで確認
        SQL> select TIME.*, CURRENT_TIMESTAMP from TIME;
        
        ID DT                TS                        TSL                       TSTZ                             CURRENT_TIMESTAMP
        -- ----------------- ------------------------- ------------------------- -------------------------------- --------------------------------
         3 15-01-06 00:45:17 15-01-06 00:45:17.809000  15-01-05 10:45:17.809000  15-01-06 00:45:17.809000 +09:00  15-01-05 10:55:14.351000 -05:00
         4 15-01-06 00:45:18 15-01-06 00:45:18.349000  15-01-05 10:45:18.349000  15-01-06 00:45:18.349000 +09:00  15-01-05 10:55:14.351000 -05:00
        
  7. 総論

     結論的にはシステムで強くタイムゾーンを意識する必要がある場合は Timestamp with time zone 型を使用します。そうではなく、システム内では一貫した時刻を管理するが、クライアント側の表示はローカル時刻で表示したい、という場合は Timestamp with local time zone 型を使うのが手軽である気がします。

  8. その他、時刻関連の注意事項

    1. 時刻データ型のサイズ

       それぞれのデータ型のサイズは以下の通りです。

      データ型 サイズ
      Date 型 7 Byte
      TimeStamp 型 7 Byte or 11 Byte
      Timestamp with time zone 型 13 Byte
      Timestamp with local time zone 型 7 Byte or 11 Byte

       公式ドキュメントは https://docs.oracle.com/cd/E16338_01/server.112/b56299/sql_elements001.htm などを参照。
       また更新ドキュメントを参照すると、タイムスタンプ型は TIMESTAMP [(fractional_seconds_precision)] という書き方ができ、精度の指定とそれによりサイズが変わる模様です。

    2. データベースのタイムゾーン(=DBTIMEZONE)が異なるサーバー間の Export/Import

       ここではテーマにしていませんが、DATE 型や TIMESTAMP 型の列は、Export したデータベースのタイムゾーンと Import するデータベースのタイムゾーンが異なると、時刻の意味が変わります。(Timezone の時刻差だけズレてしまう)
       そのようなデータ交換をする可能性があるシステムの場合は imestamp with local time zone 型または Timestamp with local time zone 型を使ったほうがよさそうです。

[Oracle] Win10 で ODP.NET 11.2 を使用したプログラムをリビルドなしで動作させる

2020年7月26日

 Windows 7 + Oracle 11gR2 時代のプログラムをリビルドすることなく Windows 10 で動作させたいのですが、ODP.NET の 11.2 は Windows 10 非対応です。そこで Windows 10 対応の ODAC を使い、ODP 11.2 のプログラムを動作させるための設定を行ってみました。

  1. Oracle ODAC Xcopy のインストール

     まず Oracle ODAC のインストールを行います。
     Windows 10 で動作させたいので、Windows 10 対応されている ODAC のうち今回は 18.3 を使用することにします。

    1. 「ODAC Runtime Downloads」で検索し、https://www.oracle.com/database/technologies/dotnet-odacdeploy-downloads.html にアクセスします。

    2. 下にスクロールさせていくと、ODAC Xcopy というセクションがあります。ここで任意のバージョンのランタイムを取得します。今回は 32bit の ODAC 18.3 を選択します。
    3. C++ 再頒布可能パッケージ をダウンロードします。

       ODAC をインストールするためには、バージョンに対応した C++ 再頒布可能パッケージが必要です。
       下にスクロールさせていくとリンクがあるので、対応したバージョンの C++ 再頒布可能パッケージ を Microsoft のサイトからダウンロードします。
       今回は 18.3 なので 2013 の C++ 再頒布可能パッケージ をダウンロードします。
       (以前の記事「Instant Client のインストール (11.2 for Windows)」ではこの記載がありませんが、この時の環境は Visual Studio がすでにインストールされた環境であったため、あらためてインストールする必要がなかったのだと思われます・・・ -_-;)

    4. リンク先に「Microsoft Visual C++ Redistributable Packages for Visual Studio 2013」があるのでそれを選択します。
    5. ダウンロードした C++ 再頒布可能パッケージをインストールします。
    6. ダウンロードした ODAC Runtime を解凍しインストールします。

       インストール先と Oracle Home 名は自由に設定できます。今回は c:\oracle\product\18.3\odac、Oracle Home 名は odac183 にします。
       また、ODP.NET 2 と ODP.NET 4 をインストールするので、以下のように 2 回インストーラーを実行します。
       また、コマンドプロンプトは管理者で実行し、ODAC Xcopy を解凍したディレクトリをカレントにおいておきます。

      C> install.bat odp.net2 c:\oracle\product\18.3\odac odac183 true
      C> install.bat odp.net4 c:\oracle\product\18.3\odac odac183 true
      

    7. インストールが終了すると、Path に定義を先頭に追加します。
      C:\oracle\Product\18.3\odac
      C:\oracle\Product\18.3\odac\bin
      

       ちなみに上記の画面キャプチャだと 3 行目に C:\oracle\Product\18.0.0\dbhomeXE\bin というのがいますが、ここれはインストール PC に Oracle XE 18c をインストールしていたためで、今回の作業とは関係ありません。

    8. インストール先にある oracle.key を参照し、レジストリを変更します。
    9. 接続先データベースの NLS_LANG にあわせます。今回は Oracle XE に接続こともあり、NLS_LANG を JAPANESE_JAPAN.AL32UTF8 に変更します。
    10. 接続先を TNSNAMES.ORA に設定します。

       本来は C:\oracle\product\18.3\odac\network\admin\ に tnsnames.ora を書くべきですが、今回の PC には Oracle XE がインストールされていた手前、二重管理が面倒なので XE 側の tnsnames.ora にリンクを貼ることでお茶を濁します(汗

      C> mklink C:\oracle\product\18.3\odac\network\admin\tnsnames.ora C:\oracle\product\18.0.0\dbhomeXE\network\admin\tnsnames.ora
      
  2. ODP.NET 11.2 のリダイレクト設定

     次に ODP.NET 11.2 のプログラムが ODP.NET 18.3 の Oracle.DataAccess.dll をロードするための設定を行います。
     また以下では Oracle Home ディレクトリを %ORACLE_HOME% と表現しています。今回の場合は上記インストールで指定した c:\oracle\product\18.3\odac になります。

    1. GAC に 18.3 の Oracle.DataAccess.dll を登録します。

       .net2 用, .net4用の Oracle.DataAccess.dll を GAC に登録します。
       Oracle.DataAccess.dll の場所は %ORACLE_HOME%\odp.net\bin です。
       その際、OraProvCfg.exe を使用しますが、ファイル名は同じですが中身は .net2 用, .net4用で異なるようなので、それぞれのフォルダにある OraProvCfg.exe を使用します。

      C> C:\oracle\product\18.3\odac\odp.net\bin\2.x\OraProvCfg.exe /action:gac /providerpath:C:\oracle\product\18.3\odac\odp.net\bin\2.x\Oracle.DataAccess.dll
      C> C:\oracle\product\18.3\odac\odp.net\bin\4\OraProvCfg.exe /action:gac /providerpath:C:\oracle\product\18.3\odac\odp.net\bin\4\Oracle.DataAccess.dll
      
    2. 11.2 用の発行者ポリシーを GAC に登録します。

       .net2 用, .net4用の Policy.2.112.Oracle.DataAccess.dll を GAC に登録します。
       Policy.2.112.Oracle.DataAccess.dll の場所は %ORACLE_HOME%\odp.net\PublisherPolicy です。
       その際、OraProvCfg.exe を使用しますが、ファイル名は同じですが中身は .net2 用, .net4用で異なるようなので、それぞれのフォルダにある OraProvCfg.exe を使用します。

      C> C:\oracle\product\18.3\odac\odp.net\bin\2.x\OraProvCfg.exe /action:gac /providerpath:C:\oracle\product\18.3\odac\odp.net\PublisherPolicy\2.x\Policy.2.112.Oracle.DataAccess.dll
      C> C:\oracle\product\18.3\odac\odp.net\bin\4\OraProvCfg.exe /action:gac /providerpath:C:\oracle\product\18.3\odac\odp.net\PublisherPolicy\4\Policy.4.112.Oracle.DataAccess.dll
      
  3. セットアップ失敗事例

     以下のようなエラーが表示された場合、セットアップに失敗しているかもしれません。

    1. GAC に登録していない

       「ファイルまたはアセンブリ、またはその依存関係の 1 つが読み込めませんでした。」が表示される場合、GAC への Oracle.DataAccess.dll 本体または発行者ポリシー(Policy.X.XXX.Oracle.DataAccess.dll)の登録に失敗しているかもしれません。

      ハンドルされていない例外: System.IO.FileNotFoundException: ファイルまたはアセンブリ 'Oracle.DataAccess, Version=4.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見 つかりません。
         場所 ConsoleApplication1.Module1.Main()
      
    2. 64bit の DLL をロードしている

       「間違ったフォーマットのプログラムを読み込もうとしました。」が表示される場合、違うプラットフォームの DLL を読み込んでいる可能性があります。今回の場合は 32 bit の DLL をロードするはずが 64 bit の DLL をロードしている可能性があります。GAC に登録した DLL のプラットフォーム(32bit or 64bit)や Path の登録, 順序等を再確認してください。

      ハンドルされていない例外: System.TypeInitializationException: 'Oracle.DataAccess.Client.OracleConnection' のタイプ初期化子 が例外をスローしました。 ---> System.BadImageFormatException: 間違ったフォーマットのプログラムを読み込もうとしました。 (HRESULT からの例外:0x8007000B)
         場所 Oracle.DataAccess.Client.OpsInit.CheckVersionCompatibility(String version)
         場所 Oracle.DataAccess.Client.OracleInit.Initialize()
         場所 Oracle.DataAccess.Client.OracleConnection..cctor()
         --- 内部例外スタック トレースの終わり ---
         場所 Oracle.DataAccess.Client.OracleConnection..ctor(String connectionString)
      
    3. C++ 再頒布可能パッケージをインストールしていない。

       「'OraOpsXX.dll' を読み込めません:指定されたモジュールが見つかりません」が表示される場合、C++ 再頒布可能パッケージ をインストールしていないか、違うバージョンをインストールしている可能性があります。インストールした C++ 再頒布可能パッケージがインストールした ODAC が指定したバージョンと同じかどうかを確認してください。

      ハンドルされていない例外: System.TypeInitializationException: 'Oracle.DataAccess.Client.OracleConnection' のタイプ初期化子 が例外をスローしました。 ---> System.DllNotFoundException: DLL 'OraOps12.dll' を読み込めません:指定されたモジュールが見つかりません。 (HRESULT からの例外:0x8007007E)
         場所 Oracle.DataAccess.Client.OpsInit.CheckVersionCompatibility(String version)
         場所 Oracle.DataAccess.Client.OracleInit.Initialize()
         場所 Oracle.DataAccess.Client.OracleConnection..cctor()
         --- 内部例外スタック トレースの終わり ---
         場所 Oracle.DataAccess.Client.OracleConnection..ctor(String connectionString)
      
    4. Path が設定されていない

       「オブジェクト参照がオブジェクトインスタンスに設定されていません。」が表示される場合、%ORACLE_HOME% および %ORACLE_HOME%\bin への PATH が設定されていない可能性があります。PATH の定義を確認してください。

      ハンドルされていない例外: System.NullReferenceException: オブジェクト参照がオブジェクト イ ンスタンスに設定されていません。
         場所 Oracle.DataAccess.Client.OracleException.get_Number()
         場所 Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck, Int32 isRecoverable, OracleLogicalTransaction m_OracleLogicalTransaction)
         場所 Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, Object src, OracleLogicalTransaction m_oracleLogicalTransaction)
         場所 Oracle.DataAccess.Client.OracleConnectionOCP.Open(OracleConnection con)
         場所 Oracle.DataAccess.Client.OracleConnection.Open()
      
    5. tnsnames.ora がない

       「TNS: 指定された接続識別子を解決できませんでした」が表示される場合、%ORACLE_HOME%network\admin\ に tnsnames.ora が作成されていない可能性があります。当該フォルダに tnsnames.ora が存在するかを確認してください。また作成していた場合、指定した接続識別子のエントリーが記載しているかを確認してください。

      ハンドルされていない例外: Oracle.DataAccess.Client.OracleException: ORA-12154: TNS: 指定された接続識別子を解決できませんで した
         場所 Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck, Int32 isRecoverable, OracleLogicalTransaction m_OracleLogicalTransaction)
         場所 Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, Object src, OracleLogicalTransaction m_oracleLogicalTransaction)
         場所 Oracle.DataAccess.Client.OracleConnectionOCP.Open(OracleConnection con)
         場所 Oracle.DataAccess.Client.OracleConnection.Open()
      
    6. tnsnames.ora に誤り

       「ORA-12545: ターゲット・ホストまたはオブジェクトが存在しないため、接続に失敗しました」が表示される場合、%ORACLE_HOME%network\admin\tnsnames.ora の定義内容に誤りがある可能性があります。定義内容を再確認してください。

      ハンドルされていない例外: Oracle.DataAccess.Client.OracleException: ORA-12545: ターゲット・ホストまたはオブジェクトが存在しないため、接続に失敗しました
         場所 Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck, Int32 isRecoverable, OracleLogicalTransaction m_OracleLogicalTransaction)
         場所 Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, Object src, OracleLogicalTransaction m_oracleLogicalTransaction)
         場所 Oracle.DataAccess.Client.OracleConnectionOCP.Open(OracleConnection con)
         場所 Oracle.DataAccess.Client.OracleConnection.Open()
      

[環境] [時刻] Windows で時刻あわせ (8) GPSで簡易時刻あわせ

2020年7月25日

 テスト環境などで、どうしてもネットワークから時刻が取得できない状況があり、そうはいっても PC 本体のクロックに依存すると標準時との誤差が分単位で出てしまうので、せめて数秒内の誤差で運用したいことがあります。ということで時刻サーバーを導入・・・といってもあれはうん十万するような代物なのでカジュアルには導入できません(汗

 そこで GPS の USB ドングルであれば比較的安価に入手できるので、それを使って時刻を取得する方法を考えてみます。
 今回は Vk-172 なる USB GPS ドングルが入手できたのでそれを使用して時刻を取得してみます。

 USB ドングルを挿すと COM ポートとして認識するので、その COM ポートに teraterm などで接続すると 1 秒に一度テキストが送信されてきます。
 流れてくるテキストのうち、先頭が $GPRMC のものに注目します。テキストは CSV 形式になっており 2 列目が時刻(UTC), 10列目が日付です。また 3 列目がステータスで有効(=A),警告(=V) などを表すようです。

$GPRMC,143116.00,V,,,,,,,110919,,,N*7C

 これは NMEA フォーマットというらしく、フォーマットの詳細は https://www.hiramine.com/physicalcomputing/general/gps_nmeaformat.html などが詳しいのでそちらを参照してください。

 このテキストを拾って時刻を取得,設定する Powershell Script が以下です。
 https://gist.github.com/ooltcloud/c780872fbbe5248bb782ef9b0401e8d2

 警告もお構いなしで、日付と時刻が受信出来たら即時刻設定、という凶悪(?)な仕様になっているので、ご使用の際は気を付けてください(汗

[環境] [時刻] Windows で時刻あわせ (7) Powershell Tools

2020年7月25日

 前回記事「Windows で時刻あわせ (6) w32tm (スタンドアロン時刻サーバー)」はこちら

 時刻同期関連の Powershell Script を置いておきます。

  1. nict.jp が提供する http による時刻配信を利用して時刻を同期する。
    https://gist.github.com/ooltcloud/20e7d7551007370b4b8e345499d79cb3

     windows time サービス (w32time/w32tm) で同期したほうがよいのは確かですが、windows time サービスによる時刻同期の設定自体が難しくてどうしても時刻同期させられない場合とか、ntp のポート (port 123) が閉じられていて同期できない場合などで、ともかく精度は問わないし時刻も戻ってもいいから(急変していいから)手早く時刻を合わせたい、という場合を想定しています。要管理者権限です。また Invoke-WebRequest が必要なので Powershell 3.0 以上の環境が必要です。(つまり Windows 7 / 2008R2 では動かない場合があります。Powershell の Version は $PSVersionTable で知ることができます。)

     とはいえ先般、当該サービスは停止の方向というアナウンスがされたので、これに頼らないほうが良いですね・・・(汗
     https://jjy.nict.go.jp/httphttps-index.html

    ネットワークを利用した時刻配信におけるNTPへの一元化

    ~ http/httpsを利用した時刻配信の停止に向けた取組み~

     NICTでは、日本標準時を生成すると共に、標準電波、光テレホンJJY、NTP等様々な方法で正確な時刻を配信しています。このうちインターネット上での配信については、NTPに加えてhttp/httpsによる時刻配信を試験的に実施してきましたが、今後別記の理由により「NTPを利用した時刻配信」に一元化する方向とし、「http/httpsを利用した時刻配信」については、停止に向けた取組みを開始させていただきます。

  2. NTP (UDP) を利用して時刻を同期する。(step 調整, Leap Indecator 等無視)
    https://gist.github.com/ooltcloud/620831658a0cd9055f53ae3ba3543ee1

     上記で「いやそうじゃない、http じゃなくて ntp のままサクッと時刻合わせがしたいんである」という人向け。
     そういうひとは桜時計( https://www.vector.co.jp/soft/win95/personal/se050672.html ) を使えって話なんだけども、実行または常駐しているのが見えるのは嫌だとか Windows 標準機能でなんとかならないか(フリーソフトは持ち込めない)という人向け。
     そんなニーズがあるのかどうかは知らないが(汗
     Powershel version 3 以上が必要です。(-shl などを使っているため。ここを書き換えると version 2 でも動くかも?)

  3. 時刻サーバーの情報を確認する。
    https://gist.github.com/ooltcloud/1ed59c8ff72a24b273269aa403f6b01f

     同期先 NTP サーバーの stratum や ルート分散 (Dispersion) を確認する用途で使用します。
     使用方法、表示例は以下のような感じ。

    PS >QueryNTPStatus "ntp.nict.jp"
    ntp.nict.jp
      閏インジケーター      : 0 (警告なし)
      階層            : 1 (1次参照。原子時計等を参照)
      モード           : 4 (Server)
      精度            : -20
      ルート遅延         : 0
      ルート分散(拡散)     : 0
      参照 ID         : 0x5443494E (NICT)
      時刻源参照時刻 (JST) : 2020/07/24 20:52:32.000 (3804580352)
      要求送信時刻  (JST) : 2020/07/24 20:52:31.455 (3804580351.45544)
      要求受信時刻  (JST) : 2020/07/24 20:52:32.237 (3804580352.23683)
      応答送信時刻  (JST) : 2020/07/24 20:52:32.237 (3804580352.23683)
    

[環境] [時刻] Windows で時刻あわせ (6) w32tm (スタンドアロン時刻サーバー)

2020年7月25日

 前回記事「Windows で時刻あわせ (5) w32tm (トラブル事例)」はこちら

【注意と免責】
 以下の内容は個人的な見解であり正しさを保証しません。正しさを必要とする場合は他のサイトの記事を参照してください。

 閉域のネットワーク環境で。とある Windows 端末を時刻マスターとし、閉域ネットワークに接続されているその他の端末の時刻をその時刻マスターと同期させたい、という要求が稀にあります。

 ろくなことにならないので、そのような要求は全力で退けましょう(何

 本気でそのような要求があるのであれば、Windows を時刻マスターにするのではなく、タイムサーバー製品の導入を検討しましょう。価格がうん十万円とかしますし、設置(アンテナ等)も面倒ですが・・・(汗

 それでもなんとか Windows を時刻マスターにしたいのである、という場合、クリアしないとならない問題があります。

  1. 時刻マスターとなる時刻サーバーを、信頼性の高い時刻サーバーとなるように設定する。

     時刻マスターとなる時刻サーバーは同期先がありません。なので、時刻同期していなくても時刻同期しているフリをする必要があります。(詳しくは「Windows で時刻あわせ (3) w32tm (Server 側)」を参照)
     具体的には以下の設定を行います。

    C> w32tm /config /update /reliable:yes /LocalClockDispersion:0 
    
  2. KB232488 問題に対応する。

     外部と同期していない場合(同期する時刻サーバーがなんらかの原因で通信できない状況であるとき)、Windows タイムデーモン(Windows Time とは別のサービスと思われる)は 1 時間に 1 回システムクロック(= Free-Running System Clock)とハードウェアクロック(=LOCAL CMOS Clock =RTC)を比較します。その結果 60 秒以上の時間差がある場合、強制的にシステムクロックの値をハードウェアクロックの値に上書きします。つまり、時刻が 1 分進んだり戻ったりします。STEP 調整で 1 分前後するので阿鼻叫喚間違いなしです(汗
     KB232488 の文書(http://support.microsoft.com/kb/946033)に書かれている仕様なのですが、当該文書は 2020/7 現在、非公開になっているようです。

     今回時刻マスターにしたい Windows は外部の時刻サーバーと同期していないので、この現象が発生します。
     対策は以下の3通りです。

    1. なにもしない

       時刻マスターは 1 分の急変が発生しますが、時刻クライアントは slew 調整をすると思われるので急変からは免れることを期待します。
       欠点は恐らく時刻クライアント間で時刻が一致しないことです。(時刻マスターの時刻が急変したタイミングでクライアントが一斉に時刻調整を始めるわけではないため。時刻クライアントは時刻マスターの時刻を毎秒単位で確認しているわけではありません。)

    2. 定期的に時刻を再設定する

       RTC とシステムクロックの差が 60 秒を超えないタイミングで時刻を再設定します。
       コマンドプロンプトの TIME コマンドで時刻を設定する場合、コマンド実行直後に設定時刻を RTC に書き込みます。これをタスクスケジューラーを使用して定期的に行うことで RTC とシステムクロックの差が大きくならないようにします。

       欠点は恐らく、時刻再設定のタイミングで微小な時刻(システムクロック)の巻戻りがあり、微小ではあるものの同じ時刻を二度繰り返すことと、その微小な誤差が累積されていくことで標準時との誤差が拡大することが予想されることです。

    3. 「自動的な時刻補正」を無効にする

       timeadjx(http://www.monyo.com/technical/products/timeadjx/)のような外部ツールを使用して RTC とシステムクロックの時差チェックを無効にします。

      C> timeadjx.exe set 156000
      

       欠点は Windows 標準でないツールを使用しなくてはならなくなることです。また同様の処理は w32tm の内部でも行っている(例えば slew 調整では上記の 156000 の値を微調整することで進めたり遅らせたりをしている)はずなので処理が競合するかもしれません。具体的には「自動的な時刻補正」を無効にしていたつもりが、その後に w32tm などの他のプロセスが後から有効に変えていた、などです。

 こんなことまでして Windows を時刻マスターにすることが良いのか?というと謎なので、そのような要求があれば今一度よく考えてから対処するようにしましょう。


 さて、ここで諸悪の元凶(?)となっている KB232488 の動作を確認してみます。

  1. 上位の時刻サーバーとの同期を切断します。

     ピア(上位の時刻サーバー)の登録を削除し、時刻ソースが「Local CMOS Clock」か「Free-running System Clock」であることを確認します。同期先サーバーを設定している場合は、存在しないアドレスに変えるかレジストリのエントリー(\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters\NtpServer)を削除(空文字に)するかします。

    C>w32tm /query /status
    閏インジケーター: 0 (警告なし)
    階層: 1 (主参照 - 電波時計で同期)
    精度: -6 (ティックごとに 15.625ms)
    ルート遅延: 0.0000000s
    ルート分散: 10.0000000s
    参照 ID: 0x4C4F434C (ソース名:  "LOCL")
    最終正常同期時刻: 2020/07/18 12:53:39
    ソース: Local CMOS Clock
    ポーリング間隔: 10 (1024s)
    
    C>w32tm /query /status
    閏インジケーター: 3 (最後の 1 分間は 61 秒)
    階層: 0 (未指定)
    精度: -6 (ティックごとに 15.625ms)
    ルート遅延: 0.0000000s
    ルート分散: 10.0000000s
    参照 ID: 0x00000000 (未指定)
    最終正常同期時刻: 2020/07/18 12:53:39
    ソース: Free-running System Clock
    ポーリング間隔: 10 (1024s)
    

     ちなみに「Local CMOS Clock」と「Free-running System Clock」の違いは以下のような感じです。

    • Local CMOS Clock

       PC 起動直後、およびその状態から時刻サーバーと同期がとれなくて 24 時間未満のときの表示。
       「Local CMOS Clock」は直感的に CMOS Clock(=RTC)と同期しているかのように見えますが、PC 起動時に RTC を参照した時刻を元にしたというだけで、動作中に RTC と協調,同期しているわけではないです。

    • Free-running System Clock

       時刻サーバーと同期がとれなくて 24 時間以上経過したときの表示。
       Free-running なので「同期する先のない」という意味だとおもってよいと思います。

     そういうわけで表現は違いますが、ともに「時刻同期できていない」という点は共通です。

  2. システムクロックと RTC の時刻を 1 分以上ずらします。

     ずらすために timeadjx (http://www.monyo.com/technical/products/timeadjx/) というツールを使用します。

     ダウンロードすると .\timeadjx-0.2\Release フォルダのに timeadjx.exe があるのでそれを使用します。

     まずは、1 秒あたり 10 秒進めるために 1560000 (= 156000ns × 10) を設定します。

    C>timeadjx set 1560000
    SetSystemTimeAdjustment() 呼び出し中...
    割込み発生時の加算時刻: 1560000 (100ns単位)
    定期的な時刻調整      : 有効
    自動的な時刻補正      : 無効
      (自動的な時刻補正の詳細は、KB232488を参照してください)
    

     実行するとものすごい勢い (1 秒で 10 秒分) で時刻が進むと思います。
     6 秒以上経過したらデフォルトに戻します。

    C>>timeadjx set 0
    SetSystemTimeAdjustment() 呼び出し中...
    割込み発生時の加算時刻: 0 (100ns単位)
    定期的な時刻調整      : 無効
    自動的な時刻補正      : 有効
      (自動的な時刻補正の詳細は、KB232488を参照してください)
    
  3. RTC の値を確認します。

     RTC(ハードウェアクロック)の値を確認するために RWEverything (http://rweverything.com/ )というツールを使用します。

     ダウンロードは RWportable~ というのを選択します。プラットフォームに応じて 32bit/64bit のいずれかをダウンロードします。

     解凍すると RW.exe というのがあるのでそれを実行します。

  4.  実行すると、メニューから「Specific」→「IO Index/Data」→「CMOS - 70/71」を選択すると、CMOS のデータを表示します。

     そうすると、RTC の時刻とシステムクロックの時刻がずれているのが確認できます。

     RTC の方は、04 番地が時、02 番地が分、00 番地が秒になっています。
     この例ではシステムクロックが 07:06:17 に対して RTC が 07:01:24 でシステムクロックが 5 分くらい進んでいます。

    Address 00h 01h 02h 03h 04h 05h 06h 07h 08h 09h
    Function
    Value(BCD) 24 01 07 19 07 20
  5. しばらく放置します。

     最大 1 時間くらい放置します。すると時刻が戻っていることに気付くはずです。
     イベントビューアをみると、システムログのソース「Kernel-General」、イベント ID=1 が記録されているのが確認できます。

     システムクロックが RTC に同期したこと、同期前と同期後の時刻が記録されています。
     22:19:35 から 22:14:43 に変更になったと書いてありますが UTC での記録ですから、JST だと 07:19:35 から 07:14:43 に変更ということですね。

  6. ふたたび RTC の値を確認します。

     RTC の時刻とシステムクロックの時刻が一致しているのが確認できます。
     この例ではシステムクロックと RTC がともに 07:20:14 となっていることが確認できます。

    Address 00h 01h 02h 03h 04h 05h 06h 07h 08h 09h
    Function
    Value(BCD) 14 20 07 19 07 20

[環境] [時刻] Windows で時刻あわせ (5) w32tm (トラブルシューティング)

2020年7月25日

 前回記事「Windows で時刻あわせ (4) w32tm (通信の確認)」はこちら

【注意と免責】
 以下の内容は個人的な見解であり正しさを保証しません。正しさを必要とする場合は他のサイトの記事を参照してください。

 w32tm は設定および時刻同期に失敗しやすいです。また失敗の原因が見えにくいこともリカバリーを難しくします。たとえば時刻 Client の設定をしていて時刻同期に失敗するとき、その原因が Client の設定ではなく Server 側の設定や挙動にある場合があります。

 ここでは代表的なトラブルを上げてみます。

  1. サーバーとクライアントの時刻の差が大きい

     境界値は知りませんが、例えば時刻が日単位で異なると時刻は同期しません。

  2. サーバーとクライアントの時刻の差が小さい

     実は正しく同期できているのですが、観測者の視点から時刻サーバーとの時刻差が見えるために時刻同期されていないと誤認されることがあります。
     デフォルトでは時刻差が 5 分以内の時に起こります。
     この辺りの詳しい動作は「step 調整」と「slew 調整」という語を検索してみて下さい。
     (slew 調整の意味が分からない場合は、w32tm を使用せず素直に桜時計を使いましょう。)

  3. w32tm /resync コマンを実行したが同期しない(時刻サーバーとの間に時間差がある)

     resync コマンドによって時刻を即同期するとは限りません。resync コマンドの help を見たらわかりますが、「今すぐ時刻の同期をとり直すようにコンピューターに指示します」と書かれています。つまり Windows time サービスに時刻の同期をとり直す "指示" を出すだけです。
     windows time サービスは指示された結果、「今は時刻合わせしなくていい」とか「サーバーから取得した時刻は信頼できないからダメだ」などの判断をする可能性があり、その結果時刻同期しない可能性があります。
     そもそも resync コマンドは「その場で step 調整するコマンド」ではありません。その点を勘違いしないようにしましょう。

  4. 通信できる状態であるはずなのに時刻設定しない

     時刻サーバーと通信できていない(例えばファイアウォールに阻まれている)のは論外として、時刻サーバーと通信できているはずなのに時刻を同期しない場合があります。
     考えられることとして、クライアントモードでアクセスしていない(対称モードでアクセスしている)可能性があります。
     クライアントモードでアクセスするためには、ntp サーバーの指定の際に末尾に ",0x8" または ",0x9" を付加して指定します。付加しない場合、対称モードでのアクセスになります。(Win7 まで。Win2008R2,Win8 以降は無指定の場合クライアントモードになるようです。)
     なぜ対称モードではだめなのか?についてはそういうものだと思ってください(汗

  5. 時刻を取得したが、時刻設定しない

     参照先の時刻サーバーから時刻を取得しているはずなのに同期しないことがあります。
     このような場合は、サーバーから取得した時刻が信頼できないものであった可能性があります。
     たとえば以下のような要因があります。

    • Stratum(階層)の値がおかしい。

       Stratum の値が 0 の場合、参照した時刻サーバーがさらに上位の時刻サーバーとの時刻同期に失敗しています。
       また、Stratum の値が 15 の場合は、その時刻サーバーとの同期はできません。

    • leap indicator(閏インジケーター)が 3(時刻同期していない)になっている。

       参照した時刻サーバーがさらに上位の時刻サーバーとの時刻同期に失敗しています。

    • Disparsion(ルート分散)の値が大きい。

       時刻のゆらぎが大きいと時刻が信頼できないと判断されることがあります。
       特に Windows の場合、同期する時刻サーバーがない場合、Disparsion の値は 10 秒で、この値だとクライアント側の w32tm は時刻が信頼できないと判断するようです。(多分。本当にそうであるかの検証は私はしていません。)

     上記の状態は、参照先の時刻サーバーが windows である場合、その参照先の時刻サーバー側で w32tm /query /status コマンド (Windows Vista, Windows Server 2008 以降) による確認することができます。

     たとえば以下の表示のようになっている時刻サーバーと同期しようとしても同期しません。なぜなら時刻サーバーが同期していないため時刻サーバーの時刻が信用できないからです。時刻サーバーとさらに上位の時刻サーバー間の通信が切断されていないかなどを確認してください。

    C>w32tm /query /status
    閏インジケーター: 3 (最後の 1 分間は 61 秒)
    階層: 0 (未指定)
    精度: -6 (ティックごとに 15.625ms)
    ルート遅延: 0.0000000s
    ルート分散: 10.0000000s
    参照 ID: 0x00000000 (未指定)
    最終正常同期時刻: 20xx/xx/xx xx:xx:xx
    ソース: Free-running System Clock
    ポーリング間隔: 10 (1024s)
    

     マーカー部分は表現は違えど皆「時刻は同期していません(=時刻に信頼性がありません)」と言っています。

  6. 時刻を取得しようとしない(通信しない)

     通信間隔が短い可能性があります。しばらく時間を置くか、通信先時刻サーバー(ピア)を変更してリトライするなどしてみてください。

  7. w32tm /resync を行って「コマンドは正しく完了しました。」と表示されるが時刻同期しない。

     「コマンドは正しく完了しました。」と表示されても、w32tm /query /status コマンドで階層(Stratum)が 0 になっているようだと時刻同期に失敗しています。前述のトラブル事例に該当するものがあるかどうかを確認してください。
     時刻同期に失敗しているのになぜ「コマンドは正しく完了しました。」と表示されるかというと、文字どおり「コマンドは正しく完了し」たからです。どこにも「時刻同期に成功しました」と書かれていない点に注意しましょう。
     時刻同期に成功したかどうかは w32tm /resync コマンドの表示ではなく、w32tm /query /status コマンドでソース(現在の時刻参照先サーバー)と階層(stratum)が正しい表示となっているかどうかで確認します。正しい表示とは、ソースが参照先の時刻サーバーになっていること、階層(Stratum)が参照先の時刻サーバーの値 + 1 となっていることです。

[環境] [時刻] Windows で時刻あわせ (4) w32tm (通信の確認)

2020年7月25日

 前回記事「Windows で時刻あわせ (3) w32tm (Server 側)」はこちら

【注意と免責】
 以下の内容は個人的な見解であり正しさを保証しません。正しさを必要とする場合は他のサイトの記事を参照してください。


 w32tm の設定作業で同期に失敗すると、なにがおかしいのか目に見えないので途方に暮れてしまいます。
 一応 w32tm /debug によるログ取得とかできますが、理解が難しいです。(多分)

 そこで、桜時計に通信の相手方になってもらい、どのような通信をしているのかを確認する方法を紹介します。

  1. 時刻サーバーからの応答の確認。

     同期したい時刻サーバー(下図では 192.168.0.1)に向けて桜時計を設定し、サーバーからの応答を確認します。
     LI と Stratum を確認します。Dispersion は確認できません。

    • OK な例

       LI(leap Indicator / 閏インジケータ)が 0, Stratum(階層)が 1 ~ 14 の間なので OK です。

    • NG な例

       LI が 3, Stratum が 0 で、ともに時刻同期ができていないという意味なので NG です。

  2. 時刻クライアント側からの要求の確認

     w32tm の manualpeerlist で指定する時刻サーバーのアドレスを桜時計を実行しているサーバーに向けます。

     MODE1 は対称モード (Synmetric Active)、MODE3 はクライアントモードです。
     MODE3 になっていれば OK です。

 また前提として UDP 123 の通信がファイアウォールで阻まれていないことは事前に確認しておいてください。
 w32tm /resync を実行しても桜時計に反応がない場合、ファイアウォール等で通信が阻害されている可能性があります。

 とはいえ、w32tm /resync を実行してもパケットを送出しないケースもあるのでその点は注意です。(汗
 間違いなく通信は通るはず…と思う場合は、ピア(=時刻サーバー)の IP アドレスを変えて試したり、w32tm /debug で記録されるログを確認するなどをしてみてください。

 また、dispersion やその他詳細なデータも確認したい、というニーズがある場合は、NTP Monitor (https://www.satsignal.eu/software/net.htm) とか良いかも知れませんので参考にしてみてください。
 

[環境] [時刻] Windows で時刻あわせ (3) w32tm (Server 側)

2020年7月25日

 前回記事「Windows で時刻あわせ (2) w32tm (Client 側)」はこちら

【注意と免責】
 以下の内容は個人的な見解であり正しさを保証しません。正しさを必要とする場合は他のサイトの記事を参照してください。

 時刻サーバーとして設定されているかどうかは、管理者権限のあるコマンドプロンプトで「w32tm /query /configuration」を実行することで確認できます。

C>w32tm /query /configuration
[構成]
----------------- (略) -----------------

[タイム プロバイダー]

NtpServer (ローカル)
DllName: C:\WINDOWS\system32\w32time.dll (ローカル)
Enabled: 1 (ポリシー)
InputProvider: 0 (ローカル)
AllowNonstandardModeCombinations: 1 (ローカル)

NtpClient (ローカル)
----------------- (略) -----------------

 NtpServer ブロックの Enabled が 1 になっていれば OK です。
 Enabled が 0 となっている場合は、グループポリシーの「コンピューターの構成→管理用テンプレート→システム→Windows タイムサービス→タイムプロバイダー」の「Windows NTP サーバーを有効にする」を「有効」にするか、レジストリ「\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer」にある Enabled を 1 にします。

 しかし場合によっては以下の設定を追加で行う必要があるかもしれません。

w32tm /config /update /reliable:yes /LocalClockDispersion:0 

 /reliable:yes は stratum を 0, leap indicator を 3 にしないための指定、/LocalClockDispersion:0 は分散(時刻のゆらぎ)が全くない(=0 秒)とする指定です。
 ちなみに「分散」は統計的な手法で求められてはいますが variance(=標準偏差を得る前の平方根を得る前の「分散」)ではなく dispersion なので日本語に惑わされないようにしましょう。(単位も s^2 じゃないからね?)

 また Windows XP 系の場合、上記オプションがないため、レジストリを直接編集します。
 場所は「\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time」の中にある以下の 3 つを変更します。

  1. Config にある AnnounceFlags を 5 に(/reliable:yes に対応) 。LocalClockDispersion を 0 (/LocalClockDispersion:0 に対応) にします。
  2. TimeProviders\NtpServer にある Enabled を 1 にします。(=Ntp Server として動作)
  3. w32time サービスを再起動します。
    net stop w32time
    net start w32time
    

 この設定は、時刻サーバーと上位の時刻サーバーとの通信が切断された場合に下位の時刻クライアントが同期しなくなることを防ぐために行います。逆に言うとこの設定を行わない場合、時刻サーバーと上位の時刻サーバーとの間に通信障害が起こると、この時刻サーバーを参照している「全ての」時刻クライアントが時刻同期しなくなる可能性があります。

 つまりこの設定は、時刻を参照させたい Windows の時刻が信頼に足らないものであっても「信頼性の高い時刻サーバーである」と騙らせる設定です。これにより時刻クライアントが時刻サーバーと同期しやすくなりますが、反面時刻サーバーの時刻がおかしくても時刻クライアントはそれに気づけないため、時刻同期できていない状況よりも深刻な問題を引き起こす可能性があります。したがって、この設定を採用する場合は、その点を考慮し採用が妥当かどうかを慎重に検討してください。

[環境] [時刻] Windows で時刻あわせ (2) w32tm (Client 側)

2020年7月25日

 前回記事「Windows で時刻あわせ (1) 桜時計」はこちら

【注意と免責】
 以下の内容は個人的な見解であり正しさを保証しません。正しさを必要とする場合は他のサイトの記事を参照してください。

 桜時計ではダメなんだ・・・という場合。windows time サービス、または w32time サービス、あるいは w32tm という windows 標準の仕組み(以下「w32tm」という呼び方で統一)があるんだからそれでやりたい、と思うこともあるでしょう。

 やめたほうがいいです(何

 必要とする機能が桜時計の機能で十分なら、桜時計の使用が一番楽ができるし確実で直感的です。もし桜時計を使いたくない理由が「Windows 標準でないから」「フリーウェアを使うのはちょっと」といった機能以外の理由なら、その理由を退けて桜時計を使う選択をしたほうがおそらく幸せになれます(汗

 それでも w32tm を使用するんだという場合、設定方法はグループポリシーを使用する方法と w32tm コマンドを使用する方法の2つがあります。

 内容的には変わりませんが、両方設定された場合、グループポリシーの設定のほうが優先されます。
 以下それぞれの手順を手短に紹介します。

  • グループポリシーを使用する方法
    1. グループポリシーエディタを起動します。(例えば 'Win' キー + 'R' キー で「ファイル名を指定して実行」を表示し、そこへ 'gpedit.msc' を入力)

    2. 「ローカル コンピューター ポリシー」→「コンピューターの構成」→「管理テンプレート」→「システム」→「Windows タイム サービス」→「タイム プロバイダー」とたどります。

    3. 「Windows NTP クライアントを構成する」を選択し、以下を設定します。

      • 「有効」を選択します。
      • 「NtpServer」に「<NTP サーバー名>,0x9」を設定します。たとえば ntp.nict.jp の場合は「ntp.nict.jp,0x9」です。0x9 が何かは他のページで調べてください。おまじないです。(0x9 = 0x8 + 0x1 で、0x8 は client mode、0x1 は同期間隔(0x0 は自動調整, 0x1 は固定、です。)
      • 「Type」は「NTP」を選択します。

    4. 「Windows NTP クライアントを有効にする」を選択し、以下を設定します。

      • 「有効」を選択します。

  • w32tm コマンドを使用する方法
    1. 管理者権限でコマンドプロンプトを起動します。

    2. 以下のコマンドを入力します。

      w32tm /config /update /manualPeerList:<NTP サーバー名>,0x9 /syncFromFlags:manual
      

      例えば ntp サーバーが ntp.nict.jp の場合は以下のようになります。

      w32tm /config /update /manualPeerList:ntp.nict.jp,0x9 /syncFromFlags:manual
      

  • 確認方法
    1. 設定した ntp サーバー(ピア)の確認

       コマンドプロンプトで「w32tm /query /peers」を実行します。(Windows Vista 以降。XP ではできません)

       実行すると以下のように表示されます。「ピア」のところに設定した ntp サーバー名が表示されていれば成功です。設定と違うものが表示されている場合、より優先される設定が行われている場合があります。その場合はローカルグループポリシーやドメインのポリシーなどに設定がないかを確認します。

      C>w32tm /query /peers
      ピア数: 1
      
      ピア: ntp.nict.jp,0x9
      状態: 保留中
      残り時間: 776.3186433s
      モード: 0 (予約)
      階層: 0 (未指定)
      ピアポーリング間隔: 0 (未指定)
      ホストポーリング間隔: 0 (未指定)
      

       Windows XP など w32tm /query コマンドがない OS で設定した内容を確認したい場合、レジストリエディタを使用すると設定内容を確認できます。場所は \HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters の NtpServer などに記録されています。

      また、グループポリシーで設定した値はここには記録されません。ここに記録されるのは w32tm コマンドで設定した内容だけです。

    2. 時刻同期の確認

       管理者権限のコマンドプロンプトで「w32tm /resync」を実行します。

       「コマンドは正しく完了しました。」と表示がでればとりあえず OK です。それ以外の場合は対策が必要です。(が、必ずしも問題は Client 側の設定ではないかもしれません。)

      C>w32tm /resync
      再同期コマンドをローカル コンピューターに送信しています。
      コマンドは正しく完了しました。
      

       「コマンドは正しく完了しました。」と表示された場合、w32tm /query /status を実行してみて、閏インジケーターが警告なし、階層が 0 以外、ソースが意図した ntp サーバー名になっていれば OK です。

      C> w32tm /query /status
      閏インジケーター: 0 (警告なし)
      階層: 2 (二次参照 - (S)NTP で同期)
      精度: -6 (ティックごとに 15.625ms)
      ルート遅延: 0.0312500s
      ルート分散: 7.7757688s
      参照 ID: 0x0A545792 (IPv6 アドレスの MD5 ハッシュ値の部分: )
      最終正常同期時刻: 20xx/xx/xx xx:xx:xx
      ソース: ntp.nict.jp,0x9
      ポーリング間隔: 6 (64s)
      
  • その他

     Windows 7 以降の場合、Windows Time サービスが「自動(トリガー開始)」になっている場合があります。

     この状態だと Windows Time サービスを起動してもすぐ停止してしまうので、管理者権限のコマンドプロンプトを起動して、以下のコマンドを実行し、トリガーを削除します。

    sc triggerinfo w32time delete
    
  •  実行後、サービス一覧をみて windows time サービスの「トリガー開始」の表示が消えていれば OK です。

    [環境] [時刻] Windows で時刻あわせ (1) 桜時計

    2020年7月25日

    【注意と免責】
     以下の内容は個人的な見解であり正しさを保証しません。正しさを必要とする場合は他のサイトの記事を参照してください。

     Windows で時刻合わせをしたいと思った場合、一番簡単で直感的なのは「桜時計」を使って時刻同期をすることです。Windows 95 時代の古いアプリケーションですが Windows 10 でもしっかり動きます。vector などでダウンロードできます。(例: https://www.vector.co.jp/soft/win95/personal/se050672.html)

     ダウンロードして中身をみると2つプログラムがありますが SW_NORAS.EXE のほうを使用します。

     立ち上げて放置するとすぐに終了してしまうので、表示されている間に「常駐する」にチェックします。すると自動でウインドウを閉じなくなるので、おちついて設定できるようになります。

     接続先のサーバーは ntp.nict.jp がおすすめです。デフォルトには入っていないので手入力します。
     また福岡大学(fukuoka-u.ac.jp)の NTP サーバーは絶対に使ってはいけません。(理由はぐぐってください)
     その他各設定の意味は README.TXT を参照してください。

     また桜時計を時刻サーバー(ntp server)にすることもできます。その場合は「SNTP サーバーとして動作する」にチェックを入れればいいだけです・・・と言いたいところですが下図のように「BIND エラー」になる場合があります。

     これは windows time サービスと競合しているためです。サービス画面 ('Win' キー + 'R' キー で「ファイル名を指定して実行」を表示し、そこへ 'services.msc' を入力) で windows time を停止/無効にするとエラーはでなくなります。(下図は停止/無効にする前の表示です。)

     桜時計はおそらく、どんな ntp client や ntp server よりも同期が楽です。エラーになった場合も表示がでるので「エラーだ」ということが捉えやすくその点でもおススメです。

    移転しました。

    2018年5月18日

     
     諸般の事情 (Mercury DB のサービス終了) をきっかけに、こちらに移転しました。

     移転元サイト (http://ooltcloud.azurewebsites.net/) は、既にサービス停止しています。

    [java] Tomcat のサーブレットで Hello World (Windows)

    2018年2月25日

     Tomcat を使ったサーブレットの作成および JSP の作成手順のメモ。
     今回は IDE に頼らず自力でコンパイル,デプロイする場合の手順です。

    Tomcat のインストール

    1. 前提条件

       JDK はインストールされていて、かつ、java.exe および javac.exe (= %JAVA_HOME%\bin) への Path は通っているものとします。

    2. Tomcat のダウンロード

       https://tomcat.apache.org/download-90.cgi から Tomcat をダウンロードします。今回は 64-bit Windows の ZIP 版を選択します。

    3. ダウンロードした ZIP の展開

       ダウンロードした ZIP の内容を解凍し、今回は C:\apache-tomcat-9.0.5 に配置します。

    4. Tomcat の起動

       C:\apache-tomcat-9.0.5\bin にある startup.bat を実行します。

       最終行に「Server startup in・・・」と表示されれば OK です。

    5. Tomcat の起動確認

       http://localhost:8080 にアクセスできることを確認します。

    Hello サーブレットの作成

    1. デプロイ先フォルダの作成

       C:\apache-tomcat-9.0.5\webapps に helloweb フォルダを作成します。その下に WEB-INF フォルダ、さらにその下に classes フォルダを作成します。

    2. サーブレットコードの作成

       C:\apache-tomcat-9.0.5\webapps\helloweb\WEB-INF\classes フォルダに helloweb.java というテキストファイルを作成します。

       helloweb.java をメモ帳などで開き、以下のコードを記述し保存します。

      import java.io.*;
      import javax.servlet.*;
      import javax.servlet.http.*;
      import javax.servlet.annotation.WebServlet;
      
      @WebServlet("/index")
      public class helloweb extends HttpServlet {
      
          public void doGet(HttpServletRequest request,HttpServletResponse response)
          throws IOException, ServletException
          {
              response.setContentType("text/html; charset=UTF-8;");
              PrintWriter out = response.getWriter();
      
              out.println("<html><body>");
              out.println("<p>こんにちは Tomcat</p>");
              out.println("</body></html>");
              out.close();
          }
      }
      
    3. コンパイル

       コンパイルします。

      C>cd C:\apache-tomcat-9.0.5\webapps\helloweb\WEB-INF\classes
      
      C>set CP=C:\apache-tomcat-9.0.5\lib\servlet-api.jar
      
      C>javac -cp %CP% helloweb.java
      
      C>dir
       ドライブ C のボリューム ラベルはありません
       ボリューム シリアル番号は 0000-0000 です
      
       C:\apache-tomcat-9.0.5\webapps\helloweb\WEB-INF\classes のディレクトリ
      
      2018/02/23  00:12    <DIR>          .
      2018/02/23  00:12    <DIR>          ..
      2018/02/23  00:12               898 helloweb.class
      2018/02/22  23:58               592 helloweb.java
                     2 個のファイル               1,490 バイト
                     2 個のディレクトリ  100,000,000,000 バイトの空き領域
      
      
    4. デプロイ

       コンパイルしたクラスファイルを Tomcat に読ませるために、Tomcat を再起動します。
       shutdown.bat を実行後、再度 startup.bat を実行します。

    5. 作成したサーブレットの確認

       http://localhost:8080/helloweb/index にアクセスします。

    Hello JSP の作成

    1. JSP コードの作成

       C:\apache-tomcat-9.0.5\webapps\helloweb フォルダに top.jsp というテキストファイルを作成します。

       top.jsp をメモ帳などで開き、以下のコードを記述し保存します。また今回はテキストファイルのエンコーディングを Shift JIS (=ANSI) で保存します。

      <%@ page language="java" contentType="text/html; charset=UTF-8;" pageEncoding="Shift_Jis" %>
      <html><body>
      
      <% out.print("<h1>Hello JSP (tomcat)</h1>"); %>
      <p>現在時刻は <%=new java.util.Date()%> です</p>
      
      </body></html>
      
    2. JSP の確認

       http://localhost:8080/helloweb/top.jsp にアクセスします。

    [java] Wildfly のサーブレットで Hello World (Windows)

    2018年2月25日

     WildFly を使ったサーブレットの作成および JSP の作成手順のメモ。
     今回は IDE に頼らず自力でコンパイル,デプロイする場合の手順です。

    WildFly のインストール

    1. 前提条件

       JDK はインストールされていて、かつ、java.exe および javac.exe (= %JAVA_HOME%;bin) への Path は通っているものとします。

    2. WildFly のダウンロード

       http://wildfly.org/downloads/ から WildFly をダウンロードします。今回は 11.0.0.Final の Java EE7 Full & Web Distribution の ZIP 版を選択します。

    3. ダウンロードした ZIP の展開

       ダウンロードした ZIP の内容を解凍し、今回は C:\wildfly-11.0.0.Final に配置します。

    4. WildFly の起動

       C:\wildfly-11.0.0.Final\bin にある standalon.bat を実行します。

       最終行に「WildFly・・・Started」と表示されれば OK です。

    5. WildFly の起動確認

       http://127.0.0.1:8080 にアクセスできることを確認します。

    Hello サーブレットの作成

    1. デプロイ先フォルダの作成

       C:\wildfly-11.0.0.Final\standalone\deployments に helloweb.war フォルダを作成します。その下に WEB-INF フォルダ、さらにその下に classes フォルダを作成します。

    2. サーブレットコードの作成

       C:\wildfly-11.0.0.Final\standalone\deployments\helloweb.war\WEB-INF\classes フォルダに helloweb.java というテキストファイルを作成します。

       helloweb.java をメモ帳などで開き、以下のコードを記述し保存します。

      import java.io.*;
      import javax.servlet.*;
      import javax.servlet.http.*;
      import javax.servlet.annotation.WebServlet;
      
      @WebServlet("/index")
      public class helloweb extends HttpServlet {
      
          public void doGet(HttpServletRequest request,HttpServletResponse response)
          throws IOException, ServletException
          {
              response.setContentType("text/html; charset=UTF-8;");
              PrintWriter out = response.getWriter();
      
              out.println("<html><body>");
              out.println("<p>こんにちは WildFly</p>");
              out.println("</body></html>");
              out.close();
          }
      }
      
    3. コンパイル

       コンパイルします。

      C>cd C:\wildfly-11.0.0.Final\standalone\deployments\helloweb.war\WEB-INF\classes
      
      C>set CP=C:\wildfly-11.0.0.Final\modules\system\layers\base\javax\servlet\api\main\jboss-servlet-api_3.1_spec-1.0.0.Final.jar
      
      C>javac -cp %CP% helloweb.java
      
      C>dir
       ドライブ C のボリューム ラベルはありません
       ボリューム シリアル番号は 0000-0000 です
      
       C:\wildfly-11.0.0.Final\standalone\deployments\helloweb.war\WEB-INF\classes の ディレクトリ
      
      2018/02/22  22:17    <DIR>          .
      2018/02/22  22:17    <DIR>          ..
      2018/02/22  22:17               899 helloweb.class
      2018/02/22  22:13               597 helloweb.java
                     2 個のファイル               1,496 バイト
                     2 個のディレクトリ  100,000,000,000 バイトの空き領域
      
      C>
      
    4. デプロイ

       C:\wildfly-11.0.0.Final\standalone\deployments フォルダに helloweb.war.dodeploy という空ファイル(0バイトのテキストファイル)を作成します。

       ファイル名が helloweb.war.deployed に変更されたらデプロイ完了です。

    5. 作成したサーブレットの確認

       http://127.0.0.1:8080/helloweb/index にアクセスします。

    Hello JSP の作成

    1. JSP コードの作成

       C:\wildfly-11.0.0.Final\standalone\deployments\helloweb.war フォルダに top.jsp というテキストファイルを作成します。

       top.jsp をメモ帳などで開き、以下のコードを記述し保存します。また今回はテキストファイルのエンコーディングを Shift JIS (=ANSI) で保存します。

      <%@ page language="java" contentType="text/html; charset=UTF-8;" pageEncoding="Shift_Jis" %>
      <html><body>
      
      <% out.print("<h1>Hello JSP</h1>"); %>
      <p>現在時刻は <%=new java.util.Date()%> です</p>
      
      </body></html>
      
    2. JSP の確認

       http://127.0.0.1:8080/helloweb/top.jsp にアクセスします。

    [鯖缶] hinemos 6 の Windows Agent が接続する Hinemos Manager を変更する

    2018年2月13日

     テストなとで Hinemos Agent の接続先 Manger を変更したい場合があったのでその時の手順のメモ。
     公式の資料があるかもしれないけど以下の内容はそれを見て書いていません。なのでそのような文書が存在するようであれば正式にはそちらを参照ください(汗

     Hinemos Agent の設定ファイルは C:\Program Files (x86)\Hinemos\Agent6.0.0\conf フォルダにあります。

    1. Agent.properties の変更

       Agent.properties を管理者で実行したメモ帳で開き、最下行に

      managerAddress=http://192.168.0.101:8081/HinemosWS/ 
      

      という設定があるので、このアドレスを変更したい manager のアドレスに変更します。ポート番号が 8081 とあるのは間違いではないのでそのままにしておきます。

    2. log4j.properties の変更

       必要なのかどうか分かりませんが log4j.properties の変更をします。
       log4j.properties 管理者で実行したメモ帳で開き、やはり最下行に

      log4j.appender.syslog.SyslogHost=192.168.0.101
      

      という設定があるので、同様にアドレスを変更します。

       ただ、最下行の一つ前の行の改行コードが CR-LF ではなく LF だけになっているようで、メモ帳だと最終行とその一つ前の行がくっついて一行に見えてしまう点には注意します。

      • LF を改行として認識してくれるエディタの場合
      • メモ帳(改行 LF を改行として認識しないエディタ)の場合
    3. 編集上の注意点

       メモ帳以外の、例えば LF を改行として認識できるエディタを安易に使用すると、場合によってはバーチャルストア上に変更が記録されてしまい、設定を変更したはずなのに(エディタで表示すると間違いなく変更されているのに)Hinemos Manager がその設定を認識しない、ということがあります。
       その場合、例えば以下のフォルダに変更内容が記録されています。(ログオンユーザーが user01 の場合の例)

      C>dir "%LOCALAPPDATA%\VirtualStore\Program Files (x86)\Hinemos\Agent6.0.0\conf"
       ドライブ C のボリューム ラベルがありません。
       ボリューム シリアル番号は 0000-0000 です
      
       C:\Users\user01\AppData\Local\VirtualStore\Program Files (x86)\Hinemos\Agent6.0.0\conf のディレクトリ
      
      2018/01/01  00:00    <DIR>          .
      2018/01/01  00:00    <DIR>          ..
      2018/01/01  00:00             6,531 Agent.properties
      2018/01/01  00:00             1,491 log4j.properties
                     2 個のファイル               8,022 バイト
                     2 個のディレクトリ  100,000,000,000 バイトの空き領域
      
      

       このフォルダに記録されているようであれば NG です。とりあえずこのフォルダのファイルは削除しましょう。
       またこの挙動が良くわからない人は、このような事態を避けるためにはメモ帳で編集したほうが無難です。

    4. 設定の反映

       設定変更後、Hinemos Agent サービスを停止してから C:\Program Files (x86)\Hinemos\Agent6.0.0\bin にある UnregistAgentService.bat と RegistAgentService.bat を順に実行すれば OK です。(管理者のコマンドプロンプトで実行します)

    5. 変更後の動作の確認

       Hinemos Agent サービスの起動を確認します。起動されていなければ手動で起動します。

       対象の Hinemos Manager の、リポジトリパースペクティブの "リポジトリ[エージェント]" ウインドウで対象ノードが表示されていれば OK です。

    [java] Windows における javapath と JAVA_HOME

    2018年2月9日

     javapath とか JAVA_HOME が分からなかったので調べた結果をメモ。以下が当たっているのかどうかは不明なので信用しないでください。

    1. javapath

       javapath というのは jre8 あたりから使用されている パブリック JRE のホスト先へのリンク(?)らしいです。
       パブリック JRE については 「プライベートJREとパブリックJRE」 を参照してください。

       具体的な場所は C:\ProgramData\Oracle\Java\javapath です。ここに java.exe, javaw.exe, javaws.exe が配置されます。
       したがって、PATH 環境変数に C:\ProgramData\Oracle\Java\javapath を追加しておけば、update で JRE のインストールフォルダが変更されても、それを意識することなく java.exe などを実行することができます。(PATH 環境変数への C:\ProgramData\Oracle\Java\javapath の追加は JRE のインストーラーがしてくれるようです。)

       ただ使用する場合は意識しなくていいですが、C:\ProgramData\Oracle\Java\javapath に java.exe などをホストする方法が前期の JRE8 と後期の JRE8 以降とで異なるようです。

       前期の JRE8 は javapath フォルダに java.exe などのシンボリックリンクを配置していました。(以下は JRE8 Update 45 の例)

      C:\ProgramData\Oracle\Java\javapath>dir
       ドライブ C のボリューム ラベルがありません。
       ボリューム シリアル番号は 0000-0000 です
      
       C:\ProgramData\Oracle\Java\javapath のディレクトリ
      
      2017/11/20  23:11    <DIR>          .
      2017/11/20  23:11    <DIR>          ..
      2017/11/20  23:11    <SYMLINK>      java.exe [C:\Program Files\Java\jre1.8.0_45\bin\java.exe]
      2017/11/20  23:11    <SYMLINK>      javaw.exe [C:\Program Files\Java\jre1.8.0_45\bin\javaw.exe]
      2017/11/20  23:11    <SYMLINK>      javaws.exe [C:\Program Files\Java\jre1.8.0_45\bin\javaws.exe]
                     3 個のファイル                   0 バイト
                     2 個のディレクトリ  10,000,000,000 バイトの空き領域
      

       後期の JRE8 からは javapath_target_xxxxxxxxx フォルダに java.exe をコピーしたうえで、javapath_target_xxxxxxxxx へのジャンクションを張るようです。(以下は JRE9 の例)

      C:\ProgramData\Oracle\Java>dir
       ドライブ C のボリューム ラベルがありません。
       ボリューム シリアル番号は 0000-0000 です
      
       C:\ProgramData\Oracle\Java のディレクトリ
      
      2018/01/17  20:08    <DIR>          .
      2018/01/17  20:08    <DIR>          ..
      2018/01/17  20:44    <DIR>          .oracle_jre_usage
      2018/01/17  20:04    <DIR>          installcache_x64
      2018/01/17  20:04    <JUNCTION>     javapath [C:\ProgramData\Oracle\Java\javapath_target_162006234]
      2018/01/17  20:04    <DIR>          javapath_target_162006234
                     0 個のファイル                   0 バイト
                     6 個のディレクトリ  10,000,000,000 バイトの空き領域
      
      C:\ProgramData\Oracle\Java>dir javapath
       ドライブ C のボリューム ラベルがありません。
       ボリューム シリアル番号は 0000-0000 です
      
       C:\ProgramData\Oracle\Java\javapath のディレクトリ
      
      2018/01/17  20:04    <DIR>          .
      2018/01/17  20:04    <DIR>          ..
      2018/01/17  20:04           231,488 java.exe
      2018/01/17  20:04           232,000 javaw.exe
      2018/01/17  20:04           335,424 javaws.exe
                     3 個のファイル             798,912 バイト
                     2 個のディレクトリ  10,000,000,000 バイトの空き領域
      

       JRE7 以前は javapath に相当するフォルダは %SystemRoot%\System32 で、そこに パブリック JRE の java.exe 等をコピーしていたようです。(未確認)

       ところで javapath には java.exe とかしかないけど、そこからどうやって jvm.dll とかを探しているんですか?って話については、自身(java.exe とか) のバージョンをキーにしてレジストリを参照しているんじゃないですかね?多分。しらんけど(汗

    2. JAVA_HOME

       JAVA_HOME とは JAVA アプリケーションがランタイムの場所を知るために使う環境変数らしいです。
       転じて使用する JRE を切り替えるために使われたりするようです。

       例えば C:\Program Files\Java\jdk1.8.0_161 にあるランタイムを使用したい場合は、JAVA_HOME 環境変数に C:\Program Files\Java\jdk1.8.0_161 を設定します。

       JAVA_HOME 環境変数が設定されていれば、アプリケーションは %JAVA_HOME%\bin\java.exe -jar myApp.jar のように実行すればいいということですね。
       ただ JAVA_HOME の設定だけだとコマンドプロンプトから実行時に上記のように JAVA_HOME も記述しないとならず煩わしいので、併せて PATH 環境変数に %JAVA_HOME%\bin; を設定することが多いようです。

       また JAVA_HOME の設定は JRE や JDK のインストーラーが自動で設定しないので、必要であれば手動で設定する必要があります。

    [鯖缶] hinemos 6 で Internal Error が発生する

    2018年2月5日

     時折、Hinemos がインターナルエラーを記録していることがあります。


     多分、Hinemos Manger をホストしているサーバーの時刻が意図している時刻と異なるので再確認してみてください。

    [root@localhost ~]# date
    2018年  2月  5日 月曜日 05:07:12 JST      ←この時刻は意図している時刻ですか?
    

    [鯖缶] hinemos 6 で Windows のイベントログを監視

    2018年2月5日
    1. 監視対象ノードで Hinemos Agent Service が起動していることを確認します。

       イベントログの監視は Hinemos Agent を経由した監視なので Hinemos Agent Service が起動している必要があります。

    2. 「EVENT FOR TRAP」の設定を変更します。

       「EVENT FOR TRAP」の設定を SNMP TRAP 監視の場合と同様にしておきます。
       設定内容は [鯖缶] hinemos 6 で Windows の SNMP Trap を監視 の 2 項を参照してください。

    3. Windows イベント監視の設定をします。

       「監視設定パースペクティブ」の”監視設定[一覧]” ウインドウで「Windows イベント監視」を作成します。
       今回は Application ログに記録するイベントソース EventTest の ID = 1 を監視することにします。


    4. Powershell を管理者として実行し テスト用のイベントソース (EventTest) を作成します。
      New-EventLog -LogName Application -Source EventTest
      
    5. Powershell からイベントを書き込みます。
      Write-EventLog -LogName Application -Source EventTest -EventId 1 -EntryType Information -Message "イベント01"
      
    6. 記録されることを確認します。

    [鯖缶] hinemos 6 で Windows の SNMP Trap を監視

    2018年2月1日

     標準的に用意されている Trap は以下らしいです。
     (ただし Windows が以下の全ての OID を Trap 送信するかどうかは確認していません)

    OID 内容
    .1.3.6.1.6.3.1.1.5.1 Cold Start
    .1.3.6.1.6.3.1.1.5.2 Warm Start
    .1.3.6.1.6.3.1.1.5.3 Link Down
    .1.3.6.1.6.3.1.1.5.4 Link Up
    .1.3.6.1.6.3.1.1.5.5 Authentication Failure

     設定は以下な感じで行います。

    1. 監視対象ノード (Windows) の SNMP Service の実行中を確認します。

       SNMP Service が実行していることを確認します。また SNMP Service の「トラップ」タブを確認し、トラップ送信先の設定が Hinemos Manager に向いていることを確認します。(以下の画像の例だと 192.168.0.101 が Hinemos Manager)
       また SNMP TRAP Service の起動は必要ありません。(これはその Windows が SNMP Trap を受信するためのサービスなので。多分)

    2. TRAP 受信するたびにイベント通知するように「EVENT FOR TRAP」の設定を変更します。

      「監視設定パースペクティブ」の"監視設定[通知]" ウインドウで「EVENT FOR TRAP」の設定をクリックします。

       「EVENT FOR TRAP」の設定を以下のように設定します。
       具体的には「重要度変化後の二回目以降の通知」を「常に通知する」に変更します。

    3. SNMPTRAP 監視の設定を変更します。

       「監視設定パースペクティブ」の"監視設定[一覧]" ウインドウで、既定で設定されている監視項目 ID「SNMPTRAP_DEFAULT」をクリックし以下のように変更します。
       具体的には、コミュニティ名は未指定のままとし「未指定のトラップ受信時に通知する」にはチェックを入れます。(とりあえず来るものは全て受け入れということで ^^;)
       また、通知に「EVENT FOR TRAP」を選択します。

    4. 監視対象ノードを再起動し、テストを行います。

       例えば結果はこんな感じで記録されます。(Windows の起動時)

    [鯖缶] Hinemos 6 で Windows のリソースを監視

    2018年1月31日

     リソース監視の設定は監視項目をプルダウンから選ぶだけです。

     リソース値は SNMP 経由で取得するため、監視対象ノードで SNMP サービスが起動している必要があります。またファイアウォールの設定も忘れずに行っておきます。(プロセス監視等と同様です。ファイアウォールの設定や SNMP サービス等の設定は「[鯖缶] Hinemos 6 agent for windows のインストール」を参照してください。)

     その他、リソース監視に関する注意事項等は以下。

    1. リソース値と OID の対応について

       監視項目とアクセス先の OID の対応は以下のようです。
       (snmpwalk の結果から推測しただけなので違うかもしれません。)

      1. Hinemos agent をインストールしなくても監視可能な項目
        監視項目 アクセス先の OID
        CPUコア別使用率 .1.3.6.1.2.1.25.3.3.1.2
        ファイルシステム使用率 .1.3.6.1.2.1.25.2.3.1
        パケット数
        エラーパケット数
        ネットワーク情報量
        .1.3.6.1.2.1.2.2.1
      2. 監視するために Hinemos agent のインストールが必要な項目
        監視項目 アクセス先の OID
        CPU使用率
        スワップI/O
        インタラプトレート
        コンテキストスイッチ
        .1.3.6.1.4.1.2021.11
        メモリ使用率 .1.3.6.1.4.1.2021.4
        ディスクI/O回数
        ディスクI/O量
        .1.3.6.1.4.1.2021.13.15
        大容量ファイルシステム使用率 .1.3.6.1.4.1.2021.9.1

       大きく分けて MIB-2 (.1.3.6.1.2.1) を参照する項目と Enterprise.2021 (.1.3.6.1.4.1.2021) を参照する項目とがあります。前者が Windows の SNMP が標準で返す値、後者が Hinemos agent をインストールすることで組み込まれる SNMP 拡張エージェントが返す値です。

       前者は Hinemos agent をインストールしなくても取得できます。
       後者は Hinemos agent のインストールが必要です。しかし Hinemos agent がインストールされていれば Hinemos agent サービスが停止していても取得できます。(SNMP サービスは起動している必要はありますが。)

    2. 似た名称のリソース値について

       監視項目に「CPUコア別使用率」と「CPU使用率」、「ファイルシステム使用率」と「大容量ファイルシステム使用率」と似た名称のものがあります。これらの違いは以下です。

      1. 「CPUコア別使用率」と「CPU使用率」

         「コア別」か「全体」かの違いです。

      2. 「ファイルシステム使用率」と「大容量ファイルシステム使用率」

         「ファイルシステム使用率」は SNMP から ディスク容量 (byte) と使用量 (Byte) を取得し、Hinemos manager が使用率 (%) を計算します。このためボリュームの容量が 2TB (多分。Int32 値なので) を超える場合オーバーフローしてしまうため計算できません。「大容量ファイルシステム使用率」は SNMP エージェント側で使用率 (%) を計算するので 2TB 以上のボリュームでも使用率を得ることができます。
         したがって、ボリューム容量が 2TB 以下で hinemos agent をインストールしない場合以外は「ファイルシステム使用率」を選択するメリットはないため、常に「大容量ファイルシステム使用率」をを選択すればよいと思います。

    3. 拡張エージェントの MIB の内容について

       Enterprise.2021 に snmpwalk でアクセスしてみると以下の出力が得られます。
       つまり以下に列挙されているリソース値が Hinemos の SNMP 拡張エージェントによる出力です。

      # Drive は A: FDD, C: HDD, Q: CD-ROM があると仮定しています。
      # 取得値はすべて 0 にしています。
      # 緑色のマーク箇所および空行は見やすくするために加工しています。
      snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.4.1.2021
      
      -- .1.3.6.1.4.1.2021.4
      UCD-SNMP-MIB::memTotalSwap.0 = INTEGER: 0 kB
      UCD-SNMP-MIB::memAvailSwap.0 = INTEGER: 0 kB
      UCD-SNMP-MIB::memTotalReal.0 = INTEGER: 0 kB
      UCD-SNMP-MIB::memAvailReal.0 = INTEGER: 0 kB
      UCD-SNMP-MIB::memTotalFree.0 = INTEGER: 0 kB
      UCD-SNMP-MIB::memBuffer.0 = INTEGER: 0 kB
      UCD-SNMP-MIB::memCached.0 = INTEGER: 0 kB
      
      -- .1.3.6.1.4.1.2021.9.1
      UCD-SNMP-MIB::dskPath.1 = STRING: A:\\
      UCD-SNMP-MIB::dskPath.3 = STRING: C:\\
      UCD-SNMP-MIB::dskPath.18 = STRING: Q:\\
      
      UCD-SNMP-MIB::dskPercent.1 = INTEGER: -1
      UCD-SNMP-MIB::dskPercent.3 = INTEGER: 0
      UCD-SNMP-MIB::dskPercent.18 = INTEGER: 0
      
      -- .1.3.6.1.4.1.2021.11
      UCD-SNMP-MIB::ssSwapIn.0 = INTEGER: 0 kB
      UCD-SNMP-MIB::ssSwapOut.0 = INTEGER: 0 kB
      UCD-SNMP-MIB::ssCpuRawUser.0 = Counter32: 0
      UCD-SNMP-MIB::ssCpuRawNice.0 = Counter32: 0
      UCD-SNMP-MIB::ssCpuRawSystem.0 = Counter32: 0
      UCD-SNMP-MIB::ssCpuRawIdle.0 = Counter32: 0
      UCD-SNMP-MIB::ssCpuRawWait.0 = Counter32: 0
      UCD-SNMP-MIB::ssCpuRawKernel.0 = Counter32: 0
      UCD-SNMP-MIB::ssCpuRawInterrupt.0 = Counter32: 0
      UCD-SNMP-MIB::ssRawInterrupts.0 = Counter32: 0
      UCD-SNMP-MIB::ssRawContexts.0 = Counter32: 0
      
      -- .1.3.6.1.4.1.2021.13.15
      UCD-DISKIO-MIB::diskIOIndex.1 = INTEGER: 1
      UCD-DISKIO-MIB::diskIOIndex.3 = INTEGER: 3
      UCD-DISKIO-MIB::diskIOIndex.17 = INTEGER: 17
      
      UCD-DISKIO-MIB::diskIODevice.1 = STRING: A
      UCD-DISKIO-MIB::diskIODevice.3 = STRING: C
      UCD-DISKIO-MIB::diskIODevice.17 = STRING: Q
      
      UCD-DISKIO-MIB::diskIONRead.1 = Counter32: 0
      UCD-DISKIO-MIB::diskIONRead.3 = Counter32: 0
      UCD-DISKIO-MIB::diskIONRead.17 = Counter32: 0
      
      UCD-DISKIO-MIB::diskIONWritten.1 = Counter32: 0
      UCD-DISKIO-MIB::diskIONWritten.3 = Counter32: 0
      UCD-DISKIO-MIB::diskIONWritten.17 = Counter32: 0
      
      UCD-DISKIO-MIB::diskIOReads.1 = Counter32: 0
      UCD-DISKIO-MIB::diskIOReads.3 = Counter32: 0
      UCD-DISKIO-MIB::diskIOReads.17 = Counter32: 0
      
      UCD-DISKIO-MIB::diskIOWrites.1 = Counter32: 0
      UCD-DISKIO-MIB::diskIOWrites.3 = Counter32: 0
      UCD-DISKIO-MIB::diskIOWrites.17 = Counter32: 0
      
      -- EOF?
      UCD-DISKIO-MIB::diskIOWrites.17 = No more variables left in this MIB View (It is past the end of the MIB tree)
      

       しかし Hinemos の拡張エージェントが NET-SNMP の持ち物である Enterprise.2021 を実装してしまうのはどうなのかな?という気は若干します。
       Linux と Windows とで アクセス先 (=OID) を同じにすることで OS を差異をなくし、Manager は監視対象ノードの OS を意識せずアクセスできるようになるというのは理解できるのですが、行儀がわるい実装な気がしますね・・・(汗

    [鯖缶] Hinemos 6 のカスタム監視 (Windows)

    2018年1月23日

     Windows に対する SNMP 監視やプロセス監視は ASCII 以外の文字が通らないという話を「[鯖缶] Hinemos 6 で windows のプロセスを監視」と「[鯖缶] hinemos 6 の文字化けについて」で書きました。(もしかしたら上手く通す方法はあるのかもしれませんが)
     「いやでも、どうしても ASCII 以外の文字を Windows から拾わないと」という場合は Hinemos agent を介したカスタム監視をするより他にありません。

     ということでカスタム監視を試してみます。

     プロセス監視の代わりのカスタム監視を考えてみます。
     以下がその手順です。

    1. 監視対象ノードで Hinemos agent が動作していることを確認します。

       監視対象の Windows で hinemos agent サービスが動作していることを確認します。
       また、リポジトリパースペクティブの "リポジトリ[エージェント]" ウインドウに監視対象ノードが表示されていることを確認します。(Windows 側で hinemos agent サービスが動作していても、"リポジトリ[エージェント]" ウインドウに表示されていない場合は動作しません。)

    2. 監視設定パースペクティブの "監視設定[一覧]" でカスタム監視(数値)を選択します。

    3. 以下の項目を画面の通りに設定します。
      • 監視項目 ID
      • スコープ
      • 間隔
      • 監視
      • 通知ID
      • アプリケーション

    4. コマンドを設定します。

       コマンドを以下のように設定します。

      powershell -command "$m = (Get-WmiObject win32_process | ? {$_.name -match 'notepad'}).CommandLine | ? {$_ -match '新しい'}; 'value,{0}' -f $m.count"
      

       上記はコマンド欄が一行でしか書けないのでワンライナーで書いていますが、以下の Powershell スクリプト相当のことを行っています。

      # wmi 経由でプロセス情報を取得する
      $a = Get-WmiObject Win32_Process
      
      # プロセス名に 'notepad' が含まれるものを抽出する
      $b = $a | 
      ? {
          $_.name -match 'notepad'
      }
      
      # さらにコマンドラインに '新しい' が含まれるものを抽出する
      $m = $b.CommandLine | 
      ? {
          $_ -match '新しい'
      } 
      
      # 結果(プロセス数)を key,value の形式で出力する 
      'value,{0}' -f $m.count
      

       このスクリプトをワンライナーに書き換えて、Powershell.exe の -command オプションに渡しています。

       また win32_process の CommandLine の値はプロセス名と引数が分かれずに一つの文字列として得られます。
       たとえば以下のようにで戻ってきます。

      "C:\WINDOWS\system32\notepad.exe"  "C:\Users\user01\Desktop\新しいテキスト ドキュメント.txt"
      

       従ってこの点は SNMP を使った標準のプロセス監視と正規表現の表現方法が異なるので注意します。

    [鯖缶] Hinemos 6 の文字化けについて

    2018年1月21日

     さて次は Himeos でリソース監視を…と思ってダイアログを開くとこんな表示がありました。
     なにやら文字が化けています。

     よく見るとリポジトリの登録状況がこんな・・・

     原因はノードの作成時に情報を SNMP 経由で取得しているのですが、その際にデバイス名が ASCII 文字以外の文字が使われてたからです。(今回の場合は「仮想イーサネットアダプター」)

    1. どうして?の前に、先に結論(汗

       Windows に対して SNMP で取得した文字列は文字化けします。具体的には、ノード登録時の SNMP による探索、SNMP 監視、プロセス監視が該当です。

    2. 対策

       ノードの登録情報は化けている部分を手入力で修正することで対応します。
       SNMP 監視とプロセス監視は対策はありません。非 ASCII 文字が監視対象にならないように注意します。どうしても何とかしたい場合にはカスタム監視を検討します。

    3. 原因 

       なぜ文字化けが起こるかというと hinemos では SNMP でデータを取得する際のエンコードは UTF-8 固定でしか取得しないからです。対して Windows の SNMP は Shift-JIS で応答します。この結果文字化けが発生します。
       従って SNMP でデータを取得する SNMP 監視はもちろんのこと、SNMP 経由で情報を取得するリポジトリへのノードの作成/変更やプロセス監視も影響を受けます。SNMP Trap 監視だけは文字コードが指定できるので大丈夫です(多分)

    4. 確認

       本当にそうであるのかどうかを確認してみます。ソースコードは https://github.com/hinemos/hinemos/find/master で公開されています。以下の引用は 2017/1/15~20 頃にしていますので多分 version 6.0.* あたりのコードです。

       まずプロセス監視のコードを確認してみます。なぜかというとプロセス監視では以下の理由で Linux 系と Windows で処理が異なると予想されるからです。

       プロセス監視は SNMP 経由で情報を取得します。例えば Path を取得するために OID = .1.3.6.1.2.1.25.4.2.1.4 をアクセスします。このとき Linux 系はファイル名を含めて戻しますが (例: HOST-RESOURCES-MIB::hrSWRunPath.1 = STRING: "/usr/lib/systemd/systemd")、Windows は ディレクトリ名まででファイル名を戻しません。(例: HOST-RESOURCES-MIB::hrSWRunPath.388 = STRING: "C:\\WINDOWS\\system32\\") 従って Linux の OID = .1.3.6.1.2.1.25.4.2.1.4 の内容(ディレクトリ名+ファイル名)の内容に合わせたいと考えたなら Windows では OID = .1.3.6.1.2.1.25.4.2.1.4(ディレクトリ名)に加えて OID = .1.3.6.1.2.1.25.4.2.1.5 (ファイル名)を取得して連結する必要があります。

       これを hinemos では RunMonitorProcess.java で実施していると思われます。該当部分は以下です。

      // Windowsの場合を考慮して、valueCommandとvalueNameを連結する
      if (command.length() == 0) {
          // パスが取得できない場合はコマンド名
          // パスがnull OR 空文字
          command = name;
      } else if (!command.startsWith("/")
              && command.endsWith("\\")
              && !command.equals(name)) {
          // 条件・・・・
          // パスが'/'以外の文字で始まり
          // パスが'\'で終わっていて
          // パスとコマンド名が違う
      
          command = command + name;
      }
      

       このコードで分かることは Linux 系か Windows かの判定は、ノードの情報(プラットフォームや OS 名)や監視設定で設定するスコープなどを見ているのではなく SNMP で取得した値を元に判断しているということです。つまり文字コードは意識していない可能性が高いということです。なぜならば Windows の時だけ文字コードの変換を行っているところがあるならそこで command = command + name; を行えばよく「/ で始まらず \ で終わる」のような条件で分岐する必要がないからです。(*1)

       また、この command や name に入る文字列を作っているのは Snmp4jPollerImpl.java であろうかと思います。以下に該当部分のコードを引用します。

      case SMIConstants.SYNTAX_OCTET_STRING:
          OctetString octStr = (OctetString) variable;
          StringBuilder value = new StringBuilder();
          byte[] bytes = octStr.getValue();
      
          ・・・・・
      
          } else {
              // WindowsのNIC名には0x00が含まれることがあるので除外する
              int length = bytes.length;
              for (int i = 0; i < bytes.length; i++) {
                  if (bytes[i] == 0x00) {
                      length = i;
                      break;
                  }
              }
              value.append(new String(bytes, 0, length));
          }
      

       byte[] bytes = octStr.getValue(); で SNMP を経由してバイトストリームを取得しています。
       バイトストリームを取得してから後は Windows を考慮してバイトストリーム中に 0x00 が含まれていた場合そこで打ち切る処理がありますが、それ以外にバイトストリームを操作する処理はありません。最終的に new String(bytes, 0, length) でバイトストリームを文字列に変換しています。このコンストラクタを使う場合、文字コードはプラットフォームデフォルトです。(*2)

       というような状況であるため、Windows から SNMP 経由で ASCII 文字以外を取得するのはおそらく絶望的です。(*3)

    5. この問題は実際に発生するか?

       恐らく SNMP 経由で ASCII 以外の文字列を取得するケースはほぼないと思います。
       リポジトリの登録情報は手修正で回避できますし、SNMP 監視で監視するデータで ASCII 以外が必要になることはほぼないかと思います。
       もっとも顕在化の可能性がありそうなのはプロセス監視ですが、プロセス名や引数にしたところで ASCII 以外の文字を使うことはほぼないと思われる) ので、この問題が実際に顕在化することはあまりないのではないかと思います。


    (*1)
     このつくりは賛否両論ありそうです。

     利点は他の設定値等を使用せずに取得した値のみで解決できることです。従って Windows なのか Linux なのかを指定しなくてもうまく対処することができます。

     欠点は想定外のケースに対応できないことです。今回の場合は後者が顕在化してしまい上手くいかないケースだと思います。つまり SNMP が返す文字列の違いだけではなく文字コードも違うという点は想定に入ってない (仕様に含めていない) ということです。
     かといって文字コードを違いを想定に追加するとした場合、現在のコードでは値をみるときには既にバイトストリームはデコードされた後ですから再デコードできませんし、仮にあらためてバイトストリームから再度デコードするとしても「Windows だったら絶対に Shift-JIS でしか戻らない」をハードコードしてしまうことになります。そうすると結局は柔軟性を失わせる結果になってしまいます。

     ここは SNMP TRAP 監視がそうしているように、SNMP 監視やプロセス監視に対しても文字コードの指定が行えるようにしたほうがよりよかった気はします。
     もちろん文字コードの指定だけだと内部的に command + name しているのはいいのか?という問題は残ったままになりますから、文字コードの指定ができれば全て解決というわけでもありません。その点ではプロセス監視については文字コードの指定ではなく OS の指定ができるようにしたほうがより良い気がします。
     なぜならば OS に依存しないコードにしようとした結果、データから OS を類推しそれによって OS に依存した処理を書いており、結局は OS 依存のコードを埋め込んでしまっているからです。それよりはプロセス監視設定に OS の項目を入れてそれにより分岐したほうがスマートである気がします。

    (*2)
     
    https://docs.oracle.com/javase/jp/6/api/java/lang/String.html#String(byte[], int, int)

    (*3)
     システムの文字コードを ja_JP.UTF-8 から ja_jp.shift_jis に代えます?それはそれで非現実的な気がします。
     仮にそうして Windows が OK になったとても UTF-8 な Linux で通らなくなりそうですし。

    [時事] [Visio] 数式エディタ3.0 の廃止

    2018年1月19日

     先日、Microsoft office の数式エディタ終了のお知らせで色々終了のお知らせ という togetter がバズっていました。

     なぜ削除されたかというと、"[バカすぎ][解決]マイクロソフト数式エディタ3.0のソースコードを紛失し、手作業で修正しようとしてついにあきらめて削除する" によると、タイトルの通りらしいです(汗

     ともかく要点は以下の模様。

    • 「Microsoft 数式 3.0」に脆弱性が見つかった。
    • 「Microsoft 数式 3.0」は互換性のために残されていた。
    • Ofiice 2007 からは「Microsoft 数式 3.0」ではなく新しい数式エディターが用意されている。
    • なので今回「Microsoft 数式 3.0」削除した。今後は新しい数式エディターを使おう(しろめ

     ということで確認してみました。(但し Office 2013 と Visio 2013 での確認です。)

    1. 「Microsoft 数式 3.0」を貼り付けたドキュメントを開き、数式をクリックしてみると確かにエディタが開かずエラーのダイアログが出ます。しかし数式は見えるので転記は可能です。(貼り付け方にもよるかもしれないが)
    2. 新しい数式エディターを確認してみます。Excel/Word/Powerpoint にはありました。今後はこれで記述するようにします。
    3. しかし Visio には新しい数式エディターはありませんでした・・・(汗

     確認の結果「修正はできないが記述した数式自体は表示できる」「Excel や Word には新しい数式エディタはあるが Visio にはない」ことが分かりました。従って、Word や Excel については、修正が必要になるたびに新しい数式エディターで転記していく、というのが基本路線になりそうです。

     しかし Visio で数式が書けないのは困ります。
     なので Visio で数式を記述したい場合は、一旦 Word で数式を記述し、それを Visio に貼りこむようにします。
     手順としては以下のような感じです。

    1. Word で数式を書きます。
    2. Word で数式の行全体を選択してコピーします。
    3. コピーの際、以下のように「オブジェクト」自体を選択しないようにします。行全体を選択するようにしてください。
    4. Visio で「形式を指定して貼り付け」を選択します。
    5. 「Microsoft Word 文書」を選択します。(ここで「Microsoft Word 文書」が表示されなかったらコピーに失敗しているので、項目 1 を再度行ってください)
    6. 貼り付けると数式が表示されます。
    7. 数式をダブルクリックすると数式が編集できます。(メニューのタブが Visio ではなく Word のものになっていることに注意)

    [文字] 通信における文字コードによる文字化けの確認方法

    2018年1月17日

     以下は Shift JIS と UTF-8 の文字コード不一致について確認する方法です。

     例えば送信元の文字列を以下のように設定します。

    縺ゅ@.txt
    

     これを受信側が上記の通り表示すれば文字コードは一致しています。しかし以下のように表示したら、送信側は Shift JIS で渡し受信側は UTF-8 で受けています。

    あし.txt
    

     同じように送信側の設定を「あし.txt」とし受信側で「縺ゅ@.txt」で表示されるなら、送信側は UTF-8 で渡し受信側は Shift JIS で受けています。

     バイナリにすれば共に E3-81-82-e3-81-97 (%e3%81%82%e3%81%97) です。

    [鯖缶] Hinemos 6 で windows のプロセスを監視

    2018年1月15日

     この記事 (http://ooltcloud.sakura.ne.jp/blog/201801/article_06231953.html) の続きです。
     以下はこの記事で作成したリポジトリの登録を前提としています。

     プロセス監視の設定にあたって 2 つ重要な点があります。

    • プロセスの設定は正規表現で記述するのですが、これがマッチしないことがある。

       たとえばメモ帳の起動(notepad.exe)を確認したい場合、"notepad" ではなく ".*notepad.*" のように書く必要があります。
       したがってどのようなコマンドラインが渡り、それをマッチする正規表現は何かを事前にテストしたうえで入力したほうが確実です。(詳細は 「プロセス監視の対象となるコマンドライン引数について」 へ)

    • プロセスを起動/停止してもそれを SNMP で検知するまでにかなりの時間がかかる。

       Windows の場合、手持ちの環境で実測したところ MIB の更新が 2 分周期のようでした。
       したがって Hinemos で 1 分周期の設定にしていても、最遅の場合 3分 (=2分 + 1分) しないと Himemos には伝わらないことになります。加えて、同じ重要度の監視結果が複数回発生しないと通知しないことにしていると(=“監視設定[通知]” の設定) さらにその分遅れます。
       なのでテストをする場合は、実際のプロセスの起動/停止からかなりの遅延が発生することを意識して行う必要があります。

     特にこの 2 つがあわさると、確認に時間ばかりかかりいつまでたっても意図した結果が得られないというループに入ることがあるので要注意です(汗

     以下は監視対象ノード (Windows) で hosts ファイルを開いたメモ帳が起動しているかどうかを確認する場合の設定手順です。

    1. 手順
      1. Hinemos リポジトリの監視対象ノードの SNMP 設定を確認します。

         リポジトリパースペクティブの “リポジトリ[ノード]” ウインドウで監視対象ノードをクリックし SNMP の設定を確認します。
         コミュニティ名やバージョンが意図したとおりであれば OK です。

      2. 監視対象ノードの SNMP サービスの起動を確認します。

         プロセス監視は Hinemos エージェントのインストールは不要ですが SNMP を介して情報を取得するので、SNMP サービスが起動している必要があります。

      3. コミュニティ名を確認します。

         コミュニティ名が意図したものであることも忘れずに確認しておきます。

      4. 監視設定を作成します。

         監視設定パースペクティブの “監視設定[一覧]” ウインドウで「作成」を選択します。

      5. プロセス監視を選択します。
      6. プロセス監視の内容を設定します。

         コマンドに以下を設定します。

        ^(|.*\\)notepad\.exe$
        

         引数に以下を設定します。

        ^( |.*\\)*hosts *$
        

         「大文字・小文字を区別しない」にチェックを入れます。

         また、以下の項目も画面の通りに設定します。

        • 監視項目 ID
        • スコープ
        • 間隔
        • 監視
        • 通知ID
        • アプリケーション

      7. 監視の確認をします。

         最初はメモ帳を起動していないので「危険」が記録されます。

      8. メモ帳を起動します。

         名前を指定して実行で「notepad c:\windows\system32\drivers\etc\hosts」と入力するなどして、host ファイルを表示させます。
         3~5 分程度の後「情報」と記録されることを確認します。

    2. プロセス監視の対象となるコマンドライン引数について

       上記手順で「コマンド」と「引数」を設定しました。
       ところがこの比較元になる SNMP から取得される文字列は想像と違うかもしれません。なぜなら同じ「メモ帳が hosts を開いている」でも起動のされかたによって渡されている文字列が違うからです。
       以下ではコマンドプロンプトで起動した例とタスクスケジューラーで起動した例を比較しています。

      1. コマンドプロンプトでメモ帳を起動します。

         cd C:\windows\system32 後に notepad C:\windows\system32\drivers\etc\hosts を実行します。

      2. タスクスケジューラーでメモ帳を起動します。

         タスクスケジューラーに以下のような登録をし、登録終了後手動で実行します。

      3. タスクマネージャーで状態を確認します。

         コマンドライン列(表示されていないようであればタイトル行を右クリックして列の追加を使って追加してください)を確認すると表示が異なっていることが確認できます。

      4. snmpwalk で確認します。

         Bash on Ubuntu on Windows または Hinemos manager などから snmpwalk を実行します。
         (snmpwalk の設定方法は
        ここを参照してください。)
         参照する OID は .1.3.6.1.2.1.25.4.2.1.2 (プロセス名) です。notepad.exe が 2 つ表示されることを確認します。
         以下の例だと OID 末尾の .8380 や .11172 が PID です。上記タスクマネージャーの表示と合致しているかも確認しておきます。

        [root@localhost ~]# snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.25.4.2.1.2 | grep "notepad"
        HOST-RESOURCES-MIB::hrSWRunName.8380 = STRING: "notepad.exe"
        HOST-RESOURCES-MIB::hrSWRunName.11172 = STRING: "notepad.exe"
        
      5. コマンドプロンプト実行の場合

         コマンドプロンプトで起動したほうの Path 名と引数を確認します。
         Path 名の OID は .1.3.6.1.2.1.25.4.2.1.4.PID, 引数の OID は .1.3.6.1.2.1.25.4.2.1.5.PID です。

         コマンドプロンプトで起動した場合は Path 名が null になっている (カレントディレクトリから起動したから) ことと、引数の先頭にスペースが挿入されていることを確認します。

        [root@localhost ~]# snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.25.4.2.1.4.8380
        HOST-RESOURCES-MIB::hrSWRunPath.8380 = ""
        
        [root@localhost ~]# snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.25.4.2.1.5.8380
        HOST-RESOURCES-MIB::hrSWRunParameters.8380 = STRING: " C:\\windows\\system32\\drivers\\etc\\hosts"
        
      6. タスクスケジューラーでの実行の場合

         タスクスケジューラーでの実行の場合 Path 名が full path になっていることと、引数の先頭にスペースが挿入されて「いない」ことを確認します。
         タスクマネージャーの表示ではスペースがあるように見えますが、snmpwalk で取得した文字列には「スペースはない」点にも注意です。(=タスクマネージャーの表示に全幅の信頼がおけるわけではないということ)

        [root@localhost ~]# snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.25.4.2.1.4.11172
        HOST-RESOURCES-MIB::hrSWRunPath.11172 = STRING: "c:\\windows\\system32\\"
        
        [root@localhost ~]# snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.25.4.2.1.5.11172
        HOST-RESOURCES-MIB::hrSWRunParameters.11172 = STRING: "C:\\windows\\system32\\drivers\\etc\\hosts"
        

      7. コマンドライン文字列には「ゆれ」がある。

         従って、プロセス監視のコマンドラインと引数は上記のような「ゆれ」が発生することを念頭に置いて設定する必要があります。
         また、正規表現と言いながら部分一致の正規表現 (例えば "notepad" と指定すること) ではマッチしません。なのでコマンドおよび引数の文字列全体に対してマッチする正規表現を作る必要があります。
         このようなことから、想定されるコマンドライン文字列に対して設定する正規表現文字列がマッチするかを事前に検証しておいたほうが賢明です。
         たとえば上記の例の場合、Windows 環境であれば Powershell を使用して確認することができます。

        " c:\windows\system32\drivers\etc\hosts" -match "^(|.*\\)*hosts *$"
        

         ただ powershell での確認は OK でも Hinemos java の正規表現を使っているのでうまくマッチしない場合があるかもしれません。その場合は java を使って確認する必要がありますが jre ではなく jdk のインストールが必要など敷居が高いです。
         java で確認する場合は、例えば以下のように行います。

        1. 確認用プログラム (regcheck.java) を作成します。
          public class regcheck {
          
              public static void main(String[] args) {
          
                  System.out.printf("target : %s\n", args[0]);
                  System.out.printf("pattern: %s\n", args[1]);
                  
                  java.util.regex.Pattern p = java.util.regex.Pattern.compile(args[1]);
                  java.util.regex.Matcher m = p.matcher(args[0]);
                  
                  System.out.println(m.matches());
                  
              }
          }
          
        2. コンパイルします。(javac は jdk のインストールフォルダの bin の中にあります)
          C>javac C:\Users\user01\Desktop\regcheck.java
          
        3. 実行します。
          C>java -cp C:\Users\user01\Desktop regcheck " c:\\windows\\system32\\drivers\\etc\\hosts" "^(|.*\\)*hosts *$"
          target :  c:\\windows\\system32\\drivers\\etc\\hosts
          pattern: ^(|.*\\)*hosts *$
          true
          
      8. その他注意点

         どうも日本語 (ASCII 文字以外) のコマンドおよび引数はうまく認識しない感じがします。
         具体的には Windows の SNMP は Shift JIS のバイトストリームを戻しているのに対し Hinemos は UTF-8 でデコードしている感じです。
         設定で変わるのかもしれませんが、その設定どこにあるかは分かっていません。
         SNMP 監視もプロセス監視も文字コードの設定はなく、SNMP TRAP 監視だけが文字コードの設定を持っているので問答無用で UTF-8 デコードなのかもしれませんが。(それだと困りますが、さりとてソースを読む気はないのです・・・)

    [鯖缶] Hinemos 6 で windows service 監視

    2018年1月13日

     この記事 (http://ooltcloud.sakura.ne.jp/blog/201801/article_06231953.html) の続きです。
     以下はこの記事で作成したリポジトリの登録を前提としています。

     Windows サービス監視は監視設定そのものよりも以下手順の項目 1 と項目 2 の作業が重要です。
     この設定に失敗していると監視設定とにらめっこしても一向に監視できるようにならないので要注意です。

    1. サービス監視対象のノードの WinRM を有効にします。

       設定方法の詳細はこちら (http://ooltcloud.sakura.ne.jp/blog/201801/article_06224628.html) を参照してください。
       (wsmancli のインストールは不要です)

    2. サービス監視対象のノードの設定で WinRM のためのユーザーとパスワードを設定します。

       “リポジトリ[ノード]” の監視対象ノードをクリックします。
       開いたノードの変更ダイアログの中ほどに「サービス→ WinRM」という項目があります。
       そこにユーザ名とパスワードを設定する箇所があるので、上記 WinRM をセットアップしたときに用意したユーザー名とパスワードを設定します。

    3. 「監視設定パースペクティブ」を開き “監視設定[一覧]” ウインドウの「+」ボタンを押します。
    4. Windows サービス監視を選択します。
    5. 設定を行います。

       今回はテスト対象のサービスとして Windows Time を使います。なのでサービス名に W32time を設定します。
       (表示名である「Windows Time」ではなく、サービス名である「W32time」を指定します。また「w32tm」でもありません。)

       また、以下の項目も画面の通りに設定します。

      • 監視項目 ID
      • スコープ
      • 間隔
      • 監視
      • 通知ID
      • アプリケーション

    6. 設定が追加出来ていたら OK です。
    7. Windows Time は起動しているので、しばらくすると「情報」が記録されるのを確認します。
    8. Windows Time サービスを停止します。
    9. しばらくすると「危険」が記録されるのを確認します。

    [鯖缶] Hinemos 6 で ping 監視

    2018年1月6日

     実際に Hinemos で ping 監視を行ってみます。
     今回はメール通知などで外部に通知することはせず、監視履歴パースペクティブに表示するところまでを行います。

     監視履歴パースペクティブには、現在の状態を示す “監視履歴[ステータス]” ウインドウと、履歴を示す “監視履歴[イベント]” ウインドウがあります。とりあえず今回はこの画面をみていれば監視ノードの状況が分かることを目指します。

     今回の手順は以下のような感じで行います。

    1. 新規監視ノードの追加
    2. PING 監視の設定
    3. PING 監視の動作確認
    4. 性能表示の確認

    1. 新規監視ノードの追加 
      1. スタートアップパースペクティブで「リポジトリパースペクティブ」を選択します。
      2. “リポジトリ[ノード]” ウインドウ上で右クリック→「作成」を選択します。
      3. ノードの作成ダイアログが出ます。赤色の箇所が必須入力項目です。
      4. 監視対象ノードに SNMP agent がいればデバイスサーチすることである程度自動入力をしてくれます。
        監視対象ノードに SNMP agent がいない場合は手入力します。

         ファシリティ ID は作成の時のみ変更可能で以後変更不可となるので慎重に決定します。
         (自動入力の内容でよいかを再確認します)
         ファシリティ名は Hinemos 内での名称です。ホスト名などとは別の名称に設定できます。

      5. 「登録」を押し “リポジトリ[ノード]” ウインドウに追加されればノードの追加は終了です。
      6. 追加したノードの情報を確認します。

         “リポジトリ[ノード]” ウインドウに表示されているノードを選択(ダブルクリック)すると、先ほど入力したときと同じダイアログが表示されます。
         設定に変更がある場合はここで入力します。(しかし前述のとおりファシリティ ID は変更できません。)

    2. PING 監視の設定
      1. テストのための前準備として、監視対象ノード (Windows) のファイアウォール設定で ping 応答を戻さない設定にします。

         「セキュリティが強化された Windows ファイアウォール」をひらき、「受信の規則」の「ファイルとプリンターの共有 (エコー要求 - ICMPv4 受信)」を無効にします。(しかしなんで「ファイルとプリンターの共有」カテゴリーなんでしょうね・・・?)

      2. スタートアップパースペクティブで「監視設定パースペクティブ」を選択します。

         まず “監視設定[通知]” ウインドウを確認します。(*1)
         今回はテストの都合で STATUS FOR POLLING と EVENT FOR POLLING の設定を変更します。

        (*1)

         しかしこの “監視設定[通知]” ウインドウの役割がいまひとつ分かりにくいのですが、要するに監視で何かを見つけた時に「何をするか」が登録されています。
         具体的には “監視履歴[ステータス]” ウインドウに表示にしたい場合はステータス通知、“監視履歴[イベント]” ウインドウに表示したい場合はイベント通知を使用します。

         デフォルトで登録されている通知 ID で STATUS FOR POLLING と STATUS FOR TRAP がありますが、通知 ID の名称に機能的な意味はありません。なので例えば TRAP でない監視に STATUS FOR TRAP を割り当てることは可能です。なぜなら中身はどらちも「ステータス通知」だからです。通知 ID の名前に惑わされないように注意します。

         またコマンド通知は hinemos manager がホストされているマシンでのコマンド実行です。監視ノード側でコマンド実行をしたい場合は JOB 通知の方を使用します。

      3. “監視設定[通知]” ウインドウの「STATUS FOR POLLING」をダブルクリックします。

         「有効にした直後は通知しない」にチェックを外します。
         「常に通知する」にチェックを入れます。(入っていることを確認します。)
         「情報」にチェックを入れます。(全てのステータスにチェックが入っていることを確認します。)

      4. “監視設定[通知]” ウインドウの「EVENT FOR POLLING」をダブルクリックします。

         「有効にした直後は通知しない」のチェックを外します。

      5. “監視設定[一覧]” ウインドウで右クリック→「作成」を押します。右クリックでなくても「+」ツールボタンを押しても OK です。
      6. 「PING 監視」を選択します。
      7. PING 監視の各パラメーターを設定します。

         「監視項目 ID」 は ID なのでファシリティ ID の時と同様に新規登録時のみ設定可能です。後からの変更できないので慎重に決定します。
         「アプリケーション」というよくわからない入力項目がありますがこれはこの監視の名前です。任意の文字列を入力します。(*2)

        (*2)
         アプリケーションというから何か外部プログラムとか API とかを書かないといけないような印象がありますが、この項目にそのような意味はありません。ただの文字列なので何を設定してもよいです。この文字列は監視履歴のウインドウなどに表示されるので、この監視が何の監視であるかが分かる文字列が設定されていればよいです。
         また、ファシリティ ID に対してファシリティ名が対応しているように、監視項目 ID に対してアプリケーションが対応しています。であれば監視項目 ID の下にアプリケーションの入力欄があればいいのでは?という疑問が湧きますが、通知で使われる名称なので通知のカテゴリーに移動してしまっているのではないかと思います。(多分。根拠はない)
         ・・・しかしこの「アプリケーション」という表記、これってインフラ専業のひとには分かりやすい表記なんですかね? 「監視名」ではだめだったのだろうか。Hinemos は「契機」なる IT っぽくない日本語を使っているので、だったらアプリケーションも日本語で書けばいいのにとか思ってしまう。(「契機」のほうはイベントと称すると監視対象のイベントと被るからそう呼んでいるのだろうか?)

      8. スコープを「登録ノードすべて」にします。

         スコープという言葉がわかりにくいのですが、要するに「PING 監視を行うノードはどこですか?」ということです。特定のノードならそれを、全部とか特定のグループであればそれを選択すれば対象のノードを監視するようになります。

      9. 通知に「STATUS FOR POLLING」と「EVENT FOR POLLING」を設定します。
      10. その他の情報を設定します。

         監視 ID を PING_01 にします。
         条件の間隔を 1 分にします。
         情報 (正常値) の応答時間を 10ms 以下にします。
         警告の応答時間を 100ms 以下にします。
         アプリケーションを PING監視(全ノード) にします。
         収集にチェックを入れます。

      11. OK を押し、"監視設定[一覧]" に登録されれば OK です。
    3. PING 監視の動作確認
      1. スタートアップパースペクティブで「監視履歴パースペクティブ」を選択します。必要に応じて更新ツールボタンを押します。
      2. 監視履歴パースペクティブを確認します。

         現在、監視対象ノードに PING は通らないので、しばらくすると「危険」が“監視履歴[ステータス]” ウインドウと“監視履歴[イベント]” ウインドウの両方に表示されます。

      3. 対象ノードに PING が通るようのファイアウォール設定で ping 応答を戻す設定にします。

         「セキュリティが強化された Windows ファイアウォール」をひらき、「受信の規則」の「ファイルとプリンターの共有 (エコー要求 – ICMPv4 受信)」を有効にします。

      4. しばらくすると(1分後) 表示がかわることを確認します。

         “監視履歴[ステータス]” ウインドウは「危険」が「情報」に変わることを確認します。
         “監視履歴[イベント]” ウインドウは「危険」は消えずに「情報」が追加されることを確認します。

      5. “監視履歴[ステータス]” ウインドウに登録されたエントリーをすべて消去します。

         対象を選択して、右クリック→「削除」、または「×」ツールボタンで押下で消すことができます。

      6. “監視履歴[イベント]” ウインドウに登録されたエントリーをすべて消去します。

         対象を選択して、右クリック→「確認」、または「☑」ツールボタン押下で消すことができます。

      7. しばらくすると(1分後) 表示がかわることを確認します。

         “監視履歴[ステータス]” ウインドウに再び「情報」が表示されることを確認します。
         “監視履歴[イベント]” ウインドウの方は変化しないことを確認します。
         (この動作は上記で STATUS FOR POLLING で「常に通知する」「情報を通知」を設定し、EVENT FOR POLLING では「通知しない」に設定したからです)

    4. 性能表示の確認
      1. パースペクティブ→パースペクティブ表示を選択します。
      2. 性能を選択します。
      3. 性能パースペクティブが表示されます。

         表示されますが、Web ブラウザを小さめに表示していると以下のように表示され「適用」ボタンが見えないことがあります。

      4. 適用ボタンの表示します。

         SXGA (1280*1024) のディスプレイで Web ブラウザを最大化表示すると、やっと「適用」ボタンが現れます(汗

      5. Web ブラウザを最大化しても表示されない場合

         ディスプレイが 1366 * 768 や 1280 * 800 の画面で最大化しても「適用」ボタンが表示できない場合、「適用」ボタンを押したいときだけ、ブラウザの拡大縮小表示を使用して縮小させます。(*3)

        (*3)
         サーバーのコンソールの主流が SXGA かもしれないけど、縦の解像度が足らないとボタンが表示されないってデザインはどうなんですかね?という気がとてもします。

      6. 性能グラフの表示

         性能が参照したいノードを選択して「適用」ボタンを押すとグラフが表示されます。

    [鯖缶] WinRM の有効化と Linux からの WSMan 経由のアクセス

    2018年1月6日

     なにやら WinRM という仕組みがあるようです。

     そもそも WinRM って何かというと、ザクっといえば WMI (Windows Management Instrumentation) を Webサービス (WS-Management プロトコル, HTTP SOAP ベース, WS は WebService の略) でアクセスできる版、みたいなやつらしいです。一方 WMI は DCOM ベースのアクセスらしいです。しかし WMI 自体が WBEM (Web Based Enterprise Management) の Microsoft の実装なので Web なんじゃないの?という話なわけですが、まあそれを言ったら DCOM も ActiveX だったりするわけで Web ですよね、みたいな話なのかな? このあたりの関係性はよくわかりませんが・・・

     なにはともあれ有効にしてみます。

    1. 管理者権限でコマンドプロンプトを起動します。
    2. winrm qc を実行します。

       qc は QuickConfig の略らしいです。

      winrm qc
      
    3. WinRM サービスを開始します。

       WinRM サービスを開始します。変更しますか [y/n]? と聞いてくるので y を押します。
       しかしここで public なネットワークに接続していると失敗します。

      WinRM はこのコンピューター上で要求を受信するように設定されていません。
      次の変更を行う必要があります:
      
      WinRM サービスを開始します。
      
      変更しますか [y/n]? y
      
      WinRM は要求を受信するように更新されました。
      
      WinRM サービスが開始されました。
      WSManFault
          Message
              ProviderFault
                  WSManFault
                      Message = このコンピューターのネットワーク接続の種類の 1 つが Public に設定されているため、WinRM ファイアウォール例外は機能しません。 ネットワーク接続の種類を Domain または Private に変更して、やり直してください。
      
      エラー番号:  -2144108183 0x80338169
      このコンピューターのネットワーク接続の種類の 1 つが Public に設定されているため 、WinRM ファイアウォール例外は機能しません。 ネットワーク接続の種類を Domain ま たは Private に変更して、やり直してください。
      

       なので WinRM を有効にする PC はルーターの内側にあるか、または public なネットワークにつながっていない状況で実行します。

       ところが Hyper-V をインストールしていたりしますと仮想 NIC が作成されていますが、それが「識別できないネットワーク」になっていると public 扱いされてはねられてしまいます。そういう場合はグループポリシーを操作するなどをして、一時的にでも public ネットワークが存在しないようにします。

    4. あらためて WinRM の設定を行います。

       WinRM サービスを開始します。変更しますか [y/n]? で y を
       WinRM ファイアウォールの例外を有効にします。~ 変更しますか [y/n]? で y を選択します。

      C>winrm qc
      WinRM はこのコンピューター上で要求を受信するように設定されていません。
      次の変更を行う必要があります:
      
      WinRM サービスを開始します。
      
      変更しますか [y/n]? y
      
      WinRM は要求を受信するように更新されました。
      
      WinRM サービスが開始されました。
      WinRM は、管理用にこのコンピューターへのリモート アクセスを許可するように設定されていません。
      次の変更を行う必要があります:
      
      WinRM ファイアウォールの例外を有効にします。
      ローカル ユーザーにリモートで管理権限を付与するよう LocalAccountTokenFilterPolicy を構成してください。
      
      変更しますか [y/n]? y
      
      WinRM はリモート管理用に更新されました。
      
      WinRM ファイアウォールの例外を有効にしました。
      ローカル ユーザーにリモートで管理権限を付与するよう LocalAccountTokenFilterPolicy を構成しました。
      

      LocalAccountTokenFilterPolicy ってのは リモート UAC のことらしいです。(=リモート接続で権限が昇格するようにする)

    5. WinRM を使用してデータを取ってみます。

       サービスの一覧を取得します。 e というのは enumerate の略です。wmicimv2/Win32_Service というのはアクセスするリソース名のようです。wmicimv2/~ で wmi のクラス名を指定すれば取得できるようです。

      winrm e wmicimv2/Win32_Service
      

       特定のサービスの情報を取得する場合は get を使用します。取得するサービス名は指定は「?Name=サービス名」といった形式で指定します。

      winrm get wmicimv2/Win32_Service?Name=W32time
      
    6. Linux からのアクセスが可能なように構成します。

       現在の設定を確認します。デフォルトの設定では Basic 認証が無効 (Basic = false), 平文が不可 (AllowUnencrypted=false) になっているはずです。

      winrm get winrm/config/service
      

       Linux からのアクセスを可能にするため Basic 認証を有効、平文を許可にします。
       (もっとセキュアな設定はあると思いますが、ここでは考えないことにします)

      winrm set winrm/config/service/auth @{Basic="true"}
      winrm set winrm/config/service @{AllowUnencrypted="true"}
      
    7. CentOS からアクセスできるかを確認します。

       まず wsmancli のインストールをします。

      yum install wsmancli
      

       CentOS ではなく Ubuntu や WSL (Bash on Ubuntu on Windows) など Debian系Linux の場合は apt-get します。

      apt-get install wsmancli
      

       以下のような感じでアクセスします。
       この際、user はドメインユーザーは指定できません。なので WinRM でアクセスする用のローカルユーザーを作成して置く必要があります。

      wsman -u [localuser] -p [password] -y basic -h 127.0.0.1 -P 5985 enumerate http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Service
      wsman -u [localuser] -p [password] -y basic -h 127.0.0.1 -P 5985 get http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Service?Name=W32time
      

    [鯖缶] Hinemos 6 agent for windows のインストール

    2017年12月26日

     Hinemos 6 agent for windows のインストールの手順のメモ。詳細は https://github.com/hinemos/hinemos/blob/master/README.jp.md にある「インストールマニュアル ( doc_install_linux_6.0_ja.pdf )」を参照してください。ファイル名に linux が入っていますが 7 章に Windows 版 agent のインストールについて書かれています。

     以下は上記文書の手順を元にアレンジしています。なのでうまく行く保証はないです。正確を求める場合は上記文書を参考にしてください。

    1. ファイアウォールの設定

       hinemos Agent は UDP 24005 を待ち受けるようなので穴を開けます。

       加えて、SNMP の穴も開けます。

    2. Windows に SNMP を導入

       以下は Windows 10 の例です。「コントロールパネル」→「プログラムのアンインストール」を選択し、「Windows の機能の有効化または無効化」→「簡易ネットワーク管理プロトコル(SMMP)」を選択します。

       Windows Server の場合はサーバーマネージャーで「管理」→「役割と機能の追加」を選択し、機能の追加で同じもの(=SNMP サービス)があるのでそれを選択します。

    3. Java のインストール

       Hinemos 6 の agent は java 8 で実装されているので jre をダウンロードおよびインストールします。
       インストール後、インストールされたフォルダを確認しておきます。

    4. 環境変数 JAVA_HOME の設定

       システム環境変数に JAVA_HOME の設定を追加します。

    5. Hinemos agent のインストール

       Hinemos のダウンロードサイト (https://github.com/hinemos/hinemos/releases/tag/v6.0.0) から Hinemos agent (今回は hinemos-agent-6.0.0-1.win.zip) をダウンロードし、その中に含まれている msi を実行します。
       ただし UAC が有効の場合、普通に msi を実行すると以下のようなエラーが発生してインストールできません。

       インストール手順 (https://github.com/hinemos/hinemos/releases/download/v6.0.0/doc_install_6.0_ja.pdf) によれぱ、意訳すると、UAC を無効にするか、Administrator ビルトインアカウントでの実行が必要があるとのこと。
       ですが、それはいささか面倒なのでコマンドプロンプトを管理者権限で起動し、そこから msi を実行することでインストールします。

       以下のように「Click the Finsh Button ~」と出れば OK です。しかしこれは正規の手順ではないので気になる人は公式の手順書通りにインストールしましょう。

    6. hinemos agent をサービスとして登録

       環境変数 JAVA_HOME の位置に JAVA がインストールされていることを再度確認します。たとえば JAVA_HOME に設定されているフォルダが存在していることを確認するのは以下のようにします。

      IF EXIST "%JAVA_HOME%" ECHO OK
      

       "OK" が表示されたら、C:\Program Files (x86)\Hinemos\Agent6.0.0\bin にある RegistAgentService.bat を実行します。

       "successfully installed" と表示されれば OK です。

    7. SNMP サービスの再起動とコミュニティの設定

       SNMP サービスを一旦停止しコミュニティ名を確認します。コミュニティ名の変更が必要であれば変更します。コミュニティ名の確認が終了したら SNMP サービスを再起動します。

       可能なら snmpwalk で hinemos agent の拡張 MIB にアクセスできるかどうかを確認します。
       たとえば以下のように、.1.3.6.1.4.1.2021 にアクセスし値がもどってくれば OK です。

      [root@localhost ~]# snmpwalk -v 2c -c Public xxx.xxx.xxx.xxx .1.3.6.1.4.1.2021
      UCD-SNMP-MIB::memTotalSwap.0 = INTEGER: xxxxxxxx kB
      
    8. Himemos Agent の起動

       Hinemos Agent の Service を起動します。起動できれば終了です。
       ここでエラーが発生する場合 JAVA_HOME の内容を確認し、JAVA_HOME が指す位置に java がインストールされていることを再確認します。

    p.s.

     しかし UAC の無効が推奨というのはどうなのだろう?今回のテストのような Client OS への agent のインストールは本来想定していないという話であれば、Server OS は Administrator アカウントが無効になっていないのでそれを使えばいいと言われればそうなのですが。
     また hinemos manager のインストールでも SELinux は無効というのがありました。アプリケーションの都合でセキュリティ要件を下げるというのはよくある話ではありますが、監視システムがセキュリティ要件を下げるってのはどうなんだろう?
     ・・・などと言う点が気になりました。このあたりは世のシステム管理者さんはあまり気にはしないのだろうか?

    [与太] そんな中山踏切

    2017年12月24日

     この記事はそんな広島の 12/7 の記事(ということで 12/24 に書いたもの)です。

     広島にはその筋(?)には有名な中山踏切という、温品方面からの車と戸坂方面からの車が合流する交通の要衝(?)であり渋滞ポイントでありながら、信号機が設置されておらず、踏切があるがゆえに右折はもちろんのこと左折直進もままならぬ(踏切手前の一時停止 / 線路内一時停止不可の縛りがある)という結構嫌われている交差点があります。

     場所はここ。(上の写真とは南北逆です)

     信号機がないのにどうやって通過するかというと阿吽の呼吸を使います。つまり互いに牽制しつつ割り込んでいく形ですね(汗
     遮断機が下りている間は戸坂方面からの車は右折のチャンスなので対向車線を逆走をするというカオスな状況も時折起こります(汗
     なのでタイミングが分からない経験値が少ない人や、そもそもそういう牽制とかが苦手な人はこの交差点は通りたくないと口を揃えます(汗

     で、この交差点。現在広島高速 5 号線建設に伴って工事中のようでして、信号がつくかどうかはしりませんが交差点の領域は広くなるようです。多分右折レーンができる。(根拠のない予想)

     かつて中山踏切 - 第一病院間は、現在の中山踏切 - 矢賀駅間と同じ程度に隘路でした。時代を経て道幅が広くなり通行しやすくなるのはよいことなのですが、往時の姿を無くすという側面もあり、諸行無常、一抹の寂しさをおぼえるのも事実でありまして、なのでここに記録として現在の姿をちょっと残しておくことに。

     以下は中山踏切バス停方向から温品方向を望んだところ。

     温品方向から中山踏切バス停方向を望んだところ。左手前方に広島高速 5 号線の工事中の高架の橋桁が見える。(わかりにくいけど)

     以下は Theta で撮影した 360° 画像。掴んで回せます。ここのライブラリを使わせてもらいました。(https://github.com/aike/thview.js)
     踏切の手前から。

     踏切と横断歩道を渡り、交差点の中央あたりから。

     バス停側横断歩道の前から。

     Theta で撮影した写真を blog に上げたかっただけなのでは?という疑惑がありますが、それは誤解ということにしておいてください(ぇ



    [Excel] Excel 2010 で動作するマクロが 2013/2016 で動作しない場合の対処

    2017年12月22日

     Excel 2010 で動いていたマクロが Excel 2013 や 2016 で動かすと挙動が異なって困った経験はありますか?私はあります(汗

     結論からいうと、私が経験した事例では以下のようなことに気を付ける必要があります。

    • 使用しないセルに条件付き書式を残さないようにする。
    • 条件付き書式でユーザー関数を呼ぶ場合、そのユーザー関数内でエラーが生じないようにする。

     よくわからない。「ともあれこれをしておけばいい」的な対処方法はないの?という場合は、マクロの開始直後と終了直前に EnableFormatConditionsCalculation を操作を追加します。以下は ActiveSheet に対して操作を追加した例です。

    Sub Macro1()
        ' 条件付き書式の適用を停止
        ActiveSheet.EnableFormatConditionsCalculation = False
    
        ' Excel 2010 まで動いていたマクロの処理
    
        ' 条件付き書式の適用を再開
        ActiveSheet.EnableFormatConditionsCalculation = True
    End Sub
    

     再現手順を以下に示します。(以下の手順は Excel 2013 64bit 版 を Windows 10 (Ver1703) 64bit 版で確認しています)

     何が起こるかを一言でいうと、マクロのプログラムカウンターが途中で消失します。つまりマクロが最後まで実行されません。(= End Sub まで行かない)

    1. Module1 シートに FuncA ユーザー関数を作成します。また作成するユーザー関数は a = 0 のときにエラーが発生するようにします。(配列でない変数を配列としてアクセスしようとしています)
      Function FuncA(a As Integer) As String
      
          Dim b As Variant
      
          If a = 0 Then
              c = b(1)
          End If
      
      End Function
      

    2. A1 セルに FuncA() を使用した条件付き書式を設定します。今回は FuncA(自身のセル) が "" のときセルの色を替えるようにします。A1 セルの値を 1 とします。
    3. A1 セルを A2 にコピーします。A2 セルの値(数式)は =A1+1 に変更します。
    4. B1 セルに A1 セルをコピーします。B1 セルの値はクリアします。従ってセルの色が変更されないことを確認します。また合計 3 つのセルに同じ条件付き書式が設定されていることを確認します。
    5. Module2 シートに Macro1() を作成します。内容はなにも設定していないセルをクリアするだけのものです。今回は C1 セルと C2 セルをクリアするマクロを作成します。
      Sub Macro1()
      
          Range("C1").ClearContents
          Range("C2").ClearContents
      
      End Sub
      
    6. 動作の確認のために作成したマクロの全ての行にブレイクポイントを設定します。また End Sub の部分にもブレイクポイントを設定します。
    7. Sub Macro1() から End Sub までの間にカーソルを置いた後に F5 キーを押しマクロを実行します。ブレイクポイントで停止しされるのを確認しながら、繰り返し F5 キーを押し End Sub までプログラムカウンタ (黄色の表示) が移動していくのを確認します。 (=期待通りに動作する)
    8. End Sub の実行も終了したら C1 セルのクリアを D1 セルのクリアに変更します。またこの操作はマクロのプログラムコードを変更することのみが目的です。D1 セルに変更すること自体に意味はありません、C1 以外ならどこのセルでも OK です。
    9. F5 を押しマクロを実行します。D1 セルのクリアのところでブレイクする(黄色表示になる)ことを確認します。
    10. ふたたび F5 を押しマクロを実行します。D1 セルのクリアの行を実行の後 C2 セルをクリアする行にプログラムカウンタが移動してブレイクされる(C2 セルのクリアのところが黄色表示になる)のを期待しますが、プログラムカウンタ (黄色の表示) は消失したまま戻ってきません。また、A2 のセル色が白になってしまいます。
    11. また B1 セルに空白や 0 以外の値を入れ上記手順を行った場合は本現象は再現しません。以下は B1 セルに値を入れてから、マクロの変更した D1 セルのクリアを C1 セルのクリアに戻して F5 を押していった場合です。プログラムカウンタ (黄色の表示) は End Sub まで移動します。(=期待通りに動作する)

     この現象は条件付き書式で呼んだユーザー関数がエラーを起こした場合に発生します。エラーがない場合は発生しません。
     しかも意図しない動作となるのは問題のある条件付き書式側の処理ではなくて、エラーが発生する関数とはまったく関係のないマクロ側だということです。マクロが途中で止まるからと言ってマクロ側のコードを一生懸命眺めても、マクロ側にはエラーはないので解決の糸口がつかめないのが特徴です(汗

     そしてこの現象は Excel のバグか?(ディグレードか?)というと、そうであろうと思います。(厳密には「バグ」ではなく「未定義の動作」なのだろうけども。)
     しかしこの現象を発生させるためにはユーザー関数のコードがバグっている必要があるのです。なので、自分の作りこんだバグを棚に上げて Microsoft を非難するわけにも・・・とモニョってしまうわけですね(汗

     ともかくも大切なことは、この現象は不要となった条件付き書式を設定したセルが放置されることによって起きる可能性が高いことです。少なくとも私が遭遇した事例はそうでした。今回の B1 セルのように見た目は異常がない場合はそれが原因になっているとは想像することすら難しいです。

     従って不要な条件付き書式付きセルが残らないようにする必要があります。残っているとパフォーマンスも無駄にしますのできれいさっぱり削除しましょう。

     いやそうはいってもチェックするのは大変すぎて・・・という場合は冒頭の通り EnableFormatConditionsCalculation で対応してみてください。おそらく期待通りに動作するはずです。しかし EnableFormatConditionsCalculation = True より後のコードを実行しない場合があるので EnableFormatConditionsCalculation = True より後には処理は入れないようにします。

    Sub Macro1()
    
        ' 条件付き書式の適用を停止
        ActiveSheet.EnableFormatConditionsCalculation = False
    
        ' Excel 2010 まで動いていたマクロの処理
        Range("D1").ClearContents
        Range("C2").ClearContents
        Range("D3").ClearContents
    
        ' 条件付き書式の適用を再開
        ActiveSheet.EnableFormatConditionsCalculation = True
        ' ここより下は実行されない場合がある
    End Sub
    

     本質的な解決は条件付き書式の条件でエラーを発生されないこと、ユーザー関数でエラーを出さないことなので、その点は踏まえたうえで EnableFormatConditionsCalculation は次善の対策とするようにしてください。

    [鯖缶] SNMP(9) Windows における Trap の送信の確認方法

    2017年12月21日

     Windows で SNMP Trap を送信されるかを確認する際のメモ。
     以下では複数の宛先、複数のコミュニティで送信できることを確認しています。

     テストツールとしてイベントログから Trap メッセージを作ることができる evntwin.exe (eventwin ではないので注意) を使用しています。(SNMP Service に付属している Windows のコンポーネントです。)
     またTrap メッセージの送信を確認するために SNMP Service が動作している PC で Wireshark を起動して確認しています。(Wireshark は https://www.wireshark.org/download.html などからダウンロードしセットアップする必要があります。また Wireshark の使用方法などは以下の手順から割愛しています)

    1. SNMP Service のプロパティのセキュリティタブをひらき Public と Private のコミュニティを 2 つ設定します。
    2. トラップタブをひらき private コミュニティの送信先として 192.168.0.1 を設定します。
    3. 次に public コミュニティの送信先として 192.168.0.2 と 192.168.0.3 の 2 箇所を設定します。設定が終了したらプロパティを閉じ SNMP Service を再起動します。
    4. Powershell を管理者として実行し Trap 送信のためのテスト用のイベントソース (SnmpTest) を作成します。
      New-EventLog -LogName Application -Source SnmpTest
      
    5. イベントトラップトランスレーター (evntwin.exe) を起動します。
      evntwin
      
    6. evntwin.exe が起動したら編集ボタンを押します。
    7. 構成の種類をカスタムにします。次にイベントソースのツリービューから先ほど Powershell で作成したイベントソース (SnmpTest) を探して選択します。最後にイベントのリストビューからイベント ID 1 を選択し、右クリックでイベントの追加を選択します。
    8. OK を押します。
    9. OK を押します。
    10. Wireshark を起動しパケットの記録を開始した後、管理者で実行中の Powershell からイベントを書き込みます。
      Write-EventLog -LogName Application -Source SnmpTest -EventId 1 -EntryType Information -Message "SNMP Trap Test"
      
    11. Trap が設定どおり 192.168.0.1 ~ 3 の 3 つが送信されていることを確認します。また 192.168.0.1 は private コミュニティ宛てであることを確認します。(同一セグメントへの送信かつ送信先が存在しない場合、Trap は送信されないのでそのような状況ではないことを事前に確認しておきます。)
    12. 192.168.0.2 ~ 3 は public コミュニティ宛てであることを確認します。
    13. 最後にテストで使用したイベントソースの削除や evntwin.exe で登録した情報を削除します。
      Remove-EventLog -Source SnmpTest
      

    [鯖缶] SNMP(8) Windows における 拡張 MIB

    2017年12月19日

     ミドルウェアソフトウェアが Windows の SNMP サービスを使用して拡張 MIB を用意するケースがありますが、どのミドルウェアが用意しているのかを調査する方法のメモ。

     HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ExtensionAgents に拡張 MIB が登録されています。拡張 MIB が一切ない場合は ExtensionAgents のキー自体がありません。ExtensionAgents に登録されている値の名前や値自体をみれば、どのミドルウェアが用意したのか大体わかります。

     以下は hinemos が拡張している例です。

    PS>gi HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ExtensionAgents
    
    
        Hive: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SNMP\Parameters
    
    
    Name                Property
    ----                --------
    ExtensionAgents     hinemos_agent : SOFTWARE\HinemosAgent\SNMPExtAgent
    

     ExtensionAgents の値は HKLM (\HKEY_LOCAL_MACHINE) なので、それを繋いでやればその先に何が登録されているのかが分かります。ただ、SOFTWARE の次に WOW6432Node を挟まないと参照出来ない場合もあるので注意です。

    PS>gi HKLM:\SOFTWARE\WOW6432Node\HinemosAgent\SNMPExtAgent
    
    
        Hive: HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\HinemosAgent
    
    
    Name                Property
    ----                --------
    SNMPExtAgent        Pathname : C:\Program Files (x86)\Hinemos\Agent6.0.0\lib\HinemosSNMPExtAgent.dll
    

    [鯖缶] SNMP(7) Windows 標準状態で SNMP

    2017年12月17日

     NET-SNMP をインストールすれば Windows でも MIB を参照できるのですが、しかしそういうツールを持ち込めない環境というのもあってそういう場合に困ります。そういう状況で MIB を参照したい場合は Oleprn というライブラリを使うことで SNMP が喋れるようです。(名前からするとプリンターのステータスを拾うためのライブラリなんですかね?)

     名前からして OLE なので Excel がインストールされているなら Excel VBA で扱うのがインテリセンスも効くので楽です。

     VBE (ALT+F11) をひらき、標準モジュールを作成して参照設定から oleprn 1.0 Type Libraty にチェックを入れます。

     コードを入力して実行します。

     上記で入力したコードは以下

    Sub test()
    
        Dim snmp As New OLEPRNLib.snmp
        ' VBScript   : snmp = CreateObject("OlePrn.OleSNMP")
        ' Powershell : $snmp = New-object -ComObject olePrn.OleSNMP
       
        snmp.Open "192.168.0.1", "public"
        getValue = snmp.Get(".1.3.6.1.2.1.1.5.0")
        Debug.Print getValue
        snmp.Close
        
    End Sub
    

     いや「Excel もないんです」という場合は vbscript を使うとかします。たぶん上記のコードの Dim snmp As New OLEPRNLib.snmp の部分を snmp = CreateObject("OlePrn.OleSNMP") にすれば動くのではないかと思います。

    set snmp = CreateObject("OlePrn.OleSNMP")
    
    snmp.Open "192.168.0.1", "public"
    getValue = snmp.Get(".1.3.6.1.2.1.1.5.0")
    WScript.Echo getValue
        
    snmp.Close
    

     Powershell でやりたい場合も同様です。変数名とか関数の括弧とかに注意する程度です。

    $snmp = New-object -ComObject olePrn.OleSNMP
    
    $snmp.Open("192.168.0.1", "public")
    $snmp.Get(".1.3.6.1.2.1.1.5.0")
    
    $snmp.Close()
    

     ただ、OID 番号で指定しないとならないのと snmpwalk のような列挙ができないので使えるシーンは限られるとは思います(汗

    [鯖缶] SNMP(6) Windows の NET-SNMP

    2017年12月17日

     個人的には使うことはないと思うのですが、ちょっと試してみたのでメモ。

    1. インストール

       インストールオプションは一切なしでも snmpwalk と snmptranslate は動作します。

       しかしデフォルトのインストールディレクトリは C:\usr ですが、それでも Path が長すぎるというエラーを吐きます。

    2. snmp.conf の位置と MIB ファイルの追加

       C:\usr\etc\snmp に snmp.conf があるのでそこに追記します。
       mibdirs のディレクトリの区切りは ; (セミコロン) を使用するようです。Linux 系の : (コロン) とは異なることに注意します。

      mibdirs C:/usr/share/snmp/mibs;C:/usr/share/snmp/mibs/screenos/6.2.0.mibs/snmpv2
      mibs all
      persistentDir C:/usr/snmp/persist
      tempFilePattern C:/usr/temp/snmpdXXXXXX
      
    3. コマンド類の Path は C:\usr\bin です。

       インストール時に PATH は追加してくれないので、snmpwalk や snmptranslate を使用する場合は c:\usr\bin に移動してから使うか、自分で環境変数 PATH に c:\usr\bin を追加します。

    4. 使用上の注意

       仕様なのかバグなのか分かりませんが、OID に . (ドット) のみを指定するとエラーになります。.1 と指定します。
       また -Of などのオプションを OID の後に入れてもエラーになります。オプション類は先に記述するようにしましょう。

      snmpwalk -c public -v 2c 192.168.0.1 .1
      
    5. おまけ: SNMPd の起動

       インストールのとき Net-SNMP Agent Service の Standard Agent か With Windows Extension DLL Support のいずれかを選ぶと(両方は選べない) C:\usr\bin\ の snmpd.exe が使えるようになります。

       Standard Agent か With Windows Extension DLL Support の違いは、Windows が標準で持つ C:\Windows\System32\inetmib1.dll や C:\Windows\System32\snmpmib.dll を使用するかどうかの差であるようです。後者は使用し前者は使用しないようです。
       後者の場合に Windows の機能の SNMP サービスがインストールされていると snmpd.exe の起動時にエラーが発生するのでインストール自体をしないようにします。前者の場合でも SNMP サービスが動作していると Listen ポートが競合し snmpd.exe が abort するので SNMP サービスは停止しておく必要があります。

       また、snmpd.exe の実行の前に C:\usr\etc\ に snmpd.conf を設定しておく必要があります。例えば以下のような定義をします。(設定の詳細は他のサイトを探してください)

      com2sec public default    public
      group   public v2c        public
      view    all    included   .1     ff
      access  public ""         any    noauth  exact  all  none  none
      
      

    [鯖缶] SNMP(5) NET-SNMP の基本的な使い方

    2017年12月17日

     NET-SNMP の使い方のメモ。
     (といっても、MIB の参照するために必要な 2 つのコマンドの主要なオプションのみメモ)

    1. snmpwalk

       実際にエージェントの MIB を参照しその値を表示します。
       以下は .1.3.6.1.2.1.1.5 を参照した例

      # オブジェクトを参照する
      # snmpwalk -c コミュニティ名 -v SNMPバージョン 機器のアドレス OID
      [root@localhost ~]# snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.1.5
      RFC1213-MIB::sysName.0 = STRING: "Router1"
      
      # -Of をつけて OID シンボルで表示 
      [root@localhost ~]# snmpwalk -Of -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.1.5
      .iso.org.dod.internet.mgmt.mib-2.system.sysName.0 = STRING: "Router1"
      
      # -On をつけて OID 番号で表示 
      [root@localhost ~]# snmpwalk -On -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.1.5
      .1.3.6.1.2.1.1.5.0 = STRING: "Router1"
      
      # OIDシンボルで指定
      [root@localhost ~]# snmpwalk -c public -v 2c .iso.org.dod.internet.mgmt.mib-2.system.sysName.0
      RFC1213-MIB::sysName.0 = STRING: "Router1"
      
      # MIB ファイル名+シンボルで指定 (正確にはファイル名ではなく、MIB ファイルの先頭行で宣言された名前)
      [root@localhost ~]# snmpwalk -c public -v 2c RFC1213-IB::sysName.0
      RFC1213-MIB::sysName.0 = STRING: "Router1"
      

       列挙する場合は以下のように。

      # mib-2.system 以下を列挙
      snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.2.1.1
      
      # mib-2 (.1.3.6.1.2.1) 以下を列挙する場合はアドレス指定はなくて良い。
      snmpwalk -c public -v 2c 192.168.0.1
      
      # 全部を列挙する場合は . のみを指定
      snmpwalk -c public -v 2c 192.168.0.1 .
      
      
    2. snmptranslate

       MIB ファイルを参照し名前解決を確認します。
       snmptranslate は MIB ファイルを参照した結果を表示しているだけなので、snmptranslate で表示で来たからと言って実際の機器にそのオブジェクトがあるとは限らない点、またはアクセス権があるとは限らない点に注意です。(= snmptranslate では成功するが snmpwalk では値が取得できないことがあります)

      # MIB ファイル名+シンボルを得る
      [root@localhost ~]# snmptranslate .1.3.6.1.2.1.1.5
      RFC1213-MIB::sysName
      
      # -Of オプションで OID シンボルを得る
      [root@localhost ~]# snmptranslate -Of .1.3.6.1.2.1.1.5
      .iso.org.dod.internet.mgmt.mib-2.system.sysName
      
      # -On オプションで OID 番号を得る
      [root@localhost ~]# snmptranslate -On .iso.org.dod.internet.mgmt.mib-2.system.sysName
      .1.3.6.1.2.1.1.5
      
      # -Tp オプションで MIB ツリーを表示する
      [root@localhost ~]# snmptranslate -Tp .1.3.6.1.2.1.1
      +--system(1)
         |
         +-- -R-- String    sysDescr(1)
         |        Textual Convention: DisplayString
         |        Size: 0..255
         +-- -R-- ObjID     sysObjectID(2)
         +-- -R-- TimeTicks sysUpTime(3)
         |  |
         |  +--sysUpTimeInstance(0)
         |
         +-- -RW- String    sysContact(4)
         |        Textual Convention: DisplayString
         |        Size: 0..255
         +-- -RW- String    sysName(5)
         |        Textual Convention: DisplayString
         |        Size: 0..255
         +-- -RW- String    sysLocation(6)
         |        Textual Convention: DisplayString
         |        Size: 0..255
         +-- -R-- INTEGER   sysServices(7)
         |        Range: 0..127
         +-- -R-- TimeTicks sysORLastChange(8)
         |        Textual Convention: TimeStamp
         |
         +--sysORTable(9)
            |
            +--sysOREntry(1)
               |  Index: sysORIndex
               |
               +-- ---- INTEGER   sysORIndex(1)
               |        Range: 1..2147483647
               +-- -R-- ObjID     sysORID(2)
               +-- -R-- String    sysORDescr(3)
               |        Textual Convention: DisplayString
               |        Size: 0..255
               +-- -R-- TimeTicks sysORUpTime(4)
                        Textual Convention: TimeStamp
      

    [鯖缶] SNMP(4) MIB ファイルの追加

    2017年12月17日

     標準でインストールされる MIB ファイルだけでは足らない場合 MIB ファイルを追加する必要があります。
     以下は ScreenOS 6.2 の MIB ファイルを追加する場合の例です。(CentOS 7, snmpwalk ver5.7.2 の例です)

    1. default の MIB の参照ディレクトリを確認する

       snmpwalk を引数なしで実行すると usage が表示されますが、その -M オプションの説明の下に default の MIB ファイルの参照ディレクトリが表示されます。

      USAGE: snmpwalk [OPTIONS] AGENT [OID]
      (中略)
      
      General options
        -m MIB[:...]          load given list of MIBs (ALL loads everything)
        -M DIR[:...]          look in given list of directories for MIBs
          (default: /usr/share/snmp/mibs/)
      (以下略)
      
    2. snmp.conf の場所を確認する

       snmpconf を実行すると、現在存在している conf が表示されます。

      [root@localhost ~]# snmpconf
      
      The following installed configuration files were found:
      
         1:  /etc/snmp/snmpd.conf
         2:  /etc/snmp/snmptrapd.conf
         3:  /root/.snmp/snmp.conf
      
      Would you like me to read them in?  Their content will be merged with the
      output files created by this session.
      
      Valid answer examples: "all", "none","3","1,2,5"
      
      Read in which (default = all): 
      

       既に作成されていればそのファイルを編集するか、削除して新しく作るのかを検討します。

       また今回は snmpconf で snmp.conf は作りませんが、snmpconf で作成すると最後に以下のようなメッセージがでます。

      The following files were created:
      
        snmp.conf
      
      These files should be moved to /usr/share/snmp if you
      want them used by everyone on the system.  In the future, if you add
      the -i option to the command line I'll copy them there automatically for you.
      
      Or, if you want them for your personal use only, copy them to
      /root/.snmp .  In the future, if you add the -p option to the
      command line I'll copy them there automatically for you.
      

      snmp.conf はみんなで使うなら /usr/share/snmp、ひとりで使うなら ~/.snmp に置きましょう、ということですね。

    3. MIB ファイルの配置

       ScreenOS 6.2 の MIB を配置します。
       今回は /tmp/6.2.0.mibs.zip の内容を /usr/share/snmp/mibs/screenos/ に展開します。

      wget https://www.juniper.net/techpubs/software/screenos/screenos6.2.0/6.2.0.mibs.zip -P /tmp
      unzip /tmp/6.2.0.mibs.zip -d /usr/share/snmp/mibs/screenos/
      
    4. snmp.conf の作成

       今回は空のファイルから直接テキストエディタを使って snmp.conf を作成します。
       mibdirs として上記の default の MIB の参照ディレクトリ(以下の緑マーキング部分)と今回新たに参照するディレクトリ(以下の赤のマーキング部分)を記述します。ディレクトリの区切り文字は : (コロン) です。
       次の行に mibs all を追記します。これは mibdirs で見つかったファイル全てを MIB ファイルとして評価するという意味 (らしい) です。

      mibdirs /usr/share/snmp/mibs:/usr/share/snmp/mibs/screenos/6.2.0.mibs/snmpv2
      mibs all
      
    5. MIB ファイルが参照されることを確認

       snmptranslate で配置した MIB ファイルが参照されることを確認します。
       追加配置した MIB ファイルの OID 番号を指定し -Of オプションを指定することで OID シンボルが表示されることを確認します。

      [root@localhost ~]# snmptranslate .1.3.6.1.4.1.3224.7.11.1 -Of
      (略)
      Unlinked OID in NETSCREEN-SET-ADMIN-USR-MIB: nsSetAdminUser ::= { netscreenSetting 11 }
      Undefined identifier: netscreenSetting near line 67 of /usr/share/snmp/mibs/NS/NS-SET-ADMIN-USR.mib
      .iso.org.dod.internet.private.enterprises.netscreen.netscreenSetting.nsSetAdminUser.nsSetAdminUserLocalTable
      

       上記ではなにかしらのエラーが出ていますが、変換自体はできているので OK です。(多分他の機器の MIB であればエラーは出ないんじゃないかな・・・?)

    6. MIB ファイルの修正

       不幸にして ScreenOS 6.2 の MIB ファイルはエラーが発生してしまったので、これを修正してみることにします。

       エラーメッセージ(前述の Unlinked OID ~)を読むと「9,49 行目は netscreenSettingMibModule だが、 67 行目は netscreenSetting であり MibModule がない」ということのようです。
       なので、9,49 行目の末尾の MibModule を削除し netscreenSetting に統一してみることにします。
       ・・・というのをエラーがでるすべての mib ファイルで実施します(汗

       9: netscreenSettingMibModule
      49: ::= { netscreenSettingMibModule 11 }
      67: nsSetAdminUser OBJECT IDENTIFIER ::= { netscreenSetting 11 }
      

       そんなのめんどくさい・・・と思う人はこちらで。

      cd /usr/share/snmp/mibs/screenos/6.2.0.mibs/snmpv2
      sed -e "s/MibModule//" NS-SET-*.mib -i
      sed -e "s/MibModule//" NS-VPN-*.mib -i
      

       MIB ファイルを修正後、再度 snmptranslate や snmpwalk を実行し、エラーが出なくなっていれば成功です。

      [root@localhost ~]# snmpwalk -c public -v 2c 192.168.0.1 .1.3.6.1.4.1.3224.7.11.1 -Of
      .iso.org.dod.internet.private.enterprises.netscreen.netscreenSetting.nsSetAdminUser.nsSetAdminUserLocalTable.nsSetAdminUserLocalEntry.nsAdminUserLocalIndex.0 = INTEGER: 0
      .iso.org.dod.internet.private.enterprises.netscreen.netscreenSetting.nsSetAdminUser.nsSetAdminUserLocalTable.nsSetAdminUserLocalEntry.nsAdminUserLocalName.0 = STRING: ns
      .iso.org.dod.internet.private.enterprises.netscreen.netscreenSetting.nsSetAdminUser.nsSetAdminUserLocalTable.nsSetAdminUserLocalEntry.nsAdminUserLocalPriv.0 = INTEGER: 250
      .iso.org.dod.internet.private.enterprises.netscreen.netscreenSetting.nsSetAdminUser.nsSetAdminUserLocalTable.nsSetAdminUserLocalEntry.nsAdminUserLocalRole.0 = INTEGER: not-assigned(0)
      

    [鯖缶] SNMP(3) NET-SNMP のインストール

    2017年12月17日

     ともかく SNMP を理解するにはやはり触ってみたい・・・のですが特に Windows の場合、標準状態で SNMP に触る方法がほぼありません(汗

     そこで Windows 10 には WSL (Bash on Ubuntu on Windows) があるので、そこに NET-SNMP をインストールして周辺機器 (ルーターとか) の MIB を参照するための環境を構築してみます。また目的は MIB を参照するためだけなので自分自身を SNMP agent にはしないことにします。

     ということで、NET-SNMP (Linux版) のインストール手順を以下にメモ。また以下のインストール作業はすべて root で行っています。

    1. Windows 10 (WSL; Bash on Ubuntu on Windows) / Debian 系 Linux の場合。

       Windows 10 の場合 x64 であれば WSL (Bash on Ubuntu on Windows) が使えるのでこれを有効にし、NET-SNMP をインストールします。実体は Ubuntu なので、インストールは Debian 系 Linux の作法で行います。

      apt-get install snmp
      apt-get install snmp-mibs-downloader
      download-mibs
      

       apt-get install snmp だけでは mib ファイルが全くインストールされないので apt-get install snmp-mibs-downloader を行いその後 download-mibs を実行します。(参考: https://l3net.wordpress.com/2013/05/12/installing-net-snmp-mibs-on-ubuntu-and-debian/)

       download-mibs が終了したら、snmptranslate を実行して期待する結果が得られるかを確認します。

      [root@localhost ~]# snmptranslate .1.3.6.1.2.1.4.20.1.1 -Of
      .iso.org.dod.internet.mgmt.mib-2.ip.ipAddrTable.ipAddrEntry.ipAdEntAddr
      

       snmptranslate を実行時に、以下のようなエラーがでる場合があります。

      Unlinked OID in IPATM-IPMC-MIB: marsMIB ::= { mib-2 57 }
      Undefined identifier: mib-2 near line 18 of /usr/share/mibs/ietf/IPATM-IPMC-MIB
      Expected "::=" (RFC5644): At line 493 in /usr/share/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB
      Expected "{" (EOF): At line 651 in /usr/share/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB
      Bad object identifier: At line 651 in /usr/share/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB
      Bad parse of OBJECT-IDENTITY: At line 651 in /usr/share/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB
      Bad operator (INTEGER): At line 73 in /usr/share/mibs/ietf/SNMPv2-PDU
      

       この場合、以下を追加で実行します。(参考: https://mistymagich.wordpress.com/2016/07/19/Ubuntu 14.04, Ubuntu 16.04上のsnmpwalk,snmptranslateでエラーが出力される)

      wget http://www.iana.org/assignments/ianaippmmetricsregistry-mib/ianaippmmetricsregistry-mib -O /usr/share/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB
      wget http://pastebin.com/raw.php?i=p3QyuXzZ -O /usr/share/mibs/ietf/SNMPv2-PDU
      wget http://pastebin.com/raw.php?i=gG7j8nyk -O /usr/share/mibs/ietf/IPATM-IPMC-MIB
      
    2. CentOS / Redhut 系 Linux の場合

       CentOS の場合は以下のようにインストールします。

      yum install net-snmp
      yum install net-snmp-utils
      

       インストール後、snmptranslate を実行して期待する結果が得られるかを確認します。

      [root@localhost ~]# snmptranslate .1.3.6.1.2.1.4.20.1.1 -Of
      .iso.org.dod.internet.mgmt.mib-2.ip.ipAddrTable.ipAddrEntry.ipAdEntAddr
      
    3. Windows 8.1 以前、および Windows 10 が x86 である場合。

       一応 Windows 版の NET-SNMP があるようです。こちら
       インストールとか使い方の参考はこちら

    [鯖缶] SNMP(2) 基本的な用語

    2017年12月17日

     SNMP に関する基本的な用語のメモ。

    1. 標準 MIB と 拡張 MIB

       MIB は大きく 2 つの領域に分かれています。

      • 標準 MIB (.1.3.6.1.2.1 .iso.org.dod.internet.mgmt.mib-2)

         標準 MIB (MIB-2) は主に機器共通で持っているであろう情報が登録されています。
         例えば .iso.org.dod.internet.mgmt.mib-2.system.sysName (.1.3.6.1.2.1.1.5) でその機器の名前を、 .iso.org.dod.internet.mgmt.mib-2.ip.ipAddrTable.ipAddrEntry.ipAdEntAddr (.1.3.6.1.2.1.4.20.1.1) はその機器が持つ ip アドレスを取得することができます。(多分)
         ただ、MIB-2 にあるデータが必ず各機器に存在するか?というとそうではなく例えば .iso.org.dod.internet.mgmt.mib-2.host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunName (.1.3.6.1.2.1.25.4.2.1.2) は Windows は実行プロセス名を列挙しますが、ScreenOS は「オブジェクトがない (=No Such Object available on this agent at this OID)」と戻します。(多分)

      • 拡張 MIB (.1.3.6.1.4.1 .iso.org.dod.internet.private.enterprises)

         拡張 MIB (enterprises) は主に機器固有の情報が集められています。
         機器固有と言っても同じアドレス (OID 番号) で機器が違うと意味が違うのは困るので enterprises の直下は各ベンダーごとに番号が割り当てられていています。Windows のレジストリに例えると HKLM:\Software の直下はベンダー名になっているようなものです。なので異なるベンダーによるアドレス重複 (OID 番号の重複) はありません。逆に機器特有の情報を取得したい場合は、当該機器の MIB ファイルを取得しないと snmpwalk 等で取得したオブジェクトが何を示すのかわからない、ということでもあります。

         例えば、77 と 311 は Microsoft が、2021 は NET-SNMP (ucdavis) が割り当てられています。

        .1.3.6.1.4.1.77   iso.org.dod.internet.private.enterprises.lanmanager
        .1.3.6.1.4.1.311  iso.org.dod.internet.private.enterprises.microsoft
        .1.3.6.1.4.1.2021 iso.org.dod.internet.private.enterprises.ucdavis
        

         OID 番号が手元にあるけど、MIB ファイルは手元にないのでそれが何かがわからない、という場合は http://oidref.com/ が便利です。oidref.com/ の次に OID 番号を入力すると何かを教えてくれます。以下は net-snmp の OID 番号を入れてみた例です。

        http://oidref.com/1.3.6.1.4.1.2021
        

    2. コミュニティ

       名前の通りコミュニティでアクセス制御に使用します。例えば public だと特定の公開範囲で読み込みのみ、private だと全ての範囲で読み書き可能、みたいな設定をします。
       しかしよく使用されている SNMP v2 はコミュニティ名指定以外の認証(パスワード等)がないので、コミュニティ名が知られるとアクセスし放題です。なのでコミュニティ名は類推しにくいユニークな名称にしたほうが良いと思われますが、大体のケースで public をそのまま使っているようです(汗

    3. エージェントとマネージャー

       エージェントが MIB を持っている側でありサーバー側です。監視対象(=機器)がエージェントになります。そして PC 等に設置されるマネージャーが MIB をアクセスするクライアント側です。
       語感的にエージェントがクライアント側、マネージャーがサーバー側のように感じますが、MIB との関係だけでいえば語感とは逆なので注意です。
       エージェントが部下、マネージャーが上司で、報告する元ネタ(=MIB)は部下が持っていて上司が適宜それを確認に行く、って感じですね。

    [鯖缶] SNMP(1) OID と MIB

    2017年12月17日

     「SNMP を使えば周辺機器などの情報や状態を知ることができる」という程度の理解で SNMP を使ってみようと調べると OID とか MIB とかよくわからない用語に圧倒されました。たとえば MIB ツリーは以下のようなの図が提示されるわけですが、これがよくわからないのです。MIB-2 の OID が .1.3.6.1.2.1 だ、と言われてもサッパリなわけです(汗

                        iso(1)             階層1
                         │ 
                        org(3)             階層2
                         │ 
                        dod(6)             階層3
                         │ 
                        internet(1)        階層4
                         │ 
                         ├────────────────────────────────────────────┬────
                        mgmt(2)                                     private(4)             階層5
                         │                                            │
                        mib─2(1)                                    enterprise(1)          階層6
                         │                                            │
     ┌─────────┬─────────┴──┬─────┬─────┬────────┬───────┬───         │
    system(1) interface(2) at(3) ip(4) icmp(5)  tcp(6)  udp(7)        │                    階層7
                                                                      │
                          ─┬────────┬──────────────┬─────────┬────────┴──────┬───
                          cisco(9) lanmanager(77) nec(119)  microsoft(311)  ucdavis(2021)  階層7
    
    
    

     なのでもう少し平易な説明ができないものか?(特に Windows ユーザー向けに)と思ったのでまとめてみることにしました。

     上記の MIB ツリー。Windows ユーザーにとって一番イメージしやすい近い存在というとレジストリエディタではないかと思います。
     たとえば、Windows の場合コンピューター名を記録している場所は .iso.org.dod.internet.mgmt.mib-2.system.sysName.0 なので、レジストリエディタに模して見ると以下のような感じです。

     Windows だとシンボルの区切りは \ (バックスラッシュまたは円記号) ですが、SMMP の場合 . (ドット) で区切ります。なので、

    .iso.org.dod.internet.mgmt.mib-2.system.sysName.0
    

    は Windows 的な表記だと

    \iso\org\dod\internet\mgmt\mib-2\system\sysName\0
    

    と等価です。ドットから始まる場合、ルートからの絶対パスであるのも考え方は同じです。

     そして、このアドレス .iso.org.dod.internet.mgmt.mib-2.system.sysName.0 に格納されている値が "ComputerName.DomainName" (文字列値) であるというわけです。

     レジストリと明らかに違うのは、SNMP のデータベースは上記のようなシンボルを文字列で管理してるのではなくて、OID (=オブジェクト ID) 番号で管理している点です。OID 番号で表すと上記のツリーは以下のようになります。

     したがって、.iso.org.dod.internet.mgmt.mib-2.system.sysName.0 は .1.3.6.1.2.1.1.5.0 と表すことができます。というかこちらが真のアドレスです。これは IP アドレスとホスト名のような関係似ていて、真のアドレス (この場合は .1.3.6.1.2.1.1.5.0) に対して別名 (この場合は .iso.org.dod.internet.mgmt.mib-2.system.sysName.0) を付けて人が読みやすいようにしています。

     それでは、IP アドレスとホスト名を対応付けている hosts のようなファイルが何か?というと、それが MIB ファイルです。

     以下は MIBファイル "SNMPv2-SMI.txt" の一部です。このようなファイルで OID 番号に対して 名前を付けています。
     (例えば org は "iso 3" なので .1.3、dod は "org 6" なので .1.3.6 といった感じです)

    SNMPv2-SMI DEFINITIONS ::= BEGIN
    
    -- the path to the root
    
    org            OBJECT IDENTIFIER ::= { iso 3 }  --  "iso" = 1
    dod            OBJECT IDENTIFIER ::= { org 6 }
    internet       OBJECT IDENTIFIER ::= { dod 1 }
    
    directory      OBJECT IDENTIFIER ::= { internet 1 }
    
    mgmt           OBJECT IDENTIFIER ::= { internet 2 }
    mib-2          OBJECT IDENTIFIER ::= { mgmt 1 }
    transmission   OBJECT IDENTIFIER ::= { mib-2 10 }
    
    experimental   OBJECT IDENTIFIER ::= { internet 3 }
    
    private        OBJECT IDENTIFIER ::= { internet 4 }
    enterprises    OBJECT IDENTIFIER ::= { private 1 }
    

     従って .iso.org.dod.internet.mgmt.mib-2.system.sysName.0 という指定をしたい場合は MIB ファイルを適切に配置することが必要になります。対して .1.3.6.1.2.1.1.5.0 でアクセスする場合には MIB ファイルは必要ではありません。

     そしてこのレジストリ全体に相当するデータベースのことを MIB (=Management Infomation Base) といいます。(したがって「MIB」と「MIB ファイル」は同じものではありません。)

    [Linux] NTP Client の設定 (CentOS 7)

    2017年12月9日

     CentOS 7 での NTP Client の設定方法のメモ。

    1. /etc/chrony.conf を編集します。

       以下を入力します。

      nano /etc/chrony.conf
      

       編集前の chrony.conf。server から始まる設定を全部削除します

      # Use public servers from the pool.ntp.org project.
      # Please consider joining the pool (http://www.pool.ntp.org/join.html).
      server 0.centos.pool.ntp.org iburst
      server 1.centos.pool.ntp.org iburst
      server 2.centos.pool.ntp.org iburst
      server 3.centos.pool.ntp.org iburst
      
      # Record the rate at which the system clock gains/losses time.
      driftfile /var/lib/chrony/drift
      

       編集後の chrony.conf。
       xxx.xxx.xxx.xxx は同期したい時刻サーバーのアドレスです。(IP でも FQDN でもどちらでも可)

      # Use public servers from the pool.ntp.org project.
      # Please consider joining the pool (http://www.pool.ntp.org/join.html).
      server xxx.xxx.xxx.xxx iburst
      
      # Record the rate at which the system clock gains/losses time.
      driftfile /var/lib/chrony/drift
      
    2. chronyd を再起動します。
      systemctl restart chronyd.service
      
    3. 更なる設定

       ここがくわしそうです。> https://qiita.com/yunano/items/7883cf295f91f4ef716b

    [Oracle] Instant Client のインストール (11.2 for Windows)

    2017年12月8日

     Oracle の インスタントクライアントのインストールのメモ。バージョンは 11.2 です。
     今回は ODP.NET (.NET4系) と SQL*Plus をインストールしてみました。

    1. ODAC112040Xcopy_32bit を ダウンロードします。

       OTN のサイト (http://www.oracle.com/technetwork/database/windows/downloads/utilsoft-087491.html) を参照し ODAC112040Xcopy_32bit.zip (ODAC 11.2 Release 6 (11.2.0.4.0) Download the XCopy version) を選択します。
       ダウンロードした ODAC112040Xcopy_32bit.zip を適当なフォルダに展開します。ここでは展開先フォルダを C:\ORATEMP とします。

    2. ODAC (Oracle Data Access Components for Windows) のインストールをします。

       管理者モードでコマンドプロンプトを起動します。
       カレントディレクトリを C:\ORATEMP (cd C:\ORATEMP) にしてから install と打ってみます。すると以下のように表示されます。

      C> install
      
      Usage:
        install.bat component_name oracle_home_path oracle_home_name [install_dependents]
      
      Example:
        install.bat all       C:\oracle myhome       (install all components)
        install.bat odp.net2  C:\oracle myhome true  (install ODP.NET 2 and its dependent components)
        install.bat odp.net4  C:\oracle myhome true  (install ODP.NET 4 and its dependent components)
        install.bat asp.net2  C:\oracle myhome true  (install ASP.NET Providers 2 and its dependent components)
        install.bat asp.net4  C:\oracle myhome true  (install ASP.NET Providers 4 and its dependent components)
        install.bat oledb     C:\oracle myhome true  (install OraOLEDB and its dependent components)
        install.bat oo4o      C:\oracle myhome true  (install OO4O and its dependent components)
        install.bat oramts    C:\oracle myhome true  (install ORAMTS and its dependent components)
        install.bat basic     C:\oracle myhome false (install Oracle Instant Client)
      

       今回は odp.net4 をインストールするので以下のように入力します。
       インストール先フォルダを C:\Oracle、Oracle Home の名前を odac112 としています。

      install odp.net4 C:\Oracle\ odac112
      
    3. instantclient-sqlplus-nt-11.2.0.4.0.zip を ダウンロードします。

       OTN のサイト (http://www.oracle.com/technetwork/topics/winsoft-085727.html) を参照し instantclient-sqlplus-nt-11.2.0.4.0.zip (Instant Client Package - SQL*Plus: Additional libraries and executable for running SQL*Plus with Instant Client) を選択します。

    4. SQL*Plus のインストールをします。

       ダウンロードした instantclient-sqlplus-nt-11.2.0.4.0.zip を解凍すると instantclient_11_2 というフォルダがあり、その中に sqlplus.exe 他のファイルがあります。このファイル群を上記で ODAC をインストールしたフォルダ (C:\Oracle) にコピーします。
       コピーした結果、sqlplus.exe と adrci.exe が同じフォルダ (C:\Oracle) にあることを確認します。

    5. レジストリに設定を追加します。

       デフォルトだと SQL*plus のメッセージが英語なので、これを日本語に変更します。
       Windows が 64bit 版の場合、レジストリエディタ等で HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Oracle\KEY_odac112 を開きます。(最後の KEY_ の後の文字列が Oracle Home の名称です)
       Windows が 32bit 版の場合や 64bit 版 の Oracle Client をインストールした場合の場所は HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\KEY_odac112 です。(途中の WOW6432Node がありません)

    6.  開いた場所に以下の値を足します。

      文字列値: NLS_LANG  値: Japanese_Japan.JA16SJISTILDE
      文字列値: SQLPATH   値: C:\Oracle
      

       これで SQL*plus が日本語表示になることを確認します。
       また、C:\Oracle に login.sql を作成すると、ログイン時に読み込んで実行されます。(login.sql を作成し set linesize 300 とかをいれておくと便利)

    7. 起動の確認をします。

       コマンドプロンプトを起動し、カレントディレクトリをインストールフォルダにします。(cd C:\Oracle)
       ここで、sqlplus /nolog と打ち、起動できれば OK です。
       ただ、この方法だと毎回カレントディレクトリをインストールフォルダにしてから実行する必要があるので、PATH を設定するなどをしたほうが良いですが、既存環境との干渉もあるかと思うので、そのあたりを配慮の上設定してください。

    8. tnsnames.ora の設定

       tnsnames.ora の設定がしたい場合はインストールディレクトリの下の network\admin (C:\Oracle\network\admin) に設置します。tnsname.ora のサンプルファイルは sample フォルダ (C:\Oracle\network\admin\sample) にあります。
       または、TNS_ADMIN 環境変数 (または上記 NLS_LANG を設定したレジストリキーに TNS_ADMIN 値) に tnsnames.ora を設置するフォルダ名を設定します。

    [Oracle] CentOS 7 minimal に Oracle Database XE 11gR2 をインストール

    2017年12月8日

     CentOS 7 minimal に Oracle Database XE 11gR2 をインストールする場合の手順のメモ。
     主にここの手順をみて行いましたので、より詳しい解説はそちらへ。
     http://www.ajisaba.net/db/ora_linux_install.html

    1. Oracle XE をダウンロードします。

       OTN のダウンロードサイト (http://www.oracle.com/technetwork/jp/database/database-technologies/express-edition/downloads/index.html) から Oracle Database Express Edition 11g Release 2 for Linux x64 を選択し oracle-xe-11.2.0-1.0.x86_64.rpm.zip をダウンロードします。

    2. CentOS 7 minimal のインストールをします。

       CentOS minimal 自体のインストール方法はこちらを参照してください。 (http://ooltcloud.sakura.ne.jp/blog/201711/article_30210409.html)
       CentOS 7.4 インストール直後, メインメモリ 1GB 環境で Oracle Database XE 11.2 はインストール出来ましたが、環境によってはメモリ不足などでインストール出来ない場合があるので注意してください。

    3. インストールした CennOS に SSH を使って root でログインします。

       ダウンロードした oracle-xe-11.2.0-1.0.x86_64.rpm.zip を /tmp に SSH-SCP で転送しておきます。

    4. インストールに必要なパッケージのインストールをします。
      yum install unzip
      yum install bc
      yum install nano
      

       bc は計算式(?)を渡すと答えを戻してくるようなコマンドのようです。Oracle のインストーラーか使っているようなので用意しておきます。bc 自体の使用例は以下のような感じです。

      echo "1+2+3" | bc
      6
      

       nano はエディタです。(個人的な好みです。お好みに応じて emacs とかどうぞ)

    5. oracle-xe-11.2.0-1.0.x86_64.rpm.zip を /tmp に解凍します。
      cd /tmp
      unzip oracle-xe-11.2.0-1.0.x86_64.rpm.zip
      
    6. /tmp/Disk1 をカレントにして、解凍された rpm をインストールします。
      cd /tmp/Disk1 
      rpm -ivh oracle-xe-11.2.0-1.0.x86_64.rpm
      
    7. インストールが完了したら /etc/init.d/oracle-xe configure を実行し、初期化を行います。

       例えば以下のような感じで入力します。(網掛部分)
       また、netstat でエラーが出ていますが問題なさそうなので無視しています。(そのかわり Listen ポートが被らないように設定します。使用ポートは ss コマンドで確認します。)
       エラーが気になる人は netstat をインストール (yum install net-tools) してから以下の初期化を実行してください。

      [root@localhost ~]# /etc/init.d/oracle-xe configure
      
      Oracle Database 11g Express Edition Configuration
      -------------------------------------------------
      This will configure on-boot properties of Oracle Database 11g Express
      Edition.  The following questions will determine whether the database should
      be starting upon system boot, the ports it will use, and the passwords that
      will be used for database accounts.  Press  to accept the defaults.
      Ctrl-C will abort.
      
      Specify the HTTP port that will be used for Oracle Application Express [8080]: 8080
      
      /etc/init.d/oracle-xe: line 362: netstat: command not found
      Specify a port that will be used for the database listener [1521]: 1521
      
      /etc/init.d/oracle-xe: line 405: netstat: command not found
      Specify a password to be used for database accounts.  Note that the same
      password will be used for SYS and SYSTEM.  Oracle recommends the use of
      different passwords for each database account.  This can be done after
      initial configuration: (管理者用のパスワード)
      Confirm the password: (管理者用のパスワード)
      
      Do you want Oracle Database 11g Express Edition to be started on boot (y/n) [y]: y
      
      Starting Oracle Net Listener...Done
      Configuring database...Done
      Starting Oracle Database 11g Express Edition instance...Done
      Installation completed successfully.
    8.  最後に Installation completed successfully. が出ていれば成功です。

    9. sqlplus 用に rlwrap をインストールします。

       標準の状態だと sqlplus でカーソルキーを使った操作ができないので rlwrap というラッパーを導入します。
       詳しくはここを参照して下さい。 https://qiita.com/inomasa/items/391c025532db6b87a1d5

       rlwrap は 公式リポジトリにはないパッケージらしく、epel リポジトリを参照する必要があるらしいです。なので、まずは epel リポジトリを参照できるようにします。
       epel についてはここを参照しました。http://note.kurodigi.com/centos7-epel/

      yum install epel-release.noarch
      

       デフォルトの設定だと公式リポジトリにあるパッケージも epel から取得するようになるらしいので、設定を変更し普段は epel を参照しないようにしておきます。

      nano /etc/yum.repos.d/epel.repo
      

       [epel] ブロックにある enabled を 0 にします。

      [epel]
      name=Extra Packages for Enterprise Linux 7 - $basearch
      #baseurl=http://download.fedoraproject.org/pub/epel/7/$basearch
      mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-7&arch=$basearch
      failovermethod=priority
      enabled=0
      gpgcheck=1
      gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
      

       rlwrap をインストールします。上記の設定で epel を参照しない設定にしたので、--enablerepo=epel オプションを付加して epel リポジトリを参照するように指定してインストールします。

      yum install --enablerepo=epel rlwrap
      
    10. 環境設定をします。

       Oracle ユーザーでログインし、.bash_profile, .bashrc の設定をします。
       Oracle のインストールで作成された oracle ユーザーには、.bash_profile, .bashrc がないらしいので、ひな形フォルダ (/etc/skel/) から .bash_profile, .bashrc をコピーしてから行います。

      su - oracle
      cp /etc/skel/.bash_profile /u01/app/oracle/
      cp /etc/skel/.bashrc /u01/app/oracle/
      nano ~/.bashrc
      

       .bashrc の内容を以下のように変更します。
       先頭の . (ドット) は忘れないように。これがないとスクリプト内で設定した環境変数が設定されません。(言い回しが難しい -_-;)

      # .bashrc
      
      # Source global definitions
      if [ -f /etc/bashrc ]; then
              . /etc/bashrc
      fi
      
      # Uncomment the following line if you don't like systemctl's auto-paging feature:
      # export SYSTEMD_PAGER=
      
      # User specific aliases and functions
      . /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh
      
    11. Oracle への接続の確認をします。

      一旦 root に戻り、その後 oracle ユーザーで再接続してから oracle への接続を確認します。

      [oracle@localhost root]$ rlwrap sqlplus / as sysdba
      
      SQL*Plus: Release 11.2.0.2.0 Production on 金 12月 8 00:00:00 2017
      
      Copyright (c) 1982, 2011, Oracle.  All rights reserved.
      
      
      
      Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
      に接続されました。
      SQL> select status from v$instance;
      
      STATUS
      ------------
      OPEN
      
      SQL> 
      

       sqlplus の前に rlwrap を書かないと、カーソルキーを操作したとき謎の文字が表示されるので注意します。

      Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
      に接続されました。
      SQL> ^[[A
      

       面倒くさい、と思うようであれば .bashrc に alias を設定してください。

      alias sqlplus='rlwrap sqlplus'
      
    12. 外部からの接続確認をします。

       外部から接続する場合にはファイアウォールを適切な設定にします。今回は面倒なので停止にします。

      systemctl stop firewalld
      systemctl disable firewalld
      

       これで、外部からの接続に成功すれば OK です。
       以下は Windows のコマンドプロンプトからの接続の例です。

      C>sqlplus sys/管理者用のパスワード@xxx.xxx.xxx.xxx/xe as sysdba
      
      SQL*Plus: Release 12.1.0.1.0 Production on 金 12月 8 20:59:15 2017
      
      Copyright (c) 1982, 2013, Oracle.  All rights reserved.
      
      
      
      Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
      に接続されました。
      SQL>
      

    [鯖缶] Hinemos 6 のインストール

    2017年12月3日

     Hinemos 6 になると Windows 版の Manager がリリースされるという話だったので期待して待っていたのですが、Windows 版はサブスクリプション契約のみの提供で、契約がない場合は試用版すら提供されないという話のようなので、あきらめて Linux 版のインストールをしてみました。以下はその際のメモ。

     Hinemos のインストールは http://www.hinemos.info/technology/nttdata/2017033001 を参考にして作業しています。なので詳しい解説はリンク先を参照してください。

     ただ、上記の手順では hinemos_change_listen_address.sh の話がでてこないので、本件については http://d.hatena.ne.jp/hinemos_dev/20170206 を参照しています。(JDK の不具合が原因らしいので、最近の JDK をインストールしていれば問題は発生しないのかもしれません)

    1. Cent OS 7 をインストールします

      Cent OS 7 のインストールは以下を参照してください。

    2. SSH で接続します。

       ローカルコンソールから作業しないように。インストール後の Hinemos が日本語表示になりません。
       SSH で接続した場合も、locale が日本語になっていることを確認します。
       適当に文字を打って、日本語のエラーメッセージが戻れば OK です。

       以下は aaaa と打ってエラーを出し、メッセージが日本語であることを確認した例。

    3. まず、Hinemos のインストールに必要なパッケージ類をインストールします。(nano をインストールしているのは個人的な好みです)
      yum install nano
      yum install unzip
      yum install vim-common
      yum install java-1.8.0-openjdk
      yum install wget
      
    4. github から Hinemos 6 の Manager と Web Client の rpm を /tmp にダウンロードします。
      cd /tmp
      wget https://github.com/hinemos/hinemos/releases/download/v6.0.0/hinemos-6.0-manager-6.0.0-1.el7.x86_64.rpm
      wget https://github.com/hinemos/hinemos/releases/download/v6.0.0/hinemos-6.0-web-6.0.0-1.el7.x86_64.rpm
      
    5. SELinux を無効にします。

      Hinemos は SELinux が無効でないとインストールできません。
      getenforce と入力して Enforcing と表示される場合、SELinux は有効なので無効にします。

      nano /etc/selinux/config
      

      SELinux の項目が enforrcing となっているので、

      disabled に変更します。

      CTRL+X で変更を保存し、再起動します。

      reboot
      

      再起動後 getenforce と入力して Disabled と表示されれば OK です。

    6. Manager と Web Client をインストールします。
      cd /tmp
      rpm -ivh hinemos-6.0-manager-6.0.0-1.el7.x86_64.rpm
      rpm -ivh hinemos-6.0-web-6.0.0-1.el7.x86_64.rpm
      
    7. Manager と Web Client を起動します。
      service hinemos_manager start
      service hinemos_web start
      
    8. listen アドレスの変更をします。
      たとえば、manager をインストールしたサーバーの IP アドレスが 192.168.0.1 である場合は以下のようにします。
      http://d.hatena.ne.jp/hinemos_dev/20170206

      service hinemos_manager stop
      service hinemos_pg start
      cd /opt/hinemos/sbin/mng
      ./hinemos_change_listen_address.sh 192.168.0.1
      service hinemos_pg stop
      service hinemos_manager start
      

      ss -ao を実行し、port 8080 の待ち受けが 127.0.0.1 ではなく 192.168.0.1 であれば OK です。

      ss -ao | grep tproxy
      tcp    LISTEN     0      50     ::ffff:192.168.0.1:tproxy               :::*
      
    9. 面倒なのでファイアウォールを止めます。(良い子は正しく必要なポートのみを開けるようにしましょう)

      systemctl stop firewalld
      systemctl disable firewalld
      
    10. Web Client への接続を確認します。

      webclient をインストールしたサーバーに接続します。
      その際、接続先 URL が ループバックアドレス (127.0.0.1) の場合は、上記で設定した listen アドレスに変更します。

      127.0.0.1 となっている場合は、

      192.168.0.1 に変更します。(パスワードも入力します)

      ログインできれば完了です。

    [Linux] CentOS 7 の CUI でネットワーク設定を確認する

    2017年12月2日

     インストール直後にイーサネットが繋がらない場合は、まずはネットワーク設定を確認する必要があります。
     以下は nmtui を使用した確認と設定の方法のメモ。

    1. "ip address" と入力し、現在の ip アドレスを確認する。(最近は ifconfig ではないらしい)
    2. "nmtui" と入力し、テキストインターフェースのネットワークマネージャーを起動する。
    3. "Edit a connection" を選択し [Enter] を押す。
    4. 編集したい NIC を選択し [Enter] を押す。
    5. 今回は自動接続されないのが問題なので、"Autometically connect" へカーソルを移動し [Space] を押す。
    6. "OK" にカーソルを移動し [Enter] を押す。
    7. "Back" にカーソルを移動し [Enter] を押す。
    8. "Quit" にカーソルを移動し [Enter] を押す。
    9. 再度 ip address" と入力し、現在の ip アドレスを確認する。

    [その他] QR コードの符号化方式

    2017年12月1日

     ちょっと気になったので QR コードの符号化方式について調べてみたのでメモ。
     けれども、以下に書くことは不正確なので注意。正しく知りたい場合は JIS X 0510 を当たるなどしてください。

     発端は「QR コードは Shift-JIS で記録している」という記述をどこかで見たこと。で、実際のところどうなのだろう?とか思って調べてみた。

     結論としては、QR コードの符号化方式は、数字, 英数字, 漢字, バイト列 の 4 つのモードがあるらしい。
     以下、それぞれの大雑把 (= つまり甚だ正確ではない) にどういうことをしているかまとめる。

    1. 数字モード

       10 進数 3 桁を 10 bit で表現する。
       1000 通り < 1024 (2^10) ということ。

    2. 英数字モード

       数字、英大文字および記号、計 45 字 2 桁を 11 bit で表現する。
       45^2 = 2025 通り < 2048 (2^11) ということ。

    3. 漢字モード

       Shift-JIS 領域 (JIS X 0208) の文字 1 桁を 13 bit で表現する。
       JIS 第 1 水準 + 第 2 水準 + 非漢字 で 6,879 文字らしいから、8192 (2^13) で入るということで。

    4. バイトモード

       8bit を表現。

     漢字モードでは Shift-JIS コードをもとに計算しますよ、ということらしいので「Shift-JIS で記録」と称している模様。なので Shift-JIS のコードそのものが記録されているわけではない点に注意。
     もっともどんなコードで記録されているかを意識する必要はないかとは思います。それよりは記録される文字の範囲は気にしておく必要がありそうです。

     それよりも残るバイトモードとは何なのか。
     以下のようなものが入るらしい。

    1. 漢字モードで入れることのできなかった領域の Shift-JIS 文字
    2. Unicode 他 Shift-JIS 以外のエンコード方式の文字
    3. 英小文字

     バイトモードというからバイトストリーム的なものが入るのかとおもったら、そういうことではなくて、基本は他のモードで入れられなかった文字がここに入る、といった趣の模様。
     しかしそうすると、このバイトモードに格納されている文字の文字コードは何? ということになるわけですが、しかしエンコード方式を QR コード内に記録するところはなさそうなので、アプリケーション側でよしなに…、ということっぽい。 ( よく調べてないので違うかもしれませんが )

     もちろんアプリケーション固有で解釈できる、ということはバイトストリーム的な扱いも可能なわけですが。
     とはいえ、この領域は多くは QR リーダー内で処理されているでしょうから手が出ない気がしますが… ( 実際はどうなのでしょう? )

     JSON や XML のような文書構造もなさそうなので、必要であればアプリケーション側で実装、ですね。

     調べた感想としては、3桁まとめて符号化して容量を減らす、などのアイデアが面白いな、よく考えられているな、って感じでした。
     あと、ASCII 領域である英小文字がバイトモードの領域 (= 符号化方式不明な領域 ) にしか入らない、って点も意外でした。

    [PS1] Powershell で WMI を参照する

    2017年12月1日

     Powershell を使って WMI オフジェクトと対話的にたわむれる方法のメモ。

    • WMI クラスの一覧を表示
      Get-WmiObject -List
      
    • 指定した WMI クラスのプロパティの一覧を表示
      # Win32_OperatingSystem クラスの場合
      (Get-WmiObject -List Win32_OperatingSystem).Properties | select Name 
      
    • 指定した WMI オブジェクトの値を表示
      # Win32_OperatingSystem オブジェクトの場合
      Get-WmiObject Win32_OperatingSystem | select * 
      
    • WQL (WMI Query Language) を使用した検索
      # Win32_OperatingSystem オブジェクトの、Caption, OSArchitecture, BuildNumber, Version, CSDVersion を取得したい場合
      $q = "
      select
        Caption,
        OSArchitecture,
        BuildNumber,
        Version,
        CSDVersion
      from
        Win32_OperatingSystem
      "
      
      Get-WmiObject -Query $q
      
    • 参考文献など

    [Win] キーボードから直接文字コードを指定して入力する方法

    2017年11月30日

     Windows ではキーボードから直接、IME に頼らず文字コードを入力することができます。

     基本的な操作は、[ALT] キーを押し込んだままテンキーで文字コードを入力し [ALT] キーを離します。
     たとえばメモ帳で、[ALT] を押す → [3] → [3] → [4] → [4] → [0] → [ALT] を離す、とすると「あ」が入力できます。33440 は「あ」の Shift-JIS における文字コード (82A0h = 33440) です。

     さて問題はここからです。

     メモ帳ではなく Wordpad で「あ」を入力したいとします。
     ところがメモ帳と同じように、[ALT] を押す → [3] → [3] → [4] → [4] → [0] → [ALT] を離す、としても意図した文字は入力できません。「芠」と謎の文字が入力されます。
     そこで Wordpad の場合は、[ALT] を押す → [1] → [2] → [3] → [5] → [4] → [ALT] を離す、とします。すると「あ」が入力されます。なぜかというと、12354 というのは「あ」の Unicode における文字コード (U+3042 = 12354) だからです。

     つまりこの「[ALT] キーを押し込んだまま文字コードを入れる」という操作は、それを受け取るアプリケーションによって挙動が異なるということです(汗

     メモ帳や Excel は Shift-JIS 派、Microsoft Word, Wordpad, Outlook は Unicode 派です。Microsoft Edge に至っては、下位 8bit を Shift-JIS で評価します。 (つまり 177 も 433 も共に「ア」と評価されます)

     Microsoft Word と Wordpad は同じかと思えばそうでもなく、例えば 177 の場合、Microsoft Word では「ア」ですが、Wordpad (Windows 8 系以前) では「±」だったりします。Wordpad のほうがより Unicode に忠実ですね。Microsoft Word のほうは 8bit 領域は Shift-JIS で判断し、それ以上は Unicode で判断といった感じです。とはいえ、Windows 10 の Wordpad では Microsoft Word と同じく「ア」だったりします(汗

     また、Windows Form (VB や C#) の場合、TextBox コントロールは Shift-JIS 派、RichTextBox コントロールは Unicode 派です。つまりこの両方のコントロールを使用したアプリケーションが存在すると、同一アプリケーション内の入力の場所ごとに違う評価がされることになります。

     とにかく重要なことは、この IME を経由しない文字コード入力というのは、受け取るアプリケーションやコントロールによって挙動が異なる、ということです。

     なので例えば QR リーダーのようなデバイスで、キーボードエミュレーション (HID 接続) を行う場合、ASCII 領域以外の文字は何が入ってくるかわからないということです。従って、キーボードエミュレーションで ASCII 領域以外の文字を入力させたい場合、上記のような傾向はあるものの確実ではないので、アプリケーションごと、入力場所毎に意図した入力ができるかどうかを検証しておく必要があります。

    [Linux] CentOS7 minimal のインストール

    2017年11月30日

     ごくたまに CentOS をインストールする必要に迫られ、またその際 GUI とか要らないので手早く、ということがあるので CentOS minimal をインストールする手順を以下にメモ。

    1. CentOS のダウンロードサイト (https://www.centos.org/download/) に行き「Minimal ISO」を選択する。
    2. 国内の適当なサイトを選択し、ダウンロードする。
    3. ダウンロードした iso (DVD) を用いて、仮想マシン等で DVD boot する。
      私の使用する PC の都合と、この記事に貼る画面サイズの都合で、今回は解像度を 800*600 としたいので、カウントダウンが表示されている間に、[Tab] キーを押す。 (ストレスなくインストールしたいなら 1024*768 以上にしたほうが良い)
      デフォルトでよい場合は「Install CentOS 7」か「Test this media & install CentOS 7」のどちらかを選択し、[Enter] を押す。
    4. カウントダウンが表示されている間に [Tab] キーを押すと、画面下部に起動コマンドが表示されることを確認。
    5. インストール時の解像度を変更したい場合は resolution_800x600 を quite のあとに追記し、[Enter] を押す。
      (下に例示画面のパラメーターの末尾の _ はカーソルでありアンダースコアではないので、アンダースコアは入力しない)
    6. 仮想マシンが Hyper-V の場合 resolution_800x600 が効かないので、代わりに video=hyperv_fb:800x600 を追記し[Enter] を押す。
      (下に例示画面のパラメーターの末尾の _ はカーソルでありアンダースコアではないので、アンダースコアは入力しない)
    7. GUI のインストール画面が表示されたら「日本語」を選択。
    8. システムにある「インストール先」を選択。
    9. インストール先のデバイスを選択。選択後、または自動的に選択されていれば、そのまま「完了」を押下し戻る。
    10. 「ネットワークとホスト名」を選択。
    11. 「イーサネット」を「ON」にする。
      必要に応じて「設定」を押す。必要なければ「完了」を押す。
    12. 「設定」を押した場合は必要な設定を行う。たとえば固定の IPv4 アドレスを付与する場合は「IPv4 のセッティング」を選択し、必要な情報を入力し「保存」を押す。
    13. 「インストールの開始」を押す。
    14. インストールしている間に「ROOT パスワード」を押す。
    15. root のパスワードを入力して「完了」を押す。
    16. インストールが終了したら「再起動」を押す。
    17. [Enter] を押す。
    18. ログインプロンプトが表示されることを確認。
    19. root ユーザーでログインできることを確認。
    20. またコンソール操作は日本語が化けるので、以後の操作は SSH を介して行います。

    [PS1] Powershell でレジストリ値の読み取り

    2017年11月18日

     以下な感じで。

    PS> (Get-Item "hklm:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").GetValue("ReleaseId")
    1703
    

     ポイントは、キーの部分 (上記だと Get-Item にかかる部分) と値 (上記だと .GetValue にかかる部分) を分けて書くところでしょうか。(一括で書けない)

    [Excel] 突如としてマクロのボタンが消えた場合の対処

    2017年11月14日

     CTRL + 6 を押してみましょう。たぶんあなたは "&" を打とうとして SHIFT ではなく CTRL を押しました(汗

     GUI で直す場合は以下で。

     この「オブジェクトの表示」が「なし」になっているときは、グラフや画像等、いわゆる「オブジェクト」の類が全て非表示になります。
     また、新たな画像の貼り付けやグラフの挿入もできなくなっていますから、そのようなことが起こった場合は、当該機能で非表示にされていないかを確認します。

    [Oracle] SQL ファイルに exit を書きたくない場合の対処

    2017年11月10日

     結論から書くとこんな感じです。(Windows)

    type test.sql | sqlplus -s / as sysdba
    

     つまり、sql ファイルの内容をパイプで sqlplus に渡してやります。

     教科書通りだと、sqlplus -s / as sysdba @test.sql なのですが、これだと exit のない sql ファイルだと sqlplus 内の入力待ちで止まってしまいます。かといって、sql ファイルに exit を足すと、sqlplus を対話的に使っているときにその sql ファイルをSQL> @test.sqlな感じで呼び出すと sqlplus を exit してしまうので(汗

    [Oracle] 表領域の使用量/空き容量を確認する方法

    2017年11月6日

     Oracle の表領域の使用量と空き容量を知る方法について、OEMDC(Oracle Enterprise Manager Database Console)や SI Object Browser などのツールを使って確認するのは容易ですが、SQL*Plus で確認する方法がわからないのことが多いので、その方法のメモ。

     まず大前提として「表領域の現在容量や空き容量という情報はない」です。あるのは「表領域に属する "データファイル" のサイズと空き容量」です。
     さらにはデータファイルについても「現在使用量」という情報はないです。データファイルの現在使用量はデータファイルのサイズから空き容量を差し引いて求めます。

     それを踏まえたうえで、例えば USERS 表領域の使用量を求めるには以下のように求めます。

    1. USERS 表領域に属するデータファイルごとのサイズを表示します。
      select FILE_ID, BYTES, FILE_NAME from DBA_DATA_FILES where TABLESPACE_NAME='USERS';
      
    2. USERS 表領域に属するデータファイルごとの空き領域を表示します。
      select FILE_ID, BYTES from DBA_FREE_SPACE where TABLESPACE_NAME='USERS';
      
    3. これらの合計の差が使用量になりますが、ファイル毎に表示されて面倒なのと、単位が Byte で桁が無駄に多いので、集計して MB で表示します。
      select sum(BYTES) / (1024*1024) from DBA_DATA_FILES where TABLESPACE_NAME='USERS'; --①
      select sum(BYTES) / (1024*1024) from DBA_FREE_SPACE where TABLESPACE_NAME='USERS'; --②
      
    4. 上記で出た数値を差し引けば(①-②)、現在の使用量が求められます。

     表領域ごとに使用量と使用率の一覧が出る…みたいな SQL が欲しい場合は、上記をヒントに自分で作るか、他の色んなサイトで紹介されているのでそちらをどうぞ(汗

     また余談ですが、上記のように迂遠な方法を使わずとも、以下の view を用いて表示する方法もあります。

    select * from DBA_TABLESPACE_USAGE_METRICS where TABLESPACE_NAME='USERS';
    

     これの注意点としては、表示される数値はブロック数(ブロックサイズは show parameter db_block_size で参照)でありバイトではない点と、AUTOEXTEND ON を指定している場合は、現在のデータベースファイルのサイズではなく、理論上の最大(ディスク容量またはデータベースファイルの最大拡張サイズ)が表示される点です。特性を理解したうえで使い分けてください。

    [VB.NET] JSON へのシリアライズとデシリアライズ

    2017年7月1日

     VB.NET で JSON を使ったシリアライズ/デシリアライズの例。
     Json.DataContractJsonSerializer は .net 4 系で追加されたようなので、.net 2 系では動作しません。

     ポイントは、シリアライズ対象のクラスに <DataContract> 属性をつけること、シリアライズ対象のメンバーに<DataMember> 属性をつけることだろうか。

    ' 参照設定 "System.Runtime.Serialization"
    
    Imports System.IO
    Imports System.Text
    Imports System.Runtime.Serialization
    
    <DataContract>
    Public Class Container
    
        <DataMember>
        Public Items As New Dictionary(Of String, Object)
    
    End Class
    
    Module Module1
    
        Sub Main()
    
            ' 元のオブジェクトの作成
            Dim obj0 = New Container
            obj0.Items.Add("AAA", 100)
            obj0.Items.Add("BBB", 250)
    
            'シリアライザー
            Dim serializer = New Json.DataContractJsonSerializer(GetType(Container))
    
            ' シリアライズ
            Dim str1 As String = ""
            Using mem = New MemoryStream()
                serializer.WriteObject(mem, obj0)
                str1 = Encoding.UTF8.GetString(mem.ToArray())
            End Using
    
            ' デシリアライズ
            Dim obj2 As Container
            Using mem = New MemoryStream(Encoding.UTF8.GetBytes(str1))
                obj2 = CType(serializer.ReadObject(mem), Container)
            End Using
    
        End Sub
    
    End Module
    

    [環境] STOP エラーの一覧

    2017年6月23日

     ブルースクリーンに表示される STOP エラーの一覧。いつも探すのに手間取るのでメモ。

     Bug Check Code Reference
     https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/bug-check-code-reference2

    [環境] 他のコンピューターに msg で通知をする。

    2017年6月11日

     Windows にはメッセージを通知する msg.exe がありますが、これを使って他のコンピューターに通知を行う場合、受信側のファイアウォールの設定が必要です。

    1. win+r を押し「ファイル名を指定して実行」を呼び出す
    2. wf.msc を入力し「セキュリティが強化された Windows ファイアウォール」を呼び出す
    3. 左ペインの「受信の規則」を選択する
    4. リモートサービス管理(NP受信)を選択する
    5. 「有効」と「接続を許可する」をチェック

    [Excel] Excel の検算に電卓が必要な理由

    2017年6月11日

     先日こんなツイートがありました。

     もちろんこれは大笑いするところですし、して良いのですが。他方「なぜ電卓で検算するのか」という点、すなわち「Excel よりも電卓のほうが信用できる」という点について理解したうえで笑ってない人も多い印象を受けたので、補足がてら以下に記す(ぇ

     例えば Excel で集計した場合、以下のようなことが起こります。

     小計を合計すれば ¥2,980 のはずですが、Excel での計算結果は ¥2,981 になってしまっています。

     なぜこのようなことが起こるかというと、表示形式で通貨にした時に、小数点以下は四捨五入で切り落としてしまう設定にしたからです。(デフォルト)

     実際、銭単位の請求は一般にはしないので円で丸めるのは正解です。しかし上記の例では表示でまるめられているだけであって、あくまで合計は ¥2,981 ではなく ¥2,980.8 なんですね。

     さて、ここまで踏まえたうえで。
     いや、小数点以下をどうかしたいという話だったらちゃんと ROUND 関数を使うなり処理すればいいのでは?という話になるのですが。しかし、今しているのは検算の話です。ワークシート本体で ROUND 関数を使うことは否定していないわけです。むしろちゃんと ROUND 関数を使ったりして合計が一致することを「検算」しようとしているわけです。

     そして「検算」という作業で。上記のような「みためは小数点以下はないけど実際には小数点以下の値があり、合計等の計算をすると、見た目の合計と真値の合計が不一致が発生する」系の検算って。Excel で出来ます?といったときに「こうすれば簡単にできるよ」と提案できる人は少数ではないかと思うわけです。

     かくて、Excel で主たる計算をした後電卓で検算する、ということが起こるわけです。

     検算するにしても Excel ですればいいじゃん?って話ではありますが、Excel だとセルの値をコピペしてしまうと同じ話が発生しますし「どのセルとどのセルを合計すれば?」という話になると、結局人の目に依存する話になってしまいます。かくて電卓が最適解になったりします。

     で、これを自動で検算させようとすると「どのセルをどうやって合計をしようとして」「そしてその正解はこうだ」的な人間の判断が必要でして、まさに AI を駆使した仕事になるわけです。 (ちなみに Excel 内の計算式は間違っている可能性があるので、それを元に検算することはできません。人は Excel の計算式を参照して検算しませんよね。)

     これが「これからはAIの時代だからお前らの仕事なんかなくなるんだぞ!」につながっているんだと思うので、深いツイートだな、とか思った次第(汗

    [与太] アジャイル開発にデメリットしか感じないという話

    2017年6月11日

     この記事はポエム記事です(汗
     個人的な偏見と恐らくは不正確な事実認識から書かれています。その点に注意して読み進めてください。

    • 前提
      • SI 案件 (=受託開発) の現場を想定
      • アジャイル開発の対義はウォーターフォール開発であると仮定
    • 結論

       モノを買う以上「機能と予算と期間」はセットであり、これらが強固に結びついている以上、(受託開発では) アジャイル開発は成り立たない。

    • 考察

       所謂受託開発や SI と呼ばれているものは、前述の「機能と予算と期間」が全て固定されたうえで契約されることがほとんどであり、また多少の細分化やフェーズ分けは行われたとしても、分けた単位ではやはり固定される。この点でアジャイル開発は適さないと考えている。
       こう書くと「それは契約の問題であり開発手法の問題ではない」という話になりがちなのだが、実際その指摘は一面的には真であると思う。しかし契約の形態が開発手法に影響を与えるのは当然であることに加え、開発手法の都合から契約を変更することはほぼ絶望的である。従って開発手法は契約に対して一方向的に従属する関係にあると言える。このため、開発手法の選択によって契約のありかたを考えないといけないなら「それは契約の問題であり開発手法の問題ではない」ということにはならない。ところがアジャイル開発が語られるうえで契約の問題はあまり話題に上ることはなく、そのあたりが齟齬や欺瞞、誤解や適合のむずかしさを生み出してしまっている源泉なのではないかと思っている。

       では逆にアジャイル開発が有効である場合は何か。それは開発チームが「機能と予算と期間」を可変させる権限を持っている場合ではないかと考えている。つまり開発を外部に委託するのではなく、内部で開発を行うケースである。これは「比較的小規模なベンチャーのようなチームが自己が提供するサービスを開発するケース」などが該当するのではないかと思う。
       しかし自己のサービスを開発する場合でも、規模が大きくなると組織の壁によって阻まれる。例えばリリース予定の機能がリリース予定の期日にリリースされることが確約できないと営業活動がむずかしいという問題がある。営業の視点では、予定された機能のリリースの期日がずれる、あるいはなくなるといったことは顧客に説明がつかなくなるので好まれない。従ってこのような力が働くとき、開発チームに対して「機能と期間」は確実に固定される。予算だけはその気になれば変動可能、というか嫌でも変動するが、しかし多くの場合は良い意味で変動することはない。

       結局は外部に向けてはウォーターフォール的な表現、内部においては許容できる範囲でアジャイル的な取り組み、というのが現状の精一杯ではないかと思う。
       また、この場合の「アジャイル的な取り組み」というのは、例えば 3 つのモジュールを作る場合に、純粋なウォーターフォール的な計画だと「設計→設計→設計→製造→製造→製造→テスト→テスト→テスト」みたいになるものを「設計→製造→テスト」× 3、みたいな感じにすることだと思ってる。つまり「動くソフトウェアを作る」ことを重視する。ただ、これも「許容できる範囲」と述べたように全てではなく、前半に設計だけで固めないとならない領域(DBの設計とか)はあるし、前述の「設計→製造→テスト」も「設計は上位のスキルが必要だ」「製造は打ち込み要素が大きくスキルが低くても構わない」「テストは当該の製造者ではない人あるいはそもそもプログラマではない人にやらせたい」などの要件、つまり人員配置の問題が絡むと、どうしてもアジャイル的なものにならない。

       この大枠ウォーターフォールなものに、ところどころ「アジャイル」的な何かを持ち込むめること、それがアジャイルのメリットだ、というならそれはメリットかもしれない。しかし実際のところ、そのレベルではアジャイル開発の登場以前からアジャイル的なものはやっていた。ただそれは「その部分はその方法が適当だったのでそうした」というだけの話である。例えばプロトタイプ開発はアジャイル開発よりも前からあり、必要があればその手法を用いることはあった。プロトタイプ開発とアジャイルは現在は違うとされているけれども「必要なものから優先的に、動くものをリリースしていく」という観点からは類似している。違うことは「顧客(特にそのサービスを使うエンドユーザー)のところまで供給されているかどうか」であって、プロトタイプ開発だって開発チーム内や顧客(元請や発注主)に対しては見せていた。だから手法としての差はあまりないと思う。それがプロトタイプコードがリリースコードであり供給相手がエンドユーザーまで延びたからアジャイルだ、といわれるのは個人的には違和感がある。

       また、アジャイルは雇用的なアプローチという見方もできる。つまり負荷の変動に弱い。時間あたりの処理量を固定して負荷を平準化し、総処理量の増加は時間方向に延ばす手法なのでそうなるのは当然ではある。
       これは例えば「初期開発では多くのリソースが必要だが、保守フェーズにはいると初期開発ほどのリソースは必要ない」というケースに対応し辛い。下手すると保守フェーズに入っても初期開発で抱えたリソース(つまり人)の雇用を維持し続ける必要がでてしまう。この問題に対する解が「受託開発」である以上、初期開発と保守フェーズの負荷を均すという対策はありえない。
       海外のことはまるで知らないが、恐らく海外では初期開発を行うに必要なリソースを雇用の後、初期開発が終了すると不要になるリソース分の雇用を切っているはずである。そうすれば「内部で開発」しつつ「必要な負荷」のコントロールができる。しかし現在の日本の雇用法制でこれができるか?というと疑問である。
       この点も、一般にアジャイル開発の説明では「チームの人数」に言及はあっても「プロジェクト全体におけるチームの数(や総人口)」に言及されることが少ない点が、誤謬を生み出している原因の一つだと思っている。

       というのが私の足りない頭で考えた現状であり、ウォーターフォールの「全ての WBS はプロジェクト開始前に計画されてなくてはならない」的な課題に対して脳内ドン・キホーテをやらかして百戦百敗した結果なのである。
       アジャイル開発のメリットに目を奪われそれを適用しようとしても、そうすると全体のバランスを崩してしまい結局はウォーターフォール的な安定に戻る。その繰り返しである。かつてメリットに見えたものはまやかしでしかないように感じている。

       ではウォーターフォールだと前述が解決できるのかというと出来ない。しかしウォーターフォールがアジャイル開発と大きく違い、かつ絶対的な利点は少なくとも「機能と予算と期間」にコミットできるという点にある。この差は大きい。それが例え「夢の計画」であっても・・・(汗

       つまりこの「機能と予算と期間」にどうコミットするか、この最初にして相当に高いハードルを超えないことには、アジャイル開発のメリットなど何一つ考えられない状況なのである。
       なので、誰かその方法を教えてください。もっともこれを超えられたとしても他にも問題は山積みだと思ってはいるが…(汗

    [VB.NET] .net remoting で RPC を行うサンプル

    2017年4月18日

     サーバー側 tcp ポート 8000 番を使ってリモートプロシージャコールを行ってみた例のメモ。

     3つのプロジェクトを作ります。

    1. RemoteClass プロジェクト (呼び出される処理)
    2. RemoteServer プロジェクト (待ち受けをするサーバープロセス)
    3. RemoteClient プロジェクト (呼び出す側のプロセス)

     コード例は以下。詳細な説明は割愛(ぇ

    1.  RemoteClass プロジェクトの内容

      Public Class RemoteClass
          Inherits MarshalByRefObject
      
          Private Shared _count As Integer
      
          Public Function Method1(a As Integer) As Integer
              RemoteClass._count += a
      
              Console.WriteLine("Server side output: {0}", _count.ToString)
      
              Return _count
          End Function
      
      End Class
      
    2.  RemoteServer プロジェクトの内容。
       (RemoteClass プロジェクトと System.Runtime.Remoting を参照し、ルート名前空間の名前を同じ名前にします)

      Imports System.Runtime.Remoting
      Imports System.Runtime.Remoting.Channels
      Imports System.Runtime.Remoting.Channels.Tcp
      
      Module Module1
      
          Sub Main()
              ChannelServices.RegisterChannel(New TcpChannel(8000), False)
      
              RemotingConfiguration.RegisterWellKnownServiceType(
                  GetType(RemoteClass),
                  "MyAPP",
                  WellKnownObjectMode.SingleCall)
      
              Console.WriteLine("Server Start")
              Console.ReadLine()
              Console.WriteLine("Server Stop")
          End Sub
      
      End Module
      
    3.  RemoteClient プロジェクトの内容。
       (RemoteClass プロジェクトと System.Runtime.Remoting を参照し、ルート名前空間の名前を同じ名前にします)

      Imports System.Runtime.Remoting
      Imports System.Runtime.Remoting.Channels
      Imports System.Runtime.Remoting.Channels.Tcp
      
      Module Module1
      
          Sub Main()
              ChannelServices.RegisterChannel(New TcpChannel(), False)
      
              Dim remoteClass = CType(Activator.GetObject(GetType(RemoteClass),
                                                  "tcp://localhost:8000/MyApp"),
                              RemoteClass)
      
      
              Dim result = remoteClass.Method1(1)
              Console.WriteLine("Client side output {0}", result)
      
              Console.ReadLine()
          End Sub
      
      End Module
      

    [WPF] WPF で点滅するテキストを実装する

    2017年4月17日

     かつてターミナルとかコンソールの時代は、テキストの装飾というと色指定か色反転 (=Inverse) か点滅かのいずれかしかなかったわけですが。

     HTML にしても XAML にしても、色指定は Foreground で、色反転相当は Background でなんとかなるわけですが、なぜか点滅が難易度高いんですよね… html はかつて netscape navigator とやらではイケたらしいですが…?

     ともかく、WPF XAML で点滅させる方法を考えてみました。以下はそのコード。

    <StackPanel>
        <StackPanel.Resources>
            <!-- 点滅のためのストーリーボードの作成 -->
            <Storyboard x:Key="BlinkingStoryboard">
                <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)"
                                               RepeatBehavior="Forever">
                    <DiscreteDoubleKeyFrame KeyTime="0"
                                            Value="0" />
                    <DiscreteDoubleKeyFrame KeyTime="0:0:0.5"
                                            Value="1" />
                    <DiscreteDoubleKeyFrame KeyTime="0:0:2"
                                            Value="0" />
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
    
            <!-- 点滅のためのスタイルの作成 -->
            <Style TargetType="TextBlock"
                   x:Key="BlinkingStyle">
                <Style.Triggers>
                    <Trigger Property="IsEnabled"
                             Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{StaticResource BlinkingStoryboard}"
                                             x:Name="BlinkingStoryboard1" />
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <StopStoryboard BeginStoryboardName="BlinkingStoryboard1" />
                        </Trigger.ExitActions>
                    </Trigger>
    
                </Style.Triggers>
            </Style>
    
        </StackPanel.Resources>
    
        <!-- 点滅対象のテキスト -->
        <TextBlock x:Name="Text1"
                   Text="点滅するテキスト"
                   Style="{StaticResource BlinkingStyle}"
                   IsEnabled="False" />
    
        <!-- 点滅/点灯を切り替えるためのボタン -->
        <StackPanel Orientation="Horizontal">
            <Button Content="点滅"
                    Width="50"
                    Click="StartBlinkingClick" />
            <Button Content="点灯"
                    Width="50"
                    Click="StopBlinkingClick" />
        </StackPanel>
    
    </StackPanel>
    

     コードビハインド側はこんな感じ。

    Private Sub StartBlinkingClick(sender As Object, e As RoutedEventArgs)
        Me.Text1.IsEnabled = True
    
    End Sub
    
    Private Sub StopBlinkingClick(sender As Object, e As RoutedEventArgs)
        Me.Text1.IsEnabled = False
    
    End Sub
    

     やっていることは以下のような感じ。

    1. storyborad を使って、点滅のためのシーケンスを作ります。上記では UIElement の Opacity を 0.5 秒間 0% (=消去)、1.5 秒間 100% (=表示) することで点滅を表現しています。
    2. その storyborad を textblock の style に適用。上記では IsEnabled の True で storyborad を開始、False で停止しています。
    3. 上記 2 つは resources 内に記述し、それぞれ名前をつけます。(x:Key="BlinkingStoryboard", x:Key="BlinkingStyle")
      また、storyboard を停止するとき、開始した storyborad のインスタンスを指定しなくてはならないので、storyborad のインスタンスに名前をつけています。(x:Name="BlinkingStoryboard1")
    4. その後 textblock にこの style を適用します。(Style="{StaticResource BlinkingStyle}")
    5. あとは、Style を適用した Textblock の IsEnabled を操作すると、点灯と点滅が切り替わります。

     いや、IsEnabled に紐づけるのどうなん?という人は DataTrigger あたりを検討してみてください。
     あるいは Style を適用せず直接コードビハインドで操作したい、という場合は以下のコードを適用します。

    Private _text1Blinking As Storyboard
    
    Private Sub StartBlinkingClick(sender As Object, e As RoutedEventArgs)
        ' Stackpanel.Resources を記述している StackPanel に x:Name="rootPanel" を追記してから以下を実行
        _text1Blinking = CType(Me.rootPanel.Resources("BlinkingStoryboard"), Storyboard)
        _text1Blinking.Begin(Me.Text1, True)
    
    End Sub
    
    Private Sub StopBlinkingClick(sender As Object, e As RoutedEventArgs)
        _text1Blinking.Stop(Me.Text1)
    
    End Sub
    
    

    [機材] RDX カートリッジを分解してみた

    2017年3月2日

     最近(でもないが)、RDX というバックアップ用のメディアがあるらしいというので調べてみた。というか中古で安く落ちていたので拾ってみました。

    • RDX ドライブ前面。
    • RDX ドライブ背面。内臓ドライブですが、USB のコネクタは普通に 3.0 の Type B。
    • カートリッジを挿入してみたところ。MO のように完全に飲み込まず、ちょっとだけ出っ張ります。
    • カートリッジを排出してみたところ。あまり飛び出ません。
    • RDX カートリッジ全景。
    • RDX カートリッジの背面側。
    • ライトプロテクトノッチ。
    • RDX カートリッジのコネクタ部。SATA コネクタそのもの・・・ (オス側)
    • ドライブの内部。
    • ドライブ内部の SATA コネクタ。(メス側)
    • カートリッジを分解。開けてみたところ。
    • HDD を取り外した後の、カートリッジ。
    • HDD (基盤側) カートリッジで見えていた SATA コネクタはベアドライブの SATA コネクタそのものでした。
    • HDD (ラベルシール側) 東芝製 MQ01ABD050。普通の 2.5インチの HDD。
    • 固定用のラバー (外側と内側)
    • Windows からは「リムーバブルディスク」として見えます。

     見ての通り、どうやらただの USB 接続のリムーバブルディスクで「SATA 2.5 インチ HDD に殻を付けた」という感じです。バックアップメディアっぽくテープデバイスとして認識するかというとそうではないようです。

     バックアップ用メディアなので基本企業向け。なので RDX カートリッジは基本高価ですね。上記写真の 500GB カートリッジを検索すると "52,600円(税抜)" とか出てきたりします。 (2017/3/2 現在)

     まだ使いこんでいないので何とも言えませんが、個人で使うのであれば裸族(リムーバブルケースと HDD ベアドライブの組み合わせ)のほうがよさそうです。

    移転しました。

    2016年8月19日

     
     諸般の事情 (Expressweb のサービス停止) により、こちらに移転しました。

     移転元サイト (http://ooltcloud.expressweb.jp/) は、Expressweb のサービス停止と同時に消滅、リンク切れする予定です。(2016 年 9 月末までは存在する予定です)

    [時事] VHS デッキの生産が終了

    2016年7月15日

     VHS デッキの国内製造が終了との由…

    船井電機、VHSデッキ撤退へ…国内で唯一生産
    船井電機は、VHS方式の家庭用ビデオデッキの生産を7月末で終了する。
    http://www.yomiuri.co.jp/economy/20160714-OYT1T50096.html

     過去のメディアがどんどん読めなくなっていくというのは寂しいね。

    [WPF] WPFプロジェクトなのに、Silverlight SDK がないと怒られることがある

    2016年6月14日

     ある日、マシン A で作成した Visual Studio 2012 の WPF のプロジェクトを、別のマシン B で開き、WPF デザイナを表示しようとしたら、「Microsoft Silverlight SDK 5.0 は見つかりませんでした」なるエラーが表示され、画面をみることができないという自体に出くわした・・・(汗

     マシン A では問題なく画面は表示されるのに・・・。もちろん マシン A, B ともに Visual Studio のバージョンは同一です。

     「デザイナーを再度読み込」んでみてもダメ。ただ、この状態でもビルドは通る。純粋にデザイナーの表示がダメなだけ。
     エラーの詳細をみても確かに Silverlight に関わるエラーであることには間違いない。でもこのプロジェクト、Silverlight ではなく WPF なんですけど・・・

    System.NotSupportedException
    Microsoft Silverlight SDK 5.0 は見つかりませんでした。正しいバージョンの Microsoft Silverlight SDK がインストールされていることを確認してください。
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.WaitForCompletion(NestedCallContext nestedCallContext, BlockingCall call, WaitHandle timeoutSignal)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.MarshalOutSynchronous(Action action, Int32 targetApartmentId, WaitHandle aborted, WaitHandle timeoutSignal)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.MarshalOut(Action action, Int32 targetApartmentId, WaitHandle aborted, CallSynchronizationMode syncMode, WaitHandle timeoutSignal)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalOut[TValue](RemoteHandle`1 targetObject, Action action, CallSynchronizationMode syncMode)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalOut[TResult,TValue](RemoteHandle`1 targetObject, Func`2 func, CallSynchronizationMode syncMode)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.LocalDesignerService.CreateDesignerImpl(IHostSourceItem item, IHostTextEditor editor, RemoteCancellationToken remoteCancelToken)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.LocalDesignerService.<>c__DisplayClass12.b__11(RemoteCancellationToken remoteToken)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.LocalDesignerService.CallWithCancellation[TResult](CancellationToken cancelToken, Func`2 func)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.LocalDesignerService.Microsoft.Expression.DesignHost.IDesignerService.CreateDesigner(IHostSourceItem item, IHostTextEditor editor, CancellationToken cancelToken)
       場所 Microsoft.Expression.DesignHost.IsolatedDesignerService.IsolatedDesignerView.CreateDesignerViewInfo(CancellationToken cancelToken)
    
    System.NotSupportedException
    Microsoft Silverlight SDK 5.0 は見つかりませんでした。正しいバージョンの Microsoft Silverlight SDK がインストールされていることを確認してください。
    
    Server stack trace: 
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.WaitForCompletion(NestedCallContext nestedCallContext, BlockingCall call, WaitHandle timeoutSignal)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.MarshalIn(Action action, Int32 targetApartmentId)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalIn(IRemoteObject targetObject, Action action)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalIn[TResult](IRemoteObject targetObject, Func`1 func)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteDesignerService.Microsoft.Expression.DesignHost.Isolation.Remoting.IRemoteDesignerService.CreateDesigner(IRemoteHostSourceItem remoteItem, IRemoteHostTextEditor remoteEditor, IRemoteCancellationToken remoteToken)
       場所 System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs)
       場所 System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg)
    
    Exception rethrown at [0]: 
       場所 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       場所 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.IRemoteDesignerService.CreateDesigner(IRemoteHostSourceItem remoteItem, IRemoteHostTextEditor remoteEditor, IRemoteCancellationToken cancelToken)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.LocalDesignerService.<>c__DisplayClass8.b__6(IRemoteDesignerService d)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.<>c__DisplayClass4`2.b__3()
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.Call.InvokeWorker()
    
    System.NotSupportedException
    Microsoft Silverlight SDK 5.0 は見つかりませんでした。正しいバージョンの Microsoft Silverlight SDK がインストールされていることを確認してください。
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.WaitForCompletion(NestedCallContext nestedCallContext, BlockingCall call, WaitHandle timeoutSignal)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.MarshalOutSynchronous(Action action, Int32 targetApartmentId, WaitHandle aborted, WaitHandle timeoutSignal)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.MarshalOut(Action action, Int32 targetApartmentId, WaitHandle aborted, CallSynchronizationMode syncMode, WaitHandle timeoutSignal)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalOut[TValue](RemoteHandle`1 targetObject, Action action, CallSynchronizationMode syncMode)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalOut[TResult,TValue](RemoteHandle`1 targetObject, Func`1 func, CallSynchronizationMode syncMode)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteWrapper`1.Invoke[T](Func`2 action)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteHostPlatformService.Microsoft.Expression.DesignHost.IHostPlatformService.GetProperty(String frameworkSpec, String propertyName)
       場所 Microsoft.Expression.DesignSurface.PlatformContext.GetProperty(String frameworkSpec, String propertyName)
       場所 Microsoft.Expression.Platform.PlatformService.GetProperty(String frameworkSpec, String propertyName)
       場所 Microsoft.Expression.DesignSurface.Assemblies.SilverlightAssemblyResolver.GetSilverlightPlatformPaths(String assemblyName)
       場所 Microsoft.Expression.DesignSurface.Assemblies.SilverlightAssemblyResolver.LoadSilverlightPlatformAssembly(String assemblyName)
       場所 Microsoft.Expression.DesignSurface.Assemblies.SilverlightAssemblyResolver.ResolveRuntimeAssembly(AssemblyName assemblyName)
       場所 Microsoft.Expression.Platform.PlatformService.ResolvePlatformAssembly(AssemblyName assemblyName)
       場所 Microsoft.Expression.DesignSurface.DesignerContext.PlatformAssemblyResolver.ResolveAssembly(AssemblyName assemblyName)
       場所 Microsoft.Expression.DesignSurface.Assemblies.AssemblyService.ResolveAssembly(AssemblyName assemblyName, IEnumerable`1 assemblyResolverTable)
       場所 Microsoft.Expression.DesignSurface.Assemblies.AssemblyService.ResolveAssembly(String assemblyPath, String assemblyFullName, String projectPath, IHostShadowCopyTask hostShadowCopyTask)
       場所 Microsoft.Expression.DesignSurface.Assemblies.ProjectAssemblyResolver.GetAssemblyInformation(String path, String assemblyFullName)
       場所 Microsoft.Expression.DesignSurface.Assemblies.ProjectAssemblyResolver.GetAssemblyInformation(IHostReferenceItem reference)
       場所 Microsoft.Expression.DesignSurface.Assemblies.ProjectAssemblyResolver.UpdateAssemblyReferences(IEnumerable`1 referencesToUpdate)
       場所 Microsoft.Expression.DesignSurface.Assemblies.ProjectAssemblyResolver..ctor(IHostProject project, DesignerContext designerContext)
       場所 Microsoft.Expression.DesignSurface.Assemblies.ProjectAssemblyService.<.ctor>b__0(IHostProject project)
       場所 Microsoft.Expression.Utility.Collections.SuspendableKeyedCollection`2.CreateItems(TKey key)
       場所 System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
       場所 Microsoft.Expression.Utility.Collections.SuspendableKeyedCollection`2.GetItem(TKey key)
       場所 Microsoft.Expression.DesignSurface.Assemblies.ProjectAssemblyService.GetProjectAssemblyResolver(IHostProject project)
       場所 Microsoft.Expression.DesignSurface.Project.ProjectContextBase.Initialize()
       場所 Microsoft.Expression.DesignSurface.Project.XamlProjectContext.Initialize()
       場所 Microsoft.Expression.DesignSurface.Project.ProjectContextManager.GetProjectContext(IHostProject project, IPlatform platform, Boolean create)
       場所 Microsoft.Expression.DesignSurface.Project.ProjectContextManager.d__d.MoveNext()
       場所 Microsoft.Expression.DesignSurface.Project.ProjectContextBase.XamlProjectMetadata.GetAssemblyForTypeResolve(String assemblyName)
       場所 Microsoft.Expression.DesignModel.Metadata.TypeResolver.GetType(ClrNamespaceIdentifier clrNamespaceIdentifier, String typeName)
       場所 Microsoft.Expression.DesignModel.Metadata.TypeResolver.ResolveType(ITypeId typeId)
       場所 Microsoft.Expression.DesignModel.Metadata.ProjectNeutralTypesAttributeTable.ResolveType(ITypeResolver typeResolver, ITypeId typeId)
       場所 Microsoft.Expression.DesignModel.Metadata.ProjectNeutralTypesAttributeTable.RegisterDataGridAttributeTable(ITypeResolver typeResolver)
       場所 Microsoft.Expression.WpfPlatform.WpfPlatform.RefreshProjectSpecificMetadata(ITypeResolver typeResolver, ITypeMetadataFactory typeMetadataFactory)
       場所 Microsoft.Expression.DesignSurface.Project.ProjectContextBase.Initialize()
       場所 Microsoft.Expression.DesignSurface.Project.XamlProjectContext.Initialize()
       場所 Microsoft.Expression.DesignSurface.Project.ProjectContextManager.GetProjectContext(IHostProject project, IPlatform platform, Boolean create)
       場所 Microsoft.Expression.DesignSurface.Project.ProjectContextManager.GetSourceItemContext(IHostSourceItem sourceItem)
       場所 Microsoft.Expression.DesignSurface.DesignerService.CreateDesigner(IHostSourceItem item, IHostTextEditor editor, CancellationToken cancelToken)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteDesignerService.<>c__DisplayClass10.<>c__DisplayClass12.b__f(CancellationToken cancelToken)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteDesignerService.CallWithCancellation[TResult](IRemoteCancellationToken remoteToken, Func`2 func)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteDesignerService.<>c__DisplayClass10.b__e()
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.<>c__DisplayClass16`1.b__15()
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.Call.InvokeWorker()
    
    System.NotSupportedException
    Microsoft Silverlight SDK 5.0 は見つかりませんでした。正しいバージョンの Microsoft Silverlight SDK がインストールされていることを確認してください。
    
    Server stack trace: 
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.WaitForCompletion(NestedCallContext nestedCallContext, BlockingCall call, WaitHandle timeoutSignal)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.MarshalIn(Action action, Int32 targetApartmentId)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalIn(IRemoteObject targetObject, Action action)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.MarshalIn[TResult](IRemoteObject targetObject, Func`1 func)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteWrapper`1.Invoke[T](Func`2 action)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteHostPlatformService.Microsoft.Expression.DesignHost.IHostPlatformService.GetProperty(String frameworkSpec, String propertyName)
       場所 System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Object[]& outArgs)
       場所 System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg)
    
    Exception rethrown at [0]: 
       場所 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       場所 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       場所 Microsoft.Expression.DesignHost.IHostPlatformService.GetProperty(String frameworkSpec, String propertyName)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteHostPlatformService.<>c__DisplayClass1.b__0(IHostPlatformService s)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteWrapper`1.<>c__DisplayClass4`1.b__3()
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.<>c__DisplayClass7`2.b__6()
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.Call.InvokeWorker()
    
    System.NotSupportedException
    Microsoft Silverlight SDK 5.0 は見つかりませんでした。正しいバージョンの Microsoft Silverlight SDK がインストールされていることを確認してください。
       場所 Microsoft.Expression.HostUtility.Platform.SilverlightDomainFactory.get_ReferenceAssembliesPath()
       場所 Microsoft.Expression.HostUtility.Platform.HostPlatformService.GetProperty(String frameworkSpec, String propertyName)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteHostPlatformService.<>c__DisplayClass1.b__0(IHostPlatformService s)
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.RemoteWrapper`1.<>c__DisplayClass4`1.b__3()
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.ThreadMarshaler.<>c__DisplayClass16`1.b__15()
       場所 Microsoft.Expression.DesignHost.Isolation.Remoting.STAMarshaler.Call.InvokeWorker()
    

     そういえばこのプロジェクト。PCL (Portable Class Library) を参照していたのでした。でも、Silverlight 5 ではなく、Silverloight 4 を要件に設定しています。

     SDK がないというので、とりあえずインストール状況を確認してみます。すると。
     マシン A のインストール状況。SDK がある。

     マシン B のインストール状況。SDK がない・・・(汗

     原因はこれですか。同じようにインストールしていたはずなのに。だれだ、マシン B から SDK Uninstall したのは・・・

     いやいや、そもそも PCL を採用したのは、このライブラリはプラットフォーム依存を排除したかったからで。なのに SDK がないとデザイナーが開けなくなるとかアリ? _| ̄|○
     そして、PCL を普通のライブラリ(DLL)に作り直して参照設定し直したら、無事デザイナーは例外を吐かなくなりました・・・(汗 
     ちなみに、設定を変えることで「PCL から普通の DLL にする」といったことは出来ませんでした。(できるのかもしれないけどやり方が分からなかった)

     教訓。ランタイムはポータブルでも開発環境はポータブルではない場合がある(追加のSDKをインストールする必要がある)点に注意(大汗

    [Excel] Excel 2010 で削除した描画が残る場合がある

    2016年6月13日

     Excel 2010 で貼り付けた画像を切り取りや削除を行った場合に、画像が残ってしまうことがあります。スクロールさせて再描画すると消えるので、オブジェクトそのものは消えているのにもかかわらず!(汗
     ぐぐると、改ページプレビューがよくないとか、ハードウェアグラフィックアクセラレーターの設定を無効にすればいいとか、KB をあてたらいいとか色々あるのですが。
     もしかしたら、そんなことではなく、印刷設定の変更で対応できるかもしれません。

     まず、削除しても画像が残るシートの「印刷範囲」は A:M のように、列指定になっているかを確認します。

     その「印刷範囲」を 行方向無限の設定をやめて、A1:M42 のように行方向の範囲を決めてやれば、削除しても画像が残らなくなります。(多分)

     特定のシートだけ削除した画像が残る、とか、新しく作った Workbook では再現しない、とかだと印刷範囲を疑ってみると解決するかもしれません。

     ・・・もっとも根本的な解決方法ではないので、対応する KB があるのであれば (= あるのかどうかは知らない) それを充てるのが良いです。ですが、勝手に KB を当てるわけにはいかない環境もあるので、そういう場合は上記を試してみるのもよいと思います。

    [線材] UTP ケーブルの RJ45 コネクタ成端処理

    2016年4月10日

     一年に一回程度しか成端工作をしない人向け…というか自分向けにメモ。
     なぜならば、一つ成端するのに1時間の時間と2つのコネクタを無駄にしたので(汗

    1. まず外被(シース)を剥きます。

       このとき 10cm くらい剥いてしまいます。勿体ない気がしますが、これは後の工程でヨリを戻しやすくするためと芯線の配列を揃えやすくするためです。最初から必要最小限の長さ(1.2cm 程度)にしてしまうと作業がまるで進まなくなったりします。
       また、外被を剥くのにカッターナイフを使用します。下手にストリッパーを使うと芯線側の絶縁被覆を傷つけることがあるので、数本の加工なら使わないほうが良いと思います。

      EtherCable01

    2. 次にヨリをほどきます。

       ヨリは根元側から先端側に向けてほどくと、根元側のヨリがよりほどけるので、後で作業がしやすいです。

      EtherCable02

    3. T568A または T568B のいずれかの並びに揃えます。

       このときの注意点としては、外被から 5mm 程度先の根元で揃っているようにします。
       先端は後で切り落としてしまうので、先端側で揃えても意味がないことに注意します。
       (下の写真は T568B 配列)

      EtherCable03

      • T568A の配列
        T568A
      • T568B の配列
        T568B
    4. 芯線を外被の先端から 1.2cm 程度のところで切り落とします。
      EtherCable04
    5. コネクタに挿します。

       このとき、並びが正しいかを確認するとともに、単色とストライプが交互になっているかを確認します。
       特に片側の端が単色ならもう片側の端はストライプであることを確認します。単色同士だったりストライプ同士だったりすると間違っているので直します。

      EtherCable05

    6. 配列が正しければ押し込みます。

       押し込んでコネクタの先端まで芯線が差し込まれていることを確認します。
       (下の写真は数本差し込み不足があります)

      EtherCable06

    7. 横からみても奥まで差し込まれているかを確認します。
       (下の写真は差し込み不足です)
      EtherCable07
    8. 奥まで差し込んだら圧着工具で圧着します。

       芯線を押し込む力を弱めると、上の写真のように芯線の差し込みが緩んでしまうので、芯線は押し付けたまま圧着します。

    9. 圧着が終了したらケーブルテスターで結線をチェックして、問題なければ終了です。

    [VB.NET] 浮動小数点で整数型と可換な最大の整数

    2016年2月22日

     浮動小数点型と整数型で、互いに誤差なく交換できる最大の整数がいくらなのかを確認。

     結論は以下。
      ・倍精度浮動小数点の場合は ±9007199254740992 (2^53)
      ・倍精度浮動小数点の場合は ±16777216 (2^24)

     これは IEEE754 の仮数部の桁数から導くことができます。

     ただ、.net 系で浮動小数点数を書式整形して表示するとおかしなことになる場合があります。

    Dim a As Double = 2 ^ 53
    Dim b As Double = a - 1
    Dim c As Long = b
    
    Console.WriteLine("{0:################}", b)
    Console.WriteLine("{0:################}", c)
    

     結果

    9007199254740990
    9007199254740991
    続行するには何かキーを押してください . . .
    

     b を c に代入すると正しく 9007199254740991 と表示されるのに、b を直接表示すると 9007199254740990 になってしまいます。

     上記は倍精度実数の場合ですが、単精度実数の場合でも同様の現象が起こります。

    Dim a As Single = 2 ^ 24
    Dim b As Single = a - 1
    Dim c As Long = b
    
    Console.WriteLine("{0:########}", b)
    Console.WriteLine("{0:########}", c)
    
    16777220
    16777215
    続行するには何かキーを押してください . . .
    

     10 進数換算で、倍精度の場合 15.6 桁、単精度で 6.9 桁が有効桁数です。従って、10 進数でその桁の数値を全桁を正確に表すことができない桁(倍精度の場合 16 桁目, 単精度の場合 7 桁目) に至る場合は、なんらかの指数計算が行われてしまう、ということのようです。(多分)

     まあ正しく表示しろ、って言われると 0.1 は 0.1000000000000000055511151228 になっちゃいますしね。ここは精度範囲外ってことで諦めですね。

     したがって、浮動小数点で整数を表現する場合、倍精度の場合 ±9007199254740992 (2^53), 単精度の場合は ±16777216 (2^24)。しかし、10進数表示を考慮に入れるなら、倍精度は 15 桁、単精度は 6 桁以内ということです。

     余談ですが javascript だと Number.MAX_SAFE_INTEGER で +9007199254740992 が得られるようです。(.net 系でもこのプロパティ欲しい)

    [VB.NET] システムクロックの精度

    2016年2月22日

     たとえば、以下のようなプログラムを実行します。

    Sub Main()
    
        Dim sw = New Stopwatch
        sw.Start()
        For i = 1 To 1000
            ' 1ms 待ち
            Threading.Thread.Sleep(1)
        Next
        sw.Stop()
    
        Console.WriteLine("経過 {0} ms", sw.Elapsed.TotalMilliseconds)
    
    End Sub
    

     1ms 待ちを 1000 回繰り返しているだけなので、期待値は 1秒(1000ms) ですがそうはなりません。
     実際に実行すると、以下のように 15 秒前後かかります。

    C> ConsoleApplication1
    経過 15076.4839 ms
    

     これは、システムクロックの精度がデフォルトでは 15.625 ms であるためです。
     つまり、1ms 止めているつもりが、システムクロックの精度が 15.625 ms であるため、15ms 前後止まっているということです。

     現在のシステムクロックの精度は Sysinternal ツールの Clockres を使うことで確認できます
     https://technet.microsoft.com/ja-jp/sysinternals/bb897568.aspx

    C>clockres
    
    ClockRes v2.0 - View the system clock resolution
    Copyright (C) 2009 Mark Russinovich
    SysInternals - www.sysinternals.com
    
    Maximum timer interval: 15.625 ms
    Minimum timer interval: 0.500 ms
    Current timer interval: 15.625 ms
    

     ということで、このシステムクロックの精度を15.625 ms から 1ms に変えてやれば、冒頭のプログラムは期待通りに動いてくれそうです。

     ということで、以下のプログラムによって、システムクロックの精度を 1ms に変えます。

    Private Declare Sub NtSetTimerResolution Lib "ntdll.dll" Alias "NtSetTimerResolution" _
    (
        ByVal desiredResolution As UInt32,
        ByVal setResolution As Boolean,
        ByRef CurrentResolution As UInt32
    )
    
    
    Sub Main()
    
        ' 1ms : 100ns (NtSetTimerResolution の設定の分解能が 100ns であるため)
        Const RATIO = 10 * 1000
    
        ' 変更値 (1ms) 
        Dim modifyMilliseconds As Double = 1
    
        ' 変更
        Dim currentMilliseconds = 0
        NtSetTimerResolution(CInt(modifyMilliseconds * RATIO), True, currentMilliseconds)
    
        ' 結果
        Console.WriteLine("Current timer interval: {0}ms", currentMilliseconds / RATIO)
        Console.Read()
    
    End Sub
    

     プログラムが終了してしまうとデフォルト値に戻ってしまうので、何かキーを入力しないと、プログラムの終了をしないようにしています。

     上記のプログラムを実行中に、冒頭のプログラムを実行してみます。
     その結果は以下。

    C> clockres
    
    ClockRes v2.0 - View the system clock resolution
    Copyright (C) 2009 Mark Russinovich
    SysInternals - www.sysinternals.com
    
    Maximum timer interval: 15.625 ms
    Minimum timer interval: 0.500 ms
    Current timer interval: 1.000 ms
    
    C> ConsoleApplication1
    経過 1718.0604 ms
    

     結果は約 1.7 秒と、まだ理論値からは外れているものの、デフォルトのシステムクロック精度のときの約 15 秒よりも大幅に理論値に近くなりました。

     このように、ms 単位の待ち時間やタイマー割り込みを使用する場合は、システムクロックの精度を気にする必要があります。
     加えて、システムクロックの精度の変更は、そのアプリケーションだけでなく Windows 全体に影響が及ぶので、軽率に変更するべきではない点にも注意が必要です。

    [Oracle] Oracle で浮動小数点数

    2016年1月7日

     Oracle で浮動小数点数を扱いたい場合、BINARY_FLOAT という型があるようです。

    create table test (
      a BINARY_FLOAT
    );
    

     無限大、非数も定義できるようです。

    insert into test values (1000000.0);
    insert into test values (0.0000001);
    insert into test values (BINARY_FLOAT_INFINITY);
    insert into test values (-BINARY_FLOAT_INFINITY);
    insert into test values (BINARY_FLOAT_NAN);
    

     select の結果は以下。

    SQL> select * from test;
    
             A
    ----------
      1.0E+006
      1.0E-007
           Inf
          -Inf
           Nan
    

    [VHDL] Std_Logic についての考察(1)

    2015年12月30日

     たとえばこんなコードを書いたとします。

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    
    entity hoge is
        Port (
            A : in   STD_LOGIC;
            B : in   STD_LOGIC;
            F : out  STD_LOGIC
        );
    end hoge;
    
    architecture Behavioral of hoge is
    begin
        F <= A;
        F <= B;
    end Behavioral;
    

     すると、こんなエラーが…(汗

    ERROR:Xst:528 - Multi-source in Unit  on signal ; this signal is connected to multiple drivers.
    

     複数のドライバーはだめですよ(1 つの出力 F に 2 つの入力 A,B は繋げられません)と言われているわけです。しかし VHDL はマルチドライバー(=複数の信号入力)を許容しているはずですし、std_logic 型というのは複数の信号が競合した時のための解決関数も用意されています。なのに、なぜこんなエラーが…

     結論からいえば、ターゲットである spartan 3e がマルチドライバーに対応していないからでした(汗

     それでは、論理的には OK なのか?ということで、テストベンチなコードでシミュレートしてみました。以下はその手順。

    1. 新しいプロジェクトを作成します。
    2. ここは適当に。ターゲットは Spartan3E で問題ないです。Language は VHDL で。
    3. プロジェクトのディレクトリとか OK だったら "Finish" で。
    4. 右クリックで "New-Source" を選択。
    5. 右ペインの "VHDL Test Bench" を選択して、ソースコードの FileName を入力して "Next >"。
    6. "Next >"。
    7. "Finish"。
    8. そうすると、テストペンチのテンプレートコードが現れます。
    9. テンプレートコードは全て削除して、以下の検証用コードに差し替えます。

      library IEEE;
      use IEEE.STD_LOGIC_1164.ALL;
      
      entity testbench is
      end testbench;
      
      architecture behavior of testbench is
      
          signal A: std_logic := 'Z';
          signal B: std_logic := 'Z';
          signal F: std_logic := 'Z';
      
      begin
          F <= A;
          F <= B;
      
          process
          begin
              wait for 1 ps ; A <= 'L';
              wait for 1 ps ; B <= 'H';
              wait for 1 ps ; A <= '0';
              wait for 1 ps ; B <= '1';
         end process;
      end;
      

       検証様の記述についての詳細は割愛しますが、signal 文のところが初期値、architecture の begin 直下の F への代入が回路本体、その下の process の中身がテスト用に与える信号の記述です。

    10. 保存します。(保存すると右ペインに保存したファイルが表示)
    11. テストベンチのソースコードを選択し、その下ペインの "ISim Simulator" の "Simulate Behavioral Model" を選択/右リリックし、"Run"。
    12. "Yes"。
    13. 実行中。
    14. 結果が表示されます。
    15. 今回の検証用コードでは、たとえば 3ps の時点で a に 0(=強い0), b に H(=弱い1)が与えられていますが、出力は 0(=強い0)と期待通りになっています。

    [PS1] Powershell で DES の暗号化と復号化を行う

    2015年12月30日

     Oracle で password という文字列を、秘密鍵 'key12345' で DES で暗号化してみます。

    SQL> select
      2      rawtohex(
      3          dbms_obfuscation_toolkit.desencrypt(
      4              input_string => 'password',
      5              key_string   => 'key12345'
      6          )
      7      ) as Encrypted
      8  from dual;
    
    ENCRYPTED
    -----------------------------------------------------
    3437EBBCAD1BB618
    

     dbms_obfuscation_toolkit.desencrypt で暗号化するわけですが、戻ってくるものは文字列ではないので、rawtohex で16進数表示化をしています。

     この結果を Powershell で正しいかどうかを検証します。

     暗号化を行うコマンドレットは標準では用意されていないようなので、まずは以下のような関数を作成します。
     要するに .net のクラスライブラリの力を借ります。

    function Encrypt($inputString, $keyString) {
    
        # 暗号化プロバイダの生成とモードの設定
        $des = New-Object System.Security.Cryptography.DESCryptoServiceProvider
        $des.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
    
        # 秘密鍵と初期化ベクタ(IV)の設定
        $des.Key = [System.Text.Encoding]::ASCII.GetBytes($keyString)
        $des.IV  = [Byte[]](0,0,0,0,0,0,0,0)
    
        # 暗号化対象の設定
        $inputBytes = [System.Text.Encoding]::ASCII.GetBytes($inputString)
        $params = ($inputBytes, 0, $inputBytes.Length)
    
        # 暗号化
        $encrypter = $des.CreateEncryptor()
        $encBytes = [System.Security.Cryptography.ICryptoTransform].GetMethod("TransformFinalBlock").Invoke($encrypter , $params)
        $encrypter.Dispose()
    
        #リターン
        Write-Output $encBytes
    }
    

    # パラメータ
    $inputString = "password"
    $keyString   = "key12345"    # 8byte以上が必要
    
    # 暗号化
    $encBytes = Encrypt $inputString $keyString
    [System.BitConverter]::ToString($encBytes)
    

     実行結果

    34-37-EB-BC-AD-1B-B6-18
    

     ついでに、この暗号が復号できるかを確認してみます。

    function Decrypt($inputBytes, $keyString) { #(★)
    
        # 暗号化プロバイダの生成とモードの設定
        $des = New-Object System.Security.Cryptography.DESCryptoServiceProvider
        $des.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
    
        # 秘密鍵と初期化ベクタ(IV)の設定
        $des.Key = [System.Text.Encoding]::ASCII.GetBytes($keyString)
        $des.IV  = [Byte[]](0,0,0,0,0,0,0,0)
    
        # 暗号化対象の設定
        $params = ($inputBytes, 0, $inputBytes.Length)
    
        # 復号化(★)
        $decrypter = $des.CreateDecryptor()
        $decBytes = [System.Security.Cryptography.ICryptoTransform].GetMethod("TransformFinalBlock").Invoke($decrypter , $params)
        $decrypter.Dispose()
    
        #リターン(★)
        $decString = [System.Text.Encoding]::ASCII.GetString($decBytes)
        Write-Output $decString
    }
    
    # パラメータ
    $inputBytes = [Byte[]](0x34, 0x37, 0xEB, 0xBC, 0xAD, 0x1B, 0xB6, 0x18)
    $keyString  = "key12345"    # 8byte以上が必要
    
    # 復号化
    Decrypt $inputBytes $keyString
    

     結果

    password
    

    [与太] 広島カープ、来年は優勝して欲しいのである

    2015年12月12日

     なんか、カープとメガフォンと私 Advent Calendar 2015 に召喚されてしまったので、書いてみる。

     広島って優勝しないですよね。しかも今年など、最後の最後で負けて B クラス落ちとは何事!という感じ(汗

     そういえば、広島ってここぞという勝負にめっぽう弱い印象。個人的に記憶に残っている「ここぞというところで負ける」といえば、新市民球場初戦敗退、旧市民球場最終戦敗退、1986年の日本シリーズ1分け3連勝からの4連敗、が思い起こされる(汗

     ただやっぱりそれより強烈な印象があるのは、1979年 V2 のときか、その翌年の V3 のときに、マジックが30くらいで点灯してから、そのままマジックが減っていって優勝したときのこと。当時、紙屋町そごうの垂れ幕でマジックのカウントダウンをやっていて。紙屋町のバス停で帰りのバスを待ちながら、その数字が減っていくのを眺めていたんですよね。是非あれをもう一度みたい!

     そういうわけなので、来年こそ、なんとか優勝してほしいのです。

    [与太] 今年もふつうに 12月3日 を迎えることができました。

    2015年12月3日

     この記事は、ふつうの広島 Advent Calendar 2015 3日目の記事です。

     今年も 12月3日 を迎えることができました。昨日は 12月2日 でしたから当然です? そう考えるのがふつーですよね。
     ところが、12月2日 の翌日が 12月3日 でなかった年がむかしあったのです。

     それは 明治5年。この年の12月2日の翌日は 明治6年1月1日 となりました。なぜかというと、それまでの旧暦(太陰太陽暦)から現在の太陽暦(グレゴリオ暦) に改暦したからです。

     つまり・・・
     12月の工数は2日間だけってことですね。一気に一か月納期が短縮したようなものです。
     さらにこの改暦の布告は、改暦の約一か月前の 11月9日 です。年明けリリース!なんてスケジュールだったりすると、残り2ヵ月あったはずが一瞬にして残り1ヵ月です。プロマネもびっくりです。でも決まったことをとやかく言っても仕方ありません。やるしかないです。プロマネはプログラマー叱咤 激励 し、プログラマーは昼夜の別なく作業して、納期に間に合わせなくてはなりません。レッツデスマーチですね!

     ふつーに 12月2日 の翌日に 12月3日 が来るって大切!
     今年もちゃんと 12月3日が来てよかった。

     ところでその時の給料ですが。当時は月給制でした。なので、2日でも1ヵ月ですから1ヵ月分の給料が!とおもいきや、2日しかないんだから12月の給料はナシね、という話になます。加えて、翌明治6年は旧暦のままであれば閏月のある年なので13ヵ月分の給料が貰える年だったのですが、太陽暦に変わったことでそれもなくなり・・・ もちろん月あたりの日数も約1日増えたわけでして(汗
     しかし、まあ改暦自体「給料を払いたくない」というのが動機らしいという話もあり、昔も今もあまり変わらず、ふつーに ブラック 厳しかったんですねえ、ということで。(残業代なんて制度もなかったでしょうしね)

     参考

    明治5年
    https://ja.wikipedia.org/wiki/明治5年
    月給制
    https://kotobank.jp/word/月給制

    [PS1] バッチファイルで設定した環境変数を Powershell 側で取得する

    2015年11月4日

     たとえばこんなバッチファイル (a.bat) があるとして。

    SET AAA=ABC123
    echo 設定しました。
    

     Powershell でこのバッチファイルを呼び出し、このバッチファイルで設定されている環境変数の値を Powershell 側で取得したい場合、以下のように書くことで取得できます。

    PS> $a = cmd /c "@echo off & call a.bat > nul & call echo %AAA%"
    PS> $a
    ABC123
    

     ポイントは、環境変数を Powershell で取得するに際し標準入出力を使っていることと、環境変数の値を標準出力に出力するに際して、call echo ~、と call をつかっているところでしょうか。

     この方法では別の問題が発生する場合もありますが、まあ一つの方法としてメモ。

    [PS1] バッチファイルでファイル名用の日付時刻を得る方法

    2015年11月2日

     もはや Windows XP や Windows Server 2003 は、この世に存在しないので以下で。

    for /f %a in ('powershell -command "Get-Date -Format ""yyyyMMddhhmmss"""') do set NOW=%a
    echo %NOW%
    

     ちなみにコマンドプロンプトのみで実現するには以下の模様です(汗
     (http://www.atmarkit.co.jp/ait/articles/0405/01/news002.html より抜粋)

    set DATE1=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2%
    set TIME2=%TIME: =0%
    set TIME3=%TIME2:~0,2%%TIME2:~3,2%%TIME2:~6,2%
    set NOW=%DATE1%%TIME3%
    echo %NOW%
    
     
    Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org