pipeによるプロセス間通信

fork() で作成した子プロセスと親プロセスの間で情報のやり取りをするために,IPC(Inter Process Communication)の一つであるパイプを利用した.
一度理解してしまえば特に難しいものではなかったので,文章としてまとめておく.

パイプの概要

シェルを使用していると,「何かのコマンドの出力をgrepしたい」というとき等,あるコマンドの出力を別のコマンドの入力として扱いたいということが多々ある.このような場合,「パイプ」という機能を使って次のようにコマンドを実行することで実現できる.

$ cat something.txt | grep Hello

上記のコマンドを実行すると,catコマンドの出力から, Hello を含んでいる行のみを画面に出力させることができる.
(上記の例ではパイプを使わずともgrepコマンド単体で同じことが可能であるが)

このように,パイプの入口・出口となるファイルディスクリプタを接続することができるという機能を持つ.

パイプの利用

次のような簡単なサンプルプログラムを作成した.

特に難しいことはやっておらず,ただ単に fork() した後,親プロセスから子プロセスに文字列を送るだけのプログラム.

パイプを使うため, fork() を呼び出す前に pipe() を呼び出しておく.
pipe() システムコールを呼び出すと,引数に与えた配列の0番目に「読み取り用」,1番目に「書き込み用」のファイルディスクリプタを格納してくれる.
これらに対して書き込み・読み取りをすると,それぞれ対応するファイルディスクリプタから読み取り・書き込みを行うことができる.

