3315e8b3 |
1 | /* -*-c-*- |
2 | * |
e55daacc |
3 | * $Id$ |
3315e8b3 |
4 | * |
5 | * Management of build processes |
6 | * |
7 | * (c) 1999 EBI |
8 | */ |
9 | |
10 | /*----- Licensing notice --------------------------------------------------* |
11 | * |
12 | * This file is part of sw-tools. |
13 | * |
14 | * sw-tools is free software; you can redistribute it and/or modify |
15 | * it under the terms of the GNU General Public License as published by |
16 | * the Free Software Foundation; either version 2 of the License, or |
17 | * (at your option) any later version. |
18 | * |
19 | * sw-tools is distributed in the hope that it will be useful, |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | * GNU General Public License for more details. |
23 | * |
24 | * You should have received a copy of the GNU General Public License |
25 | * along with sw-tools; if not, write to the Free Software Foundation, |
26 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
27 | */ |
28 | |
3315e8b3 |
29 | /*----- Header files ------------------------------------------------------*/ |
30 | |
31 | #include "config.h" |
32 | |
33 | #include <ctype.h> |
34 | #include <errno.h> |
35 | #include <signal.h> |
36 | #include <stdarg.h> |
37 | #include <stdio.h> |
38 | #include <stdlib.h> |
39 | #include <string.h> |
e55daacc |
40 | #include <time.h> |
3315e8b3 |
41 | |
42 | #include <sys/types.h> |
43 | #include <sys/time.h> |
44 | #include <sys/select.h> |
45 | #include <sys/stat.h> |
46 | #include <unistd.h> |
47 | #include <fcntl.h> |
48 | #include <sys/wait.h> |
1efab4fe |
49 | #include <sys/utsname.h> |
3315e8b3 |
50 | |
51 | #ifndef DECL_ENVIRON |
52 | extern char **environ; |
53 | #endif |
54 | |
55 | #include <mLib/alloc.h> |
56 | #include <mLib/dstr.h> |
57 | #include <mLib/exc.h> |
58 | #include <mLib/quis.h> |
59 | #include <mLib/report.h> |
60 | |
61 | #include "sw.h" |
62 | #include "sw_arch.h" |
63 | #include "sw_build.h" |
64 | #include "sw_info.h" |
65 | #include "sw_rsh.h" |
66 | |
67 | #define PRES_LINK 0 |
68 | #define PRES_DEFAULT 0 |
69 | #define PRES_PREFERENCE 0 |
70 | #include "pres_plain.h" |
71 | #include "pres_curses.h" |
72 | |
73 | /*----- Data structures ---------------------------------------------------*/ |
74 | |
75 | /*----- Static variables --------------------------------------------------*/ |
76 | |
77 | static pres *preslist = PRES_LINK; |
78 | |
79 | /*----- Main code ---------------------------------------------------------*/ |
80 | |
81 | /* --- @swbuild_archlist@ --- * |
82 | * |
83 | * Arguments: @swinfo *sw@ = pointer to the build information block |
84 | * |
85 | * Returns: A list of architectures which are to be built. |
86 | * |
87 | * Use: Decides which architectures need building, and returns them |
88 | * in a list. |
89 | */ |
90 | |
91 | archcons *swbuild_archlist(swinfo *sw) |
92 | { |
93 | archcons *a = arch_readtab(); |
94 | const char *only = 0; |
95 | unsigned and = 0, xor = 0; |
96 | |
97 | /* --- Restrict the architecture list appropriately --- */ |
98 | |
99 | only = opt_arch ? opt_arch : sw->only_arch; |
100 | |
101 | /* --- Apply the built flags --- */ |
102 | |
103 | if (!(opt_flags & optFlag_force) && sw->arch) { |
104 | archcons *aa = arch_filter(a, sw->arch, 0, 0); |
105 | archcons *c; |
106 | for (c = aa; c; c = c->cdr) |
107 | c->car->flags |= archFlag_built; |
108 | arch_free(aa); |
109 | and |= archFlag_built; |
110 | } |
111 | |
112 | return (arch_filter(a, only, and, xor)); |
113 | } |
114 | |
115 | /*----- Main build command ------------------------------------------------*/ |
116 | |
117 | /* --- @sw_run@ --- * |
118 | * |
119 | * Arguments: @int argc@ = number of command line arguments |
120 | * @char *argv[]@ = array of command line arguments |
121 | * |
122 | * Returns: Zero on success (all builds OK) or nonzero for failure. |
123 | * |
124 | * Use: Runs a multi-architecture build. |
125 | */ |
126 | |
127 | int sw_run(int argc, char *argv[]) |
128 | { |
129 | swinfo sw; |
130 | archcons *a; |
131 | pres *p = PRES_DEFAULT; |
132 | fd_set fdin; |
133 | int active = 0; |
134 | int maxfd = 0; |
135 | int rc = 0; |
136 | |
137 | /* --- Handle help on output styles --- */ |
138 | |
139 | if (opt_output && strcmp(opt_output, "help") == 0) { |
140 | printf("Presentation styles supported:"); |
141 | for (p = preslist; p; p = p->next) |
142 | printf(" %s", p->name); |
143 | putc('\n', stdout); |
144 | printf("The default presentation style is %s\n", (PRES_DEFAULT)->name); |
145 | exit(0); |
146 | } |
147 | |
148 | /* --- Validate arguments --- */ |
149 | |
150 | if (!argv[1]) |
151 | die(1, "Usage: run COMMAND [ARG...]"); |
152 | |
153 | /* --- Choose an output presentation style --- */ |
154 | |
155 | if (opt_output) { |
156 | pres *q; |
157 | size_t sz = strlen(opt_output); |
158 | p = 0; |
159 | |
160 | for (q = preslist; q; q = q->next) { |
161 | if (strncmp(opt_output, q->name, sz) == 0) { |
162 | if (q->name[sz] == 0) { |
163 | p = q; |
164 | break; |
165 | } else if (p) |
166 | die(1, "ambiguous output style `%s'", opt_output); |
167 | else |
168 | p = q; |
169 | } |
170 | } |
171 | |
172 | if (!p) |
173 | die(1, "unknown output style `%s'", opt_output); |
174 | } |
175 | |
176 | if (p->ok && !p->ok()) { |
177 | moan("output style `%s' can't run; using `plain' instead", p->name); |
178 | p = &pres_plain; |
179 | } |
180 | |
181 | /* --- Decide on an architecture --- */ |
182 | |
183 | if (swinfo_fetch(&sw)) { |
184 | die(1, "couldn't read build status: %s (try running setup)", |
185 | strerror(errno)); |
186 | } |
187 | swinfo_sanity(&sw); |
188 | a = swbuild_archlist(&sw); |
189 | |
190 | if (!a) { |
191 | moan("All desired architectures already built OK."); |
192 | moan("(Perhaps you forgot `--force', or want to say `%s reset'.)", QUIS); |
193 | return (0); |
194 | } |
195 | |
196 | /* --- Tie on remote context blocks, and crank up the presentation --- */ |
197 | |
198 | { |
199 | archcons *aa; |
200 | |
201 | for (aa = a; aa; aa = aa->cdr) |
202 | aa->car->r = xmalloc(sizeof(sw_remote)); |
203 | if (p->init && p->init(a)) |
204 | die(1, "presentation style refused to start: %s", strerror(errno)); |
205 | } |
206 | |
207 | signal(SIGINT, SIG_IGN); |
208 | signal(SIGQUIT, SIG_IGN); |
209 | |
210 | /* --- Trap any exceptions coming this way --- * |
211 | * |
212 | * It's important, for example, that a curses-based presentation system |
213 | * reset the terminal flags appropriately. |
214 | */ |
215 | |
216 | TRY { |
217 | /* --- Run remote build processes on the remote hosts --- */ |
218 | |
219 | { |
220 | archcons *aa; |
1efab4fe |
221 | dstr d = DSTR_INIT; |
222 | char **av; |
223 | struct utsname u; |
224 | |
225 | /* --- Fill in the hostname --- */ |
226 | |
bdba26c6 |
227 | if (uname(&u) < 0) |
1efab4fe |
228 | strcpy(u.nodename, "<unknown>"); |
229 | |
230 | /* --- If necessary, set up the output @argv@ array --- */ |
231 | |
232 | if (opt_flags & optFlag_percent) |
233 | av = xmalloc(argc * sizeof(char *)); |
234 | else |
235 | av = argv + 1; |
236 | |
237 | /* --- Run through the target build hosts --- */ |
3315e8b3 |
238 | |
239 | FD_ZERO(&fdin); |
240 | for (aa = a; aa; aa = aa->cdr) { |
241 | archent *e = aa->car; |
242 | sw_remote *r = e->r; |
1efab4fe |
243 | |
244 | /* --- If necessary, translate `%'-escapes --- */ |
245 | |
246 | if (opt_flags & optFlag_percent) { |
247 | char **pp, **qq; |
248 | |
249 | for (pp = argv + 1, qq = av; *pp; pp++, qq++) { |
250 | if (strchr(*pp, '%') == 0) |
251 | *qq = *pp; |
252 | else { |
253 | char *p; |
254 | char *q = *pp; |
255 | for (p = *pp; *p; p++) { |
256 | if (*p == '%') { |
257 | DPUTM(&d, q, p - q); |
258 | p++; |
259 | switch (*p) { |
260 | case 0: |
261 | DPUTC(&d, '%'); |
262 | goto done_arg; |
263 | case '%': |
264 | DPUTC(&d, '%'); |
265 | break; |
266 | case 'a': |
267 | dstr_puts(&d, e->arch); |
268 | break; |
269 | case 'h': |
270 | dstr_puts(&d, e->flags & archFlag_home ? |
271 | u.nodename : e->host); |
272 | break; |
273 | case 'P': |
274 | dstr_puts(&d, PREFIX); |
275 | break; |
276 | case 'p': |
277 | dstr_puts(&d, sw.package); |
278 | break; |
279 | case 'v': |
280 | dstr_puts(&d, sw.version); |
281 | break; |
282 | case 'u': |
283 | dstr_puts(&d, sw.maintainer); |
284 | break; |
285 | default: |
286 | DPUTC(&d, '%'); |
287 | DPUTC(&d, *p); |
288 | break; |
289 | } |
290 | q = p + 1; |
291 | } |
292 | } |
293 | DPUTM(&d, q, p - q); |
294 | done_arg: |
295 | DPUTZ(&d); |
296 | *qq = xstrdup(d.buf); |
297 | DRESET(&d); |
298 | } |
299 | } |
300 | *qq++ = 0; |
301 | } |
302 | |
303 | /* --- Start a new process off --- */ |
304 | |
3315e8b3 |
305 | if (swrsh(r, e->flags & archFlag_home ? 0 : e->host, |
1efab4fe |
306 | "build", av)) { |
3315e8b3 |
307 | dstr d = DSTR_INIT; |
308 | dstr_putf(&d, "%s: couldn't start build for architecture `%s': %s", |
309 | QUIS, e->arch, strerror(errno)); |
310 | p->output(e, d.buf, d.len); |
311 | free(r); |
312 | e->r = 0; |
313 | dstr_destroy(&d); |
314 | } else { |
315 | int fd = r->fdin; |
316 | if (fd > maxfd) |
317 | maxfd = fd; |
318 | active++; |
319 | FD_SET(fd, &fdin); |
320 | } |
1efab4fe |
321 | |
322 | /* --- Free up the argument array --- */ |
323 | |
324 | if (opt_flags & optFlag_percent) { |
325 | char **pp, **qq; |
326 | |
327 | for (pp = argv + 1, qq = av; *pp; pp++, qq++) { |
328 | if (*pp != *qq) |
329 | free(*qq); |
330 | } |
331 | } |
3315e8b3 |
332 | } |
1efab4fe |
333 | |
334 | if (opt_flags & optFlag_percent) |
335 | free(av); |
3315e8b3 |
336 | } |
337 | |
338 | /* --- Watch the builds until they do something interesting --- */ |
339 | |
340 | maxfd++; |
341 | while (active) { |
342 | fd_set f; |
343 | int n; |
344 | archcons *aa; |
345 | |
346 | /* --- Find out what interesting things are happening --- */ |
347 | |
348 | memcpy(&f, &fdin, sizeof(f)); |
349 | n = select(maxfd, &f, 0, 0, 0); |
350 | if (n < 0) { |
351 | if (errno == EINTR || errno == EAGAIN) |
352 | continue; |
353 | else |
354 | THROW(EXC_ERRNO, errno); |
355 | } |
356 | |
357 | /* --- Scan through for jobs which need attention --- */ |
358 | |
359 | for (aa = a; aa; aa = aa->cdr) { |
360 | archent *e = aa->car; |
361 | sw_remote *r = e->r; |
362 | int t; |
363 | |
364 | if (!FD_ISSET(r->fdin, &f)) |
365 | continue; |
366 | |
367 | switch (t = pkrecv(r)) { |
368 | |
369 | case PKTYPE_DATA: |
370 | p->output(e, r->buf, r->sz); |
371 | break; |
372 | |
373 | case PKTYPE_STATUS: { |
374 | dstr d = DSTR_INIT; |
375 | int ok = 1; |
376 | if (r->sz != 1) { |
377 | r->buf[r->sz] = 0; |
5636c0ce |
378 | dstr_putf(&d, "failed (%s)", r->buf); |
3315e8b3 |
379 | ok = 0; |
380 | rc = 1; |
381 | } else if (r->buf[0]) { |
5636c0ce |
382 | dstr_putf(&d, "failed (status %u)", (unsigned char)r->buf[0]); |
3315e8b3 |
383 | ok = 0; |
384 | rc = 1; |
5636c0ce |
385 | } else { |
386 | dstr_puts(&d, "finished"); |
387 | if (opt_flags & optFlag_install) |
388 | e->flags |= archFlag_built; |
389 | } |
3315e8b3 |
390 | if (p->close) |
5636c0ce |
391 | p->close(e, ok, d.buf); |
392 | dstr_destroy(&d); |
3315e8b3 |
393 | FD_CLR(r->fdin, &fdin); |
394 | close(r->fdin); |
395 | active--; |
396 | } break; |
397 | |
398 | case PKTYPE_EOF: { |
3315e8b3 |
399 | if (p->close) |
5636c0ce |
400 | p->close(e, 0, "unexpected exit"); |
3315e8b3 |
401 | rc = 1; |
402 | FD_CLR(r->fdin, &fdin); |
403 | close(r->fdin); |
404 | active--; |
405 | } break; |
406 | |
407 | default: { |
408 | const static char msg[] = "\n[Unexpected packet, type %i]\n"; |
409 | p->output(e, msg, sizeof(msg) - 1); |
410 | } break; |
411 | } |
412 | } |
413 | } |
414 | } |
415 | |
416 | /* --- Handle any exceptions coming this way --- * |
417 | * |
418 | * I could do more cleanup here (freeing buffers and so) but it's not worth |
419 | * it. Nobody's bothering to catch exceptions anyway. |
420 | */ |
421 | |
422 | CATCH { |
423 | if (p->abort) |
424 | p->abort(a); |
425 | switch (exc_type) { |
426 | case EXC_ERRNO: |
5636c0ce |
427 | die(1, "unexpected error: %s", strerror(exc_i)); |
3315e8b3 |
428 | break; |
429 | default: |
430 | RETHROW; |
431 | break; |
432 | } |
433 | } END_TRY; |
434 | |
435 | /* --- Tell the presentation that everything's done --- */ |
436 | |
437 | if (p->done) |
438 | p->done(a); |
439 | else if (opt_flags & optFlag_beep) |
440 | putchar('\a'); |
441 | |
442 | /* --- Clean up the unwanted remote contexts --- */ |
443 | |
444 | { |
445 | archcons *aa; |
446 | for (aa = a; aa; aa = aa->cdr) |
447 | free(a->car->r); |
448 | } |
449 | |
450 | /* --- Tidy away the architecture list --- */ |
451 | |
452 | arch_free(a); |
453 | |
454 | /* --- Mark built architectures as having been completed now --- */ |
455 | |
456 | if (opt_flags & optFlag_install) { |
457 | swinfo skel; |
458 | dstr d = DSTR_INIT; |
459 | swinfo_clear(&skel); |
460 | arch_toText(&d, arch_readtab(), archFlag_built, archFlag_built); |
461 | skel.arch = d.buf; |
462 | swinfo_update(&sw, &skel); |
463 | dstr_destroy(&d); |
464 | if (swinfo_put(&sw)) |
465 | die(1, "error writing build status: %s", strerror(errno)); |
466 | } |
467 | return (rc); |
468 | } |
469 | |
470 | /*----- Main remote entry point -------------------------------------------*/ |
471 | |
472 | /* --- @putf@ --- * |
473 | * |
474 | * Arguments: @sw_remote *r@ = pointer to remote context |
475 | * @FILE *fp@ = log file handle |
476 | * @const char *fmt@ = format string |
477 | * @...@ = other arguments |
478 | * |
479 | * Returns: --- |
480 | * |
481 | * Use: Reports a string to the log file and the remote controller |
482 | * process. |
483 | */ |
484 | |
485 | static void putf(sw_remote *r, FILE *fp, const char *fmt, ...) |
486 | { |
487 | va_list ap; |
488 | dstr d = DSTR_INIT; |
489 | va_start(ap, fmt); |
490 | dstr_vputf(&d, fmt, ap); |
491 | va_end(ap); |
492 | if (r) |
493 | pksend(r, PKTYPE_DATA, d.buf, d.len); |
494 | if (fp) |
495 | fwrite(d.buf, 1, d.len, fp); |
496 | dstr_destroy(&d); |
497 | } |
498 | |
499 | /* --- @swrsh_build@ --- * |
500 | * |
501 | * Arguments: @sw_remote *r@ = pointer to remote context |
502 | * @char *argv[]@ = pointer to argument list |
503 | * @char *env[]@ = pointer to environment list |
504 | * |
505 | * Returns: Doesn't. |
506 | * |
507 | * Use: Runs a remote build command. |
508 | */ |
509 | |
510 | void swrsh_build(sw_remote *r, char *argv[], char *env[]) |
511 | { |
512 | FILE *logfp; |
513 | int fd[2]; |
514 | pid_t kid; |
515 | |
1efab4fe |
516 | /* --- Validate the arguments --- */ |
3315e8b3 |
517 | |
518 | if (!argv[0]) |
519 | swdie(r, 1, "Usage: build COMMAND [ARG...]"); |
520 | |
521 | /* --- Change into architecture directory --- */ |
522 | |
523 | if (mkdir(ARCH, 0775) && errno != EEXIST) |
524 | swdie(r, 1, "mkdir(`%s') failed: %s", ARCH, strerror(errno)); |
525 | if (chdir(ARCH)) { |
526 | swdie(r, 1, "couldn't change directory to `%s': %s", |
527 | ARCH, strerror(errno)); |
528 | } |
529 | if (pipe(fd)) |
530 | swdie(r, 1, "couldn't create pipe: %s", strerror(errno)); |
531 | |
532 | /* --- Open the log file --- */ |
533 | |
534 | { |
535 | int logfd = open(".build-log", O_WRONLY | O_APPEND | O_CREAT, 0664); |
536 | time_t t; |
537 | struct tm *tm; |
538 | char buf[64]; |
539 | char **p; |
1efab4fe |
540 | struct utsname u; |
3315e8b3 |
541 | |
bdba26c6 |
542 | if (uname(&u) < 0) |
1efab4fe |
543 | swdie(r, 1, "couldn't get hostname: %s", strerror(errno)); |
3315e8b3 |
544 | if (logfd < 0) |
545 | swdie(r, 1, "couldn't open `.build-log' file: %s", strerror(errno)); |
546 | if ((logfp = fdopen(logfd, "a")) == 0) { |
547 | swdie(r, 1, "couldn't open stream on `.build-log' file: %s", |
548 | strerror(errno)); |
549 | } |
550 | t = time(0); |
551 | tm = localtime(&t); |
552 | strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm); |
1efab4fe |
553 | fprintf(logfp, "\n\n*** %s: %s started build: %s", |
554 | buf, u.nodename, argv[0]); |
3315e8b3 |
555 | for (p = argv + 1; *p; p++) |
556 | fprintf(logfp, " %s", *p); |
557 | fputs("\n\n", logfp); |
558 | } |
559 | |
560 | /* --- Start off the child process --- */ |
561 | |
562 | kid = fork(); |
563 | if (kid == 0) { |
564 | int nullfd; |
565 | close(fd[0]); |
566 | dup2(fd[1], 1); |
567 | dup2(fd[1], 2); |
568 | if (fd[1] > 2) |
569 | close(fd[1]); |
570 | close(0); |
571 | nullfd = open("/dev/null", O_RDONLY); |
572 | if (nullfd > 0) { |
573 | dup2(nullfd, 0); |
574 | close(nullfd); |
575 | } |
576 | environ = env; |
577 | execvp(argv[0], argv); |
578 | fprintf(stderr, "failed to start `%s': %s\n", argv[0], strerror(errno)); |
579 | _exit(127); |
580 | } |
581 | |
582 | /* --- Read from the pipe, and write to the socket and logfile --- */ |
583 | |
584 | close(fd[1]); |
585 | for (;;) { |
586 | ssize_t n = read(fd[0], r->buf, PKMAX); |
587 | if (!n) |
588 | break; |
589 | if (n < 0) { |
1efab4fe |
590 | putf(r, logfp, "\n*** error reading from pipe: %s\n", strerror(errno)); |
3315e8b3 |
591 | kill(kid, SIGTERM); |
592 | break; |
593 | } |
594 | fwrite(r->buf, 1, n, logfp); |
595 | pksend(r, PKTYPE_DATA, r->buf, n); |
596 | } |
597 | close(fd[0]); |
598 | |
599 | /* --- Reap exit status and produce final report --- */ |
600 | |
601 | { |
602 | int status; |
1efab4fe |
603 | if (waitpid(kid, &status, 0) < 0) { |
604 | putf(r, logfp, "\n*** error reading exit status: %s\n", |
605 | strerror(errno)); |
606 | } else { |
3315e8b3 |
607 | if (WIFSIGNALED(status)) |
1efab4fe |
608 | fprintf(logfp, "\n*** exited on signal %i\n", WTERMSIG(status)); |
3315e8b3 |
609 | else if (WIFEXITED(status)) |
1efab4fe |
610 | fprintf(logfp, "\n*** exited with status %i\n", WEXITSTATUS(status)); |
3315e8b3 |
611 | else |
1efab4fe |
612 | fprintf(logfp, "\n*** reaped, but didn't exit. Strange\n"); |
3315e8b3 |
613 | } |
614 | fclose(logfp); |
615 | swwait(r, status); |
616 | } |
617 | } |
618 | |
619 | /*----- Syntactic sugar ---------------------------------------------------*/ |
620 | |
621 | /* |
622 | * `Syntactic sugar causes cancer of the semicolon.' |
623 | * -- Alan Perlis, `Epigrams in Programming' |
624 | */ |
625 | |
626 | /* --- @build@ --- * |
627 | * |
628 | * Arguments: @char **u@ = first segment |
629 | * @char **v@ = second segment |
630 | * |
631 | * Returns: Return code from the build. |
632 | * |
633 | * Use: Combines two @argv@-style arrays and then runs a build on the |
634 | * result. |
635 | */ |
636 | |
637 | static int build(char **u, char **v) |
638 | { |
639 | size_t i, j; |
640 | char **p; |
641 | |
642 | for (i = 0, p = u; *p; p++, i++) ; |
643 | for (j = 0, p = v; *p; p++, j++) ; |
644 | p = xmalloc((i + j + 2) * sizeof(char **)); |
645 | memcpy(p + 1, u, i * sizeof(char **)); |
646 | memcpy(p + i + 1, v, j * sizeof(char **)); |
647 | p[0] = p[i + j + 1] = 0; |
648 | return (sw_run(i + j + 1, p)); |
649 | } |
650 | |
651 | /* --- @sw_make@ --- */ |
652 | |
653 | int sw_make(int argc, char *argv[]) |
654 | { |
655 | static char *mk[] = { 0, 0 }; |
656 | if (!mk[0]) { |
657 | char *m; |
658 | if ((m = getenv("SW_MAKE")) == 0 && |
659 | (m = getenv("MAKE")) == 0) |
660 | m = "make"; |
661 | mk[0] = m; |
662 | } |
663 | return (build(mk, argv + 1)); |
664 | } |
665 | |
666 | /* --- @sw_conf@ --- */ |
667 | |
668 | int sw_conf(int argc, char *argv[]) |
669 | { |
670 | static char *cf[] = { "../configure", "--prefix=" PREFIX, 0 }; |
671 | return (build(cf, argv + 1)); |
672 | } |
673 | |
674 | /*----- Other subcommands -------------------------------------------------*/ |
675 | |
676 | /* --- @sw_reset@ --- */ |
677 | |
678 | int sw_reset(int argc, char *argv[]) |
679 | { |
680 | swinfo sw, skel; |
681 | if (argc != 1) |
682 | die(1, "Usage: reset"); |
683 | if (swinfo_fetch(&sw)) { |
684 | die(1, "couldn't read build status: %s (try running setup)", |
685 | strerror(errno)); |
686 | } |
687 | swinfo_sanity(&sw); |
688 | swinfo_clear(&skel); |
689 | skel.arch = ""; |
690 | swinfo_update(&sw, &skel); |
691 | if (swinfo_put(&sw)) |
692 | die(1, "couldn't write build status: %s", strerror(errno)); |
693 | return (0); |
694 | } |
695 | |
696 | /*----- That's all, folks -------------------------------------------------*/ |