2015年10月22日木曜日

Arch Linux に sar, vmstat コマンドをインストールする。

Arch Linux では、標準では sar コマンドも iostat コマンドもインストールされていない。

$ sar
bash: sar: command not found
$ iostat
bash: iostat: command not found

両コマンドを使えるようにするには、sysstat をインストールする。

pacman -Ss コマンドでパッケージを検索する。
$ sudo pacman -Ss sysstat
community/libsysstat 0.3.0-1
    Library to query system statistics (net, resource usage, ...)
community/sysstat 11.1.3-1
    a collection of performance monitoring tools (iostat,isag,mpstat,pidstat,sadf,sar)
pacman -S コマンドでインストール。
$ sudo pacman -S sysstat
[sudo] password for ******: 
resolving dependencies...
looking for conflicting packages...

Packages (1) sysstat-11.1.3-1

Total Download Size:   0.26 MiB
Total Installed Size:  1.40 MiB

:: Proceed with installation? [Y/n] Y
:: Retrieving packages ...
 sysstat-11.1.3-1-x86_64                                                                                              268.0 KiB   487K/s 00:01 [########################################################################################] 100%
(1/1) checking keys in keyring                                                                                                                 [########################################################################################] 100%
(1/1) checking package integrity                                                                                                               [########################################################################################] 100%
(1/1) loading package files                                                                                                                    [########################################################################################] 100%
(1/1) checking for file conflicts                                                                                                              [########################################################################################] 100%
(1/1) checking available disk space                                                                                                            [########################################################################################] 100%
(1/1) installing sysstat                                                                                                                       [########################################################################################] 100%
Optional dependencies for sysstat
    tk: to use isag
    gnuplot: to use isag

2015年8月1日土曜日

yaourt のインストール


Yaourt をインストールしたので、メモします。

Yaourt とは、AUR(Arch User Repository)のパッケージを簡単にインストールできるようになるパッケージ…という程度の認識です。

インストールには、「AURを使ってパッケージをインストールする」「archlinuxfr リポジトリを追加する」の二通りの方法があります。※参考 ArchWiki > Yaourt

リポジトリを追加するほうが簡単そうではありますが、AURの使い方も学習しておきたかったので、前者の方法でインストールを行いました。


  1. aur 用のディレクトリを作成
  2. AUR 用のディレクトリを任意の場所に作成し、中に移動する。
    $ mkdir aur
    $ cd aur

  3. package-query のPKGBUILDをダウンロード
  4. Wiki の通り、Yaourt のインストール前に package-query をインストールする。
    package-query の場所はこちら。ダウンロードのには、右側のDownload tarball のURLを入力する。



    $ wget https://aur.archlinux.org/packages/pa/package-query/package-query.tar.gz
    bash: wget: command not found
    

    wget というコマンドが見つからないと言われたので、wget をインストールする。
    $ pacman -S wget
    

    ダウンロードを再チャレンジ
    $ wget https://aur.archlinux.org/packages/pa/package-query/package-query.tar.gz
    

    ダウンロードした tar.gz ファイルを解凍する。
    $ tar -xvf package-query.tar.gz
    

    解凍すると、package-query というディレクトリができるので、中に移動する。
    $ cd package-query
    

    このディレクトリの中には PKGBUILD ファイルだけが入っている。

  5. makepkg の実行
  6. PKGBUILD ファイルの含まれるディレクトリ中で makepkg コマンドを実行すると、自動でソースコードをダウンロード、コンパイルしてくれます。パッケージの依存性を自動で解決してくれる -s コマンドを付けて実行します。

    $ makepkg -s 
    -s : 依存関係を自動で解決する

  7. makepkg の実行
  8. パッケージのインストール。
    makepkg で生成されたパッケージ *****.pkg.tar.xz を、pacman -U コマンドでインストールする。

    $ sudo pacman -U package-query-*.*.*-*-x86_64.pkg.tar.xz
    * の値はバージョンによる
    

    これで package-query のインストールは完了。Yaourt も同様にインストールする。


  9. Yaourt のインストール
  10. $ wget https://aur.archlinux.org/packages/ya/yaourt/yaourt.tar.gz
    $ tar -xvf yaourt.tar.gz
    $ cd yaourt
    $ makepkg -s
    $ sudo pacman -U yaourt-*.*-*-any.pkg.tar.xz
    

参考にさせていただいた情報

  • http://mmau-mdsp.tumblr.com/post/51963901815/yaourt
  • https://wiki.archlinuxjp.org/index.php/Yaour
  • https://wiki.archlinuxjp.org/index.php/Arch_User_Repository

2015年7月25日土曜日

Xorg 日本語キーボード設定

前回、Xorg と xfce4 をインストールし、GUI環境の構築が出来たものの、キーボード入力がおかしい。。。

どうやら設定が英語キーボードになっているようだ。

CUI環境の時は、vconsole.conf を設定して jp106 のキーマップを指定したが、Xorg 環境だと別途設定しなくてはならない。

wiki の Xorg でのキーボード設定 > X の設定ファイルを使う を参考に設定した。


/etc/X11/xorg.conf.d/ 以下に、新規ファイル 10-keyboard.conf を作成する。

$ vim /etc/X11/xorg.conf.d/10-keyboard.conf

新たに作成したファイルに、以下を入力する。

Section "InputClass"
        Identifier "system-keyboard"
        MatchIsKeyboard "on"
        Option "XkbLayout" "jp,us"
        Option "XkbModel" "jp106"
        Option "XkbVariant" ",dvorak"
        Option "XkbOptions" "grp:alt_shift_toggle"
EndSection

wiki と異なるのは、XkbModel の設定を jp106 にしたところ。
再起動後に有効になる。

2015年7月23日木曜日

Virtual Box にインストールした Arch Linux のGUI環境を構築する

1 Virtualbox GuestAdditionsのインストール


pacman -S でインストールできるので、楽チン。

$ sudo pacman -S virtualbox-guest-utils

※ xorg-server も同時にインストールされた。
※ インストールの際の選択はデフォルト(mesa-libgl)を(何も入力せずにエンター)

2 Virtualboxの関連モジュールの自動起動設定


起動時にVirtualboxの関連モジュールが自動でロードされるように設定する。

まず、virtualbox.conf を新規作成する。

$ sudo vim /etc/modules-load.d/virtualbox.conf

ファイル中に、以下の三行を追記する。
vboxguest
vboxsf
vboxvideo

xorg-server 関連パッケージのインストール


xorg-server、xorg-server-utils、xorg-xinit、xterm をインストールする。

$ sudo pacman -S xorg-server xorg-server-utils xorg-xinit xterm

※ virtualbox-guest-utils のインストール時にインストールされているので、xorg-server は省いても良い。

4 ディスプレイマネージャのインストール


ディスプレイマネージャ『lightdm』をインストールする。GUIでのログインが可能になる。

$ sudo pacman -S lightdm lightdm-gtk-greeter

※ 最初、ディスプレイマネージャには『SLiM』を使用するつもりだったが、Wiki に、活動停止中なので他のディスプレイマネージャを使うよう警告が出ている。

5 デスクトップ環境のインストール


デスクトップ環境には、xfce4 を選択した。
VirtualBox 上で動かすので、それなりに軽い環境が良いと思う。

$ sudo pacman -S xfce4 xfce4-goodies gamin

※ インストールの際の選択はデフォルト(all)

xfce4 の起動確認

$ startxfce4

6 ディスプレイマネージャの自動起動を設定


$ sudo systemctl enable lightdm.service

もし複数のデスクトップ環境をインストールしていたら、lightdm の起動画面でどれを起動するか選択できる。


以上で、GUI環境で起動できるようになった。

2015年7月12日日曜日

Virtual Box にインストールした Arch Linux の初期設定をする

前回まででインストールが終わったので、初期設定を行う。

1 rootパスワードの設定


まず、root でログインする。
パスワードが設定されていないので、起動後のログイン画面で root と入力したら即rootログインができる。

login : root

root のパスワードを設定する。

# passwd

以下、パスワードを入力する。

2 新規ユーザの作成


今回の設定では、新規ユーザをwheel グループに追加し、wheel グループに対してsudoによる管理者権限を付与することにする。

# useradd -m -G wheel 新規ユーザ名 < -m : ホームディレクトリを作成する。 -G wheel : wheel グループに追加する。

# passwd 新規ユーザ名 < 新規ユーザのパスワードを設定

次に、wheel グループに対して sudoによる管理者権限を付与する。

# visudo < /etc/sudoers を編集するためのコマンド

以下の行の先頭に記述されている"#"を削除する。
#%wheel ALL=(ALL) ALL
一旦ログアウト( # exit )して、新規ユーザでログインできることを確認する。

3 bash-completion、vim のインストール(お好みで)


bash-completion のインストール

$ sudo pacman -S bash-completion

vim のインストール

$ sudo pacman -S vim


今回は、ココまで

参考にさせて頂いたページ



Virtual Box に Arch Linux をインストールしてみる その2


前回の続き

5 マウント


最初に、/ (sda3) をマウントする。

# mount /dev/sda3 /mnt

wikiに以下の記載があるので、/mnt/boot を作成して、EFI システムパーティションとして作成したsda1をマウントする。
UEFI/GPT 環境の場合、EFI システムパーティションも /boot にマウントすることを推奨します。
# mkdir -p /mnt/boot
# mount /dev/sda1 /mnt/boot < sda1パーティションを/mnt/bootにマウントする

下記のコマンドでマウントポイントを確認できる。

# lsblk | grep -v "rom\|loop\|airoot" 

6 ミラーリストの編集


リポジトリのミラーリストを編集する。

# vi /etc/pacman.d/mirrorlist

日本の2つのミラー(jaist, tsukuba)を上に移動する。



7 ベースシステムのインストール


# pacstrap -i /mnt base base-devel

8 fstabの生成

fstab:

パーティション(など)をどうやってファイルシステムにマウントするかを定義するファイル。
イマイチ働きが実感できないが、これが無いとちゃんと起動できないはず。

# genfstab -U -p /mnt >> /mnt/etc/fstab

9 chroot して、インストールしたベースシステムに入る


# arch-chroot /mnt /bin/bash

# の前が、[root@archiso /] になる。

10 ベースシステムの設定


ロケールの設定


# vi /etc/locale.gen

以下2行のコメントアウト( # )を外す。

en_US.UTF-8 UTF-8
ja_JP.UTF-8 UTF-8
# locale-gen < ロケールの生成

=> Generation complete と表示された。

# echo LANG=en_US.UTF-8 > /etc/locale.conf < locale.confの作成 ※この時点では日本語に設定しない!!

# export LANG=en_US.UTF-8 < ロケールのエクスポート

コンソールフォントとキーマップ


再起動後もキーマップがjp106になるように、vconsole.conf ファイルを新規作成。

# vi /etc/vconsole.conf

以下の2行を追記
KEYMAP=jp106
FONT=lat9w-16

タイムゾーンの設定


# ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

ハードウェアクロックをUTCに設定


# hwclock --systohc --utc

ホスト名の設定


# echo ホスト名 > /etc/hostname

同じホスト名を /etc/hosts に追加する。

# vi /etc/hosts
127.0.0.1 localhost.localdomain localhost ホスト名
::1   localhost.localdomain localhost ホスト名

ネットワークの自動起動設定


# systemctl enable dhcpcd@インターフェイス名.service 

(インターフェイス名は # ip link コマンドで調べることができる)

11 ブートローダのインストール


gumibootのインストール


# pacman -S dosfstools efibootmgr
# pacman -S gummiboot
# gummiboot --path=/boot install

# vi /boot/loader/entries/arch.conf (新規作成)
title          Arch Linux
linux          /vmlinuz-linux
initrd         /initramfs-linux.img
options        root=/dev/sda3(← / パーティション) rw

12 再起動


# exit < chroot 環境から抜ける
# shutdown -h now

システム設定で、HDDの起動順位を最上位にして(あるいは、CD/DVDからArch Linux の起動ディスクを抜いて)起動


正常にインストール出来た!



2015年7月11日土曜日

Virtual Box に Arch Linux をインストールしてみる その1

表題の通り、Virtual Box に Arch Linux をインストールしてみたので、メモ。
wiki のビギナーズガイドに基いているが、自分の環境や設定に必要な部分だけ抜き出してみた感じ。

1 準備


新規仮想マシンを作成しておく
UEFIで使用するので、システム設定で『EFIを有効化』にチェックしておく。
システム設定で『ハードウェアクロックをUTCにする』にチェックしておく。


2 起動


EFIでブートした場合、起動方法の選択画面の後で真っ黒な画面が暫く続くが、待っていればちゃんと起動する。




3 キーボード設定


インストール中のキーボードレイアウトを、jp106にする。
れをやらないと、インストール中のコマンド入力で困る( "|"とか)。

# loadkeys jp106 


4 パーティショニング

パーティションテーブルについては、以下の通り公式wikiに説明がある。
パーティションテーブルには2種類あります:
MBR: BIOS 環境で使用されます (別名 "msdos")
GPT: UEFI 環境で使用されます
この度はUEFI環境とするので、パーティションテーブルにはGPTを用いる。

パーティションの構成


以下の通りにする。

  • EFI System Partition・・・512MB
  • スワップ領域・・・2GB(2048MB)
  • / (ルートパーティション) ・・・その他全領域

記憶デバイスの確認
# lsblk | grep -v "rom\|loop\|airoot" 
lsblk だけだと、インストールメディアの情報も出力されるので、分かり難い。

NAME            MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT 
sda                    8:0      0     24G  0  disk 


パーティショニング


partedを用いてパーティショニングを行う。

# parted /dev/sda  < partedの起動
(parted)  < #から(parted)に切り替わる

(parted) mklabel gpt < パーティションタイプをgptに指定
(parted) mkpart ESP fat32 1M 513M  < EFI System Partition を512M確保
(parted) set 1 boot on  
(parted) mkpart primary linux-swap 513M 2561M  < 2GBのswap領域
(parted) mkpart primary ext4 2561M 100%  < 残りは/に
(parted) quit 

この時点でもう一度記憶デバイスを確認すると、以下のように表示された。



ファイルシステムの作成


作成したパーティションを以下のようにフォーマットする。

# mkfs.vfat -F32 /dev/sda1  < EFIシステムパーティションは、FAT32でフォーマット
# mkswap /dev/sda2
# mkfs.ext4 /dev/sda3 < sda3はext4でフォーマット



続きは次回






2015年3月11日水曜日

【Android】【CountDownTimer】 一時停止の仕組みを改善する その②

その①に引き続き、一時停止の仕組みについて検討します。

改善策② 残り時間(millisUntilFinished)を、インターバル毎に記録する。


millisUntilFinishedは、CountDownTimerクラスのonTick()メソッドで使用される変数で、残り時間(ミリ秒)の値が格納されています。これを、一時停止時に使用出来れば良いのですが、この変数はonTick()メソッドの範囲外では使えません。

そこで、あらかじめ定義した別の変数(countMillis)にmillisUntilFinishedの値をコピーする処理を、onTick()メソッドの中に追加します。これで、インターバル時間毎に、残り時間がcountMillisに記録されます。

public void onTick(long millisUntilFinished) {
   textView.setText(String.valueOf(millisUntilFinished / 1000)); // ミリ秒→秒に変換して)残り時間を表示
      countMillis = millisUntilFinished; // 残り時間をcountMillisに代入
}

カウントダウンタイマーを再スタートする際に、countMillisを残り時間(millisInFuture)にセットしてインスタンス化すれば、ミリ秒を切り捨てずに残り時間からスタートできます。(ただし、正確性は残り時間(millisUntilFinished)を記録する頻度、つまりインターバル時間にも依存します。)

// トグルON
if (isChecked) {
   myCountDownTimer = new MyCountDownTimer(countMillis, 100); //countMillisを残り時間にセット
      myCountDownTimer.start(); // タイマーをスタート
// トグルOFF
} else {
   myCountDownTimer.cancel(); // タイマーをストップ
}

なお、countMillisは、変数を定義する際に忘れずに初期化(初期値をセット)しておきます。そうしなければ、最初にタイマーをスタートする際に、countMillisには値が何も入っていないのでエラーに成ってしまいます。
また、リセットの処理にも、countMillisに初期値をセットする処理を追加します。


思いついた処理は昨日の対応策①と合わせてこの2つです。
個人的には今回紹介した②の方が、強引さが無くて好きです。

参考までに、Javaのコード全文を掲載します。
(XMLは、『【Android】【CountDownTimer】カウントダウンタイマーを、1つのボタンで開始、一時停止、リセットする。』と同じ)


import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ToggleButton;

public class MainActivity extends ActionBarActivity {

    private static MyCountDownTimer myCountDownTimer;
    private static long countMillis = 15000; // カウントダウンの残り時間(初期値)

    // CountDownTimerクラスを継承して、MyCountDownTimerを定義
    class MyCountDownTimer extends CountDownTimer {

        // 秒を表示するテキストビュー、トグルボタンのビューを取得
        TextView textView = (TextView)findViewById(R.id.textView);
        ToggleButton toggleButton = (ToggleButton)findViewById(R.id.toggleButton);

        public MyCountDownTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        // カウントダウン処理
        @Override
        public void onTick(long millisUntilFinished) {
            textView.setText(String.valueOf(millisUntilFinished / 1000)); // ミリ秒→秒に変換して)残り時間を表示
            countMillis = millisUntilFinished; // 残り時間をcountMillisに代入
        }

        // カウントダウン終了後の処理
        @Override
        public void onFinish() {
            toggleButton.setChecked(false); // toggleボタンをオフにする
            textView.setText("0");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // トグルボタンをタップした時の処理
        ((ToggleButton)findViewById(R.id.toggleButton)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                // トグルON
                if (isChecked) {
                    myCountDownTimer = new MyCountDownTimer(countMillis, 100); //countMillisを残り時間にセット

                    myCountDownTimer.start(); // タイマーをスタート

                    // トグルOFF
                } else {
                    myCountDownTimer.cancel(); // タイマーをストップ
                }
            }
        });

        // トグルボタンをロングタップした時の処理
        ((ToggleButton)findViewById(R.id.toggleButton)).setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                myCountDownTimer.cancel(); // タイマーをストップ
                countMillis = 15000; // カウントダウン時間を初期値にリセット
                ((TextView)findViewById(R.id.textView)).setText(String.valueOf(countMillis / 1000)); // テキストビューに初期値をセット
                ((ToggleButton)findViewById(R.id.toggleButton)).setChecked(false); // toggleボタンをオフにする
                return true;
            }
        });
    }
}

(関連)
【Android】【CountDownTimer】カウントダウンタイマーを、1つのボタンで開始、一時停止、リセットする。
【Android】【CountDownTimer】 0秒まで表示する (インターバルの挙動を調べてみる)
【Android】【CountDownTimer】 一時停止の仕組みを改善する その①

2015年3月10日火曜日

【Android】【CountDownTimer】 一時停止の仕組みを改善する その①

先日の投稿で作成したタイマーアプリですが、一時停止の仕組みは以下のようになっています。

① [スタート] 初期値(ミリ秒)をセットして、CountDownTimer をインスタンス化 ⇒ スタート。
② [動作中] onTick毎にtextViewに残り時間を表示。この際、「秒」に換算して表示。
③ [一時停止] CountDownTimer をキャンセル。
④ [再スタート] textViewに表示されている時間(秒)をミリ秒に換算し、新たにCountDownTimer
   をインスタンス化 ⇒ スタート

秒 ⇔ ミリ秒 の変換は、1000 を 乗 or 除算しています。

ここで問題になるのは、一時停止の際に、 1秒以下の値が切り捨てられることです。

より具体的に説明すると、

残り時間(millisUntilFuture) 5999 ミリ秒の時、残り時間は「秒」に換算(1000で除算)されて、画面には 5 と表示されます。

この時一時停止 → 再スタートすると、新たなCountDownTimerは、画面に表示されている値 5 に 1000 を掛けて、残り時間 5000ミリ秒 でスタートします。

つまり、一時停止 → 再スタートで、最大約1秒の程度の切り捨てが発生してしまうのです。

用途にもよりますが、これはイマイチだなと思い、一時停止の仕組みを改善する事にしました。

改善策① 画面表示する単位を変更する。


大変単純な案です。切り捨ての原因になる単位の変換をしない、またはする範囲を狭めます。

例えば、ミリ秒まで画面に表示するようにすると、先ほどの5999はそのままの値で画面に記録され、そのままの値で新たな CountDownTimer に利用できます。

ミリ秒まで使わなくても、一桁、二桁でも1秒以下の単位を増やすと、その分精度が上がります。

例えば、画面上の表示を [ 5.86 ] みたいに、下2桁まで表示してストップウォッチ風にするというのも面白そうです。

どうしても秒単位で表示にしたい場合は、.86 のテキストビューだけ表示を透明にすれば良いのではないでしょうか。(ちょっと場当たり的な感じがしますが。)

ストップウォッチ風の表示にするため、以下の様にコードを変更しました。



activity_main.xml


 ・ 1/10, 1/100秒を表示するためのテキストビュー(textView2)を追加
 ・ 区切りの“.”を表示するテキストビュー(textView3)を追加
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="15"
        android:id="@+id/textView"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="00"
        android:id="@+id/textView2"
        android:layout_alignTop="@+id/textView"
        android:layout_toRightOf="@+id/textView3" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="."
        android:id="@+id/textView3"
        android:layout_centerVertical="true"
        android:layout_alignTop="@+id/textView"
        android:layout_toRightOf="@+id/textView" />

    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New ToggleButton"
        android:id="@+id/toggleButton"
        android:layout_below="@+id/textView"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

MainActivity.java


 ・ textView2に、1/10, 1/100秒の桁を表示する処理を追加
  ⇒ カウントダウンタイマーのonTick()メソッドに、次の処理を追加

    textView2.setText(String.valueOf((millisUntilFinished % 1000) / 10));

    millisUntilFinished % 1000
    ⇒ millisUntilFinished(残り時間) % 1000 を秒(1000ミリ秒)で割った余り。
       秒の桁を捨てます。
    (例) 5678 % 1000 = 678 (ミリ秒)

    さらに10で割ることで、1/1000 秒の桁を捨てています。
    (例) 678 / 10 = 67 (10ミリ秒)

 ・ カウントダウンタイマーをスタートする際、textView2からも値を取得して、残り時間をセットする
    処理を追加

  String time1 = ((TextView) findViewById(R.id.textView)).getText().toString();
  String time2 = ((TextView) findViewById(R.id.textView2)).getText().toString();
  myCountDownTimer = new MyCountDownTimer(Integer.parseInt(time1) * 1000
   + Integer.parseInt(time2) * 10, 10);

  ⇒ time1、time2 の値をミリ秒に戻して、合計した値を残り時間にセットしています。

 ・ インターバル時間を10ミリ秒に設定
  ⇒ここの値が大きいと、textView2の表示がガックガックになります。

  なお、先日投稿した様に、インターバルの処理には10~20ミリ秒の誤差があります。
  したがって、(早すぎて見えませんが)1/100 秒の桁はところどころ数字が抜けていると
  思われます。ストップウォッチの精度としては、1/10 秒までが限界かと思います。

 ・ タイマー終了時に、textView1、textView2にそれぞれ"0", "00" をセット

import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ToggleButton;

public class MainActivity extends ActionBarActivity {

    static MyCountDownTimer myCountDownTimer;

    // CountDownTimerクラスを継承して、MyCountDownTimerを定義
    class MyCountDownTimer extends CountDownTimer {

        TextView textView = (TextView)findViewById(R.id.textView);
        TextView textView2 = (TextView)findViewById(R.id.textView2);
        ToggleButton toggleButton = (ToggleButton)findViewById(R.id.toggleButton);

        public MyCountDownTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        // カウントダウン処理
        @Override
        public void onTick(long millisUntilFinished) {
            textView.setText(String.valueOf(millisUntilFinished / 1000)); // 秒の桁を表示
            textView2.setText(String.valueOf((millisUntilFinished % 1000) / 10)); // 10,100分の1秒の桁を表示
        }

        // カウントダウン終了後の処理
        @Override
        public void onFinish() {
            toggleButton.setChecked(false); // toggleボタンをオフにする
            ((TextView)findViewById(R.id.textView)).setText("0");
            ((TextView)findViewById(R.id.textView2)).setText("00"); // テキストビューに00を表示
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // トグルボタンをタップした時の処理
        ((ToggleButton)findViewById(R.id.toggleButton)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                // トグルON
                if (isChecked) {
                    String time1 = ((TextView) findViewById(R.id.textView)).getText().toString(); // textViewの文字列を取得して、timeに格納
                    String time2 = ((TextView) findViewById(R.id.textView2)).getText().toString(); // textView2の文字列を取得して、timeに格納
                    myCountDownTimer = new MyCountDownTimer(Integer.parseInt(time1) * 1000 + Integer.parseInt(time2) * 10, 10); // time1,time2をミリ秒換算して合計し,その値をセットしたmyCountDownTimerをインスタンス化
                    myCountDownTimer.start(); // タイマーをスタート

                    // トグルOFF
                } else {
                    myCountDownTimer.cancel(); // タイマーをストップ
                }
            }
        });

        // トグルボタンをロングタップした時の処理
        ((ToggleButton)findViewById(R.id.toggleButton)).setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                myCountDownTimer.cancel(); // タイマーをストップ
                ((TextView)findViewById(R.id.textView)).setText("15"); // テキストビューに初期値をセット
                ((TextView)findViewById(R.id.textView2)).setText("00"); // テキストビューに初期値をセット
                ((ToggleButton)findViewById(R.id.toggleButton)).setChecked(false); // toggleボタンをオフにする
                return true;
            }
        });
    }
}
(関連)
【Android】【CountDownTimer】カウントダウンタイマーを、1つのボタンで開始、一時停止、リセットする。
【Android】【CountDownTimer】 0秒まで表示する (インターバルの挙動を調べてみる)
【Android】【CountDownTimer】 一時停止の仕組みを改善する その②

【Android】【CountDownTimer】 0秒まで表示する (インターバルの挙動を調べてみる)

先日、CountDownTimer を用いたプログラムをつくってみました。

CountDownTimer クラスの基本的な使い方としては

CountDownTimer myCountDownTimer
               = new CowntDownTimer(long millisInFuture, long countDownInterval)

上記の様にインスタンス化の際に、カウントダウン時間(millisInFuture)、インターバル時間(countDownInterval)をミリ秒で渡します。

インターバル時間は、onTick()メソッドに記載した処理が行われる間隔になります。

秒単位のタイマーであれば、残り時間の表示を1秒に1回書き換えればいいので、インターバル時間は、1秒 = 1000ミリ秒の指定で問題ないように思われます。

しかし、実際に1000ミリ秒を指定すると、タイマー終了時の時間表示が“1”で止まってしまいます。

何故なのか解明するため、ログをとってみました。


ログの取り方

onTick()メソッド内に、下記の行を追加します。
onTick()毎に、残り時間がログに出力されます。

Log.i("MainActivity", "残り時間 = " + String.valueOf(millisUntilFinished));

インターバル時間を、1000, 100, 10, 1 と変えて、試してみました。

① 1000


② 100


③ 10


④ 1



これらから、以下の様に推測しました。

  • インターバルは、ミリ秒単位で正確に刻まれている訳ではなく、誤差がある。(1000, 100 ミリ秒インターバルでは、1回毎に10ミリ秒程度、10, 1 ミリ秒インターバルでは、1回毎に10~20ミリ秒程度)
  • 最後の1回の処理は飛ばされている?(④の1ミリ秒を除けば、もう1回処理されても良さそうな時間が残っているのに、されていない。)

これによって起こりそうな問題は、以下の2つが考えられます。


① 誤差による処理の抜け落ち

例えば、秒単位で時間を表示する場合、インターバルが1000ミリ秒だと、
1005 → 895 のようにonTickが刻まれてしまう可能性があり、その際は秒数の表示が 10 → 8 となり、9秒が抜け落ちる。

② 最後の秒(0秒)が表示されない

冒頭の通り。

これを防ぐためには、


 → 誤差を考慮に入れて、短めのインターバル時間を設定する。
 → これも考慮に入れて、さらに短めのインターバル時間を表示する...というのも手ですが、最後に表示する値は、onFinish()メソッドに記載する、とした方が、ムダが無く確実な気がします。

先日のカウントダウンタイマーの場合、以下の様にonFinish()内に、textViewに“0”を表示する処理を追記しました。
 
    // カウントダウン終了後の処理
        @Override
        public void onFinish() {
            toggleButton.setChecked(false); // toggleボタンをオフにする
            textView.setText("0"); // 0を表示
        }



2015年3月8日日曜日

【Postfix】【Dovecot】メールサーバにSMTP-AUTH(送信メール認証)を実装する

先日、クイックセットアップについて記載しました。

メールサーバのクイックセットアップ①
メールサーバのクイックセットアップ②

ここから1つずつ認証や暗号化等の設定を追加していきます。

なお、主な環境は以下の通りです。

OS : CentOS6.5
MTA : postfix2.6.6
MRA : dovecot2.0.9

まずは、SMTP-AUTH(送信メール認証)機能を追加します。

※2016/05/06 一部加筆

設定の前に、SMTP-AUTHについて、(自分の頭の整理のために)ざっと説明します。

  • もともとメールシステムは、SMTPサーバを使って、誰でも匿名で、自由にメールを送信することができた。(そもそもコンピュータを使用する人自体が極端に限られていたので、悪意を持ったメールを送信する事自体が想定されていなかった。)
  • コンピュータの普及に伴いスパムメール送信や、不正中継の悪用等が増え、対策が必要になった。
  • その対策の1つとして認証機能があり、メールを送信するためには、「ユーザ名」「パスワード」を用いてログインすることが求められる。
  • サーバ管理者の立場からすると、第三者が自分のメールサーバを不正に使用することを防止できる。

以下、具体的な設定方法に移ります。

まず、SMTP-AUTHの実装には、認証システムを提供するライブラリ=SASLが必要となります。SASLにはCyrus-SASLとDovecot-SASLがありますが、ここではDovecot-SASLを用います。

Dovecot-SASLを用いるので、Postfixだけでなく、Dovecotについても、設定の変更が必要になります。


① Postfixの設定


1.PostfixがDovecot-SASLライブラリに対応しているか確認
  # postconf -a
  (Dovecot と返ってくればOK)

2./etc/postfix/main.cf の末尾に、次のパラメータを追記
 ・smtpd_sasl_type = dovecot
  ⇒ SASLプラグインタイプの設定

 ・smtpd_sasl_auth_enable = yes
  ⇒ SASL認証を有効にする

 ・smtpd_sasl_path = private/auth
  ⇒ 認証ソケットファイルの場所(/var/spool/postfixからの相対パス)。
    ソケットファイルはDovecotを再起動すると自動で作成される。

 ・smtpd_recipient_restrictions = permit_mynetworks,
                      permit_sasl_authenticated,
                      reject_unauth_destination
  ⇒ 認証を通過すればリレーを許可する設定を追記
    mynetworks 以外のネットワークからも、メール送信が可能になる。

以下2つはオプションで。

 ・broken_sasl_auth_clients = yes
  ⇒ SASL認証で[AUTH=PLAIN]を使っているクライアントソフト(古いOutlook等)
     での認証を可能とする。

 ・smtpd_sasl_authenticated_header = yes
  ⇒ Received:フィールドに認証されたユーザ名を表示

3.Postfixの設定を読み込む。
 # postfix reload


② Dovecotの設定


1./etc/dovecot/conf.d/10-auth.conf
 ・auth mechanisms = plain login
  ⇒ 平文パスワードを利用する。

2./etc/dovecot/conf.d/10-master.conf
 ・unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}




3.Dovecotを再起動
 # service dovecot restart


③ telnet でログインテストしてみる。


1.準備
 ログインには、ユーザ名、パスワードをBase64でエンコードした文字列を用いる。
 下記の様にをBase64でエンコードする。
$ perl -MMIME::Base64 -e 'print encode_base64("ユーザ名\0ユーザ名\0パスワード”);’
 エンコードされた文字列が出力される。

2.telnet で25番ポートに接続

 telnet localhost 25

 Trying ::1...

 Connected to localhost.

 Escape character is '^]'.

 220 mail.○○○○.mydns.jp ESMTP Postfix

 EHLO mail.○○○○.mydns.jp  (← 『EHLO ホスト名』を入力)

 250-mail.○○○○.mydns.jp
 250-PIPELINING
 250-SIZE 10240000
 250-VRFY
 250-ETRN
 250-AUTH PLAIN
 250-AUTH=PLAIN
 250-ENHANCEDSTATUSCODES
 250-8BITMIME
 250 DSN
 AUTH PLAIN エンコードされた文字列 (←AUTH PLAIN に続いてエンコードされた文字列を入力)
 235 2.7.0 Authentication successful (←ログイン成功!)
 QUIT
 221 2.0.0 Bye
 Connection closed by foreign host.

※手順通りにやっても正常にログインできない事象が発生。パスワードが数字から始まるとダメみたい。
(参考:postfixでsmtpログイン出来ない)
但し、ThunderBirdからは正常にログイン&メール送信が可能。

④ ThunderBird で確認してみる。


  送信サーバの設定をして、認証方式を『平文のパスワード認証』に設定する。


 メール送信時にパスワードが求められるようになった。


 (パスワードマネージャにパスワードが保存されていたら、入力は求められない。
  テストしたい場合はパスワードマネージャから一旦削除する。)

 また、クイックセットアップの段階では“Relay access denied” で、メール送信に失敗
 していた場合でも、smtpd_recipient_restrictions に permit_sasl_authenticated が
 追加されたため、メール送信可能に。

2015年3月7日土曜日

【AndroidStudio】Tip of the Day を紹介してみる 第1回

Android Studio 起動時に、Tips of the Day という使用に関する小ネタみたいなものが表示されます。読むのも面倒くさいので、さっさと閉じてしまうか、いっそ起動時に表示しないように設定してしまうのが常だったのですが、何となく気まぐれでレビューしてみる事にしました。

いつまで続くか分かりませんが、『Tip of the Day を紹介してみる 第1回 』です。

第1回は、Shift + F6 です。


ざっと翻訳すると、

  • クラス、メソッド、変数を、使われている箇所全て一括でリネームできるよ。
  • リネームしたい箇所にポインタを置いて、Shift + F6 を押して。出てきたポップアップウィンドウに新しい名前を入力するか、提示された名前の1つを選んでエンターを押してね。

くらいでしょうか。

早速、カウントダウンタイマーをつくった時のコードで試してみます。


1 変数 myCountDownTimer の1つにポインタを合わせて、Shift + F6 を押します。


  リネーム候補が出てきました。
  選択してエンターキーを押すことで、リネームできます。


2 もう一度Shift + F6 を押します。


  ポップアップウィンドウが表示され、自由にリネームできます。


3 試しに myCDT にリネームします。



  全ての myCountDownTimer がmyCDTにリネームされています。
  (上の画面内でも4箇所リネームされている事が確認できます。)


因みに右クリック → Refactor → Rename でも同様の操作ができます。
Shift + F6 の方が、いちいちカーソルを動かさなくていいので、楽ちんでエレガントですね。




2015年3月6日金曜日

【Postfix】【myDNS】Gmail宛のメールが550, "5.7.1"エラーで届かない場合の対処

自サーバーのPostfixを使ってメール送信すると、gmailアドレス宛のメールが受信拒否されてしまいました。

エラーの内容は、こちらです。
550, "5.7.1", Our system has detected that this message is likely unsolicited mail. To reduce the amount of spam sent to Gmail, this message has been blocked. For more information, review this article.(550, "5.7.1", このメッセージはシステムによって未承諾メールであると検出されました。Gmail に送信されるスパムの量を減らすために、このメッセージはブロックされています。詳細については、こちらの記事をご覧ください。)
google SMTP エラー リファレンス から引用
ちょっとググってみたところ、SPFレコードが登録されていない事が原因の様です。
参考(Postfixから送信したメールがGmailに届かないGmail相手に迷惑メール扱いされる場合(SPFレコードのIPv6対応)

myDNSを使っていれば、SPFレコードは自動生成されます。
では、何故届かなかったかというと、私がAレコードしか登録しておらず、かつ両サーバがIPv6に対応していたのが原因の様です。

MyDNSでは、

Aレコード登録 → IPv4のSPFレコード
AAAAレコード登録 → IPv6のSPFレコード

が、自動生成されるみたいです。

また、両サーバがIPv6に対応してる状態では、IPv6のアドレスで処理されるらしいので、Aレコードしか登録していない場合は、SPFレコードが無い状態になってしまいます。

解決方法としては、こちら側のサーバをIPv6を使わない設定(inet_protocols = ipv4)にするか、
MyDNSにAAAAレコードも設定して、IPv6のSPFレコードについても自動生成してもらえばOKです。


私の環境では、これで正常に届くようになりました。


2015年3月5日木曜日

【CentOS6.5】【Postfix】【Dovecot】メールサーバのクイックセットアップ②

その①から続いて、

③ Dovecotの設定


/etc/dovecot/conf.d/10-mail.conf

     mail_location = maildir:~/Maildir

       (/etc/postfix/main.cfの設定がMailboxだった場合は、
    mail_location = mbox:~/mail:INBOX=/var/mail/%u
         その場合、/var/mail/%uのアクセス権限を変更する必要あり)

/etc/dovecot/conf.d/10-auth.conf

     disable_plaintext_auth = no

       平文でのパスワード認証を禁止するか設定
       テストの段階では、暗号化はしないのでとりあえずnoに設定

Dovecot を起動(既に動いていれば、再起動)する。
# service dovecot start

④ telnetで接続して動作確認してみる。


Dovecot(POPサーバ)への接続

→ ログイン&受信メールの確認をしてみる。
→ 予め『testuser』というユーザ用のアカウントを作成し、パスワードも付与。
  # useradd testuser
    # passwd testuser

 $ telnet localhost 110
 Trying ::1...
 Connected to localhost.
 Escape character is '^]'.
 +OK Dovecot ready.
 USER testuser  (←『 USER サーバのユーザアカウント』 を入力)
 +OK
 PASS xxxxxx  (←『 PASS アカウントのパスワード』 を入力)
 +OK Logged in.
 LIST (←受け取ったメールを確認する)
 +OK 3 messages: (← 予めメールを送っておけば、届いているのが確認できる)
 1 1060 (← 3通のメールが届いている)
 2 1113
 3 1852.
 TOP 1 (←1番目のメールのヘッダを表示)
 (省略)
 RETR 1 (←1番目のメールのボディを表示)
 (省略)
 QUIT  (← telnet から抜ける。)
 +OK Logging out.
 Connection closed by foreign host.

Postfix(SMTPサーバ) への接続

→ メールを送信してみる。

 $ telnet localhost 25
 Trying ::1...
 Connected to localhost.
 Escape character is '^]'.
 220 mail.xxx.mydns.jp ESMTP Postfix
 EHLO mail.xxx.mydns.jp (← 『EHLO ホスト名』を入力)
 250-mail.xxx.mydns.jp
 250-PIPELINING
 250-SIZE 10240000
 250-VRFY
 250-ETRN
 250-ENHANCEDSTATUSCODES
 250-8BITMIME
 250 DSN
 MAIL FROM:<testuser@xxx.mydns.jp> (← 『MAIL FROM:<送信元アドレス>』を入力) 
 250 2.1.0 Ok
 RCPT TO:<送信先のメールアドレス> (← 『RCPT TO:<送信先のメールアドレス>』を入力)
 250 2.1.5 Ok
 DATA
 354 End data with <CR><LF>.<CR><LF>
 Test (←メール本文を入力する)
 . (←メール本文を終了する)
 250 2.0.0 Ok: queued as 860ADBF609
 QUIT  (← telnet から抜ける。)
 221 2.0.0 Bye
 Connection closed by foreign host.

⑤ MUA(Thunderbird)を使って送受信してみる。


ツール > アカウント設定 > アカウント操作 > メールアカウントを追加

メールアドレスは、『 ユーザアカウント@mydomain 』
パスワードは、アカウントのパスワード


続けるを押下した後、手動設定


サーバのホスト名等を入力して、再テスト → 完了


セキュリティ警告が出てくるが、取り敢えず了解しておく。

試しに他のメールアカウントと送受信してみる。 

受信は問題なく行えるはずだが、送信に関しては、私の様にVPSを使って外部のネットワーク
から接続している場合、“Relay access denied” となって、送信に失敗する。

これは、/etc/postfix/main.cf がデフォルトで

smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination
(この設定はmain.cfには記入されていないが、postconf -d コマンドで確認できる)

と設定されており、外部ネットワークからのリレーを許可しないからだ。
送信メール認証の設定後、下記の様に設定変更すれば、送信可能になる。

smtpd_recipient_restrictions = permit_mynetworks,
                                                  permit_sasl_authenticated,
                                                  (認証を通過すればリレーを許可)
                                                  reject_unauth_destination

※VPSではなく自宅のサーバでも同様の事象が起こったので、上記の説明は誤っている可能性が高い。但し、自宅サーバでも認証設定後には正常に送信できた。(2016/05/06追記)

この段階で、どうしても送信テストしたい場合は、main.cf のmynetworks に、MUAを使うクライアントのIPアドレスを追加してやれば送信できる。ただし、ダイナミックIPアドレスの場合、IPアドレスが変わってしまうので、一時的なテストに留めておいた方が良いと思う。

最小限の設定は以上。
ここから、暗号化や認証等の設定を追加していく。

【CentOS6.5】【Postfix】【Dovecot】メールサーバのクイックセットアップ①

メールサーバの再セットアップも、ひと通り終わったので、設定についてメモしておく事にした。

(※2016/05/06 一部加筆)


まずは最小限の設定で、『これだけやっておけば外部とメールが送受信できる』というところまで、
設定する。

ここまでできれば、ネットワークやiptables等の、メールソフト以外の設定には問題ない事が分かるので、後ほど認証や暗号化等の設定を追加した際にも、問題の切り分けがしやすい、はず。

環境は以下の通り。

OS : CentOS6.5
MTA : postfix2.6.6
MRA : dovecot2.0.9
MUA : Thunderbird
DNS : myDNS

ドメイン名(例) = xxx.mydns.jp (xxxは伏せ字
ホスト名(例) = mail.xxx.mydns.jp (xxxは伏せ字


  • 名前解決にはダイナミックDNSサービスの MyDNS を使っているので、下記のMXレコードの設定前に、登録と初期設定を行っておく必要がある。
  • また、ポート開放についてもここでの説明は割愛しているが、適宜サーバのiptablesやファイアウォールの設定を事前に行う必要がある。


① myDNSの設定


MXレコードとAレコードに、メールサーバのホスト名を入力する。
(他に、IPアドレスの設定は事前にやっておく)

※MXレコードの欄は、FQDNで登録しないとうまく動作しなかった。



② postfixの設定

postfixの設定ファイル 『/etc/postfix/main.cf』 の下記項目を編集する。
なお、デフォルト値はpostconf -d で確認できる。

myhostname = mail.xxx.mydns.jp

        メールサーバのホスト名(FQDN)を記入。
        DNSのMXレコード、Aレコードの設定と一致させる。

      myorigin = $mydomain (行頭の"#(コメントアウト)"を外す)

        送信メールに付加される送信元アドレスの@以降の形式を指定
        (myhostname か mydomain)。
        mydomainに設定することで、送信元メールアドレスは、foo@xxx.mydns.jpになる。

inet_interfaces = all

        外部のメールサーバから送られてくるメールを受け取る。

mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain

        他のメールサーバに転送を行わない(=このメールサーバが受信する)ホスト、ドメイン
        を指定する。$mydomain を含めておくことで、foo@xxx.mydns.jp宛のメールを受信できる。

      home_mailbox = Maildir/ (または Mailbox)を指定

        メールを保存する場所を設定。
        Mailbox 形式は、全てのメールを1つのファイルに保存する。
        Maildir/ 形式は、1つのメール=1つのファイルになる。パフォーマンスやセキュリティ等、
        Maildir/ の方が優れている、らしい。

         Mailbox を指定した場合は、さらにメールスプールディレクトリを指定する。
         → (mail_spool_directory = /var/mail または /var/spool/mail)

   ※Maildir/ ディレクトリは、各ユーザのホームディレクトリ配下に自動で生成される。   

以下は基本的にはデフォルト値のままでもいいが、場合によっては変更する。

      mydomain = xxx.mydns.jp

        何も指定しなければ、myhostnameの最初の".(ドット)"までが削除された値が指定される。
        myhostname = mail.xxx.mydns.jp であれば自動的に mydomain = xxx.mydns.jp となる。
        (ホスト名がaaa.bbb.xxx.mydns.jp のような場合は変更する)
      

      mynetworks_style = subnet

      mynetworks = aaa.bbb.ccc.0/dd(自ホストのネットワーク), 127.0.0.0/8(自ホスト)

        メールサーバを利用可能なクライアントのネットワークを設定。
        上記は両者とも自ホストと、自ホストのネットワークを指定している。
        両方記載した場合には、mynetworks が優先される。
        (自ホストのネットワークを明示したい場合には、mynetworksに記載する。)



inet_interfaces を変更した際はreloadではなく再起動が必要。
main.cf の編集後、忘れずにpostfixを再起動させる。

# service postfix restart

長くなってきたので、次回に続く

2015年3月3日火曜日

【CentOS6.5】@myhostname宛のメールは受信できるが、@mydomain宛のメールが受信できない その②

昨日の続きです。

怪しそうな設定箇所を見なおしても特に問題なく、Postfixのログをみて、ググったりしても良く分からなかった(受け取り拒否しているような痕跡もない)。

「ネットワークの設定に問題があるのかな?」と思いネットワークの設定ファイルを少しづつ変えてトライし続ける。起こっている事象から原因を特定できればいいのだが、私の様な未熟者は、取り敢えず手当たり次第に引っ掻き回して、上手くいったら、そこから理由を逆算するしかない。

私が使っているVPS 『ConoHa』は、スナップショット機能があるので、変な設定になっても一発で元に戻せる。こういう実験をする時には便利だ。

/etc/sysconfig/network
/etc/sysconfig/network-scripts/ifcfg-eth
/etc/resolv.confi

あたりを書いたり消したり、、、

挙動は変わらない。

やっぱり何かDNSの設定がおかしいのかも?と思い、myDNSの設定を変えてトライしてみる事にした。

DOMAIN INFO

myDNSの設定項目は上の通りだ。

私の設定では

Domain:hoge.mydns.jp(hogeは適当な文字列)
MXレコード : mail(メールサーバのホスト名)
Aレコード : server(サーバのホスト名)
Aレコード : www(ウェブサーバのホスト名)
Aレコード : mail(メールサーバのホスト名)

としていた。

ここで、

MXレコードをホスト名でなく、FQDNで指定してみた。 ⇒ @mydomainで届く。解決!
MXレコードを未指定(自動補完)にしてみた。 ⇒ @mydomainで届く。解決!

という訳で、MXレコードの記載が問題だったようだ。

具体的には、MXレコードにホスト名を記載すると、ホスト名の後にドメインが自動補完されると思っていたが、実際にはされていなかった?ようだ。

と、いうわけで、解決法は、MXレコードには、FQDNで記載するという事になる。

しかしmyDNSの設定について解説しているサイトでは、ホスト名だけの指定で解説されているし、設定項目の説明にも"hostname"と書かれている。私自身、以前はホスト名だけの指定で問題なかった。

何か変更があったのか、他の人にも起こっている事なのか、いつからそうなのか、謎である。

=====

ここから先は、自分の頭の整理のために、今回のトラブルの際に、名前解決で何が起こっていたのかを推測して書いていく。

ケース1 foo@hoge.mydns.jp 宛の場合


① foo@hoge.mydns.jp 宛にメールを送る。

② DNSサーバがMXレコードを参照して、メールサーバのFQDNを調べる。

 ③-A MXレコードに[mail.hoge.mydns.jp]と記載していた場合

  Aレコードを参照して、ホスト名がmail=FQDNが[mail.hoge.mydns.jp]が見つかる。

  Aレコードに記録されている[mail.hoge.mydns.jp]のIPアドレスへ配送する。

 ③-B MXレコードに[mail]と記載していた場合

  Aレコードを参照するが、FQDNが[mail]は見つからないので、配送できない。


ケース2 foo@mail.hoge.mydns.jp 宛の場合


① foo@mail.hoge.mydns.jp 宛にメールを送る。

② DNSサーバがMXレコードを参照して、メールサーバのFQDNを調べる。

 ③-A MXレコードに[mail.hoge.mydns.jp]と記載していた場合

  ケース1と同じ

 ③-B MXレコードに[mail]と記載していた場合

  Aレコードを参照するが、FQDNが[mail]は見つからないので、配送できない。

④ MXレコードからの参照とは別に、Aレコードを参照。
  @以下(mail.hoge.mydns.jp)と一致するAレコードが見つかり、指定のIPアドレスへ配送される。


ケース3 MXレコードを未指定(自動補完)の場合

① MXレコードには、ドメイン名(hoge.mydns.jp)が記述される。

② ドメイン名は、IPアドレスに紐付けられている。

② foo@hoge.mydns.jp宛のメールは、ドメイン名と紐付けられているIPアドレスへ配送される。
 (Aレコード未記載でも届くので、Aレコードは参照されていない?)
 (foo@XXX.hoge.mydns.jp 宛のメールは、Aレコードにホスト名XXXが記録されていれば届く。)



あくまで推測です。













【CentOS6.5】@myhostname宛のメールは受信できるが、@mydomain宛のメールが受信できない その①

現在、メールサーバの再構築中。

昨年夏にメールサーバをつくった時のメモ書きが残っているので、それに従えば概ねうまくいっている。

それでも、思いがけないトラブルが現れるもので、表題の通り

@myhostname宛のメールは受信できるが、@mydomain宛のメールが受信できない

という状況に陥った。

詳しい状況は次のとおりだ。

使っているソフトは、
postfix : 2.6.6
dovecot : 2.0.9

ドメイン名についてはmydnsを使っていて、下記の例の用に設定している。

domain : hoge.mydns.jp
hostname : mail.hoge.mydns.jp

既存の他のメールアドレスから新規メールを送信したとき、

foo@mail.hoge.mydns.jp ⇒ 届く ◯
foo@hoge.mydns.jp ⇒ 届かない ✕

という状況だ。
この状況で一番考えられそうなのは、

postfix の /etc/postfix/main.cf 内の設定項目 mydestination の設定を間違っていることだ。

しかし、

mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain

としていて、この設定には問題無い。

送信元の MAILER-DAEMON から返ってきたメッセージを見てみる。

(一部抜粋)
=====
<foo@hoge.mydns.jp>: host mail[xxx.xxx.xxx.xxx] said: 554 5.4.0
    Error: too many hops (in reply to end of DATA command)
=====

中継が多すぎるとの事で、ちゃんと配送されずに、どっかで堂々巡りになっている?ようだ。

mydns の設定がまずくて、ちゃんと名前解決されていないという可能性も考えたが、mydnsの設定はややこしいものではないし、前回、上手くいっていた時と同様に設定できたいたので、サーバー内のソフトやネットワークの設定ファイルの見直しにとりかかった。

そしてドハマりした。

続く




2015年3月1日日曜日

【Android】【CountDownTimer】カウントダウンタイマーを、1つのボタンで開始、一時停止、リセットする。

現在タイマーを使ったアプリを作成している。

CountDownTimer というクラスが用意されているので、それを使えばタイマー作成には結構便利。

CountDownTimer は、そのままだと一時停止が使えないので、一時停止するにはちょっと工夫が必要だ。そのやり方については、こちらの記事を参考にさせて頂いた。

ただ、参考元のやり方そのままだと、ボタンが増えて画面がちょっと煩雑になってしまう。

ストップウォッチ感覚で直感的に操作できるようにしたいので、スタート、ストップは同じボタンにしたい。

Androidには標準で『トグルボタン』という、ON/OFFを切り替えられる便利なボタンがあるので、それを利用してタイマーを自分なりに再作成してみた。



トグルボタンは、AndroidStudioのパレットからドラッグ・アンド・ドロップでXML上に配置できる。


配置したトグルボタンは、JavaでOnCheckedChangeListener に関連付け、下記の様に、if (isChecked) - else で分けて、ON/OFF時の処理をそれぞれ記載する。

        toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                
                if (isChecked) {

                    // トグルボタンがONの際の処理を記述
                    
                } else {

                    // トグルボタンがOFFの際の処理を記述

                }
            }
        });

今回のタイマーでは、

[ON] タイマーをインスタンス化 → スタート
[OFF] タイマーをストップ

という処理を記述した。

ついでに setOnLongClickListener で、トグルボタンをロングクリックした時にタイマーをリセットするように設定した。
ロングクリックでリセットする際には、トグルボタンをOFFに変更する。そうしないと、タイマーはリセットされて動いていないのに、トグルがONのままという状況が発生してしまう。

以下、ソースコード全体

MainActivity.java
import android.os.CountDownTimer;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ToggleButton;

public class MainActivity extends ActionBarActivity {

    static MyCountDownTimer myCountDownTimer;

    // CountDownTimerクラスを継承して、MyCountDownTimerを定義
    class MyCountDownTimer extends CountDownTimer {

        TextView textView = (TextView)findViewById(R.id.textView);
        ToggleButton toggleButton = (ToggleButton)findViewById(R.id.toggleButton);

        public MyCountDownTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        // カウントダウン処理
        @Override
        public void onTick(long millisUntilFinished) {
            textView.setText(String.valueOf(millisUntilFinished / 1000)); // ミリ秒 → 秒に変換して)残り時間を表示
        }

        // カウントダウン終了後の処理
        @Override
        public void onFinish() {
            toggleButton.setChecked(false); // toggleボタンをオフにする
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // トグルボタンをタップした時の処理
        ((ToggleButton)findViewById(R.id.toggleButton)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                // トグルON
                if (isChecked) {
                    String time = ((TextView) findViewById(R.id.textView)).getText().toString(); // テキストビューの文字列を取得して、timeに格納
                    myCountDownTimer = new MyCountDownTimer(Integer.parseInt(time) * 1000, 100); // timeをint型に変換した後、ミリ秒に変換。その時間をセットしたmyCountDownTimerをインスタンス化
                    myCountDownTimer.start(); // タイマーをスタート

                    // トグルOFF
                } else {
                    myCountDownTimer.cancel(); // タイマーをストップ
                }
            }
        });

        // トグルボタンをロングタップした時の処理
        ((ToggleButton)findViewById(R.id.toggleButton)).setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                myCountDownTimer.cancel(); // タイマーをストップ
                ((TextView)findViewById(R.id.textView)).setText("15"); // テキストビューに初期値をセット
                ((ToggleButton)findViewById(R.id.toggleButton)).setChecked(false); // toggleボタンをオフにする
                return true;
            }
        });
    }
}

