LD_PRELOADで動的ライブラリ関数を上書きする

動的リンクされたプログラムでは、同じ関数が複数のライブラリで定義されている場合、最初に見つかった関数が利用される。

環境変数 LD_PRELOAD で指定した共有ライブラリは最優先で読み込まれるため、簡単にプログラムの挙動を変えることができる。

実験用のプログラム

まずは乱数を10個表示するだけの簡単なプログラム(random_num.c)を用意。

/* random_num.c */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(){
  srand(time(NULL));
  int i = 10;
  while(i--) printf("%d\n",rand());
  return 0;
}

実験用プログラムの実行

コンパイルする

$ gcc random_num.c -o random_num
$ ldd random_num
	linux-vdso.so.1 =>  (0x00007fffbddff000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f89557e8000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f8955b84000)

$ nm -D /lib64/libc.so.6  | grep rand$
00000000000369d0 T rand
0000000000036490 W srand

GNU C Library(libc.so.6) とリンクされており、 実験用で利用する関数 rand もある。nm でシンボルを出力すると、 rand を確認できる。

実際に実行するとランダムに数字が10個表示される。

$ ./random_num
849453208
1166619329
1528092543
1204148216
947393683
1613042737
1981359507
1489207667
381293129
346436639

rand を上書きする

次に、本来はランダムな整数を返す rand 関数がつねに固定の整数 42 を返すように書き換え他プログラムを用意(unrandom.c)。

/* unrandom.c */
int rand(){
    return 42; //the most random number in the universe
}

共有ライブラリとしてコンパイルする

$ gcc -shared -fPIC unrandom.c -o unrandom.so
$ nm -D ./unrandom.so
                 w _Jv_RegisterClasses
00000000002007c8 A __bss_start
                 w __cxa_finalize
                 w __gmon_start__
00000000002007c8 A _edata
00000000002007d8 A _end
0000000000000578 T _fini
0000000000000420 T _init
000000000000052c T rand

LD_PRELOAD でプログラムの実行

環境変数 LD_PRELOAD で作成した共有ライブラリ を指定し、同じプログラムを実行してみる。

$ LD_PRELOAD=./unrandom.so ldd random_num
	linux-vdso.so.1 =>  (0x00007fff54f86000)
	./unrandom.so (0x00007f841064b000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f84102af000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f841084d000)
$ LD_PRELOAD=./unrandom.so ./random_num
42
42
42
42
42
42
42
42
42
42

rand 関数が固定値を返す自作ライブラリの関数に書き換わっているため、プログラムの実行結果もランダム性がなくなっているのがわかる。

rand を上書きする(元の関数も実行)

先ほどの例では関数の中身を完全に書き換えていたので、もう少しソフトに、本来の関数を実行しつつその前後で追加処理を入れたのが次のプログラム(myrandom.c)。

/* myrandom.c */
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>

typedef int (*orig_rand_f_type)();

int rand(){
  printf("custom rand is called\n");
  orig_rand_f_type orig_rand;
  orig_rand = (orig_rand_f_type)dlsym(RTLD_NEXT, "rand");
  return orig_rand();
}

dlsym(RTLD_NEXT, "rand") でデフォルト(RTLD_DEFAULT)の次に見つかる rand 関数を探し出し、実行している。
メッセージを出力している 箇所(printf("custom rand is called\n");)が追加処理。

同じようにコンパイルして実行してみる。

$ gcc -shared -fPIC myrandom.c -o myrandom.so -ldl
$ LD_PRELOAD=./myrandom.so ./random_num
custom rand is called
1096863217
custom rand is called
505523853
custom rand is called
1514541433
custom rand is called
474180237
custom rand is called
1994792782
custom rand is called
288074905
custom rand is called
1366621408
custom rand is called
1929084141
custom rand is called
1667463609
custom rand is called
1873885528

再びランダムな整数が表示されるようになり、また、追加したデバッグメッセージも表示されている。

References

Tagged with: , ,
Posted in linux

[Python]Clickでコマンドラインアプリケーションを作る

click_logo

Python でコマンドラインプログラムを書くときのライヴラリに Click というのがある。(キャッチコピーは “Command Line Interface Creation Kit”)
“The Hitchhiker’s Guide to Python” の “Command-line Application” でも CLI 用のライブラリとして clint などとともに取り上げられている。

作者の @mitsuhiko が YouTube に “Building Command Line Applications with Click” という20分弱の動画を投稿している。

固定メッセージを返すシンプルなアプリケーションを少しつづ拡張していき、最後には git 風のサブコマンドを実装する。
勉強がてら書き起こしてみる。

動画が公開されたのは Click の初回リリース間もない頃だけれど、Click の基本設計は変わっていないので、2015年3月末リリースの 最新版 0.4 でも読み替え無しに動作した。

プロジェクトの作成

$ mkdir helloclick
$ cd helloclick/

virtualenv で環境構築する

$ virtualenv venv
New python executable in venv/bin/python
Installing setuptools, pip...done.
~/helloclick$ ls venv/
bin     include lib
jsmith@latok:~/helloclick$ . venv/bin/activate
~/helloclick$ which python
/Users/jsmith/helloclick/venv/bin/python

プログラムの作成

hello で呼び出せる CLI プログラムを作成。
CLI インストールする setup.py を用意。

# setup.py
from setuptools import setup

