PerlとSendmailとBccのメーリングリスト配信スクリプト

昨日うっかりBccとCcを間違えてしまう大失態を演じてしまったため簡易ながら不特定多数にBccでメールを一斉配信してくれるスクリプトをPerlでサクッと書いてみました。
そしてどうせ書いたのならば久々に公開して有意義に使っていただこうと思い公開します。

コマンドラインベースで動けばいいかなということで機能自体には特にこだわりは無いのですが必要のある方がおりましたらご自由にコピーしてお使いください。
※ 当スクリプトを利用するにはPerlモジュールのインストールができるレベルの知識を必要とします。

実行用スクリプト
# vi infoEmail.pl

#!/usr/bin/perl
use utf8;
use strict;
use warnings;

binmode(STDOUT, ":utf8");

require "infoEmail.pm";

#
# メールマガジン配信スクリプト
#

my $mm   = infoEmail->new();
my $mime = $mm->mailer();

print $mime->as_string;
print '*' x 50 . "\n";
print "上記の内容で配信してもよろしいですか? (y/N)\n";
print '*' x 50 . "\n";
print "> ";
my $input = <STDIN>;
chomp($input);

if($input eq 'y' || $input eq 'yes'){
        $mm->sendmail($mime);
        print "メールを配信しました\n";
}else{
        print "メールの配信をキャンセルしました\n";
}

モジュール(プログラム本体)
# vi infoEmail.pm

#!/usr/bin/perl
package infoEmail;

use utf8;
use strict;
use warnings;

use Config::Simple;
use Mail::Krohn;
use Mail::Krohn::Sendmail;
use Email::MIME;
use Email::MIME::Creator;
use Encode;

sub new {
        my $class = shift;
        my $cfg = new Config::Simple('./infoEmail.conf') or die Config::Simple->error();
        my $self = {
                from         => $cfg->param('From'),
                to           => $cfg->param('To'),
                subject      => $cfg->param('Subject'),
                x_mailer     => $cfg->param('X-Mailer'),
                bccfilepath  => $cfg->param('BccFilePath'),
                bodyfilepath => $cfg->param('BodyFilePath')
        };
        return bless $self , $class;
}

sub mailer {
        my $self     = shift;
        my $bcc      = $self->read_config($self->{ bccfilepath });
        my $body     = $self->read_config($self->{ bodyfilepath });
        my $from     = $self->{ from };
        my $to       = $self->{ to };
        my $subject  = $self->{ subject };
        my $x_mailer = $self->{ x_mailer };
        my $mime = Email::MIME->create(
                header => [
                        From       => encode('MIME-Header-ISO_2022_JP' => decode('utf-8',$from)),
                        To         => encode('MIME-Header-ISO_2022_JP' => decode('utf-8',$to)),
                        Bcc        => encode('MIME-Header-ISO_2022_JP' => decode('utf-8',$bcc)),
                        Subject    => encode('MIME-Header-ISO_2022_JP' => decode('utf-8',$subject)),
                        'X-Mailer' => decode('utf-8',$x_mailer),
                ],
                attributes => {
                        content_type => 'text/plain',
                        charset      => 'ISO-2022-JP',
                        encoding     => '7bit',
                },
                body => encode('iso-2022-jp' => decode('utf-8',$body)),
        );
        return $mime;
}

sub sendmail {
        my $self = shift;
        my $mime = shift;
        my $mailer = Mail::Krohn->new();
        $mailer->send($mime);
        return 0;
}

sub read_config {
        my $self = shift;
        my $file_path = shift;

        open(my $fh, "<", $file_path) || die("Can not open file $file_path");
        my $file_contents;
        while( my $line = readline $fh ){
                $file_contents .= $line;
        }
        chomp($file_contents);
        return $file_contents;
}

1;

設定ファイル
# vi infoEmail.conf

