]> scripts.mit.edu Git - wizard.git/blob - wizard/command/__init__.py
Fix bug where php.ini not being rewritten for MediaWiki.
[wizard.git] / wizard / command / __init__.py
1 import logging
2 import traceback
3 import os
4 import sys
5 import optparse
6 import errno
7 import pwd
8 import shutil
9 import cStringIO
10
11 import wizard
12 from wizard import util, shell
13
14 def chdir_to_production():
15     if os.path.exists(".git/WIZARD_UPGRADE_VERSION"): # XXX do something more robust
16         util.chdir(shell.eval("git", "config", "remote.origin.url"))
17         return True
18     return False
19
20 logging_setup = False
21 debug = True # This will get overwritten with the real value early on
22
23 def setup_logger(options, numeric_args):
24     global logging_setup
25     if logging_setup: return logging.getLogger()
26     logger = logging.getLogger()
27     logger.handlers = [] # under certain cases, a spurious stream handler is set. We don't know why
28     logger.setLevel(logging.INFO)
29     stderr = logging.StreamHandler(sys.stderr)
30     stderr.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
31     if not options.quiet:
32         logger.addHandler(stderr)
33     else:
34         logger.addHandler(NullLogHandler()) # prevent default
35     if options.log_file:
36         setup_file_logger(options.log_file, options.debug)
37     if options.debug:
38         logger.setLevel(logging.DEBUG)
39     else:
40         stderr.setLevel(logging.WARNING)
41         if options.verbose:
42             stderr.setLevel(logging.INFO)
43     logging_setup = True
44     return logger
45
46 def setup_file_logger(log_file, debug):
47     logger = logging.getLogger()
48     file = logging.FileHandler(log_file)
49     logformatter = logging.Formatter("%(asctime)s %(levelname)s: %(message)s", "%Y-%m-%d %H:%M")
50     file.setFormatter(logformatter)
51     logger.addHandler(file)
52     if not debug:
53         file.setLevel(logging.INFO)
54     return file
55
56 def make_base_args(options, **grab):
57     """Takes parsed options, and breaks them back into a command
58     line string that we can pass into a subcommand"""
59     args = []
60     grab["debug"]   = "--debug"
61     grab["verbose"] = "--verbose"
62     grab["quiet"]   = "--quiet"
63     #grab["log_db"] = "--log-db"
64     for k,flag in grab.items():
65         value = getattr(options, k)
66         if not value: continue
67         args.append(flag)
68         if type(value) is not bool:
69             args.append(str(value))
70     return args
71
72 def security_check_homedir(location):
73     """
74     Performs a check against a directory to determine if current
75     directory's owner has a home directory that is a parent directory.
76     This protects against malicious mountpoints, and is roughly equivalent
77     to the suexec checks.
78     """
79     # XXX: this is a smidge unfriendly to systems who haven't setup
80     # nswitch.
81     try:
82         uid = util.get_dir_uid(location)
83         real = os.path.realpath(location)
84         if not real.startswith(pwd.getpwuid(uid).pw_dir + "/"):
85             logging.error("Security check failed, owner of deployment and "
86                     "owner of home directory mismatch for %s" % location)
87             return False
88     except KeyError:
89         logging.error("Security check failed, could not look up "
90                 "owner of %s (uid %d)" % (location, uid))
91         return False
92     except OSError as e:
93         logging.error("OSError: %s" % str(e))
94         return False
95     return True
96
97 def calculate_log_name(log_dir, i):
98     """
99     Calculates a log entry given a numeric identifier, and
100     directory under operation.
101     """
102     return os.path.join(log_dir, "%04d.log" % i)
103
104 def create_logdir(log_dir):
105     """
106     Creates a log directory and chmods it 777 to enable de-priviledged
107     processes to create files.
108     """
109     try:
110         os.mkdir(log_dir)
111     except OSError as e:
112         if e.errno != errno.EEXIST:
113             raise
114         #if create_subdirs:
115         #    log_dir = os.path.join(log_dir, str(int(time.time())))
116         #    os.mkdir(log_dir) # if fails, be fatal
117         #    # XXX: update last symlink
118     os.chmod(log_dir, 0o777)
119
120 class NullLogHandler(logging.Handler):
121     """Log handler that doesn't do anything"""
122     def emit(self, record):
123         pass
124
125 class WizardOptionParser(optparse.OptionParser):
126     """Configures some default user-level options"""
127     store_help = False
128     def __init__(self, *args, **kwargs):
129         kwargs["add_help_option"] = False
130         if "store_help" in kwargs:
131             self.store_help = kwargs["store_help"]
132             del kwargs["store_help"]
133         optparse.OptionParser.__init__(self, *args, **kwargs)
134     def parse_all(self, *args, **kwargs):
135         if self.store_help:
136             self.add_option("-h", "--help", action="store_true", default=False, dest="help", help=optparse.SUPPRESS_HELP)
137         else:
138             self.add_option("-h", "--help", action="help", help=optparse.SUPPRESS_HELP)
139         group = optparse.OptionGroup(self, "Common Options")
140         group.add_option("-v", "--verbose", dest="verbose", action="store_true",
141                 default=util.boolish(os.getenv("WIZARD_VERBOSE")), help="Turns on verbose output.  Envvar is WIZARD_VERBOSE")
142         group.add_option("--debug", dest="debug", action="store_true",
143                 default=util.boolish(os.getenv("WIZARD_DEBUG")), help="Turns on debugging output.  Envvar is WIZARD_DEBUG")
144         group.add_option("-q", "--quiet", dest="quiet", action="store_true",
145                 default=util.boolish(os.getenv("WIZARD_QUIET")), help="Turns off output to stdout. Envvar is WIZARD_QUIET")
146         group.add_option("--log-file", dest="log_file", metavar="FILE",
147                 default=os.getenv("WIZARD_LOGFILE"), help="Logs verbose output to file")
148         group.add_option("--directory", dest="directory", metavar="PATH",
149                 default=os.getenv("WIZARD_DIRECTORY", ".wizard"), help="Initialize this folder to store metadata.")
150         self.add_option_group(group)
151         options, numeric_args = self.parse_args(*args, **kwargs)
152         setup_logger(options, numeric_args)
153         debug = options.debug
154         # we're going to process the global --log-dir/--seen dependency here
155         if hasattr(options, "seen") and hasattr(options, "log_dir"):
156             if not options.seen and options.log_dir:
157                 options.seen = os.path.join(options.log_dir, "seen.txt")
158         return options, numeric_args
159
160 class OptionBaton(object):
161     """Command classes may define options that they sub-commands may
162     use.  Since wizard --global-command subcommand is not a supported
163     mode of operation, these options have to be passed down the command
164     chain until a option parser is ready to take it; this baton is
165     what is passed down."""
166     def __init__(self):
167         self.store = {}
168     def add(self, *args, **kwargs):
169         key = kwargs["dest"] # require this to be set
170         self.store[key] = optparse.make_option(*args, **kwargs)
171     def push(self, option_parser, *args):
172         """Hands off parameters to option parser"""
173         for key in args:
174             option_parser.add_option(self.store[key])
175
176 class Error(wizard.Error):
177     """Base error class for all command errors"""
178     pass