それマグで!

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

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

Raspberry pi はGPTを扱えるのか。

疑問に思ったので試してみました。

結論としては。使えた。です。

rpi のディスク構成を GPTに変えてみます。

GPTディスクでも問題なく扱えるのか試してみます。

最初に今の構成を確認しておきます。

lsblkMicroSDを確認します。

NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
mmcblk0     179:0    0 29.7G  0 disk
├─mmcblk0p1 179:1    0 41.8M  0 part /boot
└─mmcblk0p2 179:2    0 29.7G  0 part /

USBメモリを用意します。

lsblkUSBメモリを確認します。

sdb                  8:16   1  14.3G  0 disk

適当な手持ちの、USBメモリが16GBで、マイクロSDが32GBです。dd コマンドでまるっとコピーするのはめんどくさそうです。

今回は、rsync --one-file-system を使おうと思います。

USBメモリパーティションを作成する

既存のパーティションを消去します。

sudo sgdisk -Z /deb/sdc

sgdisk でGPTパーティションを作成するには次のようにします。

sudo sgdisk -n "${パーティション番号}:${開始セクタ}:${終了セクタ}" /dev/sdc

パーティションを作成します。

sudo sgdisk -n "0::+250M" -t 0:0700 -c 0:"boot" /dev/sdc
sudo sgdisk -n "1::" -t 1:8300 -c 1:"root" /dev/sdc

gdisk コマンドでインタラクティブにやってもいいと思います。

sudo gdisk /dev/sdb
GPT fdisk (gdisk) version 1.0.8

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): p
Disk /dev/sdb: 30031872 sectors, 14.3 GiB
Model:  SanDisk 3.2Gen1
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): BBBB17B5-7447-4F58-A8CC-8C012946858F
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 30031838
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          526335   256.0 MiB   0700  Microsoft basic data
   2          526336        30031838   14.1 GiB    8300  Linux filesystem

protectiv MBR のGPTになりました。

ファイルシステムを初期化します。

sudo mkfs.vfat /dev/sdb1
sudo mkfs.ext4 /dev/sdb2

ついでにラベルもつけておきます。

sudo fatlabel /dev/sdb1 boot
sudo tune2fs /dev/sdb2 root

ラベルを付けておくと、fstab や cmdline.txt でUUID記述より簡潔に完結するので好きです。

ファイルシステムをコピーします。

マウントポイントを作って、コピーしてUSBメモリを複製します。

ルートファイルシステムのコピー

sudo mkdir /mnt/work
sudo mount /sdb2 /mnt/work
sudo rsync -av --progress --one-file-system / /mnt/work
sudo rsync -av --progress --one-file-system / /mnt/work # チェック

bootもコピーします。

sudo mkdir /mnt2
sudo mount /sdb1 /mnt2
sudo rsync -av --progress --one-file-system /boot/firmware/cmdline.txt /mnt2
sudo rsync -av --progress --one-file-system /boot/firmware/cmdline.txt /mnt2

rsync を2回起動しているのは、転送終了のチェックのためです。

わたしの環境では、/boot ではなく、/boot/firmware になっていました。arm64(aarch64) のrpi4 でubuntuだからかもしれません、

cmdline.txt と fstab の確認

cat cmdline.txt
cat fstab.txt

わたしは、UUIDで指定せず、LABEL指定( root=LABEL=root)ですので、特に変える必要はありませんした。

うっかりミスを防ぐために、確認しておきます。UUIDであればディスクのUUIDを指定するべきでしょう。

起動してみます。

レインボーが表示されました。ブートの読み込みが終わったようです。

起動が始まりました。

GPTディスクでも問題なく扱えた

これでGPTディスクは、RaspberryPi で問題なく扱えることがわかりました。

追記

メモを見直してみると、純粋GPTじゃなくてMBR protectiveになっていた。。

稼働中Linux(raspberry pi)のバックアップを取る。`rsync --one-file-system `

raspberry piUbuntu のバックアップを取ろうと思いました。

バックアップとるといえば、dd dump/restore が定番ですが、今回は、ブートの書き換えと、initramfs の再構築のために、Raspberry上でRaspberryPiのバックアップを取る必要がありました。いちいち電源停止して再起動してたらめんどくさいので、稼働中のバックアップを取れないか模索しました。

RaspberryPi4 (arm64)のバックアップを取る--one-file-system

GNU コマンドには、--one-file-system という便利なオプションがあることを知りました。

これを使えば、マウント中のルートファイルシステムのバックアップを取ることが可能です。

--one-file-systemをつかえば、マウント箇所を無視してコピーを取れます。

rsync--one-file-system でバックアップ

--one-file-systemrsync で使えば、この通り。

rsync -avz --progress --one-file-system  /  server01:/home/takuya/backup