setup(
    name='HelloWorld',
    version='1.0',
    py_modules=['hello'],
    install_requires=[
        'Click',
    ],
    entry_points='''
        [console_scripts]
        hello=hello:cli
    '''
)

実体は hello.pycli 関数。

# hello.py
def cli():
    print 'Hello World!'

pip 経由でインストールする。
--editable .オプションをつけると working directory のプログラムを参照してくれるので、プログラムを修正後、再インストールしなくてすむ

$ pip install --editable .

インストールされていることを確認。

$ hello
Hello World!
$ which hello
/Users/jsmith/helloclick/venv/bin/hello

click でラップする

ここから Click を使ってより楽にコマンドラインプログラムを拡張していく。
cli 関数を @click.command()でラップします。

# hello.py
import click

@click.command()
def cli():
    print 'Hello World!'

実行してみる

$ hello
Hello World!
$ hello --help
Usage: hello [OPTIONS]

Options:
  --help  Show this message and exit.

通常の実行はこれまでと同じですが --help でヘルプメッセージが出力されるようになりました。

オプションを追加

出力メッセージをコントロールする --string オプションを追加します。
デフォルトは ‘World’ とします。

# hello.py
import click

@click.command()
@click.option('--string', default='World')
def cli(string):
    print 'Hello %s!' % string

実行してみる

$ hello
Hello World!
$ hello --help
Usage: hello [OPTIONS]

Options:
  --string TEXT
  --help         Show this message and exit.

$ hello --string Tom
Hello Tom!

ヘルプメッセージにも --string オプションが表示され、Hello に続く文字列もコントロールできるようになりました。

メッセージ出力で環境依存を吸収する

  • Python 2/3 の print ステートメント/関数問題
  • 出力時の文字コードのややこしい問題

を抽象化した echo 関数が Click には存在するので、これを使う。

# hello.py
import click

@click.command()
@click.option('--string', default='World')
def cli(string):
    click.echo('Hello %s!' % string)

ヘルプメッセージを補強

  • プログラムの説明(cli 関数の docstring)
  • 引数の意味(@click.option の help 引数)

をヘルプメッセージに表示します。

# hello.py
import click

@click.command()
@click.option('--string', default='World',
              help='This is  the thing that is greeted.')
def cli(string):
    """This script greets you."""
    click.echo( 'Hello %s!' % string)

実行してみる。

$ hello --help
Usage: hello [OPTIONS]

  This script greets you.

Options:
  --string TEXT  This is  the thing that is greeted.
  --help         Show this message and exit.

整数型のオプションを追加

整数を引数に取る --repeat オプションを追加します。
オプションの型はデフォルト値から推論されます。
明示的に

@click.option('--repeat', default=1, type=int, help='...')

というように書くこともできます。

# hello.py
import click

@click.command()
@click.option('--string', default='World',
              help='This is  the thing that is greeted.')
@click.option('--repeat', default=1,
              help='How many times you should be greeted.')
def cli(string, repeat):
    """This script greets you."""
    for x in xrange(repeat):
        click.echo( 'Hello %s!' % string)

実行してみる。

$ hello --help
Usage: hello [OPTIONS]

  This script greets you.

Options:
  --string TEXT     This is  the thing that is greeted.
  --repeat INTEGER  How many times you should be greeted.
  --help            Show this message and exit.
$ hello --repeat 3
Hello World!
Hello World!
Hello World!

引数に不正な型を指定するとバリデーションエラーになります。

$ hello --repeat asdasdf
Usage: hello [OPTIONS]

Error: Invalid value for "--repeat": asdasdf is not a valid integer

オプションのダッシュ(-)の扱い

@click.option('--foo-bar', default='baz') のようにダッシュ(-)が含まれる場合、アンダースコア(_)に置き換えられます。

@click.option('--foo-bar', default='baz')
def cli(foo_bar):
    ...

Click のパラメーターには

  • click.option($ ls -l のような用途)
  • click.argument($ ls a b c のような用途)

の2種類がある。

細かい違いはドキュメントを参照 http://click.pocoo.org/4/parameters/

click.argument を使って、実行結果を引数で渡したファイルに出力するようにしてみる。

# hello.py
import click

@click.command()
@click.option('--string', default='World',
              help='This is  the thing that is greeted.')
@click.option('--repeat', default=1,
              help='How many times you should be greeted.')
@click.argument('out', type=click.File('w'), default='-',
                required=False)
def cli(string, repeat, out):
    """This script greets you."""
    for x in xrange(repeat):
        click.echo( 'Hello %s!' % string, file=out)

実行してみる

$ hello foo.txt
$ cat foo.txt
Hello World!

file argument の特定

file argument で指定したファイルは lazy にオープンされる。
例えば書き込み系処理の場合、実際に書き込みが発生するまではファイルオープンされない。

$ rm bar.txt
$ hello --repeat=asdf bar.txt
Usage: hello [OPTIONS] [OUT]

Error: Invalid value for "--repeat": asdf is not a valid integer
$ cat bar.txt
cat: bar.txt: No such file or directory

コマンドラインオプションでエラー発生のため、何も書き込みが発生しておらず、ファイルが作成されていません。

実際に正常系の書き込みをします。

$ hello --repeat=3 bar.txt
$ cat bar.txt
Hello World!
Hello World!
Hello World!

ファイル作成されました。

サブコマンド

git add には -f -m などいろいろなサブコマンドが定義されている。
これまで $ hello --message xxx としてきたのを $ hello say --message xxxようにサブコマンド sayを定義するには以下の様にする。

