# $HeadURL$ $LastChangedRevision$ # -*- coding: utf-8 -*- ######################################################################## # # Modules # ######################################################################## import sys import syslog import os import re ######################################################################## # # Characteristics of this module # ######################################################################## # (not needed in python) ######################################################################## # # Public functions # ######################################################################## ######################################################################## # # Public functions: entry point related # ######################################################################## # (none) ######################################################################## # # Public functions: error stack related # ######################################################################## # (none) ######################################################################## # # Public functions: string/stream manipulation functions # ######################################################################## # (none) ######################################################################## # # Public functions: messaging functions # ######################################################################## #DIFFSYNC: internal def internal(text): _display_message("INTERNAL ERROR: %s" % (text), 0, syslog.LOG_CRIT) sys.exit(2) #DIFFSYNC: error def error(text): _display_message("ERROR: %s" % (text), 1, syslog.LOG_ERR) sys.exit(1) #DIFFSYNC: warning def warning(text): _display_message("WARNING: %s" % (text), 2, syslog.LOG_WARNING) #DIFFSYNC: info def info(text): _display_message("INFO: %s" % (text), 3, syslog.LOG_INFO) #DIFFSYNC: debug def debug(level, text): if not isinstance(level, int): internal('miniade_debug: %s: invalid level' % (level)) _display_message("DEBUG[%s]: %s" % (level, text), level, syslog.LOG_DEBUG) #DIFFSYNC: bad_usage def bad_usage(): progname = get_progname() sys.stderr.write('%s: ERROR: type \'%s --help\' for correct usage.\n' % (progname, progname)) sys.exit(1) ######################################################################## # # Public functions: filename manipulation functions # ######################################################################## # (none) ######################################################################## # # Public functions: file handle manipulation functions # ######################################################################## # (none) ######################################################################## # # Public functions: UUID-related # ######################################################################## # (none) ######################################################################## # # Public functions: file content manipulation functions # ######################################################################## # (none) ######################################################################## # # Public functions: option processing # ######################################################################## #DIFFSYNC: process_options def process_options(special_options_handler=None, help_handler=None): global _verboselevel, _simulate # Defaults for options if help_handler is None: help_handler = _help_fallback version_handler = _version_fallback list_paths_handler = _paths_fallback if special_options_handler is None: special_options_handler = _special_options_fallback standard_options_enabled = True unknown_options_enabled = False # Process options (to the process_options() call, not # to the program itself). # (not applicable to function with parameters) # Process arguments # (no arguments 'cos python function can manipulate sys.argv, unlike in bash) # Sanity checks and derivations # Guts # Process each option on the *calling script's* command line. while True: if len(sys.argv) == 1: break # Standard options if standard_options_enabled and re.search('^--debug=(?:0|[1-9][0-9]*)$', sys.argv[1]): _verboselevel = int(sys.argv[1][len('--debug='):]) elif standard_options_enabled and sys.argv[1] == '-d' and len(sys.argv) >= 3 and re.search('^(?:0|[1-9][0-9]*)$', sys.argv[2]): _verboselevel = int(sys.argv[2]) del sys.argv[1] elif standard_options_enabled and sys.argv[1] in ['-n','--simulate']: _simulate = True elif sys.argv[1] == '--simulate=true': _simulate = True elif sys.argv[1] == '--simulate=false': _simulate = False elif standard_options_enabled and sys.argv[1] in ['-v','--verbose']: _verboselevel = 3 elif standard_options_enabled and sys.argv[1] in ['-V','--version']: version_handler() elif standard_options_enabled and sys.argv[1] in ['-p','--paths']: list_paths_handler() elif sys.argv[1] in ['-h','--help']: help_handler() elif sys.argv[1] == '--': del sys.argv[1] break # '-' may be an *argument* indicating to use stdin. elif sys.argv[1] == '-': break # For all non-standard options ... elif sys.argv[1].startswith('-'): # ... if special options handler also didn't recognise it ... if not special_options_handler(): # ... if we allow such options (typically when delegated to sub-main-like # function) then just break out of loop, without shifting off ... if unknown_options_enabled: break # ... otherwise error ... else: bad_usage() # ... and if the special options handler *did* recognise it then # no action needed (the special option handler will have done # any extra shifts itself). # For all non-option-like things just break out of loop so as to # pass them on to *argument* processing. else: break # Shift off the option itself. del sys.argv[1] return 0 ######################################################################## # # Public functions: special functions # ######################################################################## # (none) ######################################################################## # # Public functions: user related # ######################################################################## # (none) ######################################################################## # # Public functions: process management functions # ######################################################################## #DIFFSYNC: evaler def evaler(): internal('evaler: not implemented') ######################################################################## # # Public functions: temporary file related functions # ######################################################################## # (none) ######################################################################## # # Public functions: database related functions # ######################################################################## # (none) ######################################################################## # # Public functions: variable related functions # ######################################################################## # (none) ######################################################################## # # Public functions: locking related functions # ######################################################################## #DIFFSYNC: lock def lock(lock_file): tmp_lock_file = '%s.%d' % (lock_file, os.getpid()) # Create a uniquely named temporary lock file debug(10, 'lock: creating temporary lock file ...') fp = open(tmp_lock_file, 'w') fp.write('%d\n' % os.getpid()) fp.close() # If can easily lock, return debug(10, 'lock: sliding temporary lock into place ...') try: os.link(tmp_lock_file, lock_file) os.unlink(tmp_lock_file) return True except: pass # If lock is fresh, return debug(10, 'lock: presumable lock file exists; checking if fresh ...') if lock_is_fresh(lock_file): os.unlink(tmp_lock_file) return False # Remove empty or stale lock file warning('%s: empty or stale; removing ...' % (lock_file)) os.unlink(lock_file) # If can easily lock, return debug(10, 'lock: again trying sliding temporary lock into place ...') try: os.link(tmp_lock_file, lock_file) os.unlink(tmp_lock_file) return True except: pass # If we get this far then something when wrong internal('lock: can\'t lock (hint: try manually running: ln %s %s)' % (tmp_lock_file, lock_file)) #DIFFSYNC: unlock def unlock(lock_file): os.unlink(lock_file) #DIFFSYNC: lock_is_fresh def lock_is_fresh(lock_file): # If lock file not empty and not stale; return if not os.path.isfile(lock_file): debug(10, 'lock_is_fresh: not a file; returning False ...') return False try: fp = open(lock_file) pid = int(fp.readline()) fp.close() except: internal('lock_is_fresh: failed to get PID from lockfile') if not os.path.isdir('/proc/%d' % (pid)): return False # If got to here then lock is fresh. return True ######################################################################## # # Public functions: directory content management functions # ######################################################################## # (none) ######################################################################## # # Public functions: miscellaneous # ######################################################################## #DIFFSYNC: check_ssh_ok def check_ssh_ok(): internal('check_ssh_ok: not implemented') #DIFFSYNC: validate_command def validate_command(): internal('validate_command: not implemented') ######################################################################## # # Public functions: access module-private variable # ######################################################################## #DIFFSYNC: get_verboselevel def get_verboselevel(): global _verboselevel return _verboselevel #DIFFSYNC: set_verboselevel def set_verboselevel(verboselevel): global _verboselevel _verboselevel = verboselevel #DIFFSYNC: get_simulate def get_simulate(): global _simulate return _simulate #DIFFSYNC: set_simulate def set_simulate(simulate): global _simulate _simulate = simulate #DIFFSYNC: get_progname def get_progname(): global _progname return _progname #DIFFSYNC: set_progname def set_progname(progname): global _progname _progname = progname ######################################################################## # # Module-private functions # ######################################################################## #DIFFSYNC: _display_message def _display_message(text, level, syslog_level): global _display_callback_refs for writerfunc_ref in _display_callback_refs: writerfunc_ref(text, level, syslog_level) #DIFFSYNC: _display_message_stderr def _display_message_stderr(text, level, syslog_level): global _progname, _verboselevel if _verboselevel < level: return if sys.stderr.isatty(): sys.stderr.write('%s: %s\n' % (_progname, text)) #DIFFSYNC: _display_message_syslog def _display_message_syslog(text, level, syslog_level): global _syslog_opened_flag, _progname, _verboselevel if _verboselevel < level: return if not _syslog_opened_flag: syslog.openlog(_progname, logoption=syslog.LOG_PID) _syslog_opened_flag = True syslog.syslog((syslog.LOG_LOCAL0|syslog_level), text) #DIFFSYNC: _display_message_log_file def _display_message_log_file(text, level, syslog_level): internal('_display_message_log_file: not implemented') #DIFFSYNC: _display_message_dev_null def _display_message_dev_null(text, level, syslog_level): internal('_display_message_dev_null: not implemented') #DIFFSYNC: _replace_function def _replace_function(old_fncname, new_fncname): code = ''' def %s(*other_args): warning('%s() is obsolete; switch to %s()') return %s(*other_args) ''' % (old_fncname, old_fncname, new_fncname, new_fncname) exec(code, globals()) #DIFFSYNC: _help_fallback def _help_fallback(): progname = get_progname() sys.stdout.write('Usage: %s [ ]\n' % (progname)) sys.exit(0) #DIFFSYNC: _version_fallback def _version_fallback(): progname = get_progname() sys.stdout.write('%s version 0\n' % (progname)) sys.exit(0) #DIFFSYNC: _paths_fallback def _paths_fallback(): sys.exit(0) #DIFFSYNC: _special_options_fallback def _special_options_fallback(): return False #DIFFSYNC: _initialise def _initialise(): _replace_function('process_standard_options', 'process_options') pass ######################################################################## # # Public variables # ######################################################################## # (none - and keep it that way!) ######################################################################## # # Module-private variables with public access functions # ######################################################################## _progname = sys.argv[0].rpartition("/")[2] _verboselevel = 2 _simulate = False _display_callback_refs = [ _display_message_stderr ] ######################################################################## # # Other module-private variables # ######################################################################## _syslog_opened_flag = False ######################################################################## # # Actual code # ######################################################################## _initialise()