[pSync って何?] [動作環境] [インストール [実行ファイルのインストール] [同期ディレクトリの設定] [インストールの確認]] [使い方] [エラーメッセージとその対処法] [終了コード] [変更履歴] [ライセンス] [その他] [技術資料 [通信プロトコル] [同期アルゴリズム [psync_new()] [psync_free()] [run()] [使用例]] [シリアライザ [WRITE()] [READ()]]]

pSync

pSync って何?

下記特徴を持ったネットワーク双方向ファイル同期ツール。

動作環境

pSync を動作させるには下記環境が必要。

インストール

同期元と相手の両方に 実行ファイルのインストール同期ディレクトリの設定 の作業が必要。

実行ファイルのインストール

下記で実行ファイルが ~/bin/ へインストールされる。

$ curl -O http://kobayasy.com/psync/psync.tar.gz
$ tar xzf psync.tar.gz
$ cd psync
$ ./configure --prefix=$HOME
$ make install

同期ディレクトリの設定

下記は ~/pSync/Private/~/pSync/Work/ を同期ディレクトリに設定して、それぞれに識別名 privatework を付ける設定例。

$ cat <<EOF > ~/.psync.conf
#ID     Directory
private pSync/Private
work    pSync/Work
EOF
$ chmod 600 ~/.psync.conf
$ install -m700 -d ~/pSync/Private
$ install -m700 -d ~/pSync/Work

インストールの確認

下記を実行。 下記は同期相手が guest@example.com の場合の例なので実際の相手に合わせて置き換える事。

$ psync
pSync version 1.5 (protocol pSp1)

Usage: psync [-v|-q] [USER@]HOST
       psync --help

USER@HOST
  HOST           hostname
  USER           username (default: current login user)

subcommand
 --help          show this help

options
  -v, --verbose  increase verbosity
  -q, --quiet    suppress non-error messages

$ ssh guest@example.com psync
pSync version 1.5 (protocol pSp1)
(以下略)

使い方

下記を実行するだけ。 下記は同期相手が guest@example.com の場合の例なので実際の相手に合わせて置き換える事。 これで同期ディレクトリ内のファイルが同期相手と同期する。

$ psync guest@example.com
private
work
$ 

エラーメッセージとその対処法