たとえば、USBメモリに、バックアップを取る。

mkdir -p /mnt/work/chroot
mount /dev/sdb1 /mnt/work/chroot
rsync -avz --progress --one-file-system  /  /mnt/work/chroot

tar--one-file-systemでバックアップ

--one-file-systemをtar コマンドとを使えばこの通り。

tar cvf - / --one-file-system | ssh  server01 'cat | pigz -  > raspi.tgz '

USBメモリに、tar バックアップを取る。

tar cvf --one-file-system  -  / | tar xvf - -C /mnt/work/chroot

なぜこんなことをしているのか。

感のいい読者さんなら、なぜdump/restoreを使わないんだ。なぜLVM Snapshotを使わないんだと思うかもしれません。

dump/restore は ext4 ファイルシステムが前提です。今回は非ext4 でした。

なぜLVMスナップショットを使わないのかは、そもそもRaspberry Pi は標準インストールがext4です。LVMは非搭載です。

btrfs なら send /recieve があるだろ。と思うかもしれません。今回はbtrfs に限定せずにext4 でも btrfs でも使える方法を模索した結果です。

Raspberry Pi のような ext4 ディスクに最適です。dump/restoreやdd/ddrescue別コマンド体系を覚えなくてすみます。初心者向けではないでしょうか。

--one-file-system のメリット

--one-file-system はいくつかのGNUコマンドで使えます。

私がちょっと調べた限りでは、 tarrsync 、なんと cp コマンドで扱える。

ファイルシステムを横断しない、別のファイルシステムは無視するので、ext4 ベースのシステムには便利です。

バックアップのために仮想マシンを停止しなくて良い、スナップショットをつかなくてもいい。とある程度手軽です。

デメリット

巨大ファイルの高頻度の書き込みがあると、rsync に失敗する。rsync 中にファイルが更新されてしまうと、Rsyncに失敗します。

そのため、巨大ファイルがある場合は、プロセスを停止しておくか、Syncのインターバルの調整が必要です。

スワップファイルを停止する

sudo swapoff /var/swapimg

ext4 に書き込みタイミングを遅らせる /etc/fstab

### ext4 に書き込みは5分一回にする。
LABEL=writable  /        ext4   discard,errors=remount-ro,commit=300    0 1

まとめ

--one-file-system でルートファイルシステムのコピーは恐ろしく簡単になります。

とくに、RaspberryPi( / , /boot 分離構成 かつ Ext4デフォルト) のルートファイルシステムを手軽に扱えて便利でした。

もっと早く知りたかった。

参考資料

https://superuser.com/questions/626141/rsync-recursive-on-same-mount-only

LUKSのキーの存在チェック(パスフレーズのテスト)

LUKSのキーがただし正しいかチェックしたい。

LUKSのキー、これだったかかな?と候補があるときに使えます。

また。複数の鍵を登録後に登録チェック、鍵の削除後のチェックなどが必要だと思います。

そのときに、鍵の存在チェックだけをコマンドでおこない自動化出来たらと思います。

LUKS キー チェック

sudo cryptsetup luksOpen --test-passphrase /dev/xxx

実行例。

sudo cryptsetup luksOpen --test-passphrase /dev/sdg3
Enter passphrase for /dev/sda3:
No key available with this passphrase.
Enter passphrase for /dev/sda3:

ポイントとしては、dm-cryptを luksOpen で開く。パスワード(またはパスフレーズ)を入力してデバイスを開く。ただし、dry-run (--test-passphrase ) で行う。これにより、LUKSにパスワードを使っているか、パスワードが正しいか、チェックすることができる。

標準入力から渡す

パスフレーズを使っているばあいは、標準入力から渡せる。

echo -n my_pass_phrase  | sudo cryptsetup luksOpen --test-passphrase /dev/xxx

KeyFileを指定する。

鍵ファイルを指定してる場合は、オプションで指定できる。

sudo cryptsetup luksOpen --key-file path/to/key --test-passphrase /dev/xxx

予備鍵の管理に

LUKSの予備(スペア)キーの管理、鍵ファイルのバックアップが正しいかどうか。それらをチェックすることが出来るので安心。

このパスワード登録してたっけ。というときに重要。

何本のパスワード・鍵が登録されているか。

cryptsetup luksDump /dev/sda2

キースロットが鍵本数。luksDumpコマンドでは利用中の鍵の本数がわかる。

LUKS header information

Data segments:
  0: crypt
Keyslots:
  0: luks2
        Key:        256 bits
        Digest ID:  0
Tokens:
Digests:
  0: pbkdf2
        Hash:       sha256
        Iterations: 26089

パスワードをチェックして、不要な鍵を見つけたら

パスワードを指定して消す

 cryptsetup luksRemoveKey

この場合は先頭から消される。

