読者です 読者をやめる 読者になる 読者になる

selinux上のCGIでjavaを動かせるようにする方法

これまで使ってたCGIを移設したら動かなくなって、その理由がselinuxのポリシーだった。 ポリシーの作り方と書き換え方をメモ。

環境はCentOS6.6で、kernel 2.6.32、apache2.2.15 この上でブラウザからjavaを実行しようとしたら

Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00007fcf589ac000, 2555904, 1) failed; error='Permission denied' (errno=13)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (malloc) failed to allocate 2555904 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /tmp/jvm-21000/hs_error.log

こんな感じのエラーが出力された。

コマンドラインで該当するjavaを動かすと正常に動作する。

とりあえずPermission deniedなことはわかったけど、なんのパーミッションがないのか分からず、検索したところ

In case anyone else is looking for a way to resolve this without explicitly disabling SELinux, the wiki page at centos.org helped me understand what was going on.
 
By default, the security context that Java is running in when called from Apache does not allow things like reading a file, allocating memory, accessing the network etc. The easiest way to resolve these errors is by using the semodule and audit2allow applications. Essentially, check the SELinux audit log (could be /var/logs/audit/audit.log, but I used /var/log/messages and extracted the avc errors). In the log you should see things like
linux - Run java in php apache2 - Super User

ということでどうやらselinuxだからメモリアロケーションの権限が無いらしい。ここに書いてあるwiki HowTos/SELinux - CentOS Wiki に解決法があった。

audit.logというログファイルをもとに追加するポリシーを作成するらしい。 まず作成・登録するためのツール群が入ってなかったのでインストール

$ sudo yum install policycoreutils-python

あとはcentos wikiと同じだけど、javaなので

$ sudo grep java_t /var/log/audit/audit.log | audit2allow -m javalocal > javalocal.te

としてjavalocal.teの中身を確認。

module javalocal 1.0;

require {
        type httpd_sys_script_t;
        class process execmem;
}

#============= httpd_sys_script_t ==============

#!!!! This avc is allowed in the current policy
allow httpd_sys_script_t self:process execmem;

関係ありそうなことが書いてあるか確認する。

上のようにcentos wikiに書いてあるとおりやったけど、下のように後から全部を読み込ませても同じ結果になることを確認した。audit2allowが良きに計らってくれるらしい。

$ sudo cat /var/log/audit/audit.log | audit2allow -m javalocal > javalocal.te

確認したら、実際に登録するppファイルを作成する

$ sudo grep java_t /var/log/audit/audit.log | audit2allow -M javalocal

これでjavalocal.ppができているので

$ sudo semodule -i javalocal.pp

で登録。

これで無事にCGIJavaを動かせるようになった。

IRangesパッケージのnarrowにおけるstart

例によってR言語上級ハンドブックを使った勉強会からのネタ。

Section26でBioconductorのIRangesパッケージを使った区間データの取り扱いのところで出てくるnarrow関数の引数がわかりにくかった。

narrow関数

こんな感じで区間を狭める

# Bioconductor のインストール
> source("http://bioconductor.org/biocLite.R")
> biocLite("IRanges")

# データの生成
> y.start <- seq.int(4, 22, by = 6)
> y.end   <- seq.int(7, 30, by = 6)

# IRanges オブジェクトに格納する
> y <- IRanges(y.start, y.end)
> y
> y
IRanges of length 4
    start end width
[1]     4   7     4
[2]    10  13     4
[3]    16  19     4
[4]    22  25     4

> narrow(y, start=1:3, width=2)
IRanges of length 4
    start end width
[1]     4   5     2
[2]    11  12     2
[3]    18  19     2
[4]    22  23     2

このnarrowの引数のstartがピンとこなかった。

このstartは各区間の何番目から開始するかという値でベクトルで与えると対応する区間が指定した場所からのスタートになる。
上の例だとc(1,2,3)というベクトルが与えられているので

  • 区間1(4〜7)では1番目の4から
  • 区間2(10〜13)では2番めの11から
  • 区間3(16〜19)では3番目の18から

となる。与えられなかった区間4(22〜25)では1番目の22から始まっている。

またendという引数もあって同様に終わるところを指定できる。

Rでマルチコア計算

R言語上級ハンドブックのChapter2 Section25「マルチコアで計算を繰り返し行う」でmulticoreパッケージを使った演算を行ってる。Windows環境ではこのパッケージが動かないので、ここにメモ。
計算はすべてMacでやってます。

実行時間を比較

テキストではモンテカルロ積分を使った演算をして、マルチコアを用いるmclapply、sapply、forの3通りの実装で速度を比較しています。

# テストデータを作成
> x.num <- 100
> x <- seq(0.1, 2.5, length = x.num)
> m <- 100000
> u <- runif(m)

