バッチ系システムの設計で
cron から定期実行
だけだとちょっとタイミング的に厳しいかも?
ということがあり
調べていたところSQLからシェルをキックできることが判明!
User Define Function
の頭文字からUDFと呼ばれてるものを使用すればイケるみたい
ということがわかったので
早速
どんなものなのかを試してみました。
そのときのメモを載せておきます。
##環境
MySQLが載ってるサーバです。
# cat /etc/issue
CentOS release 6.8 (Final)
Kernel \r on an \m
MySQLです。
mysql> SELECT version();
+------------+
| version() |
+------------+
| 5.7.12-log |
+------------+
1 row in set (0.16 sec)
##流れ的なもの
MySQLに自作のファンクションを作成し、登録する。
そのファンクションからシェルをキックするという流れです。
自作するファンクションはストアド感覚な感じですが
c++で作成したプログラムを登録することが可能。
cにはsystem関数という実行環境のコマンドをキックできる関数があるので
それを利用するという感じになります。
ということで、
まずはc言語のソースを。。
コピペでOKです。
#include <mysql.h>
#include <m_string.h>
extern "C" {
my_bool exec_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
my_ulonglong exec(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
void exec_deinit(UDF_INIT *initid);
}
// init
my_bool exec_init(UDF_INIT *initid, UDF_ARGS *args, char *message){
if(args->arg_count == 1 && args->arg_type[0]==STRING_RESULT){
return 0;
} else {
strcpy(message, "Expected exactly one string type parameter" );
return 1;
}
}
my_ulonglong exec(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error){
return system(args->args[0]);
}
void exec_deinit(UDF_INIT *initid){
// do nothing
}
次にこれをコンパイル。
ということでコンパイラーをインストール。
自分は以下のものを入れました。
- gcc-c++-4.4.7-17.el6.x86_64.rpm
- libstdc++-devel-4.4.7-17.el6.x86_64.rpm
yumでもokだと思います。
自分の場合、このサーバは外部につながってなかったのでrpmになりました・・・
コンパイラを入れたら上記ソースをコンパイルしてみます。
g++ -Wall -I/usr/include/mysql -shared -o exec.so -fPIC exec.cpp
成功すると exec.so がカレントに出力されるのでこれをmysqlのpluginにコピーする
cp exec.so /usr/lib64/mysql/plugin
あとはMySQLからcreate functionすれば完成です。
mysql> CREATE FUNCTION exec RETURNS int SONAME 'exec.so';
レプリケーションしている場合は要注意です!
##実行
早速実行するため適当なシェルを用意する
# cat /work/catt.sh
#!/bin/sh
ls -al /work > /tmp/catt
で、実行。
mysql> select exec("/work/catt.sh");
+-----------------------+
| exec("/work/catt.sh") |
+-----------------------+
| 32256 |
+-----------------------+
1 row in set (0.01 sec)
あれ?戻り値おかしいぞ?
/tmp を覗いてもファイルがないし。。
念のため
/var/log/mysqld.logを覗いてみる。
sh: /work/catt.sh: 許可がありません
え?シェルの実行権限あるよ!
一応chmodで777にしてみたけど結果は変わらず・・・
・・・ここでハマる事、数時間。
原因はコレでした
# getenforce
Enforcing
selinuxが有効だった・・・(ubuntu でいうところの apparmor)
ということでこれを解除します。
# setenforce 0
# getenforce
Permissive
この後、/etc/sysconfig/selinux を編集。
SELINUX=の右辺をdisabledに修正後、サーバーをrebootする。
ちと自分の場合、諸事理由ですぐにはrebootできなかったので
Permissive
のまま続行~
mysql> select exec("/work/catt.sh");
+-----------------------+
| exec("/work/catt.sh") |
+-----------------------+
| 0 |
+-----------------------+
1 row in set (0.02 sec)
お、成功か?
# ll /tmp/catt
-rw-r-----. 1 mysql mysql 234 10月 24 16:18 2017 catt
ファイル出力できてました。成功です。
##最後に
シェルをキックするのはこれでいけますが
シェルをキックして、その戻り値をselectでそのまま表示
というのはコレではできないので
キックしたシェルからDBに結果を書き込むなどの処理が必要になってきます。
この調査途中でシェルの結果を得るのもできそうだったので
今度時間あるときに調べてみようかと思います。
以上ご参考になれば。