NAMELIST IOを利用したパラメータ入力の例文

諸般の事情により、共同研究者がFortranで開発してくれた計算プログラムを用いて研究をしています。計算パラメータがソースファイルに直書きだったので、Fortranの入出力をいろいろ調べていました。どうやら、Fortran2003でようやく標準化された入出力が準備されたようで、試したところ、少なくともgfortran 4.4とIntel Fortran Compiler for Linux 11.0では、namelistの内部read文以外は動くようでした。サンプルプログラムはそれなりに見つかったのですが、iostatusまでまじめに考慮したサンプルが見つからなかったので、とりあえず書いてみました。エラーが負だったり正だったりと、過去の遺産からかばらばらなのが、面倒です。
このサンプルでは、コマンドラインに複数のパラメータファイルを指定して、それぞれ読み込んで、値を出力するようにしています。

program main
  implicit none

  integer :: a,b,c

  integer :: clcount
  character(len=256) :: arg
  integer :: arglen,argerr
  integer :: ioerr
  integer :: i

  namelist /param/ a,b,c
  a=1
  b=2
  c=2

  clcount=command_argument_count()
  if (clcount == 0) then
    stop "*** no input file specified ***"
  endif
  do i=1,clcount
    call get_command_argument(i,arg,arglen,ioerr)
    if (ioerr > 0) then
      stop "*** input file name retrieval fails ***"
    elseif (ioerr == -1) then
      stop "*** input file name too long ***"
    endif
    open(unit=100,file=arg,iostat=ioerr)
    if (ioerr > 0) then
      stop "*** Cannot open input file ***"
    endif
    read(unit=100,nml=param,iostat=ioerr)
    if (ioerr < 0) then
      print*,"*** parameter reading FAILED. Use default value instead ***"
    endif
    close(unit=100)
    write(unit=6,nml=param)
  enddo
end program main

OpenSSHでVPNを構築する(tap編)

sshさえ通ればなんでもできる、とはよく言われることです。勤務先などで、ネットワークの制限が厳しくて、困っている方もいるのではないでしょうか。
最近のOpenSSHは、OSのtun/tapデバイスを利用して、VPNを構築することができます。tunを利用したVPNは解説がいくつかあったのですが、tapを利用してVPNを構築する方法(特に日本語)の解説がみつからなかったので、自分で行った設定をまとめてみます。前提条件は、以下の通りです。

  1. クライアント/サーバの双方がUNIX系である
    • 調べてないのでわかりませんが、tun/tapデバイスを作成する都合上、cygwinおよびcolinuxでは難しいのではないかと思っています。
  2. クライアント/サーバの双方にrootでアクセスできる
    • クライアント側はともかく、サーバ側に必要というのが厳しい条件です。OSと設定によっては非特権ユーザでもtun/tapデバイスを作成することができるようですが、詳しくは調べていません。
    • たとえrootアクセスが可能でも、tun/tapデバイスの作成が許可されない設定になっているVPSなどの仮想環境では不可能なこともあるようです。
  3. サーバ側でiptablesの設定が可能
    • サーバ側のネットワークへのアクセスが必要でないなら不要
    • ブリッジネットワークの設定が可能な人なら不要

さて、tapでは仮想ネットワークが構成されます。ここでは、次のようなネットワーク設定を用いることにします。また、クライアントからサーバを経由してのネットワーク接続ができるような設定にもします。

マシン インターフェース名 IPアドレス ネットマスク
server tap0 192.168.100.1 255.255.255.0
client tap0 192.168.100.2 255.255.255.0

さて、実際にVPN接続の設定に移ります。特権アカウントでの手動ネットワーク設定がかかわるところなので、Debian系列とRedHat系列で設定ファイルが異なります。以下はすべてDebian GNU/Linux (squeeze以降)を仮定します。

rootでのssh接続を可能にする

サーバ側の/etc/ssh/sshd_configの設定変更

最低セキュリティを次の設定まで緩めないといけません。tapだけでなく、tunなVPNも利用する予定があるならPermitTunnelをyesにしましょう。

PermitRootLogin forced-commands-only
PermitTunnel ethernet
サーバ側の/root/.ssh/authorized_keysへ公開鍵の登録

さすがにrootでのパスワード認証を常にOKするのはまずいので、クライアント側でVPN用の鍵を作ります。この鍵は、VPN構築専用で、自動化するためにパスフレーズを空にします。なお、パスフレーズが空でも、秘密鍵さえちゃんと安全に管理しておけば、パスワード認証よりも安全です。今回の事例では、tapvpnという名前にします。

