env EDITOR=vipw vipwしたらエラーになる件について
面白いこと聞いたので調べてみた.
# env EDITOR=vipw vipw usage: vipw [-d directory] vipw: pw_edit(): No such file or directory
この2行のエラーについて.環境はFreeBSD 9.0-RELEASE.
vipwのソースコードは/usr/src/usr.sbin/vipw
# wc vipw.c 137 566 3804 vipw.c
なんとお手軽……(ゴクリ
vipw の概要は,初期化 → pw_init() → pw_lock() → pw_tmp() → pw_edit() -> pw_mkdb() → pw_fini() な感じ.vipw.c では EDITOR 環境変数を読まない.pw_hoge() は /usr/src/lib/libutil/pw_util.cにあるのでそっちをみる.
見てみると,EDITOR環境変数を読み込んで,無ければviを設定してる.
/usr/src/lib/libutil/pw_util.c 289 pw_edit(int notsetuid) 290 { ... 297 if ((editor = getenv("EDITOR")) == NULL) 298 editor = _PATH_VI;
その次はパスワードファイルを編集するプロセスを fork する.321 行目でエディタを起動.env EDITOR=vipw してるので,vipwに tempname を引数として与えて起動することになる.man vipw すれば分かる通り,vipw に -d オプション以外の引数はエラーになる.
これが1行目のエラー.
このときに errno が -1 になって,_exit() で子プロセス(env EDITORの方)の戻り値は -1 に.
/usr/src/lib/libutil/pw_util.c 289 pw_edit(int notsetuid) 290 { ... 309 switch ((editpid = fork())) { 310 case -1: 311 return (-1); 312 case 0: 313 sigaction(SIGINT, &sa_int, NULL); 314 sigaction(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 }
vipw に戻って,switch(pw_edit(0)) してるところを見ると,子プロセス(env EDITORの方)の戻り値が -1 だったので, err(1, "pw_edit()") になる.
これが2行目のエラー.
/usr/src/usr.sbin/vipw/vipw.c 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 printf("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 }
tempname は masterpasswd から生成してて,masterpasswd と同じディレクトリに,ファイル名を pw.XXXXXX にして一時ファイルを作成する.
/usr/src/usr.sbin/vipw/vipw.c 213 int 214 pw_tmp(int mfd) 215 { ... 221 if (*masterpasswd == '\0') 222 return (-1); 223 if ((p = strrchr(masterpasswd, '/'))) 224 ++p; 225 else 226 p = masterpasswd; 227 if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX", 228 (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) { 229 errno = ENAMETOOLONG; 230 return (-1); 231 }
masterpasswd は _PATH_MASTERPASSWD から生成してる._PATH_MASTERPASSWD は/usr/include/pwd.h に書いてある.
93 int 94 pw_init(const char *dir, const char *master) 95 { ... 110 if (master == NULL) { 111 if (dir == NULL) { 112 strcpy(masterpasswd, _PATH_MASTERPASSWD); 113 } else if (snprintf(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 }
/usr/include/pwd.h 64 #define _PATH_PWD "/etc" 65 #define _PATH_PASSWD "/etc/passwd" 66 #define _PASSWD "passwd" 67 #define _PATH_MASTERPASSWD "/etc/master.passwd" 68 #define _MASTERPASSWD "master.passwd"