Package Gnumed :: Package pycommon :: Module gmHooks
[frames] | no frames]

Source Code for Module Gnumed.pycommon.gmHooks

  1  """GNUmed hooks framework. 
  2   
  3  This module provides convenience functions and definitions 
  4  for accessing the GNUmed hooks framework. 
  5   
  6  This framework calls the script 
  7   
  8          ~/.gnumed/scripts/hook_script.py 
  9   
 10  at various times during client execution. The script must 
 11  contain a function 
 12   
 13  def run_script(hook=None): 
 14          pass 
 15   
 16  which accepts a single argument <hook>. That argument will 
 17  contain the hook that is being activated. 
 18  """ 
 19  # ======================================================================== 
 20  __author__  = "K. Hilbert <Karsten.Hilbert@gmx.net>" 
 21  __license__ = "GPL v2 or later (details at http://www.gnu.org)" 
 22   
 23  # stdlib 
 24  import os 
 25  import sys 
 26  import stat 
 27  import logging 
 28   
 29   
 30  # GNUmed libs 
 31  if __name__ == '__main__': 
 32          sys.path.insert(0, '../../') 
 33  from Gnumed.pycommon import gmDispatcher 
 34  from Gnumed.pycommon import gmTools 
 35   
 36   
 37  _log = logging.getLogger('gm.hook') 
 38  # ======================================================================== 
 39  known_hooks = [ 
 40          u'post_patient_activation', 
 41          u'post_person_creation', 
 42   
 43          u'shutdown-post-GUI', 
 44          u'startup-after-GUI-init', 
 45          u'startup-before-GUI', 
 46   
 47          u'request_user_attention', 
 48          u'app_activated_startup', 
 49          u'app_activated', 
 50          u'app_deactivated', 
 51   
 52          u'after_substance_intake_modified', 
 53          u'after_test_result_modified', 
 54          u'after_soap_modified', 
 55          u'after_code_link_modified', 
 56   
 57          u'after_new_doc_created', 
 58          u'before_print_doc', 
 59          u'before_fax_doc', 
 60          u'before_mail_doc', 
 61          u'before_print_doc_part', 
 62          u'before_fax_doc_part', 
 63          u'before_mail_doc_part', 
 64          u'before_external_doc_access', 
 65   
 66          u'db_maintenance_warning' 
 67  ] 
 68   
 69  _log.debug('known hooks:') 
 70  for hook in known_hooks: 
 71          _log.debug(hook) 
 72   
 73  # ======================================================================== 
 74  hook_module = None 
 75   
76 -def import_hook_module(reimport=False):
77 78 global hook_module 79 if not reimport: 80 if hook_module is not None: 81 return True 82 83 # hardcoding path and script name allows us to 84 # not need configuration for it, the environment 85 # can always be detected at runtime (workplace etc) 86 script_name = 'hook_script.py' 87 script_path = os.path.expanduser(os.path.join('~', '.gnumed', 'scripts')) 88 full_script = os.path.join(script_path, script_name) 89 90 if not os.access(full_script, os.F_OK): 91 _log.warning('creating default hook script') 92 f = open(full_script, 'w') 93 f.write(""" 94 # known hooks: 95 # %s 96 97 def run_script(hook=None): 98 pass 99 """ % '# '.join(known_hooks)) 100 f.close() 101 os.chmod(full_script, 384) 102 103 if os.path.islink(full_script): 104 gmDispatcher.send ( 105 signal = 'statustext', 106 msg = _('Script must not be a link: [%s].') % full_script 107 ) 108 return False 109 110 if not os.access(full_script, os.R_OK): 111 gmDispatcher.send ( 112 signal = 'statustext', 113 msg = _('Script must be readable by the calling user: [%s].') % full_script 114 ) 115 return False 116 117 script_stat_val = os.stat(full_script) 118 _log.debug('hook script stat(): %s', script_stat_val) 119 script_perms = stat.S_IMODE(script_stat_val.st_mode) 120 _log.debug('hook script mode: %s (oktal: %s)', script_perms, oct(script_perms)) 121 if script_perms != 384: # octal 0600 122 if os.name in ['nt']: 123 _log.warning('this platform does not support os.stat() file permission checking') 124 else: 125 gmDispatcher.send ( 126 signal = 'statustext', 127 msg = _('Script must be readable by the calling user only (permissions "0600"): [%s].') % full_script 128 ) 129 return False 130 131 try: 132 tmp = gmTools.import_module_from_directory(script_path, script_name) 133 except StandardError: 134 _log.exception('cannot import hook script') 135 return False 136 137 hook_module = tmp 138 # if reimport: 139 # reload(tmp) # this has well-known shortcomings ! 140 141 _log.info('hook script: %s', full_script) 142 return True
143 # ======================================================================== 144 __current_hook_stack = [] 145
146 -def run_hook_script(hook=None):
147 # NOTE: this just *might* be a huge security hole 148 149 _log.info('told to pull hook [%s]', hook) 150 151 if hook not in known_hooks: 152 raise ValueError('run_hook_script(): unknown hook [%s]' % hook) 153 154 if not import_hook_module(reimport = False): 155 _log.debug('cannot import hook module, not pulling hook') 156 return False 157 158 if hook in __current_hook_stack: 159 _log.error('hook-code cycle detected, aborting') 160 _log.error('current hook stack: %s', __current_hook_stack) 161 return False 162 163 __current_hook_stack.append(hook) 164 165 try: 166 hook_module.run_script(hook = hook) 167 except StandardError: 168 _log.exception('error running hook script for [%s]', hook) 169 gmDispatcher.send ( 170 signal = u'statustext', 171 msg = _('Error running hook [%s] script.') % hook, 172 beep = True 173 ) 174 if __current_hook_stack[-1] != hook: 175 _log.error('hook nesting errror detected') 176 _log.error('latest hook: expected [%s], found [%s]', hook, __current_hook_stack[-1]) 177 _log.error('current hook stack: %s', __current_hook_stack) 178 else: 179 __current_hook_stack.pop() 180 return False 181 182 if __current_hook_stack[-1] != hook: 183 _log.error('hook nesting errror detected') 184 _log.error('latest hook: expected [%s], found [%s]', hook, __current_hook_stack[-1]) 185 _log.error('current hook stack: %s', __current_hook_stack) 186 else: 187 __current_hook_stack.pop() 188 189 return True
190 # ======================================================================== 191 if __name__ == '__main__': 192 193 run_hook_script(hook = 'shutdown-post-GUI') 194 run_hook_script(hook = 'invalid hook') 195 196 # ======================================================================== 197