############################################################
# メールマガジン設定ファイル
############################################################
# 送信元
From         = "ぼっちちゃん" <めーる@あどらあ>
# 送信先
To           = "ぼっちちゃん" <めーる@あどらあ>
# 件名
Subject      = ぼっちちゃんと愉快な仲間たちめーりんぐりすと
# メーラー名
X-Mailer     = My Best Friends Mailing List
############################################################
# 送り先リストの記述されたファイル
BccFilePath  = ./infoEmail_bcc.tmp
# 本文の記述されたファイル
BodyFilePath = ./infoEmail_body.tmp
############################################################

配信用メール本文
# vi infoEmail_body.tmp

お友達100人できるかな?

=======================================
"ぼっちちゃん" <めーる@あどらあ~>
ぼっちちゃんと愉快な仲間たちめーりんぐりすと
=======================================

配信対象のアドレスリスト
# vi infoEmail_bcc.tmp

おともだち1@あどらあ, めーる2@あどらあ, めーる3@あどらあ, めーる4@あどらあ, めーる5@あどらあ

久々にPerl触ったらだいぶ忘れてました(^_^;)

参考サイト
第20回 Email::Sender:メールを送信する:モダンPerlの世界へようこそ|gihyo.jp … 技術評論社

NagiosでMySQLが異常となる

Nagios上では”Return code of 127 is out of bounds – plugin may be missing”というエラーが出ている

試しに手動実行してみる
# /usr/local/nagios/libexec/check_mysql -H localhost -u root -p ***********************
/usr/local/nagios/libexec/check_mysql: error while loading shared libraries: libmysqlclient.so.16: cannot open shared object file: No such file or directory