# hello.py
import click

@click.group()
def cli(config, verbose, home_directory):
    pass

@cli.command()
@click.option('--string', default='World',
              help='This is  the thing that is greeted.')
@click.option('--repeat', default=1,
              help='How many times you should be greeted.')
@click.argument('out', type=click.File('w'), default='-',
                required=False)
def say(config, string, repeat, out):
    """This script greets you."""
    for x in xrange(repeat):
        click.echo( 'Hello %s!' % string, file=out)

具体的には

  • CLI のエントリーポイント cli 関数を @click.group() でデコレート
  • サブコマンド say@cli.command でデコレート

している。

ヘルプメッセージの変化を確認してみる。

$ hello --help
Usage: hello [OPTIONS] COMMAND [ARGS]...

Options:
  --help                 Show this message and exit.

Commands:
  say  This script greets you.
$ hello say --help
Usage: hello say [OPTIONS] [OUT]

  This script greets you.

Options:
  --string TEXT     This is  the thing that is greeted.
  --repeat INTEGER  How many times you should be greeted.
  --help            Show this message and exit.

トップレベルコマンドをサブコマンドから利用する

@click.group() デコレーターはサブコマンドが呼ばれると必ず呼ばれる

トップレベル(@click.group)で定義されたオプション(--verbose など)をサブコマンド(@clic.command)から利用するには click.make_pass_decorator を活用する。

# hello.py
import click

class Config(object):

    def __init__(self):
        self.verbose = False

pass_config = click.make_pass_decorator(Config, ensure=True)

@click.group()
@click.option('--verbose', is_flag=True)
@click.option('--home-directory', type=click.Path())
@pass_config
def cli(config, verbose, home_directory):
    config.verbose = verbose
    if home_directory is None:
        home_directory = '.'
    config.home_directory = home_directory

@cli.command()
@click.option('--string', default='World',
              help='This is  the thing that is greeted.')
@click.option('--repeat', default=1,
              help='How many times you should be greeted.')
@click.argument('out', type=click.File('w'), default='-',
                required=False)
@pass_config
def say(config, string, repeat, out):
    """This script greets you."""
    if config.verbose:
        click.echo('We are in verbose mode')
    click.echo('Home directory is %s' % config.home_directory)
    for x in xrange(repeat):
        click.echo( 'Hello %s!' % string, file=out)

実行してみる

$ hello --home-directory /tmp say --string foo --repeat 3
Home directory is /tmp
Hello foo!
Hello foo!
Hello foo!

$ hello say
Home directory is .
Hello World!

$ hello --verbose say
We are in verbose mode
Home directory is .
Hello World!

$ hello --help
Usage: hello [OPTIONS] COMMAND [ARGS]...

Options:
  --verbose
  --home-directory PATH
  --help                 Show this message and exit.

Commands:
  say  This script greets you.
$ hello say --help
Usage: hello say [OPTIONS] [OUT]

  This script greets you.

Options:
  --string TEXT     This is  the thing that is greeted.
  --repeat INTEGER  How many times you should be greeted.
  --help            Show this message and exit.

ということで期待通り。

I hope you can build some very cool applications with Click this way.

Tagged with: , ,
Posted in python

データ検証などで利用するMerkle Treeのメモ

データの検証などで利用される Merkle tree/マークル木/hash tree/ハッシュ木についてメモ。

Merkle Tree の概要

wikipedia の概要をそのままコピペ。

暗号理論および計算機科学において、ハッシュ木(Hash tree, ハッシュツリー)またはマークル木(Merkle tree)とは、より大きなデータ(例えばファイル)の要約結果を格納する木構造の一種であり、データの検証を行う際に使用される。このデータ構造はハッシュリストとハッシュチェインの組み合わせでできており、ハッシュ法の延長上にある手法といえる。

Merkle tree の利用例

merkle tree が2つ与えられた時に、ルートノードから子ノードに向かってハッシュ値をたどっていけば、どこが壊れているのか特定が簡単で、壊れている部分木だけを更新すればよい。
Amazon dynamo/cassandra/riak はレプリカ間の同期(anti-entorpy)に Merkle tree を使っている。

別の例として、AWS Glacier のマルチパートアップロード

  1. マルチパートアップロード開始時にブロックサイドを指定
  2. マルチパートアップロード時に各ブロックのデータとハッシュ値と位置(アーカイブのXバイト-Yバイト目)を指定
  3. マルチパートアップロード終了時にアーカイブ全体のファイルサイズと Merkle tree のルートハッシュ値を指定

するAPIになっている。

サーバ側は、分割アップロードされた各ブロックを位置順に並び直し、Merkle tree のルートハッシュ値を求め、クライアントがマルチパート終了時に送信したチェックサムと突き合わせている。

ハッシュ値を実際に計算してみる

Merkle Tree の理解を深めるため wikipedia の記事で使われてる例を元に 実際に Merkle Tree のハッシュを計算してみる。

処理の流れは以下

  1. データをブロックサイズで分割
  2. 各ブロックのハッシュ値を求め、葉ノードとする
  3. 親ノードは各子ノードのハッシュ値を連結した文字列のハッシュ値とする
  4. 根ノードに辿り着くまでこの操作を繰り返す。

データを分割

ハッシュ値を求めるファイル(サイズは 4 MiB)を作成する。

