usr.sbin/makefs: Sync with sys/vfs/hammer2
[dragonfly.git] / usr.bin / dsynth / repo.c
blobbde0b602673fc5811e0161f567ac935e3f4bc056
1 /*
2 * Copyright (c) 2019-2020 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * This code uses concepts and configuration based on 'synth', by
8 * John R. Marino <draco@marino.st>, which was written in ada.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * 3. Neither the name of The DragonFly Project nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific, prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
37 #include "dsynth.h"
38 #include <openssl/md5.h>
40 typedef struct pinfo {
41 struct pinfo *next;
42 char *spath;
43 int foundit;
44 int inlocks;
45 } pinfo_t;
47 static void removePackagesMetaRecurse(pkg_t *pkg);
48 static int pinfocmp(const void *s1, const void *s2);
49 static void scanit(const char *path, const char *subpath,
50 int *countp, pinfo_t ***list_tailp,
51 int inlocks);
52 pinfo_t *pinfofind(pinfo_t **ary, int count, char *spath);
53 static void childRebuildRepo(bulk_t *bulk);
54 static void scandeletenew(const char *path);
56 static void rebuildTerminateSignal(int signo);
57 static char *md5lkfile(char *rpath, int which);
58 static int lkdircount(char *buf);
60 static char *RebuildRemovePath;
62 void
63 DoRebuildRepo(int ask)
65 bulk_t *bulk;
66 FILE *fp;
67 int fd;
68 char tpath[256];
69 const char *sufx;
71 if (ask) {
72 if (askyn("Rebuild the repository? ") == 0)
73 return;
77 * Scan the repository for temporary .new files and delete them.
79 scandeletenew(RepositoryPath);
82 * Generate temporary file
84 snprintf(tpath, sizeof(tpath), "/tmp/meta.XXXXXXXX.conf");
86 signal(SIGTERM, rebuildTerminateSignal);
87 signal(SIGINT, rebuildTerminateSignal);
88 signal(SIGHUP, rebuildTerminateSignal);
90 RebuildRemovePath = tpath;
92 sufx = UsePkgSufx;
93 fd = mkostemps(tpath, 5, 0);
94 if (fd < 0)
95 dfatal_errno("Cannot create %s", tpath);
96 fp = fdopen(fd, "w");
97 fprintf(fp, "version = %d;\n", MetaVersion);
98 fprintf(fp, "packing_format = \"%s\";\n", sufx + 1);
99 fclose(fp);
102 * Run the operation under our bulk infrastructure to
103 * get the correct environment.
105 initbulk(childRebuildRepo, 1);
106 queuebulk(tpath, NULL, NULL, NULL);
107 bulk = getbulk();
109 if (bulk->r1)
110 printf("Rebuild succeeded\n");
111 else
112 printf("Rebuild failed\n");
113 donebulk();
115 remove(tpath);
118 static void
119 repackage(const char *basepath, const char *basefile,
120 const char *decomp_suffix, const char *comp_suffix,
121 const char *decomp, const char *comp);
123 static void
124 childRebuildRepo(bulk_t *bulk)
126 FILE *fp;
127 char *ptr;
128 size_t len;
129 pid_t pid;
130 const char *cav[MAXCAC];
131 char *pkg_path;
132 int cac;
133 int repackage_mode = 0;
136 * We have to use the pkg-static that we built as part of the
137 * build process to rebuild the repo because the system pkg might
138 * not be compatible with the repo format changes made in 1.17.
140 asprintf(&pkg_path, "%s/Template/usr/local/sbin/pkg-static", BuildBase);
142 cac = 0;
143 cav[cac++] = pkg_path;
144 cav[cac++] = "repo";
145 cav[cac++] = "-m";
146 cav[cac++] = bulk->s1;
147 cav[cac++] = "-o";
148 cav[cac++] = PackagesPath;
151 * The yaml needs to generate paths relative to PackagePath
153 if (strncmp(PackagesPath, RepositoryPath, strlen(PackagesPath)) == 0)
154 cav[cac++] = PackagesPath;
155 else
156 cav[cac++] = RepositoryPath;
158 printf("pkg repo -m %s -o %s %s\n", bulk->s1, cav[cac-2], cav[cac-1]);
160 fp = dexec_open(NULL, cav, cac, &pid, NULL, 1, 0);
161 while ((ptr = fgetln(fp, &len)) != NULL)
162 fwrite(ptr, 1, len, stdout);
163 if (dexec_close(fp, pid) == 0)
164 bulk->r1 = strdup("");
167 * Check package version. Pkg version 1.12 and later generates
168 * the proper repo compression format. Prior to that version
169 * the repo directive always generated .txz files.
171 cac = 0;
172 cav[cac++] = pkg_path;
173 cav[cac++] = "-v";
174 fp = dexec_open(NULL, cav, cac, &pid, NULL, 1, 0);
175 if ((ptr = fgetln(fp, &len)) != NULL && len > 0) {
176 int v1;
177 int v2;
179 ptr[len-1] = 0;
180 if (sscanf(ptr, "%d.%d", &v1, &v2) == 2) {
181 printf("pkg repo - pkg version: %d.%d\n", v1, v2);
182 if (v1 > 1 || (v1 == 1 && v2 >= 12))
183 repackage_mode = 1;
186 dexec_close(fp, pid);
189 * Repackage the .txz files created by pkg repo if necessary
191 if (repackage_mode == 0 && strcmp(UsePkgSufx, ".txz") != 0) {
192 const char *comp;
193 const char *decomp;
195 printf("pkg repo - recompressing digests and packagesite\n");
197 if (strcmp(UsePkgSufx, ".tar") == 0) {
198 decomp = "unxz";
199 comp = "cat";
200 } else if (strcmp(UsePkgSufx, ".tgz") == 0) {
201 decomp = "unxz";
202 comp = "gzip";
203 } else if (strcmp(UsePkgSufx, ".tbz") == 0) {
204 decomp = "unxz";
205 comp = "bzip";
206 } else if (strcmp(UsePkgSufx, ".tzst") == 0) {
207 decomp = "unxz";
208 comp = "zstd";
209 } else {
210 dfatal("recompressing as %s not supported",
211 UsePkgSufx);
212 decomp = "unxz";
213 comp = "cat";
215 repackage(PackagesPath, "digests",
216 ".txz", UsePkgSufx,
217 decomp, comp);
218 repackage(PackagesPath, "packagesite",
219 ".txz", UsePkgSufx,
220 decomp, comp);
221 } else if (repackage_mode == 1 && strcmp(UsePkgSufx, ".txz") != 0) {
222 const char *comp;
223 const char *decomp;
225 printf("pkg repo - recompressing meta\n");
227 if (strcmp(UsePkgSufx, ".tar") == 0) {
228 decomp = "cat";
229 comp = "xz";
230 } else if (strcmp(UsePkgSufx, ".tgz") == 0) {
231 decomp = "gunzip";
232 comp = "xz";
233 } else if (strcmp(UsePkgSufx, ".tbz") == 0) {
234 decomp = "bunzip2";
235 comp = "xz";
236 } else if (strcmp(UsePkgSufx, ".tzst") == 0) {
237 decomp = "unzstd";
238 comp = "xz";
239 } else {
240 dfatal("recompressing from %s not supported",
241 UsePkgSufx);
242 decomp = "cat";
243 comp = "cat";
245 repackage(PackagesPath, "meta",
246 UsePkgSufx, ".txz",
247 decomp, comp);
249 free (pkg_path);
252 static
253 void
254 repackage(const char *basepath, const char *basefile,
255 const char *decomp_suffix, const char *comp_suffix,
256 const char *decomp, const char *comp)
258 char *buf;
260 asprintf(&buf, "%s < %s/%s%s | %s > %s/%s%s",
261 decomp, basepath, basefile, decomp_suffix,
262 comp, basepath, basefile, comp_suffix);
263 if (system(buf) != 0) {
264 dfatal("command failed: %s", buf);
266 free(buf);
269 void
270 DoUpgradePkgs(pkg_t *pkgs __unused, int ask __unused)
274 void
275 PurgeDistfiles(pkg_t *pkgs)
277 pinfo_t *list;
278 pinfo_t *item;
279 pinfo_t **list_tail;
280 pinfo_t **ary;
281 char *dstr;
282 char *buf;
283 int count;
284 int delcount;
285 int i;
287 printf("Scanning distfiles... ");
288 fflush(stdout);
289 count = 0;
290 list = NULL;
291 list_tail = &list;
292 scanit(DistFilesPath, NULL, &count, &list_tail, 0);
293 printf("Checking %d distfiles\n", count);
294 fflush(stdout);
296 ary = calloc(count, sizeof(pinfo_t *));
297 for (i = 0; i < count; ++i) {
298 ary[i] = list;
299 list = list->next;
301 ddassert(list == NULL);
302 qsort(ary, count, sizeof(pinfo_t *), pinfocmp);
304 for (; pkgs; pkgs = pkgs->bnext) {
305 if (pkgs->distfiles == NULL || pkgs->distfiles[0] == 0)
306 continue;
307 ddprintf(0, "distfiles %s\n", pkgs->distfiles);
308 dstr = strtok(pkgs->distfiles, " \t");
309 while (dstr) {
310 for (;;) {
312 * Look for distfile
314 if (pkgs->distsubdir) {
315 asprintf(&buf, "%s/%s",
316 pkgs->distsubdir, dstr);
317 } else {
318 buf = dstr;
320 item = pinfofind(ary, count, buf);
321 if (item)
322 item->foundit = 1;
323 if (item && item->inlocks == 0) {
325 * Look for the lock file
327 int scount;
329 scount = lkdircount(buf);
331 for (i = 0; i <= scount; ++i) {
332 item = pinfofind(ary, count,
333 md5lkfile(buf, i));
334 if (item)
335 item->foundit = 1;
340 * Cleanup and iterate
342 if (buf != dstr) {
343 free(buf);
344 buf = NULL;
346 if (strrchr(dstr, ':') == NULL)
347 break;
348 *strrchr(dstr, ':') = 0;
350 dstr = strtok(NULL, " \t");
354 delcount = 0;
355 for (i = 0; i < count; ++i) {
356 item = ary[i];
357 if (item->foundit == 0) {
358 ++delcount;
361 if (delcount == 0) {
362 printf("No obsolete source files out of %d found\n", count);
363 } else if (askyn("Delete %d of %d items? ", delcount, count)) {
364 printf("Deleting %d/%d obsolete source distfiles\n",
365 delcount, count);
366 for (i = 0; i < count; ++i) {
367 item = ary[i];
368 if (item->foundit == 0) {
369 asprintf(&buf, "%s/%s",
370 DistFilesPath, item->spath);
371 if (remove(buf) < 0)
372 printf("Cannot delete %s\n", buf);
373 else
374 printf("Deleted %s\n", item->spath);
375 free(buf);
381 free(ary);
384 void
385 RemovePackages(pkg_t *list)
387 pkg_t *scan;
388 char *path;
390 for (scan = list; scan; scan = scan->bnext) {
391 if ((scan->flags & PKGF_MANUALSEL) == 0)
392 continue;
393 if (scan->pkgfile) {
394 scan->flags &= ~PKGF_PACKAGED;
395 scan->pkgfile_size = 0;
396 asprintf(&path, "%s/%s", RepositoryPath, scan->pkgfile);
397 if (remove(path) == 0)
398 printf("Removed: %s\n", path);
399 free(path);
401 if (scan->pkgfile == NULL ||
402 (scan->flags & (PKGF_DUMMY | PKGF_META))) {
403 removePackagesMetaRecurse(scan);
408 static void
409 removePackagesMetaRecurse(pkg_t *pkg)
411 pkglink_t *link;
412 pkg_t *scan;
413 char *path;
415 PKGLIST_FOREACH(link, &pkg->idepon_list) {
416 scan = link->pkg;
417 if (scan == NULL)
418 continue;
419 if (scan->pkgfile == NULL ||
420 (scan->flags & (PKGF_DUMMY | PKGF_META))) {
421 removePackagesMetaRecurse(scan);
422 continue;
424 scan->flags &= ~PKGF_PACKAGED;
425 scan->pkgfile_size = 0;
427 asprintf(&path, "%s/%s", RepositoryPath, scan->pkgfile);
428 if (remove(path) == 0)
429 printf("Removed: %s\n", path);
430 free(path);
434 static int
435 pinfocmp(const void *s1, const void *s2)
437 const pinfo_t *item1 = *(const pinfo_t *const*)s1;
438 const pinfo_t *item2 = *(const pinfo_t *const*)s2;
440 return (strcmp(item1->spath, item2->spath));
443 pinfo_t *
444 pinfofind(pinfo_t **ary, int count, char *spath)
446 pinfo_t *item;
447 int res;
448 int b;
449 int e;
450 int m;
452 b = 0;
453 e = count;
454 while (b != e) {
455 m = b + (e - b) / 2;
456 item = ary[m];
457 res = strcmp(spath, item->spath);
458 if (res == 0)
459 return item;
460 if (res < 0) {
461 e = m;
462 } else {
463 b = m + 1;
466 return NULL;
469 void
470 scanit(const char *path, const char *subpath,
471 int *countp, pinfo_t ***list_tailp,
472 int inlocks)
474 struct dirent *den;
475 pinfo_t *item;
476 char *npath;
477 char *spath;
478 DIR *dir;
479 struct stat st;
481 if ((dir = opendir(path)) != NULL) {
482 while ((den = readdir(dir)) != NULL) {
483 if (den->d_namlen == 1 && den->d_name[0] == '.')
484 continue;
485 if (den->d_namlen == 2 && den->d_name[0] == '.' &&
486 den->d_name[1] == '.')
487 continue;
488 asprintf(&npath, "%s/%s", path, den->d_name);
489 if (lstat(npath, &st) < 0) {
490 free(npath);
491 continue;
493 if (S_ISDIR(st.st_mode)) {
494 int sublocks;
496 sublocks =
497 (strcmp(den->d_name, ".locks") == 0);
499 if (subpath) {
500 asprintf(&spath, "%s/%s",
501 subpath, den->d_name);
502 scanit(npath, spath, countp,
503 list_tailp, sublocks);
504 free(spath);
505 } else {
506 scanit(npath, den->d_name, countp,
507 list_tailp, sublocks);
509 } else if (S_ISREG(st.st_mode)) {
510 item = calloc(1, sizeof(*item));
511 if (subpath) {
512 asprintf(&item->spath, "%s/%s",
513 subpath, den->d_name);
514 } else {
515 item->spath = strdup(den->d_name);
517 item->inlocks = inlocks;
519 **list_tailp = item;
520 *list_tailp = &item->next;
521 ++*countp;
522 ddprintf(0, "scan %s\n", item->spath);
524 free(npath);
526 closedir(dir);
531 * This removes any .new files left over in the repo. These can wind
532 * being left around when dsynth is killed.
534 static void
535 scandeletenew(const char *path)
537 struct dirent *den;
538 const char *ptr;
539 DIR *dir;
540 char *buf;
542 if ((dir = opendir(path)) == NULL)
543 dfatal_errno("Cannot scan directory %s", path);
544 while ((den = readdir(dir)) != NULL) {
545 if ((ptr = strrchr(den->d_name, '.')) != NULL &&
546 strcmp(ptr, ".new") == 0) {
547 asprintf(&buf, "%s/%s", path, den->d_name);
548 if (remove(buf) < 0)
549 dfatal_errno("remove: Garbage %s\n", buf);
550 printf("Deleted Garbage %s\n", buf);
551 free(buf);
554 closedir(dir);
557 static void
558 rebuildTerminateSignal(int signo __unused)
560 if (RebuildRemovePath)
561 remove(RebuildRemovePath);
562 exit(1);
567 * There will be a .locks sub-directory in /usr/distfiles and also
568 * in each sub-directory underneath it containing the MD5 sums for
569 * the files in that subdirectory.
571 * This is a bit of a mess. Sometimes the .locks/ for a subdirectory
572 * are in parentdir/.locks and not parentdir/subdir/.locks. The invocation
573 * of do-fetch can be a bit messy so we look for a .locks subdir everywhere.
575 * The /usr/dports/Mk/Scripts/do-fetch.sh script uses 'echo blah | md5',
576 * so we have to add a newline to the buffer being md5'd.
578 * The pass-in rpath is relative to the distfiles base.
580 static char *
581 md5lkfile(char *rpath, int which_slash)
583 static char mstr[128];
584 static char lkfile[128];
585 uint8_t digest[MD5_DIGEST_LENGTH];
586 int bplen;
587 int i;
589 bplen = 0;
590 for (i = 0; i < which_slash; ++i) {
591 while (rpath[bplen] && rpath[bplen] != '/')
592 ++bplen;
593 if (rpath[bplen])
594 ++bplen;
596 snprintf(mstr, sizeof(mstr), "%s\n", rpath + bplen);
597 MD5(mstr, strlen(mstr), digest);
599 snprintf(lkfile, sizeof(lkfile),
600 "%*.*s.locks/"
601 "%02x%02x%02x%02x%02x%02x%02x%02x"
602 "%02x%02x%02x%02x%02x%02x%02x%02x"
603 ".lk",
604 bplen, bplen, rpath,
605 digest[0], digest[1], digest[2], digest[3],
606 digest[4], digest[5], digest[6], digest[7],
607 digest[8], digest[9], digest[10], digest[11],
608 digest[12], digest[13], digest[14], digest[15]);
610 return lkfile;
613 static int
614 lkdircount(char *buf)
616 int i;
617 int n;
619 n = 0;
620 for (i = 0; buf[i]; ++i) {
621 if (buf[i] == '/')
622 ++n;
624 return n;