# Copyright 1999-2017. Parallels IP Holdings GmbH. All Rights Reserved. from optparse import OptionParser, OptionError import subprocess import sys import os.path import pwd import re import logging as log from plesk_exec import drop_privileges DEFAULT_LANG_CONFIG = "/etc/default/locale" def prepare_default_env(user): # man 5 crontab: # Several environment variables are set up automatically by the cron(8) daemon. # SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd line of the crontab's owner. # PATH is set to "/usr/bin:/bin". env = dict() env["SHELL"] = '/bin/sh' env["PATH"] = '/usr/bin:/bin' env["LOGNAME"] = user env["HOME"] = pwd.getpwnam(user).pw_dir env["PWD"] = env["HOME"] return env def set_lang_env(env): if not DEFAULT_LANG_CONFIG: return env try: # if default locale is not configured then corresponding variables are not set. with open(DEFAULT_LANG_CONFIG) as f: for line in f: m = re.match(r'^(LANG|LANGUAGE)\s*=\s*"?([^\"\s]+)"?$', line) if m: env[m.group(1)] = m.group(2) except IOError: pass return env def set_user_specified_env(env, override_vars): # man 5 crontab: # HOME, SHELL, and PATH may be overridden by settings in the crontab; # LOGNAME is the user that the job is running from, and may not be changed. unchangable = ["LOGNAME"] if override_vars is not None: for pair in override_vars: (key, value) = pair.split("=", 1) if key not in unchangable: env[key] = value return env def split_command(line): # man 5 crontab: # Percent-signs (%) in the command, unless escaped with backslash (\), will be changed into newline characters, and all data # after the first % will be sent to the command as standard input. lines = [x.replace('\%', '%') for x in re.split(r'(? 1: log_level = log.DEBUG elif verbosity == 1: log_level = log.INFO log.basicConfig( format="%(asctime)s %(levelname)s: %(message)s", level=log_level, datefmt="%Y-%m-%d %H:%M:%S", ) def main(): try: parser = OptionParser() parser.description = "Execute COMMAND in cron-like environment of specified USER." parser.add_option("-u", "--user", action="store", type="string", help="run by specified USER", metavar="USER") parser.add_option("-c", "--command", action="store", type="string", help="specify COMMAND for execution", metavar="COMMAND") parser.add_option("-e", "--env", action="append", type="string", dest="override_env", help="additional environment variables", metavar="KEY=VALUE") parser.add_option("-v", "--verbose", action="count", help="increase verbosity") (options, args) = parser.parse_args() configure_logging(options.verbose) if options.user is None: parser.print_help() raise OptionError("USER is not specified", "--user") if options.command is None: parser.print_help() raise OptionError("COMMAND is not specified", "--command") cron_env = prepare_default_env(options.user) cron_env = set_lang_env(cron_env) cron_env = set_user_specified_env(cron_env, options.override_env) drop_privileges(options.user) sys.exit(run_command(cron_env, options.command)) except Exception, e: log.error(e) log.debug(e, exc_info=True) sys.exit(1) if __name__ == "__main__": main() # vim: ft=python ts=4 sts=4 sw=4 et :