2014/12/19
子供の写真をバックアップ
今回は 「子供」×「ネット」 Advent Calendar 2014 の 19日目の記事として書きました。
難しい話題が続いた様ですので、ここらでちょっと気分転換に軽いお話を…
子供が産まれたら皆さん写真を一杯撮影しますよね?
『え?そうでもない?』…そ、そうですか(笑
私は写真を一杯いっぱい撮影しちゃいました。
我が子のために為にコンパクトデジタルカメラを買い換え、
それでは物足りずにデジタル一眼レフカメラを購入し、
明るいレンズ…望遠レンズ…とすっかり泥沼にはまり込んでいます。
特にお勧めは F値が 1.4 の単焦点レンズで……っと、今回はそういう話ではなくて
撮影したデータを保存するというお話です。
一般的に言ってデジタルカメラで撮影した画像は
お手持ちのパソコンのハードディスクに転送して保存していると思います。
その中でお気に入りの何枚かは印刷して保存しているでしょうが、
かつての銀塩カメラと違って全てを印刷する事は少ないのではないでしょうか?
そうなると我が子の写真データはパソコンの中だけに保存されているという事は割とよくある事だと思います。
でも、ちょっと待って下さい。(通販番組風)
そのパソコン本当に大丈夫でしょうか?
ある日パソコンが起動しなくなってしまうのは割と良くある事です。
コンピュータに詳しければパソコンが起動しなくてもお子様の写真を取り出す事はできますが、
普通はそんな事なかなか出来ないと思います。
それに実はパソコンのハードディスクというのは意外と信頼性が低く、
ちょっとした衝撃や場合によっては近所の落雷などでデータが消える危険性があります。
消えてしまって悲しい思いをするまえにちょっとした手間でバックアップを是非!
そんな大切な写真のバックアップにはこちらの商品をぜひ……じゃなくて
今回は我が家でのデータ保全をご紹介致します。
我が家 (というか私) は自宅では MacBook PRO をメインに愛用しています。
写真などのデータも基本はこの中ですのでこのマシンの中身がマスターデータとなります。
OS X と言えば標準で搭載されるバックアップツールの TimeMachine が有名です。
普段持ち歩き用に MacBook Air を使っていますので、
重要なデータ (というか殆どの個人データ) はメインの MacBook PRO と同期しています。
ですので全ての基本は MacBook PRO のデータ (画像データの場合は Picture フォルダ) のバックアップになります。
ご多分に漏れず我が家にも TimeCapsule があるので (但し内蔵 HDD は何度か交換済み)、
OS の機能を利用して TimeCapsule にバックアップしています。
MacBook PRO が起動している間は自動でバックアップをしてくれるので便利です。
問題点は若干信頼性が低い所でしょうか。
ですので TimeMachine だけではなく他のバックアップ手段と併用しています。
同期は OS X に標準のユーティリティ rsync コマンドを利用しています。
rsync は unix で古くから利用されている同期のためのユーティリティで、
ターミナル画面上から利用します。
ターミナルからの利用なので少々ハードルが高く感じますが、
基本は『どこのデータ』を『どこに』同期するか指定して実行するだけです。
例えば以下の様に実行すると Picture フォルダをホスト macbookair の Picture フォルダに同期します。
通信は ssh を利用していますが ssh-keygen で生成した鍵認証を利用していますので、
接続時にパスフレーズを聞かれる事なく接続できるので便利ですし自動処理に向いています。
$ for i in Documents Pictures Movies Musics
> do
> rsync -avz -e ssh ${i} macbookair.example.com:.
> done
自宅の LAN 内で MacBook PRO と MacBook Air が同時に起動されている状態では、
上記の処理を自動で定期的に実行する事で MacBook Air のデータを最新の状態に更新し
同時にデータのバックアップ (単なる二重化) としても役立てています。
正確には NAS ではなくサーバのストレージです。
自宅には FreeBSD で構築したサーバが設置してあるのですが、
そのサーバに大容量の HDD を増設してバックアップ用の NAS としています。
前述した rsync を利用してこちらも自動で定期的にストレージにデータを転送しています。
$ for i in Documents Pictures Movies Musics > do > rsync -avz -e ssh ${i} storage.example.com:/var/backup/ > done
他の用途で利用している VPS (さくらの一番安いタイプですが十分です)のストレージに、
前述した自宅サーバに増設した HDD のデータを定期的に同期しています。
自宅サーバは FreeBSD ですので unix の強力なツールが利用できるので、
この同期処理も勿論 rsync を利用しています。
$ rsync -avz -e ssh /var/backup vps.example.com:/var/backup
このさくらの VPS は親戚向けに娘の写真を公開する Web サーバとしても利用しています。
公開には WS-Slideshow という flash のアルバムを利用していますが、
自作のシェルスクリプト を利用して
WS-Slideshow の設定ファイル (xml) やサムネイルファイルを作成しています (宣伝乙)。バックアップしたデータはスクリプトで html などを自動生成して公開しています。
余談ですが、この VPS は 娘の名前の漢字ドメインで運用しています。
娘には unix によるサーバ管理の基礎知識をしっかりと教えて、 娘が成人する時にドメインの権限を委譲するのが今から楽しみです。 (「そんなの要らないよ」と言われるのが今から怖い…)
年に1回娘の誕生日の翌日に前年の1年分の写真や動画データを DVD に保存しています。
正と副の2枚を焼いて保存していますが、
実は光媒体はデータの保存期間が割と短いので気休め程度です。
データ保存の本命 Amazon S3 です。
ご存じの方も多いと思いますが、通信販売で有名な Amazon は Amazon Web Service (通称 AWS) という
いわゆるクラウドコンピューティングのサービスも提供しています。
この AWS の中で S3 というクラウトストレージ (インターネット上のハードディスクの様なモノです)は
お手軽、安心、そこそこ安価なのでこれをバックアップに利用しない手はありません。
AWS の画面は日本語化を進めているそうなのですが、一部(殆ど)英語ですので
若干ハードルが高いのですが、実は簡単な英語ですし機能も単純ですのでそれほど難しくないと思います。
AWS S3 ではデータを「バケット」と呼ばれるコンテナに格納します。
バケットはパソコンのフォルダーの様なものと考えて下さい。
ですので写真を入れるためのバケットを好きな名前で作成します。
ちなみにこのバケットの名前は全世界でユニークにする必要がある様なので、
「pictures」など単純な名前は多分利用できないと思います。
バケットが出来たら早速写真を転送してみます。
ブラウザを経由した Amazon S3 の管理画面からでもアップロードは可能ですが、
Amazon S3 用に色々なツールが公開されているのでそれらを利用するのが良いでしょう。
私はサーバに s3cmd というツールを導入して定期的に同期処理を実行しています。
$ s3cmd sync -rr /var/backup s3://my-picture-backet/Picture
ちなみに我が家では娘が産まれてから 8 年分の写真データを全てバックアップしていますが、
容量は 100GB (枚数で言うと 200,000枚) 程度で費用は大体毎月 $ 4.00 (500円) 程度です。
AWS の他の機能 (Cloud Front) を利用すればアップロードした写真を公開したり、 色々とできる事は沢山あるのですが長くなってしまうので今回は残念ながら割愛します。
追記
rsync は OS X 標準ではありませんでした。Mac Ports を利用して後から導入したコマンドです。
2014/10/30
2014/08/31
PCH Coffee
知り合いが鵠沼海岸にパンケーキのお店を開店したとの事で
早速家族で遊びに行って来ました。
こぢんまりした店内の内装はとってもセンスが良くまとまっていて、 いつまでもゆっくりしていたくなります。
娘はフルーツが盛りだくさんの「季節のフルーツパンケーキ」、
私はラズベリーとバナナがトッピングされた「チョコチップパンケーキを頂きました」。
ちなみに奥さんは「エッグベネディクトパンケーキ」を頂いたのですが、 写真を撮り忘れました(普段食事を撮影する習慣がないので…つい(汗
ちなみに甘くない具のパンケーキは初めてだったのですが とっても美味しかったです♪
とってもフワフワで美味しいパンケーキを堪能しました。
2014/07/29
ANA整備工場見学に行って来たよ!
今年の2月に申し込んだ ANA 整備工場見学。
非常に人気が高く申し込んでから半年も待たされましたが、
何とか無事に整備工場の見学をしてきました。
朝早くから起きて羽田空港まで車で移動、
空港から羽田整備場まではモノレールで移動です。
モノレールの駅から炎天下を 20分程度歩くといよいよ入り口が見えてきて、
否が応でも気持ちが盛り上がります。
受付を済ませるとまずはスライドや動画などを交えた座学。
お話するのはキャビンアテンダントさんか地上スタッフさんですが、 流石に人前でお話するのは慣れていて上手です。 ここでは『Boeing 787 に搭載されている Rolls-Royce製のエンジンは 何と一基35億円もする!』などのトリビアを仕入れます。
座学の後は待ちに待った工場見学の始まりです。
工場の中なのでヘルメットを被る必要がありテンションも高まります。
整備工場入るとまずはその広さに驚かされます。 あの巨大なジェット旅客機が最大で6機も収容できるサイズは圧巻です。
当日の整備工場では Boeing 777 や 787 などが整備されていました。
Boeing 787 の特長のある翼端や
騒音を低減すると言われるシェブロンノズルも目の前で見れました。
こんなに間近で飛行機を見るのは始めての経験ですし、 エンジンが外され整備中の機体を見るのも勿論初めてなので大感激です。
整備工場のすぐ外には特別塗装の Boeing 787 二号機が停機し 出発前の点検整備的な事が実施されていました。
わずか1時間半程度の整備工場見学ですが、 とっても貴重で楽しい経験ができました。
娘よりも親の方がテンション上がって楽しんでいたのは内緒です
2014/07/27
キャンプ
軟弱アウトドア派にとって素晴らしい季節が到来したので、
家族でキャンプに行ってきました。
娘にとっては(そして奥さんにとっても)初めてのキャンプ、
意外に臆病な娘は果たしてちゃんとテントで寝る事が出来るのだろうか?
などと一抹の不安を残しながらキャンプに出かけてきました。
家族では初めてのキャンプなので、 ある程度は設備が整っているキャンプ場が良いかな?と思い、 選んだのは自宅からも割と近い西丹沢のウェルキャンプ西丹沢。 サイトは川沿いから数メートル高くなった場所にあり、 午後からは木陰で直射日光も防げるという絶好のロケーションでした。
ドームテントやバーベキューグリルなどを設営したら川遊び!
適度に冷たく綺麗な流れの川で目一杯楽しみます。
川遊びを堪能した後はバーベキューで舌鼓!
自然の中で食べる食材の何と美味しい事か。
一通り遊んだらいよいよテント泊。
微妙に霧雨が降る中テントに入って寝る準備をしたら、
川遊びなどで相当疲れていたのか、
恐がりもせずにあっという間に寝入ってしまった娘。
普段怖がりだと思っていたけど意外と図太い一面もあるのか(笑
翌朝は昨夜と打って変わって快晴!(日頃の行いが…)
朝食を食べたらテントを干しながらまたまた水遊び。
かくして一泊の家族初キャンプは無事に楽しく終わりました
追記
我々が出かけた丁度一週間後にこちらのキャンプ場にて 痛ましい事故が起きてしまいました。亡くなられた母子のご冥福をお祈りします。
2014/07/16
迷惑メイル送信ドメイン
昨日くらいから携帯のメイルアドレスにやたら届く様になった 迷惑メイルの差し出しドメイン一覧。
- anon-elec.com
- man-infe.com
- oasi-powe.net
- nati-poss.com
- comp-reme.net
- chri-disi.com
- abil-desi.net
- c-comf.net
- init-hall.com
- de-scre.com
wget (1) などで文中の URL にアクセスしてみると、 どのリンクも最終的に pc.exsr.net にリダイレクトされている様。
$ whois exsr.net Registrars. Domain Name: exsr.net Registry Domain ID: Registrar WHOIS Server: whois.discount-domain.com Registrar URL: http://www.onamae.com Updated Date: 2013-11-19 10:05:11.0 Creation Date: 2013-11-14 11:32:19.0 Registrar Registration Expiration Date: 2014-11-14 11:32:19.0 Registrar: GMO INTERNET, INC. Registrar IANA ID: 49 Registrar Abuse Contact Email: abuse@gmo.jp Registrar Abuse Contact Phone: Domain Status: ACTIVE Registry Registrant ID: Registrant Name: Hiroshi Tsumura Registrant Organization: skypia Registrant Street1: Minamisenba 3-2-28 Registrant Street2: Registrant City: Oosaka-shi Chuuou-ku Registrant State/Province: Osaka Registrant Postal Code: 542-0081 Registrant Country: JP Registrant Phone: +81.0668091939 Registrant Phone Ext: Registrant Fax: +81.0668091940 Registrant Fax Ext: Registrant Email: makaimakau4ah555@yahoo.co.jp Registry Admin ID: Admin Name: Hiroshi Tsumura Admin Organization: skypia Admin Street1: Minamisenba 3-2-28 Admin Street2: Admin City: Oosaka-shi Chuuou-ku Admin State/Province: Osaka Admin Postal Code: 542-0081 Admin Country: JP Admin Phone: +81.0668091939 Admin Phone Ext: Admin Fax: +81.0668091940 Admin Fax Ext: Admin Email: makaimakau4ah555@yahoo.co.jp Registry Tech ID: Tech Name: Hiroshi Tsumura Tech Organization: skypia Tech Street1: Minamisenba 3-2-28 Tech Street2: Tech City: Oosaka-shi Chuuou-ku Tech State/Province: Osaka Tech Postal Code: 542-0081 Tech Country: JP Tech Phone: +81.0668091939 Tech Phone Ext: Tech Fax: +81.0668091940 Tech Fax Ext: Tech Email: makaimakau4ah555@yahoo.co.jp Name Server: ns1.value-domain.com Name Server: ns2.value-domain.com DNSSEC: URL of the ICANN WHOIS Data Problem Reporting System: http://wdprs.internic.net/ >>> Last update of WHOIS database: 2013-11-19 10:05:11.0 <<<…だそうです。
迷惑メイルの差し出しドメインを whois (1) してみると
殆どが Organization に exsr か skypia と書かれている様ですね。
ちなみに exsr も skypia も
検索すると
色々と出てきますね。
(どちらも結構有名みたいですが繋がってるんですかね)
追記
gTLD を変更する様ですね。 docomo の迷惑メイルは部分一致なので gTLD 無しの登録が良いかも?2014/06/19
Amazon AWS 上でホスト情報を取得する
Amazon AWS で ELB + AutoScaling を利用している場合など、
IP アドレスが頻繁に変わってしまうので、
グローバルIP、プライベートIP を取得して一覧表示するスクリプト。
ホスト名は Name タグの Value に格納されている必要がある。
aws コマンドは EC2 インスタンスで Amazon Linux を起動した場合は
標準でインストールされている筈。
EC2 インスタンスに適切な role が設定されていれば aws はそのまま動作するが、
role が設定されていない場合でも設定する事で利用可能になる。
引数でホスト名 (Name タグで指定された値) が指定された場合、 デフォルトではグローバル IP のみを出力するが -p オプションでプライベートIP のみを表示する。
1#!/bin/sh 2 3# リージョン 4region="--region ap-northeast-1" 5# 出力はテキスト形式 6output="--output text" 7# 出力項目を指定 (ホスト名(=タグ)、インスタンスID、パブリックIP、プライベートIP 8base="--query Reservations[*].Instances[*]." 9# Name タグの値表示 10name="Tags[?Key==\`Name\`].Value," 11# インスタンスID、パブリックIP、プライベートIP 12default="${name}InstanceId,PublicIpAddress,PrivateIpAddress" 13# フィルタ指定 (running 状態のみ出力) 14filter="--filter Name=instance-state-name,Values=running" 15 16# aws コマンド 17aws="/usr/bin/aws ec2 describe-instances ${region} ${output}" 18 19# メイン関数 20function doit() 21{ 22 23 local _filter _query 24 25 if [ -n "${1}" ] 26 then 27 _filter="${filter} Name=tag:Name,Values=${1}" 28 _query=${base}[${3}${2}] 29 else 30 _filter=${filter} 31 _query=${base}[${default}] 32 fi 33 34 ${aws} ${_filter} ${_query} 35 36} 37 38# オプション解析 39if opt=`getopt p $*` 40then 41 set -- ${opt} 42 while [ -n "${1}" ] 43 do 44 case "${1}" in 45 -p ) 46 target=PrivateIpAddress;; 47 -- ) 48 shift 49 break;; 50 esac 51 shift 52 done 53else 54 echo "Usage: ${0##*/} [-p][host [... host]" 1>&2 55 exit 255 56fi 57 58if [ $# -eq 1 ] 59then 60 doit ${1} ${target:-PublicIpAddress} 61elif [ $# -gt 0 ] 62then 63 for i in $* 64 do 65 doit ${i} ${target:-PublicIpAddress} ${name} 66 done | awk '{ if(NR % 2) ip = $0; else printf "%s %s\n", $0, ip }' 67else 68 doit | awk '{ if(NR % 2) ip = $0; else printf "%s %s\n", $1, ip }' 69fi
2013/11/23 追記
複数ホスト名が指定された場合はホスト名も表示する様変更。2014/05/23
checkinstall で rpm パッケージの作成がエラーになる場合
rpm パッケージを手軽に作成するために checkinstall は非常に便利だが、 現行の checkinstall にはインストール対象となるソフトウェアに ファイルに対するシンボリックリンクが含まれると rpm パッケージが作成できなくなる不具合がある様だ。
この不具合は checkinstall が rpm パッケージ用の spec ファイルを生成する際に、 ターゲットとなるオブジェクトがディレクトリかシンボリックリンクなら 無条件にディレクトリと仮定して %dir と出力しているために発生している。 そのために、例えばインストール時に libhoge.so.X.Y を libhoge.so にシンボリックリンクする様なソフトウェアの rpm パッケージを作成しようとするとエラーとなってしまう。
そこでターゲットなるオブジェクトがシンボリックリンクの場合、
リンク先を確認してディレクトリだった場合のみ
%dir を出力する様にするパッチを作成した。
*** /usr/sbin/checkinstall~ 2014-05-21 12:29:30.000000000 +0900 --- /usr/sbin/checkinstall 2014-05-23 23:51:09.131081380 +0900 *************** *** 2428,2434 **** # Prepare directories to be included in the .spec file mv ${TMP_DIR}/newfiles ${TMP_DIR}/newfiles.tmp cat ${TMP_DIR}/newfiles.tmp | while read line; do ! [ -d "${BUILD_DIR}/${line}" -o -L "${BUILD_DIR}/${line}" ] && echo -n "%dir " >> ${TMP_DIR}/newfiles echo "\"/${line}\"" >> ${TMP_DIR}/newfiles done --- 2428,2435 ---- # Prepare directories to be included in the .spec file mv ${TMP_DIR}/newfiles ${TMP_DIR}/newfiles.tmp cat ${TMP_DIR}/newfiles.tmp | while read line; do ! [ -d "${BUILD_DIR}/${line}" ] && echo -n "%dir " >> ${TMP_DIR}/newfiles ! [ -L "${BUILD_DIR}/${line} -a -d `ls -l ${BUILD_DIR}/${line} | awk '$0=$NF'` ] && echo -n "%dir" >> ${TMP_DIR}/newfiles echo "\"/${line}\"" >> ${TMP_DIR}/newfiles done
/usr/sbin/checkinstall にこのパッチを適用する事で
シンボリックリンク先がディレクトリの場合のみ
%dir を出力する様に変更される。
シンボリックリンクがネストしている場合は正しく動作しないので
あくまでも暫定的なパッチだが、
取りあえず rpm パッケージを作成した場合にはそれなりに有用だと思う。
一応 checkinstall の配布元の bugzilla にはパッチを添えて報告してある。
ちなみに checkinstall で rpm の作成がエラーになる場合は 殆どが自動生成された spec ファイルが問題な場合なので、 --review-spec オプションを指定して checkinstall を実行する事で、 rpm パッケージの作成処理を実行する前に 自動生成された spec ファイルを編集できるので問題を回避できる事が多い。
# checkinstall --review-spec</i>
2014/05/22
Amazon Linux AMI 2014.03.1 (64bit) への checkinstall 導入
Amazon AWS の EC2 インスタンスで動作している Amazon Linux AMI 2014.03.1
(Linux ****** 3.10.40-50.136.amzn1.x86_64 #1 SMP Tue May 13 21:35:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux) への
checkinstall の導入時にエラーとなった場合の対処方法。
Amazon AWS の EC2 インスタンスに checkinstall コマンドを導入しようとして、 ネットなどの情報に基づいて以下のコマンドを実行した所コンパイルエラーが発生した。
$ git clone http://checkinstall.izto.org/checkinstall.git $ cd checkinstall $ vi Makefile checkinstall checkinstallrc-dist installwatch/Makefile $ make : : make -C installwatch make[1]: ディレクトリ `/home/mitz/checkinstall/installwatch' に入ります gcc -Wall -c -D_GNU_SOURCE -DPIC -fPIC -D_REENTRANT -DVERSION=\"0.7.0beta7\" installwatch.c installwatch.c:2960:5: error: conflicting types for ‘readlink’ int readlink(const char *path,char *buf,size_t bufsiz) { ^ In file included from installwatch.c:41:0: /usr/include/unistd.h:831:16: note: previous declaration of ‘readlink’ was here extern ssize_t readlink (const char *__restrict __path, ^ installwatch.c:3098:5: error: conflicting types for ‘scandir’ int scandir( const char *dir,struct dirent ***namelist, ^ In file included from installwatch.c:49:0: /usr/include/dirent.h:255:12: note: previous declaration of ‘scandir’ was here extern int scandir (const char *__restrict __dir, ^ installwatch.c:3714:5: error: conflicting types for ‘scandir64’ int scandir64( const char *dir,struct dirent64 ***namelist, ^ In file included from installwatch.c:49:0: /usr/include/dirent.h:278:12: note: previous declaration of ‘scandir64’ was here extern int scandir64 (const char *__restrict __dir, ^ make[1]: *** [installwatch.o] エラー 1 make[1]: ディレクトリ `/home/mitz/checkinstall/installwatch' から出ます make: *** [all] エラー 2 $ざっと調べてみると glibc のバージョン情報を格納していると思われるマクロ GLIBC_MINOR の値が不正で readlink()、 及び内部で定義している scandir()、scandir64() のプロトタイプ宣言が正しく展開されていない様だったので、 他にもいくつか発生したコンパイルエラーを修正して 取りあえずコンパイルしてインストール可能なパッチを作成した。
本来は Amazon LINUX 上でも glibc のバージョンを正しく取得できる様にするべきなのだが、 諸処の事情でそこまで修正する余裕がないので暫定的な対策のみとなっている。
1diff -rc checkinstall/installwatch/installwatch.c checkinstall~/installwatch/installwatch.c 2*** checkinstall/installwatch/installwatch.c 2014-05-21 12:41:51.659879736 +0900 3--- checkinstall~/installwatch/installwatch.c 2014-05-21 12:41:16.107577033 +0900 4*************** 5*** 99,105 **** 6 static int (*true_xstat)(int,const char *,struct stat *); 7 static int (*true_lxstat)(int,const char *,struct stat *); 8 9! #if(GLIBC_MINOR >= 10) 10 11 static int (*true_scandir)( const char *,struct dirent ***, 12 int (*)(const struct dirent *), 13--- 99,105 ---- 14 static int (*true_xstat)(int,const char *,struct stat *); 15 static int (*true_lxstat)(int,const char *,struct stat *); 16 17! #if 1 18 19 static int (*true_scandir)( const char *,struct dirent ***, 20 int (*)(const struct dirent *), 21*************** 22*** 130,136 **** 23 static int (*true_open64)(const char *, int, ...); 24 static struct dirent64 *(*true_readdir64)(DIR *dir); 25 26! #if(GLIBC_MINOR >= 10) 27 static int (*true_scandir64)( const char *,struct dirent64 ***, 28 int (*)(const struct dirent64 *), 29 int (*)(const struct dirent64 **,const struct dirent64 **)); 30--- 130,136 ---- 31 static int (*true_open64)(const char *, int, ...); 32 static struct dirent64 *(*true_readdir64)(DIR *dir); 33 34! #if 1 35 static int (*true_scandir64)( const char *,struct dirent64 ***, 36 int (*)(const struct dirent64 *), 37 int (*)(const struct dirent64 **,const struct dirent64 **)); 38*************** 39*** 2545,2551 **** 40 } 41 42 FILE *fopen(const char *pathname, const char *mode) { 43! FILE *result; 44 instw_t instw; 45 int status=0; 46 47--- 2545,2551 ---- 48 } 49 50 FILE *fopen(const char *pathname, const char *mode) { 51! FILE *result = NULL; 52 instw_t instw; 53 int status=0; 54 55*************** 56*** 2956,2962 **** 57 return result; 58 } 59 60! #if (GLIBC_MINOR <= 4) 61 int readlink(const char *path,char *buf,size_t bufsiz) { 62 int result; 63 #else 64--- 2956,2962 ---- 65 return result; 66 } 67 68! #if 0 69 int readlink(const char *path,char *buf,size_t bufsiz) { 70 int result; 71 #else 72*************** 73*** 3096,3103 **** 74 } 75 76 int scandir( const char *dir,struct dirent ***namelist, 77! int (*select)(const struct dirent *), 78! #if (GLIBC_MINOR >= 10) 79 int (*compar)(const struct dirent **,const struct dirent **) ) { 80 #else 81 int (*compar)(const void *,const void *) ) { 82--- 3096,3103 ---- 83 } 84 85 int scandir( const char *dir,struct dirent ***namelist, 86! int (*filter)(const struct dirent *), 87! #if 1 88 int (*compar)(const struct dirent **,const struct dirent **) ) { 89 #else 90 int (*compar)(const void *,const void *) ) { 91*************** 92*** 3114,3124 **** 93 /* We were asked to work in "real" mode */ 94 if( !(__instw.gstatus & INSTW_INITIALIZED) || 95 !(__instw.gstatus & INSTW_OKWRAP) ) { 96! result=true_scandir(dir,namelist,select,compar); 97 return result; 98 } 99 100! result=true_scandir(dir,namelist,select,compar); 101 102 return result; 103 } 104--- 3114,3124 ---- 105 /* We were asked to work in "real" mode */ 106 if( !(__instw.gstatus & INSTW_INITIALIZED) || 107 !(__instw.gstatus & INSTW_OKWRAP) ) { 108! result=true_scandir(dir,namelist,filter,compar); 109 return result; 110 } 111 112! result=true_scandir(dir,namelist,filter,compar); 113 114 return result; 115 } 116*************** 117*** 3712,3719 **** 118 } 119 120 int scandir64( const char *dir,struct dirent64 ***namelist, 121! int (*select)(const struct dirent64 *), 122! #if (GLIBC_MINOR >= 10) 123 int (*compar)(const struct dirent64 **,const struct dirent64 **) ) { 124 #else 125 int (*compar)(const void *,const void *) ) { 126--- 3712,3719 ---- 127 } 128 129 int scandir64( const char *dir,struct dirent64 ***namelist, 130! int (*filter)(const struct dirent64 *), 131! #if 1 132 int (*compar)(const struct dirent64 **,const struct dirent64 **) ) { 133 #else 134 int (*compar)(const void *,const void *) ) { 135*************** 136*** 3730,3740 **** 137 /* We were asked to work in "real" mode */ 138 if( !(__instw.gstatus & INSTW_INITIALIZED) || 139 !(__instw.gstatus & INSTW_OKWRAP) ) { 140! result=true_scandir64(dir,namelist,select,compar); 141 return result; 142 } 143 144! result=true_scandir64(dir,namelist,select,compar); 145 146 return result; 147 } 148--- 3730,3740 ---- 149 /* We were asked to work in "real" mode */ 150 if( !(__instw.gstatus & INSTW_INITIALIZED) || 151 !(__instw.gstatus & INSTW_OKWRAP) ) { 152! result=true_scandir64(dir,namelist,filter,compar); 153 return result; 154 } 155 156! result=true_scandir64(dir,namelist,filter,compar); 157 158 return result; 159 }2014/05/22 に git clone した checkinstall のソースに このパッチを適用してコンパイル、動作する事は確認できている。
Amazon Linux で checkinstall の導入に困っている方の参考になればと思い公開してみる。
2014/03/27
LDAP 登録用スクリプト
以前、某社内のユーザ管理を ldap 化した際に作成した
ldap 登録用の ldif ファイルを生成するスクリプト。
漢字の名字と名前を入力すると kakashi を利用して
ひらがなの名字、名前、ローマ字の名字、名前を自動的に取得し
ldap 登録用の ldif ファイルを生成して ldap 登録まで自動で行う。
当初は csv 型式のファイルから必要な情報を取得して
連続で ldap に登録するバッチスクリプトとして作成したのだが、
初期登録が完了した後はたまに発生するユーザの追加処理にのみ使用するので
対話的に実行するコマンドに作り直した。
漢字をひらがな/ローマ字に変換するために kakashi とnkf が必要になる。
漢字のよみは自動では完全には取得できないので変換した値を確認する様にしている。
ローマ字の氏名は私の趣味で名字は全て大文字、名前はキャピタライズとしている。
1#!/bin/sh 2# All rights reserved, copyright (C) 2014, Mitzyuki IMAIZUMI 3# 4 5# ldap 用設定 6passwd="password" 7org="dc=example,dc=com" 8 9# コマンド定義 10id="`/usr/bin/id -u`" 11kakasi=/usr/bin/kakasi 12slappasswd="/usr/sbin/slappasswd -h {md5}" 13toutf="/usr/bin/nkf -w" 14toeuc="/usr/bin/nkf -e" 15ldapadd="/usr/bin/ldapadd -x -D cn=Manager,${org}" 16ldappasswd="/usr/bin/ldappasswd -x -D cn=Manager,${org}" 17ldapsearch="/usr/bin/ldapsearch -LL -x -D cn=Manager,${org}" 18 19# uid 格納ファイル 20uidnum=/etc/uid 21 22# OU 選択肢 23oulist="Board Sales Develop Affair" 24 25# 入力用タイトル 26_uid="ユーザID" 27_sn="漢字名字" 28_gn="漢字名前" 29_hsn="名字よみ" 30_hgn="名前よみ" 31_tel="内線番号" 32_ou="所属" 33 34# 35# 汎用入力処理 36# $1: 入力された値を格納する変数名 37# $2: 必須フラグ 38# 39getans() 40{ 41 42 title=`eval "echo \\$_$1"` 43 var=$1 44 require=${2:-true} 45 46 while true 47 do 48 echo -n "${title} を入力して下さい: " 49 read ans 50 51 if [ -z "${ans}" ] 52 then 53 if ${require} 54 then 55 echo "${title} の入力は必須です" 56 else 57 echo "${titoe} なし" 58 break 59 fi 60 else 61 break 62 fi 63 done 64 65 eval "$var=${ans}" 66 67} 68 69# 70# カナ確認/入力処理 71# $1: 入力された値を格納する変数名 72# 73getkana() 74{ 75 76 title=`eval "echo \\$_$1"` 77 var=$1 78 79 echo -n "${title} は \"${2}\" で正しいですか?([y]/n): " 80 read ans 81 if [ "${ans}" = "n" ] 82 then 83 getans ${var} 84 else 85 eval "$var=${2}" 86 fi 87 88} 89 90# 91# 所属選択処理 92# $1: 入力された値を格納する変数名 93# 94getou() 95{ 96 97 title=`eval "echo \\$_$1"` 98 var=$1 99 100 while true 101 do 102 i=1 103 set ${oulist} 104 for o 105 do 106 echo "${i} ) ${o}" 107 i=`expr ${i} + 1` 108 done 109 echo -n "${title} を選択して下さい: " 110 read ans 111 if [ "${ans}" -ge 1 -a "${ans}" -lt $i ] 112 then 113 eval "$var=\${$ans}" 114 break 115 else 116 echo "入力が不正です" 117 fi 118 done 119 120} 121 122# 123# UID 取得処理 124# 125getuid() 126{ 127 128 num=`cat ${uidnum}` 129 expr ${num} + 1 > ${uidnum} 130 131} 132 133# 134# People 用 ldif ファイルの生成 135# 136ladduser() 137{ 138 139 cat <<-EOF | ${toutf} 140 dn: uid=${uid},ou=People,${org} 141 uid: ${uid} 142 objectclass: posixAccount 143 objectclass: top 144 objectclass: shadowAccount 145 objectclass: inetOrgPerson 146 cn: ${sn} ${gn} 147 cn: ${hsn} ${hgn} 148 cn: ${rgn} ${rsn} 149 sn: ${rsn} 150 givenname: ${rgn} 151 mail: ${uid}@example.com 152 o: Example, Inc. 153 ou: ${ou} 154 loginShell: /bin/bash 155 uidNumber: ${num} 156 gidNumber: ${num} 157 homeDirectory: /home/${uid} 158 gecos: ${rgn} ${rsn} 159EOF 160 test -n "${tel}" && echo "telephoneNumber: ${tel}" 161 echo 162 163} 164 165# 166# Group 用 ldif ファイルの生成 167# 168laddgroup() 169{ 170 171 cat <<-EOF | ${toutf} 172 dn: cn=${uid},ou=Group,${org} 173 objectclass: posixGroup 174 objectclass: top 175 cn: ${uid} 176 userPassword: {crypt}x 177 gidNumber: ${num} 178 memberUid: ${uid} 179 180EOF 181 182} 183 184# 185# ID が既に使用済みか確認する 186# $1: ID 187# 188existldap() 189{ 190 191 if ! egrep -q "^${1}:" /etc/passwd 192 then 193 return `${ldapsearch} uid=${1} -w ${passwd} | grep uid | wc -l` 194 else 195 return 1 196 fi 197 198} 199 200# 201# メイン処理 202# 203if [ "${id}" != "0" ] 204then 205 echo "root にて実行して下さい" 1>&2 206 exit 255 207fi 208 209# 情報入力 210while true 211do 212 while true 213 do 214 getans "uid" 215 if existldap ${uid} 216 then 217 break 218 fi 219 echo "${uid} は既に使用されています" 1>&2 220 done 221 getans "sn" 222 getans "gn" 223 gettel "tel" false 224 getkana "hsn" "`echo ${sn} | ${toeuc} | ${kakasi} -JH | ${toutf}`" 225 getkana "hgn" "`echo ${gn} | ${toeuc} | ${kakasi} -JH | ${toutf}`" 226 getou "ou" 227 228 echo 229 for i in uid sn gn hsn hgn tel 230 do 231 eval "echo \$_$i: \$$i" 232 done 233 234 echo -n "この情報で正しいですか?([y]/n): " 235 read ans 236 if [ "${ans}" != "n" ] 237 then 238 break 239 fi 240done 241 242# ローマ字表記の名字取得(全て大文字) 243rsn=`echo ${hsn} | ${toeuc} | ${kakasi} -Ha -Ka -Ja -Ea -ka | tr '[a-z]' '[A-Z]'` 244# ローマ字表記の名前取得(キャピタライズ) 245rgn=`echo ${hgn} | ${toeuc} | ${kakasi} -Ha -Ka -Ja -Ea -ka | awk '{ print toupper(substr($0, 1, 1)) substr($0, 2, length($0) - 1) }'` 246getuid 247# People 登録 248ladduser | ${ldapadd} -w ${passwd} 249# Group 登録 250laddgroup | ${ldapadd} -w ${passwd} 251# パスワード自動生成 252${ldappasswd} uid=${uid},ou=People,${org} -w ${passwd}
あまり需要がないとは思うけど折角なので公開してみる。
2014/03/25
キャピタライズ
英単語の先頭を大文字に置換する処理をキャピタライズというが、
このキャピタライズ処理を unix 標準のコマンドで実装しようという話。
置換と言えばまず思い浮かぶのが sed (1) なのだが、
GNU 拡張された sed (1) の場合は簡単に置換が可能なのだが、
FreeBSD の標準の sed (1) では簡単にいかない。
- GNU sed の場合
-
$ echo "foo" | sed 's/\(.\)\(.*\)/\U\1\L\2/g' Foo
GNU 拡張された sed (1) には \U と /L という 演算子があるので大文字、小文字変換が簡単に可能である。
- FreeBSD 標準の sed の場合
-
$ echo "foo" | sed 's/\(.\)\(.*\)/\U\1\L\2/g' UfLoo
FreeBSD 標準の sed (1) だと正しく変換されない。
- awk を利用した場合
-
sed (1) ではなく awk (1) を利用すれば
FreeBSD でもキャピタライズ処理は簡単にできる。
$ echo "foo" | awk '{ print toupper(substr($0, 1, 1)) substr($0, 2, length($0) - 1) }' Foo
awk (1) には大文字変換関数が実装されているので、 切り出した1文字目を大文字変換すれば簡単に実現できる。
- FreeBSD で sed を利用した場合
-
どうしても FreeBSD の sed (1) を利用したい場合、
tr (1) も併用する事にはなるが以下の処理で可能だ。
$ eval `echo "foo" | sed 's/\(.\)\(.*\)/\/bin\/echo -n \1 | tr "[a-z]" "[A-Z]"; echo \2/g'` Foo
sed (1) でtr (1) を利用した置換スクリプトを出力して eval (1) で実行した結果を表示しているのだが もはやネタとしか思えなくもない。
良い子は真似しないで下さい(笑
2014/03/04
C9A4 V6 650 + docomo 携帯での通信設定
CX-5 のメイカー純正カーナビゲーション ALPINE C9A4 V6 650 を
docomo の携帯電話と Bluetooth 接続して通信設定をしていたのだが、
先日、携帯電話を変更してからデータ通信が出来なくなっていた。
ナビゲーションと携帯電話は Bluetooth でペアリングが出来ていて
音声通話などは普通に利用できているのだが、
ナビゲーション側から地図データの差分更新などのデータ通信を開始すると
「接続できませんでした」というエラーが表示されてしまい
データ通信ができない状況だった。
データ通信のために接続を開始している携帯電話の画面を参照すると
mopera.ne.jp に接続しようとして切断されているのが確認できたが、
現在 docomo の携帯によるデータ通信は
mopera.net に接続する必要があるので、
以下の操作で C9A4 V6 650 に初期設定されている接続先を
mopera.net に変更する事で無事にデータ通信が可能となった。
- 画面左下の 設定 ボタンクリック
- 画面右上の 設定・編集 をタッチ
- 画面右下の Bluetooth設定 をタッチ
- Bluetooth機器設定の右にある 変更 をタッチ
- 選択リストから利用している Bluetooth 機器(携帯)をタッチ
- 画面下の 通信接続先 をタッチ
- 選択リストから NTT docomo FOMA / パケット通信 をタッチ
- 画面下の 詳細設定 をタッチ
- 電話番号の「*99***1#」を「*99***3#」に変更
なお、データ通信には大量のパケット通信が発生するので
パケット通信代が非常に高額になってしまう可能性もあるので、
十分に気をつけて設定する必要がある。
2014/02/06
OSX の pkgutil コマンド
OSX で .pkg 型式のインストーラを実行する時に
このパッケージは、ソフトウェアをインストールできるかどうかを判断するプログラムを実行します。
多くの場合は問題なくインストールできるのだが、 稀に
より新しいバージョンのソフトウェアがインストールされているのでインストールできません
的なメッセージでインストーラが終了してしまう場合がある。この様な場合、大抵は /Applications フォルダ等に存在する 該当のアプリケーションを削除するだけで 新しいアプリケーションのインストールが可能となるのだが、 ごくごく稀に削除しただけではインストーラが同じメッセージを表示してしまい インストール出来ない場合がある。
そもそも OSX の場合、アンインストーラが附属する一部のアプリケーション以外は
ファインダなどで /Applications フォルダの
アプリケーションアイコンをみ箱にドラッグするなどして削除すれば
アンインストールできるのだが、
それだけだと設定情報やインストール情報などがシステムに残ってしまう可能性がある。
ところが OSX の標準型式に準拠したインストーラからインストールされたソフトウェアは
pkgutil (1) コマンドを利用する事で
アプリケーションや設定情報などを完全に消去でき、
殆どの場合は上記インストーラによるチェックも問題なく通過できる様になる。
pkgutil (1) コマンドの基本的な利用方法
- 現在インストールされているパッケージ名の一覧を表示する。
$ pkgutil --pkgs
$ pkgutil --files パッケージ名
実行する場合は管理者権限が必要なので sudo (8) などで 管理者権限を取得する必要がある。
# pkgutil --unlink パッケージ名
実行する場合は管理者権限が必要なので sudo (8) などで 管理者権限を取得する必要がある。
# pkgutil --forget パッケージ名
pkgutil を実行する事でパッケージのインストール情報を完全に削除できるので、 アプリケーションを完全に削除したい場合は便利。
2014/02/01
ショルダーバッグ
ちょっと遅めのクリスマスプレゼント 兼 すごく遅めの誕生日プレゼントとして
ショルダーバッグを貰った。
シボ感のある上質な革を使用したシンプルで上品なショルダーバッグで、
デザインも容量も使い勝手も質感も全てが気に入っている。
たまたま遊びに行った羽田空港の国際線ターミナルのショップで見かけて、
お互いに一目で気に入った 輪怐・LIN-KUのショルダーバッグ。
普段ちょっと出かける時などに財布や携帯電話、サングラスケースなど
ちょっとしたモノを入れて気軽に持ち運べるバッグが欲しかったので、
まさにうってつけの素敵なバッグと出会えて非常に重宝している。
なかなか品質の良い皮革を使用しているとの事なので、
ちゃんと手入れをすればこの先何十年も愛用できると思う。
良い感じに使いこまれた革の表情を想像すると今から楽しみだ。
2014/01/16
爪切り
長年愛用していた爪切りの刃が欠けてしまったので、
複数の知人から絶賛されていた木屋の爪切りを購入した。
そもそも木屋というのは創業1792年の包丁などで有名な老舗らしく、
その老舗が丹精こめて作ったという爪切りの逸品らしい。
一般的に売られている爪切りはステンレス製の刃を採用しているが、
こちらの製品の刃は何と鋼製でまるで包丁の様な仕上げ。
小型ながら持った感じもずっしりと重くて、
控えめながら「道具」としての主張も忘れていない感じ。
爪を切る度にしっかりした手応えのある重さを感じる事ができ、
その度に「良い道具を使っている」という自己満足に浸れる(笑)。
今や 100円均一でも爪切りは買えるのだが、 たかだか数千円で爪を切る度にちょっとだけ良い気分になれるのは ある意味で安上がりな幸せじゃないかと思う。