\!/ KyuuKazami \!/

Path : /proc/self/root/proc/thread-self/root/opt/aws/bin/
Upload :
Current File : //proc/self/root/proc/thread-self/root/opt/aws/bin/cfn-hup

#!/usr/bin/python2.7

#==============================================================================
# Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#==============================================================================
import signal
import cfnbootstrap
from cfnbootstrap import update_hooks, util
from optparse import OptionParser
import ConfigParser
import logging
import os
import threading
import datetime
import sys
import random
try:
    import simplejson as json
except ImportError:
    import json

# throws: ValueError, ConfigParser.NoOptionError
def get_umask_from_conf(config):
        octal_as_string = config.get('main', 'umask')
        # interpret the number as octal even if it's not specified as octal (just like the Linux umask command)
        safe_octal_as_string = '0' + octal_as_string
        ret_val = int(safe_octal_as_string, 8)
        if (ret_val > 0777) or (ret_val < 0):
            raise ValueError("Octal number for umask out of range: %s" % oct(ret_val))
        return ret_val

if os.name == 'nt':
    default_confdir = os.path.expandvars('${SystemDrive}\cfn')
else:
    default_confdir = '/etc/cfn'

parser = OptionParser()
parser.add_option("-c", "--config", help="The configuration directory (default: %s)" % default_confdir,
                  type="string", dest="config_path", default=default_confdir)
parser.add_option("", "--no-daemon", help="Do not daemonize",
                  dest="no_daemon", action="store_true")

parser.add_option("-v", "--verbose", help="Enables verbose logging",
                  action="store_true", dest="verbose")

(options, args) = parser.parse_args()

fatal_event = threading.Event()

# with configureLogging in this place (that is: outside main) cfn-hup.log won't get the benefit of umask setting, but
# we take additional care of disabling group and world writable permissions at the creation place
cfnbootstrap.configureLogging("DEBUG", filename='cfn-hup.log')

def parse_config_file():
    if not options.config_path:
        logging.error("Error: A configuration path must be specified")
        parser.print_help(sys.stderr)
        sys.exit(1)

    if not os.path.isdir(options.config_path):
        logging.error("Error: Could not find configuration at %s", options.config_path)
        sys.exit(1)

    try:
        return update_hooks.parse_config(options.config_path)
    except ValueError, e:
        logging.error("Error: %s", str(e))
        sys.exit(1)


def main(main_config, processor, cmd_processor):
    if options.no_daemon:
        if processor:
            processor.process()
        if cmd_processor:
            cmd_processor.register()
            cmd_processor.process()
    else:
        interval = 15
        if main_config.has_option('main', 'interval'):
            interval = main_config.getint('main', 'interval')
            if interval < 1:
                logging.error("Invalid interval (must be 1 minute or greater): %s", interval)
                sys.exit(1)

        timers = [None, None]
        timer_lock = threading.Lock()
        def do_process(last_log=datetime.datetime.utcnow()):
            if fatal_event.isSet():
                return

            if datetime.datetime.utcnow() - last_log > datetime.timedelta(minutes=5):
                last_log = datetime.datetime.utcnow()
                logging.info("cfn-hup processing is alive.")

            try:
                processor.process()
            except update_hooks.FatalUpdateError, e:
                logging.exception("Fatal exception")
                fatal_event.set()
            except Exception, e:
                logging.exception("Unhandled exception")

            with timer_lock:
                if fatal_event.isSet():
                    timers[0] = None
                    return
                last_timer = threading.Timer(interval * 60 + random.randint(-30, 30), do_process, (), {'last_log' : last_log})
                last_timer.start()
                timers[0] = last_timer

        def do_cmd_process(last_log=datetime.datetime.utcnow()):
            if fatal_event.isSet():
                return

            if datetime.datetime.utcnow() - last_log > datetime.timedelta(minutes=5):
                last_log = datetime.datetime.utcnow()
                logging.info("command processing is alive.")

            delay = 0
            try:
                if not cmd_processor.is_registered():
                    cmd_processor.register()
                if cmd_processor.creds_expired():
                    logging.error("Expired credentials found; skipping process")
                    delay = 20
                else:
                    cmd_processor.process()
            except update_hooks.FatalUpdateError, e:
                logging.exception("Fatal exception")
                fatal_event.set()
            except Exception, e:
                logging.exception("Unhandled exception")

            with timer_lock:
                if fatal_event.isSet():
                    timers[1] = None
                    return
                if delay > 0:
                    last_cmd_timer = threading.Timer(delay, do_cmd_process, (), {'last_log' : last_log})
                    last_cmd_timer.start()
                    timers[1] = last_cmd_timer
                else:
                    threading.Thread(target=do_cmd_process, args=(), kwargs={'last_log' : last_log}).start()
                    timers[1] = None

        if processor:
            do_process()
        if cmd_processor:
            do_cmd_process()

        while not fatal_event.isSet():
            fatal_event.wait(1)  # Allow signals

        with timer_lock:
            if timers[0] is not None:
                timers[0].cancel()

            if timers[1] is not None:
                timers[1].cancel()

        sys.exit(0)

main_config, processor, cmd_processor = parse_config_file()

if options.no_daemon:
    verbose = options.verbose or main_config.has_option('main', 'verbose') and main_config.getboolean('main', 'verbose')
    cfnbootstrap.configureLogging("DEBUG" if verbose else "INFO", filename='cfn-hup.log')
    main(main_config, processor, cmd_processor)
elif os.name == 'nt':
    print >> sys.stderr, "Error: cfn-hup cannot be run directly in daemon mode on Windows"
    sys.exit(1)
else:
    try:
        import daemon
    except ImportError:
        print >> sys.stderr, "Daemon library was not installed; please install python-daemon"
        sys.exit(1)

    try:
        from daemon import pidlockfile
    except ImportError:
        from daemon import pidfile as pidlockfile

    def kill(signal_num, stack_frame):
        logging.info("Shutting down cfn-hup")
        fatal_event.set()

    # Default umask value (mask out world and group writable)
    umask_parameter = 0022
    try:
        umask_parameter = get_umask_from_conf(main_config)
        logging.info("Setting umask value to: %s", oct(umask_parameter))
    except ValueError, e:
        logging.error("Invalid octal value specified for umask in config file: %s", str(e))
        sys.exit(1)
    except ConfigParser.NoOptionError:
        logging.info("No umask value specified in config file. Using the default one: %s", oct(umask_parameter))

    # Close root logger before starting up daemon to avoid collision
    logging.getLogger().handlers[0].close()

    with daemon.DaemonContext(pidfile=pidlockfile.TimeoutPIDLockFile('/var/run/cfn-hup.pid', 300),
                              signal_map={signal.SIGTERM : kill}, umask=umask_parameter):
        try:
            verbose = options.verbose or main_config.has_option('main', 'verbose') and main_config.getboolean('main', 'verbose')
            cfnbootstrap.configureLogging("DEBUG" if verbose else "INFO", filename='cfn-hup.log')
            main(main_config, processor, cmd_processor)
        except Exception, e:
            logging.exception("Unhandled exception: %s", str(e))
            sys.exit(1)

@KyuuKazami