pipe() を呼び出した跡は通常通り fork() を呼び出す.
これにより,子プロセスが作成され, pipe() によって作成されたファイルディスクリプタのペアも複製される.
その後,親プロセスと子プロセスで,次の必要ないファイルディスクリプタをそれぞれクローズしておく.

  • 親プロセス→読み取り用のファイルディスクリプタ( fds[0]
  • 親プロセスからは書き込みのみを行うため
  • 子プロセス→書き込み用のファイルディスクリプタ( fds[1]
  • 子プロセスからは読み取りのみを行うため

あとは,通常通り read()write() を呼び出すだけ.

今回は, fork() した後でも親・子ともに同じプログラムを実行していたが, execve() 等を使って子プロセスでは別のプログラムを動作させることももちろん可能.
この場合,子プロセスで標準入力からデータを読み込みたい場合は, dup2() を使って fds[0]0 (標準入力)に複製してあげると良い.

参考文献

  • Michael Kerrisk,Linuxプログラミングインタフェース,2012年12月 発行,ISBN978-4-87311-585-6

HTML等をGitHubへpushした時にWebサーバへ自動でデプロイする

HTMLなどの静的サイトをGitHubで管理していると,それを毎回手動でWebサーバへ反映させるのが面倒になってくる.

これを解決するために,GitHubとTravis CIを使って,GitHubのmasterブランチへpushした時に自動でWebサーバへデプロイする環境を作ったので,手順をまとめておく.

GitHubのリポジトリを準備

まずはHTML等を置いておくためのリポジトリをGitHubに作成しておく.

Travis CIとの連携

次に,Travis CIにGitHubのアカウントでログインする.

ログインしたら,右上のアカウント名の部分をから,「Accounts」へ移動する.すると,自分のGitHubリポジトリの一覧が表示される.
ここから,自動デプロイしたいリポジトリを探し出し,リポジトリ名の左側にある「×」をクリックして「◯」へ変更する.これでTravis CIとの連携設定が完了した.

サーバ側での設定

Webサーバ側で,自動デプロイ用に新しいユーザとグループを作成しておく.例ではCentOS7を使用している.

# useradd app
# passwd app
# groupadd deploy
# usermod -aG deploy app

ユーザとグループを作成し終えた後に,先程作成したユーザに切り替えてSSHのキーペアを作成する.この際, 作成するキーペアにパスフレーズを設定してはいけないことに注意する.ここでパスフレーズを設定してしまうと,Travis CIがWebサーバにデプロイしようとする際にパスフレーズ待ちで停止してしまうため,デプロイが完了しない.

# su - app
$ ssh-keygen
$ mv ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys

SSHキーペアの作成が完了したら,自動デプロイしたいリポジトリをサーバ側にcloneする.clone先はDocumentRootなど,実際に本番で配置する場所にする.

$ git clone <repository url> /path/to/document/root

リポジトリのclone後,Travis側からのpushを許可するために以下の設定を行う.

$ git config --local receive.denyCurrentBranch updateInstead

上記の設定まで完了したら,cloneしたリポジトリのディレクトリ内に移動し,SSH秘密鍵の暗号化を行う.暗号化にはTravis CLIを使用するため,必要に応じてインストールをしておく.

$ cd /path/to/repository
$ touch .travis.yml     # .travis.ymlを作成しておく
$ gem install travis -v 1.8.8 --no-rdoc --no-ri
$ travis login --org    # GitHubのアカウントでログイン
$ travis encrypt-file /path/to/private/key --add

秘密鍵の暗号化が完了すると, <秘密鍵の名前>.enc というファイルが生成されるので,存在を確認しておく.
暗号化した秘密鍵は,リポジトリ内に .travis ディレクトリを作成してその中に入れておくと良い.

$ mkdir .travis
$ mv id_rsa.enc .travis

ここまでの作業が終了したら,再度Travis CIの画面に戻る.先程GitHubリポジトリとTravis CIの連携を行った画面で,リポジトリ名の左にある歯車マークをクリックして設定画面へ移る.

設定画面へ移動したら,Environment Variablesの部分までスクロールし, encrypted_ から始まる環境変数が2つ設定されていることを確認する.もしこれらの変数が存在していない場合は,Travis CLIでログインが正しく出来ていないか,先程の秘密鍵暗号化の手順が上手くいっていない可能性があるので再度やり直す.

自動デプロイ用に次の3つの環境変数を追加する.追加の際,「Display value in build log」はOFFにしておく.これがONになっていると,ビルドログ中に設定した環境変数の値が表示されてしまう.

  • IP : デプロイ先WebサーバのIPアドレス
  • PORT : デプロイ先Webサーバへpushする際のポート.SSHを使う場合は22を指定する.
  • DEPLOY_DIR : Webサーバ内でのリポジトリ配置先を指定(例: /var/www/html ).

ここまで完了すればTravis CI側での設定は以上となる.

最後に, .travis.yml とデプロイ用に使用するシェルスクリプトを用意してリポジトリ内に配置する.
下記に .travis.yml と デプロイ用シェルスクリプト( deploy.sh )の例を掲載しておく.
.travis.yml 内の $encrypted_xxxxxx_key$encrypted_xxxxxx_iv に関しては,各自Travis CIのEnvironment Variablesに設定されていたものに変更する.

.travis.yml

addons:
    ssh_known_hosts: $IP

before_install:
- openssl aes-256-cbc -K $encrypted_xxxxxx_key -iv $encrypted_xxxxxx_iv
    -in .travis/id_rsa.enc -out .travis/id_rsa -d

script: ""

after_success:
    - ssh-keyscan -t rsa $IP >> ~/.ssh/known_hosts
    - bash .travis/deploy.sh

deploy.sh

#!/bin/bash

eval "$(ssh-agent -s)"
chmod 600 .travis/id_rsa
ssh-add .travis/id_rsa

git config --global push.default matching
git remote add deploy ssh://git@$IP:$PORT$DEPLOY_DIR
git push deploy master

.travis.yml はリポジトリの最上位階層, deploy.sh.travis ディレクトリ内に配置した.
最終的なディレクトリ構造は次のようになる.

.
├── index.html
├── .travis.yml
└── .travis
     ├── deploy.sh
     └── id_rsa.enc

ここまで完了したら,後はリポジトリにpushするたびに自動でTravis CIが動作し,サーバへ自動で変更を反映してくれるはず.

Linux パッケージ管理コマンド備忘録

よく忘れてしまうので備忘録として残しておく.

apt-get

APT (Advanced Packaging Tool) というパッケージ管理ツールに含まれるコマンド.
apt-getコマンドでパッケージ管理を始めるには,まず /etc/apt/sources.list にパッケージを管理しているサイトのURLを記述する.

$ apt-get [option] subcommand package-name
オプション 説明
-d ファイルをダウンロードする(インストールはしない)
-s システムを変更せず動作をシュミレートする
サブコマンド 説明
clean 過去に取得して保存中のパッケージを削除する
dist-upgrade システムを最新にアップグレードする
install パッケージをインストールまたはアップグレードする
remove パッケージをアンインストールする
update パッケージデータベースを更新する
upgrade システムの全パッケージを安全にアップグレードする

yum

yum (Yellow dog Updater, Modified) の設定は, /etc/yum.conf/etc/yum.repos.d ディレクトリ以下のファイルで行う.

オプション 説明
check-update アップデート対象のパッケージリストを表示する
update パッケージ名 指定したパッケージをアップデートする
install パッケージ名 指定したパッケージをインストールする
remove パッケージ名 指定したパッケージをアンインストールする
info パッケージ名 指定したパッケージの情報を表示する
list 全パッケージ情報をリスト表示する
repolist リポジトリ一覧を表示する
search キーワード パッケージ情報をキーワードで検索する
search all キーワード パッケージをキーワードで検索する(パッケージ名および説明文等すべて)
grouplist パッケージグループをリスト表示する
groupinstall グループ 指定したグループのパッケージをインストールする

Base64のデコード・エンコード

Webの世界などで使用されているBase64のデコード・エンコードについて.
CTF (Capture The Flag) でも使用されることがあるので,軽くまとめてみた.

UNIXコマンド

エンコード

Base64コマンドを使用する.

$ echo 'base64 encode' | base64
YmFzZTY0IGVuY29kZQo=

-iオプションを使用すると,ファイルから文字列を読み込んでエンコードする.
-oオプションを使用すると,結果をファイルに書き込む.

$ base64 -i input.txt -o output.txt

input.txtの中身

base64 encode

output.txtの中身

YmFzZTY0IGVuY29kZQo=

デコード

-Dオプションをつける.

$ echo 'YmFzZTY0IGVuY29kZQo=' | base64 -D
base64 encode

エンコードと同じく,-iオプションと-oオプションは有効.

Python

標準ライブラリであるbase64モジュールを使用する.

エンコード

base64.b64encode()メソッドを使う.

>>> import base64
>>> s = 'base64 encode'
>>> base64.b64encode(s.encode('utf-8'))
b'YmFzZTY0IGVuY29kZQ=='

デコード

base64.b64decode()メソッドを使う.

>>> import base64
>>> encoded = b'YmFzZTY0IGVuY29kZQ=='
>>> base64.b64decode(encoded)
b'base64 encode'

どちらのメソッドも引数,返り値ともにバイトオブジェクトということに注意.