パスワードをチェックして、パスワードがわからない登録(スロット)を見つけたら

luks のKillSlot を使ってスロットを削除する。

 cryptsetup luksKillSlot

luksRemoveKey は指定した鍵を探して消す。

まとめ

このように、パスワードを検査することで得られることがある。

2023-11-30

killslot で検索にかからないのでキーワードを追加

参考資料

https://sleeplessbeastie.eu/2019/03/27/how-to-test-luks-passphrase/

ddしたSSD/HDDの容量が認識されない(容量の違うディスクにDDしたとき)

dd / gddrescue を掛けたときに、容量が認識されない

サイズの大きなディスクを交換しようと、ssd をまるごとddしました。

ddrescue /dev/sdc /dev/sda 

元のディスクは、GPT/EFIでLUKSであり、容量も64GBなので、dump/restore は使っていません。新しいディスクは250GBです。

容量認識されませんでした。

dd済みディスクは無事に稼働しましたが、残容量が変わりません。パーティション切り直しても治りません。

sudo sgdisk -p /dev/sda
Disk /dev/sda: 468862128 sectors, 223.6 GiB
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624         2549759   732.0 MiB   8300
   3         2549760       468862094   58.5 GiB   8300  Linux filesystem

GPTの更新。

gpt テーブルが更新されてないと予測し対応をしらべたら、発見した。

Run x(expert mode) e(fix the pointer) m(return to main menu) p(re-check your modification) w(save!), then everything is ok.

gdisk で x (エキスパートモード) 、 e ( ポインタ先修正)、m (メインメニューへ)、p (再チェック)、w (保存)

私はこの方法で治りました。

ただし、怖いので、事前にパーティションのレイアウトのバックアップ取りました。

sudo sgdisk -b out.sda /dev/sda
sudo gdisk /dev/sda

これで、ddを更に安心に使えます。

参考資料

dhcpのリクエスト・レスポンスの応答を調べる dhcpdump

dhcp のパケットを見たい。

sudo tcpdump -i macvlan0 port 67 or port 68 -e -n

ただ、これだと凄く使いにくい。もっとわかりやすく見たい。

dhcpdump が便利。

tcpdump を使わずに dhcpdumpを使うともっと簡単にdhcpのパケットを追いかけることが出来る。専用コマンド便利ですね。

sudo apt install dhcpdump

利用例

sudo dhcpdump -i macvlan0
  TIME: 2023-01-18 17:36:40.745
    IP: 0.0.0.0 (52:54:0:60:2b:ac) > 255.255.255.255 (ff:ff:ff:ff:ff:ff)
    OP: 1 (BOOTPREQUEST)
 HTYPE: 1 (Ethernet)
  HLEN: 6
  HOPS: 0
   XID: 5d5415d9
  SECS: 1
 FLAGS: 0
CIADDR: 0.0.0.0
YIADDR: 0.0.0.0
SIADDR: 0.0.0.0
GIADDR: 0.0.0.0
CHADDR: 52:54:00:60:2b:ac:00:00:00:00:00:00:00:00:00:00
 SNAME: .
 FNAME: .
OPTION:  53 (  1) DHCP message type         1 (DHCPDISCOVER)
OPTION:  61 ( 19) Client-identifier         ff:56:50:4d:98:00:02:00:00:ab:11:e7:5f:9b:8b:d2:86:d0:a4
OPTION:  55 ( 11) Parameter Request List      1 (Subnet mask)
                          3 (Routers)
                         12 (Host name)
                         15 (Domainname)
                          6 (DNS server)
                         26 (Interface MTU)
                         33 (Static route)
                        121 (Classless Static Route)
                        119 (Domain Search)
                         42 (NTP servers)
                        120 (SIP Servers DHCP Option)

OPTION:  57 (  2) Maximum DHCP message size 576
OPTION:  12 (  6) Host name                 ubuntu
---------------------------------------------------------------------------

  TIME: 2023-01-18 17:36:43.761
    IP: 0.0.0.0 (52:54:0:60:2b:ac) > 255.255.255.255 (ff:ff:ff:ff:ff:ff)
    OP: 1 (BOOTPREQUEST)
 HTYPE: 1 (Ethernet)
  HLEN: 6
  HOPS: 0
   XID: 5d5415d9
  SECS: 3
 FLAGS: 0
CIADDR: 0.0.0.0
YIADDR: 0.0.0.0
SIADDR: 0.0.0.0
GIADDR: 0.0.0.0
CHADDR: 52:54:00:60:2b:ac:00:00:00:00:00:00:00:00:00:00
 SNAME: .
 FNAME: .
