问题
mbatchd 的 initSubmitReq()(lsbatch/daemons/mbd.job.c)在初始化 submitReq 模板时写了这样一行:
jobBill->umask = umask(0077);
意图是「读出当前 umask 并记录到字段里」,但 umask() 是读写一体的系统调用——它返回旧值的同时会把进程 umask 设成传入的新值。这行只取了返回值,没有把 umask 改回去,于是 mbatchd 的进程 umask 被永久改成 0077。
并且被捕获的那个值其实从未被使用:getMergeSubReq() 紧接着调用的 mergeSubReq() 会执行 to->umask = old->umask;,用原作业请求里的值(来自提交用户的真实 umask)把它覆盖掉。也就是说,这一行唯一的实际效果就是破坏进程 umask。
代码出自 2011-06-05 的 openlava 首次提交(58eb4746),并非 volclava 引入。
调用链
有两条路径会走到 initSubmitReq(),它们是同一条链,区别只在于触发时机。
一、运行时:用户执行 bmod。只有对 PEND 作业的 bmod 才会走到这里;对 RUN 作业的 bmod 会在更早的检查处被拒绝,不触发本问题。
do_modifyReq
└─ modifyJob() mbd.job.c:5732
└─ modifyAJob()
└─ getMergeSubReq() mbd.job.c:6270
├─ initSubmitReq() mbd.job.c:8209 ← umask(0077),不恢复
└─ mergeSubReq() mbd.job.c:7358
└─ to->umask = old->umask; ← 覆盖掉刚捕获的值
二、启动时:replay 事件日志。这是「重启无法恢复」的原因。main() 里 umask(022) 排在 minit() 之前,而 replay 发生在 minit() 内部,所以只要 lsb.events 里还留着 JOB_MODIFY2 记录,replay 就会重新把 umask 打成 0077。
main()
├─ umask(022) mbd.main.c:433 ← 先设为 022
└─ minit(FIRST_START) mbd.main.c:444
└─ init_log()
└─ replay lsb.events
└─ replay_modifyjob2() ← 遇到 JOB_MODIFY2 事件
└─ modifyJob() ← 同上,再次 umask(0077)
影响
只要执行过一次 bmod,mbatchd 的 umask 就会变成 0077,并且永远不会恢复——重启 mbatchd 也没用,因为 replay 事件时对 bmod 的事件同样会执行 modifyJob。后果是 mbatchd 之后创建的文件,默认权限从 0644 变成 0600,需要 chmod。
一个不需要任何新增代码就会踩到的场景是守护进程日志。ls_syslog() 每次写入前都会 lstat 日志文件,发现不存在就用 fopen(logfile, "a") 重建,而这条重建路径没有 chmod(初始化路径 openLogFile() 是有的)。因此只要做过 bmod、又轮转或删除过 mbatchd.log,重建出来的日志文件就是 0600,管理员账号反而读不了自己的守护进程日志。
实测对照,操作完全相同(删除日志文件后等待 mbatchd 重建),唯一变量是 umask:
| 事件日志中有无 bmod 记录 |
mbatchd umask |
重建后的日志文件 |
volclava 能否读取 |
| 无 |
0022 |
-rw-r--r-- |
可读 |
| 有 |
0077 |
-rw------- |
Permission denied |
问题
initSubmitReq()(lsbatch/daemons/mbd.job.c)在初始化 submitReq 模板时写了这样一行:umask()是读写一体的系统调用——它返回旧值的同时会把进程 umask 设成传入的新值。这行只取了返回值,没有把 umask 改回去,于是 mbatchd 的进程 umask 被永久改成 0077。getMergeSubReq()紧接着调用的mergeSubReq()会执行to->umask = old->umask;,用原作业请求里的值(来自提交用户的真实 umask)把它覆盖掉。也就是说,这一行唯一的实际效果就是破坏进程 umask。58eb4746),并非 volclava 引入。调用链
initSubmitReq(),它们是同一条链,区别只在于触发时机。main()里umask(022)排在minit()之前,而 replay 发生在minit()内部,所以只要lsb.events里还留着JOB_MODIFY2记录,replay 就会重新把 umask 打成 0077。影响
modifyJob。后果是 mbatchd 之后创建的文件,默认权限从 0644 变成 0600,需要 chmod。ls_syslog()每次写入前都会lstat日志文件,发现不存在就用fopen(logfile, "a")重建,而这条重建路径没有 chmod(初始化路径openLogFile()是有的)。因此只要做过 bmod、又轮转或删除过mbatchd.log,重建出来的日志文件就是 0600,管理员账号反而读不了自己的守护进程日志。