client$ sudo -H ssh-keygen -t rsa -f tapvpn
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/tapvpn):
Enter file in which to save the key (/root/.ssh/tapvpn):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/tapvpn.
Your public key has been saved in /root/.ssh/tapvpn.pub.
The key fingerprint is:
dc:f1:54:b9:1c:21:9d:3a:70:59:b2:50:55:ad:3f:ec root@client
The key's randomart image is:
+--[ RSA 2048]----+
|          ..+===o|
|          ..o== .|
|          .ooo + |
|       . . +o +  |
|        S . .... |
|               o.|
|              . .|
|               E |
|                 |
+-----------------+

公開鍵の内容を一般ユーザ環境にコピーして、tapデバイスの設定と制限を行います。

client$ sudo cp /root/.ssh/tapvpn.pub .
...
client$ cat tapvpn.pub
no-X11-forwarding,no-agent-forwarding,no-pty,tunnel="0",command="/sbin/ifdown tap0; /sbin/ifup tap0" ssh-rsa AAAA....== root@client

次に、上記のように作ったVPN用の公開鍵(/root/.ssh/tapvpn.pub)をサーバにコピーします。一般ユーザならssh-copy-idコマンドで一発なのですが、サーバにrootで直にログインすることはできないですし、一般ユーザでserverにログインします。なお、server上のアカウントではsudoでなんでもできることを仮定します。

client$ scp tapvpn.pub server:
client$ ssh server
server$ echo tapvpn.pub | sudo tee -a /root/.ssh/authorized_keys

なお、authorized_keysにオプションを指定するのもよいでしょう。この段階で、クライアントからrootで認証が成功するかどうかを試しておきましょう。シェルのコンソールはでなくとも、認証が通っていれば成功です。Ctrl-Cで終了してください。

client$ sudo -H ssh -v -i /root/.ssh/tapvpn server

クライアント側での/root/.ssh/configの編集

tapの設定をコマンドラインで行うのは面倒なので、一発接続ができるように設定を行います。/root/.ssh/configを編集します。

client$ sudo cat /root/.ssh/config
...
Host server-vpn
  HostName server
  IdentityFile %d/.ssh/tapvpn
  Tunnel ethernet
  TunnelDevice 0

サーバ側でのネットワーク設定の編集

ここまでで、仮想ネットワークデバイスの準備はできているのですが、IPなどの設定が一切していません。今回は、クライアントからはサーバ側のネットワークにサーバマシンによるiptablesによるNATを使って接続する設定にします。変更するのは/etc/network/interfacesです。

server$ cat /etc/network/interfaces
...
auto eth0
iface eth0 inet static
        address XXX.XXX.XXX.XXX
        netmask YYY.YYY.YYY.YYY
        pre-up echo 1 > /proc/sys/net/ipv4/ip_forward
        pre-up iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -j MASQUERADE
...
iface tap0 inet static
        address 192.168.100.1
        netmask 255.255.255.0

クライアント側のネットワーク設定

今回は、ドラスティックに、クライアントマシンからの通信は、すべてサーバ側を経由するようにします。なお、autosshパッケージを利用します。また、やはり/etc/network/interfacesを編集します。サーバの実IPアドレスがXXX.XXX.XXX.XXX、クライアントの(VPN接続前の)ゲートウェイのアドレスをZZZ.ZZZ.ZZZ.ZZZとします。

client$ cat /etc/network/interfaces
...
iface tap0 inet static
        pre-up autossh -f server-vpn
        pre-up sleep 5
        address 192.168.100.2
        netmask 255.255.255.0
        up route add XXX.XXX.XXX.XXX gw ZZZ.ZZZ.ZZZ.ZZZ eth0
        up route add default gw 192.168.100.1 tap0
        up route del default gw ZZZ.ZZZ.ZZZ.ZZZ eth0
        down route add default gw ZZZ.ZZZ.ZZZ.ZZZ eth0
        down route del default gw 192.168.100.1 tap0
        down route del XXX.XXX.XXX.XXX gw ZZZ.ZZZ.ZZZ.ZZZ eth0
        post-down killall autossh

接続のOn/Offの確認とDNSの設定

ここまでの設定で、VPN接続が可能になっているはずです。

client$ sudo ifup tap0
client$ sudo ifdown tap0

設定の確認は、ifconfigや、netstatを利用します。

client$ /sbin/ifconfig -a
client$ /bin/netstat -rn