OPTION:  53 (  1) DHCP message type         3 (DHCPREQUEST)
OPTION:  61 ( 19) Client-identifier         ff:56:50:4d:98:00:02:00:00:ab:11:e7:5f:9b:8b:d2:86:d0:a4
OPTION:  55 ( 11) Parameter Request List      1 (Subnet mask)
                          3 (Routers)
                         12 (Host name)
                         15 (Domainname)
                          6 (DNS server)
                         26 (Interface MTU)
                         33 (Static route)
                        121 (Classless Static Route)
                        119 (Domain Search)
                         42 (NTP servers)
                        120 (SIP Servers DHCP Option)

OPTION:  57 (  2) Maximum DHCP message size 576
OPTION:  54 (  4) Server identifier         192.168.1.1
OPTION:  50 (  4) Request IP address        192.168.1.193
OPTION:  12 (  6) Host name                 ubuntu
---------------------------------------------------------------------------

参考資料

https://matoken.org/blog/2019/04/10/a-memo-to-capture-dhcp-packets/

sedで指定行表示 / head コマンド相当

指定行を表示 / head 相当をする

以前、sed で行挿入をシンプルに行う。 とういう記事を書いたとき、範囲指定について言及した。

今回は、sedの範囲指定を使って 先頭からN行を取得してみる。

範囲指定の練習

行番号を指定して、その行を表示する。

指定行を表示 / head 相当

これは、範囲'1,22' について p (print)を指定しています。

sed -n '1,22p' 

これは、次のコマンドと同じ。

head -n 22

範囲指定について

指定範囲x操作 でコマンドが構成されています。操作に sを指定すると置換(substituttion)が行われる。

sed は、ed/ex/vim などと同じed進化系なので、sai などの指定方法はvim と同じ。

vim の場合も範囲x操作 で指定する、たとえば %s/foo/bar/%=全体s=置換する/search/replace/ のようになっている

sed の場合は、範囲指定を処略すると読み込み行がすべて対象になる。 s/foo/bar/で全行を置換処理する。世間のsedのサンプルは、全行指定して「範囲」の存在を無視するので、sedが分かりづらい。

tail はめんどくさい

tail -n 1 相当ならすぐに作れるのだが。

cat /etc/passwd | sed -n '$p'

tail -n 5 相当は末尾から数え直すのがとてもめんどくさい

同じことを試してた先人がいるので、それを参考にする。

https://qiita.com/angel_p_57/items/e7335f35ea5e1c7c3f02

関連記事

sed で行挿入をシンプルに行う。

armbian(orange-pi)でraspi-config

raspi-config の armbian 版コマンドの存在に気づいた。

こんなのがあったのか。

楽だわ

sudo armbian-config

もう、Raspiは手に入らないし、raspi-configと同等のコマンドがあるのなら、初心者にもおすすめできますよね。Armbian対応Orange Pi でいいかもしれない。

raspi ubuntu で gpu_mem 設定

raspi-ubuntugpu_mem 設定

ubunturaspberry pi にインストールすると、raspi-config がないので、gpu_memをコマンドから操作できない。

gpu_memの値は config.txtで設定してやる。

現在の設定値を確認

takuya@raspi-ubuntu:~$ vcgencmd get_mem gpu
gpu=76M

デフォルトは76Mなんすかね。変な数字ですね。ディストリビューションの設計者が決めたのかな。微調整を考えてこの数字なんだろうか。

/boot/firmware/config.txt

gpu_mem=32M

うちは、サーバ版なので、HDMIもつないでないし。極限まで減らしてみた。あまりに減らしすぎると、気づかない別の視点からボトルネックが発生しそうではあるが。

設定後の確認

takuya@raspi-ubuntu:~$ vcgencmd get_mem gpu
gpu=32M

参考資料

https://nosubject.io/raspberry-pi4-gpu-memory-configuration/

rclone でリモートのファイルを検索する。

rclone でリモートのファイルを検索する。

rclone でリモートファイルを復元したい。

消しちゃったファイルの復元

ファイルの一覧を取り出して、もとのファイル名を探す。

取り出せそう

リモートファイルの一覧

ワイルドカードで出来る。

rclone ls 'my-crypted:/backup/*20221129*'

一度結果をファイル一覧をまるっと取り出したほうが楽かもしれない。

rclone ls 'my-crypted:/backup/*20221129*' > list.txt

リモートファイルの復元

rclone copy 'my-crypted:/backup/*20221129*' ./restore

仮想マシンのポーズ・サスペンド と 再開・レジューム(libvirt)

kvm + qemu で virsh から構成した仮想マシンを一時停止・再開(レジューム)はどうやるんだろう。とおもって調べたら。コマンドが用意されていた

ポーズ・サスペンド と 再開・レジューム

## サスペンド(一時停止)
sudo virsh suspend test
## レジューム(再開)
sudo virsh resume test

個人的には使うことはないが、ライブマイグレーションよりは手軽な方法としてありかもしれない。マイグレ中には停止しなくちゃいけないし。

