• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
exec_check(string const * command,LIST ** pShell,int * error_length,int * error_max_length)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 
exec_cmd(string const * command,int flags,ExecCmdCallback func,void * closure,LIST * shell)101 void exec_cmd
102 (
103     string const * command,
104     int flags,
105     ExecCmdCallback func,
106     void * closure,
107     LIST * shell
108 )
109 {
110     char * s;
111     char * e;
112     char * p;
113     int vms_status;
114     int status;
115     int rstat = EXEC_CMD_OK;
116     int exit_reason = EXIT_OK;
117     timing_info time_info;
118     timestamp start_dt;
119     struct tms start_time;
120     struct tms end_time;
121     char * cmd_string = command->value;
122 
123 
124     /* Start the command */
125 
126     timestamp_current( &time_info.start );
127     times( &start_time );
128 
129     /* See if command is more than one line discounting leading/trailing white
130      * space.
131      */
132     for ( s = cmd_string; *s && isspace( *s ); ++s );
133 
134     e = p = strchr( s, '\n' );
135 
136     while ( p && isspace( *p ) )
137         ++p;
138 
139     /* If multi line or long, write to com file. Otherwise, exec directly. */
140     if ( ( p && *p ) || ( e - s > WRTLEN ) )
141     {
142         FILE * f;
143 
144         /* Create temp file invocation. */
145 
146         if ( !*commandbuf )
147         {
148             OBJECT * tmp_filename = 0;
149 
150             tmp_filename = path_tmpfile();
151 
152 
153             /* Get tmp file name is VMS-format. */
154             {
155                 string os_filename[ 1 ];
156                 string_new( os_filename );
157                 path_translate_to_os( object_str( tmp_filename ), os_filename );
158                 object_free( tmp_filename );
159                 tmp_filename = object_new( os_filename->value );
160                 string_free( os_filename );
161             }
162 
163             commandbuf[0] = '@';
164             strncat( commandbuf + 1, object_str( tmp_filename ),
165                      VMS_COMMAND_MAX - 2);
166         }
167 
168 
169         /* Open tempfile. */
170         if ( !( f = fopen( commandbuf + 1, "w" ) ) )
171         {
172             err_printf( "[errno %d] failed to wite cmd_string file '%s': %s",
173                 errno, commandbuf + 1, strerror(errno) );
174             rstat = EXEC_CMD_FAIL;
175             exit_reason = EXIT_FAIL;
176 
177             times( &end_time );
178 
179             timestamp_current( &time_info.end );
180             time_info.system = (double)( end_time.tms_cstime -
181                 start_time.tms_cstime ) / 100.;
182             time_info.user   = (double)( end_time.tms_cutime -
183                 start_time.tms_cutime ) / 100.;
184 
185             (*func)( closure, rstat, &time_info, "" , "", exit_reason  );
186             return;
187         }
188 
189 
190         /* Running from TMP, so explicitly set default to CWD. */
191         {
192             char * cwd = NULL;
193             int cwd_buf_size = VMS_PATH_MAX;
194 
195             while ( !(cwd = getcwd( NULL, cwd_buf_size ) ) /* alloc internally */
196                      && errno == ERANGE )
197             {
198                 cwd_buf_size += VMS_PATH_MAX;
199             }
200 
201             if ( !cwd )
202             {
203                 perror( "can not get current working directory" );
204                 exit( EXITBAD );
205             }
206 
207             fprintf( f, "$ SET DEFAULT %s\n", cwd);
208 
209             free( cwd );
210         }
211 
212 
213         /* For each line of the command. */
214         while ( *cmd_string )
215         {
216             char * s = strchr( cmd_string,'\n' );
217             int len = s ? s + 1 - cmd_string : strlen( cmd_string );
218 
219             fputc( '$', f );
220 
221             /* For each chunk of a line that needs to be split. */
222             while ( len > 0 )
223             {
224                 char * q = cmd_string;
225                 char * qe = cmd_string + MIN( len, WRTLEN );
226                 char * qq = q;
227                 int quote = 0;
228 
229                 /* Look for matching "s -- expected in the same line. */
230                 for ( ; q < qe; ++q )
231                     if ( ( *q == CHAR_DQUOTE ) && ( quote = !quote ) )
232                         qq = q;
233 
234                 /* When needs splitting and is inside an open quote,
235                  * back up to opening quote and split off at it.
236                  * When the quoted string spans over a chunk,
237                  * pass string as a whole.
238                  * If no matching quote found, dump the rest of command.
239                  */
240                 if (  len > WRTLEN && quote )
241                 {
242                     q = qq;
243 
244                     if ( q == cmd_string )
245                     {
246                         for ( q = qe; q < ( cmd_string + len )
247                                       && *q != CHAR_DQUOTE ; ++q) {}
248                         q = ( *q == CHAR_DQUOTE) ? ( q + 1 ) : ( cmd_string + len );
249                     }
250                 }
251 
252                 fwrite( cmd_string, ( q - cmd_string ), 1, f );
253 
254                 len -= ( q - cmd_string );
255                 cmd_string = q;
256 
257                 if ( len )
258                 {
259                     fputc( '-', f );
260                     fputc( '\n', f );
261                 }
262             }
263         }
264 
265         fclose( f );
266 
267         if ( DEBUG_EXECCMD )
268         {
269             FILE * f;
270             char buf[ WRTLEN + 1 ] = { 0 };
271 
272             if ( (f = fopen( commandbuf + 1, "r" ) ) )
273             {
274                 int nbytes;
275                 printf( "Command file: %s\n", commandbuf + 1 );
276 
277                 do
278                 {
279                     nbytes = fread( buf, sizeof( buf[0] ), sizeof( buf ) - 1, f );
280 
281                     if ( nbytes ) fwrite(buf, sizeof( buf[0] ), nbytes, stdout);
282                 }
283                 while ( !feof(f) );
284 
285                 fclose(f);
286             }
287         }
288 
289         /* Execute command file */
290         vms_status = system( commandbuf );
291         status = get_status( vms_status );
292 
293         unlink( commandbuf + 1 );
294     }
295     else
296     {
297         /* Execute single line command. Strip trailing newline before execing.
298          * TODO:Call via popen() with capture of the output may be better here.
299          */
300         if ( e ) *e = 0;
301 
302         status = VMS_SUCCESS;  /* success on empty command */
303         if ( *s )
304         {
305             vms_status = system( s );
306             status = get_status( vms_status );
307         }
308     }
309 
310 
311     times( &end_time );
312 
313     timestamp_current( &time_info.end );
314     time_info.system = (double)( end_time.tms_cstime -
315         start_time.tms_cstime ) / 100.;
316     time_info.user   = (double)( end_time.tms_cutime -
317         start_time.tms_cutime ) / 100.;
318 
319 
320     /* Fail for error or fatal error. OK on OK, warning or info exit. */
321     if ( ( status == VMS_ERROR ) || ( status == VMS_FATAL ) )
322     {
323         rstat = EXEC_CMD_FAIL;
324         exit_reason = EXIT_FAIL;
325     }
326 
327     (*func)( closure, rstat, &time_info, "" , "", exit_reason  );
328 }
329 
330 
exec_wait()331 void exec_wait()
332 {
333     return;
334 }
335 
336 
337 /* get_status() - returns status of the VMS command execution.
338    - Map VMS status to its severity (lower 3-bits)
339    - W-DCL-IVVERB is returned on unrecognized command -- map to general ERROR
340 */
get_status(int vms_status)341 int get_status( int vms_status )
342 {
343 #define VMS_STATUS_DCL_IVVERB 0x00038090
344 
345     int status;
346 
347     switch (vms_status)
348     {
349     case VMS_STATUS_DCL_IVVERB:
350         status = VMS_ERROR;
351         break;
352 
353     default:
354         status = vms_status & 0x07; /* $SEVERITY bits */
355     }
356 
357     return status;
358 }
359 
360 
361 #define __NEW_STARLET 1
362 
363 #include <stdio.h>
364 #include <stdlib.h>
365 #include <time.h>
366 #include <ssdef.h>
367 #include <stsdef.h>
368 #include <jpidef.h>
369 #include <efndef.h>
370 #include <iosbdef.h>
371 #include <iledef.h>
372 #include <lib$routines.h>
373 #include <starlet.h>
374 
375 
376 /*
377  * get_cpu_time() - returns CPU time in CLOCKS_PER_SEC since process start.
378  * on error returns (clock_t)-1.
379  *
380  * Intended to emulate (system + user) result of *NIX times(), if CRTL times()
381  * is not available.
382 *  However, this accounts only for the current process. To account for child
383 *  processes, these need to be directly spawned/forked via exec().
384 *  Moreover, child processes should be running a C main program or a program
385 *  that calls VAXC$CRTL_INIT or DECC$CRTL_INIT.
386 */
387 
get_cpu_time()388 clock_t get_cpu_time()
389 {
390     clock_t result = (clock_t) 0;
391 
392     IOSB iosb;
393     int status;
394     long cputime = 0;
395 
396 
397     ILE3 jpi_items[] = {
398         { sizeof( cputime ), JPI$_CPUTIM, &cputime, NULL }, /* longword int, 10ms */
399         { 0 },
400     };
401 
402     status = sys$getjpiw (EFN$C_ENF, 0, 0, jpi_items, &iosb, 0, 0);
403 
404     if ( !$VMS_STATUS_SUCCESS( status ) )
405     {
406         lib$signal( status );
407 
408         result = (clock_t) -1;
409         return result;
410     }
411 
412 
413     result = ( cputime / 100 ) * CLOCKS_PER_SEC;
414 
415     return result;
416 }
417 
418 
419 # endif /* VMS */
420 
421