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