Error: で始まるメッセージ
同期相手との同期ファイル処理開始前に発生したエラーを示す。 Error: に続くメッセージの原因とその対策は下記。
Protocol
同期相手への接続に失敗した。 引数の同期相手ホスト名とログイン名に間違いがないかを確認。 同期相手の pSync が正しくインストールされていない事も考えられるので、インストールの確認 も再確認する。
Handshake
同期相手とのハンドシェークに失敗した。
System
システム的な原因によるエラーを示す。
(その他)
設定ファイルかコマンド引数に問題がある事を示す。
Local error: 又は Remote error: で始まるメッセージ
同期相手への接続完了後に発生したエラーを示し,Local error: は同期元側に、Remote error: は同期相手側にその原因がある事を示している。 error: に続くメッセージの原因とその対策は下記。
File type
同期ディレクトリ内に非対応な種類のファイルが存在している。 もしくはディレクトリでないファイルを同期ディレクトリに指定している事を示している。 pSync が対応しているのは 通常ファイル と ディレクトリ、シンボリックリンク のみなので、デバイスファイル等、その他の種類が含まれていないかを確認して、修正するか削除する。 下記コマンドでこのエラー原因のファイルを検出出来る。
$ find 同期ディレクトリ ! -type f ! -type d ! -type l
File permission
同期ディレクトリ自身かその中に、ユーザー権限で読み出し出来ないファイル か ユーザー権限でアクセスできないディレクトリ が存在する事を示している。 その場合,パーミッションを修正するか削除する。 下記コマンドでこのエラー原因のファイルを検出出来る。
$ find 同期ディレクトリ -type f ! -perm -400 -o -type d ! -perm -700 -o -type l ! -perm -400
Make file
同期ディレクトリ内へのファイル生成に失敗した事を示している。 ディスク容量が足りなくなった場合もこのエラーになる。
Open file
同期ディレクトリ内のファイルオープンに失敗した事を示している。
Write file
同期ディレクトリ内へのファイル書き込に失敗した事を示している。 ディスク容量が足りなくなった場合もこのエラーになる。
Read file
同期ディレクトリ内のファイル読み出しに失敗した事を示している。
Link file
同期ディレクトリ内でのハードリンク生成に失敗した事を示している。 ディスク容量が足りなくなった場合もこのエラーになる。
Remove file
同期ディレクトリ内のファイル削除に失敗した事を示している。
Move file
同期ディレクトリ内でのファイル移動に失敗した事を示している。
Write file-stat
同期ディレクトリ内のファイルパーミッションか更新時刻情報の書き換えに失敗した事を示している。
Read file-stat
同期ディレクトリ内のファイルステータス読み出しに失敗した事を示している。
Upload file-stat
同期情報の(原因発生側から見て)アップロードに失敗した事を示す。 途中で同期先との接続が切れた場合、このエラーになる。
Download file-stat
同期情報の(原因発生側から見て)ダウンロードに失敗した事を示す。 途中で同期先との接続が切れた場合、このエラーになる。
Upload file
同期ファイルの(原因発生側から見て)アップロードに失敗した事を示す。 途中で同期先との接続が切れた場合、このエラーになる。
Download file
同期ファイルの(原因発生側から見て)ダウンロードに失敗した事を示す。 途中で同期先との接続が切れた場合、このエラーになる。
Make data-file
同期情報ファイル(同期ディレクトリ/.psync/last)の生成に失敗した事を示す。 ディスク容量が足りなくなった場合もこのエラーになる。
Open data-file
同期情報ファイル(同期ディレクトリ/.psync/last)の読み出しに失敗した事を示す。
Write data-file
同期情報ファイル(同期ディレクトリ/.psync/last)への書き込みに失敗した事を示す。 ディスク容量が足りなくなった場合もこのエラーになる。
Read data-file
同期情報ファイル(同期ディレクトリ/.psync/last)からの読み出しに失敗した事を示す。
Memory
メモリ不足を示す。 他のプロセスを終了させて使えるメモリを増やせば解決するはず。
Interrupted
Ctrl-C 入力か kill pid コマンド、もしくは同期相手との接続切れによる中断を示す。
System
システム的な原因によるエラーを示す。
Unknown
原因不明なエラーを示す(ソフトウェアのバグの可能性が高い)。
Skip, local not ready. 又は Skip, remote not ready.
エラー扱いではないが、識別名が示す同期ディレクトリが既に同期処理中である為、同期処理を飛ばした事を示す。 Skip, local not ready. の場合は同期元側、Skip, remote not ready. の場合は同期相手側が処理中である事を示している。 他ホストからのアクセスや cron による自動起動で、裏で pSync が動作しているのでその終了を待つ。 裏で動作している pSync は下記コマンドで確認できる。
$ ps ax | grep psync
裏で pSync が動作していないのにこのメッセージが出る場合は前回の同期処理が Ctrl-| 入力や kill -9 pid コマンドで強制終了された等の原因で、同期ディレクトリがロックされた状態のまま残ってしまっている可能性が考えられる。 その場合、下記コマンドで強制的にロックを解除する事が出来る。
$ rm -r 同期ディレクトリ/.psync/lock

終了コード

psync コマンドの終了コードの意味は下記。

0
問題なく正常終了した。 メッセージ出力の Skip, local not ready. と Skip, remote not ready. はエラー扱いではないので、他にエラーがなければこのコードになる。
1〜99
同期相手との同期処理中にエラー終了した。
101〜199
同期相手との接続完了後の同期処理外でエラー終了した。
201〜254
同期相手との接続完了前にエラー終了した。 設定ファイルか引数に問題があるか、同期相手の pSync が正しくインストールされていない可能性が高い。
255
原因不明なエラーにより終了した(ソフトウェアのバグの可能性が高い)。

変更履歴

Version 1.5  (6th of September, 2018)
  • Make ルールに distclean を追加。
  • 情報の削除日時生成アルゴリズムを変更。
  • 同期ファイル情報収集中の中断判定頻度を上げた。
  • SIGHUP シグナルにも中断処理を入れた。
Version 1.4  (25th of August, 2018)
  • シグナルによる中断処理を安全なコードに修正。
  • エラーメッセージをエラーコードから文字列に変更。
  • psync.c 内の外部公開関数名を psync_*() に変更。
Version 1.3  (16th of August, 2018)
  • エラーメッセージ表示の時、識別名の先頭が数字だった場合に正しい表示をしない問題を修正。
  • その他、動作に影響の無い細かい修正。
Version 1.2  (4th of August, 2018)
  • ソースコードの実装ミスを修正(更新前の状態がディレクトリだった場合の同期処理)。
  • 同期ディレクトリ内に未対応なファイルが含まれていた場合の扱いを「存在しない物として扱う。」から「エラー終了。」に修正。
