]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Copyright 1993, 1995 Christopher Seiwald. | |
3 | * | |
4 | * This file is part of Jam - see jam.c for Copyright information. | |
5 | */ | |
6 | ||
7 | /* This file is ALSO: | |
8 | * Copyright 2001-2004 David Abrahams. | |
9 | * Copyright 2015 Artur Shepilko. | |
10 | * Distributed under the Boost Software License, Version 1.0. | |
11 | * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) | |
12 | */ | |
13 | ||
14 | ||
15 | /* | |
16 | * execvms.c - execute a shell script, ala VMS. | |
17 | * | |
18 | * The approach is this: | |
19 | * | |
20 | * If the command is a single line, and shorter than WRTLEN (what we believe to | |
21 | * be the maximum line length), we just system() it. | |
22 | * | |
23 | * If the command is multi-line, or longer than WRTLEN, we write the command | |
24 | * block to a temp file, splitting long lines (using "-" at the end of the line | |
25 | * to indicate contiuation), and then source that temp file. We use special | |
26 | * logic to make sure we do not continue in the middle of a quoted string. | |
27 | * | |
28 | * 05/04/94 (seiwald) - async multiprocess interface; noop on VMS | |
29 | * 12/20/96 (seiwald) - rewritten to handle multi-line commands well | |
30 | * 01/14/96 (seiwald) - do not put -'s between "'s | |
31 | * 01/19/15 (shepilko)- adapt for jam-3.1.19 | |
32 | */ | |
33 | ||
34 | #include "jam.h" | |
35 | #include "lists.h" | |
36 | #include "execcmd.h" | |
37 | #include "output.h" | |
38 | ||
39 | #ifdef OS_VMS | |
40 | ||
41 | #include <stdio.h> | |
42 | #include <string.h> | |
43 | #include <stdlib.h> | |
44 | #include <ctype.h> | |
45 | #include <times.h> | |
46 | #include <unistd.h> | |
47 | #include <errno.h> | |
48 | ||
49 | ||
50 | #define WRTLEN 240 | |
51 | ||
52 | #define MIN( a, b ) ((a) < (b) ? (a) : (b)) | |
53 | ||
54 | #define CHAR_DQUOTE '"' | |
55 | ||
56 | #define VMS_PATH_MAX 1024 | |
57 | #define VMS_COMMAND_MAX 1024 | |
58 | ||
59 | #define VMS_WARNING 0 | |
60 | #define VMS_SUCCESS 1 | |
61 | #define VMS_ERROR 2 | |
62 | #define VMS_FATAL 4 | |
63 | ||
64 | char commandbuf[ VMS_COMMAND_MAX ] = { 0 }; | |
65 | ||
66 | ||
67 | static int get_status(int vms_status); | |
68 | static clock_t get_cpu_time(); | |
69 | ||
70 | /* | |
71 | * exec_check() - preprocess and validate the command. | |
72 | */ | |
73 | ||
74 | int exec_check | |
75 | ( | |
76 | string const * command, | |
77 | LIST * * pShell, | |
78 | int * error_length, | |
79 | int * error_max_length | |
80 | ) | |
81 | { | |
82 | int const is_raw_cmd = 1; | |
83 | ||
84 | /* We allow empty commands for non-default shells since we do not really | |
85 | * know what they are going to do with such commands. | |
86 | */ | |
87 | if ( !command->size && ( is_raw_cmd || list_empty( *pShell ) ) ) | |
88 | return EXEC_CHECK_NOOP; | |
89 | ||
90 | return is_raw_cmd | |
91 | ? EXEC_CHECK_OK | |
92 | : check_cmd_for_too_long_lines( command->value, MAXLINE, error_length, | |
93 | error_max_length ); | |
94 | } | |
95 | ||
96 | ||
97 | /* | |
98 | * exec_cmd() - execute system command. | |
99 | */ | |
100 | ||
101 | void exec_cmd | |
102 | ( | |
103 | string const * command, | |
104 | ExecCmdCallback func, | |
105 | void * closure, | |
106 | LIST * shell | |
107 | ) | |
108 | { | |
109 | char * s; | |
110 | char * e; | |
111 | char * p; | |
112 | int vms_status; | |
113 | int status; | |
114 | int rstat = EXEC_CMD_OK; | |
115 | int exit_reason = EXIT_OK; | |
116 | timing_info time_info; | |
117 | timestamp start_dt; | |
118 | struct tms start_time; | |
119 | struct tms end_time; | |
120 | char * cmd_string = command->value; | |
121 | ||
122 | ||
123 | /* Start the command */ | |
124 | ||
125 | timestamp_current( &time_info.start ); | |
126 | times( &start_time ); | |
127 | ||
128 | /* See if command is more than one line discounting leading/trailing white | |
129 | * space. | |
130 | */ | |
131 | for ( s = cmd_string; *s && isspace( *s ); ++s ); | |
132 | ||
133 | e = p = strchr( s, '\n' ); | |
134 | ||
135 | while ( p && isspace( *p ) ) | |
136 | ++p; | |
137 | ||
138 | /* If multi line or long, write to com file. Otherwise, exec directly. */ | |
139 | if ( ( p && *p ) || ( e - s > WRTLEN ) ) | |
140 | { | |
141 | FILE * f; | |
142 | ||
143 | /* Create temp file invocation. */ | |
144 | ||
145 | if ( !*commandbuf ) | |
146 | { | |
147 | OBJECT * tmp_filename = 0; | |
148 | ||
149 | tmp_filename = path_tmpfile(); | |
150 | ||
151 | ||
152 | /* Get tmp file name is VMS-format. */ | |
153 | { | |
154 | string os_filename[ 1 ]; | |
155 | string_new( os_filename ); | |
156 | path_translate_to_os( object_str( tmp_filename ), os_filename ); | |
157 | object_free( tmp_filename ); | |
158 | tmp_filename = object_new( os_filename->value ); | |
159 | string_free( os_filename ); | |
160 | } | |
161 | ||
162 | commandbuf[0] = '@'; | |
163 | strncat( commandbuf + 1, object_str( tmp_filename ), | |
164 | VMS_COMMAND_MAX - 2); | |
165 | } | |
166 | ||
167 | ||
168 | /* Open tempfile. */ | |
169 | if ( !( f = fopen( commandbuf + 1, "w" ) ) ) | |
170 | { | |
171 | printf( "can't open cmd_string file\n" ); | |
172 | rstat = EXEC_CMD_FAIL; | |
173 | exit_reason = EXIT_FAIL; | |
174 | ||
175 | times( &end_time ); | |
176 | ||
177 | timestamp_current( &time_info.end ); | |
178 | time_info.system = (double)( end_time.tms_cstime - | |
179 | start_time.tms_cstime ) / 100.; | |
180 | time_info.user = (double)( end_time.tms_cutime - | |
181 | start_time.tms_cutime ) / 100.; | |
182 | ||
183 | (*func)( closure, rstat, &time_info, "" , "", exit_reason ); | |
184 | return; | |
185 | } | |
186 | ||
187 | ||
188 | /* Running from TMP, so explicitly set default to CWD. */ | |
189 | { | |
190 | char * cwd = NULL; | |
191 | int cwd_buf_size = VMS_PATH_MAX; | |
192 | ||
193 | while ( !(cwd = getcwd( NULL, cwd_buf_size ) ) /* alloc internally */ | |
194 | && errno == ERANGE ) | |
195 | { | |
196 | cwd_buf_size += VMS_PATH_MAX; | |
197 | } | |
198 | ||
199 | if ( !cwd ) | |
200 | { | |
201 | perror( "can not get current working directory" ); | |
202 | exit( EXITBAD ); | |
203 | } | |
204 | ||
205 | fprintf( f, "$ SET DEFAULT %s\n", cwd); | |
206 | ||
207 | free( cwd ); | |
208 | } | |
209 | ||
210 | ||
211 | /* For each line of the command. */ | |
212 | while ( *cmd_string ) | |
213 | { | |
214 | char * s = strchr( cmd_string,'\n' ); | |
215 | int len = s ? s + 1 - cmd_string : strlen( cmd_string ); | |
216 | ||
217 | fputc( '$', f ); | |
218 | ||
219 | /* For each chunk of a line that needs to be split. */ | |
220 | while ( len > 0 ) | |
221 | { | |
222 | char * q = cmd_string; | |
223 | char * qe = cmd_string + MIN( len, WRTLEN ); | |
224 | char * qq = q; | |
225 | int quote = 0; | |
226 | ||
227 | /* Look for matching "s -- expected in the same line. */ | |
228 | for ( ; q < qe; ++q ) | |
229 | if ( ( *q == CHAR_DQUOTE ) && ( quote = !quote ) ) | |
230 | qq = q; | |
231 | ||
232 | /* When needs splitting and is inside an open quote, | |
233 | * back up to opening quote and split off at it. | |
234 | * When the quoted string spans over a chunk, | |
235 | * pass string as a whole. | |
236 | * If no matching quote found, dump the rest of command. | |
237 | */ | |
238 | if ( len > WRTLEN && quote ) | |
239 | { | |
240 | q = qq; | |
241 | ||
242 | if ( q == cmd_string ) | |
243 | { | |
244 | for ( q = qe; q < ( cmd_string + len ) | |
245 | && *q != CHAR_DQUOTE ; ++q) {} | |
246 | q = ( *q == CHAR_DQUOTE) ? ( q + 1 ) : ( cmd_string + len ); | |
247 | } | |
248 | } | |
249 | ||
250 | fwrite( cmd_string, ( q - cmd_string ), 1, f ); | |
251 | ||
252 | len -= ( q - cmd_string ); | |
253 | cmd_string = q; | |
254 | ||
255 | if ( len ) | |
256 | { | |
257 | fputc( '-', f ); | |
258 | fputc( '\n', f ); | |
259 | } | |
260 | } | |
261 | } | |
262 | ||
263 | fclose( f ); | |
264 | ||
265 | if ( DEBUG_EXECCMD ) | |
266 | { | |
267 | FILE * f; | |
268 | char buf[ WRTLEN + 1 ] = { 0 }; | |
269 | ||
270 | if ( (f = fopen( commandbuf + 1, "r" ) ) ) | |
271 | { | |
272 | int nbytes; | |
273 | printf( "Command file: %s\n", commandbuf + 1 ); | |
274 | ||
275 | do | |
276 | { | |
277 | nbytes = fread( buf, sizeof( buf[0] ), sizeof( buf ) - 1, f ); | |
278 | ||
279 | if ( nbytes ) fwrite(buf, sizeof( buf[0] ), nbytes, stdout); | |
280 | } | |
281 | while ( !feof(f) ); | |
282 | ||
283 | fclose(f); | |
284 | } | |
285 | } | |
286 | ||
287 | /* Execute command file */ | |
288 | vms_status = system( commandbuf ); | |
289 | status = get_status( vms_status ); | |
290 | ||
291 | unlink( commandbuf + 1 ); | |
292 | } | |
293 | else | |
294 | { | |
295 | /* Execute single line command. Strip trailing newline before execing. | |
296 | * TODO:Call via popen() with capture of the output may be better here. | |
297 | */ | |
298 | if ( e ) *e = 0; | |
299 | ||
300 | status = VMS_SUCCESS; /* success on empty command */ | |
301 | if ( *s ) | |
302 | { | |
303 | vms_status = system( s ); | |
304 | status = get_status( vms_status ); | |
305 | } | |
306 | } | |
307 | ||
308 | ||
309 | times( &end_time ); | |
310 | ||
311 | timestamp_current( &time_info.end ); | |
312 | time_info.system = (double)( end_time.tms_cstime - | |
313 | start_time.tms_cstime ) / 100.; | |
314 | time_info.user = (double)( end_time.tms_cutime - | |
315 | start_time.tms_cutime ) / 100.; | |
316 | ||
317 | ||
318 | /* Fail for error or fatal error. OK on OK, warning or info exit. */ | |
319 | if ( ( status == VMS_ERROR ) || ( status == VMS_FATAL ) ) | |
320 | { | |
321 | rstat = EXEC_CMD_FAIL; | |
322 | exit_reason = EXIT_FAIL; | |
323 | } | |
324 | ||
325 | (*func)( closure, rstat, &time_info, "" , "", exit_reason ); | |
326 | } | |
327 | ||
328 | ||
329 | void exec_wait() | |
330 | { | |
331 | return; | |
332 | } | |
333 | ||
334 | ||
335 | /* get_status() - returns status of the VMS command execution. | |
336 | - Map VMS status to its severity (lower 3-bits) | |
337 | - W-DCL-IVVERB is returned on unrecognized command -- map to general ERROR | |
338 | */ | |
339 | int get_status( int vms_status ) | |
340 | { | |
341 | #define VMS_STATUS_DCL_IVVERB 0x00038090 | |
342 | ||
343 | int status; | |
344 | ||
345 | switch (vms_status) | |
346 | { | |
347 | case VMS_STATUS_DCL_IVVERB: | |
348 | status = VMS_ERROR; | |
349 | break; | |
350 | ||
351 | default: | |
352 | status = vms_status & 0x07; /* $SEVERITY bits */ | |
353 | } | |
354 | ||
355 | return status; | |
356 | } | |
357 | ||
358 | ||
359 | #define __NEW_STARLET 1 | |
360 | ||
361 | #include <stdio.h> | |
362 | #include <stdlib.h> | |
363 | #include <time.h> | |
364 | #include <ssdef.h> | |
365 | #include <stsdef.h> | |
366 | #include <jpidef.h> | |
367 | #include <efndef.h> | |
368 | #include <iosbdef.h> | |
369 | #include <iledef.h> | |
370 | #include <lib$routines.h> | |
371 | #include <starlet.h> | |
372 | ||
373 | ||
374 | /* | |
375 | * get_cpu_time() - returns CPU time in CLOCKS_PER_SEC since process start. | |
376 | * on error returns (clock_t)-1. | |
377 | * | |
378 | * Intended to emulate (system + user) result of *NIX times(), if CRTL times() | |
379 | * is not available. | |
380 | * However, this accounts only for the current process. To account for child | |
381 | * processes, these need to be directly spawned/forked via exec(). | |
382 | * Moreover, child processes should be running a C main program or a program | |
383 | * that calls VAXC$CRTL_INIT or DECC$CRTL_INIT. | |
384 | */ | |
385 | ||
386 | clock_t get_cpu_time() | |
387 | { | |
388 | clock_t result = (clock_t) 0; | |
389 | ||
390 | IOSB iosb; | |
391 | int status; | |
392 | long cputime = 0; | |
393 | ||
394 | ||
395 | ILE3 jpi_items[] = { | |
396 | { sizeof( cputime ), JPI$_CPUTIM, &cputime, NULL }, /* longword int, 10ms */ | |
397 | { 0 }, | |
398 | }; | |
399 | ||
400 | status = sys$getjpiw (EFN$C_ENF, 0, 0, jpi_items, &iosb, 0, 0); | |
401 | ||
402 | if ( !$VMS_STATUS_SUCCESS( status ) ) | |
403 | { | |
404 | lib$signal( status ); | |
405 | ||
406 | result = (clock_t) -1; | |
407 | return result; | |
408 | } | |
409 | ||
410 | ||
411 | result = ( cputime / 100 ) * CLOCKS_PER_SEC; | |
412 | ||
413 | return result; | |
414 | } | |
415 | ||
416 | ||
417 | # endif /* VMS */ | |
418 |