1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 2003-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21
22 /*
23 * jcl program exec and script loop
24 */
25
26 #include "jcllib.h"
27
28 #include <coshell.h>
29 #include <errno.h>
30 #include <ls.h>
31 #include <tm.h>
32 #include <tmx.h>
33
34 /*
35 * if name contains invalid export id chars then
36 * return a new name with those chars converted to '_<XX>_'
37 */
38
39 static char*
fmtexport(const char * name)40 fmtexport(const char* name)
41 {
42 register int c;
43 register const char* s;
44 register char* t;
45 register int n;
46 char* b;
47
48 s = name;
49 c = *s++;
50 if (isalpha(c) || c == '_')
51 do
52 {
53 if (!(c = *s++))
54 return (char*)name;
55 } while (isalnum(c) || c == '_');
56 n = 1;
57 while (c = *s++)
58 if (!isalnum(c) && c != '_')
59 n++;
60 t = b = fmtbuf(strlen(name) + 4 * n + 1);
61 for (s = name; c = *s++;)
62 if (!isalnum(c) && c != '_')
63 t += sfsprintf(t, 5, "_%02X_", c);
64 else
65 *t++ = c;
66 *t = 0;
67 return b;
68 }
69
70 /*
71 * output dd dsname with &path => /tmp file map
72 */
73
74 static char*
dsn(Jcl_t * jcl,Jcldd_t * dd,const char * path,int mark)75 dsn(Jcl_t* jcl, Jcldd_t* dd, const char* path, int mark)
76 {
77 char* s;
78
79 if (mark && dd->disp[0] == JCL_DISP_MOD && !(dd->flags & JCL_DD_DIR))
80 sfprintf(jcl->vp, "+");
81 if (*path == '&')
82 {
83 if (*++path == '&')
84 path++;
85 sfprintf(jcl->vp, jcl->tmp);
86 }
87 sfprintf(jcl->vp, "%s", fmtquote(path, "\"", "\"", strlen(path), FMT_SHELL|FMT_PARAM));
88 if (!(s = sfstruse(jcl->vp)))
89 nospace(jcl, NiL);
90 return s;
91 }
92
93 /*
94 * create dd dir if it doesn't exist
95 */
96
97 static void
checkdir(Jcl_t * jcl,Jcldd_t * dd)98 checkdir(Jcl_t* jcl, Jcldd_t* dd)
99 {
100 register char* s;
101 Jcldir_t* dir;
102
103 if (!dtmatch(jcl->outdir, dd->path))
104 {
105 if (dir = vmnewof(jcl->vm, NiL, Jcldir_t, 1, strlen(dd->path)))
106 {
107 strcpy(dir->name, dd->path);
108 dtinsert(jcl->outdir, dir);
109 }
110 s = dsn(jcl, dd, dd->path, 0);
111 sfprintf(jcl->tp, "[[ ! -d %s && ! -f %s ]] && mkdir -p %s\n", s, s, s);
112 }
113 }
114
115 /*
116 * execution loop
117 */
118
119 int
jclrun(Jcl_t * scope)120 jclrun(Jcl_t* scope)
121 {
122 register Jcl_t* jcl;
123 register Jclstep_t* step;
124 register Jcldd_t* dd;
125 register Jclcat_t* cat;
126 register Jclsym_t* sym;
127 register char* s;
128 char* arg;
129 char* t;
130 Coshell_t* co;
131 Cojob_t* cj;
132 Jcldd_t* xx;
133 Jcldd_t* std[4];
134 Dtlink_t k;
135 Jcl_t* top;
136 int code;
137 int del;
138 int n;
139 int i;
140 double pct;
141 double real;
142 double user;
143 double sys;
144 Time_t start;
145 unsigned long flags;
146 char subdir[64];
147
148 if (!(jcl = jclopen(scope, scope->step->command, scope->flags|scope->step->flags, scope->disc)))
149 return -1;
150 if (jcl->flags & JCL_EXEC)
151 {
152 if (!(co = coopen(pathshell(), (jcl->flags & (JCL_EXEC|JCL_TRACE)) == (JCL_EXEC|JCL_TRACE) ? CO_ANY : (CO_ANY|CO_SILENT), NiL)))
153 {
154 if (jcl->disc->errorf)
155 (*jcl->disc->errorf)(NiL, jcl->disc, 2, "%s: cannot connect to coshell", jcl->name);
156 jclclose(jcl);
157 return -1;
158 }
159 start = tmxgettime();
160 user = co->user;
161 sys = co->sys;
162 }
163 code = 0;
164 if (jcl->name && (!scope || !scope->scope || (scope->scope->flags & JCL_SCOPE)))
165 {
166 top = jcl;
167 if (!(jcl->flags & JCL_EXEC))
168 {
169 if (jcl->flags & JCL_VERBOSE)
170 sfprintf(sfstdout, ": JOB %s\nexport %sJOBNAME=%s\ncode=0\n", jcl->name, JCL_AUTO, jcl->name);
171 if (jcl->flags & JCL_TRACE)
172 sfprintf(sfstderr, "+ : JOB %s\n", jcl->name);
173 }
174 if (jcl->flags & JCL_SUBDIR)
175 {
176 if (jcl->flags & JCL_EXEC)
177 {
178 t = fmttime("%y-%m-%d", time(NiL));
179 n = 0;
180 for (;;)
181 {
182 sfsprintf(subdir, sizeof(subdir), "%s.%s.%d", jcl->name, t, ++n);
183 if (!mkdir(subdir, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH))
184 break;
185 if (errno != EEXIST)
186 {
187 if (jcl->disc->errorf)
188 (*jcl->disc->errorf)(NiL, jcl->disc, ERROR_SYSTEM|2, "%s: cannot create job subdirectory", subdir);
189 jclclose(jcl);
190 return -1;
191 }
192 }
193 if (chdir(subdir))
194 {
195 if (jcl->disc->errorf)
196 (*jcl->disc->errorf)(NiL, jcl->disc, ERROR_SYSTEM|2, "%s: cannot run in job subdirectory", subdir);
197 jclclose(jcl);
198 return -1;
199 }
200 sfsync(sfstdout);
201 sfsync(sfstderr);
202 for (i = 0; i < elementsof(redirect); i++)
203 if (jcl->redirect[i] > 0)
204 {
205 close(redirect[i].fd);
206 if ((n = open(redirect[i].file, O_CREAT|O_TRUNC|O_WRONLY|O_APPEND|O_BINARY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != redirect[i].fd)
207 {
208 close(n);
209 dup2(jcl->redirect[i], redirect[i].fd);
210 }
211 }
212 sfset(sfstdout, SF_LINE, 1);
213 sfset(sfstderr, SF_LINE, 1);
214 if (jcl->flags & JCL_TRACE)
215 sfprintf(sfstderr, "+ mkdir %s\n+ cd %s\n", subdir, subdir);
216 if (jcl->flags & JCL_VERBOSE)
217 sfprintf(sfstdout, "STARTED AT %s\n", fmttime("%K", time(NiL)));
218 }
219 else if (jcl->flags & JCL_VERBOSE)
220 {
221 sfprintf(sfstdout, "n=0\nt=$(date +%%y-%%m-%%d)\nwhile :\ndo d=%s.$t.$((++n))\n [[ -d $d ]] || break\ndone\nmkdir $d && cd $d || exit 1\n", jcl->name);
222 sfputr(sfstdout, "exec > SYSOUT 2> SYSERR\nTIMEFORMAT='USAGE CPU=%P%% REAL=%R USR=%U SYS=%S'\ntime {\ndate +'STARTED AT %K'", '\n');
223 }
224 }
225 }
226 else
227 for (top = scope; !top->name && !(top->flags & JCL_SCOPE) && top->scope; top = top->scope);
228 while (step = jclstep(jcl))
229 {
230 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
231 if (dd->flags & JCL_DD_ALIAS)
232 {
233 dd->flags &= ~JCL_DD_ALIAS;
234 xx = 0;
235 t = fmtbuf(n = strlen(step->name) + strlen(dd->path) + 2);
236 sfsprintf(t, n, "%s.%s", step->name, dd->path);
237 for (scope = jcl; scope; scope = scope->scope)
238 if ((xx = (Jcldd_t*)dtmatch(scope->step->dd, t)) ||
239 (xx = (Jcldd_t*)dtmatch(scope->step->dd, dd->path)))
240 break;
241 if (!xx)
242 {
243 if (jcl->disc->errorf)
244 (*jcl->disc->errorf)(NiL, jcl->disc, 2, "%s: DD not defined", dd->path);
245 code = -1;
246 goto bad;
247 }
248 k = dd->link;
249 s = dd->name;
250 *dd = *xx;
251 dd->link = k;
252 dd->name = s;
253 }
254 if (!jcl->tmp)
255 {
256 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
257 {
258 if (dd->path && *dd->path == '&')
259 break;
260 for (cat = dd->cat; cat; cat = cat->next)
261 if (cat->path && *cat->path == '&')
262 break;
263 }
264 if (dd)
265 {
266 if (jcl->flags & JCL_EXEC)
267 {
268 if (!(s = getenv("TMPDIR")))
269 s = "/tmp";
270 sfprintf(jcl->vp, "%s/job.%s.%lu.", s, jcl->name, (unsigned long)getpid());
271 }
272 else
273 sfprintf(jcl->vp, "${TMPDIR:-/tmp}/job.%s.$$.", jcl->name);
274 if (!(s = sfstruse(jcl->vp)))
275 nospace(jcl, NiL);
276 jcl->tmp = stash(jcl, jcl->vm, s, 0);
277 if ((jcl->flags & (JCL_VERBOSE|JCL_EXEC)) == JCL_VERBOSE)
278 sfprintf(sfstdout, "trap 'code=$?; rm -rf %s*; exit $code' 0 1 2\n", jcl->tmp);
279 }
280 }
281 jcl->steps++;
282 if (jcl->flags & (JCL_LISTEXEC|JCL_LISTINPUTS|JCL_LISTOUTPUTS|JCL_LISTPROGRAMS|JCL_LISTSCRIPTS))
283 {
284 if (jcl->flags & JCL_LISTEXEC)
285 uniq(jcl->name, step->command, 0, jcl->disc);
286 else if (jcl->flags & (JCL_LISTINPUTS|JCL_LISTOUTPUTS))
287 {
288 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
289 {
290 flags = dd->disp[0] == JCL_DISP_NEW ? JCL_LISTOUTPUTS : JCL_LISTINPUTS;
291 if (dd->path && *dd->path != '&')
292 uniq(dd->path, NiL, flags, jcl->disc);
293 for (cat = dd->cat; cat; cat = cat->next)
294 if (*cat->path != '&')
295 uniq(cat->path, NiL, flags, jcl->disc);
296 }
297 }
298 else if (jcl->flags & JCL_LISTPROGRAMS)
299 {
300 if (step->flags & JCL_PGM)
301 uniq(step->command, NiL, 0, jcl->disc);
302 }
303 else if (jcl->flags & JCL_LISTSCRIPTS)
304 {
305 if (!(step->flags & JCL_PGM) && (s = jclfind(jcl, step->command, 0, 0, NiL)))
306 uniq(s, NiL, 0, jcl->disc);
307 }
308 }
309 if (!jcleval(jcl, jcl->cond, code))
310 break;
311 if (jcleval(jcl, step->cond, code))
312 {
313 if (step->flags & JCL_PGM)
314 {
315 std[0] = std[1] = std[2] = std[3] = 0;
316 sfprintf(jcl->tp, ": %s\n", step->name);
317 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
318 switch (dd->flags & (JCL_DD_DIR|JCL_DD_SYSIN|JCL_DD_SYSOUT|JCL_DD_SYSERR|JCL_DD_REFERENCE))
319 {
320 case JCL_DD_DIR:
321 if (dd->disp[0] == JCL_DISP_NEW || dd->disp[0] == JCL_DISP_MOD)
322 checkdir(jcl, dd);
323 break;
324 case JCL_DD_SYSIN:
325 if (dd->path)
326 std[0] = dd;
327 if (dd->here)
328 std[3] = dd;
329 break;
330 case JCL_DD_SYSOUT:
331 std[1] = dd;
332 break;
333 case JCL_DD_SYSERR:
334 std[2] = dd;
335 break;
336 default:
337 if (!(dd->flags & JCL_DD_DUMMY) && (jcl->flags & (JCL_EXEC|JCL_VERBOSE)) && dd->path && *dd->path != '&' && dd->disp[0] == JCL_DISP_NEW && (*dd->path != '$' || *(dd->path + 1) != '(') && (s = strrchr(dd->path, '/')))
338 {
339 *s = 0;
340 checkdir(jcl, dd);
341 *s = '/';
342 }
343 break;
344 }
345 for (dd = (Jcldd_t*)dtfirst(jcl->dd); dd; dd = (Jcldd_t*)dtnext(jcl->dd, dd))
346 if ((dd->flags & (JCL_DD_MARKED|JCL_DD_SYSIN|JCL_DD_SYSOUT|JCL_DD_SYSERR|JCL_DD_REFERENCE)) == JCL_DD_MARKED && (dd->disp[0] == JCL_DISP_NEW || dd->disp[0] == JCL_DISP_MOD))
347 {
348 s = dsn(jcl, dd, dd->path, 0);
349 sfprintf(jcl->tp, "[[ -e %s ]] || > %s\n", s, s);
350 }
351 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
352 if ((dd->flags & (JCL_DD_MARKED|JCL_DD_SYSIN|JCL_DD_SYSOUT|JCL_DD_SYSERR|JCL_DD_REFERENCE)) == JCL_DD_MARKED && (dd->disp[0] == JCL_DISP_NEW || dd->disp[0] == JCL_DISP_MOD))
353 {
354 s = dsn(jcl, dd, dd->path, 0);
355 sfprintf(jcl->tp, "[[ -e %s ]] || > %s\n", s, s);
356 }
357 if ((dd = std[0]) && dd->cat)
358 {
359 std[0] = std[3] = 0;
360 sfprintf(jcl->tp, "cat %s", dsn(jcl, dd, dd->path, 0));
361 for (cat = dd->cat; cat; cat = cat->next)
362 sfprintf(jcl->tp, " \\\n\t%s", dsn(jcl, dd, cat->path, 0));
363 sfprintf(jcl->tp, " |\n");
364 }
365 if (jcl->flags & JCL_EXEC)
366 sfprintf(jcl->tp, "%sJOBNAME=%s ", JCL_AUTO, top->name);
367 sfprintf(jcl->tp, "%s=%s", fmtexport("STEP"), step->name);
368 s = " \\\n";
369 for (dd = (Jcldd_t*)dtfirst(jcl->dd); dd; dd = (Jcldd_t*)dtnext(jcl->dd, dd))
370 if (!(dd->flags & JCL_DD_REFERENCE))
371 {
372 sfprintf(jcl->tp, "%s%s=%s", s, fmtexport(dd->name), dsn(jcl, dd, dd->path, 1));
373 for (cat = dd->cat; cat; cat = cat->next)
374 sfprintf(jcl->tp, "'\n\t'%s", dsn(jcl, dd, cat->path, 1));
375 }
376 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
377 if (dd->path && !(dd->flags & (JCL_DD_SYSIN|JCL_DD_SYSOUT|JCL_DD_SYSERR|JCL_DD_REFERENCE)))
378 {
379 sfprintf(jcl->tp, "%s%s=%s", s, fmtexport(dd->name), dsn(jcl, dd, dd->path, 1));
380 for (cat = dd->cat; cat; cat = cat->next)
381 sfprintf(jcl->tp, "'\n\t'%s", dsn(jcl, dd, cat->path, 1));
382 s = " \\\n";
383 }
384 del = 0;
385 sfprintf(jcl->tp, "%sDDIN='", s);
386 s = "";
387 for (dd = (Jcldd_t*)dtfirst(jcl->dd); dd; dd = (Jcldd_t*)dtnext(jcl->dd, dd))
388 if (!(dd->flags & (JCL_DD_SYSIN|JCL_DD_SYSOUT|JCL_DD_SYSERR|JCL_DD_REFERENCE)) && dd->disp[0] != JCL_DISP_NEW && dd->disp[0] != JCL_DISP_MOD)
389 {
390 sfprintf(jcl->tp, "%s%s", s, fmtexport(dd->name));
391 s = " ";
392 }
393 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
394 if (!(dd->flags & (JCL_DD_SYSIN|JCL_DD_SYSOUT|JCL_DD_SYSERR|JCL_DD_REFERENCE)) && dd->disp[0] != JCL_DISP_NEW && dd->disp[0] != JCL_DISP_MOD)
395 {
396 sfprintf(jcl->tp, "%s%s", s, fmtexport(dd->name));
397 s = " ";
398 }
399 sfprintf(jcl->tp, "' \\\nDDOUT='");
400 s = "";
401 for (dd = (Jcldd_t*)dtfirst(jcl->dd); dd; dd = (Jcldd_t*)dtnext(jcl->dd, dd))
402 if (!(dd->flags & (JCL_DD_SYSIN|JCL_DD_SYSOUT|JCL_DD_SYSERR|JCL_DD_REFERENCE)) && (dd->disp[0] == JCL_DISP_NEW || dd->disp[0] == JCL_DISP_MOD))
403 {
404 sfprintf(jcl->tp, "%s%s", s, fmtexport(dd->name));
405 s = " ";
406 }
407 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
408 {
409 if (!(dd->flags & (JCL_DD_SYSIN|JCL_DD_SYSOUT|JCL_DD_SYSERR|JCL_DD_REFERENCE)) && (dd->disp[0] == JCL_DISP_NEW || dd->disp[0] == JCL_DISP_MOD))
410 {
411 sfprintf(jcl->tp, "%s%s", s, fmtexport(dd->name));
412 s = " ";
413 }
414 if (dd->disp[1] == JCL_DISP_DELETE)
415 del |= 1;
416 if (dd->disp[2] == JCL_DISP_DELETE)
417 del |= 2;
418 }
419 sfprintf(jcl->tp, "' \\\n%s", step->command);
420 if (s = step->parm)
421 {
422 if (*s == '(')
423 {
424 arg = strcpy(fmtbuf(strlen(s) + 1), s);
425 while (s = jclparm(&arg))
426 sfprintf(jcl->vp, ",%s", s);
427 if (!(s = sfstruse(jcl->vp)))
428 nospace(jcl, NiL);
429 if (*s == ',')
430 s++;
431 for (t = s + strlen(s); t > s && *--t == ' ';);
432 if (*t == ')')
433 *++t = 0;
434 else if (*t == ',')
435 *t = 0;
436 }
437 sfprintf(jcl->tp, " %s", fmtquote(s, "$'", "'", strlen(s), FMT_SHELL));
438 }
439 for (sym = (Jclsym_t*)dtfirst(step->syms); sym; sym = (Jclsym_t*)dtnext(step->syms, sym))
440 sfprintf(jcl->tp, " %s=%s", fmtquote(sym->name, "'", "'", strlen(sym->name), FMT_SHELL), fmtquote(sym->value, "$'", "'", strlen(sym->value), 0));
441 if (dd = std[0])
442 {
443 sfprintf(jcl->tp, " < %s", dsn(jcl, dd, dd->path, 0));
444 std[3] = 0;
445 }
446 if (dd = std[1])
447 sfprintf(jcl->tp, " > %s", dsn(jcl, dd, dd->path, 0));
448 if (dd = std[2])
449 sfprintf(jcl->tp, " 2> %s", dsn(jcl, dd, dd->path, 0));
450 if (dd = std[3])
451 sfprintf(jcl->tp, " <<%s\n%s%s", fmtquote(dd->dlm, "'", "'", strlen(dd->dlm), 1), dd->here, dd->dlm);
452 sfprintf(jcl->tp, "\ncode=$?\n");
453 if (del)
454 {
455 if (del & 1)
456 {
457 sfprintf(jcl->tp, "if (( ! $code ))\nthen\n\trm -rf");
458 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
459 if (dd->disp[1] == JCL_DISP_DELETE)
460 sfprintf(jcl->tp, " %s", dsn(jcl, dd, dd->path, 0));
461 }
462 if (del & 2)
463 {
464 if (del & 1)
465 sfprintf(jcl->tp, "\nelse\n");
466 else
467 sfprintf(jcl->tp, "\nif (( $code ))\nthen\n");
468 sfprintf(jcl->tp, "\trm -rf");
469 for (dd = (Jcldd_t*)dtfirst(step->dd); dd; dd = (Jcldd_t*)dtnext(step->dd, dd))
470 if (dd->disp[2] == JCL_DISP_DELETE)
471 sfprintf(jcl->tp, " %s", dsn(jcl, dd, dd->path, 0));
472 }
473 sfprintf(jcl->tp, "\nfi\n");
474 }
475 if (jcl->flags & JCL_EXEC)
476 sfprintf(jcl->tp, "exit $code\n");
477 if (!(s = sfstruse(jcl->tp)))
478 nospace(jcl, NiL);
479 }
480 else if ((jcl->flags & (JCL_VERBOSE|JCL_EXEC)) == JCL_VERBOSE)
481 sfprintf(sfstdout, ": %s PROC %s\n", step->name, step->command);
482 else if ((jcl->flags & (JCL_TRACE|JCL_EXEC)) == (JCL_TRACE|JCL_EXEC))
483 sfprintf(sfstderr, "+ : %s PROC %s\n", step->name, step->command);
484 if (step->flags & JCL_PGM)
485 {
486 if ((jcl->flags & (JCL_VERBOSE|JCL_EXEC)) == JCL_VERBOSE)
487 sfputr(sfstdout, s, -1);
488 if (sfsync(sfstdout))
489 {
490 if (jcl->disc->errorf)
491 (*jcl->disc->errorf)(NiL, jcl->disc, 2, "write error");
492 code = -1;
493 break;
494 }
495 if (!(jcl->flags & JCL_EXEC))
496 code = 0;
497 else if (!(cj = coexec(co, s, CO_APPEND, redirect[0].file, redirect[1].file, NiL)) || !(cj = cowait(co, cj, -1)))
498 code = 127;
499 else
500 code = cj->status;
501 }
502 else if (jcl->flags & JCL_RECURSE)
503 code = jclrun(jcl);
504 else
505 code = 0;
506 if (code)
507 jcl->failed++;
508 else
509 jcl->passed++;
510 if (jclrc(jcl, step, code) < 0)
511 break;
512 }
513 }
514 bad:
515 if (jcl->tmp && (jcl->flags & JCL_EXEC))
516 {
517 sfprintf(jcl->tp, "rm -rf %s*\n", jcl->tmp);
518 if (!(s = sfstruse(jcl->tp)))
519 nospace(jcl, NiL);
520 if (cj = coexec(co, s, CO_APPEND, redirect[0].file, redirect[1].file, NiL))
521 cowait(co, cj, -1);
522 }
523 if (jcl == top)
524 {
525 if (jcl->flags & JCL_GDG)
526 {
527 s = "gdgupdate";
528 if (!(jcl->flags & JCL_EXEC))
529 sfputr(sfstdout, s, '\n');
530 else if (cj = coexec(co, s, CO_APPEND, redirect[0].file, redirect[1].file, NiL))
531 cowait(co, cj, -1);
532 }
533 if ((jcl->flags & (JCL_SUBDIR|JCL_VERBOSE)) == (JCL_SUBDIR|JCL_VERBOSE))
534 {
535 if (jcl->flags & JCL_EXEC)
536 sfprintf(sfstdout, "COMPLETED AT %s\n", fmttime("%K", time(NiL)));
537 else
538 sfputr(sfstdout, "date +'COMPLETED AT %K'\n}", '\n');
539 }
540 if ((jcl->flags & (JCL_EXEC|JCL_SUBDIR)) == (JCL_EXEC|JCL_SUBDIR) && chdir("..") && jcl->disc->errorf)
541 (*jcl->disc->errorf)(NiL, jcl->disc, ERROR_SYSTEM|2, "%s: cannot chdir to job subdirectory parent", subdir);
542 if ((jcl->flags & (JCL_EXEC|JCL_VERBOSE)) == (JCL_EXEC|JCL_VERBOSE))
543 {
544 start = tmxgettime() - start;
545 user = (co->user - user) / CO_QUANT;
546 sys = (co->sys - sys) / CO_QUANT;
547 if (pct = (real = (double)start / 1e9))
548 pct = 100.0 * ((user + sys) / pct);
549 sfprintf(sfstdout, "USAGE CPU=%.2f%% REAL=%.2f USR=%.2f SYS=%.2f\n", pct, real, user, sys);
550 }
551 }
552 code = jclclose(jcl);
553 if (scope)
554 jclrc(scope, NiL, code);
555 return code;
556 }
557