スナップショットなどで、内部的には使われてるみたい。 https://computingforgeeks.com/virsh-commands-cheatsheet/

自分のライブラリをgem にして自分で使う、自作gem install

書いたソースコードをgem にして自分で使いたい。

自分で書いたソースコードを再利用するために gem の仕組みを活用し、再利用する。

目標

自分の git ライブラリを読み込み活用する。

必要なもの

  • 自分のライブラリ
  • bundler
  • ruby

手順

  • gemspec を作って、git に設置する。
  • 自分のソースコードをgitにおく
  • 使う側のGemfileを作る
  • bundle installで gitから取得する。

rubygem以外からのインストール方法

ざっと調べた限りでは、使いやすそうな2つの方法が見つかります。path: でパスを指定する、git: でgitレポジトリを指定する。この2つです。

方法1 path:で指定

同一サーバー内であれば、これも快適です。git:を使う前のチェックにも使えます。

gem 'my-libs', path:'/path/to//my-libs'

方法2 git から取得する方法

git 経由(SSH / HTTPS / PATH ) で gitを使って取得します。

gem 'my-libs', git:'ssh://git@gitlab.example.tld:/takuya/my-libs.git

git 経由では、HTTPS/SSHのよく使う指定以外に、PATHで指定も可能です。

gitをローカルパスで指定する例

gem 'my-libs', git:'/home/takuya/my-libs'

ソースコードをライブラリ化するには

それでは自分のソースコードを、ライブラリとして再利用可能にするにはどうすればいいのでしょうか。それはgemspecファイルを使います。

gemspec から、gemsが作られます。作成されたgemsがbundle install によって、プロジェクトに取り込まれます。これにより再利用が可能になるわけです。

gemspec の例。

require_relative "lib/version"

Gem::Specification.new do |spec|
  spec.name          = "my-libs"
  spec.version       = Takuya::MyLibs::VERSION
  spec.authors       = ["takuya"]
  ## 略
  spec.bindir        = "bin"
  spec.require_paths = ["lib"]
end

gemspecを使わずとも、ライブラリを再利用することも可能ですし、gemを作成することも可能。LOAD_PATHをカスタマイズしてもいいです。

ですが、gemspecを使って考えることを減らし、メジャーな方法に倣うことで検索性が向上します。最初にちょっと時間をかけたほうが、最終的には時短になると思います。

gemspec の作成

自分のソースコードをライブラリにするためには、gemspecが必要です。gemspecファイルをゼロから書いていたら大変です。そこで便利なコマンドが用意されています。

bundle gem my_gem_name

名前は、snake_caseで記載するといいでしょう。

snake_caseで記載すると、module SnakeCaseが作られます。 cabab-caseで記載すると、module Cabab::Caseが作られます。

このコマンドでmy_gem_name.gemspec が作成されます。ファイルには、雛形が書かれています。TODO箇所はご自身の記入箇所です。

雛形のTODOは、https://rubygems.org/ に公開に必要な項目です。ライブラリを自分で使うだけなら、記入は不要です。

インポートに、最低限必要な項目は、次のとおりです。

  spec.name = "my_libs"
  spec.version = '1.0'
  spec.summary = 'Example for import personal library'
  spec.authors = ["takuya"]

次の設定項目は重要です。(必須ではありませんが、書いたほうがいいでしょう)

  spec.require_paths = ["lib"]

これらの最低限の項目がgemspecに記載されていれば bundle install で使うことができます。ご自身で使うだけなら、最低限の記入でも問題ありません。

もしrubygems.org に公開を目標にしているなら、詳細な記入が必要です。

自作ライブラリを参照する方法

冒頭に述べたとおり、アプリケーションから自分のライブラリを再利用するときは、対象の参照をGemfile に記入します。

Gemfileサンプル

gem 'my_libs', path:'../my_libs'

Gemfileを書いたら準備完了です。

Gemfileの内容をbundle install します。

bundle インストールしたら、Gemfile.lock へ実際のインストール先が記入されます。

path: でインストールした場合

:path はちょっと特殊(?)です。Gemfileで、:gitを使わずに :path で参照した場合はbundleはソースコードのコピーをプロジェクトに作りません。

:pathでインストールしたら、$LOAD_PATH該当ディレクトリが追加されます。

ruby のLOAD_PATH の確認

puts $LOAD_PATH

# /Users/takuya/.rbenv/versions/2.7.1/lib/ruby/2.7.0
# /Users/takuya/.rbenv/versions/2.7.1/lib/ruby/site_ruby
# (中略)
# /Users/takuya/Desktop/my_libs

$LOAD_PATHを見ると、:path で指定したgemspecにPATHが通されているのがわかります。

