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のショルダーバッグ。

Image: lin-ku.jpg

輪怐 LIN-KU

普段ちょっと出かける時などに財布や携帯電話、サングラスケースなど ちょっとしたモノを入れて気軽に持ち運べるバッグが欲しかったので、 まさにうってつけの素敵なバッグと出会えて非常に重宝している。
なかなか品質の良い皮革を使用しているとの事なので、 ちゃんと手入れをすればこの先何十年も愛用できると思う。
良い感じに使いこまれた革の表情を想像すると今から楽しみだ。

2014/01/16

爪切り

長年愛用していた爪切りの刃が欠けてしまったので、 複数の知人から絶賛されていた木屋の爪切りを購入した。
そもそも木屋というのは創業1792年の包丁などで有名な老舗らしく、 その老舗が丹精こめて作ったという爪切りの逸品らしい。
一般的に売られている爪切りはステンレス製の刃を採用しているが、 こちらの製品の刃は何と鋼製でまるで包丁の様な仕上げ。

Image: IMG_1145.JPG

梨地仕上げで KIYA のロゴ入り

小型ながら持った感じもずっしりと重くて、 控えめながら「道具」としての主張も忘れていない感じ。
爪を切る度にしっかりした手応えのある重さを感じる事ができ、 その度に「良い道具を使っている」という自己満足に浸れる(笑)。

Image: IMG_1144.JPG

やすりもしっかりしている

今や 100円均一でも爪切りは買えるのだが、 たかだか数千円で爪を切る度にちょっとだけ良い気分になれるのは ある意味で安上がりな幸せじゃないかと思う。

2013/11/23

Exif 情報から GPS の緯度/経度情報を削除する

例によって こちら のブログをネタに 他人の褌で相撲を取る記事です。
元記事では Exif 情報を操作する時に、 バイナリを直接操作するのではなく od (1) を利用してバイナリをテキスト型式に変換し、 テキスト操作した後で再度バイナリ型式に戻すという 正統的な手法でのアプローチをしています。
しかし dd (1) を利用すると シェルスクリプトからバイナリを操作できるので、 バイナリ型式の画像を直接操作してしまうというアプローチを取ってみます。

画像ファイルに格納されている Exif 情報を簡単に解析して GPS 情報を格納している領域 (GPSInfoIFD) から 緯度/経度情報だけをゼロに置換する処理となっています。

