20200628のLinuxに関する記事は4件です。

LinuxのOpenVPNですべてのトラフィックをVPN経由にする方法

.ovpnファイルに下記の一行を追加して接続する。

redirect-gateway def1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ルートディレクトリ(/)とホームディレクトリ(~)のちゃんとした理解

ものすごい細かいことだけど、パスの指定方法での~(チルダ)と/(スラッシュ)の理解が曖昧で気持ち悪い思いをしたのでメモ。

/: ルートディレクトリ
~:今のユーザーのホームディレクトリ
~taro: taroというユーザーのホームディレクトリ

スラッシュの意味合い

ルートディレクトリの/と、各ファイルやディレクトリの前につく/は意味合いが違っている模様。

  • 前者:ルートディレクトリそのもの
  • 後者:ディレクトリを区切るもの

なので、一見ルートディレクトリのせいで「ディレクトリとは末尾にスラッシュが付いているもの」という勘違いを(少なくも筆者は)しちゃうが、hogehoge/がディレクトリなのではなくhogehogeがディレクトリなのだ。ホームディレクトリを~/だと思ってしまっている人は多いのではないか?

~ユーザー名

また~taroでtaroさんのホームディレクトリを指定できるのは初耳で、結構ググったけどこれについて深く語っている記事は見当たらなかった。こういう指定の仕方があるんですね。

スクリーンショット 2020-06-28 15.57.03.png

teratailで教えてもらった?
https://teratail.com/questions/273447

Node.jsのSass特有の書き方

今回がそもそも、RailsアプリでBootstrapを導入しようとして疑問にわいたことだったのだが、Node.jsのSass特有の書き方として、以下のような相対パス?の書き方が使えるらしい。

@import '~bootstrap/scss/bootstrap';

上記はmyapp/node_modules/bootstrap/dist/css/bootstrap.cssを指定できる。モジュールをユーザーに見立てているのかな?

まとめ

こういうのって「そういうもの」と暗記する人が多いと思うんだけど、数学の公式と一緒で「なぜそうなるのか?」という部分を理解したほうが知識を応用できると思うんです。

今回のものを踏まえると、WebのURLでhttps://example.jphttps://example.jp/のどっちを正規URLとすべきかという問題は、前者のほうが本来は正しいのではないかと考えちゃう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Activiti 6 のインストール

試したのでメモ

前提

  • JDK インストール済み
  • Tomcat インストール済み

手順

  1. あらかじめ Activiti 6 の WAR ファイルをサーバに配置しておく

    # curl -LkvOf https://github.com/Activiti/Activiti/releases/download/activiti-6.0.0/activiti-6.0.0.zip
    
  2. Activiti 6 実行用ユーザを作成する

    # useradd activiti
    
  3. Activiti 6 用のディレクトリを作成する

    # mkdir /opt/activiti
    
  4. Tomcat のディレクトリを作成したディレクトリ内に配置する

    # mv /root/apache-tomcat-8.5.56 /opt/activiti/tomcat
    
  5. デフォルトで配置されている Web アプリを tomcat/webapps から削除する

    # rm -rf /opt/activiti/tomcat/webapps/*
    
  6. Activiti 6 の WAR ファイルを tomcat/webapps 配下に配置する

    # cp /root/activiti-*.war /opt/activiti/tomcat/webapps/
    
  7. tomcat/bin/setenv.sh ファイルを作成する

    # vi /opt/activiti/tomcat/bin/setenv.sh
    
    CATALINA_PID=$CATALINA_HOME/activiti.pid
    
    ### 以下は JDK 11 のときに記載
    CATALINA_OPTS="--add-opens=java.base/java.lang=ALL-UNNAMED \
        --add-opens=java.base/java.io=ALL-UNNAMED \
        --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED"
    export CATALINA_OPTS
    
  8. 所有者を変更する

    # chown -R activiti: /opt/activiti
    
  9. サービスファイルを作成する

    [Unit]
    Description=Activiti Service
    
    [Service]
    Type=forking
    User=activiti
    PIDFile=/opt/activiti/tomcat/activiti.pid
    ExecStart=/opt/activiti/tomcat/bin/startup.sh
    ExecStop=/opt/activiti/tomcat/bin/shutdown.sh
    
    [Install]
    WantedBy=multi-user.target
    
  10. 起動する

    # systemctl daemon-reload && systemctl start actitivi
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

signal送信元を知る

はじめに

プロセス間の非同期通信としてよく使われるsignalですが、
送信元を知る必要があり方法を調べてみたので備忘メモとして残しておきます。

試した環境は以下のとおりです。

$ lsb_release -d
Description:    Ubuntu 18.04.4 LTS
$ uname -r
5.3.0-61-generic
$ trace-cmd --version | grep version
trace-cmd version 2.6.1
$ stap --version | head -1
Systemtap translator/driver (version 4.3/0.170, commit release-4.3-0-gc9c23c987d81)

方法1. sigaction(2)で知る

signalを受け取るプロセスで送信元の情報を取得するアプローチです。

signalを受け取るプロセスを変更することができて、
かつプロセスでハンドリングできるsignalが調査対象の場合に使える方法です。
SIGKILLのようにプロセスでハンドリングできないsignalには使えません。

sigaction(2)は、sa_flagsSA_SIGINFOを指定することで、
シグナルハンドラでsignalに関する詳細な情報siginfo_tを受け取ることができるようになります。
si_pidで送信元のPIDを、si_uidで送信元の実ユーザIDを知ることができます。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