(追記予定: 2010/09/04) 運用時にroot権限を用いない方法があるので、改定します。

zshの設定ファイル読み込み順番に関するメモ

login shell のとき
/etc/zshenv -> $ZDOTDIR/.zshenv -> /etc/zprofile -> $ZDOTDIR/.zprofile -> /etc/zshrc -> $ZDOTDIR/.zshrc -> /etc/zlogin -> $ZDOTDIR/.zlogin
login shell ではないが、対話的な shell のとき
/etc/zshenv -> $ZDOTDIR/.zshenv -> /etc/zshrc -> $ZDOTDIR/.zshrc
非対話的な shell のとき
/etc/zshenv -> $ZDOTDIR/.zshenv
  • $ZDOTDIR/.zshenv は非対話的に呼びだされたときも読み込まれるので、画面表示を入れてはいけない

Boost.Randomで適当な分布関数を用いる

Boost.Randomの利点は、generator(乱数生成器)とdistribution(分布関数)を分離できるところです。d:id:faith_and_brave:20090629:1246263401であげられている、「80%の確率で0, 20%の確率で1」といった事例は、まさにBernoulli Distributionそのものなので、次のように書いたほうが分布関数の意味がわかりやすいと思います。

#include <iostream>
#include <ctime>
#include <boost/random.hpp>

using namespace boost;

int main(int, char**)
{
    mt19937 gen(static_cast<unsigned long>(std::time(0)));
    bernoulli_distribution<> dst(0.2);// 0.2の確率でtrue
    variate_generator<mt19937, bernoulli_distribution<> > rand(gen, dst);

    for (int i = 0; i < 10; i++) {
        std::cout << rand() << std::endl;
    }
}

POP3サーバからIMAP4サーバにメールを移動する

現在、ネットワークアクセス制限がきついところに出入りしているのですが、外部からメールを送受信するのも大変なので、IMAP4が使えるGMailにメールをすべて移動することにしました。Pythonの勉強を兼ねて、Python クックブック 第2版を参考にしてPOP3サーバからIMAP4サーバへメールを移動させる自作してみました。エラー処理が微妙に足りない気がします。今後daemon化を考えるときには、もう少しエラー処理やクラス化を考えないとなぁと思います。

import getpass
import poplib
import imaplib
import email
from email.Utils import parsedate

POPHOST = "popserver"
POPUSER = "username1"
POPPASS = ""

IMAPHOST = "imapserver"
IMAPUSER = "username2"
IMAPPASS = ""

try:
    pop = poplib.POP3_SSL(POPHOST)
    pop.user(POPUSER)
    if not POPPASS or POPPASS=='=':
        POPPASS = getpass.getpass("Password for %s@%s (POP3):" % (POPUSER, POPHOST))
    pop.pass_(POPPASS)

    msgCount, _ = pop.stat()
    print "Logged in to POP3 server as %s@%s." % (POPUSER, POPHOST)
    print "Status: %d message(s)" % msgCount

    imap = imaplib.IMAP4_SSL(IMAPHOST)
    if not IMAPPASS or IMAPPASS=='=':
        IMAPPASS = getpass.getpass("Password for %s@%s (IMAP4):" % (IMAPUSER, IMAPHOST))
    imap.login(IMAPUSER, IMAPPASS)
    imap.select()
    print "Logged in to IMAP4 server as %s@%s." % (IMAPUSER, IMAPHOST)

    for msgNum in range(1, msgCount+1):
        _, message, _ = pop.retr(msgNum)
        msg = email.message_from_string('\n'.join(message))
        datetime = parsedate(msg.get('Date'))
        imap.append('INBOX', [], datetime, str(msg))
        pop.dele(msgNum)

    print "Closing POP3 session"
    pop.quit()

    print "Closing IMAP4 session"
    imap.close()
    imap.logout()

except poplib.error_proto, detail:
    print "POP3 Protocol Error:", detail
except imap.error, detail:
    print "IMAP4 Protocol Error:", detail

Python クックブック 第2版

Python クックブック 第2版

Sun X4100 ServerのシリアルコンソールをDebian GNU/Linuxから使う

