ときどきの雑記帖 RE* (新南口)
5年後の世界
今週のしずえさん
海外の雪だるま(の特徴)。
しかしタイミングよく(?)雪が降ったなあ(1月6日)。
福徳神社の算額
正月休み最終日に福徳神社に行ってみたのだけど、 そこそこ初詣客が並んでいたので境内には入らず遠目で眺めただけ。 たぶんあれが算額だろうなというものは見えたもののはっきりと確認はできず。
ということで時間をおいてからまた行く(忘れなければ)。
プラネテス
BS12 の映画の時間と被るのがなあ…
京大のアレ(続々)
海外での反応
- Japan: Lost 77 TB of data because supercomputer crashed during normal backup : programming
- Japan HP accidentally deleted 77TB data in Kyoto U. supercomputing system | Hacker News
- University loses 77TB of research data due to backup error | Hacker News
- University loses 77TB of research data due to backup error | Hacker News
- 77TB of Research Data Lost Because of HPE Software Update | Hacker News
- University loses 77TB of research data due to backup error | Hacker News
- Japan: Lost 77 TB of data because supercomputer crashed during normal backup | Hacker News
- University loses 77TB of research data due to backup error | Hacker News
Hacker News の方はスレッドがたくさんあるけど伸びているのはひとつだけ。
FreeBSDとかashとか
bashでない普通のshやkshでは起きない。もしくは*BSD系tcshでも起きない。バイナリであってもi-nodeのリンクカウントが0に成るだけで元のファイルは残っててそれが実行される。cronから呼ばれる時は/var/spool/cron/の下にスクリプトがコピーされて実行される。今回の挙動はSysVやLinux系だけでは?
— だよもんフレンズは在宅BSD仕事したい (@daemon1995) December 29, 2021
というツイートや
Twitter で daemon1995 さんからご指摘頂き、確認したところ、Linux の dash(/bin/sh)、zsh では再現、FreeBSD の sh/bash では再現せず。 どうやら UNIX System-V だと再現する様です。
という記述がありますが追記をさらに見ていくと
FreeBSD は stdio のバッファリングの関係で 512 バイトぐらいで検証した方が良さそうでしたので確認しました。
ですので FreeBSD でも再現します。
というものがあったので(ry
[fl]seek
前回、 (bashと)UNIX v7のshやzshでは 不思議なlseekやfseekの呼び出しがあるということを 少しだけ書きましたが、 ほかにもいくつかのリポジトリを見た感じでは [fl]seekを呼び出しているものはほかにもあるようです (「呼び出している箇所がある」ということ以上のことは今回調べていませんが)。
readシステムコールで毎回改行までだけ読むというのは効率的には実装できないので、stdio関数を使っていたらなおさら適当な境界まで読み、同一inode上書きでも行ごとには発生しなそう。bashの場合は子プロセス実行のたびに意図的にsync_buffered_stream()が逆シークしてfdを巻き戻し読みなおしてますね
— 技師長 (@gishicho) December 30, 2021
fish
tcsh
ksh
- ksh93/ksh: ksh 93u+m: current development of KornShell, based off the last stable AT&T release (93u+ 2012-08-01)
- Search · lseek
sh (FreeBSD)
これはgithubでサーチしてもリポジトリを対象にしてもOS丸ごとから検索されてしまうので 一層わからなさが(笑)
ピンポイントでいくつかのファイルを直接見たところたとえば こういうものが見つかった。
freebsd-src/miscbltin.c at 14a634df53d2de65233e490d0bfa806a05baff7c · freebsd/freebsd-src
static void
fdctx_init(int fd, struct fdctx *fdc)
{
off_t cur;
/* Check if fd is seekable. */
cur = lseek(fd, 0, SEEK_CUR);
*fdc = (struct fdctx){
.fd = fd,
.buflen = (cur != -1) ? READ_BUFLEN : 1,
.ep = &fdc->buf[0], /* No data */
};
}
freebsd-src/miscbltin.c at 14a634df53d2de65233e490d0bfa806a05baff7c · freebsd/freebsd-src
static void
fdctx_destroy(struct fdctx *fdc)
{
off_t residue;
if (fdc->buflen > 1) {
/*
* Reposition the file offset. Here is the layout of buf:
*
* | off
* v
* |*****************|-------|
* buf ep buf+buflen
* |<- residue ->|
*
* off: current character
* ep: offset just after read(2)
* residue: length for reposition
*/
residue = (fdc->ep - fdc->buf) - fdc->off;
if (residue > 0)
(void) lseek(fdc->fd, -residue, SEEK_CUR);
}
}
「いかにも」な印象があるけどこれらの関数を呼び出しているところも見ておかないと、 迂闊に結論は出せないかな。
いえなんかしつこくてすいませんw
— 技師長 (@gishicho) December 30, 2021
/bin/sh だと 8K 読んでおしまいなので、実行中の書き換え時の挙動も sh と bash で違うと言えるのではないかなと思います(今回初めて知りました)
まあそんなことするな、ってのがそもそもではありますが pic.twitter.com/mjVRLMAGrx
dash (/ FreeBSD /bin/sh / busybox)
調べてみると現状のFreeBSDの/bin/shは dashやbusybox(のsh)とルーツが同じらしいということを知ったので、 それじゃあとdashのコードを見てみると
- Debian Almquist shell - Wikipedia
- Almquist Shell - Wikipedia
- ash variants
- tklauser/dash: DASH Shell (clone of git://git.kernel.org/pub/scm/utils/dash/dash.git)
seekは使っていなかった。 そして読み込みに関してはIBUFSIZ(環境によるが512程度?)バイトずつread(2)で読んでいた。
ざっとmainからの呼び出しを潜るとこんな感じ。
main (main.c)
procargs (option.c)
setinputfile (input.c)
preadbuffer()
pgetc()
preadfd()
ということで、ちょっと大きめのスクリプトを用意しておいて後ろの方(ってどこですか)を 書き換えたり追記するなどのことをしないと今回の問題を引き起こした現象は発生しない (つまり、「*BSDでは起きない」とか「xxというシェルでは起きない」とは簡単には言えないのではないか) んじゃなかろうか。
おまけ
v7のshについて。
chkopen(idf)
STRING idf;
{
REG INT rc;
IF (rc=open(idf,0))<0
THEN failed(idf,badopen);
ELSE return(rc);
FI
}
/* open input file if specified */
IF comdiv
THEN estabf(comdiv); input = -1;
ELSE input=((flags&stdflg) ? 0 : chkopen(cmdadr));
comdiv--;
FI
のようにファイルディスクリプタを取得しておいて、 色々通りながら
v7unix/word.c at master - v7unix/v7unix
readc()
{
REG CHAR c;
REG INT len;
REG FILE f;
retry:
IF peekc
THEN c=peekc; peekc=0;
ELIF (f=standin, f->fnxt!=f->fend)
THEN IF (c = *f->fnxt++)==0
THEN IF f->feval
THEN IF estabf(*f->feval++)
THEN c=EOF;
ELSE c=SP;
FI
ELSE goto retry; /* = c=readc(); */
FI
FI
IF flags&readpr ANDF standin->fstak==0 THEN prc(c) FI
IF c==NL THEN f->flin++ FI
ELIF f->feof ORF f->fdes<0
THEN c=EOF; f->feof++;
ELIF (len=readb())<=0
THEN close(f->fdes); f->fdes = -1; c=EOF; f->feof++;
ELSE f->fend = (f->fnxt = f->fbuf)+len;
goto retry;
FI
return(c);
}
LOCAL readb()
{
REG FILE f=standin;
REG INT len;
REP IF trapnote&SIGSET THEN newline(); sigchk() FI
PER (len=read(f->fdes,f->fbuf,f->fsiz))<0 ANDF trapnote DONE
return(len);
}
のようにFILE構造体経由かつその中身を直接触って読み込みをしているんですね。 今では考えられないことだけど (そう言えばむかーしのMS-DOS用のプログラムでもMS-CのFILE構造体のメンバーを直接アクセスしているものがあったような)。
ファイルディスクリプタ絡みでよくわからなかったのが v7unix/main.c - v7unix/v7unix
/* move input */
IF input>0
THEN Ldup(input,INIO);
input=INIO;
FI
/* move output to safe place */
IF output==2
THEN Ldup(dup(2),OTIO);
output=OTIO;
FI
/* used for input and output of shell */
#define INIO 10
#define OTIO 11
ファイルディスクリプタをdupするときにわざわざ(?)その結果を10以上にしていること。 これはbashなどでも踏襲されているのでなにか目的(と理由)があってのことだとは思うのだけど、 「これだ」と断言できるだけの情報がない。
その他
- bashのスクリプト読み込みの動き - てきとうなメモ
- 自己追記によるFizzBuzz
- linux - Linuxはシェルスクリプトをどのように扱いますか?
- Question : How Does Linux deal with shell scripts?
- Shell - How Does Linux deal with shell scripts ? iTecTec
- ksh : 私のコマンドラインの履歴に突然アクセスできないのはなぜですか?
- linux - Edit shell script while it’s running - Stack Overflow
- filesystem - Can I modify a bash script (.sh) file while it is running? - Ask Ubuntu
- linux - What happens if you edit a script during execution? - Unix & Linux Stack Exchange
install
さて今回の「事故」を受けて、 シェルスクリプトのファイルを置き換えるのにcpではなく installを使えといった意見も見られましたが
また macOS と FreeBSD では inode は変わりました。つまり install コマンドで inode が変わるかどうかはカーネル、 ファイルシステム、実装などの違いで異なるようです。
install も実装によってはセーフではないので一応ちゃんと調べないと怖い https://t.co/Gp0RoSvv2t
— AoiMoe a.k.aしお兄P (@AoiMoe) December 29, 2021
man 見ると install は 4.2BSD で入ったと書いてあるのでソースをみた https://t.co/sBFHP7BxEn
— AoiMoe a.k.aしお兄P (@AoiMoe) December 29, 2021
install に -c を付けなくてもいいことを今日知った俺
— AoiMoe a.k.aしお兄P (@AoiMoe) December 29, 2021
ということでFreeBSDのもの(のたぶん最新)とGNU coreutils(9.0)のものをざっとみたところ (FreeBSDのinstallコマンドのソースはたぶん↓のリストの最後のxinstall.c)、
FreeBSDのものではたとえば freebsd-src/xinstall.c
if (rename(tempfile, to_name) < 0) {
serrno = errno;
unlink(tempfile);
errno = serrno;
err(EX_OSERR, "rename: %s to %s",
tempfile, to_name);
}
のように一時ファイルに書いたものをリネームするような手順をとっているのだけど、 Coreutilsのもの coreutils/install.c at master · coreutils/coreutils ではどうかというと
main()
install_file_in_file()
install_file()
copy_file()
copy.c/copy()
copy_internal()
copy_reg()
のように潜っていって、最深部(?)のcopy_reg()では 同じ名前のファイルがすでにあった場合に それを削除してから新たにファイルを作成しなおすようなことはせず 上書きしているっぽい(要確認 and 無保証)。 コードとしてはunlink(2)を呼び出したりしている部分もあるのだけど、 installからの呼び出しではそこを通らないように読める。
というのも、installコマンドが使う複数のフラグを初期化している関数がある coreutils/install.c at master · coreutils/coreutils のだけど
static void
cp_option_init (struct cp_options *x)
{
cp_options_default (x);
x->copy_as_regular = true;
x->reflink_mode = REFLINK_AUTO;
x->dereference = DEREF_ALWAYS;
x->unlink_dest_before_opening = true;
x->unlink_dest_after_failed_open = false;
x->hard_link = false;
x->interactive = I_UNSPECIFIED;
x->move_mode = false;
x->install_mode = true;
x->one_file_system = false;
x->preserve_ownership = false;
x->preserve_links = false;
x->preserve_mode = false;
x->preserve_timestamps = false;
x->explicit_no_preserve_mode = false;
x->reduce_diagnostics=false;
x->data_copy_required = true;
x->require_preserve = false;
x->require_preserve_xattr = false;
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
x->backup_type = no_backups;
/* Create destination files initially writable so we can run strip on them.
Although GNU strip works fine on read-only files, some others
would fail. */
x->set_mode = true;
x->mode = S_IRUSR | S_IWUSR;
x->stdin_tty = false;
x->open_dangling_dest_symlink = false;
x->update = false;
x->require_preserve_context = false; /* Not used by install currently. */
x->preserve_security_context = false; /* Whether to copy context from src. */
x->set_security_context = NULL; /* Whether to set sys default context. */
x->preserve_xattr = false;
x->verbose = false;
x->dest_info = NULL;
x->src_info = NULL;
}
unlinkを使うかどうかなどを分けるx->move_mode = false;
をこのあとtrueにしている部分が見当たらないから。
name mangling of python
【Python3】プライベートなインスタンス変数にクラス外からアクセスできてしまう - Qiita を読んで初めて知ったのだけど Pythonにはこういう「からくり」があったのね。
On encountering name mangled attributes, Python transforms these names by prepending a single underscore and the name of the enclosing class, for example:
>>> class Test: ... def __mangled_name(self): ... pass ... def normal_name(self): ... pass >>> t = Test() >>> [attr for attr in dir(t) if "name" in attr] ['_Test__mangled_name', 'normal_name']
クラスのプライベートメンバについて適切なユースケース(特にサブクラスで定義された名前との衝突を避ける場合)があるので、 名前マングリング (name mangling) と呼ばれる、限定されたサポート機構があります。 __spam (先頭に二個以上の下線文字、末尾に一個以下の下線文字) という形式の識別子は、 _classname__spam へとテキスト置換されるようになりました。ここで classname は、現在のクラス名から先頭の下線文字をはぎとった名前になります。 このような難号化 (mangle) は、識別子の文法的な位置にかかわらず行われるので、クラス定義内に現れた識別子全てに対して実行されます。
なるほど。
GNUjdoc
- ASCII.jp:GNUjdocが、GNU ProjectのWebサイトの「Software」ページに加わる
- GNUjdoc - GNU Project - Free Software Foundation (FSF)
- Index of GNUjdoc
- 第3章 受賞者インタビュー(2)─奥地 秀則氏:2008年度日本OSS貢献者賞受賞者インタビュー | エンジニアマインド … 技術評論社