$ FILENAME=file.txt
$ dd if=/dev/urandom of=$FILENAME bs=1048576 count=4
4+0 records in
4+0 records out
4194304 bytes (4.2 MB) copied, 3.37527 s, 1.2 MB/s

ブロックサイズ 1MiB でファイル分割する。
分割ファイル名は”L”で始まり、分割ファイルは1からの数字で採番(–numeric=1)する

$ BLOCKSIZE=1048576 # 1MiB
$ split -b  -d $FILENAME
$ split --bytes=$BLOCKSIZE --numeric=1 --suffix-length=1 $FILENAME L

$ ll
total 8192
-rw-rw-r-- 1 jsmith jsmith 1048576 Apr 11 20:31 L1
-rw-rw-r-- 1 jsmith jsmith 1048576 Apr 11 20:31 L2
-rw-rw-r-- 1 jsmith jsmith 1048576 Apr 11 20:31 L3
-rw-rw-r-- 1 jsmith jsmith 1048576 Apr 11 20:31 L4
-rw-rw-r-- 1 jsmith jsmith 4194304 Apr 11 20:30 file.txt

ちなみに –numeric オプションは version 8.16 以上でないと動作しない。最近の Amazon Linux であれば OK.

葉ノード

各ブロックのハッシュ(今回はハッシュ関数に sha256 を利用)を求める

Hash 0-0 = hash(L1)
...
Hash 1-1 = hash(L4)

なので、 openssl コマンドでハッシュ値を求める

$ openssl dgst -sha256 -binary L1 > H00
$ openssl dgst -sha256 -binary L2 > H01
$ openssl dgst -sha256 -binary L3 > H10
$ openssl dgst -sha256 -binary L4 > H11

親ノード

次に、これらノードの親ノードのハッシュ値を求める。
Hash 0-0 と Hash 0-1 の親ノード Hash 0 のハッシュを求めるには、Hash 0-0 と Hash 0-1 を連結した文字列をハッシュ関数に渡せばよい。つまり

Hash 0 = hash(Hash 0-0 + Hash 0-1)

openssl コマンドで書き換えると

$ cat H00 H01 | openssl dgst -sha256 -binary > H0

となる。同様に

Hash 1 = hash(Hash 1-0 + Hash 1-1)

なので

$ cat H10 H11 | openssl dgst -sha256 -binary > H1

となる。

根ノード

最後に、根ノードのハッシュ値を求める。親ノードの時と同様に

TopHash = hash(Hash0 + Hash1)

openssl コマンドで書き換えると

$ cat H0 H1 | openssl dgst -sha256 -hex # ハッシュ値を確認しやすいようにバイナリ(-binary)ではなくヘックス値(-hex)で出力
(stdin)= dc6dbbb01f9cba0439ded090133e6250ada132e915f15d989d12d7ab4b601657

検証として aws-cli が glacier API で利用している Merkle Tree 実装を利用して、ハッシュ値が一致しているか確認。

$ cat tree_hash.py
# vim: set fileencoding=utf8
import sys
from botocore.utils import calculate_tree_hash
print (calculate_tree_hash(open(sys.argv[1], "rb")))

aws glacer のハッシュ関数は sha256 で、 aws-cli は内部的にブロックサイズを 1MiB としているので、コマンドラインの計算結果と一致するはず。

$ python tree_hash.py file.txt
dc6dbbb01f9cba0439ded090133e6250ada132e915f15d989d12d7ab4b601657

ということで、完全一致。めでたしめでたし。

Tagged with: , ,
Posted in algorithm

[AWS]ELBのプライベートIPアドレスを調べる

aws

Summary

VPC 内にたてた ELB(internet-facing/internal) のプライベート IP アドレスをコマンドラインツールの AWS-CLI から調べる方法をメモ。

tl;dr : EC2 DescribeNetworkInterfaces を使う

EC2 サービスには DescribeNetworkInterfaces という NIC 向けの API が存在する。この API を使うと ELB のグローバル/プライベート IP アドレスを確認できる。

NIC 一覧からお目当ての ELB を突き止めるには?

ELB インスタンスの NIC の設定は aws が裏でやっているためか、ELB のNIC は Attachment => InstanceOwnerId“amazon-elb” となっている。

また NIC の Description も aws が勝手に埋めており、“ELB “ となっている。
例えば LoadBalancerName“test-elb” の ELB は Description“ELB test-elb” となる。
※この仕様は undocumented なのでいつ変わってもおかしくない。

コマンド例

ELB の NIC 一覧を表示

$ aws ec2 describe-network-interfaces --filters Name=attachment.instance-owner-id,Values=amazon-elb

LoadBalancerName が “STG” で始まる ELB の NIC 一覧を表示

$ aws ec2 describe-network-interfaces --filters Name=description,Values='ELB STG*'

出力例

JSON 生の出力

JSON そのまま出力すると以下のようになる。
レスポンスにある"PrivateIpAddress" が文字通りプライベートIPアドレス。

