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

pSync

pSync って何?

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

動作環境

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

インストール

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

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

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

$ wget 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
# ~/.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.3 (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.3 (protocol pSp1)
(以下略)

使い方

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

$ psync guest@example.com
private
work
$ 

変更履歴

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 がそのヘッダファイル。

new_priv()

#include "psync.h"
PRIV *new_priv(const char *dirname, time_t expire)
機能:
同期ディレクトリ前準備。 同じディレクトリに対して重複して前準備は出来ない。
引数:
name
同期ディレクトリを指定。 カレントディレクトリからの相対パスで指定。
expire
削除情報保持期間を秒単位で指定。 例えば30日後まで保持したいならば 2,592,000=30×24×60×60 を指定する。
戻り値:
同期ディスクリプタを返す。
≠NULL
同期準備完了。
=NULL
同期準備失敗。

free_priv()

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

run()

#include "psync.h"
int run(bool master, PRIV *priv)
機能:
ファイル同期実行。
引数:
master
動作モードを指定。 同期元に true を、同期相手に false を指定する。
priv->fdin
同期通信受信ディスクリプタを指定。
priv->fdout
同期通信送信ディスクリプタを指定。
priv->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;
    PRIV *priv;
    bool master;
    pthread_t tid;
} RUN_PARAM;
static void *run_thread(void *data) {
    RUN_PARAM *param = data;

    param->status = run(param->master, param->priv);
    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, .priv = NULL},
        {.status = INT_MIN, .priv = 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].priv = new_priv(dirname1, expire * (60*60*24));
    if (param[0].priv == NULL)
        goto error;
    param[1].priv = new_priv(dirname2, expire * (60*60*24));
    if (param[1].priv == NULL)
        goto error;
    param[0].priv->fdin = fd[0][0], param[0].priv->fdout = fd[1][1];
    param[1].priv->fdin = fd[1][0], param[1].priv->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].priv != NULL)
            free_priv(param[n1].priv);
    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旧バージョンはメンテナンスされないので最新バージョンを使う事をお勧めする。
*6同期ディレクトリからの相対パス名でバイト換算。
*7最後のワーニングメッセージはC++ならば解るけど、Cのビルドで表示される意図が不明。 Cでは文字列に数値を追加している訳ではなくポインタを加算している表記なので、"a string does not append" なのは当然の事。
Copyright (c) 2018 Yuichi Kobayashi <kobayasy@kobayasy.com>