流石にシェル芸では無理なのでシェルスクリプトです。

  1#!/bin/sh
  2
  3#
  4# Endian を考慮して 4 バイト数字を取得する
  5#   一度 od(1) で 16 進数に変換しバイト並びを修正した後に 10 進数に変換する
  6#
  7#   $1: ファイル
  8#   $2: オフセット
  9#
 10get4Byte()
 11{
 12
 13    if [ ${endian} = "MM" ]
 14    then
 15        printf "%d" `dd if="${1}" bs=1 skip=${2} count=4 2> /dev/null | od -x |
 16            sed -n "s/^0000000 *\(..\)\(..\)[   ]*\(..\)\(..\)/0x\2\1\4\3/p"`
 17    else
 18        printf "%d" `dd if="${1}" bs=1 skip=${2} count=4 2> /dev/null | od -x |
 19            sed -n "s/^0000000 *\(..\)\(..\)[   ]*\(..\)\(..\)/0x\1\2\3\4/p"`
 20    fi
 21        
 22
 23}
 24
 25#
 26# Endian を考慮して 2 バイト数字を取得する
 27#   一度 od(1) で 16 進数に変換しバイト並びを修正した後に 10 進数に変換する
 28#
 29#   $1: ファイル
 30#   $2: オフセット
 31#
 32get2Byte()
 33{
 34
 35    if [ ${endian} = "MM" ]
 36    then
 37        printf "%d" `dd if="${1}" bs=1 skip=${2} count=2 2> /dev/null | od -x |
 38            sed -n "s/^0000000 *\(..\)\(..\)/0x\2\1/p"`
 39    else
 40        printf "%d" `dd if="${1}" bs=1 skip=${2} count=2 2> /dev/null | od -x |
 41            sed -n "s/^0000000 *\(..\)\(..\)/0x\1\2/p"`
 42    fi
 43
 44}
 45
 46#
 47# 任意の長さの文字列を取得する
 48#
 49#   $1: ファイル
 50#   $2: オフセット
 51#   $3: 文字列長
 52#
 53getString()
 54{
 55
 56    dd if="${1}" bs=1 skip=${2} count=${3} 2> /dev/null
 57
 58}
 59
 60#
 61# GPSInfoIFD の解析を行い緯度/経度情報をクリアする
 62#
 63#   $1: ファイル
 64#   $2: GPSInfoIFD のタグ数
 65#   $3: GPSInfoIFD のオフセット位置
 66#
 67gps()
 68{
 69
 70    local   i   j   offset  tag val
 71
 72    i=0
 73
 74    while [ ${i} -lt ${2} ]
 75    do
 76        # GPSInfoIFD のタグ情報
 77        offset=`expr ${3} + 14 + \( ${i} \* 12 \)`
 78        tag=`get2Byte "${1}" ${offset}`
 79        # タグ種類
 80        #   2: 緯度
 81        #   4: 経度
 82        if [ ${tag} -eq 2 -o ${tag} -eq 4 ]
 83        then
 84            # データの数とデータのオフセットを取得
 85            # 緯度/経度情報は値のタイプに RATIONAL(8バイト長) が指定されている
 86            num=`get4Byte "${1}" \`expr ${offset} + 4\``
 87            val=`get4Byte "${1}" \`expr ${offset} + 8\``
 88
 89            # 情報を削除するためにオフセットから 8 * データの数をゼロクリア
 90            dd if=/dev/zero of="${1}" bs=1 conv=notrunc \
 91                seek=`expr ${val} + 12` count=`expr ${num} \* 8` 2> /dev/null
 92        fi
 93        i=`expr $i + 1`
 94    done
 95
 96}
 97
 98#
 99# 0th IFD データの解析処理
100#
101#   $1: ファイル
102#   $2: 0th IFD のタグ数
103#
104ifd()
105{
106
107    local   i   offset  count   position
108
109    i=0
110
111    while [ ${i} -lt ${2} ]
112    do
113        # 0th IFD のタグ情報
114        offset=`expr 22 + \( ${i} \* 12 \)`
115        # GPSInfoIFDPointer の場合はオフセットとタグ数を取得
116        if [ `get2Byte "${1}" ${offset}` -eq 34853 ]
117        then
118            position=`get4Byte "${1}" \`expr ${offset} + 8\``
119            count=`get2Byte "${1}" \`expr ${position} + 12\``
120
121            gps "${1}" ${count} ${position}
122        fi
123        i=`expr $i + 1`
124    done
125
126}
127
128# 処理開始
129if [ "`getString "${1}" 6 4`" = "Exif" ]
130then
131    endian=`getString "${1}" 12 2`
132
133    ifd "${1}" `get2Byte "${1}" 20`
134fi
    

Image: before.jpg

処理前
Image: after.jpg

処理後
GPS 情報が削除されており緯度/経度がそれぞれゼロになっています。

2013/11/11

grep -o

少々前の話ではあるが、某ブログ にて grep -o について言及する記事が出ていた。
非常に便利そうな機能ではあるのだが、 POSIX (IEEE Std 1003.1, 2013 Edition) 準拠ではなく独自拡張の機能の様なので POSIX の範囲での実現方法を考えてみた。

例えば

$ grep -o 'href="[^"]*"'
    
$ sed -n 's/.*\(href="[^"]*"\).*/\1/p'
    
で代用可能なので、
$ cat << EOF | sed -n 's/.*\(href="[^"]*"\).*/\1/p'
<!DOCTYPE html>
<html>
    <body>
        <a href="http://aho.aho" target="_blank">あほ</a>
        <img src="aho.jpg" alt="aho">エロい画像</a>
    </body>
