1
2
3 __doc__ = """GNUmed client launcher.
4
5 This is the launcher for the GNUmed GUI client. It takes
6 care of all the pre- and post-GUI runtime environment setup.
7
8 --quiet
9 Be extra quiet and show only _real_ errors in the log.
10 --debug
11 Pre-set the [debug mode] checkbox in the login dialog to
12 increase verbosity in the log file. Useful for, well, debugging :-)
13 --slave
14 Pre-set the [enable remote control] checkbox in the login
15 dialog to enable the XML-RPC remote control feature.
16 --hipaa
17 Enable HIPAA functionality which has user impact.
18 --profile=<file>
19 Activate profiling and write profile data to <file>.
20 --text-domain=<text domain>
21 Set this to change the name of the language file to be loaded.
22 Note, this does not change the directory the file is searched in,
23 only the name of the file where messages are loaded from. The
24 standard textdomain is, of course, "gnumed.mo".
25 --log-file=<file>
26 Use this to change the name of the log file.
27 See gmLog2.py to find out where the standard log file would
28 end up.
29 --conf-file=<file>
30 Use configuration file <file> instead of searching for it in
31 standard locations.
32 --lang-gettext=<language>
33 Explicitly set the language to use in gettext translation. The very
34 same effect can be achieved by setting the environment variable $LANG
35 from a launcher script.
36 --override-schema-check
37 Continue loading the client even if the database schema version
38 and the client software version cannot be verified to be compatible.
39 --skip-update-check
40 Skip checking for client updates. This is useful during development
41 and when the update check URL is unavailable (down).
42 --local-import
43 Adjust the PYTHONPATH such that GNUmed can be run from a local source tree.
44 --ui=<ui type>
45 Start an alternative UI. Defaults to wxPython if not specified.
46 Valid values: chweb (CherryPy), wxp (wxPython), web (ProxiedWeb)
47 --version, -V
48 Show version information.
49 --help, -h, or -?
50 Show this help.
51 """
52
53 __version__ = "$Revision: 1.169 $"
54 __author__ = "H. Herb <hherb@gnumed.net>, K. Hilbert <Karsten.Hilbert@gmx.net>, I. Haywood <i.haywood@ugrad.unimelb.edu.au>"
55 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
56
57
58 import sys
59 import os
60 import platform
61 import logging
62 import signal
63 import os.path
64 import shutil
65 import stat
66
67
68
69 if __name__ != "__main__":
70 print "GNUmed startup: This is not intended to be imported as a module !"
71 print "-----------------------------------------------------------------"
72 print __doc__
73 sys.exit(1)
74
75
76
77 if os.name in ['posix'] and os.geteuid() == 0:
78 print """
79 GNUmed startup: GNUmed should not be run as root.
80 -------------------------------------------------
81
82 Running GNUmed as <root> can potentially put all
83 your medical data at risk. It is strongly advised
84 against. Please run GNUmed as a non-root user.
85 """
86 sys.exit(1)
87
88
89
90 current_client_version = u'GIT HEAD'
91
92 current_client_branch = u'GIT tree'
93
94 _log = None
95 _cfg = None
96 _old_sig_term = None
97 _known_short_options = u'h?V'
98 _known_long_options = [
99 u'debug',
100 u'slave',
101 u'skip-update-check',
102 u'profile=',
103 u'text-domain=',
104 u'log-file=',
105 u'conf-file=',
106 u'lang-gettext=',
107 u'ui=',
108 u'override-schema-check',
109 u'local-import',
110 u'help',
111 u'version',
112 u'hipaa'
113 ]
114
115 _known_ui_types = [
116 u'web',
117 u'wxp',
118 u'chweb'
119 ]
120
121 import_error_sermon = """
122 GNUmed startup: Cannot load GNUmed Python modules !
123 ---------------------------------------------------
124 CRITICAL ERROR: Program halted.
125
126 Please make sure you have:
127
128 1) the required third-party Python modules installed
129 2) the GNUmed Python modules linked or installed into site-packages/
130 (if you do not run from a CVS tree the installer should have taken care of that)
131 3) your PYTHONPATH environment variable set up correctly
132
133 sys.path is currently set to:
134
135 %s
136
137 If you are running from a copy of the CVS tree make sure you
138 did run gnumed/check-prerequisites.sh with good results.
139
140 If you still encounter errors after checking the above
141 requirements please ask on the mailing list.
142 """
143
144
145 missing_cli_config_file = u"""
146 GNUmed startup: Missing configuration file.
147 -------------------------------------------
148
149 You explicitly specified a configuration file
150 on the command line:
151
152 --conf-file=%s
153
154 The file does not exist, however.
155 """
156
157
158 no_config_files = u"""
159 GNUmed startup: Missing configuration files.
160 --------------------------------------------
161
162 None of the below candidate configuration
163 files could be found:
164
165 %s
166
167 Cannot run GNUmed without any of them.
168 """
169
170
171
173
174 if not u'--local-import' in sys.argv:
175 return
176
177 print "Running from local source tree ..."
178
179 local_python_base_dir = os.path.dirname (
180 os.path.abspath(os.path.join(sys.argv[0], '..'))
181 )
182
183
184
185 link_name = os.path.join(local_python_base_dir, 'Gnumed')
186 if not os.path.exists(link_name):
187 real_dir = os.path.join(local_python_base_dir, 'client')
188 print "Creating module import symlink ..."
189 print ' real dir:', real_dir
190 print ' link:', link_name
191 os.symlink(real_dir, link_name)
192
193 print "Adjusting PYTHONPATH ..."
194 sys.path.insert(0, local_python_base_dir)
195
197
198 local_repo_path = os.path.expanduser(os.path.join (
199 '~',
200 '.gnumed',
201 'local_code',
202 str(current_client_branch)
203 ))
204 local_wxGladeWidgets_path = os.path.join(local_repo_path, 'Gnumed', 'wxGladeWidgets')
205
206 if not os.path.exists(local_wxGladeWidgets_path):
207 _log.debug('[%s] not found', local_wxGladeWidgets_path)
208 _log.info('local wxGlade widgets repository not available')
209 return
210
211 _log.info('local wxGlade widgets repository found:')
212 _log.info(local_wxGladeWidgets_path)
213
214 if not os.access(local_wxGladeWidgets_path, os.R_OK):
215 _log.error('invalid repo: no read access')
216 return
217
218 all_entries = os.listdir(os.path.join(local_repo_path, 'Gnumed'))
219 _log.debug('repo base contains: %s', all_entries)
220 all_entries.remove('wxGladeWidgets')
221 try:
222 all_entries.remove('__init__.py')
223 except ValueError:
224 _log.error('invalid repo: lacking __init__.py')
225 return
226 try:
227 all_entries.remove('__init__.pyc')
228 except ValueError:
229 pass
230
231 if len(all_entries) > 0:
232 _log.error('insecure repo: additional files or directories found')
233 return
234
235
236 stat_val = os.stat(local_wxGladeWidgets_path)
237 _log.debug('repo stat(): %s', stat_val)
238 perms = stat.S_IMODE(stat_val.st_mode)
239 _log.debug('repo permissions: %s (octal: %s)', perms, oct(perms))
240 if perms != 448:
241 if os.name in ['nt']:
242 _log.warning('this platform does not support os.stat() permission checking')
243 else:
244 _log.error('insecure repo: permissions not 0600')
245 return
246
247 print "Activating local wxGlade widgets repository ..."
248 sys.path.insert(0, local_repo_path)
249 _log.debug('sys.path with repo:')
250 _log.debug(sys.path)
251
263
274
279
281 from Gnumed.pycommon import gmCfg2
282
283 global _cfg
284 _cfg = gmCfg2.gmCfgData()
285 _cfg.add_cli (
286 short_options = _known_short_options,
287 long_options = _known_long_options
288 )
289
290 val = _cfg.get(option = '--debug', source_order = [('cli', 'return')])
291 if val is None:
292 val = False
293 _cfg.set_option (
294 option = u'debug',
295 value = val
296 )
297
298 val = _cfg.get(option = '--slave', source_order = [('cli', 'return')])
299 if val is None:
300 val = False
301 _cfg.set_option (
302 option = u'slave',
303 value = val
304 )
305
306 val = _cfg.get(option = '--skip-update-check', source_order = [('cli', 'return')])
307 if val is None:
308 val = False
309 _cfg.set_option (
310 option = u'skip-update-check',
311 value = val
312 )
313
314 val = _cfg.get(option = '--hipaa', source_order = [('cli', 'return')])
315 if val is None:
316 val = False
317 _cfg.set_option (
318 option = u'hipaa',
319 value = val
320 )
321
322 val = _cfg.get(option = '--local-import', source_order = [('cli', 'return')])
323 if val is None:
324 val = False
325 _cfg.set_option (
326 option = u'local-import',
327 value = val
328 )
329
330 _cfg.set_option (
331 option = u'client_version',
332 value = current_client_version
333 )
334
335 _cfg.set_option (
336 option = u'client_branch',
337 value = current_client_branch
338 )
339
340
342 _log.critical('SIGTERM (SIG%s) received, shutting down ...' % signum)
343 gmLog2.flush()
344 print 'GNUmed: SIGTERM (SIG%s) received, shutting down ...' % signum
345 if frame is not None:
346 print '%s::%s@%s' % (frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno)
347
348
349
350 if _old_sig_term in [None, signal.SIG_IGN]:
351 sys.exit(signal.SIGTERM)
352 else:
353 _old_sig_term(signum, frame)
354
358
369
371 src = [(u'cli', u'return')]
372
373 help_requested = (
374 _cfg.get(option = u'--help', source_order = src) or
375 _cfg.get(option = u'-h', source_order = src) or
376 _cfg.get(option = u'-?', source_order = src)
377 )
378
379 if help_requested:
380 print _(
381 'Help requested\n'
382 '--------------'
383 )
384 print __doc__
385 sys.exit(0)
386
405
406
408 """Create needed paths in user home directory."""
409
410 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'scripts')))
411 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'spellcheck')))
412 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'tmp')))
413 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'docs')))
414 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT')))
415 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR')))
416 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'xDT')))
417 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'logs')))
418
419 paths = gmTools.gmPaths(app_name = u'gnumed')
420
421 open(os.path.expanduser(os.path.join('~', '.gnumed', 'gnumed.conf')), 'a+').close()
422
425
427 """Detect and setup access to GNUmed config file.
428
429 Parts of this will have limited value due to
430 wxPython not yet being available.
431 """
432
433 enc = gmI18N.get_encoding()
434 paths = gmTools.gmPaths(app_name = u'gnumed')
435
436 candidates = [
437
438 [u'workbase', os.path.join(paths.working_dir, 'gnumed.conf')],
439
440 [u'system', os.path.join(paths.system_config_dir, 'gnumed-client.conf')],
441
442 [u'user', os.path.join(paths.user_config_dir, 'gnumed.conf')],
443
444 [u'local', os.path.join(paths.local_base_dir, 'gnumed.conf')]
445 ]
446
447 explicit_fname = _cfg.get(option = u'--conf-file', source_order = [(u'cli', u'return')])
448 if explicit_fname is None:
449 candidates.append([u'explicit', None])
450 else:
451 candidates.append([u'explicit', explicit_fname])
452
453 for candidate in candidates:
454 _cfg.add_file_source (
455 source = candidate[0],
456 file = candidate[1],
457 encoding = enc
458 )
459
460
461 if explicit_fname is not None:
462 if _cfg.source_files['explicit'] is None:
463 _log.error('--conf-file argument does not exist')
464 sys.exit(missing_cli_config_file % explicit_fname)
465
466
467 found_any_file = False
468 for f in _cfg.source_files.values():
469 if f is not None:
470 found_any_file = True
471 break
472 if not found_any_file:
473 _log.error('no config file found at all')
474 sys.exit(no_config_files % '\n '.join(candidates))
475
476
477 fname = u'mime_type2file_extension.conf'
478 _cfg.add_file_source (
479 source = u'user-mime',
480 file = os.path.join(paths.user_config_dir, fname),
481 encoding = enc
482 )
483 _cfg.add_file_source (
484 source = u'system-mime',
485 file = os.path.join(paths.system_config_dir, fname),
486 encoding = enc
487 )
488
490 global ui_type
491
492 ui_type = _cfg.get(option = u'--ui', source_order = [(u'cli', u'return')])
493
494 if ui_type in [True, False, None]:
495 ui_type = 'wxp'
496
497 ui_type = ui_type.strip()
498
499 if ui_type not in _known_ui_types:
500 _log.error('unknown UI type: %s', ui_type)
501 _log.debug('known UI types: %s', str(_known_ui_types))
502 print "GNUmed startup: Unknown UI type (%s). Defaulting to wxPython client." % ui_type
503 ui_type = 'wxp'
504
505 _log.debug('UI type: %s', ui_type)
506
508
509 db_version = gmPG2.map_client_branch2required_db_version[current_client_branch]
510 _log.info('client expects database version [%s]', db_version)
511 _cfg.set_option (
512 option = u'database_version',
513 value = db_version
514 )
515
516
517 timezone = _cfg.get (
518 group = u'backend',
519 option = 'client timezone',
520 source_order = [
521 ('explicit', 'return'),
522 ('workbase', 'return'),
523 ('local', 'return'),
524 ('user', 'return'),
525 ('system', 'return')
526 ]
527 )
528 if timezone is not None:
529 gmPG2.set_default_client_timezone(timezone)
530
533
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560 logging.raiseExceptions = False
561
572
573
574
575 setup_python_path()
576 setup_logging()
577 log_startup_info()
578 setup_console_exception_handler()
579 setup_cli()
580 setup_signal_handlers()
581 setup_local_repo_path()
582
583 from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks
584 setup_locale()
585 handle_help_request()
586 handle_version_request()
587 setup_paths_and_files()
588 setup_date_time()
589 setup_cfg()
590 setup_ui_type()
591
592 from Gnumed.pycommon import gmPG2
593 if ui_type in [u'web']:
594 gmPG2.auto_request_login_params = False
595 setup_backend()
596
597
598 gmHooks.run_hook_script(hook = u'startup-before-GUI')
599
600 if ui_type == u'wxp':
601 from Gnumed.wxpython import gmGuiMain
602 profile_file = _cfg.get(option = u'--profile', source_order = [(u'cli', u'return')])
603 if profile_file is not None:
604 _log.info('writing profiling data into %s', profile_file)
605 import profile
606 profile.run('gmGuiMain.main()', profile_file)
607 else:
608 gmGuiMain.main()
609 elif ui_type == u'web':
610 from Gnumed.proxiedpyjamas import gmWebGuiServer
611 gmWebGuiServer.main()
612
613 elif ui_type == u'chweb':
614 from Gnumed.CherryPy import gmGuiWeb
615 gmGuiWeb.main()
616
617 gmHooks.run_hook_script(hook = u'shutdown-post-GUI')
618
619 shutdown_backend()
620 shutdown_tmp_dir()
621 _log.info('Normally shutting down as main module.')
622 shutdown_logging()
623
624
625