Version 1.1  (28th of July, 2018)
  • パス名バッファと送受信バッファのサイズを調整。
Version 1.0  (16th of July, 2018)
最初の公開版。
  • MSync の双方向ファイル同期アルゴリズムをベースに新規作成。

ライセンス

本ツールの正式名称は頭1文字目が小文字で2文字目が大文字、それ以降が小文字表記の pSync で、オリジナル配布元は http://kobayasy.com/psync/

この文章を含め pSync の配布ファイルは下記ライセンスの元、使用と再配布を許可する。

Copyright (c) 2018 by Yuichi Kobayashi <kobayasy@kobayasy.com>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

その他

その他、作者のメモとか、どうでも良い事とか、実は大事な事とか... いろいろ思い付いたらここに追記。

これ以降はいい加減にしかメンテナンスされていおらず、旧バージョンの情報も残っており最新バージョンとは合わない部分もあるので注意。

技術資料

通信プロトコル

ファイル同期の通信プロトコルはこんな感じ。

同期アルゴリズム

双方向ファイル同期アルゴリズムは、psync.c と psync_utils.h で実装されており、psync.h がそのヘッダファイル。

psync_new()

#include "psync.h"
PSYNC *psync_new(const char *dirname, time_t expire, volatile sig_atomic_t *stop)
機能:
同期ディレクトリ前準備。 同じディレクトリに対して重複して前準備は出来ない。
引数:
name
同期ディレクトリを指定。 カレントディレクトリからの相対パスで指定。
expire
削除情報保持期間を秒単位で指定。 例えば30日後まで保持したいならば 2,592,000=30×24×60×60 を指定する。
stop
中断フラグポインタ。
stop=NULL
中断機能を使わない。
*stop=0→1
0から1への変更のみが許された操作で、1を設定すると処理を中断出来る。 中断機能を使う場合は前もって *stop=0 に設定した状態で本関数を呼ぶ事。
戻り値:
同期ディスクリプタを返す。
≠NULL
同期準備完了。
=NULL
同期準備失敗。

psync_free()

#include "psync.h"
void psync_free(PSYNC *psync)
機能:
同期ディレクトリの後処理。
引数:
psync
後処理後、開放する同期ディスクリプタを指定。
戻り値:
(なし)

run()

#include "psync.h"
int run(bool master, PSYNC *psync)
機能:
ファイル同期実行。
引数:
master
動作モードを指定。 同期元に true を、同期相手に false を指定する。
psync->fdin
同期通信受信ディスクリプタを指定。
psync->fdout
同期通信送信ディスクリプタを指定。
psync->info
情報出力ディスクリプタを指定。
戻り値:
正数(0を含む)
ファイル同期成功。
負数
ファイル同期失敗。

使用例

下記はカレントディレクトリからの相対パスディレクトリ Test1/Test2/ を同期するコーディング例。

/* dirsync.c
 * Example code: Synchronize local directory "./Test1/" and "./Test2/"
 */

#include <limits.h>
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include "psync.h"

#define EXPIRE_DEFAULT 400  /* [days] */

typedef struct {
    int status;
    PSYNC *psync;
    bool master;
    pthread_t tid;
} RUN_PARAM;
static void *run_thread(void *data) {
    RUN_PARAM *param = data;

    param->status = run(param->master, param->psync);
    return NULL;
}
static void dirsync(const char *dirname1, const char *dirname2, time_t expire) {
    int fd[2][2] = {
        {-1, -1},
        {-1, -1}
    };
    RUN_PARAM param[2] = {
        {.status = INT_MIN, .psync = NULL},
        {.status = INT_MIN, .psync = NULL}
    };
    unsigned int n1, n2;

    for (n1 = 0; n1 < 2; ++n1) {
        pipe(fd[n1]);
        for (n2 = 0; n2 < 2; ++n2)
            if (fd[n1][n2] == -1)
                goto error;
    }
    param[0].psync = psync_new(dirname1, expire * (60*60*24), NULL);
    if (param[0].psync == NULL)
        goto error;
    param[1].psync = psync_new(dirname2, expire * (60*60*24), NULL);
    if (param[1].psync == NULL)
        goto error;
    param[0].psync->fdin = fd[0][0], param[0].psync->fdout = fd[1][1];
    param[1].psync->fdin = fd[1][0], param[1].psync->fdout = fd[0][1];
    param[0].master = true;
    param[1].master = false;
    if (pthread_create(&param[0].tid, NULL, run_thread, &param[0]) != 0)
      goto error;
    if (pthread_create(&param[1].tid, NULL, run_thread, &param[1]) != 0)
      goto error;
    if (pthread_join(param[0].tid, NULL) != 0)
      goto error;
    if (pthread_join(param[1].tid, NULL) != 0)
      goto error;
error:
    for (n1 = 0; n1 < 2; ++n1)
        if (param[n1].psync != NULL)
            psync_free(param[n1].psync);
    for (n1 = 0; n1 < 2; ++n1)
        for (n2 = 0; n2 < 2; ++n2)
            if (fd[n1][n2] != -1)
                close(fd[n1][n2]);
    printf("status = %d, %d\n", param[0].status, param[1].status);
}