activity_main.xml
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="15"
        android:id="@+id/textView"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New ToggleButton"
        android:id="@+id/toggleButton"
        android:layout_below="@+id/textView"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

如何でしょうか。


(関連)
2015/3/10 【Android】【CountDownTimer】 0秒まで表示する (インターバルの挙動を調べてみる)
2015/3/10 【Android】【CountDownTimer】 一時停止の仕組みを改善する その①
2015/3/11 【Android】【CountDownTimer】 一時停止の仕組みを改善する その②

2015年2月28日土曜日

【SyntaxHighlighter】ブログ記事中のコードを分かり易く表示する

ブログを書く際、コードや設定ファイルを記載する事があるが、本文と同様の書式で書いているので、イマイチ見栄えもしないし、読み難い。

他のブログ等ではカッコよく、分かり易く表示されているので、自分も見習ってみたいと思った。
ちょいと調べてみたところ、Syntax Highlighter というのを使うのが良さそうだ。

導入に当たってはこちらの記事(Blogger に Syntax Highlighter を導入する)を大いに参考にさせて頂いた。

一点、若干つまずいたところは、Bloggerのプレビュー画面では、表示が反映されないというところだ。

公開状態にすれば、問題なく表示されるようだ。

試しに、昨日ブログに掲載したコード(+α)を再掲してみる。


