それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

php7で拡張機能のAPIが変わって動かなくなってる pamをなんとかした

php 7 で pamモジュールが動かなかった。

install してもPHPのPAMが動かなかったんですね。

PHPC言語APIが変わってた

コンパイルエラーなので、調べていくと型宣言辺で落ちてた。 調べていくと、拡張機能APIが更新されてたことがわかったなど

この方法で、PAMを使ったphp拡張の作り方がわかった。

PHPの拡張をCで書く方法が分かったし世界が広がリング

http://keicode.com/cgi/how-to-handle-parameters-php-extension.php

php-pamについて

ドコをどう書き換えるべきか書いてあるのはこちら。

https://wiki.php.net/phpng-upgrading

--- /tmp/pam-1.0.3/pam.c        2009-11-29 19:49:35.000000000 +0900
+++ pam.c       2017-10-19 01:52:15.028468810 +0900
@@ -227,8 +227,13 @@
 PHP_FUNCTION(pam_auth)
 {
        char *username, *password;
-       int username_len, password_len;
-       zval *status = NULL, **server, **remote_addr;
+       size_t username_len, password_len;
+       /* zval status; */
+       /* zval *server; */
+       /* zval *remote_addr; */
+       zval *status = NULL;
+       zval *server;
+       zval *remote_addr;
        zend_bool checkacctmgmt = 1;

        pam_auth_t userinfo = {NULL, NULL};
@@ -244,26 +249,28 @@
        userinfo.name = username;
        userinfo.pw = password;

+       /* printf("Hello"); */
+
        if ((result = pam_start(PAM_G(servicename), userinfo.name, &conv_info, &pamh)) != PAM_SUCCESS) {
                if (status) {
                        spprintf(&error_msg, 0, "%s (in %s)", (char *) pam_strerror(pamh, result), "pam_start");
-                       zval_dtor(status);
-                       ZVAL_STRING(status, error_msg, 0);
+                       ZVAL_STRING(status, error_msg);
+                       efree(error_msg);
                }
                RETURN_FALSE;
        }

-       if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&server) == SUCCESS && Z_TYPE_PP(server) == IS_ARRAY) {
-               if (zend_hash_find(Z_ARRVAL_PP(server), "REMOTE_ADDR", sizeof("REMOTE_ADDR"), (void **)&remote_addr) == SUCCESS && Z_TYPE_PP(remote_addr) == IS_STRING) {
-                       pam_set_item(pamh, PAM_RHOST, Z_STRVAL_PP(remote_addr));
+       if ( (server=zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1 ) )  != NULL  && Z_TYPE_P(server) == IS_ARRAY) {
+               if ( ( remote_addr = zend_hash_str_find( Z_ARRVAL_P(server), "REMOTE_ADDR", sizeof("REMOTE_ADDR")-1 ) )   != NULL && Z_TYPE_P(remote_addr) == IS_STRING) {
+                       pam_set_item(pamh, PAM_RHOST, Z_STRVAL_P(remote_addr));
                }
        }

        if ((result = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS) {
                if (status) {
                        spprintf(&error_msg, 0, "%s (in %s)", (char *) pam_strerror(pamh, result), "pam_authenticate");
-                       zval_dtor(status);
-                       ZVAL_STRING(status, error_msg, 0);
+                       ZVAL_STRING(status, error_msg);
+                       efree(error_msg);
                }
                pam_end(pamh, PAM_SUCCESS);
                RETURN_FALSE;
@@ -274,7 +281,8 @@
                        if (status) {
                                spprintf(&error_msg, 0, "%s (in %s)", (char *) pam_strerror(pamh, result), "pam_acct_mgmt");
                                zval_dtor(status);
-                               ZVAL_STRING(status, error_msg, 0);
+                               ZVAL_STRING(status, error_msg);
+                               efree(error_msg);
                        }
                        pam_end(pamh, PAM_SUCCESS);
                        RETURN_FALSE;
@@ -312,7 +320,8 @@
                if (status) {
                        spprintf(&error_msg, 0, "%s (in %s)", (char *) pam_strerror(pamh, result), "pam_start");
                        zval_dtor(status);
-                       ZVAL_STRING(status, error_msg, 0);
+                       ZVAL_STRING(status, error_msg);
+                       efree(error_msg);
                }
                RETURN_FALSE;
        }
@@ -321,7 +330,8 @@
                if (status) {
                        spprintf(&error_msg, 0, "%s (in %s)", (char *) pam_strerror(pamh, result), "pam_authenticate");
                        zval_dtor(status);
-                       ZVAL_STRING(status, error_msg, 0);
+                       ZVAL_STRING(status, error_msg);
+                       efree(error_msg);
                }
                pam_end(pamh, PAM_SUCCESS);
                RETURN_FALSE;
@@ -331,7 +341,8 @@
                if (status) {
                        spprintf(&error_msg, 0, "%s (in %s)", (char *) pam_strerror(pamh, result), "pam_chauthtok");
                        zval_dtor(status);
-                       ZVAL_STRING(status, error_msg, 0);
+                       ZVAL_STRING(status, error_msg);
+                       efree(error_msg);
                }
                pam_end(pamh, PAM_SUCCESS);
                RETURN_FALSE;

htop の更新頻度(インターバル)を指定する

htop コマンドで更新の時間を指定する。

htop -d X

X には in tenths of second つまり 10分のX秒で更新される

何もしないとデフォルトは2秒(X=20)だった

1秒おきに更新させるには

htop -d 10

0.5 秒間隔の更新は

htop -d 5

こんな感じ。

わかりにくいよねコレ ミリ秒とか指定できたほうが便利だよね・・・

htop はの更新間隔はデフォルト2秒ってことがわかったことのほうが大きいかな。

WEBユーザのログイン認証をssh経由でやる、割と強引な方法

認証面倒くさい。

LDAPで連携くんだり、PAMで認証したり、OAuthしたり、ユーザ認証をする方法は山ほどある。 でも、それぞれ一長一短があってそれらを解決するいい方法がほしいと思っていた。

WEBのユーザのログイン認証をSSHでやる

ひょんなことから、sshpass というコマンドを知ったので、これをつかって、WEBなどのユーザ認証をローカルのLinuxユーザーとパスワードで出来るんじゃないかと思ってやってみた。

仕組み

フォームから受け取ったユーザ名パスワードをつかってSSHで認証してOKならセッションを発行する

ssh のコマンドはパスワードを受け付けないし、expectなどは面倒なので sshpass コマンドを使うと解決する

サンプルコード

<?php
function alternative_pam_auth_by_shell_ssh( $user , $pass ){

  $allow_group='students'; //このグループに所属するユーザーだけに限定する
  if ( ! check_user_group($user, $allow_group ) ) {
    return false;
  }

  $sshpass = exec('/usr/bin/which sshpass');
  $user_esc = escapeshellarg($user);
  $pass_esc = escapeshellarg($pass);

  $cmd = "{$sshpass} -p ${pass_esc}  ssh -o 'StrictHostKeyChecking no' {$user_esc}@192.168.0.22 whoami 2>&1";
  $str = exec($cmd,$out,$ret);
  if ( $ret > 0  ){
    return false;
  }
  return strpos( $str, $user ) !== false;
}

root に総当りされる・・・

このままだとroot に総当りされるので、WEBからのssh経由でログインできるユーザのグループを限定する

<?php
function check_user_group( $user, $group ) {

  $user = escapeshellarg($user);
  $groups = exec('/usr/bin/which groups');
  $cmd = "{$groups} '${user}'";
  $ret = exec($cmd, $code );
  $ok = strpos($ret ,$group) !== false ;

  return $ok;
}

割りと解決する

ユーザのログインを別個に作るのは、面倒だし。登録画面も面倒だ、SSHSSHで管理したいと思うし。

かといって、次のアイデアには各種の懸念もある。

  • LDAPにユーザ管理を任せるの設定が煩雑だ
  • LDAPだととldif を覚えるのがメンテ上のハードルになり教えるのが面倒だ。
  • PAMだとwww-data に 読み取り権限を与える必要がある。
  • あらゆるプログラムが/etc/shadow を読み取るわけにも行かないし。
  • setuid したコマンドを別途用意するのも面倒だ。
  • OAuthにたよるとSSHログインと管理が別になる
  • SSHをOAuth対応させるとなるとAuthenticator必須になったりコレも面倒だ。
  • MySQLをPAM認証してMySQLに投げるとshell経由じゃないと怒られる

どの方法も一長一短があってむずかしい。

SSH に丸投げでどうだろう。

そこで、ユーザー認証してくれるプログラムで常時起動してるものに認証を投げたら楽じゃんと思って試してみた。

sshにお願いするのはパスワードのチェックだけで権限などは別にいらないしセッション管理もWEBは独立してるので楽だ。 こういう専用のライブラリがアレば良いのだけれど。。。

しかたないんので こういう sshヒューリスティックなハックに頼ることになった。

どうせどの方法もメリットデメリットがあるので、一番手軽な方法があっても良いんじゃないかと思ったり。

認証ハック関連の商品

gitignroeの除外まとめ2〜指定ディレクトリのみ有効化 - 除外の無視で特定フォルダを管理下に

指定したフォルダだけをgit対象にしたいことありませんか?

10個あるファイルのうち1ファイルだけをgit対象にし、残り9ファイルを無視したい。

私であれば ln -sシンボリックリンクを使うのですが、世の中にはシンボリックリンクを使えない不幸な人達と環境があるのです。wordpress のみが動くレンサバにFTPアクセスをしている場合とか。

オススメは ~/.configのgit管理

全部除外して、必要なものだけをいれる。

# ignore all
*
# ## but exclude thees files
!rclone
!rclone/*
!nvim
!nvim/*

ディレクトリの指定とファイルの指定を2重に書いてるのはgitの空フォルダの仕様と、フォルダと無視の取扱にある。

だめな例

*
!rclone/

フォルダだけ対象にすると rclone/は入るけど、中のファイルは先頭の * にマッチして無視される。

そしてgitには空フォルダに見える。gitは空フォルダを無視するので、結果として除外の無視を書いた結果でも

このために、フォルダの中身も無視除外と書く必要があるわけ。

この辺が分からず、サブディレクトリの無視の除外設定に手間取る人が多い様に思った。とくにQootaとか見てると。

gitignore の無視ファイルで無視したい

ワードプレスを例に説明します。

wordpress の直下のファイルで wp-content / wp-config だけを対象にしたい。

f:id:takuya_1st:20180305172803p:plain

gitignore で指定ファイルだけを管理下に置く(ignoreの除外)を使う

最初に、全部除外して、必要なファイルだけを git に入れます。

## 全部除外する
*
####################
## 必要なものだけをいれる
!wp-config.php
!wp-content/
!wp-content/*

サブディレクトリのサブディレクトリだけを入れたいときは?

再帰的にフォルダ内部を最下層まで除外に含めるときは、次のようにbash見たいなワイルドカードを書いてあげると上手く当たる。

## 全部除外する
*
####################
## 必要なものだけをいれる
!wp-config.php
!wp-content/
!wp-content/*
!wp-content/thmes/**

これで、好きな場所をgit 管理して、好きなようにフォルダをgit push して楽ができるよ。

man 読んだら書いてた

 A trailing "/**" matches everything inside. 
For example, "abc/**" matches all files inside directory "abc", relative to the location of ...(..)

参考資料

  • man gitignore

gitignore は man に記述在るので楽だよね*1

ignore and commit に関係する商品

*1:ポエム書く前に man 読んでほしいけど、man よむよりググちゃうんだろうな。

SSHのパスワード認証をシェル経由で手軽に行えるコマンド

ssh のパスワード認証を使いたい。

ssh で鍵登録は場合によって面倒くさい。

パスワードは弱いってのはわかる。でもシェルを実行するユーザごとに鍵をつかったり鍵管理は煩雑になる。

sshpass コマンド

ssh の認証をパスワードで、シェルコマンドでおこなえるコマンドが作られていた。それがsshpass

apt install sshpass

使い方

使い方も超簡単です。基本的な使い方は次の通り。

sshpass -p 'MySecretPassword' ssh username@example.com

シェルコマンドの実行に便利

SSH経由でリモートサーバーにログインをしてコマンドを実行したいときにパスワード認証でぱぱっと作ることが出来る。

私はこれをつかって raspi を一斉に更新したり、raspiに動画再生させたりしてる。手軽で便利すぎ

#!/usr/bin/env bash 

function get_pass(){
  ## ここで何かパスワード取り出し処理
}

MY_PASSWORD=$( get_pass )

sshpass -p MY_PASSWORD  ssh username@example.com /usr/local/sbin/remote_command

利用上の注意点

SSHdにパスワードログインを許可するのは基本的に危険。

SSHのパスワード認証は総当りで絶対突破されると思ったほうが良い。

そのためパスワード認証を有効にするときは /etc/ssh/sshd_config でサーバーへのパスワードをローカルIPに限定するなどの処理を書いておくほうが無難。ルーターがミライに乗っ取られたときはアウトだけど・・・

PasswordAuthentication no
## 指定アドレスからのSSHログインのみに限定する
match address 192.168.0.*
  PasswordAuthentication yes
## 指定ユーザにだけSSHパスワードログインを許可する
### ユーザ名にも総当りされないようにランダムキーを含めたほうが無難
match user my-own-admin-5ooOuA8bXKesVqCQ
  PasswordAuthentication yes

他にもポートを変えるとか

# What ports, IPs and protocols we listen for
Port 2223
Port 2222

他にも iptables recent を有効にするとか、Listenアドレスからv6を外すとか、あれこれ対策をした上でパスワードを使ったほうが良いと思います。

関連資料

参考資料

https://unix.stackexchange.com/questions/38737/ssh-login-with-clear-text-password-as-a-parameter

関連アイテム

OpenSSH[実践]入門 Software Design plus

OpenSSH[実践]入門 Software Design plus

nginx で特定の拡張子をphpで処理する(SetHandler代替)

特定の拡張子をphpなどで処理する

css や js だけじゃなく 画像などをphpで処理したい。Apacheだと addhandler / sethandler で出来るアレ。nginx でもやろうかなと。

画像のリクエストログやリサイズをWebサーバーでやってるとかったるいので、phpで処理してキャッシュ制御すれば楽だよね。セッションチェックとかさ。

nginx での設定例

拡張子php と zip を php-fpmに飛ばしている例。

    location ~ \.(php|zip)$ {
      include fastcgi_params;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      fastcgi_param SCRIPT_FILENAME /var/www/example.com/public/info.php$fastcgi_script_name;
      fastcgi_split_path_info ^(.+\.php)(/.+)$;
      fastcgi_pass unix:/path/to/php-fpm.sock;
    }

ポイント

SCRIPT_FILENAME の指定 。

php の場合は SCRIPT_FILENAME を使ってPATHINFOで実行するファイルを指定することが出来て、これをphp-fpm経由にしてあげれば、任意のファイルを好きなファイルで処理できる。便利。

PATHINFOで取得した場合

$_SERVER['ORIG_SCRIPT_FILENAME']
$_SERVER['ORIG_SCRIPT_NAME']

リクエストされたファイル名

$_SERVER['PATH_TRANSLATED']

php-fpm で php.iniの設定渡す/display_errorsなどをPHP_VALUEで設定する。

PHP_FLAGを使いたい。

apache の mod_phpapachefcgiなら簡単にできるんだけど、nginxだとfcgiでどうやって 初期設定を渡したら良いんだろうか。

こうすれば出来る。

fastcgi_param PHP_FLAG "display_errors=on \n display_startup_errors=on";
fastcgi_param PHP_VALUE "error_reporting=-1";

php をfpm で設定するときに、在るサイトだけ display_errors をオンにしたいとか在るんですよ。

日本語でググっても php-fpm.ini のグローバル設定を変えろ的な記事しかなかったので、アレコレ調べた。

サイトごとにエラー設定を変える。

トラブルが出ているサイトを一時的にdisplay_errors して見てみるとか。

デプロイ時だけエラーになってなんだコレ??ってphpのエラーを追いかけるときかに便利。

サイトごとのエラーログが便利。

fastcgi_param PHP_VALUE  "error_log=/var/log/nginx/my-site.error.log"

複数書きたいときは改行

改行で書くのが楽でいいよ。スペース区切りはダメ。

    fastcgi_param   PHP_VALUE "
        display_errors=on
        display_startup_errors=on
        error_reporting=E_ALL
        error_log=/var/log/nginx/comic-viewer.error.log";

または Name=Value\nName=Value にする.

だめな例

  fastcgi_param   PHP_VALUE "display_errors=on";
  fastcgi_param   PHP_VALUE "display_startup_errors=on";

PHP_VALUE は1つしか取ってくれない。 (そのうちに、nginxやphp-fpmのバージョンアップで解決するんじゃないかな。

ただし再起動が必要

 sudo systemctl restart  nginx php7.0-fpm

fpm の再起動をすると設定が反映される。再起動をしなくてもキャッシュが消えたら反映されると思うけど、php-fpmのキャッシュ機構は多分複雑だろうから追いかけ用途思っても時間が足りなくて出来てない。

前の記事

nginx + php-fpm で display_startup_erros=on にしてシンタックスエラーを表示する。 - それマグで!

PHP

phpで別アプリへのセッション受け渡し

php から別のアプリへセッションを受け渡したい

認証済みのセッションキーを、別のアプリへ受け渡ししたい。今回は mp4 と vlc でテストした。

渡す時

<?php
$url = 'http://example.com/sample.mp4';
$uri = "${uri}&PHPSESSID=".session_id();
$url = 'vlc-x-callback://x-callback-url/stream?url=$url'
header("Location: {$uri}");

受け取る時

ini_set("session.use_trans_sid",true;)
session_start();

フレームワークのサポートも、GET引数のチェックも何も要らない。便利。

引数の名前

デフォルトは "PHPSESSID" なので、ソレを入れても良い。 厳密にやるなら session_name()を使う。

$uri = "${uri}&".session_name()."=".session_id();
$uri = "${uri}&sid=".session_id();

サンプル

<?php

// これだけ
ini_set("session.use_trans_sid",true);
ini_set('session.use_only_cookies', 'off');
session_start();

if ( !empty($_SESSION['is_login']) && $_SESSION['is_login'] ) {
  echo "OK";
  http_response_code(200);
  return ;
}else{
  echo "OK";
  http_response_code(401);
  return ;
}

参考資料

http://php.net/manual/en/session.idpassing.php

セッション

ハンマーセッション!(7) (週刊少年マガジンコミックス)

ハンマーセッション!(7) (週刊少年マガジンコミックス)

ls コマンドで結果を1行ごと1列にならべてループを楽にしたい

ls の結果からディレクトリ名を除きたい。

ls すると結果がズラッと並んでしまって、後処理をしにくい

takuya@Desktop$ ls /
'['     cat     cp    date   df           echo   expr       kill   launchctl   ln   mkdir   pax   pwd   rm      sh      stty   tcsh   unlink      zsh
 bash   chmod   csh   dd     domainname   ed     hostname   ksh    link        ls   mv      ps    rcp   rmdir   sleep   sync   test   wait4path

1行に並べたい

なんと、手軽に1行に並べることが出来ます!single-column -1 オプションです。

takuya@Desktop$ ls -1 /bin
'['
bash
cat
chmod
cp
csh
date
dd
df
domainname
echo
ed
expr
hostname

man からの抜粋

え?カンマ区切りとかできるんだけどwww

      --format=WORD
              across -x, commas -m, horizontal -x, long -l, single-column -1, verbose -l, vertical -C

-1 (one) と -l (エル) は似てる上に同じカテゴリの仲間だったなんて驚き!

ループで回すならIFSで済むんだ。

IFSで済むんだけど、ruby IO.readlineやExcel など、ファイル読み込み系のプログラムから読み込むときは、開業が楽だよねやっぱり。

関連する記事

IFS 関連の記事

参考資料

ls -d 関連商品

L.S.D(1) (NINO)

L.S.D(1) (NINO)

NginxでContent-Typeを指定する。

nginx で Content-Typeを指定する。

すぐに皆が思いつきそうな設定例はこれ。わりと何処にでも書いてある。だけど、、、本当にコレがベターなの?

      location ~ \.mkv$  {
       default_type video/mp4;
      }

types がある。

マニュアル読んでると types がった。

types {
  video/mp4 mkv;
}

types は server や location コンテキスト内部にも記述ができる。

server {
  location ~ / {
    types {
      video/mp4 mkv;
    }
  }
}

ちなみに、if の中で default_type は出来ないので、if 文は使えない。

nginx って if 文で一見すると読みやすそうだけど、設定していくと if 文は悪 if is Evil とか書いてる文書にぶち当たるし、 if の中で出来ることは限られる。

nginx って意外と使いにくい。

参考資料

http://nginx.org/en/docs/http/ngx_http_core_module.html#types

find コマンドでgit フォルダを無視したい(ただし.gitフォルダは表示したい

find コマンドで gitがズラッと並ぶの不便

ああ、、、、要らないよこんなの。。。

takuya@pages$ find
takuya@pages-sample$ $(which find ) -type f
./.git/COMMIT_EDITMSG
./.git/config
./.git/description
./.git/FETCH_HEAD
./.git/HEAD
./.git/index
./.git/info/exclude
./.git/logs/HEAD
./.git/logs/refs/heads/master
./.git/logs/refs/remotes/origin/master
./.git/objects/02/399e371c5d109e1c8221c74c9135a0c069d8fb
./.git/objects/07/968b16567a3c9b2eae0a3749eeb4f16d90d16c
./.git/objects/0d/e9c93edbe76efb6f0deedb1078746667460c0e
./.git/objects/0e/5e7135586f82d8b80f243e18c57443fca70b00

not マッチを使う。

find -mindepth 1 -not -iwholename *.git/*

not マッチをして .git を無視した例

takuya@pages-sample$ find
/Users/takuya/.bin/find -mindepth 1 -not -iwholename *.git/*
./.git
./.gitignore
./.gitlab-ci.yml
./public
./public/index.html
./README.md

object は要らないけど、hookやconfigは見たいんだ。

find の -not は重ねて書くと、or 条件で積み重ねてていく。 というか、マッチしたものを除外するので正確には、-not 条件を 足す感じ

find -mindepth 1  \
-not -iwholename *.git/objects* \
-not -iwholename *.git/logs* \
-not -iwholename *.git/refs* \
-not -iwholename *.git/*HEAD* \
-not -iwholename *.git/in*

適度に選ぶ。

ガッツリ.git 消しちゃうのも不便。

.gitignroe.git/config とか .gitlatb-ci.yml とかもあるので適度に必要なものだけを残して、見ても仕方ないものを消せる。

find に ignroe scm っていうオプションがアレばいいのにね。

関連記事

git 萌え本

わかばちゃんと学ぶ Git使い方入門〈GitHub、Bitbucket、SourceTree〉

わかばちゃんと学ぶ Git使い方入門〈GitHub、Bitbucket、SourceTree〉

vimでphp.iniの設定ファイルを開くときのハイライトさせるファイルタイプ設定

vim で iniファイルのハイライト

ini ファイルは dosini という名前でデフォルト登録されている。

:set ft=dosini

モードラインを書いておく。

ファイルの1行目に書いておけば、モードラインとして設定を拾ってくれるので多少ファイルの中身とファイルの拡張子が合わないときでも便利。

 # vim: ft=dosini ts=2 sw=2 sts=2 sr noet:

こうしておけば、php-fpm.conf とか iniのくせに conf 拡張子になってるものにも対応できる

ちなみに、php.iniの場合は コメントアウトの文字が # じゃなくて ; なので注意

; vim: ft=dosini ts=2 sw=2 sts=2 sr noet:

設定例

f:id:takuya_1st:20180225030816p:plain

関連資料

vim のモードラインでvim設定(タブなど)をファイルに書く - それマグで!

参考資料

http://www.gregfreeman.io/2011/vim-syntax-highlighting-for-php-fpm-conf-files/

2018年8月21日追記

末尾に、 「:」が抜けていたので修正。

vim スタイル

ln -s で相対シンボリック作成のやりかたと解説。

ln -s で失敗した経験ありますよね?

/var/www/log/index.php から /var/www/html/index.php へリンクする場合。こういうときに、相対パスをぱぱっと作れたら嬉しいでしょ?

でも、ぱぱっと作れなかったり、相対パスを作ったつもりがリンク切れてたり。。そして、失敗を繰り返してしまう。

最後には、諦めてフルパスでリンク書いてたり。しませんか?

ところが、フルパスだとバックアップフォルダ移動後にリンクが壊れたりする。

相対パスでリンクすると、あとで楽だもんね。

だから基本的に相対パスで書きたいよね?うん。だよね???だよね???

というわけで、相対パスシンボリックリンクの作り方。

お急ぎの人は

記事の最後へどうぞ。

次のようなリンクを作ってみます。

ファイルをリンクする場合に相対パス絶対パスで作ってみます。

絶対パスなら次のように

ln -s /var/www/log/index.php  /var/www/html/index.php

相対パスなら次のように・・・?

ln -s ./index.php  /var/www/html/index.php

ん?相対パスのスタートはどこだ?誰から見た相対パスを入れるんだ?

相対パスは、宛先ファイルから見た相対です。

/var/www/log/index.php から /var/www/html/index.php へリンクする場合。

つまり宛先からみた、相対パスをいれる。

ln -s ../log/index.php  /var/www/html/index.php

相対パスの求め方。

作りたいリンクから見た相対パスを1番目に指定する。

ln -s は2番目の引数を考えてから、1番目の引数を考える。 ところが一般的なコマンドは目的地(2番目)のコマンドの内容を考える。なので、思考のパターンが逆になる。

このへんがいつもわかりにくいミスする原因なんだと思う。

カレントディレクトリ。

ln で作成するリンクのファイルからみて相対パスにカレントディレクトリは考えない。

作成するリンクと、作業中のカレントディレクト

作成するリンクファイルはワーキングディレクトリからの相対パスで指定することが出来る。

cd /var/www/
ln -s ../log/index.php  ./html/index.php

ここで、 次に注意する。

  • ../log/index.php作成するリンクから見た 相対パス
  • ./html/index.php作業ディレクトリからみた 相対パス

引数それぞれで、相対パスの元が異なるの。ここがよくミスしたり誤解が増えるポイントだと思うんですよね。

ポイントのおさらい

  • 第1引数  作成ファイルから見た 相対パス
  • 第2引数 ワーキングディレクトリからみた 相対パス

相対パスの求め方

たとえば realpath を使ってみる。

$ realpath --relative-to=$( dirname /var/www/html/index.php)  /var/www/log/index.php
../log/index.php
$ ln -s ../log/index.php  /var/www/html/index.php

コレらを併せて。

ln -s $(realpath --relative-to=$( dirname /var/www/html/index.php)  /var/www/log/index.php)  /var/www/html/index.php

面倒くさい。

realpath と ln -s を組み合わせるのは本当に面倒くさい

-r オプション最強

この目的のためにあるのが、r オプション

ln -sr で解決

相対パスを自動て解決してくれるのが -r オプション。まじでコレ付けるだけで解決する魔法のオプション

ln -s -r /var/www/log/index.php /var/www/index.php

r 付けるだけ これで今の理屈を全部やってくれる。知らなかったら絶対に損するオプション。

どこで活躍する?

バージョンごとにフォルダ変えたり、dockerfile とか

sites-enablesites-available など 設定ファイルの有効・無効をリンクで切り替える構成にするときにすごく重宝する。

-r オプションが無いんだけど怒!!

そういうときは、困りますよね。 ワーキング・フォルダを移動します。 リンクを作成するフォルダに cd してから相対パスで指定するとうまくいきます。

cd /var/www/html  ## リンク作成するディレクトリへ先にcd します。
ln -s ../log/index.html ./index.html

ディレクトリのリンクの場合も同じです。

関連資料

bashでファイルパスの相対パスを得る(絶対パスの取得やパスの正規化も)/realpath利用 - それマグで!

参考資料

man ln

関連アイテム

ファミコンミニ リンクの冒険

ファミコンミニ リンクの冒険

f:id:takuya_1st:20180225020756p:plain:w200

Systemd 時代のresolv.conf

/etc/resolv.confがない。

驚いた、Resove.conf がなくなってるんですよ

systemd 管理下に置かれた

/etc/systemd/resolved.conf

代わりに、次のファイルがが出来た

昔からある resolv.conf は systemd が管理してしまう。

もしかしてリンク?

takuya@ubuntu01:~$ ll /etc/resolv.conf
lrwxrwxrwx 1 root root 39 11月  8 22:56 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

なんてことだ。。。。なんて面倒くさいことをやってくれるんだ。。。

リロードが必要

DNSなど名前解決設定を更新したら、sysd でリロードが必要だった。

systemctl restart systemd-resolved.service

ナゼこんなことになってるのか。

resolv.conf は systemd の unit になった。 ネットワーク設定に従ってDHCPで書き換えることが多くて、ネットワークのターゲットに従う。

個人的には、ネームサーバーなんて固定で良いんだけどなぁ。

ruby のDateTimeを30分(1800)すすめる

ruby のDateTimeの時刻をすすめる

datetime + 1800/(24*60*60).to_f

ただ時間をすすめるだけだと、単純にInt秒数を足すことが最短経路だった。

いちいち AcrtiveSupport 的なものに頼るのも面倒な話だ。だけどもう少し楽にならないのかな。

ad