/* SHUTDOWN -- Call this to exit gracefully from the whole cl; never return. * Write out any remaining PF_UPDATE'd pfiles by restoring topd to just above * first task unless we are in batch mode, then just flush io and die.. * So that the restor will include the cl's pfile and any other pfiles that * might have been cached or assigned into, we force its topd to be * below its pfile head. See the "pfp < topdp" loop in restor(). * Don't bother with restor'ing if BATCH since we don't want to write out * anything then anyway. */ static void shutdown (void) { float cpu, clk; pr_dumpcache (0, YES); /* flush process cache */ clgflush(); /* flush graphics output */ if (firstask->t_flags & T_BATCH) { iofinish (currentask); if (notify()) { cpu = (float)c_cputime(cpustart) / 1000.; clk = (float)c_clktime(clkstart); fprintf (stderr, "\n[%d] done %.1f %.0m %d%%\n", bkgno, cpu, clk/60., (int)((clk > 0 ? cpu / clk : 0.) * 100.)); } } else { firstask->t_topd = dereference (firstask->t_ltp) + LTASKSIZ; restor (firstask); } yy_startblock (LOG); /* flush and close log */ close_logfile (logfile()); clexit(); }
/* BKG_CLOSE -- Close a bkg job. Called after determining that the job has * terminated. */ static void bkg_close ( int job, /* job ordinal */ int pmsg /* print termination message */ ) { register struct _bkgjob *bk = &jobtable[job-1]; bk->b_clock = c_clktime (bk->b_clock); bk->b_exitcode = c_prcldpr (bk->b_jobno); bk->b_flags &= ~(J_RUNNING|J_SERVICE); if (bk->b_verbose && (pmsg > 1 || (pmsg == 1 && !notify()))) { if (bk->b_exitcode != OK) eprintf ("[%d] exit %d\n", job, bk->b_exitcode); else eprintf ("[%d] done\n", job); } /* Make a logfile entry, saying the background job ended. */ if (keeplog() && log_background()) { char buf[SZ_LINE]; sprintf (buf, "Stop [%d]", job); putlog (0, buf); } }
/* TODAY -- Get todays date as a string, for datestamping the logfile. */ char * today (void) { static char datebuf[64]; c_cnvtime (c_clktime(0L), datebuf, 64); return (datebuf); }
/* BKG_JOBSTATUS -- Print the status of one or more background jobs. * format jobno, elapsed clock time, status, user command, e.g.: * * [1] 1:34 Running command_1 * [2] 14:09 Stopped command_2 * [3] 1:34 +Done command_3 * [4] 1:34 Exit 23 command_4 * * A job will remain in the job table until another job is submitted which uses * the same slot. */ void bkg_jobstatus ( FILE *fp, /* output file */ int job /* job(s) */ ) { register struct _bkgjob *bk; register int j, n, ch; register char *ip; long seconds; char *outstr = NULL; bkg_update (1); for (bk=jobtable, j=1; j <= NBKG; j++, bk++) if ((job == 0 && bk->b_jobno) || job == j) { /* Print jobno. */ fprintf (fp, " [%d] ", j); /* If the clock is still running b_clock contains the start * time. If the job terminated it contains the elapsed time * at job termination. */ if (busy(j)) seconds = c_clktime (bk->b_clock); else seconds = bk->b_clock; fprintf (fp, "%6.0m ", (float)seconds / 60.0); fputc ((j == lastjobno) ? '+' : ' ', fp); /* Print job status. */ if (busy(j)) { if (bk->b_flags & J_SERVICE) outstr = "Stopped"; else outstr = "Running"; } else if (bk->b_flags & J_KILLED) { outstr = "Killed"; } else if (bk->b_exitcode == OK) { outstr = "Done"; } else sprintf (outstr, "Exit %d", bk->b_exitcode); fprintf (fp, "%-10s", outstr); /* Finally, print user command followed by newline. */ n = c_envgeti ("ttyncols") - (8 + 8 + 10) - 1; ip = bk->b_cmd; while (--n >= 0 && (ch = *ip++) != EOS) if (ch == '\n' || ch == '\t') fputc (' ', fp); else fputc (ch, fp); fputc ('\n', fp); } }
/* C_MAIN -- Called by the SPP procedure in cl.x to fire up the CL. * In effect we are chained to the IRAF Main, being called immediately after * the file system, etc. is initialized. When we exit we signal that the * interpreter be skipped, proceeding directly to process shutdown. */ int c_main ( PKCHAR *cmd /* host command line */ ) { XINT bp; /* Save the setjmp vector of the IRAF Main for restoration at clexit * time. We need to intercept all errors and do error recovery * ourselves during normal execution, but when the CL exits we are * not prepared to deal with errors occuring during shutdown. */ XMJBUF (&bp); jumpcom = (long *)&Memc[bp]; cl_amovi ((int *)jumpcom, (int *)jmp_save, LEN_JUMPBUF); /* Init clexit() in case we have to panic stop. */ if (setjmp (jmp_clexit)) goto exit_; /* Set up dictionary and catch signals. If we are background, read in * file and jump right into run, else hand craft first task. Die if * these fail. */ startup (); if (setjmp(child_startup)) { cpustart = c_cputime (0L); clkstart = c_clktime (0L); execute (BACKGROUND); } else { login ((char *) cmd); execute (FOREGROUND); logout(); execute (FOREGROUND); } shutdown(); exit_: /* Return to the IRAF Main. The PR_EXIT code commands the main to * skip the interpreter loop and shutdown. Restore the error * jump vector in the IRAF Main so that it can handle errors occuring * during shutdown; we are turning control back over to the Main. * This is ugly, but the real problem is the jump vectors. There * seems to be no alternative to this sort of thing... */ cl_amovi ((int *)jmp_save, (int *)jumpcom, LEN_JUMPBUF); return (PR_EXIT | (logout_status << 1)); }
/* BKG_SPAWN -- Spawn a new background job. Called by main() when we have * seen an '&'. */ void bkg_spawn ( char *cmd /* command entered by user to spawn job */ ) { register struct _bkgjob *bk; register int jobno, stat; char clprocess[SZ_PATHNAME]; char *wbkgfile(); char *bkgfile; /* Find first unused slot in a circular search. */ bkg_update (1); jobno = (lastjobno == NBKG) ? 1 : lastjobno + 1; while (jobno != lastjobno) { if (!busy (jobno)) break; if (jobno++ >= NBKG) jobno = 1; } if (jobno == lastjobno) cl_error (E_UERR, "no more background job slots"); /* Write bkgfile. Delete any dreg bkg communication files. */ bkg_delfiles (jobno); bkgfile = wbkgfile (jobno, cmd, NULL); /* Spawn bkg job. */ sprintf (clprocess, "%s%s", CLDIR, CLPROCESS); intr_disable(); jobtable[jobno-1].b_jobno = stat = c_propdpr (findexe (firstask->t_curpack, clprocess), bkgfile, bkgmsg); if (stat == NULL) { c_delete (bkgfile); intr_enable(); cl_error (E_IERR, "cannot spawn background CL"); } else { bk = &jobtable[jobno-1]; bk->b_flags = J_RUNNING; bk->b_clock = c_clktime (0L); bk->b_verbose = 2; strncpy (bk->b_cmd, cmd, SZ_CMD); *(bk->b_cmd+SZ_CMD) = EOS; intr_enable(); } eprintf ("[%d]\n", lastjobno = jobno); /* Make a logfile entry, saying we started the background job. */ if (keeplog() && log_background()) { char buf[SZ_LINE]; sprintf (buf, "Start [%d]", jobno); putlog (0, buf); } }
/* EXECUTE -- Each loop corresponds to an exec in the interpreted code. * This occurs when a script task or process is ready to run. In background * mode, we skip the preliminaries and jump right in and interpret the * compiled code. */ static void execute (int mode) { int parsestat; XINT old_parhead; char *curcmd(); alldone = 0; gologout = 0; if (mode == BACKGROUND) { if (setjmp (jumpcom)) onerr(); goto bkg; } /* Called when control stack contains only the firsttask. ONEOF sets * alldone true when eof/bye is seen and currentask=firstask, * terminating the loop and returning to main. */ do { /* Bkg_update() checks for blocked or finished bkg jobs and prints * a message if it finds one. This involves one or more access() * calls so don't call it more than every 5 seconds. The errenv * jump vector is used by cl_error() for error restart. The JUMPCOM * vector is used to intercept system errors which would otherwise * restart the CL. */ if (currentask->t_flags & T_INTERACTIVE) { static long last_clktime; if (c_clktime (last_clktime) > BKG_QUANTUM) { last_clktime = c_clktime (0L); bkg_update (1); } validerrenv = 1; setjmp (errenv); ninterrupts = 0; if (setjmp (jumpcom)) onerr(); } else if (!(currentask->t_flags & T_SCRIPT)) setjmp (intenv); pc = currentask->t_bascode; currentask->t_topd = topd; currentask->t_topcs = topcs; recursion = 0; errlev = 0; c_erract (OK); yeof = 0; /* In the new CL the parser needs to know more about parameters * than before. Hence param files may be read in during parsing. * Since we discard the dictionary after parsing we must unlink * these param files, and re-read them when the * program is run. This is inefficient but appears to work. */ old_parhead = parhead; if (gologout) yeof++; else { yy_startblock (LOG); /* start new history blk */ parsestat = yyparse(); /* parse command block */ topd = currentask->t_topd; /* discard addconst()'s */ topcs = currentask->t_topcs; /* discard compiler temps */ parhead = old_parhead; /* forget param files. */ if (parsestat != 0) cl_error (E_IERR, "parser gagged"); } if (dobkg) { bkg_spawn (curcmd()); } else { bkg: if (yeof) oneof(); /* restores previous task */ else { /* set stack above pc, point pc back to code */ topos = basos = pc - 1; pc = currentask->t_bascode; } if (!alldone) run(); /* run code starting at pc */ } } until (alldone); }