From 6c12f313e32815e0247e8d350ba9db84afb54092 Mon Sep 17 00:00:00 2001 From: benoitc Date: Mon, 22 Feb 2010 14:20:06 +0100 Subject: [PATCH] on irc jbergstroem noticed that gunicorn behavior is different than nginx one in the way it manages processes uid/gid. Only nginx workers get uid/gid and master if launch as root stay root. This patch give ti gunicorn the same behavior. --- gunicorn/arbiter.py | 2 +- gunicorn/config.py | 28 +++++++++++++++++++- gunicorn/main.py | 26 +----------------- gunicorn/management/commands/run_gunicorn.py | 1 - gunicorn/util.py | 21 ++++++++++++++- gunicorn/worker.py | 8 ++++-- 6 files changed, 55 insertions(+), 31 deletions(-) diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py index e9efa560..8b5477ae 100644 --- a/gunicorn/arbiter.py +++ b/gunicorn/arbiter.py @@ -356,7 +356,7 @@ class Arbiter(object): continue worker = Worker(i, self.pid, self.LISTENER, self.modname, - self.timeout/2.0, self.debug) + self.timeout/2.0, self.conf) self.conf.before_fork(self, worker) pid = os.fork() if pid != 0: diff --git a/gunicorn/config.py b/gunicorn/config.py index f5cebcff..287b756b 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -3,7 +3,9 @@ # This file is part of gunicorn released under the MIT license. # See the NOTICE for more information. +import grp import os +import pwd import sys from gunicorn import util @@ -104,13 +106,37 @@ class Config(object): @property def umask(self): - if not self.conf['umask']: + if not self.conf.get('umask'): return 0 umask = self.conf['umask'] if isinstance(umask, basestring): return int(umask, 0) return umask + @property + def uid(self): + if not self.conf.get('user'): + return os.geteuid() + + user = self.conf.get('user') + if user.isdigit() or isinstance(user, int): + uid = int(user) + else: + uid = pwd.getpwnam(user).pw_uid + return uid + + @property + def gid(self): + if not self.conf.get('group'): + return os.getegid() + group = self.conf.get('group') + if group.isdigit() or isinstance(group, int): + gid = int(group) + else: + gid = grp.getgrnam(group).gr_gid + + return gid + def _hook(self, hookname, *args): hook = self.conf.get(hookname) diff --git a/gunicorn/main.py b/gunicorn/main.py index 0f11dec4..63ac3fc2 100644 --- a/gunicorn/main.py +++ b/gunicorn/main.py @@ -3,12 +3,9 @@ # This file is part of gunicorn released under the MIT license. # See the NOTICE for more information. -import ctypes -import grp import logging import optparse as op import os -import pwd import pkg_resources import sys @@ -99,26 +96,7 @@ def daemonize(umask): os.dup2(0, 1) os.dup2(0, 2) -def set_owner_process(user,group): - """ set user and group of workers processes """ - if group: - if group.isdigit() or isinstance(group, int): - gid = int(group) - else: - gid = grp.getgrnam(group).gr_gid - - try: - os.setgid(gid) - except OverflowError: - # versions of python < 2.6.2 don't manage unsigned int for - # groups like on osx or fedora - os.setgid(-ctypes.c_int(-gid).value) - if user: - if user.isdigit() or isinstance(user, int): - uid = int(user) - else: - uid = pwd.getpwnam(user).pw_uid - os.setuid(uid) + def main(usage, get_app): """ function used by different runners to setup options @@ -137,7 +115,6 @@ def main(usage, get_app): else: os.umask(conf['umask']) os.setpgrp() - set_owner_process(conf['user'], conf['group']) configure_logging(conf) arbiter.run() @@ -173,7 +150,6 @@ def paste_server(app, global_conf=None, host="127.0.0.1", port=None, else: os.umask(conf['umask']) os.setpgrp() - set_owner_process(conf["user"], conf["group"]) configure_logging(conf) arbiter.run() diff --git a/gunicorn/management/commands/run_gunicorn.py b/gunicorn/management/commands/run_gunicorn.py index 912887ed..2209a4a5 100644 --- a/gunicorn/management/commands/run_gunicorn.py +++ b/gunicorn/management/commands/run_gunicorn.py @@ -71,7 +71,6 @@ class Command(BaseCommand): else: os.umask(conf['umask']) os.setpgrp() - set_owner_process(conf["user"], conf["group"]) configure_logging(conf) arbiter.run() except WSGIServerException, e: diff --git a/gunicorn/util.py b/gunicorn/util.py index 3fd22351..48d4ffa6 100644 --- a/gunicorn/util.py +++ b/gunicorn/util.py @@ -3,6 +3,7 @@ # This file is part of gunicorn released under the MIT license. # See the NOTICE for more information. +import ctypes import errno import fcntl import os @@ -29,7 +30,25 @@ monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - +def set_owner_process(uid,gid): + """ set user and group of workers processes """ + if gid: + try: + os.setgid(gid) + except OverflowError: + # versions of python < 2.6.2 don't manage unsigned int for + # groups like on osx or fedora + os.setgid(-ctypes.c_int(-gid).value) + + if uid: + os.setuid(uid) + +def chown(path, uid, gid): + try: + os.chown(path, uid, gid) + except OverflowError: + os.chown(path, uid, -ctypes.c_int(-gid).value) + def parse_address(host, port=None, default_port=8000): if host.startswith("unix:"): return host.split("unix:")[1] diff --git a/gunicorn/worker.py b/gunicorn/worker.py index 5d04e8f8..4a76c9f5 100644 --- a/gunicorn/worker.py +++ b/gunicorn/worker.py @@ -27,14 +27,16 @@ class Worker(object): PIPE = [] - def __init__(self, workerid, ppid, socket, app, timeout, debug=False): + def __init__(self, workerid, ppid, socket, app, timeout, conf): self.nr = 0 self.id = workerid self.ppid = ppid - self.debug = debug + self.debug = conf['debug'] + self.conf = conf self.socket = socket self.timeout = timeout self.fd, self.tmpname = tempfile.mkstemp(prefix="wgunicorn-") + util.chown(self.tmpname, conf.uid, conf.gid) self.tmp = os.fdopen(self.fd, "r+b") self.app = app self.alive = True @@ -81,6 +83,8 @@ class Worker(object): os.chmod(self.tmpname, self.spinner) def init_process(self): + util.set_owner_process(self.conf.uid, self.conf.gid) + # init pipe self.PIPE = os.pipe() map(util.set_non_blocking, self.PIPE)