chiark / gitweb /
Fix call to `execl'.
[sw-tools] / src / sw_info.c
1 /* -*-c-*-
2  *
3  * $Id: sw_info.c,v 1.5 1999/06/24 16:02:12 mdw Exp $
4  *
5  * Maintenance of `.sw-info' files
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
29 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: sw_info.c,v $
32  * Revision 1.5  1999/06/24 16:02:12  mdw
33  * Fix call to `execl'.
34  *
35  * Revision 1.4  1999/06/24 15:57:07  mdw
36  * Stupid bug fix.  Code wouldn't even compile.  Tut.
37  *
38  * Revision 1.3  1999/06/24 15:51:59  mdw
39  * Add support for the `sw-precommit' script.
40  *
41  * Revision 1.2  1999/06/18 18:58:45  mdw
42  * Various tidyings.
43  *
44  * Revision 1.1.1.1  1999/06/02 16:53:35  mdw
45  * Initial import.
46  *
47  */
48
49 /*----- Header files ------------------------------------------------------*/
50
51 #include "config.h"
52
53 #include <ctype.h>
54 #include <errno.h>
55 #include <signal.h>
56 #include <stddef.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <time.h>
61
62 #include <sys/types.h>
63 #include <sys/wait.h>
64 #include <pwd.h>
65 #include <unistd.h>
66
67 #ifndef DECL_ENVIRON
68   extern char **environ;
69 #endif
70
71 #include <mLib/alloc.h>
72 #include <mLib/dstr.h>
73 #include <mLib/lock.h>
74 #include <mLib/quis.h>
75 #include <mLib/report.h>
76
77 #include "sw_arch.h"
78 #include "sw_env.h"
79 #include "sw_info.h"
80
81 /*----- Static variables --------------------------------------------------*/
82
83 static struct swfield {
84   char *name;
85   size_t off;
86   char *desc;
87 } swfields[] = {
88   { "package", offsetof(swinfo, package), "Package" },
89   { "version", offsetof(swinfo, version), "Version" },
90   { "maintainer", offsetof(swinfo, maintainer), "Maintained by" },
91   { "date", offsetof(swinfo, date), "Last modified" },
92   { "only-arch", offsetof(swinfo, only_arch), "Only build for" },
93   { "arch", offsetof(swinfo, arch), "Successfully built" },
94   { 0, 0 }
95 };
96 typedef struct swfield swfield;
97
98 #define SWINFO(sw, off) (*(char **)((char *)sw + off))
99
100 /*----- Main code ---------------------------------------------------------*/
101
102 /* --- @swinfo_clear@ --- *
103  *
104  * Arguments:   @swinfo *sw@ = pointe to info block
105  *
106  * Returns:     ---
107  *
108  * Use:         Clears an info block so that it doesn't contain anything.
109  *              This is mainly useful when building skeletons to apply using
110  *              @swinfo_update@.
111  */
112
113 void swinfo_clear(swinfo *sw)
114 {
115   swfield *f;
116   for (f = swfields; f->name; f++)
117     SWINFO(sw, f->off) = 0;
118
119
120 /* --- @swinfo_fetch@ --- *
121  *
122  * Arguments:   @swinfo *sw@ = pointer to info block to fill in
123  *
124  * Returns:     Zero if OK, else nonzero.
125  *
126  * Use:         Fills in the info block if it can.
127  */
128
129 int swinfo_fetch(swinfo *sw)
130 {
131   FILE *fp;
132   dstr d = DSTR_INIT;
133   swfield *f;
134   int line = 0;
135
136   /* --- Initialize the various fields --- */
137
138   swinfo_clear(sw);
139
140   /* --- Open the data file --- */
141
142   if ((fp = fopen(".sw-info", "r")) == 0)
143     return (-1);
144
145   /* --- Read the lines from the file --- */
146
147   for (; dstr_putline(&d, fp) != EOF; DRESET(&d)) {
148     char *p = d.buf;
149     char *key, *value;
150     line++;
151
152     /* --- Find the field name --- */
153
154     while (isspace((unsigned char)*p))
155       p++;
156     if (*p == 0 || *p == '#')
157       continue;
158
159     /* --- Find the end of the field name --- */
160
161     key = p;
162     while (*p && *p != '=' && !isspace((unsigned char)*p))
163       p++;
164     
165     /* --- Skip an optional `=' sign --- */
166
167     {
168       char ch = *p;
169       if (ch)
170         *p++ = 0;
171       while (isspace((unsigned char)*p))
172         p++;
173       if (*p == '=' && ch != '=') {
174         p++;
175         while (isspace((unsigned char)*p))
176           p++;
177       } 
178     }
179
180     value = p;
181     
182     /* --- Look up the key in the table --- */
183
184     for (f = swfields; f->name; f++) {
185       if (strcmp(key, f->name) == 0)
186         goto found;
187     }
188     moan("unknown key `%s' at line %i in `.sw-info'", key, line);
189     continue;
190
191     /* --- Put the value in --- */
192
193   found:
194     SWINFO(sw, f->off) = xstrdup(value);
195   }
196
197   fclose(fp);
198   dstr_destroy(&d);
199   return (0);
200 }
201
202 /* --- @swinfo_sanity@ --- *
203  *
204  * Arguments:   @swinfo *sw@ = pointer to an info block
205  *
206  * Returns:     Yes, if the block is OK.
207  *
208  * Use:         Objects if the information in the info block is bad.
209  */
210
211 void swinfo_sanity(swinfo *sw)
212 {
213   if (!sw->package)
214     die(1, "unknown package name.  Try `%s setup PACKAGE VERSION'.", QUIS);
215   if (!sw->version)
216     die(1, "unknown package version.  Try `%s setup PACKAGE VERSION'.",
217         QUIS);
218   if (!sw->maintainer)
219     die(1, "unknown maintainer.  Try `%s setup PACKAGE VERSION'.", QUIS);
220   if (!sw->date)
221     die(1, "unknown date.  Try `%s setup PACKAGE VERSION'.", QUIS);
222 }
223
224 /* --- @swinfo_put@ --- *
225  *
226  * Arguments:   @swinfo *sw@ = pointer to an info block
227  *
228  * Returns:     Zero if it worked, nonzero if not.
229  *
230  * Use:         Writes an info block to the appropriate file.
231  */
232
233 int swinfo_put(swinfo *sw)
234 {
235   FILE *fp;
236   swfield *f;
237   int e;
238
239   /* --- Quick sanity check --- */
240
241   swinfo_sanity(sw);
242
243   /* --- Write the new data out --- */
244
245   if ((fp = fopen(".sw-info.new", "w")) == 0)
246     return (-1);
247   for (f = swfields; f->name; f++) {
248     if (!SWINFO(sw, f->off))
249       continue;
250     if (fprintf(fp, "%s = %s\n", f->name, SWINFO(sw, f->off)) == EOF) {
251       e = errno;
252       fclose(fp);
253       goto tidy_0;
254     }
255   }
256   if (fclose(fp) == EOF) {
257     e = errno;
258     goto tidy_0;
259   }
260
261   /* --- Carefully! replace the old one --- *
262    *
263    * Keep an old one around just in case, unless the renames fail because
264    * files don't exist.
265    */
266
267   remove(".sw-info.older");             /* Don't care if this fails */
268   if (rename(".sw-info.old", ".sw-info.older") && errno != ENOENT)
269     { e = errno; goto tidy_0; }
270   if (rename(".sw-info", ".sw-info.old") && errno != ENOENT)
271     { e = errno; goto tidy_1; }
272   if (rename(".sw-info.new", ".sw-info"))
273     { e = errno; goto tidy_2; }
274   remove(".sw-info.older");             /* Don't care if this fails */
275   return (0);
276
277   /* --- Tidy up if anything went tits-up --- */
278
279 tidy_2:
280   rename(".sw-info.old", ".sw-info");
281 tidy_1:
282   rename(".sw-info.older", ".sw-info.old");
283 tidy_0:
284   remove(".sw-info.new");
285   errno = e;
286   return (-1);
287 }
288
289 /* --- @swinfo_destroy@ --- *
290  *
291  * Arguments:   @swinfo *sw@ = pointer to info block
292  *
293  * Returns:     ---
294  *
295  * Use:         Destroys an info block when it's not useful any more.
296  */
297
298 void swinfo_destroy(swinfo *sw)
299 {
300   swfield *f;
301
302   for (f = swfields; f->name; f++) {
303     if (SWINFO(sw, f->off))
304       free(SWINFO(sw, f->off));
305   }
306 }
307
308 /* --- @swinfo_update@ --- *
309  *
310  * Arguments:   @swinfo *sw@ = pointer to info block
311  *              @swinfo *skel@ = pointer to skeleton values
312  *
313  * Returns:     ---
314  *
315  * Use:         Updates the fields in an information block, except for the
316  *              architecture names stuff.  (I'll leave that for later,
317  *              because it's rather more involved.
318  */
319
320 void swinfo_update(swinfo *sw, swinfo *skel)
321 {
322   char ubuf[20];
323   char dbuf[20];
324   swfield *f;
325
326   /* --- Set up a default maintainer --- */
327
328   if (!skel->maintainer && !sw->maintainer) {
329     char *u = getenv("USER");
330
331     if (!u)
332       u = getenv("LOGNAME");
333     if (!u) {
334       struct passwd *pw = getpwuid(getuid());
335       if (!pw) {
336         moan("you don't seem to exist");
337         sprintf(ubuf, "uid %i", (int)getuid());
338         u = ubuf;
339       } else
340         u = pw->pw_name;
341     }
342     skel->maintainer = u;
343   }
344
345   /* --- Set up a default date --- */
346
347   {
348     time_t t = time(0);
349     struct tm *tm = localtime(&t);
350     strftime(dbuf, sizeof(dbuf), "%Y-%m-%d", tm);
351     skel->date = dbuf;
352   }
353
354   /* --- Set a default architecture list --- */
355
356   if (!skel->arch && !sw->arch)
357     skel->arch = "";
358
359   /* --- Grind through all the fields --- */
360
361   for (f = swfields; f->name; f++) {
362     if (!SWINFO(skel, f->off) || strcmp(SWINFO(skel, f->off), "-") == 0)
363       continue;
364     if (SWINFO(sw, f->off))
365       free(SWINFO(sw, f->off));
366     SWINFO(sw, f->off) = xstrdup(SWINFO(skel, f->off));
367   }
368 }
369
370 /*----- Subcommands -------------------------------------------------------*/
371
372 /* --- @sw_setup@ --- */
373
374 int sw_setup(int argc, char *argv[])
375 {
376   swinfo sw, skel;
377   if (argc < 3 || argc > 4)
378     die(1, "Usage: setup PACKAGE VERSION [MAINTAINER]");
379   swinfo_fetch(&sw);
380   swinfo_clear(&skel);
381   skel.package = argv[1];
382   skel.version = argv[2];
383   if (argv[3])
384     skel.maintainer = argv[3];
385   swinfo_update(&sw, &skel);
386   swinfo_put(&sw);
387   return (0);
388 }
389
390 /* --- @sw_status@ --- */
391
392 int sw_status(int argc, char *argv[])
393 {
394   swinfo sw;
395   swfield *f;
396   if (swinfo_fetch(&sw)) {
397     die(1, "couldn't read build status: %s (try running setup)",
398         strerror(errno));
399   }
400   for (f = swfields; f->name; f++) {
401     if (SWINFO(&sw, f->off))
402       printf("%s: %s\n", f->desc, SWINFO(&sw, f->off));
403   }
404   return (0);
405 }
406
407 /* --- @sw_commit@ --- */
408
409 int sw_commit(int argc, char *argv[])
410 {
411   swinfo sw;
412
413   if (argc != 1)
414     die(1, "Usage: commit");
415   if (swinfo_fetch(&sw)) {
416     die(1, "couldn't read build status: %s (try running setup)",
417         strerror(errno));
418   }
419
420   /* --- Make sure everything has been built properly --- */
421
422   {
423     archcons *a, *aa;
424     archcons *all = arch_readtab();
425
426     if (sw.arch) {
427       for (a = aa = arch_filter(all, sw.arch, 0, 0); a; a = a->cdr)
428         a->car->flags |= archFlag_built;
429       arch_free(aa);
430     }
431
432     aa = arch_filter(all, sw.only_arch, archFlag_built, 0);
433     if (aa) {
434       const char *sep = "";
435       fprintf(stderr, "%s: not built for ", QUIS);
436       for (a = aa; a; a = a->cdr) {
437         fprintf(stderr, "%s%s", sep, a->car->arch);
438         sep = ", ";
439       }
440       fputc('\n', stderr);
441       exit(1);
442     }
443     arch_free(aa);
444   }
445
446   /* --- Run the local precommit check script --- */
447
448   {
449     pid_t kid;
450     struct sigaction sa, sa_int, sa_quit;
451     int status;
452
453     /* --- Make sure I can trap the child's death --- */
454
455     sa.sa_handler = SIG_IGN;
456     sigemptyset(&sa.sa_mask);
457     sa.sa_flags = 0;
458     if (sigaction(SIGINT, &sa, &sa_int) || sigaction(SIGQUIT, &sa, &sa_quit))
459       die(1, "couldn't set signal dispositions: %s", strerror(errno));
460
461     /* --- Spawn off a child process --- */
462
463     fflush(0);
464     kid = fork();
465     if (kid < 0)
466       die(1, "error from fork: %s", strerror(errno));
467     if (kid == 0) {
468       sym_table t;
469
470       /* --- Re-enable signals --- */
471
472       sigaction(SIGINT, &sa_int, 0);
473       sigaction(SIGQUIT, &sa_quit, 0);
474
475       /* --- Set up the environment --- */
476
477       sym_create(&t);
478       env_import(&t, environ);
479       env_put(&t, "SW_PACKAGE", sw.package);
480       env_put(&t, "SW_VERSION", sw.version);
481       env_put(&t, "SW_MAINTAINER", sw.maintainer);
482       env_put(&t, "SW_DATE", sw.date);
483       env_put(&t, "SW_ARCHLIST", sw.arch);
484       env_put(&t, "SW_PREFIX", PREFIX);
485       environ = env_export(&t);
486
487       /* --- Run the commit check script --- */
488
489       execl(PREFIX "/share/sw-precommit", "sw-precommit",
490             sw.package, (char *)0);
491       if (errno == ENOENT)
492         _exit(0);
493       die(1, "couldn't run " PREFIX "/share/sw-precommit: %s",
494           strerror(errno));
495     }
496
497     /* --- Wait for the child to finish --- */
498
499     if (waitpid(kid, &status, 0) < 0)
500       die(1, "error waiting for child: %s", strerror(errno));
501     if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0))
502       die(1, "sw-precommit failed");
503     sigaction(SIGINT, &sa_int, 0);
504     sigaction(SIGQUIT, &sa_quit, 0);
505   }
506
507   /* --- Write to the index file --- */
508
509   {
510     FILE *fp = fopen(PREFIX "/sw-index", "a");
511     swfield *f;
512     const char *sep = "";
513
514     if (!fp)
515       die(1, "couldn't open index file: %s", strerror(errno));
516     if (lock_file(fileno(fp), LOCK_EXCL))
517       die(1, "couldn't obtain lock on index file: %s", strerror(errno));
518
519     for (f = swfields; f->name; f++) {
520       if (SWINFO(&sw, f->off)) {
521         fprintf(fp, "%s%s = %s", sep, f->name, SWINFO(&sw, f->off));
522         sep = "; ";
523       }
524     }
525     fputc('\n', fp);
526     fclose(fp);
527   }
528
529   return (0);
530 }
531
532 /*----- That's all, folks -------------------------------------------------*/