# 並列化して確率密度関数を計算
> system.time(mclapply(x,
+   function(x) {
+     g <- x * exp( -(u * x)^2 / 2)
+     mean(g) / sqrt(2 * pi) + 0.5
+   }
+ ))
   ユーザ   システム       経過  
    38.962      7.810      0.152 

# 並列化せずに確率密度関数を計算 (sapply)
> system.time(sapply(x,
+   function(x) {
+     g <- x * exp( -(u * x)^2 / 2)
+     mean(g) / sqrt(2 * pi) + 0.5
+   }
+ ))
   ユーザ   システム       経過  
     0.228      0.126      0.354 

# 並列化せずに確率密度関数を計算 (for)
> system.time(numeric(x.num)
+ for ( i in 1:x.num ) {
+   g   <- x[i] * exp( -(u * x[i])^2 / 2)
+   cdf[i] <- mean(g) / sqrt(2 * pi) + 0.5
+ })
   ユーザ   システム       経過  
     0.226      0.110      0.335 

経過時間(テキスト137ページの表のelapsed)を比較するとmclapplyが倍以上速い。

mclapply sapply for
経過時間 0.152 0.354 0.335

計算してるiMacは4コアなので並列効率はそこそこ?

sapplyとforが似たり寄ったり

もっとsapplyのほうが速いって聞いてたんですけど。というわけでもうちょっと計算してみた。

# テストデータを作成
> x.num <- 100
> x <- seq(0.1, 2.5, length = x.num)
> m <- 1000000
> u <- runif(m)
> mc <- numeric(100)
> for(i in 1:100){
+     t <- system.time(mclapply(x,
+         function(x) {
+             g <- x * exp( -(u * x)^2 / 2)
+             mean(g) / sqrt(2 * pi) + 0.5
+         }
+     ))
+     mc[i] = t[3]
+ }

mを10倍にして100回施行。これをmclapply、sapply、forについてそれぞれ実行して、それぞれの経過時間のmeanとvarを計算。

mclapply sapply for
mean 1.88988 5.05238 5.06731
var 0.0006068137 0.01092876 0.008823186

せっかくなんでグラフにしてみた。

うーん、sapplyとforが変わらないな。

MacでRcppを動かしたい

珍しく続けて更新。
R言語上級ハンドブックを使った勉強会をしてまして、躓いたところを補完しておきます。

環境

MacOSX10.9 Marvericks、Xcode 5.0.1の上でThe R Project for Statistical Computingで配布されているR 3.0.2 binary for Mac OS Xを動かしてる。

C++のコードをコンパイルしようとするとエラー

Chapter1 section13 「S4/Refference ClassをRcppで生成する」ではC++のソースをコンパイルしてRで使おうっていう章でテキストは以下のような流れ。

> install.packages(c("inline", "Rcpp"))
> library(inline)
> library(Rcpp)
> inc <- '
using namespace Rcpp;
using namespace std;
class Student {
    :(中略)
}
'
> mod_std <- cxxfunction(signature(),plugin="Rcpp",includes=inc)
> mod_student <- Module("mod_student", getDynLib(mod_std))
> Student <- mod_student$Student
> student1 <- new (Student, 18, "Taro")

incにコードが入ってて、それをcxxfunctionでコンパイルしたい。
だがしかし、cxxfunctionを実行すると、