$ aws ec2 describe-network-interfaces --filters Name=attachment.instance-owner-id,Values=amazon-elb
{
    "NetworkInterfaces": [
        {
            "Status": "in-use",
            "MacAddress": "dummy",
            "SourceDestCheck": true,
            "VpcId": "vpc-asdfqwer",
            "Description": "ELB TEST-Internal",
            "NetworkInterfaceId": "eni-4321",
            "PrivateIpAddresses": [
                {
                    "PrivateDnsName": "ip-10-0-18-218.ap-northeast-1.compute.internal",
                    "Primary": true,
                    "PrivateIpAddress": "10.0.18.218"
                }
            ],
            "PrivateDnsName": "ip-10-0-18-218.ap-northeast-1.compute.internal",
            "AvailabilityZone": "ap-northeast-1b",
            "Attachment": {
                "Status": "attached",
                "DeviceIndex": 1,
                "AttachTime": "2015-04-04T15:07:13.000Z",
                "DeleteOnTermination": false,
                "AttachmentId": "eni-attach-2a293b2d",
                "InstanceOwnerId": "amazon-elb"
            },
            "PrivateIpAddress": "10.0.18.218"
            ...,
        },
        {
            "Status": "in-use",
            "MacAddress": "dummy",
            "Description": "ELB TEST-Internet",
            "Association": {
                "PublicIp": "54.123.456.789",
                "PublicDnsName": "ec2-54-123-456-789.ap-northeast-1.compute.amazonaws.com",
                "IpOwnerId": "amazon-elb"
            },
            "NetworkInterfaceId": "eni-1234",
            "PrivateIpAddresses": [
                {
                    "PrivateDnsName": "ip-10-0-18-243.ap-northeast-1.compute.internal",
                    "Association": {
                        "PublicIp": "54.123.456.789",
                        "PublicDnsName": "ec2-54-123-456.789.ap-northeast-1.compute.amazonaws.com",
                        "IpOwnerId": "amazon-elb"
                    },
                    "Primary": true,
                    "PrivateIpAddress": "10.0.18.243"
                }
            ],
            "PrivateDnsName": "ip-10-0-18-243.ap-northeast-1.compute.internal",
            "Attachment": {
                "Status": "attached",
                "DeviceIndex": 1,
                "AttachTime": "2015-04-04T15:06:43.000Z",
                "DeleteOnTermination": false,
                "AttachmentId": "eni-attach-24293b23",
                "InstanceOwnerId": "amazon-elb"
            },
            "PrivateIpAddress": "10.0.18.243"
            ...,
        }
        ...
    ]
}

出力対象を絞る

LoadBalancerName が TEST で始まる ELB の NIC を対象に “Description” と “PrivateIpAddress” だけを出力すると以下のようになる。

$ aws ec2 describe-network-interfaces --filters Name=description,Values='ELB TEST*' | jq ".NetworkInterfaces[] | {Description, PrivateIpAddress}"
{
  "PrivateIpAddress": "10.0.18.218",
  "Description": "ELB TEST-Internal"
}
{
  "PrivateIpAddress": "10.0.18.243",
  "Description": "ELB TEST-Internet"
}

ELB DescribeLoadBalancers は使えないの?

Elastic Load Balancing(ELB) には DescribeLoadBalancers という API があるのでこれで完結すれば楽なのだが、レスポンスのモデル LoadBalancerDescription を見ても、プライベート IP らしきフィールドはない。

試しに出力結果に含まれる DNSName の名前を引くと

  • internet-facing の ELB はグローバグIPアドレス
  • internal の ELB はプライベートIPアドレス

がかえってきて、internet-facing の場合にプライベートIPアドレスを取得できない。

残念

Tagged with: , , ,
Posted in aws

MySQLのバイナリーログからデータ更新過程を確認したい

logo-mysql
CMSなどで同じ記事が何度も更新されたとして、DBにある最終形は別にして、記事が更新された過程を知りたいといことがまれにある。この過程をバイナリーログ(binlog)から復元する方法を RDS MySQL 固有の動きを中心にメモ。

環境は

  • データベースは RDS 版 MySQL(5.6)
  • クライアントは EC2 上の Amazon Linux

バイナリーログの用途

バイナリーログはテーブルへの更新を連続的に記録するもので

  • レプリケーション
  • 差分バックアップ/リストア

などで利用することが可能。

binlog の中を覗いてみる

次にメインの binlog を覗いてみる。

binlog を確認

MySQL に接続して show binary logs コマンドで確認する。

mysql> show binary logs;
+----------------------------+-----------+
| Log_name                   | File_size |
+----------------------------+-----------+
| mysql-bin-changelog.073463 |  14947249 |
| mysql-bin-changelog.073464 |   7125230 |
| mysql-bin-changelog.073465 |   7796951 |
| mysql-bin-changelog.073466 |  14170845 |
+----------------------------+-----------+
4 rows in set (0.00 sec)

binlog をローカルに落とす

binlog のファイル一覧を確認したところで、binlog ファイルをローカルに落として中身を確認する。

$ mysqlbinlog --read-from-remote-server -h HOST_NAME -u USER_NAME -p mysql-bin-changelog.073466 --result-file=mysql-bin-changelog.07346
Enter password:
$ ls
mysql-bin-changelog.073466
$ vi mysql-bin-changelog.073466

あとはファイル内のレプリケーション用SQLからお目当てのSQLを探し当てれば OK。

リカバリ目的などで生の binlog が欲しい時は MySQL5.6 から追加された --raw も渡すこと。バイナリーログも mysqlbinlog に直接食わせればテキスト形式に変換できる。

binlog 関連の設定

binlog retention period

通常の MySQL であれば、バイナリーログの最終更新日からの経過日数に従って自動的にバイナリログを削除するためのオプション expire_logs_days が実装されている。(奥野 pp.93)
しかし、RDB MySQL ではこのオプションを操作できない。

mysql> SHOW VARIABLES LIKE 'expire_logs_days';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| expire_logs_days | 0     |
+------------------+-------+
1 row in set (0.00 sec)