int main(int argc, char *argv[]) {
    dirsync("Test1", "Test2", EXPIRE_DEFAULT);
    return 0;
}

これをビルドするための Makefile はこんな感じ。

# Makefile

CFLAGS = -O2

dirsync : dirsync.o psync.o
	$(CC) -lpthread -o $@ $^

psync.o : psync.c psync_utils.h
dirsync.o : dirsync.c psync.h

%.o : %.c
	$(CC) $(CFLAGS) -c $<

clean :
	$(RM) dirsync
	$(RM) dirsync.o psync.o

シリアライザ

C言語用の手軽に使えるシリアライザが探しても見つからくてなくて自作した。 psync_utils.h で定義しているマクロの WRITE() と READ() がそれ。 扱えるのは整数の数値だけだけど 248bit の値まで対応しているので、全ての整数値型変数に対応できるはず。

READ() の data 型は WRITE() のそれと一致している必要は無いが、値が収まる型である事。 例えば WRITE() 側の型が intmax_t であっても、その値が 255 ならば READ() 側は uin8_t で受ける事が出来る。

WRITE()

WRITE(data, fd, status)
機能:
変数 data の値をシリアライズして、ファイルディスクリプタ fd に書き出す。
引数:
data
シリアライズする数値が代入された任意の整数型変数を指定。 この変数の値は破壊される。 248bit 以下の整数型変数ならば符号付き/符号なし問わず何でも良い。 ポインタではなく整数型変数その物である事。 数値や数式、関数は指定できない。
fd
シリアライズしたデータを書き込むファイルディスクリプタを措定。
status
シリアイズ処理の結果を収納する変数を指定。
正数(0を含む)
シリアイズ成功。
負数
シリアイズ失敗。 原因としては以下が考えられる。 data の型が 248bit を越えている。 fd への書き込みに失敗。
戻り値:
(なし)

READ()

READ(data, fd, status)
機能:
ファイルディスクリプタ fd から読み出したシリアライズされた数値を、デシリアライズして変数 data に代入する。
引数:
data
デシリアイズした数値を代入する任意の整数型変数を指定。 この変数の値は破壊される。 248bit 以下の整数型変数ならば符号付き/符号なし問わず何でも良い。 ポインタではなく整数型変数その物である事。 数値や数式、関数は指定できない。
fd
デシリアライズするデータを読み出すファイルディスクリプタを措定。
status
デシリアイズ処理の結果を収納する変数を指定。
正数(0を含む)
デシリアイズ成功。 この場合、data の値はデシリアイズした数値が入っている。
負数
デシリアイズ失敗。 この場合、data の値は不定で無効。 原因としては以下が考えられる。 fd からの読み出しに失敗。 fd から読み出したデータがシリアライズされたデータでない。 シリアライズ結果の値が data に入りきらずオーバーフロー(アンダーフロー)した。
戻り値:
(なし)

*1シンボリックリンクが使える環境ならば lutimes も実装されていると思われる。
*2始めて使う場合は試してからにして欲しいので、既存のディレクトリを使うのはお進めしない。
*3ASCIIコードの HT(09h) と SP(20h) をスペースと認識。
*4正確にはASCIIコードの NUL(00h) と HT(09h)、LF(0Ah)、CR(0Dh)、SP(20h)、#(23h) を含める事が出来ない。
*5旧バージョンはメンテナンスされないので最新バージョンを使う事をお勧めする。
*6IOアクセスのタイムアウト時間設定が30秒なので、最低でも30秒は待つて欲しい。
*7同期ディレクトリからの相対パス名でバイト換算。
*8最後のワーニングメッセージはC++ならば解るけど、Cのビルドで表示される意図が不明。 Cでは文字列に数値を追加している訳ではなくポインタを加算している表記なので、"a string does not append" なのは当然の事。
Copyright (c) 2018 Yuichi Kobayashi <kobayasy@kobayasy.com>