以下にエラー dyn.load(libLFile) : 
   共有ライブラリ '/var/folders/9r/x2b16sds2l30w0tds8p29k740000gn/T//RtmpbtB1sk/filed517926b08c.so' を読み込めません: 
  dlopen(/var/folders/9r/x2b16sds2l30w0tds8p29k740000gn/T//RtmpbtB1sk/filed517926b08c.so, 6): Symbol not found: __Z8demangleRKNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
  Referenced from: /var/folders/9r/x2b16sds2l30w0tds8p29k740000gn/T//RtmpbtB1sk/filed517926b08c.so
  Expected in: flat namespace
 in /var/folders/9r/x2b16sds2l30w0tds8p29k740000gn/T//RtmpbtB1sk/filed517926b08c.so

なんかのダイナミックライブラリが読み込めてないって怒られる。
調べてみたところ、Rをコンパイルした環境と使ってる環境が違うんじゃないかって話で、具体的にはRはlvm-gcc 4.2を使ってコンパイルされてて、MacOSXコンパイラがclang++なのが問題っぽい。

Rをコンパイルすればいい

手っ取り早く解決するためにRをコンパイルすることにした。とはいってもソース落としてきたりするのは面倒なのでhomebrew*1で一発解決。

$ brew install R

このRは/usr/local/bin/にインストールされる*2。先にあったバイナリ版のRは/usr/bin/Rにシンボリックリンクがあるのでこれを消して新しくbrew版のRのリンクを置いておく。これでRStudioからも見える。

$ rm /usr/bin/R
$ ln -s /usr/local/bin/R /usr/bin

これで無事にC++のコードをコンパイルすることができた。

Windows環境で出るエラーについて

以下のような別のエラーが出てて

Error in compileCode(f, code, language = language, verbose = verbose) : 
  Compilation ERROR, function(s)/method(s) not created! 
In addition: Warning message:
running command 'C:/PROGRA~1/R/R-30~1.2/bin/x64/R CMD SHLIB file156c720a4197.cpp 2> file156c720a4197.cpp.err.txt' had status 1

ざっと調べたところfortranコンパイラが足りないとかなんとか。とりあえずいろいろ導入してくれるRTools(http://cran.r-project.org/bin/windows/Rtools/)っていうのを入れるといいらしい。Rcpp・inlineパッケージを使ってC++とRを連携させる - My Life as a Mock Quantに詳しく書いてあったので見てみるといいかもしれません。

【おまけ】RStudioでC++コードを簡単にコンパイルする

  1. あらかじめRcppとinlineパッケージを読み込んでおく
  2. 左上の新規作成アイコンの右の小さい矢印からプルダウンメニューを開いてC++Fileを選択
  3. 開いたウインドウにコードを入力して保存(名前つける。例えばhoge.cpp)
  4. コンソールからsourceCpp("hoge.cpp")を実行
  5. 使える

*1:homebrewのインストールと使い方はHomebrewの使い方 - maruko2 Note.とかを参考に。

*2:本当は/usr/local/Cellar/r/3.0.2/bin/Rにあるけどわかりやすいからこっちで

正規表現の後読みと先読みとAtomic grouping

R言語上級ハンドブックを使って勉強会をしてて、perl互換の正規表現というところで引っかかったのでメモ。
31ページから引用

# Perl互換の正規表現による柔軟な文字列抽出
text <- "@bar foo@example.com @foo@"
# 先読み・後読みなどの利用
strapply(text, "(?<!\\w)@(?>\\w+)(?!@)", backref = 0, perl = TRUE)

\が二重になってるのはRの仕様で文字列中の\をエスケープしないと関数に\として渡せないかららしい。

perl正規表現では(?~)は特別な意味を持っていて、そのうち先読みと後読み、それからAtomic groupingの3つがここで使われてる。
ちなみに上記のマッチの結果は@barになる。

先読みと後読み

マッチする条件を前後に拡張する。

パターン 意味
(?<=hoge) 前にhogeがあるときだけマッチ
(?<!hoge) 前にhogeがないときだけマッチ
(?=hoge) 後にhogeがあるときだけマッチ
(?!hoge) 後にhogeがないときだけマッチ

さっきの正規表現の最初と最後はそれぞれ

(?<!\\w)
(次に続く@の)前に\wがない
(?!@)
(前にある“@(?>\\w+)”の)後に@がない

という意味になる。

先読みと後読みを効果的に使う例として桁のカンマを挿入するcommafying numbersが有名で

my $text = "The population of 298444215 is growing";
text =~ s/(?<=\d)(?=(\d\d\d)+(?!\d))/,/g;
print "$text\n";
# The population of 298,444,215 is growing

とかできる。

atomic grouping

真ん中の正規表現(?>\\w+)はatomic groupingというやつで「強欲な」マッチングをする。
強欲の説明はコードをみたほうが話が早い。

my $text = "something";
if($text =~ /\w+ing/){
    print "MATCH\n";
}else{
    print "NOT match\n";
}
if($text =~ /(?>\w+)ing/){
    print "MATCH\n";
}else{
    print "NOT match\n";
}

これを実行すると

MATCH
NOT match

となる。
はじめの\w+は単純な正規表現ですべての半角英数字とアンダースコアが1つ以上続くという意味。そのあとにingが続いている。この場合マッチングではingの部分はパターン中のingに譲って\w+はsomethにマッチする
つぎの(?>\w+)は「強欲な」マッチングで後に続くingに譲らない。そのため(?>\w+)がsomething全体にマッチする。結局(?>\w+)ingはsomethingにマッチできない。

このテキストで、atomic groupingを使って結果が変わるわけでもないのにどうして採用したのか分からないけど、強欲なマッチングについて知らなかったのでタメにはなった。

参考にした本

詳説 正規表現 第3版

詳説 正規表現 第3版

R言語上級ハンドブック

R言語上級ハンドブック

LDRからホットキー一発でPocketに投げるUserScript

iPhoneにしてReeder使い始めるためにGoogleReaderに変更したんで約4年弱かな。LDRに返り咲いた。
その記念にChromeでPocketにホットキー一発でポストできるスクリプトを今の仕様で動くようにしてみたよ。
Pocket from LDR for Greasemonkey

実はこのままだと使えなくて各自書きなおしてもらわないといけないのでめんどくさい。すまん。
詳しい使い方とかはREADMEかlivedoor readerからPocketにワンキーで投稿する | ふて寝。で。

gitの人はこちらからどうぞ。
Pocket from LDR for Greasemonkey