Upstart - 如何将重载信号发送到脚本作为不同的用户启动?

时间:2013-09-19 17:38:55

标签: python linux centos daemon upstart

我有python程序(几个脚本)需要在远程机器上运行作为守护进程,这是CentOS 6.4。所以,我认为Upstart是要走的路。

基本要求是:

  • 启动/停止守护程序应用程序的简便方法
  • 重生的应用程序如果崩溃
  • 应用必须在非特权用户下运行
  • 能够处理SIGHUP以重新加载配置

该服务器上的Upstart版本为0.6.5,因此节setuidsetgid不可用(它们仅出现在1.4中)。所以我使用cookbook中的official workaround。我可以启动/停止我的应用程序,但我的主要问题是我的应用程序没有收到重新加载信号。

我将提供可以重现问题的剥离脚本。

Python脚本(app.py):

import os
import time
import signal
import logging

logging.basicConfig(filename='hup.log', level=logging.INFO,
    format="%(asctime)s: %(levelname)s: %(message)s")
log = logging.getLogger(__name__)


running = True

def run():
    log.info('PROGRAM STARTUP')
    log.info('Current pid: %d' % os.getpid())

    while running:
        log.info('Doing some hard work')
        time.sleep(10)
    else:
        log.info('PROGRAM TEARDOWN')

def signal_handler(signum, frame):
    log.info("Received Signal: %s at frame: %s" % (signum, frame))

    if signum == signal.SIGTERM:
        log.info('Received request to terminate daemon (SIGTERM)')
        global running
        running = False
    elif signum == signal.SIGHUP:
        log.info('Received reload config request (SIGHUP)')
        pass  # reload config here


signal.signal(signal.SIGHUP, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

run()

Upstart配置(hup.conf):

start on runlevel [2345]
stop on runlevel [!2345]

respawn
respawn limit 10 5

chdir /home/dev/prj/im

script
    # activate the virtual environment
    . /home/dev/.virtualenvs/myvenv/bin/activate

    # This line runs script as root
    # exec python app.py

    # this is official workaround for running prcesses as different
    # user in upstart version prior to 1.4
    # Run as user 'dev'
    exec su -s /bin/sh -c 'exec "$0" "$@"' dev -- python app.py
end script

输出(如您所见,python app.py的实际流程与新贵节目的PID不同):

[dev@localhost ~]$ sudo start hup
hup start/running, process 8608
[dev@localhost ~]$ ps -elf | grep app.py
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root      8608     1  0  80   0 - 16143 wait   19:53 ?        00:00:00 su -s /bin/sh -c exec "$0" "$@" dev -- python app.py
4 S dev       8613  8608  0  80   0 -  7866 poll_s 19:53 ?        00:00:00 python app.py
[dev@localhost ~]$ sudo reload hup
[dev@localhost ~]$ sudo stop hup
hup stop/waiting

日志显示没有收到重载信号:

2013-09-19 20:00:36,092: INFO: PROGRAM STARTUP
2013-09-19 20:00:36,093: INFO: Current pid: 8613
2013-09-19 20:00:36,093: INFO: Doing some hard work
2013-09-19 20:00:45,287: INFO: Received Signal: 15 at frame: <frame object at 0xba2dd0>
2013-09-19 20:00:45,287: INFO: Received request to terminate daemon (SIGTERM)
2013-09-19 20:00:45,287: INFO: PROGRAM TEARDOWN

但是,当我使用hup.conf取消注释exec python app.py中的行(并使用exec su...注释一行)时,一切正常,但应用以root身份运行。

[dev@localhost ~]$ sudo start hup
hup start/running, process 8811
[dev@localhost ~]$ ps -elf | grep app.py
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root      8811     1  0  80   0 -  8412 poll_s 20:13 ?        00:00:00 python app.py
[dev@localhost ~]$ sudo reload hup
[dev@localhost ~]$ sudo stop hup
hup stop/waiting

日志文件显示已收到SIGHUP信号:

2013-09-19 20:13:40,290: INFO: PROGRAM STARTUP
2013-09-19 20:13:40,290: INFO: Current pid: 8811
2013-09-19 20:13:40,291: INFO: Doing some hard work
2013-09-19 20:13:59,739: INFO: Received Signal: 1 at frame: <frame object at 0x7d44c0>
2013-09-19 20:13:59,739: INFO: Received reload config request (SIGHUP)
2013-09-19 20:13:59,739: INFO: Doing some hard work
2013-09-19 20:14:04,313: INFO: Received Signal: 15 at frame: <frame object at 0x7d44c0>
2013-09-19 20:14:04,313: INFO: Received request to terminate daemon (SIGTERM)
2013-09-19 20:14:04,313: INFO: PROGRAM TEARDOWN

我的主要问题是当我在这个旧版本的暴发版上的不同用户下运行脚本时如何使reload命令工作?

1 个答案:

答案 0 :(得分:1)

我最终以root身份运行我的脚本,然后将权限删除给所需的用户。 要以root身份运行脚本,我已从上面的Upstart配置中取消注释exec python app.py。 要删除权限,我使用this answer:

中稍加修改的代码
def drop_privileges(uid_name, gid_name):
    '''If running as root, this function will try to change current uid and gid
    to given values.
    May raise OSError in case of error.
    :param uid_name: Name of the user we need to be running as.
    :param gid_name: Name of the group we need to be running as.
    '''

    starting_uid = os.getuid()
    starting_gid = os.getgid()

    log.info('drop_privileges: started as %s/%s' %
        (pwd.getpwuid(starting_uid).pw_name,
        grp.getgrgid(starting_gid).gr_name))

    if starting_uid == 0:
        # If we started as root, drop privs and become the specified user/group
        log.info("drop_privileges: trying to drop provileges to '%s'" % uid_name)

        # Get the uid/gid from the name
        running_uid = pwd.getpwnam(uid_name).pw_uid
        running_gid = grp.getgrnam(gid_name).gr_gid

        # Try setting the new uid/gid
        # These calls may result in exception, let it propagate back
        # Exception thrown is: OSError (e.errno == 1 means 'Operation not
        # permitted')
        os.setgid(running_gid)
        os.setuid(running_uid)

        final_uid = os.getuid()
        final_gid = os.getgid()
        log.info('drop_privileges: running as %s/%s' %
            (pwd.getpwuid(final_uid).pw_name,
             grp.getgrgid(final_gid).gr_name))