確認してみると”libmysqlclient.so.16″がないことが分かる
# /sbin/ldconfig -v | grep mysql
/sbin/ldconfig: Path `/usr/lib64/mysql’ given more than once
/usr/lib/mysql:
libmysqlclient.so.18 -> libmysqlclient_r.so
/usr/lib64/mysql:
libmysqlclient.so.18 -> libmysqlclient_r.so
libmysqlclient_r.so.15 -> libmysqlclient_r.so.15.0.0
libmysqlclient.so.15 -> libmysqlclient.so.15.0.0

パッケージを追加しておく
# yum install mysqlclient16

再び手動実行してみる
# /usr/local/nagios/libexec/check_mysql -H localhost -u root -p ***********************
Uptime: 12635 Threads: 1 Questions: 25334 Slow queries: 0 Opens: 44 Flush tables: 1 Open tables: 37 Queries per second avg: 2.005

ESXi5.1を623860から914609へアップデートする

必要なファイルをダウンロードしてデータストアにアップロードしておく。
なお、パッチを当てると当てていたドライバが剥がれるためもう一度当てなおさなければならない。
ドライバに漏れがあると結構、作業工程的にも精神的にも大変なことになるため十分漏れがないように注意しておく。

パッチ
VMware Store and Account

ドライバ
Adaptec – Adaptec Driver: AACRAID Driver v1.2.1-29900 for VMware Download Detail
ESXi5.1のドライバを作成してみる(intel 82579LM/82574L編) << 環境さんぷる
※ ここではESXi5.1用のNICとRAIDカードのドライバをダウンロードしているがそれぞれの環境に合わせて揃える。

対象のESXi5.1のゲストマシンをシャットダウンし、メンテナンスモードに切り替える。
SSHを使いrootでログインし下記のコマンドを入力する。

アップデート・パッチのインストール

# esxcli software vib install -d /vmfs/volumes/datastore1/ESXi510-201212001.zip

※ 場合によっては非常に時間がかかる事があるが気長に待つ。

ドライバのインストール

# esxcli software acceptance set --level=CommunitySupported
# esxcli software vib install -v /vmfs/volumes/datastore1/net-e1000e-2.1.4.x86_64.vib
# esxcli software vib install -d /vmfs/volumes/datastore1/aacraid-esxi5.1-1.2.1.29900.zip –no-sig-check
# esxcli software vib install -v /vmfs/volumes/datastore1/vmware-esxi-drivers-scsi-aacraid-510.5.2.1.29900.-1.1.5.799733.x86_64.vib –no-sig-check

※ ここではdatastore1にアップロードしているがそれぞれの環境に合わせる。

終わったら再起動し様子を見る。
クライアントのアップデートについて聞いてくるのでアップデートする。
ビルド番号が上がっていてゲストマシンも正常に認識していたらメンテナンスモードを終了し再起動をかける。

ここまでで、アップデートは完了です。

ホットバックアップの再設定
ESXi5.1をアップデートして気づいたのですが/etc/rc.localを編集しようとしたところ”Operation not permitted”と言われた。そのため、”/etc/rc.local.d/local.sh”を書き換える方法で再設定する。

# vi /etc/rc.local.d/local.sh

最下部に追記する。

# ESXi5.1 HOT Back UP
/bin/kill $(cat /var/run/crond.pid)
/bin/echo "0 17 * * 1-5 /vmfs/volumes/datastore1/ghettoVCB/ghettoVCB.sh \
-f /vmfs/volumes/datastore1/ghettoVCB/backup_vmlist.txt \
-g /vmfs/volumes/datastore1/ghettoVCB/ghettoVCB.conf \
> /vmfs/volumes/datastore1/ghettoVCB/backup_log/ghettoVCB-backup-\$(date +%s).log" \
>> /var/spool/cron/crontabs/root
/bin/crond

※ 5.1から”/bin/busybox”コマンドが消えたようなので”/bin/crond”としておく。

しかしこのままではバージョンが合わないエラーが出たりwhoamiコマンドが無いなど正常にスクリプトが動作しないようなので書き換える。

# vi /vmfs/volumes/datastore1/ghettoVCB/ghettoVCB.sh

230行付近を編集

編集前

	ESX_VERSION=$(vmware -v | awk '{print $3}')
	if [[ "${ESX_VERSION}" == "5.0.0" ]]; then
		VER=5
        elif [[ "${ESX_VERSION}" == "4.0.0" ]] || [[ "${ESX_VERSION}" == "4.1.0" ]]; then
                VER=4
        else
                ESX_VERSION=$(vmware -v | awk '{print $4}')
                if [[ "${ESX_VERSION}" == "3.5.0" ]] || [[ "${ESX_VERSION}" == "3i" ]]; then
                        VER=3
                else
                        echo "You're not running ESX(i) 3.5, 4.x, 5.x!"
                        exit 1
                fi
        fi

編集後

	ESX_VERSION=$(vmware -v | awk '{print $3}')
	if [[ "${ESX_VERSION}" == "5.0.0" ]] || [[ "${ESX_VERSION}" == "5.1.0" ]]; then
		VER=5
        elif [[ "${ESX_VERSION}" == "4.0.0" ]] || [[ "${ESX_VERSION}" == "4.1.0" ]]; then
                VER=4
        else
                ESX_VERSION=$(vmware -v | awk '{print $4}')
                if [[ "${ESX_VERSION}" == "3.5.0" ]] || [[ "${ESX_VERSION}" == "3i" ]]; then
                        VER=3
                else
                        echo "You're not running ESX(i) 3.5, 4.x, 5.x!"
                        exit 1
                fi
        fi
# vi /vmfs/volumes/datastore1/ghettoVCB/ghettoVCB-restore.sh

86行付近を編集

編集前

	ESX_VERSION=$(vmware -v | awk '{print $3}')
	if [ "${ESX_VERSION}" == "5.0.0" ]; then
		VER=5
        elif [[ "${ESX_VERSION}" == "4.0.0" ]] || [[ "${ESX_VERSION}" == "4.1.0" ]]; then
                VER=4
        else
                ESX_VERSION=$(vmware -v | awk '{print $4}')
                if [[ "${ESX_VERSION}" == "3.5.0" ]] || [[ "${ESX_VERSION}" == "3i" ]]; then
                        VER=3
                else
			echo "You're not running ESX(i) 3.5, 4.x, 5.x!"
                        exit 1
                fi
        fi

編集後

	ESX_VERSION=$(vmware -v | awk '{print $3}')
	if [[ "${ESX_VERSION}" == "5.0.0" ]] || [[ "${ESX_VERSION}" == "5.1.0" ]]; then
		VER=5
        elif [[ "${ESX_VERSION}" == "4.0.0" ]] || [[ "${ESX_VERSION}" == "4.1.0" ]]; then
                VER=4
        else
                ESX_VERSION=$(vmware -v | awk '{print $4}')
                if [[ "${ESX_VERSION}" == "3.5.0" ]] || [[ "${ESX_VERSION}" == "3i" ]]; then
                        VER=3
                else
			echo "You're not running ESX(i) 3.5, 4.x, 5.x!"
                        exit 1
                fi
        fi

101行付近を編集

編集前

if [ ! $(whoami) == "root" ]; then
        logger "info" "This script needs to be executed by \"root\"!"
        echo "ERROR: This script needs to be executed by \"root\"!"
        exit 1
fi

編集後

if [ ! $(who | awk '{print $1}') == "root" ]; then
        logger "info" "This script needs to be executed by \"root\"!"
        echo "ERROR: This script needs to be executed by \"root\"!"
        exit 1
fi

動作チェックを行い、正常に動作していたら再設定完了です。

参考サイト
ESXi5.0マシンを2台作成 | SS-NET サポート
富士通 PRIMERGY TX100 S3 の ESXi5.1 838463 を 5.1 914609 (ESXi510-201212001.zip) にアップデートする。 << 環境さんぷる
VMWARE 5.1 Could not find a trusted signer
【ESXi 5.0】仮想マシンのオンラインバックアップ – OSSでLinuxサーバ構築
Modifying ghettoVCB to run on VMware ESXi 5.1

PerlでメールをBccで送信する Email::MIME + Mail::Krohn

メールをEmail::Sendを利用し送信しようとしたところ下記のように色々と怒られたため調べてみたところ
スクリプト起動時にReturn::Value::NO_CLUCKに値を入れれば良いということが分かったが実にスマートではないので代わりになるモジュールを探してみた

Return::Value is deprecated at /usr/lib/perl5/site_perl/5.8.8/Return/Value.pm line 13
        require Return/Value.pm called at /usr/lib/perl5/site_perl/5.8.8/Email/Send.pm line 11
        Email::Send::BEGIN() called at /usr/lib/perl5/site_perl/5.8.8/Return/Value.pm line 0
        eval {...} called at /usr/lib/perl5/site_perl/5.8.8/Return/Value.pm line 0
        require Email/Send.pm called at infoEmail.pl line 6
        main::BEGIN() called at /usr/lib/perl5/site_perl/5.8.8/Return/Value.pm line 0
        eval {...} called at /usr/lib/perl5/site_perl/5.8.8/Return/Value.pm line 0

必要なモジュールをインストールする
# cpan install parent
# cpan install Class::Accessor::Lite
# cpan install Mail::Krohn

#!/usr/bin/perl
use strict;
use warnings;

use utf8;
use Mail::Krohn;
use Mail::Krohn::Sendmail;
use Email::MIME;
use Email::MIME::Creator;
use Encode;

MAIN:{
    my $mime = Email::MIME->create(
        header => [
            From => 'そうしんもとめーる@あどれす',
            To   => 'そうしんもとめーる@あどれす',
            Bcc  => 'そうしんさきめーる@あどれす',
            Subject => Encode::encode( 'MIME-Header-ISO_2022_JP', 'テストメールです' ),
            'X-Mailer' => 'TestMailer',
        ],
        attributes => {
            content_type => 'text/plain',
            charset      => 'iso-2022-jp',
            encoding     => '7bit',
        },
        parts => [
                Encode::encode( 'iso-2022-jp', 'でばっぐおいしー!!' ),
        ],
    );

    my $mailer = Mail::Krohn->new();
    $mailer->send($mime);
}

参考サイト
Email::MIME::CreatorとEmail::Sendでメール送信
[Perl]Email::SendつかったらReturn::Valueにdeprecatedだと怒られた

CentOS5.7 Email::Send インストール

# cpan install Perl::OSType <- make testでエラーが起きる # yum --enablerepo=epel install perl-Perl-OSType # cpan install Module::Build # cpan install Module::Pluggable # cpan install Email::Send

CentOS5.7 Apache2 DOS攻撃対策

DOS攻撃の対策モジュールを変更したのをメモするのを忘れていたのでメモしておきます。

旧記事
Dosアタック対策 Apache2 Vine Linux

# wget http://ncu.dl.sourceforge.net/sourceforge/moddosdetector/mod_dosdetector-0.2.tar.gz
# tar xvzf mod_dosdetector-0.2.tar.gz
# cd mod_dosdetector-0.2
# make
# make install

# vi /etc/httpd/conf.d/dos_attack.conf

RewriteEngine On
RewriteCond %{ENV:SuspectHardDoS} =1
RewriteRule .*  - [R=503,L]
ErrorDocument 503 "Server is busy."

AddType image/vnd.microsoft.icon .ico

DoSDetection On
DoSPeriod 60
DoSThreshold 70
DoSHardThreshold 90
DoSBanPeriod 7200
DoSTableSize 500
DoSIgnoreContentType ^(image/|application/|text/javascript|text/css)

ESXi5 コマンドから仮想マシンの電源を管理する

対象の仮想マシンのIDを調べる
# vim-cmd vmsvc/getallvms

IDに各当する仮想マシンの状態を確認する(“ID”には数字が入る)
# vim-cmd vmsvc/power.getstate ID

IDに各当する仮想マシンの電源を入れる(“ID”には数字が入る)
# vim-cmd vmsvc/power.on ID

IDに各当する仮想マシンの電源を切る(“ID”には数字が入る)
# vim-cmd vmsvc/power.off ID

IDに各当する仮想マシンを再起動する(“ID”には数字が入る)
# vim-cmd vmsvc/power.reboot ID

ArduinoでRELET(FeliCa電子マネー残高照会機)モドキを作ろう

作ろうと考えるようになったいきさつ

以前、家電量販店を歩いてるとSuicaやEdyなどの電子マネーの残高を確認できる電子マネービュアー「リレット」EV10 | KING JIMが販売されており思わず『買いたい』と思いましたが、値札を見たらお値段が・・・・。度々家電量販店に行くも今度買おうと自分に言い聞かせてたらいつの間にか生産中止になってしまたようで・・・・・。仕方ないから自分で自由に機能を拡張できるFeliCa電子マネー残高照会機を作ろうと思い立ちました。

初代残高照会機(Arduino UNO + PaSoRi)

実は、以前にも同様の目的でFeliCaの残高照会機を作った事があったのですが、想像よしていたものより大きく据え置きにするしかなかったため今回これよりも遥かに小さい物を作成しようと考えました。

道具の準備

・ハンダ線
・ハンダ吸い取り線
・フラックス
・熱収縮チューブ
・ハンダこて
・こて台
・こて拭き
・精密ピンセット
・カッターナイフ

電子工作をする上でとくに特別な物はないですが、結構細かい部品をハンダ付けするため精密ピンセットとフラックスがあるといいかもしれません。

部品の準備

・FeliCa リーダー・ライター RC-S620S
・FeliCa RC-S620S ピッチ変換基板のセット(フラットケーブル付き)
・Arduino Pro mini 5V
・SB0802GN
・AS1322A/V2
・Polymer Lithium Ion Battery – 110mAh
・JST-PHオスコネクター
・タクトスイッチ
・1μF積層セラミックチップコンデンサ 2個
・1μF積層セラミックコンデンサ 1個
・10kΩチップ抵抗 2個
・ビニール線

・FT232RL搭載小型USB-シリアルアダプタ 5V
・LiPo Charger Basic – Micro-USB

 今回手軽に持ち運べるよう小型化するために、Arduino Pro miniを選択しましたが、通常8Mhzな3.3V版では115200bpsの転送レートに対応していないためRC-S620/Sと通信することが出来ないので5V版を準備してください。
 なお、同じく軽量化小型化のためにLCD液晶にStrawberryLinux製のミニI2C液晶モジュールを使用しました。このパーツを使用すると一部チップ部品のハンダ付けが必要となりますが、I2C通信を利用する事により使用するピンを劇的に減らすことが可能になります。

ハードウェアの作成

Arduinoにシリアル通信用の足をハンダ付けし、裏面にプルアップ抵抗用にチップ抵抗(10kΩ)を2個ハンダ付けします。

StrawberryLinux製のミニI2C液晶モジュールはコンデンサを3個ハンダ付けしなければならないので、表面にチップコンデンサ(1μF)を2個、裏にコンデンサ(1μF)を1個ハンダ付けします。

StrawberryLinux製の昇圧型DC-DCコンバータモジュールは5V引き出す場合ハンダジャンパーが必要になるのでハンダでショートさせます。

最後にこのような配線になるように各種パーツをハンダ付けしていきます。バッテリーは使い切ると外して充電器に接続し充電します。
※ 当初、CR2032で動かす予定でしたが大電流放電が苦手なようでうまく動きませんでした。
※ RC-S620/Sは配線を間違うと一瞬で壊れます。私は試作の時にうっかり2台も壊してしまいまいした・・・・
※ 今回何となく不安でRAWに昇圧モジュールのOUTを繋いでいますが電圧を5Vに調整しているのでVCCに繋いでも一応動いたりします。

これでハードは完成です。

ライブラリの準備

Arduino向けRC-S620/S制御ライブラリ
I2C液晶ライブラリ – Okiraku Programming

それぞれのライブラリをダウンロードしlibrariesフォルダーに入れ、RC-S620/S制御ライブラリ“RCS620S.cpp”を開き#include “WProgram.h”を#include “Arduino.h”に書き換えて上書き保存します。I2C液晶ライブラリも“LCD_I2C.h”の中身を下記から次のように通り書き換えます。
※ 実は、“LCD_I2C.h”の書き換えは#include “Arduino.h”じゃなくて#include <wiring_private.h>でもよかったり?

書き換え前

#ifndef LCD_I2C_h
#define LCD_I2C_h
extern "C" {
#include <math.h>
#include <wiring.h>
}
#include <Wire.h>

書き換え後

#ifndef LCD_I2C_h
#define LCD_I2C_h
#include <math.h>
#include "Arduino.h"
#include <Wire.h>

ソフトウエアの作成

アトリエのどか様の公開されているスケッチを参考に実際にSuica、PASMO、Edy、nanaco、waonなどのFeliCa電子マネーの残高を表示させるように記述しました。

※ Arduino Pro miniではRC-S620/Sを接続しているとスケッチをArduinoに書き込めないという問題が発生しました。スケッチを書き込む場合は一度ArduinoとRC-S620/Sを繋ぐフレキケーブルを外しておく必要があります。

#include <Wire.h>
#include <LCD_I2C.h>
#include <RCS620S.h>

// Circuit
// RESET    - 1 RST
// Analog 5 - 2 SCL (internally pulled up)
// Analog 4 - 3 SDA (internally pulled up)
// GND      - 4 VSS
// 5V       - 5 VDD

// RCS620S
#define COMMAND_TIMEOUT               400
#define POLLING_INTERVAL              500
#define RCS620S_MAX_CARD_RESPONSE_LEN 30

// FeliCa Service/System Code
#define CYBERNE_SYSTEM_CODE           0x0003
#define COMMON_SYSTEM_CODE            0xFE00
#define PASSNET_SERVICE_CODE          0x090F
#define EDY_SERVICE_CODE              0x170F
#define NANACO_SERVICE_CODE           0x564F
#define WAON_SERVICE_CODE             0x680B

RCS620S rcs620s;

void setup(){
  LCD.begin(5);
  Serial.begin(115200);
}

void loop(){
  uint32_t balance;
  uint8_t buf[RCS620S_MAX_CARD_RESPONSE_LEN];
  
  rcs620s.timeout = COMMAND_TIMEOUT;
  
  // サイバネ領域
  if(rcs620s.polling(CYBERNE_SYSTEM_CODE)){
    // Suica PASMO
    if(requestService(PASSNET_SERVICE_CODE)){
      if(readEncryption(PASSNET_SERVICE_CODE, 0, buf)){
        // Little Endianで入っているPASSNETの残高を取り出す
        balance = buf[23];                  // 11 byte目
        balance = (balance << 8) + buf[22]; // 10 byte目
        // 残高表示
        printBalanceLCD("PASSNET", &balance);
      }
    }
  }
  
  // 共通領域
  else if(rcs620s.polling(COMMON_SYSTEM_CODE)){
    // Edy
    if(requestService(EDY_SERVICE_CODE)){
      if(readEncryption(EDY_SERVICE_CODE, 0, buf)){
        // Big Endianで入っているEdyの残高を取り出す
        balance = buf[26];                  // 14 byte目
        balance = (balance << 8) + buf[27]; // 15 byte目
        // 残高表示
        printBalanceLCD("Edy", &balance);
      }
    }
    
    // nanaco
    if(requestService(NANACO_SERVICE_CODE)){
      if(readEncryption(NANACO_SERVICE_CODE, 0, buf)){
        // Big Endianで入っているNanacoの残高を取り出す
        balance = buf[17];                  // 5 byte目
        balance = (balance << 8) + buf[18]; // 6 byte目
        balance = (balance << 8) + buf[19]; // 7 byte目
        balance = (balance << 8) + buf[20]; // 8 byte目
        // 残高表示
        printBalanceLCD("nanaco", &balance);
      }
    }
    
    // waon
    if(requestService(WAON_SERVICE_CODE)){
      if(readEncryption(WAON_SERVICE_CODE, 1, buf)){
        // Big Endianで入っているWaonの残高を取り出す
        balance = buf[17];                  // 21 byte目
        balance = (balance << 8) + buf[18]; // 22 byte目
        balance = (balance << 8) + buf[19]; // 23 byte目
        balance = balance & 0x7FFFE0;       // 残高18bit分のみ論理積で取り出す
        balance = balance >> 5;             // 5bit分ビットシフト
        // 残高表示
        printBalanceLCD("waon", &balance);
      }
    }
  }
  
  // デフォルト表示
  else{
    LCD.clear();
    LCD.move(0);
    LCD.print("Touch");
    LCD.move(0x44);
    LCD.print("Card");
  }
  
  rcs620s.rfOff();
  delay(POLLING_INTERVAL);
}

// request service
int requestService(uint16_t serviceCode){
  int ret;
  uint8_t buf[RCS620S_MAX_CARD_RESPONSE_LEN];
  uint8_t responseLen = 0;
  
  buf[0] = 0x02;
  memcpy(buf + 1, rcs620s.idm, 8);
  buf[9] = 0x01;
  buf[10] = (uint8_t)((serviceCode >> 0) & 0xff);
  buf[11] = (uint8_t)((serviceCode >> 8) & 0xff);

  ret = rcs620s.cardCommand(buf, 12, buf, &responseLen);
  
  if(!ret || (responseLen != 12) || (buf[0] != 0x03) ||
      (memcmp(buf + 1, rcs620s.idm, 8) != 0) || ((buf[10] == 0xff) && (buf[11] == 0xff))) {
    return 0;
  }

  return 1;
}

int readEncryption(uint16_t serviceCode, uint8_t blockNumber, uint8_t *buf){
  int ret;
  uint8_t responseLen = 0;
  
  buf[0] = 0x06;
  memcpy(buf + 1, rcs620s.idm, 8);
  buf[9] = 0x01; // サービス数
  buf[10] = (uint8_t)((serviceCode >> 0) & 0xff);
  buf[11] = (uint8_t)((serviceCode >> 8) & 0xff);
  buf[12] = 0x01; // ブロック数
  buf[13] = 0x80;
  buf[14] = blockNumber;

  ret = rcs620s.cardCommand(buf, 15, buf, &responseLen);

  if (!ret || (responseLen != 28) || (buf[0] != 0x07) ||
      (memcmp(buf + 1, rcs620s.idm, 8) != 0)) {
    return 0;
  }

  return 1;
}

void printBalanceLCD(char *card_name, uint32_t *balance){
  char result[8];
  sprintf(result, "%u", *balance);
  LCD.clear();
  LCD.move(0);
  LCD.print(card_name);
  LCD.move(0x40);
  LCD.print(0xE6);
  LCD.print(0x20);
  LCD.print(result);
  return;
}

作成過程

Arduino Pro miniとチップ抵抗のハンダ付けが終わった写真

ミニI2C液晶モジュールとチップコンデンサのハンダ付けが終わった写真

ミニI2C液晶モジュールとコンデンサのハンダ付けが終わった写真

全ての組立終わり、スケッチの書き込みの準備中の写真

作成例

作成したハードにスケッチを書き込み、調度よさそうな大きさのケースに格納してみました。



機能拡張のヒント

地方の各種交通機関のFeliCaカードに対応させても良いかもしれません。
残高と一緒に経由駅を表示させたり購入履歴を表示できるようにしても良いかもしれません。
おさいふケータイなどの三者間通信に対応しても面白いかもしれません。

各種パーツの図の配布

今回Arduino Pro miniやミニI2C液晶モジュールなどの図を作成しましたのでpng形式で提供させて頂きます。
FeliCa残高照会機 パーツ一覧

参考

かお(・v・)もじ SF Checker – アトリエのどか
第四章 低電圧I2C液晶ディスプレイ
felicalib プロジェクト Wiki
ICカードのフォーマット解析
I2C液晶ライブラリ – Okiraku Programming

Perl Linux CPU使用率 Memory使用率の高いプロセスを探し出す

最近尋常じゃないほどサーバリソースを喰い潰すプロセスがいるようなので調査しようと監視システムを書くことにしました。
一応PerlではProc::ProcessTableと呼ばれるモジュールにてプロセスを監視できるようなのでサクッとCPU使用率の高いプロセスを見つけ出すプログラムを書いてみました。

#!/usr/bin/perl
use warnings;
use strict;
use Proc::ProcessTable;

my $Processtable = new Proc::ProcessTable;

foreach my $item (@{$Processtable->table}){
        my $pctcpu = sprintf("%5.1f", $item->pctcpu);
        my $pctmem = sprintf("%5.1f", $item->pctmem);
        if($pctcpu > 50){
                print $item->pid ." ". getpwuid($item->uid) ." " . $pctcpu . " ". $pctmem ." ". $item->cmndline . "\n";
        }
}

実行するとちゃんとCPU使用率の高いプロセスを特定してくれました。

# perl ProcessKill.pl
3509 root 100.0   0.0 155 perl -e while (1) { $i++ }

参考
Proc::ProcessTable size differences

atコマンドがエラーを起こす

$ echo “xmms -p” |sudo at 0900
warning: commands will be executed using /bin/sh
Cannot open lockfile /var/spool/cron/atjobs/.SEQ: No such file or directory

atdを再起動させても解決しなかったため手動でロックファイルを作成したところ解決しました

$ sudo touch /var/spool/cron/atjobs/.SEQ
$ sudo chown daemon:daemon /var/spool/cron/atjobs/.SEQ