mysql> SET GLOBAL expire_logs_days=3;
ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER privilege(s) for this operation

かわりにストアド経由で binlog の保持期間(binlog retention hours) を操作する。

Amazon RDS normally purges a binary log as soon as possible, but the binary log must still be available on the instance to be accessed by mysqlbinlog. To specify the number of hours for RDS to retain binary logs, use the mysql.rds_set_configuration stored procedure and specify a period with enough time for you to download the logs. After you set the retention period, monitor storage usage for the DB instance to ensure that the retained binary logs do not take up too much storage.
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_LogAccess.Concepts.MySQL.html

binlog retention hours のデフォルト値を確認

mysql> call mysql.rds_show_configuration \G
*************************** 1. row ***************************
       name: binlog retention hours
      value: NULL
description: binlog retention hours specifies the duration in hours before binary logs are automatically deleted.
1 row in set (0.01 sec)


Query OK, 0 rows affected (0.01 sec)

ログの保持期間を 72 時間に変更する。

mysql> call mysql.rds_set_configuration('binlog retention hours', 72);
Query OK, 0 rows affected (0.01 sec)


mysql> call mysql.rds_show_configuration \G
*************************** 1. row ***************************
       name: binlog retention hours
      value: 72
description: binlog retention hours specifies the duration in hours before binary logs are automatically deleted.
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

ログの保持期間を初期値に戻す

デフォルト値 null に戻すには、ストアドにそのまま null を渡せばよい。

mysql> call mysql.rds_set_configuration('binlog retention hours', null);
Query OK, 0 rows affected (0.01 sec)

mysql> call mysql.rds_show_configuration \G
*************************** 1. row ***************************
       name: binlog retention hours
      value: NULL
description: binlog retention hours specifies the duration in hours before binary logs are automatically deleted.
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

バイナリーログの形式

通常の MySQL はバイナリーログの形式として

  • STATEMENT causes logging to be statement based.
  • ROW causes logging to be row based.
  • MIXED causes logging to use mixed format.

の中から選択可能。

http://dev.mysql.com/doc/refman/5.6/en/replication-options-binary-log.html#sysvar_binlog_format

RDS MySQL は ROW または MIXED が選択可能でデフォルトは MIXED

mysql> SHOW VARIABLES LIKE 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+
1 row in set (0.01 sec)

ROW にすると、ログサイズは増加する。

MySQL 5.6 Client のインストール

RHEL ベースの Amazon Linux でインストール可能な最新版の MySQL は --enablerepo=epel を有効にしても 5.5 系。
バックアップ・リカバリ目的でバイナリー形式の binlog が欲しい時は mysqlbinlog コマンドに –raw オプションを渡す。
ただし、このオプションは 5.6.0 で導入されたものなので、5.6 の MySQL クライアントをインストールする。

まずは 5.5 系の MySQL クライアントをアンインストールする。

$ mysql -V
mysql  Ver 14.14 Distrib 5.5.42, for Linux (x86_64) using readline 5.1
$ mysqlbinlog -V
mysqlbinlog Ver 3.3 for Linux at x86_64

$ sudo yum remove mysql

次に 5.6 系を rpm からインストールする。
ダウンロードサイト http://dev.mysql.com/downloads/mysql/ からアーキテクチャ(32/64)にあった “Client Utilities” を選択する。

$ sudo yum localinstall http://dev.mysql.com/get/Downloads/MySQL-5.6/MySQL-client-5.6.23-1.el6.x86_64.rpm
...
Installed:
  MySQL-client.x86_64 0:5.6.23-1.el6


Complete!
$ mysql --version
mysql  Ver 14.14 Distrib 5.6.23, for Linux (x86_64) using  EditLine wrapper
$ mysqlbinlog --version
mysqlbinlog Ver 3.4 for Linux at x86_64

5.6 系の MySQL クライアントをインストールできた。

References

Tagged with: ,
Posted in database

loggerコマンドの1024バイト出力制限を回避する

logger コマンドの1024バイト制限

$ cmd | logger というように、コマンドの出力結果を logger コマンド経由で syslog 出力することがある。
極稀に syslog のメッセージが途切れているという事象があり、調べてみると logger コマンドは 1024 バイトまでしか出力できないようにハードコードされているとわかる。

1024 バイト制限は “RFC 3164 : The BSD syslog Protocol” に起因していて

The total length of the packet MUST be 1024 bytes or less.
If the packet has been expanded beyond 1024 bytes, then the relay MUST truncate the packet to be 1024 bytes.
http://tools.ietf.org/html/rfc3164

といったことが書かれている。

実際にこの動作を確認してみる

] $ python -c 'print "a"*1020 + "1234"'  | logger

というメッセージに対して

Dec 20 20:18:25 host01 jsmith: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa123
Dec 20 20:18:25 host01 jsmith: 4

というように 1023 バイトでメッセージが分断されている

1024バイトを超えて syslog 出力する方法

logger コマンドはハードコードされているので諦め、あまり凝らずに 1024 バイトを超えて syslog 出力する方法についてメモ。

一つ目の mlogger は既存の logger のコードを拡張して 1024 バイト制限を取っ払っている。
もう一つの方法は、標準入力を syslog(3) に渡すだけの省エネで姑息(だけとこれで困らないよね?)な手段。

mlogger コマンド