ライブラリ側のgemspecにspec.require_paths=['lib'] を記入しておくと、spec.require_paths で指定したPATH値(ここでは["lib"]) がPATHに追加されていることでしょう。

: path指定はGemfie.lockに

Gemfileの:path記載で$LOAD_PATHの書き換え処理が行われますが、bundleインストール後は、Gemfile.lock に記載されています。

Gemfile.lock のPATH記載の例。

PATH
  remote: /Users/takuya/Desktop/my_libs
  specs:
    my_libs (0.0.1)

pathを使うと上記のように、Gemfile.lock にPATHが記載されます。gem自体はvendorにコピーされないという点は心に留めておきましょう。

実際にやってみます。

ここまで概要がわかれば、実際にやってみます。

ライブラリ作成する。

bundle gem my_libs

ライブラリ用のパッケージ一式が生成されます。

最低限必要なもの以外を消す。

最低限必要なファイル以外を削除する。動作をわかりやすく。

rm -rf CODE_OF_CONDUCT.md Rakefile bin/ sig/ README.md

gemspec の作成

こちらも、最低限必要なgemspecを書きます。

echo '# frozen_string_literal: true

require_relative "lib/my_libs/version"

Gem::Specification.new do |spec|
  spec.name = "my_libs"
  spec.version = MyLibs::VERSION
  spec.summary = "Example for import personal library"
  spec.authors = ["takuya"]
  spec.require_paths = ["lib"]
end' > my_libs.gemspec

ライブラリ作成します。

my_libs/lib/my_libs.rb

# frozen_string_literal: true

require_relative "my_libs/version"

module MyLibs
  class Error < StandardError; end
  def self.greetings
     puts "hello from my libs"
  end
end

なにか関数を作っておきます。

git に保存して、レポジトリへ

ライブラリの動作を確認を終えたら、git commitgit push を実行します。

git remote add origin git@gitlab.example.tld:takuya/my_libs.git
git commit -am initial 
git push origin master

git に push してサーバーに保存します。

ライブラリを利用する

ライブラリを使うプロジェクトを生成する。

bundle gem sample_app
cd sample_app

こちらも、最低限の必要なファイル以外を削除し、動作を明確にします。

rm -rf CODE_OF_CONDUCT.md Rakefile bin/ sig/ README.md sample_app.gemspec

gemspec で指定も可能なのですが、今回はGemfileを使います。

PATHを使ってインポートテスト。

一番シンプルなLOAD_PATHを使ったインポートを試しておきます。

echo "# frozen_string_literal: true

source "https://rubygems.org"

gem 'my_libs', path:'../my_libs'
" > GemFile

mylibs のgem をインストールします。

bundle install 

ここまが動いたら、準備(gemspec作成)に問題なしと確認できます。

PATHインストールがうまく行ったら、次に備えsample-appのインストール状態を初期化し直します。

rm Gemfile.lock
rm vendor -rf 

もしエラーになったら、my_libsのgemspecを見直しましょう。

冒頭に述べたとおり、path:はLOAD_PATHの追加です。my_libsソースにシンタックス・エラーがあろうとも、path:を使うと検出が行えません。注意してください。

git を使ったインポート

ライブラリをgitでインストールします。ただし、https:/git:はまだ使いません。

git をパス参照(ローカルパス)で利用します。

git + path でインストール設定を書きます。

Gemfile

# frozen_string_literal: true

gem 'my_libs', git:'../my_libs'

インストールしてテスト

インストール、動作チェックします。

bundle install

プロジェクトのvendorにコピーされているか、確認します。

ls vendor/bundle/ruby/*/bundler/gems/my_libs*/

もしエラーになる場合は、vendorを一度消してみてください。

rm -rf vendor

それでもエラーに場合は、mylibs 側でコミット忘れかもしれません。commitを確認してください。

cd ../mylibs
git st 
gti commit -am update
cd ../sample_app
rm -rf vendor 
bundle install 

これで、git コマンド経由のインストールの完成です。

インストールができたら、Gemfile.lock を確認してみます。

 cat Gemfile.lock

Gemfile.lock

GIT
  remote: ../my_libs
  revision: 6f50e23037f26f03cbXXXXXX
  specs:

Gemfile.lockにはgit のコミット番号(ハッシュ値)が記載されていることがわかります。

今回はバージョンを指定してないので、デフォルト・ブランチのコミット番号(ハッシュ値)が識別に利用されています。

最後に、http経由のインストールにします。

これで、ライブラリ作成・保存、Gemfileでのgitインストールが出来ました。最後に、ローカル・パス参照を、リモート・サイトのGitレポジトリを参照に切替えます。

完成したmylibs をpush

cd ../mylibs
git add .
git commit -m fix
git push origin master

mylibsをhttp/ssh 経由で参照するようにする。