</html>
EOF
href="http://aho.aho"
    
という結果が得られる。
素晴らしい! POSIX 準拠なコマンドで POSIX 非準拠をぶち破ったぞ!
論破! 論破! 論破!

ただし grep (1) の -E オプションは POSIX に準拠しているが、 sed (1) の -E オプションは POSIX に定義されていないので、
$ grep -o -E '(src|href)="[^"]*"'
    
には対応できない。
FreeBSD の sed (1) など -E オプションが利用できれば
$ sed -n -E 's/.*((src|href)="[^"]*").*/\1/p'
    
とする事で対応できるのだが、
$ cat << EOF | sed -n -E 's/.*((src|href)="[^"]*").*/\1/p'
<!DOCTYPE html>
<html>
    <body>
        <a href="http://aho.aho" target="_blank">あほ</a>
        <img src="aho.jpg" alt="aho">エロい画像</a>
    </body>
</html>
EOF
href="http://aho.aho"
src="aho.jpg"
    
POSIX の範疇を逸脱した時点で grep -o を使えば良いとなってしまう。

残念っ!

2013/11/23 追記

頑張る必要は全くありません(笑)
普段は GNU の便利なオプションも使っていますし、 POSIX 準拠していないコマンドやオプションも使ってます。
純粋に楽しいからやってるだけです。

2013/10/30

Happy, so happy!


Happy Birthday to my sweet heart!
You always make me happy, Thank you for your love. Sweets are forever!

2013/10/02

国別フィルタの自動生成ツール

bsdhack.org ドメインは FreeBSD でサーバを構築しているのだが、 特定の国からのアタックや迷惑メイルが非常に多いので ipfw (8) によるフィルタリングを利用して 指定した国からのパケットを拒否する設定を自動生成するツールを作成した。
APNIC から IPアドレスの割当リストを取得して、 IPアドレスを CIDR 型式に修正した上で ipfw (8) のコマンドラインパラメタを自動生成している。
スクリプトは FreeBSD の ipfw (8) 向けだが、 出力部分を適宜修正する事で Linux の iptables (8) 向けの 設定も出力可能だと思う。

  1#!/usr/bin/perl
  2use Socket;
  3
  4# IP アドレス割当リスト取得 URL
  5$url = "http://ftp.apnic.net/stats/apnic/delegated-apnic-latest";
  6
  7# FreeBSD の ipfw (8) のルール番号
  8if(@ARGV < 1){
  9    $rule = 2000;
 10}
 11else{
 12    $rule = $ARGV[0];
 13}
 14
 15# 拒否対象の国コード
 16@country = ('KR', 'CN');
 17
 18foreach $i (@country){
 19    $country{$i} = 1;
 20}
 21
 22if(open(IN, "fetch -q -o - $url|")){
 23    # 拒否対象の国の場合は IP アドレスを保存
 24    while(<IN>){
 25        if(/^apnic\|(..)\|ipv4\|(\d+.\d+.\d+.\d)\|(\d+)/){
 26            if($country{$1}){
 27                $table{inet_aton($2)} = $3;
 28            }
 29        }
 30    }
 31    close(IN);
 32
 33    # 取得した IP 割当リストから開始アドレスと個数を取得
 34    foreach $net (sort keys %table){
 35        $addr = unpack('N', $net);
 36        $num = $table{$net};
 37        while($num == $num[0] && ($addr ^ $addr[0]) == $num){
 38            shift @addr;
 39            shift @num;
 40            $addr &= ~$num;
 41            $num <<= 1;
 42        }
 43        unshift(@addr, $addr);
 44        unshift(@num, $num);
 45    }
 46
 47    # IP アドレスと個数を CIDR 型式に変換して出力
 48    while (@addr){
 49        for($num = pop(@num), $mask = 32; $num > 1; $num >>= 1, $mask--){}
 50        $filt = inet_ntoa(pack('N', pop(@addr))) . "/$mask";
 51        # FreeBSD の ipfw(8) 向け出力
 52        print "\${fwcmd} add $rule deny ip from $filt to any in recv \${wan}\n";
 53        # Linux の iptables(8) 向け出力
 54        # print "\${iptables} -A INPUT -s $filt -j DROP\n";
 55        $rule++;
 56    }
 57}
 58
 590;
    