mloggerlogger のフォーク。
UDP/TCP/Socket といった各プロトコルで logger の上限サイズをあげている。

コードは github に上がっているので、野良インストールする。

$ git clone https://github.com/nbrownus/mlogger.git
Cloning into 'mlogger'...
remote: Counting objects: 119, done.
remote: Total 119 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (119/119), 66.73 KiB | 103.00 KiB/s, done.
Resolving deltas: 100% (53/53), done.
Checking connectivity... done.
$ make
gcc -Wall -Wextra -O3 -o mlogger mlogger.c
$ ./mlogger --help

Usage:
mlogger [options] [message]

Options:
 -d, --udp              use UDP (TCP is default)
 -i, --id               log the process ID too
 -f, --file <file>      log the contents of this file
 -h, --help             display this help text and exit
 -n, --server <name>    write to this remote syslog server
 -P, --port <number>    use this UDP port
 -p, --priority <prio>  mark given message with this priority
 -s, --stderr           output message to standard error as well
 -t, --tag <tag>        mark every line with this tag
 -u, --socket <socket>  write to this Unix socket
 -I, --indent <ms>      Will consider intended lines continuation of the previous line, within the timeout
                        specified in milliseconds
 -V, --version          output version information and exit

This mlogger is configured to send messages up to 65536 bytes.

mlogger で出力してみる

$ python -c 'print "a"*1024 + "1234"' | ./mlogger   # message size > 1kb
$ python -c 'print "a"*1024*2 + "1234"' | ./mlogger # message size > 2kb

ローカルサーバの /var/log/syslogを確認

Dec 20 20:19:22 host01 -c: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1234
Dec 20 20:19:24 host01 -c: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1234

1024 バイトに引っかかることなく出力できている。

syslog(3)直接呼び出し版

標準入力を syslog(3) に渡すプログラムを好きな言語で書く

この方式だと UNIX domain socket の制限に引っかからないサイズ内であれば、メッセージ送信できる。

rsyslog の wiki ページに Perl/C 版があるのでまずはそれをコピペ

http://wiki.rsyslog.com/index.php/Working_Apache_and_Rsyslog_configuration

元の wiki は Apache のログをパイプで渡すことを前提にしていて identhttpd になっていたりするので、適宜修正する。

Perl version

#!/usr/bin/perl
# usage:
# $ cmd | pl-logger.pl
use Sys::Syslog qw (:DEFAULT setlogsock);

setlogsock('unix');

# open our log socket
openlog('httpd', 'pid', 'local6');

# log all our input
while (<STDIN>) {
  syslog('info', $_);
}

# close the log socket
closelog;

C version

# usage:
# $ cmd | c-logger
#include <stdio.h>
#define SYSLOG_NAMES
#include <syslog.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, const char *argv[])
{
  if (2 != argc)
  {
    fprintf(stderr, "Usage: stdin-syslogd priority\n");
    exit (1);
  }

  const char *priority_name = argv[1];
  int priority = -1;
  int i;

  for (i = 0; prioritynames[i].c_name; ++i)
  {
    if (! strcmp(prioritynames[i].c_name, priority_name))
    {
      priority = prioritynames[i].c_val;
      break;
    }
  }

  if (priority < 0)
  {
    fprintf(stderr, "stdin-syslogd: priority \"%s\" not recognized",
      priority_name);
    exit (1);
  }

  openlog("httpd", LOG_PID|LOG_NDELAY, LOG_LOCAL6);

  char buffer[8192];

  while (NULL != fgets(buffer, sizeof(buffer), stdin))
  {
    syslog(priority, "%s", buffer);
  }
  return 0;
}

Python version

ついでに Python にも移植。

#!/usr/bin/python
# usage:
# $ cmd | py-logger.py
import sys
import syslog

syslog.openlog("py-logger",
               logoption=syslog.LOG_PID|syslog.LOG_NDELAY,
               facility=syslog.LOG_LOCAL6)
for line in sys.stdin:
    syslog.syslog(syslog.LOG_INFO, line)

実際に動かしてみると

$ python -c 'print "a"*1024*2 + "1234"' | ./py-logger.py

というメッセージに対して

Dec 20 22:12:30 host01 py-logger[22320]: aaaaaaaa...[snip]...aaa1234

というように 1024 バイトを超えて出力できている。

Tagged with: , ,
Posted in linux

タイムゾーンの設定方法をメモ(RHEL6, RHEL7, Ubuntu編)

  • RHEL6(amazon Linux含む)
  • RHEL7(systemd)
  • Ubuntu

の各環境でタイムゾーンを設定する方法をメモ。

RHEL6

まずは RedHat 6系の場合。

amazon ec2 のドキュメントにわかりやすい説明があるので、これを読めばOK

Setting the Time for Your Linux Instance – Amazon Elastic Compute Cloud
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html

タイムゾーンの一覧を取得

/usr/share/zoneinfo 以下のタイムゾーン情報から設定したいタイムゾーンを調べる

~]$ ls -F /usr/share/zoneinfo
Africa/      Chile/   GB         Indian/      MST         PRC        UTC
America/     CST6CDT  GB-Eire    Iran         MST7MDT     PST8PDT    WET
Antarctica/  Cuba     GMT        iso3166.tab  Navajo      right/     W-SU
Arctic/      EET      GMT0       Israel       NZ          ROC        zone.tab
Asia/        Egypt    GMT-0      Jamaica      NZ-CHAT     ROK        Zulu
Atlantic/    Eire     GMT+0      Japan        Pacific/    Singapore
Australia/   EST      Greenwich  Kwajalein    Poland      Turkey
Brazil/      EST5EDT  Hongkong   Libya        Portugal    UCT
Canada/      Etc/     HST        MET          posix/      Universal
CET          Europe/  Iceland    Mexico/      posixrules  US/

