1 /* 2 * Copyright (c) 2019 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * This code uses concepts and configuration based on 'synth', by 8 * John R. Marino <draco@marino.st>, which was written in ada. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 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. 23 * 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. 36 */ 37 #include "dsynth.h" 38 39 typedef struct pinfo { 40 struct pinfo *next; 41 char *spath; 42 int foundit; 43 } pinfo_t; 44 45 static void removePackagesMetaRecurse(pkg_t *pkg); 46 static int pinfocmp(const void *s1, const void *s2); 47 static void scanit(const char *path, const char *subpath, 48 int *countp, pinfo_t ***list_tailp); 49 pinfo_t *pinfofind(pinfo_t **ary, int count, char *spath); 50 static void childRebuildRepo(bulk_t *bulk); 51 static void scandeletenew(const char *path); 52 53 static void rebuildTerminateSignal(int signo); 54 55 static char *RebuildRemovePath; 56 57 void 58 DoRebuildRepo(int ask) 59 { 60 bulk_t *bulk; 61 FILE *fp; 62 int fd; 63 char tpath[256]; 64 const char *sufx; 65 66 if (ask) { 67 if (askyn("Rebuild the repository? ") == 0) 68 return; 69 } 70 71 /* 72 * Scan the repository for temporary .new files and delete them. 73 */ 74 scandeletenew(RepositoryPath); 75 76 /* 77 * Generate temporary file 78 */ 79 snprintf(tpath, sizeof(tpath), "/tmp/meta.XXXXXXXX.conf"); 80 81 signal(SIGTERM, rebuildTerminateSignal); 82 signal(SIGINT, rebuildTerminateSignal); 83 signal(SIGHUP, rebuildTerminateSignal); 84 85 RebuildRemovePath = tpath; 86 87 sufx = UsePkgSufx; 88 fd = mkostemps(tpath, 5, 0); 89 if (fd < 0) 90 dfatal_errno("Cannot create %s", tpath); 91 fp = fdopen(fd, "w"); 92 fprintf(fp, "version = 1;\n"); 93 fprintf(fp, "packing_format = \"%s\";\n", sufx + 1); 94 fclose(fp); 95 96 /* 97 * Run the operation under our bulk infrastructure to 98 * get the correct environment. 99 */ 100 initbulk(childRebuildRepo, 1); 101 queuebulk(tpath, NULL, NULL, NULL); 102 bulk = getbulk(); 103 104 if (bulk->r1) 105 printf("Rebuild succeeded\n"); 106 else 107 printf("Rebuild failed\n"); 108 donebulk(); 109 110 remove(tpath); 111 } 112 113 static void 114 repackage(const char *basepath, const char *basefile, 115 const char *decomp_suffix, const char *comp_suffix, 116 const char *decomp, const char *comp); 117 118 static void 119 childRebuildRepo(bulk_t *bulk) 120 { 121 FILE *fp; 122 char *ptr; 123 size_t len; 124 pid_t pid; 125 const char *cav[MAXCAC]; 126 int cac; 127 int repackage_mode = 0; 128 129 cac = 0; 130 cav[cac++] = PKG_BINARY; 131 cav[cac++] = "repo"; 132 cav[cac++] = "-m"; 133 cav[cac++] = bulk->s1; 134 cav[cac++] = "-o"; 135 cav[cac++] = PackagesPath; 136 137 /* 138 * The yaml needs to generate paths relative to PackagePath 139 */ 140 if (strncmp(PackagesPath, RepositoryPath, strlen(PackagesPath)) == 0) 141 cav[cac++] = PackagesPath; 142 else 143 cav[cac++] = RepositoryPath; 144 145 printf("pkg repo -m %s -o %s %s\n", bulk->s1, cav[cac-2], cav[cac-1]); 146 147 fp = dexec_open(cav, cac, &pid, NULL, 1, 0); 148 while ((ptr = fgetln(fp, &len)) != NULL) 149 fwrite(ptr, 1, len, stdout); 150 if (dexec_close(fp, pid) == 0) 151 bulk->r1 = strdup(""); 152 153 /* 154 * Check package version. Pkg version 1.12 and later generates 155 * the proper repo compression format. Prior to that version 156 * the repo directive always generated .txz files. 157 */ 158 cac = 0; 159 cav[cac++] = PKG_BINARY; 160 cav[cac++] = "-v"; 161 fp = dexec_open(cav, cac, &pid, NULL, 1, 0); 162 if ((ptr = fgetln(fp, &len)) != NULL && len > 0) { 163 int v1; 164 int v2; 165 166 ptr[len-1] = 0; 167 if (sscanf(ptr, "%d.%d", &v1, &v2) == 2) { 168 if (v1 > 1 || (v1 == 1 && v2 >= 12)) 169 repackage_mode = 1; 170 } 171 } 172 dexec_close(fp, pid); 173 174 /* 175 * Repackage the .txz files created by pkg repo if necessary 176 */ 177 if (repackage_mode == 0 && strcmp(UsePkgSufx, ".txz") != 0) { 178 const char *comp; 179 const char *decomp; 180 181 printf("pkg repo - recompressing digests and packagesite\n"); 182 183 if (strcmp(UsePkgSufx, ".tar") == 0) { 184 decomp = "unxz"; 185 comp = "cat"; 186 } else if (strcmp(UsePkgSufx, ".tgz") == 0) { 187 decomp = "unxz"; 188 comp = "gzip"; 189 } else if (strcmp(UsePkgSufx, ".tbz") == 0) { 190 decomp = "unxz"; 191 comp = "bzip"; 192 } else { 193 dfatal("recompressing as %s not supported", 194 UsePkgSufx); 195 decomp = "unxz"; 196 comp = "cat"; 197 } 198 repackage(PackagesPath, "digests", 199 ".txz", UsePkgSufx, 200 decomp, comp); 201 repackage(PackagesPath, "packagesite", 202 ".txz", UsePkgSufx, 203 decomp, comp); 204 } else if (repackage_mode == 1 && strcmp(UsePkgSufx, ".txz") != 0) { 205 const char *comp; 206 const char *decomp; 207 208 printf("pkg repo - recompressing meta\n"); 209 210 if (strcmp(UsePkgSufx, ".tar") == 0) { 211 decomp = "cat"; 212 comp = "xz"; 213 } else if (strcmp(UsePkgSufx, ".tgz") == 0) { 214 decomp = "gunzip"; 215 comp = "xz"; 216 } else if (strcmp(UsePkgSufx, ".tbz") == 0) { 217 decomp = "bunzip2"; 218 comp = "xz"; 219 } else { 220 dfatal("recompressing from %s not supported", 221 UsePkgSufx); 222 decomp = "cat"; 223 comp = "cat"; 224 } 225 repackage(PackagesPath, "meta", 226 UsePkgSufx, ".txz", 227 decomp, comp); 228 } 229 } 230 231 static 232 void 233 repackage(const char *basepath, const char *basefile, 234 const char *decomp_suffix, const char *comp_suffix, 235 const char *decomp, const char *comp) 236 { 237 char *buf; 238 239 asprintf(&buf, "%s < %s/%s%s | %s > %s/%s%s", 240 decomp, basepath, basefile, decomp_suffix, 241 comp, basepath, basefile, comp_suffix); 242 if (system(buf) != 0) { 243 dfatal("command failed: %s", buf); 244 } 245 free(buf); 246 } 247 248 void 249 DoUpgradePkgs(pkg_t *pkgs __unused, int ask __unused) 250 { 251 dfatal("Not Implemented"); 252 } 253 254 void 255 PurgeDistfiles(pkg_t *pkgs) 256 { 257 pinfo_t *list; 258 pinfo_t *item; 259 pinfo_t **list_tail; 260 pinfo_t **ary; 261 char *dstr; 262 char *buf; 263 int count; 264 int delcount; 265 int i; 266 267 printf("Scanning distfiles... "); 268 fflush(stdout); 269 count = 0; 270 list = NULL; 271 list_tail = &list; 272 scanit(DistFilesPath, NULL, &count, &list_tail); 273 printf("Checking %d distfiles\n", count); 274 fflush(stdout); 275 276 ary = calloc(count, sizeof(pinfo_t *)); 277 for (i = 0; i < count; ++i) { 278 ary[i] = list; 279 list = list->next; 280 } 281 ddassert(list == NULL); 282 qsort(ary, count, sizeof(pinfo_t *), pinfocmp); 283 284 for (; pkgs; pkgs = pkgs->bnext) { 285 if (pkgs->distfiles == NULL || pkgs->distfiles[0] == 0) 286 continue; 287 ddprintf(0, "distfiles %s\n", pkgs->distfiles); 288 dstr = strtok(pkgs->distfiles, " \t"); 289 while (dstr) { 290 for (;;) { 291 if (pkgs->distsubdir) { 292 asprintf(&buf, "%s/%s", 293 pkgs->distsubdir, dstr); 294 item = pinfofind(ary, count, buf); 295 ddprintf(0, "TEST %s %p\n", buf, item); 296 free(buf); 297 buf = NULL; 298 } else { 299 item = pinfofind(ary, count, dstr); 300 ddprintf(0, "TEST %s %p\n", dstr, item); 301 } 302 if (item) { 303 item->foundit = 1; 304 break; 305 } 306 if (strrchr(dstr, ':') == NULL) 307 break; 308 *strrchr(dstr, ':') = 0; 309 } 310 dstr = strtok(NULL, " \t"); 311 } 312 } 313 314 delcount = 0; 315 for (i = 0; i < count; ++i) { 316 item = ary[i]; 317 if (item->foundit == 0) { 318 ++delcount; 319 } 320 } 321 if (askyn("Delete %d of %d items? ", delcount, count)) { 322 printf("Deleting %d/%d obsolete source distfiles\n", 323 delcount, count); 324 for (i = 0; i < count; ++i) { 325 item = ary[i]; 326 if (item->foundit == 0) { 327 asprintf(&buf, "%s/%s", 328 DistFilesPath, item->spath); 329 if (remove(buf) < 0) 330 printf("Cannot delete %s\n", buf); 331 free(buf); 332 } 333 } 334 } 335 336 337 free(ary); 338 } 339 340 void 341 RemovePackages(pkg_t *list) 342 { 343 pkg_t *scan; 344 char *path; 345 346 for (scan = list; scan; scan = scan->bnext) { 347 if ((scan->flags & PKGF_MANUALSEL) == 0) 348 continue; 349 if (scan->pkgfile) { 350 scan->flags &= ~PKGF_PACKAGED; 351 scan->pkgfile_size = 0; 352 asprintf(&path, "%s/%s", RepositoryPath, scan->pkgfile); 353 if (remove(path) == 0) 354 printf("Removed: %s\n", path); 355 free(path); 356 } 357 if (scan->pkgfile == NULL || 358 (scan->flags & (PKGF_DUMMY | PKGF_META))) { 359 removePackagesMetaRecurse(scan); 360 } 361 } 362 } 363 364 static void 365 removePackagesMetaRecurse(pkg_t *pkg) 366 { 367 pkglink_t *link; 368 pkg_t *scan; 369 char *path; 370 371 PKGLIST_FOREACH(link, &pkg->idepon_list) { 372 scan = link->pkg; 373 if (scan == NULL) 374 continue; 375 if (scan->pkgfile == NULL || 376 (scan->flags & (PKGF_DUMMY | PKGF_META))) { 377 removePackagesMetaRecurse(scan); 378 continue; 379 } 380 scan->flags &= ~PKGF_PACKAGED; 381 scan->pkgfile_size = 0; 382 383 asprintf(&path, "%s/%s", RepositoryPath, scan->pkgfile); 384 if (remove(path) == 0) 385 printf("Removed: %s\n", path); 386 free(path); 387 } 388 } 389 390 static int 391 pinfocmp(const void *s1, const void *s2) 392 { 393 const pinfo_t *item1 = *(const pinfo_t *const*)s1; 394 const pinfo_t *item2 = *(const pinfo_t *const*)s2; 395 396 return (strcmp(item1->spath, item2->spath)); 397 } 398 399 pinfo_t * 400 pinfofind(pinfo_t **ary, int count, char *spath) 401 { 402 pinfo_t *item; 403 int res; 404 int b; 405 int e; 406 int m; 407 408 b = 0; 409 e = count; 410 while (b != e) { 411 m = b + (e - b) / 2; 412 item = ary[m]; 413 res = strcmp(spath, item->spath); 414 if (res == 0) 415 return item; 416 if (res < 0) { 417 e = m; 418 } else { 419 b = m + 1; 420 } 421 } 422 return NULL; 423 } 424 425 void 426 scanit(const char *path, const char *subpath, 427 int *countp, pinfo_t ***list_tailp) 428 { 429 struct dirent *den; 430 pinfo_t *item; 431 char *npath; 432 char *spath; 433 DIR *dir; 434 struct stat st; 435 436 if ((dir = opendir(path)) != NULL) { 437 while ((den = readdir(dir)) != NULL) { 438 if (den->d_namlen == 1 && den->d_name[0] == '.') 439 continue; 440 if (den->d_namlen == 2 && den->d_name[0] == '.' && 441 den->d_name[1] == '.') 442 continue; 443 asprintf(&npath, "%s/%s", path, den->d_name); 444 if (lstat(npath, &st) < 0) { 445 free(npath); 446 continue; 447 } 448 if (S_ISDIR(st.st_mode)) { 449 if (subpath) { 450 asprintf(&spath, "%s/%s", 451 subpath, den->d_name); 452 scanit(npath, spath, 453 countp, list_tailp); 454 free(spath); 455 } else { 456 scanit(npath, den->d_name, 457 countp, list_tailp); 458 } 459 } else if (S_ISREG(st.st_mode)) { 460 item = calloc(1, sizeof(*item)); 461 if (subpath) { 462 asprintf(&item->spath, "%s/%s", 463 subpath, den->d_name); 464 } else { 465 item->spath = strdup(den->d_name); 466 } 467 **list_tailp = item; 468 *list_tailp = &item->next; 469 ++*countp; 470 ddprintf(0, "scan %s\n", item->spath); 471 } 472 free(npath); 473 } 474 closedir(dir); 475 } 476 } 477 478 /* 479 * This removes any .new files left over in the repo. These can wind 480 * being left around when dsynth is killed. 481 */ 482 static void 483 scandeletenew(const char *path) 484 { 485 struct dirent *den; 486 const char *ptr; 487 DIR *dir; 488 char *buf; 489 490 if ((dir = opendir(path)) == NULL) 491 dfatal_errno("Cannot scan directory %s", path); 492 while ((den = readdir(dir)) != NULL) { 493 if ((ptr = strrchr(den->d_name, '.')) != NULL && 494 strcmp(ptr, ".new") == 0) { 495 asprintf(&buf, "%s/%s", path, den->d_name); 496 if (remove(buf) < 0) 497 dfatal_errno("remove: Garbage %s\n", buf); 498 printf("Deleted Garbage %s\n", buf); 499 free(buf); 500 } 501 } 502 closedir(dir); 503 } 504 505 static void 506 rebuildTerminateSignal(int signo __unused) 507 { 508 if (RebuildRemovePath) 509 remove(RebuildRemovePath); 510 exit(1); 511 512 } 513