[ボタンをタップする毎に、画面の数字を0から1つずつ増やしていくプログラム]

MainActivity.java
  public class MainActivity extends ActionBarActivity {

      private static int i = 0;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);

          final TextView textView = (TextView) findViewById(R.id.textView);
          final Button button = (Button) findViewById(R.id.button);

          // ボタンをクリックした際の処理
          button.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  i++; // i を1増やす
                  textView.setText(String.valueOf(i)); // i の値を表示する
              }
          });
      }
  }

activity_main.xml
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="0"
        android:id="@+id/textView"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Plus Button"
        android:id="@+id/button"
        android:layout_below="@+id/textView"
        android:layout_centerHorizontal="true" />
  </RelativeLayout>

2015年2月27日金曜日

【Android】【Java】 setText() に数値を渡す

Android Studio アプリ開発備忘録


第2回 『 setText() に数値を渡す方法』


setText() は、テキストビューに任意の文字列をセットできるメソッドである。

 TextView.setText("任意の文字列")

と、いう具合に使う。

ある時、プログラムの中で使用されている変数 int i の値を表示したくなった。

TextView.setText(int i) 

とすればエラーになるし、

TextView.setText("i")

とすれば当然画面には 『i』 という文字が表示されてしまう。

これを解決するには String.valueOf() を使えば良いようだ。

 TextView.setText(String.valueOf(i))

これで、変数 i の値を表示できる。

試しに、ボタンをタップする毎に、画面の数字を0から1つずつ増やしていくプログラムを作ってみた。

(※別途、XMLでボタンとテキストビューを用意しておく必要がある。
===================================================================
public class MainActivity extends Activity {

private static int i = 0;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
   
        final TextView textView = (TextView) findViewById(R.id.textview);
        final Button button = (Button) findViewById(R.id.button);

        // ボタンをクリックした際の処理
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                i++; // i を1増やす
                TextView.setText(String.valueOf(i)); // i の値を表示する
            }
        });
===================================================================

こちらのサイトによると、変換する方法は、この方法を含めて3つあるようだ。