研究室でSun X4100 Serverを運用しているのですが、管理の都合でDebian GNU/Linux (lenny)を導入しました。この機種には標準オプションとしてサービスプロセッサ(ILOM)が付属しており、リモートからの電源On/Off、グラフィカルコンソールの転送まで行うことができます。グラフィカルコンソールの転送は便利なのですが、sshdが起動していない状態の管理のみを行えばよい、という単純な状況では、オーバースペックすぎます。そこで、ILOMのもうひとつの機能である、シリアルコンソールを利用できるようにします。キャラクタベースなので、ネットワークの負荷も軽く、Java等の追加ソフトウェアも必要ありません。Solarisを導入した際には、シリアルポートを認識すると、そのポート上のコンソールが自動で有効になりましたが、Debian GNU/Linux (lenny)ではやってくれないため、いくつか手動での設定が必要になります。
なお、すばらしいことに、Sun X4100 ServerのILOMでは、このシリアルポートからBIOSの情報、SAS RAIDの設定すらできるようになっています。

ログインするための設定

まず、OS上からILOMのシリアルポートが認識されているかどうかを確かめます。次のようにポートがあることを確認できればOKです。

shell $ dmesg | grep ttyS
[    1.117705] serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[    1.117705] 00:06: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A

次に、/etc/inittabファイルを編集して、シリアルポートからログインできるようにします。Debian GNU/Linux (lenny)では、設定例はすでに記載されており、コメントアウトされているだけなので、簡単です。

#T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100

上のような設定例の行があるので、ttySの後の数字がdmesgの出力と同じものをみつけて、行頭の#をはずします。多くの場合ttyS0になっていると思います。

T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100

これで設定完了です。再起動は必要なく、次のコマンドを打ったら、すぐに使えるようになっているはずです。

shell # init q

ブートローダの設定

上までの設定が完了すると、BIOSはいじれますし、ログインすることもできるのですが、ブートローダがシリアルコンソールに出てきませんし、起動させるkernelの変更や、kernelに与えるオプションもいじることができません。起動時のログもシリアルコンソールに表示されません。トラブルシューティングができない片手落ちの状態です。ここでは、私がこれまで慣れているDebian GNU/Linux特有の方法を用いたGRUB Legacy(0.9x)の設定を行います。
まず、/boot/grub/menu.lst (またはインストール方法によっては/boot/boot/grub/menu.lst)ファイルを開いて5行目に、次の行を挿入します。

## serial console
serial --unit=0 --speed=9600 --word=8 --parity=no --stop=1
terminal --timeout=10 serial console

この2行で、通常の端末tty0とシリアルコンソールttyS0の両方からGRUBメニューにアクセスできるようになります。次に、起動時のカーネルログをシリアルコンソールにも表示する設定を行います。/boot/grub/menu.lstを再度開いて、次のkoptで始まる行を探します。

# kopt=root=/dev/sda1 ro

この行の最後に、次のように設定を付け加えます。このとき、行頭の#を削除してはいけません。consoleで始まる設定が2つともあるのが正しい設定です。Debianでは、この行を設定することで、カーネルのアップデートの際にも自動的にオプションを付与してくれるようになります。

# kopt=root=/dev/sda1 ro console=tty0 console=ttyS0,9600

あとは、この設定ファイルを反映させるだけです。インストールするべきMBRの位置は、/boot/grub/device.mapを見て決めます。今回X4100では、/dev/sdbになっていました。

shell # update-grub
shell # cat /boot/grub/device.map
(hd0)   /dev/sdb
shell # grub-install /dev/sdb

上のようにインストールすると、GRUB Legacyの設定が完了します。

セキュリティなどに関する補足

シリアルコンソールの設定は、検索するとたくさんでてきます。今回のこのメモでGRUBへのセキュリティに関する記述を無視しているのは、このシリアルコンソールはILOMに接続されており、ILOMに接続する際のパスワードでは守られているからです。本当のRS-232C接続ポートがあるという場合には、やはり、GRUBのパスワードなどの設定は避けて通れないものと思います。

Mac mini初期設定

研究室で新たなクライアントマシンとしてMac miniを購入しました。初MacOSX(Tiger)になります。
クライアント兼計算マシンとして利用する予定です。設定とソフトウェアについてまとめてみます。Beginning OS Xを参考にしました。

あとはmigemoがほしいですね。

Terminal.appの設定

Debianにログインした際にBack Spaceが効かなくなりました。まあよくあることです。ググってみると、Back Spaceキーで'^H'を送出させた上で

$ stty erase '^H'

で解決する、というのが多かったです。しかしながら、screenのセッションを切り替える中で'^H'と'^?'を切り替えるのは難しいです。そもそも、Terminal.app自体はBSで'^?'を出しています。どうやらTERM=xterm-colorがTerminal.appとはあっていないということのようなので、TERMをいじることで解決することにしました。

$ echo "export TERM=xterm" >> $HOME/.bash_profile