3 * Dump custom Lisp images for faster script execution
5 * (c) 2020 Mark Wooding
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Runlisp, a tool for invoking Common Lisp scripts.
12 * Runlisp is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * Runlisp is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with Runlisp. If not, see <https://www.gnu.org/licenses/>.
26 /*----- Header files ---------------------------------------------------------*/
43 #include <sys/select.h>
53 /*----- Static data -------------------------------------------------------*/
55 /* The state required to break an output stream from a subprocess into lines
56 * so we can prefix them appropriately. Once our process starts, the `buf'
57 * points to a buffer of `MAXLINE' bytes. This is arranged as a circular
58 * buffer, containing `len' bytes starting at offset `off', and wrapping
59 * around to the start of the buffer if it runs off the end.
61 * The descriptor `fd' is reset to -1 after it's seen end-of-file.
64 int fd
; /* our file descriptor (or -1) */
65 char *buf
; /* line buffer, or null */
66 unsigned off
, len
; /* offset */
68 #define MAXLINE 16384u /* maximum acceptable line length */
70 /* Job-state constants. */
72 JST_READY
, /* not yet started */
73 JST_DELETE
, /* just delete the image file */
74 JST_RUN
, /* currently running */
75 JST_DEAD
, /* process exited */
79 /* The state associated with an image-dumping job. */
81 struct treap_node _node
; /* treap intrusion */
82 struct job
*next
; /* next job in whichever list */
83 unsigned op
; /* operation (`JOP_...') */
84 struct argv av
; /* argument vector to execute */
85 char *imgnew
, *imgout
; /* staging and final output files */
86 unsigned st
; /* job state (`JST_...') */
87 FILE *log
; /* log output file (`stdout'?) */
88 pid_t kid
; /* process id of child (or -1) */
89 int exit
; /* exit status from child */
90 struct linebuf out
, err
; /* line buffers for stdout, stderr */
92 #define JOB_NAME(job) TREAP_NODE_KEY(job)
93 #define JOB_NAMELEN(job) TREAP_NODE_KEYLEN(job)
95 static struct treap jobs
= TREAP_INIT
, /* Lisp systems seen so far */
96 good
= TREAP_INIT
; /* files ok to be in image dir */
97 static struct job
/* lists of jobs by state */
98 *job_ready
, **job_ready_tail
= &job_ready
, /* some have tail pointers... */
99 *job_delete
, **job_delete_tail
= &job_delete
,
100 *job_run
, *job_dead
; /* ... and some don't */
101 static unsigned nrun
, maxrun
= 1; /* running and maximum job counts */
102 static int rc
= 0; /* code that we should return */
103 static int nullfd
; /* file descriptor for `/dev/null' */
104 static const char *tmpdir
; /* temporary directory path */
106 static int sig_pipe
[2] = { -1, -1 }; /* pipe for reporting signals */
107 static sigset_t caught
, pending
; /* signals we catch; have caught */
108 static int sigloss
= -1; /* signal that caused us to lose */
110 static unsigned flags
= 0; /* flags for the application */
111 #define AF_BOGUS 0x0001u /* invalid comand-line syntax */
112 #define AF_SETCONF 0x0002u /* explicit configuration */
113 #define AF_DRYRUN 0x0004u /* don't actually do it */
114 #define AF_ALL 0x0008u /* dump all known Lisps */
115 #define AF_FORCE 0x0010u /* dump even if images exist */
116 #define AF_CHECKINST 0x0020u /* check Lisp exists before dump */
117 #define AF_REMOVE 0x0040u /* remove selected Lisp images */
118 #define AF_CLEAN 0x0080u /* remove other Lisp images */
119 #define AF_JUNK 0x0100u /* remove unrecognized files */
121 /*----- Miscellany --------------------------------------------------------*/
123 /* Report a (printf(3)-style) message MSG, and remember to fail later. */
124 static PRINTF_LIKE(1, 2) void bad(const char *msg
, ...)
125 { va_list ap
; va_start(ap
, msg
); vmoan(msg
, ap
); va_end(ap
); rc
= 127; }
127 /*----- File utilities ----------------------------------------------------*/
129 /* Main recursive subroutine for `recursive_delete'.
131 * The string DD currently contains the pathname of a directory, without a
132 * trailing `/' (though there is /space/ for a terminating zero or whatever).
133 * Recursively delete all of the files and directories within it. Appending
134 * further text to DD is OK, but clobbering the characters which are there
135 * already isn't allowed.
137 static void recursive_delete_(struct dstr
*dd
)
143 /* Open the directory. */
144 dd
->p
[n
] = 0; dir
= opendir(dd
->p
);
146 lose("failed to open directory `%s' for cleanup: %s",
147 dd
->p
, strerror(errno
));
149 /* We'll need to build pathnames for the files inside the directory, so add
150 * the separating `/' character. Remember the length of this prefix
151 * because this is the point we'll be rewinding to for each filename we
156 /* Now go through each file in turn. */
159 /* Get a filename. If we've run out then we're done. Skip the special
160 * `.' and `..' entries.
162 d
= readdir(dir
); if (!d
) break;
163 if (d
->d_name
[0] == '.' && (!d
->d_name
[1] ||
164 (d
->d_name
[1] == '.' && !d
->d_name
[2])))
167 /* Rewind the string offset and append the new filename. */
168 dd
->len
= n
; dstr_puts(dd
, d
->d_name
);
170 /* Try to delete it the usual way. If it was actually a directory then
171 * recursively delete it instead. (We could lstat(2) it first, but this
172 * should be at least as quick to identify a directory, and it'll save a
173 * lstat(2) call in the (common) case that it's not a directory.
176 else if (errno
== EISDIR
) recursive_delete_(dd
);
177 else lose("failed to delete file `%s': %s", dd
->p
, strerror(errno
));
180 /* We're done. Try to delete the directory. (It's possible that there was
181 * some problem with enumerating the directory, but we'll ignore that: if
182 * it matters then the directory won't be empty and the rmdir(2) will
188 lose("failed to delete directory `%s': %s", dd
->p
, strerror(errno
));
191 /* Recursively delete the thing named PATH. */
192 static void recursive_delete(const char *path
)
194 struct dstr d
= DSTR_INIT
;
195 dstr_puts(&d
, path
); recursive_delete_(&d
); dstr_release(&d
);
198 /* Configure a file descriptor FD.
200 * Set its nonblocking state to NONBLOCK and close-on-exec state to CLOEXEC.
201 * In both cases, -1 means to leave it alone, zero means to turn it off, and
202 * any other nonzero value means to turn it on.
204 static int configure_fd(const char *what
, int fd
, int nonblock
, int cloexec
)
208 if (nonblock
!= -1) {
209 fl
= fcntl(fd
, F_GETFL
); if (fl
< 0) goto fail
;
210 if (nonblock
) nfl
= fl
| O_NONBLOCK
;
211 else nfl
= fl
&~O_NONBLOCK
;
212 if (fl
!= nfl
&& fcntl(fd
, F_SETFL
, nfl
)) goto fail
;
216 fl
= fcntl(fd
, F_GETFD
); if (fl
< 0) goto fail
;
217 if (cloexec
) nfl
= fl
| FD_CLOEXEC
;
218 else nfl
= fl
&~FD_CLOEXEC
;
219 if (fl
!= nfl
&& fcntl(fd
, F_SETFD
, nfl
)) goto fail
;
225 bad("failed to configure %s descriptor: %s", what
, strerror(errno
));
229 /* Create a temporary directory and remember where we put it. */
230 static void set_tmpdir(void)
232 struct dstr d
= DSTR_INIT
;
236 /* Start building the path name. Remember the length: we'll rewind to
237 * here and try again if our first attempt doesn't work.
239 dstr_putf(&d
, "%s/runlisp.%d.", my_getenv("TMPDIR", "/tmp"), getpid());
242 /* Keep trying until it works. */
245 /* Build a complete name. */
246 d
.len
= n
; dstr_putf(&d
, "%d", rand());
248 /* Try to create the directory. If it worked, we're done. If it failed
249 * with `EEXIST' then we'll try again for a while, but give up it it
250 * doesn't look like we're making any progress. If it failed for some
251 * other reason then there's probably not much hope so give up.
253 if (!mkdir(d
.p
, 0700)) break;
254 else if (errno
!= EEXIST
)
255 lose("failed to create temporary directory `%s': %s",
256 d
.p
, strerror(errno
));
257 else if (++i
>= 32) {
258 d
.len
= n
; dstr_puts(&d
, "???");
259 lose("failed to create temporary directory `%s': too many attempts",
264 /* Remember the directory name. */
265 tmpdir
= xstrndup(d
.p
, d
.len
); dstr_release(&d
);
268 /*----- Signal handling ---------------------------------------------------*/
270 /* Forward reference into job management. */
271 static void reap_children(void);
273 /* Clean things up on exit.
275 * Currently this just means to delete the temporary directory if we've made
278 static void cleanup(void)
279 { if (tmpdir
) { recursive_delete(tmpdir
); tmpdir
= 0; } }
281 /* Check to see whether any signals have arrived, and do the sensible thing
284 static void check_signals(void)
290 /* Ensure exclusive access to the signal-handling machinery, drain the
291 * signal pipe, and take a copy of the set of caught signals.
293 sigprocmask(SIG_BLOCK
, &caught
, &old
);
294 pend
= pending
; sigemptyset(&pending
);
296 n
= read(sig_pipe
[0], buf
, sizeof(buf
));
297 if (!n
) lose("(internal) signal pipe closed!");
300 if (errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
)
301 lose("failed to read signal pipe: %s", strerror(errno
));
302 sigprocmask(SIG_SETMASK
, &old
, 0);
304 /* Check for each signal of interest to us.
306 * Interrupty signals just set `sigloss' -- the `run_jobs' loop will know
307 * to unravel everything if this happens. If `SIGCHLD' happened, then
308 * check on job process status.
310 if (sigismember(&pend
, SIGINT
)) sigloss
= SIGINT
;
311 else if (sigismember(&pend
, SIGHUP
)) sigloss
= SIGHUP
;
312 else if (sigismember(&pend
, SIGTERM
)) sigloss
= SIGTERM
;
313 if (sigismember(&pend
, SIGCHLD
)) reap_children();
316 /* The actual signal handler.
318 * Set the appropriate signal bit in `pending', and a byte (of any value)
319 * down the signal pipe to wake up the select(2) loop.
321 static void handle_signal(int sig
)
326 /* Ensure exclusive access while we fiddle with the `caught' set. */
327 sigprocmask(SIG_BLOCK
, &caught
, &old
);
328 sigaddset(&pending
, sig
);
329 sigprocmask(SIG_SETMASK
, &old
, 0);
331 /* Wake up the select(2) loop. If this fails, there's not a lot we can do
334 DISCARD(write(sig_pipe
[1], &x
, 1));
337 /* Install our signal handler to catch SIG.
339 * If `SIGF_IGNOK' is set in F then don't trap the signal if it's currently
340 * ignored. (This is used for signals like `SIGINT', which usually should
341 * interrupt us; but if the caller wants us to ignore them, we should do as
344 * WHAT describes the signal, for use in diagnostic messages.
346 #define SIGF_IGNOK 1u
347 static void set_signal_handler(const char *what
, int sig
, unsigned f
)
349 struct sigaction sa
, sa_old
;
351 sigaddset(&caught
, sig
);
354 if (sigaction(sig
, 0, &sa_old
)) goto fail
;
355 if (sa_old
.sa_handler
== SIG_IGN
) return;
358 sa
.sa_handler
= handle_signal
;
359 sigemptyset(&sa
.sa_mask
);
360 sa
.sa_flags
= SA_NOCLDSTOP
;
361 if (sigaction(sig
, &sa
, 0)) goto fail
;
366 lose("failed to set %s signal handler: %s", what
, strerror(errno
));
369 /*----- Line buffering ----------------------------------------------------*/
371 /* Find the next newline in the line buffer BUF.
373 * The search starts at `BUF->off', and potentially covers the entire buffer
374 * contents. Set *LINESZ_OUT to the length of the line, in bytes. (Callers
375 * must beware that the text of the line may wrap around the ends of the
376 * buffer.) Return zero if we found a newline, or nonzero if the search
379 static int find_newline(struct linebuf
*buf
, size_t *linesz_out
)
383 if (buf
->off
+ buf
->len
<= MAXLINE
) {
384 /* The buffer contents is in one piece. Just search it. */
386 nl
= memchr(buf
->buf
+ buf
->off
, '\n', buf
->len
);
387 if (nl
) { *linesz_out
= (nl
- buf
->buf
) - buf
->off
; return (0); }
390 /* The buffer contents is in two pieces. We must search both of them. */
392 nl
= memchr(buf
->buf
+ buf
->off
, '\n', MAXLINE
- buf
->off
);
393 if (nl
) { *linesz_out
= (nl
- buf
->buf
) - buf
->off
; return (0); }
394 nl
= memchr(buf
->buf
, '\n', buf
->len
- (MAXLINE
- buf
->off
));
396 { *linesz_out
= (nl
- buf
->buf
) + (MAXLINE
- buf
->off
); return (0); }
402 /* Write a completed line out to the JOB's log file.
404 * The line starts at BUF->off, and continues for N bytes, not including the
405 * newline (which, in fact, might not exist at all). Precede the actual text
406 * of the line with the JOB's name, and the MARKER character, and follow it
407 * with the TAIL text (which should include an actual newline character).
409 static void write_line(struct job
*job
, struct linebuf
*buf
,
410 size_t n
, char marker
, const char *tail
)
412 fprintf(job
->log
, "%-13s %c ", JOB_NAME(job
), marker
);
413 if (buf
->off
+ n
<= MAXLINE
)
414 fwrite(buf
->buf
+ buf
->off
, 1, n
, job
->log
);
416 fwrite(buf
->buf
+ buf
->off
, 1, MAXLINE
- buf
->off
, job
->log
);
417 fwrite(buf
->buf
, 1, n
- (MAXLINE
- buf
->off
), job
->log
);
419 fputs(tail
, job
->log
);
422 /* Collect output lines from JOB's process and write them to the log.
424 * Read data from BUF's file descriptor. Output complete (or overlong) lines
425 * usng `write_line'. On end-of-file, output any final incomplete line in
426 * the same way, close the descriptor, and set it to -1.
428 static void prefix_lines(struct job
*job
, struct linebuf
*buf
, char marker
)
430 struct iovec iov
[2]; int niov
;
434 /* Read data into the buffer. This fancy dance with readv(2) is probably
437 * We can't have BUF->len = MAXLINE because we'd have flushed out a
438 * maximum-length buffer as an incomplete line last time.
440 assert(buf
->len
< MAXLINE
);
442 iov
[0].iov_base
= buf
->buf
+ buf
->len
;
443 iov
[0].iov_len
= MAXLINE
- buf
->len
;
445 } else if (buf
->off
+ buf
->len
>= MAXLINE
) {
446 iov
[0].iov_base
= buf
->buf
+ buf
->off
+ buf
->len
- MAXLINE
;
447 iov
[0].iov_len
= MAXLINE
- buf
->len
;
450 iov
[0].iov_base
= buf
->buf
+ buf
->off
+ buf
->len
;
451 iov
[0].iov_len
= MAXLINE
- (buf
->off
+ buf
->len
);
452 iov
[1].iov_base
= buf
->buf
;
453 iov
[1].iov_len
= buf
->off
;
456 n
= readv(buf
->fd
, iov
, niov
);
459 /* If there's no data to read after all then just move on. Otherwise we
462 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) return;
463 lose("failed to read job `%s' output stream: %s",
464 JOB_NAME(job
), strerror(errno
));
467 /* Include the new material in the buffer length, and write out any
468 * complete lines we find.
471 while (!find_newline(buf
, &linesz
)) {
472 write_line(job
, buf
, linesz
, marker
, "\n");
473 buf
->len
-= linesz
+ 1;
474 buf
->off
+= linesz
+ 1; if (buf
->off
>= MAXLINE
) buf
->off
-= MAXLINE
;
478 /* If there's nothing left then we might as well reset the buffer offset
479 * to the start of the buffer.
482 else if (buf
->len
== MAXLINE
) {
483 /* We've filled the buffer with stuff that's not a whole line. Flush it
486 write_line(job
, buf
, MAXLINE
, marker
, " [...]\n");
487 buf
->off
= buf
->len
= 0;
491 /* We've hit end-of-file. Close the stream, and write out any
492 * unterminated partial line.
494 close(buf
->fd
); buf
->fd
= -1;
496 write_line(job
, buf
, buf
->len
, marker
, " [missing final newline]\n");
500 /*----- Job management ----------------------------------------------------*/
502 /* Consider a Lisp system description and maybe add a job to the right queue.
504 * The Lisp system is described by the configuration section SECT. Most of
505 * the function is spent on inspecting this section for suitability and
506 * deciding what to do about it.
508 * The precise behaviour depends on F, which should be the bitwise-OR of a
509 * `JQ_...' constant and zero or more flags, as follows.
511 * * The bits covered by `JMASK_QUEUE' identify which queue the job should
512 * be added to if the section defines a cromulent Lisp system:
514 * -- `JQ_NONE' -- don't actually make a job at all;
515 * -- `JQ_READY' -- add the Lisp to the `job_ready' queue, so we'll; or
516 * -- `JQ_DELETE' -- add the Lisp to the `job_delete' queue.
518 * * `JF_PICKY': The user identified this Lisp system explicitly, so
519 * complain if the configuration section doesn't look right. This is
520 * clear if the caller is just enumerating all of the configuration
521 * sections: without this feature, we'd be checking everything twice,
522 * which (a) is inefficient, and -- more importantly -- (b) could lead to
523 * problems if the two checks are inconsistent.
525 * * `JF_CHECKINST': Ignore this Lisp if `AF_CHECKINST' is set and it's not
526 * actually installed. (This is usually set for `JQ_READY' calls, so
527 * that we don't try to dump Lisps which aren't there, but clear for
528 * `JQ_DELETE' calls so that we clear out Lisps which have gone away.)
530 * * `JF_CHECKEXIST': Ignore this Lisp if its image file already exists.
532 * * `JF_NOTICE': Record the Lisp's image basename in the `good' treap so
533 * that we can identify everything else we find in the image directory as
536 #define JMASK_QUEUE 3u /* which queue to add good Lisp to */
537 #define JQ_NONE 0u /* don't add to any queue */
538 #define JQ_READY 1u /* `job_ready' */
539 #define JQ_DELETE 2u /* `job_delete' */
540 #define JF_PICKY 4u /* lose if section isn't Lisp defn */
541 #define JF_CHECKINST 8u /* maybe check Lisp is installed */
542 #define JF_CHECKEXIST 16u /* skip if image already exists */
543 #define JF_NOTICE 32u /* record Lisp's image basename */
545 #define JADD_NAMED (JQ_READY | JF_PICKY | JF_CHECKINST)
546 #define JADD_DEFAULT (JQ_READY | JF_CHECKINST)
547 #define JADD_CLEANUP (JQ_DELETE)
548 #define JADD_NOTICE (JQ_NONE)
549 static void add_job(unsigned f
, struct config_section
*sect
)
552 struct job
*job
, ***tail
;
553 struct treap_path path
, jobpath
;
554 struct config_var
*dumpvar
, *cmdvar
, *imgvar
;
555 struct treap_node
*n
;
556 struct dstr d
= DSTR_INIT
;
557 struct argv av
= ARGV_INIT
;
558 char *imgnew
= 0, *imgout
= 0;
562 /* We'll want the section's name for all sorts of things. */
563 name
= CONFIG_SECTION_NAME(sect
);
564 len
= CONFIG_SECTION_NAMELEN(sect
);
566 /* Check to see whether this Lisp system is already queued up.
568 * We'll get around to adding the new job node to the treap right at the
569 * end, so use a separate path object to keep track of where to put it.
571 job
= treap_probe(&jobs
, name
, len
, &jobpath
);
573 if ((f
&JF_PICKY
) && verbose
>= 1)
574 moan("ignoring duplicate Lisp `%s'", JOB_NAME(job
));
578 /* Check that the section defines a Lisp, and that it can be dumped.
580 * It's not obvious that this is right. Maybe there should be some
581 * additional flag so that we don't check dumpability if we're planning to
582 * delete the image. But it /is/ right: since the thing which tells us
583 * whether we can dump is that the section tells us the image's name, if
584 * it can't be dumped then we won't know what file to delete! So we have
587 if (!config_find_var(&config
, sect
, CF_INHERIT
, "run-script")) {
588 if (f
&JF_PICKY
) lose("unknown Lisp implementation `%s'", name
);
589 else if (verbose
>= 3) moan("skipping non-Lisp section `%s'", name
);
592 imgvar
= config_find_var(&config
, sect
, CF_INHERIT
, "image-file");
595 lose("Lisp implementation `%s' doesn't use custom images", name
);
596 else if (verbose
>= 3)
597 moan("skipping Lisp `%s': no custom image support", name
);
601 /* Check that the other necessary variables are present. */
602 dumpvar
= config_find_var(&config
, sect
, CF_INHERIT
, "dump-image");
604 lose("variable `dump-image' not defined for Lisp `%s'", name
);
605 cmdvar
= config_find_var(&config
, sect
, CF_INHERIT
, "command");
607 lose("variable `command' not defined for Lisp `%s'", name
);
609 /* Build the job's command line. */
610 config_subst_split_var(&config
, sect
, dumpvar
, &av
);
612 lose("empty `dump-image' command for Lisp implementation `%s'", name
);
614 /* If we're supposed to check that the Lisp exists before proceeding then
615 * do that. There are /two/ commands to check: the basic Lisp command,
616 * /and/ the command to actually do the dumping, which might not be the
617 * same thing. (Be careful not to check the same command twice, though,
618 * because that would cause us to spam the user with redundant
621 if ((f
&JF_CHECKINST
) && (flags
&AF_CHECKINST
)) {
623 fef
= (verbose
>= 3 ? FEF_VERBOSE
: 0);
624 config_subst_var(&config
, sect
, cmdvar
, &d
);
625 if (!found_in_path_p(d
.p
, fef
)) {
627 moan("skipping Lisp `%s': can't find Lisp command `%s'",
631 if (STRCMP(d
.p
, !=, av
.v
[0]) && !found_in_path_p(av
.v
[0], fef
)) {
632 moan("skipping Lisp `%s': can't find dump command `%s'",
638 /* If we're supposed to, then notice that this is the name of a good Lisp
642 dstr_reset(&d
); config_subst_var(&config
, sect
, imgvar
, &d
);
643 n
= treap_probe(&good
, d
.p
, d
.len
, &path
);
645 n
= xmalloc(sizeof(*n
));
646 treap_insert(&good
, &path
, n
, d
.p
, d
.len
);
650 /* Collect the output image file names. */
652 config_subst_string_alloc(&config
, sect
, "<internal>", "${@image-new}");
654 config_subst_string_alloc(&config
, sect
, "<internal>", "${@image-out}");
656 /* If we're supposed to check whether the image file exists, then we should
659 if ((f
&JF_CHECKEXIST
) && !(flags
&AF_FORCE
)) {
660 if (!access(imgout
, F_OK
)) {
662 moan("skipping Lisp `%s': image `%s' already exists", name
, imgout
);
663 f
= (f
&~JMASK_QUEUE
) | JQ_NONE
;
667 /* All preflight checks complete. Build the job and hook it onto the end
668 * of the list. (Steal the command-line vector so that we don't try to
669 * free it during cleanup.)
671 switch (f
&JMASK_QUEUE
) {
672 case JQ_NONE
: tail
= 0; break;
673 case JQ_READY
: tail
= &job_ready_tail
; break;
674 case JQ_DELETE
: tail
= &job_delete_tail
; break;
677 job
= xmalloc(sizeof(*job
));
679 job
->kid
= -1; job
->log
= 0;
680 job
->out
.fd
= -1; job
->out
.buf
= 0;
681 job
->err
.fd
= -1; job
->err
.buf
= 0;
682 job
->av
= av
; argv_init(&av
);
683 job
->imgnew
= imgnew
; job
->imgout
= imgout
; imgnew
= imgout
= 0;
684 treap_insert(&jobs
, &jobpath
, &job
->_node
, name
, len
);
685 if (tail
) { **tail
= job
; *tail
= &job
->next
; }
688 /* All done. Cleanup time. */
689 for (i
= 0; i
< av
.n
; i
++) free(av
.v
[i
]);
690 free(imgnew
); free(imgout
);
691 dstr_release(&d
); argv_release(&av
);
694 /* As `add_job' above, but look the Lisp implementation up by name.
696 * The flags passed to `add_job' are augmented with `JF_PICKY' because this
697 * is an explicitly-named Lisp implementation.
699 static void add_named_job(unsigned f
, const char *name
, size_t len
)
701 struct config_section
*sect
;
703 sect
= config_find_section_n(&config
, 0, name
, len
);
704 if (!sect
) lose("unknown Lisp implementation `%.*s'", (int)len
, name
);
705 add_job(f
| JF_PICKY
, sect
);
708 /* Free the JOB and all the resources it holds.
710 * Close the pipes; kill the child process. Everything must go.
712 static void release_job(struct job
*job
)
717 if (job
->kid
> 0) kill(job
->kid
, SIGKILL
); /* ?? */
718 if (job
->log
&& job
->log
!= stdout
) fclose(job
->log
);
719 free(job
->imgnew
); free(job
->imgout
);
720 for (i
= 0; i
< job
->av
.n
; i
++) free(job
->av
.v
[i
]);
721 argv_release(&job
->av
);
722 free(job
->out
.buf
); if (job
->out
.fd
>= 0) close(job
->out
.fd
);
723 free(job
->err
.buf
); if (job
->err
.fd
>= 0) close(job
->err
.fd
);
724 j
= treap_remove(&jobs
, JOB_NAME(job
), JOB_NAMELEN(job
)); assert(j
== job
);
728 /* Do all the necessary things when JOB finishes (successfully or not).
730 * Eventually the job is freed (using `release_job').
732 static void finish_job(struct job
*job
)
738 /* Start a final line to the job log describing its eventual fate.
740 * This is where we actually pick apart the exit status. Set `ok' if it
741 * actually succeeded, because that's all anything else cares about.
743 fprintf(job
->log
, "%-13s > ", JOB_NAME(job
));
744 if (WIFEXITED(job
->exit
)) {
745 if (!WEXITSTATUS(job
->exit
))
746 { fputs("completed successfully\n", job
->log
); ok
= 1; }
748 fprintf(job
->log
, "failed with exit status %d\n",
749 WEXITSTATUS(job
->exit
));
750 } else if (WIFSIGNALED(job
->exit
))
751 fprintf(job
->log
, "killed by signal %d (%s%s)", WTERMSIG(job
->exit
),
752 #if defined(HAVE_STRSIGNAL)
753 strsignal(WTERMSIG(job
->exit
)),
754 #elif defined(HAVE_DECL_SYS_SIGLIST)
755 sys_siglist
[WTERMSIG(job
->exit
)],
760 WCOREDUMP(job
->exit
) ?
"; core dumped" :
764 fprintf(job
->log
, "exited with incomprehensible status %06o\n",
767 /* If it succeeded, then try to rename the completed image file into place.
769 * If that caused trouble then mark the job as failed after all.
771 if (ok
&& rename(job
->imgnew
, job
->imgout
)) {
772 fprintf(job
->log
, "%-13s > failed to rename Lisp `%s' "
773 "output image `%s' to `%s': %s",
774 JOB_NAME(job
), JOB_NAME(job
),
775 job
->imgnew
, job
->imgout
, strerror(errno
));
779 /* If the job failed and we're being quiet then write out the log that we
782 if (!ok
&& verbose
< 2) {
785 n
= fread(buf
, 1, sizeof(buf
), job
->log
);
786 if (n
) fwrite(buf
, 1, n
, stdout
);
787 if (n
< sizeof(buf
)) break;
791 /* Also make a node to stderr about what happened. (Just to make sure
792 * that we've gotten someone's attention.)
794 if (!ok
) bad("failed to dump Lisp `%s'", JOB_NAME(job
));
796 /* Finally free the job control block. */
800 /* Called after `SIGCHLD': collect exit statuses and mark jobs as dead. */
801 static void reap_children(void)
803 struct job
*job
, **link
;
809 /* Collect a child exit status. If there aren't any more then we're
812 kid
= waitpid(0, &st
, WNOHANG
);
815 /* Try to find a matching job. If we can't, then we should just ignore
818 for (link
= &job_run
; (job
= *link
); link
= &job
->next
)
819 if (job
->kid
== kid
) goto found
;
823 /* Mark the job as dead, save its exit status, and move it into the dead
826 job
->exit
= st
; job
->st
= JST_DEAD
; job
->kid
= -1; nrun
--;
827 *link
= job
->next
; job
->next
= job_dead
; job_dead
= job
;
830 /* If there was a problem with waitpid(2) then report it. */
831 if (kid
< 0 && errno
!= ECHILD
)
832 lose("failed to collect child process exit status: %s", strerror(errno
));
835 /* Execute the handler for some JOB. */
836 static NORETURN
void job_child(struct job
*job
)
839 !(flags
&AF_CHECKINST
) && verbose
>= 2 ? TEF_VERBOSE
: 0);
840 moan("failed to run `%s': %s", job
->av
.v
[0], strerror(errno
));
844 /* Start up jobs while there are (a) jobs to run and (b) slots to run them
847 static void start_jobs(void)
849 struct dstr d
= DSTR_INIT
;
850 int p_out
[2], p_err
[2];
854 /* Keep going until either we run out of jobs, or we've got enough running
857 while (job_ready
&& nrun
< maxrun
) {
859 /* Set things up ready. If things go wrong, we need to know what stuff
860 * needs to be cleaned up.
862 job
= job_ready
; job_ready
= job
->next
;
863 p_out
[0] = p_out
[1] = p_err
[0] = p_err
[1] = -1;
865 /* If we're not actually going to do anything, now is the time to not do
868 if (flags
&AF_DRYRUN
) {
869 if (try_exec(&job
->av
,
871 (verbose
>= 2 && !(flags
&AF_CHECKINST
) ?
874 else if (verbose
>= 2)
875 printf("%-13s > not dumping `%s' (dry run)\n",
876 JOB_NAME(job
), JOB_NAME(job
));
881 /* Make a temporary subdirectory for this job to use. */
882 dstr_reset(&d
); dstr_putf(&d
, "%s/%s", tmpdir
, JOB_NAME(job
));
883 if (mkdir(d
.p
, 0700)) {
884 bad("failed to create working directory for job `%s': %s",
885 JOB_NAME(job
), strerror(errno
));
889 /* Create the job's log file. If we're being verbose then that's just
890 * our normal standard output -- /not/ stderr: it's likely that users
891 * will want to pipe this stuff through a pager or something, and that'll
892 * be easier if we use stdout. Otherwise, make a file in the temporary
898 dstr_puts(&d
, "/log"); job
->log
= fopen(d
.p
, "w+");
900 lose("failed to open log file `%s': %s", d
.p
, strerror(errno
));
903 /* Make the pipes to capture the child process's standard output and
906 if (pipe(p_out
) || pipe(p_err
)) {
907 bad("failed to create pipes for job `%s': %s",
908 JOB_NAME(job
), strerror(errno
));
911 if (configure_fd("job stdout pipe", p_out
[0], 1, 1) ||
912 configure_fd("job stdout pipe", p_out
[1], 0, 1) ||
913 configure_fd("job stderr pipe", p_err
[0], 1, 1) ||
914 configure_fd("job stderr pipe", p_err
[1], 0, 1) ||
915 configure_fd("log file", fileno(job
->log
), 1, 1))
918 /* Initialize the line-buffer structures ready for use. */
919 job
->out
.buf
= xmalloc(MAXLINE
); job
->out
.off
= job
->out
.len
= 0;
920 job
->out
.fd
= p_out
[0]; p_out
[0] = -1;
921 job
->err
.buf
= xmalloc(MAXLINE
); job
->err
.off
= job
->err
.len
= 0;
922 job
->err
.fd
= p_err
[0]; p_err
[0] = -1;
923 dstr_reset(&d
); argv_string(&d
, &job
->av
);
925 /* Print a note to the top of the log. */
926 fprintf(job
->log
, "%-13s > starting %s\n", JOB_NAME(job
), d
.p
);
928 /* Flush the standard output stream. (Otherwise the child might try to
933 /* Spin up the child process. */
936 bad("failed to fork process for job `%s': %s",
937 JOB_NAME(job
), strerror(errno
));
941 if (dup2(nullfd
, 0) < 0 ||
942 dup2(p_out
[1], 1) < 0 ||
943 dup2(p_err
[1], 2) < 0)
944 lose("failed to juggle job `%s' file descriptors: %s",
945 JOB_NAME(job
), strerror(errno
));
949 /* Close the ends of the pipes that we don't need. Move the job into
952 close(p_out
[1]); close(p_err
[1]);
954 job
->st
= JST_RUN
; job
->next
= job_run
; job_run
= job
; nrun
++;
958 /* Clean up the wreckage if it didn't work. */
959 if (p_out
[0] >= 0) close(p_out
[0]);
960 if (p_out
[1] >= 0) close(p_out
[1]);
961 if (p_err
[0] >= 0) close(p_err
[0]);
962 if (p_err
[1] >= 0) close(p_err
[1]);
966 /* All done except for some final tidying up. */
970 /* Take care of all of the jobs until they're all done. */
971 static void run_jobs(void)
973 struct job
*job
, *next
, **link
;
979 /* If there are jobs still to be started and we have slots to spare then
980 * start some more up.
984 /* If the queues are now all empty then we're done. (No need to check
985 * `job_ready' here: `start_jobs' would have started them if `job_run'
988 if (!job_run
&& !job_dead
) break;
991 /* Prepare for the select(2) call: watch for the signal pipe and all of
994 #define SET_FD(dir, fd) do { \
996 FD_SET(_fd, &fd_##dir); \
997 if (_fd >= nfd) nfd = _fd + 1; \
1000 FD_ZERO(&fd_in
); nfd
= 0;
1001 SET_FD(in
, sig_pipe
[0]);
1002 for (job
= job_run
; job
; job
= job
->next
) {
1003 if (job
->out
.fd
>= 0) SET_FD(in
, job
->out
.fd
);
1004 if (job
->err
.fd
>= 0) SET_FD(in
, job
->err
.fd
);
1006 for (job
= job_dead
; job
; job
= job
->next
) {
1007 if (job
->out
.fd
>= 0) SET_FD(in
, job
->out
.fd
);
1008 if (job
->err
.fd
>= 0) SET_FD(in
, job
->err
.fd
);
1013 /* Find out what's going on. */
1014 if (select(nfd
, &fd_in
, 0, 0, 0) < 0) {
1015 if (errno
== EINTR
) continue;
1016 else lose("select failed: %s", strerror(errno
));
1019 /* If there were any signals then handle them. */
1020 if (FD_ISSET(sig_pipe
[0], &fd_in
)) {
1023 /* We hit a fatal signal. Kill off the remaining jobs and abort. */
1024 for (job
= job_ready
; job
; job
= next
)
1025 { next
= job
->next
; release_job(job
); }
1026 for (job
= job_run
; job
; job
= next
)
1027 { next
= job
->next
; release_job(job
); }
1028 for (job
= job_dead
; job
; job
= next
)
1029 { next
= job
->next
; release_job(job
); }
1034 /* Log any new output from the running jobs. */
1035 for (job
= job_run
; job
; job
= job
->next
) {
1036 if (job
->out
.fd
>= 0 && FD_ISSET(job
->out
.fd
, &fd_in
))
1037 prefix_lines(job
, &job
->out
, '|');
1038 if (job
->err
.fd
>= 0 && FD_ISSET(job
->err
.fd
, &fd_in
))
1039 prefix_lines(job
, &job
->err
, '*');
1042 /* Finally, clear away any dead jobs once we've collected all their
1045 for (link
= &job_dead
, job
= *link
; job
; job
= next
) {
1046 if (job
->out
.fd
>= 0 && FD_ISSET(job
->out
.fd
, &fd_in
))
1047 prefix_lines(job
, &job
->out
, '|');
1048 if (job
->err
.fd
>= 0 && FD_ISSET(job
->err
.fd
, &fd_in
))
1049 prefix_lines(job
, &job
->err
, '*');
1051 if (job
->out
.fd
>= 0 || job
->err
.fd
>= 0) link
= &job
->next
;
1052 else { *link
= next
; finish_job(job
); }
1057 /*----- Main program ------------------------------------------------------*/
1059 /* Help and related functions. */
1060 static void version(FILE *fp
)
1061 { fprintf(fp
, "%s, runlisp version %s\n", progname
, PACKAGE_VERSION
); }
1063 static void usage(FILE *fp
)
1066 usage: %s [-RUadfinqrv] [+RUdfinr] [-c CONF] [-o [SECT:]VAR=VAL]\n\
1067 [-O FILE|DIR] [-j NJOBS] [LISP ...]\n",
1071 static void help(FILE *fp
)
1073 version(fp
); fputc('\n', fp
); usage(fp
);
1076 -h, --help Show this help text and exit successfully.\n\
1077 -V, --version Show version number and exit successfully.\n\
1080 -n, --dry-run Don't run run anything (useful with `-v').\n\
1081 -q, --quiet Don't print warning messages.\n\
1082 -v, --verbose Print informational messages (repeatable).\n\
1085 -c, --config-file=CONF Read configuration from CONF (repeatable).\n\
1086 -o, --set-option=[SECT:]VAR=VAL Set configuration variable (repeatable).\n\
1089 -O, --output=FILE|DIR Store image(s) in FILE or DIR.\n\
1090 -R, --remove-other Delete image files for other Lisp systems.\n\
1091 -U, --remove-unknown Delete unrecognized files in image dir.\n\
1092 -a, --all-configured Select all configured implementations.\n\
1093 -d, --cleanup Delete images which are no longer wanted.\n\
1094 -f, --force Dump images even if they already exist.\n\
1095 -i, --check-installed Check Lisp systems exist before dumping.\n\
1096 -j, --jobs=NJOBS Run up to NJOBS jobs in parallel.\n\
1097 -r, --remove-image Delete image files, instead of creating.\n",
1101 static void show_job_list(const char *what
, struct job
*job
)
1103 struct dstr d
= DSTR_INIT
;
1107 for (; job
; job
= job
->next
) {
1108 if (first
) first
= 0;
1109 else dstr_puts(&d
, ", ");
1110 dstr_putf(&d
, "`%s'", JOB_NAME(job
));
1112 if (first
) dstr_puts(&d
, "(none)");
1114 moan("%s: %s", what
, d
.p
);
1118 int main(int argc
, char *argv
[])
1120 struct config_section_iter si
;
1121 struct config_section
*sect
;
1122 struct config_var
*var
;
1123 const char *out
= 0, *p
, *q
, *l
;
1126 struct dstr d
= DSTR_INIT
;
1133 /* Command-line options. */
1134 static const struct option opts
[] = {
1135 { "help", 0, 0, 'h' },
1136 { "version", 0, 0, 'V' },
1137 { "output", OPTF_ARGREQ
, 0, 'O' },
1138 { "remove-other", OPTF_NEGATE
, 0, 'R' },
1139 { "remove-unknown", OPTF_NEGATE
, 0, 'U' },
1140 { "all-configured", 0, 0, 'a' },
1141 { "config-file", OPTF_ARGREQ
, 0, 'c' },
1142 { "force", OPTF_NEGATE
, 0, 'f' },
1143 { "check-installed", OPTF_NEGATE
, 0, 'i' },
1144 { "jobs", OPTF_ARGREQ
, 0, 'j' },
1145 { "dry-run", OPTF_NEGATE
, 0, 'n' },
1146 { "set-option", OPTF_ARGREQ
, 0, 'o' },
1147 { "quiet", 0, 0, 'q' },
1148 { "remove-image", OPTF_NEGATE
, 0, 'r' },
1149 { "verbose", 0, 0, 'v' },
1153 /* Initial setup. */
1154 set_progname(argv
[0]);
1157 /* Parse the options. */
1158 optprog
= (/*unconst*/ char *)progname
;
1160 #define FLAGOPT(ch, f) \
1164 case ch | OPTF_NEGATED: \
1169 i
= mdwopt(argc
- 1, argv
+ 1, "hVO:R+U+ac:d+f+i+j:n+o:qr+v", opts
, 0, 0,
1170 OPTF_NEGATION
| OPTF_NOPROGNAME
);
1173 case 'h': help(stdout
); exit(0);
1174 case 'V': version(stdout
); exit(0);
1175 case 'O': out
= optarg
; break;
1176 FLAGOPT('R', AF_CLEAN
);
1177 FLAGOPT('U', AF_JUNK
);
1178 case 'a': flags
|= AF_ALL
; break;
1179 case 'c': read_config_path(optarg
, 0); flags
|= AF_SETCONF
; break;
1180 FLAGOPT('f', AF_FORCE
);
1181 FLAGOPT('i', AF_CHECKINST
);
1182 case 'j': maxrun
= parse_int("number of jobs", optarg
, 1, 65535); break;
1183 FLAGOPT('n', AF_DRYRUN
);
1184 case 'o': if (set_config_var(optarg
)) flags
|= AF_BOGUS
; break;
1185 case 'q': if (verbose
) verbose
--; break;
1186 FLAGOPT('r', AF_REMOVE
);
1187 case 'v': verbose
++; break;
1188 default: flags
|= AF_BOGUS
; break;
1194 /* CHeck that everything worked. */
1196 if ((flags
&AF_ALL
) ? optind
< argc
: optind
>= argc
) flags
|= AF_BOGUS
;
1197 if (flags
&AF_BOGUS
) { usage(stderr
); exit(127); }
1199 /* Load default configuration if no explicit files were requested. */
1200 if (!(flags
&AF_SETCONF
)) load_default_config();
1202 /* OK, so we've probably got some work to do. Let's set things up ready.
1203 * It'll be annoying if our standard descriptors aren't actually set up
1204 * properly, so we'll make sure those slots are populated. We'll need a
1205 * `/dev/null' descriptor anyway (to be stdin for the jobs). We'll also
1206 * need a temporary directory, and it'll be less temporary if we don't
1207 * arrange to delete it when we're done. And finally we'll need to know
1208 * when a child process exits.
1211 fd
= open("/dev/null", O_RDWR
);
1212 if (fd
< 0) lose("failed to open `/dev/null': %s", strerror(errno
));
1213 if (fd
> 2) { nullfd
= fd
; break; }
1215 configure_fd("null fd", nullfd
, 0, 1);
1218 lose("failed to create signal pipe: %s", strerror(errno
));
1219 configure_fd("signal pipe (read end)", sig_pipe
[0], 1, 1);
1220 configure_fd("signal pipe (write end)", sig_pipe
[1], 1, 1);
1221 sigemptyset(&caught
); sigemptyset(&pending
);
1222 set_signal_handler("SIGTERM", SIGTERM
, SIGF_IGNOK
);
1223 set_signal_handler("SIGINT", SIGINT
, SIGF_IGNOK
);
1224 set_signal_handler("SIGHUP", SIGHUP
, SIGF_IGNOK
);
1225 set_signal_handler("SIGCHLD", SIGCHLD
, 0);
1227 /* Create the temporary directory and export it into the configuration. */
1229 config_set_var(&config
, builtin
, CF_LITERAL
, "@%tmp-dir", tmpdir
);
1230 config_set_var(&config
, builtin
, 0,
1231 "@tmp-dir", "${@BUILTIN:@%tmp-dir}/${@name}");
1233 /* Work out where the image files are going to go. If there's no `-O'
1234 * option then we use the main `image-dir'. Otherwise what happens depends
1235 * on whether this is a file or a directory.
1238 config_set_var(&config
, builtin
, 0,
1239 "@image-out", "${@image-dir}/${image-file}");
1240 else if (!stat(out
, &st
) && S_ISDIR(st
.st_mode
)) {
1241 config_set_var(&config
, builtin
, CF_LITERAL
, "@%out-dir", out
);
1242 config_set_var(&config
, builtin
, 0,
1243 "@image-out", "${@BUILTIN:@%out-dir}/${image-file}");
1244 } else if (argc
- optind
!= 1)
1245 lose("can't dump multiple Lisps to a single output file");
1246 else if (flags
&AF_JUNK
)
1247 lose("can't clear junk in a single output file");
1248 else if (flags
&AF_CLEAN
)
1249 lose("can't clean other images with a single output file");
1251 config_set_var(&config
, builtin
, CF_LITERAL
, "@image-out", out
);
1253 /* Set the staging file. */
1254 config_set_var(&config
, builtin
, 0, "@image-new", "${@image-out}.new");
1256 /* Dump the final configuration if we're being very verbose. */
1257 if (verbose
>= 5) dump_config();
1259 /* There are a number of different strategies we might employ, depending on
1260 * the exact request.
1263 * REMOVE CLEAN JUNK selected others junk?
1265 * * nil nil ready/delete -- no
1266 * * nil t ready/delete none yes
1267 * nil t nil ready delete no
1268 * nil t t ready -- yes
1269 * t t nil -- delete no
1273 /* First step: if `AF_REMOVE' and `AF_CLEAN' are not both set, then scan
1274 * the selected Lisp systems and add them to the appropriate queue.
1276 * Bit-hack: if they are not both set, then their complements are not both
1279 if (~flags
&(AF_REMOVE
| AF_CLEAN
)) {
1281 /* Determine the flags for `add_job' when we select the Lisp systems. If
1282 * we intend to clear junk then we must notice the image names we
1283 * encounter. If we're supposed to check that Lisps exist before dumping
1284 * then do that -- but it doesn't make any sense for deletion.
1286 f
= flags
&AF_REMOVE ? JQ_DELETE
: JQ_READY
;
1287 if (flags
&AF_JUNK
) f
|= JF_NOTICE
;
1288 if (flags
&AF_CHECKINST
) f
|= JF_CHECKINST
;
1289 if (!(flags
&(AF_FORCE
| AF_REMOVE
))) f
|= JF_CHECKEXIST
;
1291 /* If we have named Lisps, then process them. */
1292 if (!(flags
&AF_ALL
))
1293 for (i
= optind
; i
< argc
; i
++)
1294 add_named_job(f
, argv
[i
], strlen(argv
[i
]));
1296 /* Otherwise we're supposed to dump `all' of them. If there's a `dump'
1297 * configuration setting then we need to parse that. Otherwise we just
1301 var
= config_find_var(&config
, toplevel
, CF_INHERIT
, "dump");
1303 /* No setting. Just do all of the Lisps which look available. */
1306 for (config_start_section_iter(&config
, &si
);
1307 (sect
= config_next_section(&si
)); )
1310 /* Parse the `dump' list. */
1312 dstr_reset(&d
); config_subst_var(&config
, toplevel
, var
, &d
);
1313 p
= d
.p
; l
= p
+ d
.len
;
1315 while (p
< l
&& ISSPACE(*p
)) p
++;
1318 while (p
< l
&& !ISSPACE(*p
) && *p
!= ',') p
++;
1319 add_named_job(f
, q
, p
- q
);
1320 while (p
< l
&& ISSPACE(*p
)) p
++;
1321 if (p
< l
&& *p
== ',') p
++;
1327 /* Second step: if exactly one of `AF_CLEAN' and `AF_JUNK' is set, then we
1328 * need to scan all of the remaining Lisps and add them to the `delete'
1331 if (!(flags
&AF_CLEAN
) != !(flags
&AF_JUNK
)) {
1333 /* Determine the flag settings. If we're junking, then we're not
1334 * cleaning -- we just want to mark images belonging to other Lisps as
1335 * off-limits to the junking scan.
1337 f
= flags
&AF_CLEAN ? JQ_DELETE
: JQ_NONE
| JF_NOTICE
;
1339 /* Now scan the Lisp systems. */
1340 for (config_start_section_iter(&config
, &si
);
1341 (sect
= config_next_section(&si
)); )
1345 /* Terminate the job queues. */
1346 *job_ready_tail
= 0;
1347 *job_delete_tail
= 0;
1349 /* Report on what it is we're about to do. */
1351 show_job_list("dumping Lisp images", job_ready
);
1352 show_job_list("deleting Lisp images", job_delete
);
1355 /* If there turns out to be nothing to do, then mention this. */
1356 if (!(flags
&AF_REMOVE
) && verbose
>= 2 && !job_ready
)
1357 moan("no Lisp images to dump");
1359 /* Run the dumping jobs. */
1362 /* Check for any last signals. If we hit any fatal signals then we should
1363 * kill ourselves so that the exit status will be right.
1366 if (sigloss
) { cleanup(); signal(sigloss
, SIG_DFL
); raise(sigloss
); }
1368 /* Now delete Lisps which need deleting. */
1369 while (job_delete
) {
1370 job
= job_delete
; job_delete
= job
->next
;
1371 if (flags
&AF_DRYRUN
) {
1373 moan("not deleting `%s' image `%s' (dry run)",
1374 JOB_NAME(job
), job
->imgout
);
1377 moan("deleting `%s' image `%s' (dry run)",
1378 JOB_NAME(job
), job
->imgout
);
1379 if (unlink(job
->imgout
) && errno
!= ENOENT
)
1380 bad("failed to delete `%s' image `%s': %s",
1381 JOB_NAME(job
), job
->imgout
, strerror(errno
));
1385 /* Finally, maybe delete all of the junk files in the image directory. */
1386 if (flags
&AF_JUNK
) {
1388 var
= config_find_var(&config
, builtin
, CF_INHERIT
, "@image-dir");
1389 assert(var
); out
= config_subst_var_alloc(&config
, builtin
, var
);
1393 lose("failed to open image directory `%s': %s", out
, strerror(errno
));
1395 dstr_puts(&d
, out
); dstr_putc(&d
, '/'); o
= d
.len
;
1397 moan("cleaning up junk in image directory `%s'", out
);
1399 de
= readdir(dir
); if (!de
) break;
1400 if (de
->d_name
[0] == '.' &&
1401 (!de
->d_name
[1] || (de
->d_name
[1] == '.' && !de
->d_name
[2])))
1403 n
= strlen(de
->d_name
);
1404 d
.len
= o
; dstr_putm(&d
, de
->d_name
, n
+ 1);
1405 if (!treap_lookup(&good
, de
->d_name
, n
)) {
1406 if (flags
&AF_DRYRUN
) {
1408 moan("not deleting junk file `%s' (dry run)", d
.p
);
1411 moan("deleting junk file `%s'", d
.p
);
1412 if (unlink(d
.p
) && errno
!= ENOENT
)
1413 bad("failed to delete junk file `%s': %s", d
.p
, strerror(errno
));
1423 /*----- That's all, folks -------------------------------------------------*/