先说症状:
collectd exec插件调用的几个外部脚本,其中总会随机有一个缺少COLLECTD_HOSTNAME和COLLECTD_INTERVAL环境变量。
搜了一下是这个bug https://github.com/collectd/collectd/issues/3041
然后我好奇啊,就读了一下修改前后的代码,发现collectd不按套路出牌。
带有bug的版本:
先setenv()设置主进程自己的环境变量,然后尝试fork(),如果成功,在子进程里execvp();主进程重新unsetenv()恢复主进程自己的环境变量。在多个exec密集执行的时候,都会访问主进程的环境变量,会有race condition,偶尔会发生前一个exec插件刚unsetenv()然后后一个exec插件开始fork()的情况,丢失环境变量。
修复后的版本:
先fork(),在子进程里准备环境变量数组,尝试execvpe()带签署环境变量数组作为参数,执行新进程(execvpe()为GNU专有扩展),或者先设置extern char **environ指针指向准备好的数组,然后execvp()执行新进程直接继承。
别人家套路:
先准备环境变量数组,然后fork(),在子进程里execve()并使用前述环境变量数组作为参数。