日本の場合

  • Japan
  • Asia/Tokyo

と複数の選択肢があるが、実体は同じ

~]$ md5sum /usr/share/zoneinfo/Asia/Tokyo /usr/share/zoneinfo/Japan
9e165b3822e5923e4905ee1653a2f358  /usr/share/zoneinfo/Asia/Tokyo
9e165b3822e5923e4905ee1653a2f358  /usr/share/zoneinfo/Japan

候補の中には America/Indiana/Marengo のように Area/Location の2階層で完結しないものもあるので、タイムゾーンを操作するプログラムを書いている人は注意が必要。

ハードウェアクロック向け設定

ファイル /etc/sysconfig/clockZONE エントリーを先ほど決めたタイムゾーンに変更する

~]$ cat /etc/sysconfig/clock
ZONE="Asia/Tokyo"
UTC=true

システムクロック向け設定

システムワイドにタイムゾーン設定を管理する /etc/localtime を修正
シンボリックリンクの向き先をさきほど決めたタイムゾーンに変更する

~]$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

設定を有効にする

~]$ sudo reboot

RHEL7(systemd)

RHEL7 からはタイムゾーンの操作は systemd の1コマンドである timedatectl 経由でするのがお作法

https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System_Administrators_Guide/chap-Configuring_the_Date_and_Time.html

タイムゾーンの一覧を取得

]$ sudo timedatectl list-timezones
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
Africa/Asmara
Africa/Bamako
...

設定を変更

~]$ sudo timedatectl set-timezone Europe/Prague

設定を確認

~]$ sudo timedatectl
      Local time: Sat 2015-02-07 09:35:20 CET
  Universal time: Sat 2015-02-07 08:35:20 UTC
        RTC time: Sat 2015-02-07 08:35:19
        Timezone: Europe/Prague (CET, +0100)
     NTP enabled: yes
NTP synchronized: yes
 RTC in local TZ: no
      DST active: no
 Last DST change: DST ended at
                  Sun 2014-10-26 02:59:59 CEST
                  Sun 2014-10-26 02:00:00 CET
 Next DST change: DST begins (the clock jumps one hour forward) at
                  Sun 2015-03-29 01:59:59 CET
                  Sun 2015-03-29 03:00:00 CEST

Ubuntu

Ubuntu14.04 からは timedatectl が使える(systemd-services パッケージの1プログラム)。

timedatestl が使えないことを前提に、次の wiki を参考に dpkg-reconfigure 経由で設定する。

https://help.ubuntu.com/community/UbuntuTime

システムクロック向け設定

Debian/Ubuntu 系は /etc/timezone でシステムのタイムゾーンを管理している。
RHEL6 の時と同じく /usr/share/zoneinfo 以下のタイムゾーン情報から設定したいタイムゾーンを調べて設定する。

~]$ echo "Asia/Tokyo" | sudo tee /etc/timezone
Asia/Tokyo

設定を有効にする

~]$ sudo dpkg-reconfigure --frontend noninteractive tzdata

Current default time zone: 'Asia/Tokyo'
Local time is now:      Sat Feb  7 16:03:24 JST 2015.
Universal Time is now:  Sat Feb  7 07:03:24 UTC 2015.

dpkg-reconfigure コマンドから --frontend noninteractive オプションを省くと、タイムゾーンを選びながら設定できる。

/etc/timezone と /etc/localtime の優先順位

/etc/timezone は Debian/Ubuntu 固有で /etc/localtime は Linux 全般で利用されている。
Ubuntu の wiki には /etc/timezone を変更する手順しか書かれていないけれども、素直に /etc/localtime を変更してもタイムゾーンは切り替わる。
dpkg-reconfigure --frontend noninteractive tzdata を実行した時に /etc/timezone/etc/localtime が食い違った時、どのように振る舞うのか確認してみた。

dpkg-reconfigure-priority

まとめると /etc/timezone/etc/localtime のどちらかが現在のタイムゾーンと異なっていればタイムゾーンも新しいものに変更され、現在のタイムゾーンと /etc/timezone/etc/localtime がすべて異なる場合 /etc/localtime の設定が優先される

Tagged with: , , , ,
Posted in linux
Archives
  • RT @__apf__: How to write a research paper: a guide for software engineers & practitioners. docs.google.com/presentation/d… /cc @inwyrd 6 months ago
  • RT @HayatoChiba: 昔、自然と対話しながら数学に打ち込んだら何かを悟れるのではと思いたち、専門書1つだけ持ってパワースポットで名高い奈良の山奥に1週間籠ったことがある。しかし泊まった民宿にドカベンが全巻揃っていたため、水島新司と対話しただけで1週間過ぎた。 それ… 7 months ago
  • RT @googlecloud: Ever wonder what underwater fiber optic internet cables look like? Look no further than this deep dive w/ @NatAndLo: https… 7 months ago
  • @ijin UTC+01:00 な時間帯で生活しています、、、 1 year ago
  • RT @mattcutts: Google's world-class Site Reliability Engineering team wrote a new book: amazon.com/Site-Reliabili… It's about managing produc… 1 year ago