1 __doc__ = """GNUmed general tools."""
2
3
4 __version__ = "$Revision: 1.13 $"
5 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
6 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
7
8
9
10 import os
11 import sys
12 import logging
13 import subprocess
14 import shlex
15
16
17 _log = logging.getLogger('gm.shell')
18 _log.info(__version__)
19
20
22
23 _log.debug('cmd: [%s]', cmd)
24 dirname = os.path.dirname(cmd)
25 _log.debug('dir: [%s]', dirname)
26 if dirname != u'':
27 _log.info('command with full or relative path, not searching in PATH for binary')
28 return (None, None)
29
30 env_paths = os.environ['PATH']
31 _log.debug('${PATH}: %s', env_paths)
32 for path in env_paths.split(os.pathsep):
33 candidate = os.path.join(path, cmd).encode(sys.getfilesystemencoding())
34 if os.access(candidate, os.X_OK):
35 _log.debug('found [%s]', candidate)
36 return (True, candidate.decode(sys.getfilesystemencoding()))
37 else:
38 _log.debug('not found: %s', candidate)
39
40 _log.debug('command not found in PATH')
41
42 return (False, None)
43
45
46 if not cmd.startswith('wine'):
47 _log.debug('not a WINE call: %s', cmd)
48 return (False, None)
49
50 exe_path = cmd.encode(sys.getfilesystemencoding())
51
52 exe_path = exe_path[4:].strip().strip('"').strip()
53
54 if os.access(exe_path, os.R_OK):
55 _log.debug('WINE call with UNIX path: %s', exe_path)
56 return (True, cmd)
57
58
59 found, full_winepath_path = is_cmd_in_path(cmd = r'winepath')
60 if not found:
61 _log.error('[winepath] not found, cannot check WINE call for Windows path conformance: %s', exe_path)
62 return (False, None)
63
64
65 cmd_line = r'%s -u "%s"' % (
66 full_winepath_path.encode(sys.getfilesystemencoding()),
67 exe_path
68 )
69 _log.debug('converting Windows path to UNIX path: %s' % cmd_line)
70 cmd_line = shlex.split(cmd_line)
71 try:
72 winepath = subprocess.Popen (
73 cmd_line,
74 stdout = subprocess.PIPE,
75 stderr = subprocess.PIPE,
76 universal_newlines = True
77 )
78 except OSError:
79 _log.exception('cannot run <winepath>')
80 return (False, None)
81
82 stdout, stderr = winepath.communicate()
83 full_path = stdout.strip('\r\n')
84 _log.debug('UNIX path: %s', full_path)
85
86 if winepath.returncode != 0:
87 _log.error('<winepath -u> returned [%s], failed to convert path', winepath.returncode)
88 return (False, None)
89
90 if os.access(full_path, os.R_OK):
91 _log.debug('WINE call with Windows path')
92 return (True, cmd)
93
94 _log.warning('Windows path [%s] not verifiable under UNIX: %s', exe_path, full_path)
95 return (False, None)
96
98 _log.debug('searching for [%s]', binary)
99
100 binary = binary.lstrip()
101
102
103 if os.access(binary, os.X_OK):
104 _log.debug('found: executable explicit path')
105 return (True, binary)
106
107
108 found, full_path = is_cmd_in_path(cmd = binary)
109 if found:
110 if os.access(full_path, os.X_OK):
111 _log.debug('found: executable in ${PATH}')
112 return (True, full_path)
113
114
115 is_wine_call, full_path = is_executable_by_wine(cmd = binary)
116 if is_wine_call:
117 _log.debug('found: is valid WINE call')
118 return (True, full_path)
119
120
121 if os.name == 'nt':
122
123 if not (binary.endswith('.exe') or binary.endswith('.bat')):
124 _log.debug('re-testing with [.exe] appended')
125 exe_binary = binary + r'.exe'
126 found_dot_exe_binary, full_path = detect_external_binary(binary = exe_binary)
127 if found_dot_exe_binary:
128 return (True, full_path)
129
130 _log.debug('re-testing with [.bat] appended')
131 bat_binary = binary + r'.bat'
132 found_bat_binary, full_path = detect_external_binary(binary = bat_binary)
133 if found_bat_binary:
134 return (True, full_path)
135
136 return (False, None)
137
139 found = False
140 binary = None
141
142 for cmd in binaries:
143 _log.debug('looking for [%s]', cmd)
144 if cmd is None:
145 continue
146 found, binary = detect_external_binary(binary = cmd)
147 if found:
148 break
149
150 return (found, binary)
151
153 """Runs a command in a subshell via standard-C system().
154
155 <command>
156 The shell command to run including command line options.
157 <blocking>
158 This will make the code *block* until the shell command exits.
159 It will likely only work on UNIX shells where "cmd &" makes sense.
160 """
161 if acceptable_return_codes is None:
162 acceptable_return_codes = [0]
163
164 _log.debug('shell command >>>%s<<<', command)
165 _log.debug('blocking: %s', blocking)
166 _log.debug('acceptable return codes: %s', str(acceptable_return_codes))
167
168
169 command = command.strip()
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 if blocking is True:
190 if command[-2:] == ' &':
191 command = command[:-2]
192 elif blocking is False:
193 if command[-2:] != ' &':
194 command += ' &'
195
196 _log.info('running shell command >>>%s<<<', command)
197
198 ret_val = os.system(command.encode(sys.getfilesystemencoding()))
199 _log.debug('os.system() returned: [%s]', ret_val)
200
201 exited_normally = False
202
203 if not hasattr(os, 'WIFEXITED'):
204 _log.error('platform does not support exit status differentiation')
205 if ret_val in acceptable_return_codes:
206 _log.info('os.system() return value contained in acceptable return codes')
207 _log.info('continuing and hoping for the best')
208 return True
209 return exited_normally
210
211 _log.debug('exited via exit(): %s', os.WIFEXITED(ret_val))
212 if os.WIFEXITED(ret_val):
213 _log.debug('exit code: [%s]', os.WEXITSTATUS(ret_val))
214 exited_normally = (os.WEXITSTATUS(ret_val) in acceptable_return_codes)
215 _log.debug('normal exit: %s', exited_normally)
216 _log.debug('dumped core: %s', os.WCOREDUMP(ret_val))
217 _log.debug('stopped by signal: %s', os.WIFSIGNALED(ret_val))
218 if os.WIFSIGNALED(ret_val):
219 _log.debug('STOP signal was: [%s]', os.STOPSIG(ret_val))
220 _log.debug('TERM signal was: [%s]', os.TERMSIG(ret_val))
221
222 return exited_normally
223
225
226 found, binary = find_first_binary(binaries = binaries)
227
228 if not found:
229 _log.warning('cannot find any of: %s', binaries)
230 if run_last_one_anyway:
231 binary = binaries[-1]
232 _log.debug('falling back to trying to run [%s] anyway', binary)
233 else:
234 return False
235
236 return run_command_in_shell(command = '%s %s' % (binary, args), blocking = blocking, acceptable_return_codes = acceptable_return_codes)
237
238
239
240 if __name__ == '__main__':
241
242 if len(sys.argv) < 2:
243 sys.exit()
244
245 if sys.argv[1] != u'test':
246 sys.exit()
247
248 logging.basicConfig(level = logging.DEBUG)
249
251 found, path = detect_external_binary(binary = sys.argv[2])
252 if found:
253 print "found as:", path
254 else:
255 print sys.argv[2], "not found"
256
258 print "-------------------------------------"
259 print "running:", sys.argv[2]
260 if run_command_in_shell(command=sys.argv[2], blocking=True):
261 print "-------------------------------------"
262 print "success"
263 else:
264 print "-------------------------------------"
265 print "failure, consult log"
266
269
272
273
274 test_detect_external_binary()
275
276
277
278
279