Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
env EDITOR=vipw vipwしてみた
Search
Ogata Yasuhiko
March 05, 2013
280
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
env EDITOR=vipw vipwしてみた
Ogata Yasuhiko
March 05, 2013
Featured
See All Featured
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.5k
How STYLIGHT went responsive
nonsquared
100
6.2k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.6k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.2k
Designing Powerful Visuals for Engaging Learning
tmiket
1
450
Building the Perfect Custom Keyboard
takai
2
820
KATA
mclloyd
PRO
35
15k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
220
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Transcript
env EDITOR=vipw vipw してみた @yogata
vipw • ユーザ追加・削除・情報変更する • vipw is 神
vipw useradd/adduser メリット Jviが使える 特に無い デメリット 特にない Luserとaddのどっちが 先かわからない
こんなにもすばらしいvipw • 使いたくなってきたはず • でも……viは慣れてないし…… env
EDITOR=XXX vipw
vipw と EDITOR変数 • env EDITOR=nano vipw
vipw と EDITOR変数 • env EDITOR=emacs vipw
vipw と EDITOR変数 ( ^o^) env EDITOR=emacs vipw →
emacsで開く ( ˘⊖˘) 。o( env EDITOR=vipw vipwしたらどうなるんだ) |Terminal.app| ┗(☋` )┓三 ( ◠‿◠ )☛ そこに気づいてしまったか ▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわあああああああ
やってみる # env EDITOR=vipw vipw rootになるのを 忘れずに
やってみる
調べる • FreeBSD – /usr/src/usr.sbin/vipw/vipw.c – /usr/src/lib/libuMl/pw_uMl.c – /usr/include/pwd.h
– err周りはここらへん • /usr/src/lib/libc/gen/err.c
vipw.c (1/3) 65 int
66 main(int argc, char *argv[]) 67 { 68 const char *passwd_dir = NULL; 69 int ch, pfd, ad; 70 char *line; 71 size_t len; 72 73 while ((ch = getopt(argc, argv, "d:")) != -‐1) 74 switch (ch) { 75 case 'd': 76 passwd_dir = optarg; 77 break; 78 case '?': 79 default: 80 usage(); 81 } 82 83 argc -‐= opMnd; 84 argv += opMnd; vipwのオプションは-‐dだけ.それ 以外のオプションが来たらusage() する
vipw.c (2/3) 85
86 if (argc != 0) 87 usage(); 88 89 if (pw_init(passwd_dir, NULL) == -‐1) 90 err(1, "pw_init()"); 91 if ((pfd = pw_lock()) == -‐1) { 92 pw_fini(); 93 err(1, "pw_lock()"); 94 } 95 if ((ad = pw_tmp(pfd)) == -‐1) { 96 pw_fini(); 97 err(1, "pw_tmp()"); 98 } 99 (void)close(ad); 100 /* Force umask for parMal writes made in the edit phase */ 101 (void)umask(077); pw_tmp() ? pw_lock() ? pw_init() ?
vipw.c (3/3) 103 for
(;;) { 104 switch (pw_edit(0)) { 105 case -‐1: 106 pw_fini(); 107 err(1, "pw_edit()"); 108 case 0: 109 pw_fini(); 110 errx(0, "no changes made"); 111 default: 112 break; 113 } 114 if (pw_mkdb(NULL) == 0) { 115 pw_fini(); 116 errx(0, "password list updated"); 117 } 118 prina("re-‐edit the password file? "); 119 fflush(stdout); 120 if ((line = fgetln(stdin, &len)) == NULL) { 121 pw_fini(); 122 err(1, "fgetln()"); 123 } 124 if (len > 0 && (*line == 'N' || *line == 'n')) 125 break; 126 } 127 pw_fini(); 128 exit(0); 129 } pw_edit() ? pw_mkdb() ? パスワードファイルを再構築に失 敗したら,再度ファイルを編集する か確認する
vipw.c まとめ オプション処理.-‐dだけ キニナル
pw_init() (1/2) 93 int 94 pw_init(const char *dir,
const char *master) 95 { 96 #if 0 97 struct rlimit rlim; 98 #endif 99 100 if (dir == NULL) { 101 strcpy(passwd_dir, _PATH_ETC); 102 } else { 103 if (strlen(dir) >= sizeof(passwd_dir)) { 104 errno = ENAMETOOLONG; 105 return (-‐1); 106 } 107 strcpy(passwd_dir, dir); 108 } 109 -‐dオプションが無ければ master.passwdファイルがあるディ レクトリの初期値として_PATH_ETC を使う(/usr/include/pwd.h)
pw_init() (2/2) 110 if (master
== NULL) { 111 if (dir == NULL) { 112 strcpy(masterpasswd, _PATH_MASTERPASSWD); 113 } else if (snprina(masterpasswd, sizeof(masterpasswd), "%s/%s", 114 passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) { 115 errno = ENAMETOOLONG; 116 return (-‐1); 117 } 118 } else { 119 if (strlen(master) >= sizeof(masterpasswd)) { 120 errno = ENAMETOOLONG; 121 return (-‐1); 122 } 123 strcpy(masterpasswd, master); 124 } master.passwdのファイル名を指定す ることができるが,vipw.cからはNULL なので_PATH_MASTERPASSWD (/usr/include/pwd.h)を使う
vipw.c まとめ オプション処理.-‐dだけ パスワードファイルの場所を探す
pw_lock() 166 int 167 pw_lock(void)
168 { 169 170 if (*masterpasswd == '\0') 171 return (-‐1); <snip> 179 for (;;) { 180 struct stat st; 181 182 lockfd = open(masterpasswd, O_RDONLY, 0); 183 if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -‐1) 184 err(1, "%s", masterpasswd); 185 /* XXX vulnerable to race condiMons */ 186 if (flock(lockfd, LOCK_EX|LOCK_NB) == -‐1) { 187 if (errno == EWOULDBLOCK) { 188 errx(1, "the password db file is busy"); 189 } else { 190 err(1, "could not lock the passwd file: "); 191 } 192 } <snip> ロックする master.passwdファイルを開いて
vipw.c まとめ オプション処理.-‐dだけ パスワードファイルの場所を探す パスワードファイルをロックする
pw_tmp() (1/2) 213 int 214 pw_tmp(int
mfd) 215 { 216 char buf[8192]; 217 ssize_t nr; 218 const char *p; 219 int ad; 220 221 if (*masterpasswd == '\0') 222 return (-‐1); 223 if ((p = strrchr(masterpasswd, '/'))) 224 ++p; 225 else 226 p = masterpasswd; 227 if (snprina(tempname, sizeof(tempname), "%.*spw.XXXXXX", 228 (int)(p -‐ masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { 229 errno = ENAMETOOLONG; 230 return (-‐1); 231 } 232 if ((ad = mkstemp(tempname)) == -‐1) 233 return (-‐1); master.passwdファイルがあるディ レクトリに一時ファイルpw.XXXXXX (Xは乱数に置き換え)を作成.
pw_tmp() (2/2) 234 if
(mfd != -‐1) { 235 while ((nr = read(mfd, buf, sizeof(buf))) > 0) 236 if (write(ad, buf, (size_t)nr) != nr) 237 break; 238 if (nr != 0) { 239 unlink(tempname); 240 *tempname = '\0'; 241 close(ad); 242 return (-‐1); 243 } 244 } 245 return (ad); 246 } master.passwdの中身を 一時ファイルにコピー
vipw.c まとめ オプション処理.-‐dだけ パスワードファイルの場所を探す パスワードファイルをロックする パスワードファイルと同じディレク
トリに作業用ファイルを作って, 中身をコピーする
pw_edit() (1/4) 288 int 289 pw_edit(int
notsetuid) 290 { <snip> 297 if ((editor = getenv("EDITOR")) == NULL) 298 editor = _PATH_VI; 299 if (stat(tempname, &st1) == -‐1) 300 return (-‐1); <snip> パスワードファイルを編集する エディタを設定.EDITOR変数の 設定が無ければviを設定
pw_edit() (2/4) 309 switch
((editpid = fork())) { 310 case -‐1: 311 return (-‐1); 312 case 0: 313 sigacMon(SIGINT, &sa_int, NULL); 314 sigacMon(SIGQUIT, &sa_quit, NULL); 315 sigprocmask(SIG_SETMASK, &oldsigset, NULL); 316 if (notsetuid) { 317 (void)setgid(getgid()); 318 (void)setuid(getuid()); 319 } 320 errno = 0; 321 execlp(editor, basename(editor), tempname, (char *)NULL); 322 _exit(errno); 323 default: 324 /* parent */ 325 break; 326 } master.passwdをコピーした一時 ファイル(tempname)を引数にし てエディタ(editor)を開く. 104 switch (pw_edit(0)) { 105 case -‐1: 106 pw_fini(); 107 err(1, "pw_edit()"); <snip> 113 } vipw.c 後述
pw_edit() (3/4) 327 for
(;;) { 328 if (waitpid(editpid, &pstat, WUNTRACED) == -‐1) { 329 if (errno == EINTR) 330 conMnue; 331 unlink(tempname); 332 editpid = -‐1; 333 break; 334 } else if (WIFSTOPPED(pstat)) { 335 raise(WSTOPSIG(pstat)); 336 } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { 337 editpid = -‐1; 338 break; 339 } else { 340 unlink(tempname); 341 editpid = -‐1; 342 break; 343 } 344 } execlp()が正常終了したら一時 ファイルを残して終了する execlp()が異常終了したら一時 ファイルを削除して終了する エラーだったらファイルを削除し て終了する waitpid()でエラーだったり,execlp()が異常終了したらファイルを削除する
pw_edit() (4/4) 345 sigacMon(SIGINT,
&sa_int, NULL); 346 sigacMon(SIGQUIT, &sa_quit, NULL); 347 sigprocmask(SIG_SETMASK, &oldsigset, NULL); 348 if (stat(tempname, &st2) == -‐1) { 349 return (-‐1); 350 } 351 return (st1.st_mMme != st2.st_mMme); 352 } execlp()が正常終了したら一時 ファイルを残して終了する 104 switch (pw_edit(0)) { 105 case -‐1: 106 pw_fini(); 107 err(1, "pw_edit()"); 108 case 0: 109 pw_fini(); 110 errx(0, "no changes made"); 111 default: 112 break; 113 } 変更がない時 変更がある時 vipw.c waitpid()でエラーだったりexeclp()で不 正終了していたらtempnameファイルは 無いのでstat()が-‐1になりreturn(-‐1) EDITORの終了を失敗し たり,execlp()でEDITOR が不正終了した場合
後述のアレ pw_edit()のところで execlp(editor, basename(editor), tempname, (char *)NULL);
↓ execlp(“vipw” “vipw”, “pw.XXXXX”, (char *)NULL); こうなって,結局のところ # vipw pw.XXXXX な感じになる
そういえば • vipwコマンドに”pw.XXXXX”引数は不正なので usage() while ((ch = getopt(argc,
argv, "d:")) != -‐1) switch (ch) { … default: usage(); } # env EDITOR=vipw vipw usage: vipw [-‐d directory] vipw: pw_edit(): No such file or directory つまりこうなる
EDITORが不正終了したので 327 for (;;)
{ 328 if (waitpid(editpid, &pstat, WUNTRACED) == -‐1) { 329 if (errno == EINTR) 330 conMnue; 331 unlink(tempname); 332 editpid = -‐1; 333 break; 334 } else if (WIFSTOPPED(pstat)) { 335 raise(WSTOPSIG(pstat)); 336 } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) { 337 editpid = -‐1; 338 break; 339 } else { 340 unlink(tempname); 341 editpid = -‐1; 342 break; 343 } 344 } execlp()が不正終了したら一時 ファイルを削除して終了する pw_edit()
pw_edit()を振り返って 345 sigacMon(SIGINT, &sa_int,
NULL); 346 sigacMon(SIGQUIT, &sa_quit, NULL); 347 sigprocmask(SIG_SETMASK, &oldsigset, NULL); 348 if (stat(tempname, &st2) == -‐1) { 349 return (-‐1); 350 } 351 return (st1.st_mMme != st2.st_mMme); 352 } 104 switch (pw_edit(0)) { 105 case -‐1: 106 pw_fini(); 107 err(1, "pw_edit()"); 108 case 0: 109 pw_fini(); 110 errx(0, "no changes made"); 111 default: 112 break; 113 } vipw.c execlp()で不正終了していたら tempnameファイルは無いので stat()が-‐1になりreturn(-‐1) EDITORの終了を失敗し たり,execlp()でEDITOR が不正終了した場合 # env EDITOR=vipw vipw usage: vipw [-‐d directory] vipw: pw_edit(): No such file or directory
vipw.c まとめ オプション処理.-‐dだけ パスワードファイルの場所を探す パスワードファイルをロックする パスワードファイルと同じディレク
トリに作業用ファイルを作って, 中身をコピーする EDITORで指定したコマンドに作業用ファイルを渡したり, コマンドが異常終了したら作業ファイルを消したり, 作業ファイルの更新時間を確認したり, 作業ファイルが消されてたら更新時間を確認できない のでエラーを返したり, する.
(参考) env EDITOR=nano vipwのとき pw.XXXXX の部分
pw_mkdb() • 省略
まとめ • vipwは内部でexeclp()を使ってEDITORを実行し,引 数に作業用ファイルを渡す • 1つ目のエラーはEDITOR=vipwの時にvipwコマンド が-‐d以外の引数を許容しないエラー
• 2つ目のエラーは,1つ目のエラーに起因して作業用 ファイルを消し,その後の処理で作業用ファイルを 見に行こうとするが存在しないので出るエラー # env EDITOR=vipw vipw usage: vipw [-‐d directory] vipw: pw_edit(): No such file or directory