計算機クラスタ等でlinuxbrewが使えないときの対処

# linuxbrewについて
既にいくつかの記事にも書かれています通り、linuxbrewは

- 管理者権限がなくてもパッケージのインストールが可能
- ホームディレクトリにインストールができる
- ディストロ非依存

というような一般人にとって大変ありがたいパッケージマネージャです。（[参考](https://qiita.com/thermes/items/926b478ff6e3758ecfea))
使いどころとしては、レンタルサーバなどで使う方が多いかもしれませんが、計算機クラスタやスパコンを利用する場合にも、管理者権限を持つことは通常ないので重宝するかと思います。

ところが、お使いの環境によっては上手く動作してくれない場合があるようです(してくれませんでした)。こんなに便利なものを使えないままにしておくのはもったいないので、なんとか動作するように工夫したことを紹介したいと思います。

**注意**
実際にソースコードに基づいて解決してはいないので、この記事で紹介するのはあくまで**対処法**です。これがベストな策とは言えません。試すときはシステムを壊したりしないようくれぐれも注意してください。
また、ここに書かれている方法以外にもっと良い方法があればぜひとも教えていただきたいと思っています。

# 症状と原因

まず、現象としては、linuxbrewを[公式HP](http://linuxbrew.sh/)の方法に従って、管理者権限のない環境でインストールしたところ

```
$ brew update
-e:1:in `flock': No locks available (Errno::ENOLCK)
	from -e:1:in `<main>'
Error: Another active Homebrew update process is already in progress.
Please wait for it to finish or terminate it to continue.
```

というエラーがでてしまうというものです。

もしお使いの環境が、計算機クラスタ等でNFSを利用してホームディレクトリを共有している環境である場合、[flock(2)のmanページ](http://man7.org/linux/man-pages/man2/flock.2.html)に記述のある

>In Linux kernels up to 2.6.11, flock() does not lock files over NFS
(i.e., the scope of locks was limited to the local system). 

あたりのことが原因かもしれません。このことは、[linuxbrewのIssues](https://github.com/Linuxbrew/brew/issues/129)にも記載がありました。その解決策としては`flock`が正常に動作する領域を利用するという方針のようです。


# 対処編
さて、ここからが本題ですが、状況をそろえるという意味でも、もう一度 linuxbrewをインストールしなおしてみましょう。そんなの良いから早く解決策見せてみろという方は、[真対処編](#真対処編)へお進みください。
まずは，リモートマシンにログインします。自分の環境は、

- CentOS Linux release 7.4.1708 (Core) 
- NFS：v3 (`nfsstat -m`　コマンドで確認できます。そのとき、`local_lock=none`とかなっているかもしれません)
- rubyなし

です。それではインストールしていきます。

### 1. ホームディレクトリへrubyのインストール
[公式HP](http://linuxbrew.sh/)には`sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"`を実行しろとありますが、いきなり無視します。というのも、このスクリプトではrubyの有無を確認して、あればシステムのものを使うような仕様ですが、何かとホーム以下のものを使ったほうがエラーの対処がしやすいかと思います。そこで、

```
$ cd $HOME
$ curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh　-o brew_install.sh
$ more brew_install.sh
#!/bin/sh
set -eu
which ruby >/dev/null || eval "`curl -fsSL https://raw.githubusercontent.com/Linuxbrew/
install/master/install-ruby`"
exec ruby -e "`curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/in
stall`"
```
このようにすると、`install.sh`がなにをしているかわかります。スクリプト2行目でrubyの有無を調べ、なければインストールする流れになっていますが、今回は有無にかかわらずインストールしてもらうために、`which ruby >/dev/null || `の部分と後半のbrew本体部分を削除します。改変後のスクリプトは以下のようになります。

```
$ more ruby_install.sh
#!/bin/sh
set -eu
eval "`curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install-ru
by`"
```
これを実行します。

```
==> Installing successful
==> /users/username/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.3/bin/ruby
ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-linux]
==> Add Ruby to your PATH by running:
PATH=/users/username/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.3/bin:$PATH
```
最後の行にあるように今インストールしたrubyのパスを`.bashrc`等に追加します。再ログインor`source .bashrc`でパスを反映させ、`which ruby`でパスを通したところになっていることを確認します。これで、ホーム以下へのrubyのインストールができました。

### 2.homebrew本体のインストール

続いて、homebrew 本体を入れていきます。こちらは、先程保存したスクリプトをそのまま実行します。基本的に指示通り進めて、`/home/username`以下にインストールしましょう。

```
Checking out files: 100% (1149/1149), done.
HEAD is now at 1d949e1 GlibcRequirement: Use OS::Linux::Glibc [Linux]
flock: 99: No locks available
Error: Another active Homebrew update process is already in progress.
Please wait for it to finish or terminate it to continue.
Failed during: /users/rshimura/.linuxbrew/bin/brew update --force

```
さて，このようなエラーを出すことができれば、無事__インストールは成功__です。
この時点で違うエラーが出てしまったり場合は、今までの手順を見直すか、そもそも、原因がflock以外にある可能性があるかもしれません（他の対処が必要）。


# 真対処編

対処としてはシンプルで、NFSでマウントしていないディレクトリである`/var/tmp/`以下を利用します。
homebrewがファイルロックしたいディレクトリは`.linuxbrew/var/homebrew/locks`です。よって、`/var/tmp/`以下から`.linuxbrew/var/homebrew/locks`へソフトリンクをはることで、対処することができます。

```
$ cd .linuxbrew/var/homebrew
$ mv locks bak.locks # 削除しても問題ないはずですが，一応とっておきます
$ mkdir /var/tmp/rshimura_locks
$ ln -s /var/tmp/rshimura_locks ./locks
```
これで、`brew update`などとすると、正常に動いてくれるようになっているはずです。

### 小細工

さて，インストール自体は完了しましたが，先ほどリンクを貼った`/var/tmp`は名前の通りテンポラリーなディレクトリなので，その下にあるものはシステムによって勝手に消されてしまうことがあります。毎度リンクを貼り直すのは面倒ですので，`.bashrc`などに次のような関数・エイリアスを定義します。
`lock_dir`は各自のお名前に変更してください。

```
alias brew='linuxbrew'
function linuxbrew(){
    lock_dir=/var/tmp/user-name_lock
    brew_path=$HOME/.linuxbrew/bin
    if [ -d $lock_dir ]; then
        $brew_path/brew $@
    else
        echo 'DO NOT exist lock file'
        echo "make directory $lock_dir ..."
        mkdir $lock_dir
        echo "remove invalid symlink ..."
        rm -rf $HOME/.linuxbrew/var/homebrew/locks
        echo "symlink to $HOME/.linuxbrew/var/homebrew/locks"
        ln -s $lock_dir $HOME/.linuxbrew/var/homebrew/locks
        $brew_path/brew $@
    fi
}
```

これで，`/var/tmp/`にlockディレクトリが確保されている場合は通常通り実行し，削除されてしまった場合には，新たに作り直し，リンクを貼ったのち，brewを実行してくれるようになります。