FreeBSD の場合は WAN 側のインタフェイス名が '${wan}' 変数に、 iptables コマンドの実行ファイル名が '${ipfw}' 変数に それぞれ格納されている事を前提としたスクリプトになっているので、 利用する際は適宜変更しながら使って下さい。

2013/09/18

main 関数の書き方

こちらの記事に触発されて、 C で同じお題のプログラム (echo (1) のサブセット)を書いてみた。

main には殆ど処理を記述せずに パラメタ解析や割り込みなどの初期設定のみをしてから、 実際のメイン処理(この場合は echo)を実行するというスタンスは同じです。
ただし、今回の例で言えば getopt(3) を利用して オプション解析を済ませた(実際に echo 出力する)パラメタ群のループは main 側で行う方が好きかな。

  1#include    <stdio.h>
  2#include    <unistd.h>
  3
  4void    echo(const char *p, int nonl)
  5{
  6
  7    /*
  8     * ここは以下の様にすると printf(3)、putchar(3) の呼び出しを
  9     * printf(3) の呼び出しのみに変更できソースもスッキリする。
 10     * 1 文字の出力を '%s' で変換する無駄が微妙に気になるのだが、
 11     * どちらかと言えば改行を出力する事の方が多いと思うので
 12     * putchar(3) の呼び出しがない方が良いのか
 13
 14     printf("%s%s", p, nonl ? "" : "\n");
 15
 16     */
 17
 18    printf("%s", p);
 19    if(!nonl)
 20        putchar('\n');
 21
 22}
 23
 24int     main(int argc, char *argv[])
 25{
 26
 27    int     i,
 28            nonl = 0;
 29
 30    while((i = getopt(argc, argv, "n")) != EOF){
 31        switch(i){
 32            case 'n':
 33                nonl = 1;
 34                break;
 35
 36            default:
 37                break;
 38        }
 39    }
 40
 41    for(i=optind; i<argc; i++)
 42        echo(argv[i], nonl);
 43
 44    exit(0);
 45
 46}
    

2013/08/02

シェルスクリプトでループ

シェルスクリプトでループ処理をする場合、 最も多用するのは while だと思う。
良く見かけるのは次の様な書き方だろう。

    :
while true
do
    処理
    if 条件
    then
        break
    fi
done
    :
    
勿論この書き方でも問題なく動作するが、 条件がコマンドの戻り値の場合は以下の方が簡潔に記述できる。
コマンドの戻り値が 0 (正常終了の場合) ループを続けたい場合。
    :
while コマンド
do
    処理
done
    :
    
コマンドの戻り値が 0 以外(正常終了以外の場合) ループを続けるのであれば while の替わりに until が利用できる。
例えば再起動中のリモートサーバに ssh (1) 接続したい場合、 以下の方法で繰り返し接続を試行する事ができる。
until ssh -o 'ConnectTimeout 5' リモートサーバ
do
    sleep 10
done
    
ssh (1) が成功するとリモートサーバから exit した時点で until の条件が真になるのでループを抜ける事ができる。

2013/07/31

MySQL データベースを Amazon S3 にバックアップするスクリプト

自宅サーバで運用している MySQL データベースのデータを Amazon S3 にバックアップするためのスクリプトを作成したので公開。

今回作成したスクリプトでは、 ローカルディレクトリに YYYYMMDD 型式で 7 世代分のデータをフルダンプし、 毎週日曜日の深夜に Amason S3 に当日分のバックアップを転送している。
mysqldump で取得したバックアップデータに VIEW 情報が含まれていると データベースのレストア時にエラーが発生する可能性がある様なので、 mysqldump 時に VIEW 定義は除外してダンプを取得し VIEW 情報のみを別途ダンプする様にしている。

