在 supervisord 启动的 Python 进程下获取用户的主文件夹

Get user's home folder under Python process started by supervisord

我想将登录密码存储在 Django 配置中当前用户帐户的文件中。我正在使用推荐的便携方式获取主文件夹,如:

os.path.expanduser("~")

这适用于所有环境,包括本地环境和在服务器上使用 gunicorn -D config.wsgi 启动时的环境。

我的问题是,我引入了 supervisord 来控制 gunicorn 进程,现在这个功能不起作用,它只是 returns /.

这是supervisord.conf

的相关部分
[program:kek_django]
command=.../venv/bin/gunicorn config.wsgi
directory=.../django
user=testuser

在此环境下,os.path.expanduser("~") 变为 /

你能告诉我如何通过修复环境或用于检测主目录的函数来解决这个问题吗?

注意:OS 是 FreeBSD 10,如果相关的话

更新:os.environ 在 运行 进程下报告以下内容:

'SUPERVISOR_SERVER_URL': 'unix:///var/run/supervisor/supervisor.sock',
'RC_PID': '84177',
'SERVER_SOFTWARE': 'gunicorn/19.3.0',
'SUPERVISOR_ENABLED': '1', 
'SUPERVISOR_PROCESS_NAME': 'test_django',
'PWD': '/',
'DJANGO_SETTINGS_MODULE': 'config.settings.production', 
'SUPERVISOR_GROUP_NAME': 'test_django', 
'PATH': '/sbin:/bin:/usr/sbin:/usr/bin',
'HOME': '/'

正如 supervisordSubprocess Environment 文档所说:

No shell is executed by supervisord when it runs a subprocess, so environment variables such as USER, PATH, HOME, SHELL, LOGNAME, etc. are not changed from their defaults or otherwise reassigned. This is particularly important to note when you are running a program from a supervisord run as root with a user= stanza in the configuration. Unlike cron, supervisord does not attempt to divine and override “fundamental” environment variables like USER, PATH, HOME, and LOGNAME when it performs a setuid to the user defined within the user= program config option. If you need to set environment variables for a particular program that might otherwise be set by a shell invocation for a particular user, you must do it explicitly within the environment= program config option. An example of setting these enviroment variables is as below.

[program:apache2]
command=/home/chrism/bin/httpd -c "ErrorLog /dev/stdout" -DFOREGROUND
user=chrism
environment=HOME="/home/chrism",USER="chrism"

所以,这才是真正的解决方法。 (如果您动态构建 supervisord.conf 文件并且需要知道如何动态查找这些值,我可以解释一下,但这很简单,而且我认为您无论如何都不需要它。)

[program:kek_django]
command=.../venv/bin/gunicorn config.wsgi
directory=.../django
user=testuser
environment=HOME="/home/testuser"

如果这对您没有意义,请考虑:

如果你是 运行 supervisord 作为 root,它没有 testuserHOME 或其他任何东西。它所做的只是setuid(testuser),这只是改变了它的用户ID;它不会给 shell 或系统的任何其他部分任何机会来设置 testuser 的变量。大多数类似的工具都有伪造它的变通方法,遵循 cron 如何工作的老套路,但 supervisord 有意选择不这样做。


或者,正如 expanduser 的文档所说:

On Unix, an initial ~ is replaced by the environment variable HOME if it is set; otherwise the current user’s home directory is looked up in the password directory through the built-in module pwd. An initial ~user is looked up directly in the password directory.

快速浏览一下 the source 就会发现它以最明显的方式做到了这一点。

因此,您的代码中存在三个明显的解决方法:

  1. 使用 ~testuser 而不是 ~(如果需要,您甚至可以通过用户名以编程方式生成)。
  2. 编写您自己的 expanduser 函数,只执行 pwd.getpwuid(os.getuid()).pw_dir 而不检查 HOME
  3. 如果是 /,则在启动时手动将 HOME 设置为 pwd.getpwuid(os.getuid()).pw_dir