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
265 _log.info(u'Starting up as main module (%s).', __version__)
266 _log.info(u'GNUmed client version [%s] on branch [%s]', current_client_version, current_client_branch)
267 _log.info(u'Platform: %s', platform.uname())
268 _log.info(u'Python %s on %s (%s)', sys.version, sys.platform, os.name)
269 try:
270 import lsb_release
271 _log.info(u'%s' % lsb_release.get_distro_information())
272 except ImportError:
273 pass
274 _log.info('process environment:')
275 for key, val in os.environ.items():
276 _log.info(u' %s: %s', (u'${%s}' % key).rjust(30), val)
277
282
284 from Gnumed.pycommon import gmCfg2
285
286 global _cfg
287 _cfg = gmCfg2.gmCfgData()
288 _cfg.add_cli (
289 short_options = _known_short_options,
290 long_options = _known_long_options
291 )
292
293 val = _cfg.get(option = '--debug', source_order = [('cli', 'return')])
294 if val is None:
295 val = False
296 _cfg.set_option (
297 option = u'debug',
298 value = val
299 )
300
301 val = _cfg.get(option = '--slave', source_order = [('cli', 'return')])
302 if val is None:
303 val = False
304 _cfg.set_option (
305 option = u'slave',
306 value = val
307 )
308
309 val = _cfg.get(option = '--skip-update-check', source_order = [('cli', 'return')])
310 if val is None:
311 val = False
312 _cfg.set_option (
313 option = u'skip-update-check',
314 value = val
315 )
316
317 val = _cfg.get(option = '--hipaa', source_order = [('cli', 'return')])
318 if val is None:
319 val = False
320 _cfg.set_option (
321 option = u'hipaa',
322 value = val
323 )
324
325 val = _cfg.get(option = '--local-import', source_order = [('cli', 'return')])
326 if val is None:
327 val = False
328 _cfg.set_option (
329 option = u'local-import',
330 value = val
331 )
332
333 _cfg.set_option (
334 option = u'client_version',
335 value = current_client_version
336 )
337
338 _cfg.set_option (
339 option = u'client_branch',
340 value = current_client_branch
341 )
342
343
345 _log.critical('SIGTERM (SIG%s) received, shutting down ...' % signum)
346 gmLog2.flush()
347 print 'GNUmed: SIGTERM (SIG%s) received, shutting down ...' % signum
348 if frame is not None:
349 print '%s::%s@%s' % (frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno)
350
351
352
353 if _old_sig_term in [None, signal.SIG_IGN]:
354 sys.exit(signal.SIGTERM)
355 else:
356 _old_sig_term(signum, frame)
357
361
372
374 src = [(u'cli', u'return')]
375
376 help_requested = (
377 _cfg.get(option = u'--help', source_order = src) or
378 _cfg.get(option = u'-h', source_order = src) or
379 _cfg.get(option = u'-?', source_order = src)
380 )
381
382 if help_requested:
383 print _(
384 'Help requested\n'
385 '--------------'
386 )
387 print __doc__
388 sys.exit(0)
389
408
409
411 """Create needed paths in user home directory."""
412
413 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'scripts')))
414 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'spellcheck')))
415 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'docs')))
416 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT')))
417 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR')))
418 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'xDT')))
419 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'logs')))
420
421 paths = gmTools.gmPaths(app_name = u'gnumed')
422
423 open(os.path.expanduser(os.path.join('~', '.gnumed', 'gnumed.conf')), 'a+').close()
424
427
429 """Detect and setup access to GNUmed config file.
430
431 Parts of this will have limited value due to
432 wxPython not yet being available.
433 """
434
435 enc = gmI18N.get_encoding()
436 paths = gmTools.gmPaths(app_name = u'gnumed')
437
438 candidates = [
439
440 [u'workbase', os.path.join(paths.working_dir, 'gnumed.conf')],
441
442 [u'system', os.path.join(paths.system_config_dir, 'gnumed-client.conf')],
443
444 [u'user', os.path.join(paths.user_config_dir, 'gnumed.conf')],
445
446 [u'local', os.path.join(paths.local_base_dir, 'gnumed.conf')]
447 ]
448
449 explicit_fname = _cfg.get(option = u'--conf-file', source_order = [(u'cli', u'return')])
450 if explicit_fname is None:
451 candidates.append([u'explicit', None])
452 else:
453 candidates.append([u'explicit', explicit_fname])
454
455 for candidate in candidates:
456 _cfg.add_file_source (
457 source = candidate[0],
458 file = candidate[1],
459 encoding = enc
460 )
461
462
463 if explicit_fname is not None:
464 if _cfg.source_files['explicit'] is None:
465 _log.error('--conf-file argument does not exist')
466 sys.exit(missing_cli_config_file % explicit_fname)
467
468
469 found_any_file = False
470 for f in _cfg.source_files.values():
471 if f is not None:
472 found_any_file = True
473 break
474 if not found_any_file:
475 _log.error('no config file found at all')
476 sys.exit(no_config_files % '\n '.join(candidates))
477
478
479 fname = u'mime_type2file_extension.conf'
480 _cfg.add_file_source (
481 source = u'user-mime',
482 file = os.path.join(paths.user_config_dir, fname),
483 encoding = enc
484 )
485 _cfg.add_file_source (
486 source = u'system-mime',
487 file = os.path.join(paths.system_config_dir, fname),
488 encoding = enc
489 )
490
492 global ui_type
493
494 ui_type = _cfg.get(option = u'--ui', source_order = [(u'cli', u'return')])
495
496 if ui_type in [True, False, None]:
497 ui_type = 'wxp'
498
499 ui_type = ui_type.strip()
500
501 if ui_type not in _known_ui_types:
502 _log.error('unknown UI type: %s', ui_type)
503 _log.debug('known UI types: %s', str(_known_ui_types))
504 print "GNUmed startup: Unknown UI type (%s). Defaulting to wxPython client." % ui_type
505 ui_type = 'wxp'
506
507 _log.debug('UI type: %s', ui_type)
508
510
511 db_version = gmPG2.map_client_branch2required_db_version[current_client_branch]
512 _log.info('client expects database version [%s]', db_version)
513 _cfg.set_option (
514 option = u'database_version',
515 value = db_version
516 )
517
518
519 timezone = _cfg.get (
520 group = u'backend',
521 option = 'client timezone',
522 source_order = [
523 ('explicit', 'return'),
524 ('workbase', 'return'),
525 ('local', 'return'),
526 ('user', 'return'),
527 ('system', 'return')
528 ]
529 )
530 if timezone is not None:
531 gmPG2.set_default_client_timezone(timezone)
532
535
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562 logging.raiseExceptions = False
563
574
575
576
577 setup_python_path()
578 setup_logging()
579 log_startup_info()
580 setup_console_exception_handler()
581 setup_cli()
582 setup_signal_handlers()
583 setup_local_repo_path()
584
585 from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks
586 setup_locale()
587 handle_help_request()
588 handle_version_request()
589 setup_paths_and_files()
590 setup_date_time()
591 setup_cfg()
592 setup_ui_type()
593
594 from Gnumed.pycommon import gmPG2
595 if ui_type in [u'web']:
596 gmPG2.auto_request_login_params = False
597 setup_backend()
598
599
600 gmHooks.run_hook_script(hook = u'startup-before-GUI')
601
602 if ui_type == u'wxp':
603 from Gnumed.wxpython import gmGuiMain
604 profile_file = _cfg.get(option = u'--profile', source_order = [(u'cli', u'return')])
605 if profile_file is not None:
606 _log.info('writing profiling data into %s', profile_file)
607 import profile
608 profile.run('gmGuiMain.main()', profile_file)
609 else:
610 gmGuiMain.main()
611 elif ui_type == u'web':
612 from Gnumed.proxiedpyjamas import gmWebGuiServer
613 gmWebGuiServer.main()
614
615 elif ui_type == u'chweb':
616 from Gnumed.CherryPy import gmGuiWeb
617 gmGuiWeb.main()
618
619 gmHooks.run_hook_script(hook = u'shutdown-post-GUI')
620
621 shutdown_backend()
622 shutdown_tmp_dir()
623 _log.info('Normally shutting down as main module.')
624 shutdown_logging()
625
626
627