cd ../sample_app
echo '# frozen_string_literal: true

source "https://rubygems.org"

# Specify your gem's dependencies in sample_app-app.gemspec
#
gem "my_libs", git:"git+ssh://gitlab.example.tld/takuya/my_libs.git'
' > Gemfile

mylibsをインストールします。

bundle install 

これで、自作のソースコードをGemfileで参照できます。

アップデートする場合

自作ライブラリにミスが見つかったら、オリジナルを修正してgit pushをします。そしてアップデートをかけます。

cd mylibs 
vim lib/my_libs/my_libs.rb
git commit -am update 
git push origin master

最新版を取得し直します。

cd sample_app
bundle update my_libs

バージョンやタグを未指定でGemfileを使うと、デフォルトブランチ(master / main)から取得するので、アップデートは常にレポジトリの最新版を取得なります。なのでシンプルです。

ただし、bundle update my_libs のたびに、./vendor に過去コピーが溜まっていくので注意が必要です。

以上でライブラリ作成が完成。

ライブラリ作成をし、gitからインストールすることで、自分のソースコード使い回すことが出来るようになり、よく使う処理や自分の癖をサポートするソース・コードをコピペせずに済みます。

まとめ

自分のソースコードライブラリ化してインストールする方法には、次の2つが選択肢になります。一つは、LOAD_PATH使う、もう一つはGemを使うです。

Gemを使う場合は、pathでLOAD_PATHを使うか、git を使う方法があります。

Gem と git を使う場合は、ローカル・パスか、リモート(https / ssh) を選ぶ事ができます。

つまりこうなります。

  • LOAD_PATH
    • LOAD_PATH.push
    • gem path:
  • gem git:
    • git://path/to/git/dir
    • git+ssh://git@example.tld:2222/takuya/repo.git
    • git+https://example.tld/takuya/repo.git

油断すると、少々ややこしいです。LOAD_PATHとgem path / gem git+pathが混乱しそうですが、落ち着いて考えるとすっきりわかる筈です。いちいちgit commit / git push しなくてもライブラリのインポート設定や動作チェックが出来るので知っておくと時短になります。

bundle gem で生成されるファイル

今回は使いませんでしたが、bundle gem が生成するファイルには役割があります。

  • CODE_OF_CONDUCT.md このgemのコントリビューターにへのルール
  • LICENSE.txt gemは基本的にMIT
  • テスト spec RSpec
  • bin ロード状態でirb起動
  • Rakefile rake タスク