Amason S3 への転送は S3 tools が公開している s3cmd を利用しているので、 s3cmdpython のインストールは必須。
当然 Amazon S3 も利用できる様にしておく必要がある。

以下のスクリプトは MySQL の接続用パスワードや Amazon S3 のアクセスキーなどを平文で保存しているので、 ファイルのアクセス権限には十分に注意が必要である。
バックアップは root 権限で実行する様にし ファイルの権限は 500 に設定しておく事が望ましい。

  1#!/bin/sh
  2
  3# MySQL 用の設定
  4database="データベース名"
  5user="-uユーザ -pパスワード"
  6
  7# Amazon S3 用の設定
  8backet="backet 名"
  9PASSPHRASE="パスフレーズ"
 10AWS_ACCESS_KEY_ID="アクセスキーID"
 11AWS_SECRET_ACCESS_KEY="SECRET アクセスキー"
 12
 13# ローカルにバックアップする世代数
 14max=7
 15# Amazon S3 に転送する曜日指定 (0: 日曜 … 6: 土曜)
 16dayofweek=0
 17
 18# コマンド定義
 19ls="/bin/ls -1"
 20wc="/usr/bin/wc -l"
 21rm="/bin/rm -r"
 22head="/usr/bin/head -1"
 23mkdir="/bin/mkdir -p"
 24
 25# ファイルとディレクトリ定義
 26logfile="/var/log/`basename ${0}`.log"          # ログファイル名
 27backupdir="/var/db/backup"                      # バックアップ先ディレクトリ
 28target="`date '+%Y/%m/%d'`"                     # Amason S3 でバックアップを格納するディレクトリ名
 29targetdir="${backupdir}/`date '+%Y%m%d'`"       # ローカルでバックアップを格納するディレクトリ名
 30backupfile="${targetdir}/all_dump.sql"          # バックアップファイル名
 31createview="${targetdir}/create_view.sql"
 32createtable="${targetdir}/create_table.sql"
 33
 34# MySQL コマンドの定義
 35mysql="/usr/bin/mysql ${socket} ${user} -B -N -s -r "
 36mysqldump="/usr/bin/mysqldump ${user}
 37        -R --hex-blob --single-transaction"
 38sql="select TABLE_NAME from information_schema.TABLES
 39        where TABLE_SCHEMA = '${database}' and TABLE_TYPE = 'VIEW';"
 40
 41# s3cmd の定義
 42s3cmd="/usr/bin/s3cmd put -rr"
 43export PASSPHRASE AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
 44
 45# ロギング開始
 46umask 700
 47exec > ${logfile} 2>&1
 48set -x
 49
 50# ローカルディレクトリの準備
 51# 最大世代数以上の場合は一番古い世代を削除する
 52test `${ls} ${backupdir} | ${wc}` -ge ${max} &&
 53    ${rm} ${backupdir}/`${ls} ${backupdir} | ${head}`
 54${mkdir} ${targetdir}
 55
 56echo "DB backup: start: `date`"
 57
 58# ダンプから除外するために VIEW を取得し create view 文をダンプする
 59for i in `${mysql} ${database} -e "${sql}"`
 60do
 61    view="${view} --ignore-table=${database}.${i}"
 62    ${mysql} ${database} -e "show create view ${i}"
 63done > ${createview}
 64
 65# 全データダンプ
 66${mysqldump} ${view} ${database} > ${backupfile}
 67
 68# create table 文のダンプ
 69${mysqldump} --no-data ${view} ${database} > ${createtable}
 70
 71# 指定された曜日の場合は Amazon S3 に転送する
 72# 以下の 1行をコメントとすると 毎日 Amazon S3 に転送する
 73test `date '+%w'` -eq ${dayofweek} &&
 74    ${s3cmd} ${targetdir}/ s3://${backet}/${target}/
 75
 76echo "DB backup: end: `date`"
    


Copyright © 2008-2020 Mitzyuki IMAIZUMI. All rights reserved.