diff options
-rw-r--r-- | docs/roadmap.txt | 13 | ||||
-rw-r--r-- | paste/app_setup.py | 91 | ||||
-rwxr-xr-x | paste/server.py | 5 | ||||
-rwxr-xr-x | scripts/paste-setup | 2 |
4 files changed, 104 insertions, 7 deletions
diff --git a/docs/roadmap.txt b/docs/roadmap.txt index e0f313d..fab8e69 100644 --- a/docs/roadmap.txt +++ b/docs/roadmap.txt @@ -32,8 +32,6 @@ paste-setup * An application could be mapped to a particular URL, given a configuration file that describes the application. -* **``--daemon`` option for running as background process** - * Template files should have optional ``.ext_tmpl`` extension, where ``_tmpl`` gets stripped. This way the currently-invalid ``.py`` template files wouldn't be recognized as Pythohn files. @@ -41,6 +39,13 @@ paste-setup * Allow applications to easily add their own commands, e.g., ``add-user``. +* Better PID management when in daemon mode (don't clobber a PID file + of a running process) + +* Add options to manage the daemon process, like force + reloading/restarting (HUP?) and stopping; make init.d scripts as + minimal as possible. + paste.docsupport ---------------- @@ -178,5 +183,5 @@ No module yet, so these are listed generally. Done ==== -I just wrote this, so nothing that's shown up on this list is done -yet! +25 May 2005: + **``--daemon`` option for running as background process** diff --git a/paste/app_setup.py b/paste/app_setup.py index 6516c1c..06ef3cf 100644 --- a/paste/app_setup.py +++ b/paste/app_setup.py @@ -270,10 +270,98 @@ class CommandServe(Command): name = 'serve' summary = 'Run server' parser = standard_parser(simulate=False) + parser.add_option( + '--daemon', + help="Run in daemon (background) mode", + dest="daemon", + action="store_true") + parser.add_option( + '--user', + metavar="USER_OR_UID", + help="User to run as (only works if started as root)", + dest="user") + parser.add_option( + '--group', + metavar="GROUP_OR_GID", + help="Group to run as (only works if started as root)", + dest="group") + parser.add_option( + '--pid-file', + metavar="FILENAME", + help="Write PID to FILENAME", + dest="pid_file") + parser.add_option( + '--log-file', + metavar="FILENAME", + help="File to write stdout/stderr to (other log files may still be generated by the application)", + dest="log_file") def command(self): + conf = self.config + verbose = conf.get('verbose') or 0 + if conf.get('daemon'): + # We must enter daemon mode! + if verbose > 1: + print 'Entering daemon mode' + pid = os.fork() + if pid: + sys.exit() + # Always record PID and output when daemonized + if not conf.get('pid_file'): + conf['pid_file'] = 'server.pid' + if not conf.get('log_file'): + conf['log_file'] = 'server.log' + if conf.get('pid_file'): + # @@: We should check if the pid file exists and has + # an active process in it + if verbose > 1: + print 'Writing pid %s to %s' % (os.getpid(), conf['pid_file']) + f = open(conf['pid_file'], 'w') + f.write(str(os.getpid())) + f.close() + if conf.get('log_file'): + if verbose > 1: + print 'Logging to %s' % conf['log_file'] + f = open(conf['log_file'], 'a', 1) # 1==line buffered + sys.stdout = sys.stderr = f + f.write('-'*20 + + ' Starting server PID: %s ' % os.getpid() + + '-'*20 + '\n') + self.change_user_group(conf.get('user'), conf.get('group')) sys.exit(server.run_commandline(self.args)) + def change_user_group(self, user, group): + if not user and not group: + return + uid = gid = None + if group: + try: + gid = int(group) + except ValueError: + import grp + try: + entry = grp.getgrnam(group) + except KeyError: + raise KeyError( + "Bad group: %r; no such group exists" % group) + gid = entry[2] + try: + uid = int(user) + except ValueError: + import pwd + try: + entry = pwd.getpwnam(user) + except KeyError: + raise KeyError( + "Bad username: %r; no such user exists" % user) + if not gid: + gid = entry[3] + uid = entry[2] + if gid: + os.setgid(gid) + if uid: + os.setuid(uid) + def parse_args(self, args): # Unlike most commands, this takes arbitrary options and folds # them into the configuration @@ -287,8 +375,9 @@ class CommandServe(Command): self.list_servers(conf) sys.exit() CONFIG.push_process_config(conf) - sys.exit(server.run_server(conf, app)) self.config = conf + self.options = None + self.args = [] def help(self, config): # Here we make a fake parser just to get the help diff --git a/paste/server.py b/paste/server.py index 1f82124..2db0ed7 100755 --- a/paste/server.py +++ b/paste/server.py @@ -63,7 +63,7 @@ def load_commandline(args, allow_reload=True): conf = pyconfig.Config(with_default=True) conf.load_commandline( args, bool_options=['help', 'verbose', 'reload', 'debug', 'quiet', - 'no_verbose', 'list_servers'], + 'no_verbose', 'list_servers', 'daemon'], aliases={'h': 'help', 'v': 'verbose', 'f': 'config_file', 'D': 'debug', 'q': 'quiet'}) if conf.get('no_verbose'): @@ -198,7 +198,8 @@ def restart_with_reloader(conf): args, new_environ) if exit_code != 3: return None, exit_code - print "Exit code 3; restarting server" + if conf['verbose']: + print '-'*20, 'Restarting', '-'*20 if __name__ == '__main__': sys.exit(run_commandline(sys.argv[1:])) diff --git a/scripts/paste-setup b/scripts/paste-setup index 16eaac5..79d589b 100755 --- a/scripts/paste-setup +++ b/scripts/paste-setup @@ -13,6 +13,8 @@ relative_paste = os.path.join( if os.path.exists(relative_paste): sys.path.insert(0, os.path.dirname(relative_paste)) +import paste.app_setup +print paste.app_setup.__file__ from paste import app_setup sys.exit(app_setup.run(sys.argv)) |