tar を使ってSSH越しに転送する。(パイプ利用

tar を使ってフォルダをそのまま転送したい。

tar を使って表示出入力を使ってファイルを転送したい。

tar cfh -  /etc/letsencrypt/live/ | ssh root@srv4 'tar xvf - -C /mnt/hdd  '

転送の前後でディレクトリを移動する

tar  cfh - -C /etc/letsencrypt/  live  | ssh root@srv4 'tar xvf - -C  /etc/letsencrypt/'

よくある失敗

tar がややこしくて、ついついrsync を利用しますが、落ち着いてオプションの引数の順番を見直しましょう。

この記事の目的の「圧縮しながら転送し、転送先で展開する」をパイプでまとめてやるとき、-C オプションと - の指定位置が混乱しがち。

展開時のよくある間違い。

-C の記述位置が間違い

tar xvf - -C /mnt/hdd   #正しい 
tar xvf -C /mnt/hdd -  # 間違い
tar -C /mnt/hdd xvf  -  # 間違い

アーカイブ格納時のよくある間違い

- の位置がだめ

tar cfh -  /etc/ | cat - 

|パイプ忘れ(パイプつけないと、 - が解釈されない?)

tar cfh -  /etc/ 

f 忘れ

tar zch -  /etc/  | cat - > out.tar

格納時の-C の場所が悪い

tar  cfh - -C /etc/letsencrypt/  live   ## 正しい
tar  cfh -  live -C /etc/letsencrypt/   ## 間違い
tar  -C /etc/letsencrypt/ cfh -  live    ## 間違い

転送先の設定

転送先サーバーのAuthorizedKeysを工夫すれば、SSHコマンドを省略して、指定コマンドだけが実行される。

これを使うと、Root権限で転送をかけても、少しだけ(気持ちだけ)安全がある。

厳密にやると、ディレクトリトラバーサル脆弱性、上位ディレクトリへのシンボリックリンクを転送など、面倒なことが多いので気休め程度になる。

自動化するときにオペレーションミスの事故を防止するには効果的。

~/.ssh/authorized_keys

command="tar xvf - -C /etc/letsencrypt/live/" ssh-rsa AAAAB3NzaC1yc...QW1YfiMs= root@only-tar-letsencrypt

接続と転送

公開鍵にコマンドを指定したので、コマンドは不要になる。

tar  cfh - -C /etc/letsencrypt/  live  | ssh root@srv4 

ポエム

rsync を使えばいいだろうと思う?rsync は何でもできてしまうので、シビアでセンシティブ情報を転送するときは、rsyncを使いたくない。SSHを開けたくない。

たとえば、つぎのように、機能限定の公開鍵を作って、バックアップを定期的に行いたい。 sshの公開鍵 authorized_keys ファイルの制限機能について調べてみたら楽しかった.(実行コマンド制限など) - それマグで!

tar コマンドでリンクの実体(リンク先の本体)を格納する

tar コマンドを使うときに、シンボリックリンクの実体を格納する。そのままではリンクが格納されてしまう。

どうしても「リンク先」の「実体」がほしいときに使う

リンクの代わりにリンク先を格納する

tar  cvf --dereference out.tar  /etc/letsencrypt/live

または

tar -cvfh out.tar  /etc/letsencrypt/live

シンプルに

tar cvfh out.tar  /etc/letsencrypt/live

man tar より

       -h, --dereference
              シンボリックリンクを辿り、それが指しているファイルをアーカイブに入れる  (訳注: シンボリックリンクが指し
              ている実ファイルが、 同一アーカイブに収録されるときは、そのハードリンクを、収録されないときは実ファイル
              を、 シンボリックリンクのファイル名でアーカイブに入れるようだ)。

man を見れば一目瞭然。だけど、「シンボリックリンクをたどる」という検索語が思いつかなくて、少し悩んだ。

日常生活では、シンボリックリンクとは言わないし、リンクをたどるとは言わない。リンク先だもん。

しかも、「デリファレンス」とか、日本語話者は日常生活で使わないもんな。C言語を触ってるとデリファレンスは日常なんだろうけど。

参考

man tar

Twitterのおすすめユーザー(Who to Follow)が代わり映えしないので、消したい。ずっと同じ人がレコメンドされてる。

ツイッターのおすすめユーザがうざい。

もうね、3年近く同じユーザがレコメンドされてるのよ。リアルの職場の知り合いだから絶対フォローを押したくないの。ずっと出てくるの。

ツイッター「フォロー押すまで、帰らない」って居座り続けるんですよ。ええ私は、永遠にフォローを押すことは無いんですけど。むしろレコメンドされるユーザーは、ブロックしてもいいと思ってたりする。

「おすすめユーザ」を消す

そこで、CSSセレクタのマッチングを工夫して「非表示」にすることを試みた。ツイッターdiv[data-testid="cellInnerDiv"] に全部のコンテンツが入ってるし、ReactなのでCSSクラス名も不安定。

そこでCSSのクラス名以外で該当ボックスにマッチさせる必要があります。これがずいぶんと長い間出来なかったんです。Xpathを使ってました。

最近になって:has() セレクタがまともに使えるようになりました。

これで「〇〇を含む親ボックス」が取得可能になり、CSSセレクタによるコンテンツブロックがはかどるようになりました。

おすすめユーザを非表示にする例

! 2023-01-08 https://twitter.com
twitter.com##div[aria-label="Timeline"]>div[data-testid="cellInnerDiv"]:has(div[aria-label^="Follow"][data-testid$="follow"])
twitter.com##div[aria-label="Timeline"]>div[data-testid="cellInnerDiv"]:has(a[href^="/i/connect_people?user_id="])

従来は、次のようなXPATHを書く必要がありました。

twitter.com##:xpath(//div[@data-testid="cellInnerDiv"][ .//span[contains(., "Who to follow")]])
twitter.com##:xpath(//div[@data-testid="cellInnerDiv"][ .//span[contains(., "おすすめユーザ")]])
twitter.com##:xpath(//div[@data-testid="cellInnerDiv"][.//span[contains(., "Who to follow")]]/following-sibling::div[position()<4])

div[following-sibling::div[position()<4] で隣接ノードを絞り込むほうが確実なんですよね。

ちなみに、メッセージを消す例

メッセージ(DM)のボックス消したいと思う人は多いのではないでしょうか。

twitter.com##div[data-testid="DMDrawer"]
twitter.com##div[data-testid="DMDrawerHeader"]

VLCプレイヤーでChrome Castを使う。

vlc で再生するときCastを使う

ブラウザだと簡単にキャスト出来るんだけど、VLCは出来ないと思ってた。

GoogleChrome cast を使ってGoogle HomeGoogle nest hub )を通して再生出来たらいいなと思ってた。

メニューありました。

再生ー>レンダラGoogle Homeが出現しています。これを選べば再生の出力先がGoogle Homeになります。便利。

設定は、オーディオ→デバイスだと思ってたから、ずっと出来ないと思いこんでた。Chromeキャストは「再生」メニューです。