static int g_sig;
static siginfo_t g_siginfo;

static void
my_sigaction (int sig, siginfo_t *info, void *ucontext)
{
  g_sig = sig;
  // memcpy(3) is a async-signal-safe function
  // according to man signal-safety(7)
  memcpy (&g_siginfo, info, sizeof(g_siginfo));
}

static char*
code2str (int code)
{
  switch (code)
    {
    case SI_USER: return "SI_USER";
    case SI_KERNEL: return "SI_KERNEL";
    case SI_QUEUE: return "SI_QUEUE";
    case SI_TIMER: return "SI_TIMER";
    case SI_MESGQ: return "SI_MESGQ";
    case SI_ASYNCIO: return "SI_ASYNCIO";
    case SI_SIGIO: return "SI_SIGIO";
    case SI_TKILL: return "SI_TKILL";
    default: return "unknown";
    }
}

int
main (void)
{
  struct sigaction act;
  act.sa_flags = SA_SIGINFO;
  act.sa_sigaction = my_sigaction;
  int ret = sigaction(SIGTERM, &act, NULL);
  if (ret < 0)
    {
      perror ("sigaction");
      exit (EXIT_FAILURE);
    }
  printf ("pid: %d\n", getpid());

  sleep (10000);

  fprintf (stderr, "sig: %d, si_pid: %d, si_uid: %d, si_code: %s\n",
           g_sig,
           g_siginfo.si_pid,
           g_siginfo.si_uid,
           code2str(g_siginfo.si_code));

  return 0;
}

方法2.trace-cmd (=ftrace)で知る

kernelは誰が誰にsignalを送ったか知っているので、kernelのイベントをトレースすることで調べるアプローチです。
kernelのイベントトレースには、ftraceを使います。

ftraceでsignal_generateのイベントをトレースすることで送信元を特定することができます。
debugfsからftraceを直接操作してもいいですが、ここではtrace-cmdを使ってトレースする方法を紹介します。

[トレース開始]
$ sudo trace-cmd start -e signal_generate -f 'sig==9 && pid == 6985'

[トレース終了]
$ sudo trace-cmd stop

[トレース結果の確認]
$ sudo trace-cmd show
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1   #P:8
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
     test_sender-6993  [001] d...  1660.447368: signal_generate: sig=9 errno=0 code=0 comm=test_receiver pid=6985 grp=1 res=0

pid=6985のプロセスにsig=9を送ったのは、test_senderというPIDが6993のプロセスであることがわかります。

ここでsignal_generateイベントは大量に発生するので、上記のように-fオプションを使い適切にフィルタをかけたほうが良いです。
上記では、signal番号とsignalを受け取るpidでフィルタしています。

フィルタ関数で使えるfield名は、trace-cmd listコマンドで確認できます。

$ trace-cmd list -F -e signal_generate
system: signal
name: signal_generate
ID: 194
format:
    field:unsigned short common_type;   offset:0;   size:2; signed:0;
    field:unsigned char common_flags;   offset:2;   size:1; signed:0;
    field:unsigned char common_preempt_count;   offset:3;   size:1; signed:0;
    field:int common_pid;   offset:4;   size:4; signed:1;

    field:int sig;  offset:8;   size:4; signed:1;
    field:int errno;    offset:12;  size:4; signed:1;
    field:int code; offset:16;  size:4; signed:1;
    field:char comm[16];    offset:20;  size:16;    signed:1;
    field:pid_t pid;    offset:36;  size:4; signed:1;
    field:int group;    offset:40;  size:4; signed:1;
    field:int result;   offset:44;  size:4; signed:1;

方法3.systemtapで知る

方法2と同じく、kernelのイベントをトレースすることで調べるアプローチです。
kernelのイベントトレースにsystemtapを使います。

下記のようにワンライナーでモニタできます。

$ stap -e 'probe signal.send { if (sig==9 && sig_pid==8722) printf("%s: %s(%d) -> %s(%d)\n", sig_name, execname(), pid(), pid_name, sig_pid) }'
SIGKILL: test_sender(9191) -> test_receiver(8722)

スクリプトにして、ついでに引数取れるようにすると以下のようになります。
(qiitaにsystemtapモードがないようなので、c言語モードにしています)

signal_sender.stp
#!/usr/bin/env stap
probe signal.send
{
  if (sig==strtol(@1,10) && sig_pid==strtol(@2,10))
    printf("%s: %s(%d) -> %s(%d)\n", sig_name, execname(), pid(), pid_name, sig_pid)
}

systemtapはPCだとこのようにスマートにできるのですが、
組み込みだとコンパイル環境と実行環境が別になるため手順が少し煩雑になるのが欠点です。

まとめ

他にもあると思いますが、選択肢として3つ紹介しました。
調べた中にはSELinuxのaudit.logを利用するなんて方法もありました(ご興味があればこちらをご覧ください)。
個人的には、使いやすさと環境のハードルのバランスがいいのは、ftrace(trace-cmd)かなと思ってます。
ちょっと力尽きてしまいましたが、eBPFでもできるでしょうからeBPF版もそのうち追記したいです。

参考

Man page of SIGACTION
trace-cmd(1) - Linux man page
stap(1): systemtap script translator/driver - Linux man page
trace-cmdコマンドの使い方 - Qiita
プロセスへの SIGKILL の送信元を追跡する方法 - Red Hat Customer Portal
takeoverjp/test_signal: signal simple test program

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む