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, const char *sufx, 115 const char *comp, const char *decomp); 116 117 static void 118 childRebuildRepo(bulk_t *bulk) 119 { 120 FILE *fp; 121 char *ptr; 122 size_t len; 123 pid_t pid; 124 const char *cav[MAXCAC]; 125 int cac; 126 int repackage_needed = 1; 127 128 cac = 0; 129 cav[cac++] = PKG_BINARY; 130 cav[cac++] = "repo"; 131 cav[cac++] = "-m"; 132 cav[cac++] = bulk->s1; 133 cav[cac++] = "-o"; 134 cav[cac++] = PackagesPath; 135 136 /* 137 * The yaml needs to generate paths relative to PackagePath 138 */ 139 if (strncmp(PackagesPath, RepositoryPath, strlen(PackagesPath)) == 0) 140 cav[cac++] = PackagesPath; 141 else 142 cav[cac++] = RepositoryPath; 143 144 printf("pkg repo -m %s -o %s %s\n", bulk->s1, cav[cac-2], cav[cac-1]); 145 146 fp = dexec_open(cav, cac, &pid, NULL, 1, 0); 147 while ((ptr = fgetln(fp, &len)) != NULL) 148 fwrite(ptr, 1, len, stdout); 149 if (dexec_close(fp, pid) == 0) 150 bulk->r1 = strdup(""); 151 152 /* 153 * Check package version. Pkg version 1.12 and later generates 154 * the proper repo compression format. Prior to that version 155 * the repo directive always generated .txz files. 156 */ 157 cac = 0; 158 cav[cac++] = PKG_BINARY; 159 cav[cac++] = "-v"; 160 fp = dexec_open(cav, cac, &pid, NULL, 1, 0); 161 if ((ptr = fgetln(fp, &len)) != NULL && len > 0) { 162 int v1; 163 int v2; 164 165 ptr[len-1] = 0; 166 if (sscanf(ptr, "%d.%d", &v1, &v2) == 2) { 167 if (v1 > 1 || (v1 == 1 && v2 >= 12)) 168 repackage_needed = 0; 169 } 170 } 171 dexec_close(fp, pid); 172 173 /* 174 * Repackage the .txz files created by pkg repo if necessary 175 */ 176 if (repackage_needed && strcmp(UsePkgSufx, ".txz") != 0) { 177 const char *comp; 178 const char *decomp; 179 180 printf("pkg repo - version requires repackaging\n"); 181 182 if (strcmp(UsePkgSufx, ".tar") == 0) { 183 decomp = "unxz"; 184 comp = "cat"; 185 } else if (strcmp(UsePkgSufx, ".tgz") == 0) { 186 decomp = "unxz"; 187 comp = "gzip"; 188 } else if (strcmp(UsePkgSufx, ".tbz") == 0) { 189 decomp = "unxz"; 190 comp = "bzip"; 191 } else { 192 dfatal("repackaging as %s not supported", UsePkgSufx); 193 decomp = "unxz"; 194 comp = "cat"; 195 } 196 repackage(PackagesPath, "digests", UsePkgSufx, 197 comp, decomp); 198 repackage(PackagesPath, "packagesite", UsePkgSufx, 199 comp, decomp); 200 } else if (strcmp(UsePkgSufx, ".txz") != 0) { 201 printf("pkg repo - version does not require repackaging\n"); 202 } 203 } 204 205 static 206 void 207 repackage(const char *basepath, const char *basefile, const char *sufx, 208 const char *comp, const char *decomp) 209 { 210 char *buf; 211 212 asprintf(&buf, "%s < %s/%s.txz | %s > %s/%s%s", 213 decomp, basepath, basefile, comp, basepath, basefile, sufx); 214 if (system(buf) != 0) { 215 dfatal("command failed: %s", buf); 216 } 217 free(buf); 218 } 219 220 void 221 DoUpgradePkgs(pkg_t *pkgs __unused, int ask __unused) 222 { 223 dfatal("Not Implemented"); 224 } 225 226 void 227 PurgeDistfiles(pkg_t *pkgs) 228 { 229 pinfo_t *list; 230 pinfo_t *item; 231 pinfo_t **list_tail; 232 pinfo_t **ary; 233 char *dstr; 234 char *buf; 235 int count; 236 int delcount; 237 int i; 238 239 printf("Scanning distfiles... "); 240 fflush(stdout); 241 count = 0; 242 list = NULL; 243 list_tail = &list; 244 scanit(DistFilesPath, NULL, &count, &list_tail); 245 printf("Checking %d distfiles\n", count); 246 fflush(stdout); 247 248 ary = calloc(count, sizeof(pinfo_t *)); 249 for (i = 0; i < count; ++i) { 250 ary[i] = list; 251 list = list->next; 252 } 253 ddassert(list == NULL); 254 qsort(ary, count, sizeof(pinfo_t *), pinfocmp); 255 256 for (; pkgs; pkgs = pkgs->bnext) { 257 if (pkgs->distfiles == NULL || pkgs->distfiles[0] == 0) 258 continue; 259 ddprintf(0, "distfiles %s\n", pkgs->distfiles); 260 dstr = strtok(pkgs->distfiles, " \t"); 261 while (dstr) { 262 for (;;) { 263 if (pkgs->distsubdir) { 264 asprintf(&buf, "%s/%s", 265 pkgs->distsubdir, dstr); 266 item = pinfofind(ary, count, buf); 267 ddprintf(0, "TEST %s %p\n", buf, item); 268 free(buf); 269 buf = NULL; 270 } else { 271 item = pinfofind(ary, count, dstr); 272 ddprintf(0, "TEST %s %p\n", dstr, item); 273 } 274 if (item) { 275 item->foundit = 1; 276 break; 277 } 278 if (strrchr(dstr, ':') == NULL) 279 break; 280 *strrchr(dstr, ':') = 0; 281 } 282 dstr = strtok(NULL, " \t"); 283 } 284 } 285 286 delcount = 0; 287 for (i = 0; i < count; ++i) { 288 item = ary[i]; 289 if (item->foundit == 0) { 290 ++delcount; 291 } 292 } 293 if (askyn("Delete %d of %d items? ", delcount, count)) { 294 printf("Deleting %d/%d obsolete source distfiles\n", 295 delcount, count); 296 for (i = 0; i < count; ++i) { 297 item = ary[i]; 298 if (item->foundit == 0) { 299 asprintf(&buf, "%s/%s", 300 DistFilesPath, item->spath); 301 if (remove(buf) < 0) 302 printf("Cannot delete %s\n", buf); 303 free(buf); 304 } 305 } 306 } 307 308 309 free(ary); 310 } 311 312 void 313 RemovePackages(pkg_t *list) 314 { 315 pkg_t *scan; 316 char *path; 317 318 for (scan = list; scan; scan = scan->bnext) { 319 if ((scan->flags & PKGF_MANUALSEL) == 0) 320 continue; 321 if (scan->pkgfile) { 322 scan->flags &= ~PKGF_PACKAGED; 323 scan->pkgfile_size = 0; 324 asprintf(&path, "%s/%s", RepositoryPath, scan->pkgfile); 325 if (remove(path) == 0) 326 printf("Removed: %s\n", path); 327 free(path); 328 } 329 if (scan->pkgfile == NULL || 330 (scan->flags & (PKGF_DUMMY | PKGF_META))) { 331 removePackagesMetaRecurse(scan); 332 } 333 } 334 } 335 336 static void 337 removePackagesMetaRecurse(pkg_t *pkg) 338 { 339 pkglink_t *link; 340 pkg_t *scan; 341 char *path; 342 343 PKGLIST_FOREACH(link, &pkg->idepon_list) { 344 scan = link->pkg; 345 if (scan == NULL) 346 continue; 347 if (scan->pkgfile == NULL || 348 (scan->flags & (PKGF_DUMMY | PKGF_META))) { 349 removePackagesMetaRecurse(scan); 350 continue; 351 } 352 scan->flags &= ~PKGF_PACKAGED; 353 scan->pkgfile_size = 0; 354 355 asprintf(&path, "%s/%s", RepositoryPath, scan->pkgfile); 356 if (remove(path) == 0) 357 printf("Removed: %s\n", path); 358 free(path); 359 } 360 } 361 362 static int 363 pinfocmp(const void *s1, const void *s2) 364 { 365 const pinfo_t *item1 = *(const pinfo_t *const*)s1; 366 const pinfo_t *item2 = *(const pinfo_t *const*)s2; 367 368 return (strcmp(item1->spath, item2->spath)); 369 } 370 371 pinfo_t * 372 pinfofind(pinfo_t **ary, int count, char *spath) 373 { 374 pinfo_t *item; 375 int res; 376 int b; 377 int e; 378 int m; 379 380 b = 0; 381 e = count; 382 while (b != e) { 383 m = b + (e - b) / 2; 384 item = ary[m]; 385 res = strcmp(spath, item->spath); 386 if (res == 0) 387 return item; 388 if (res < 0) { 389 e = m; 390 } else { 391 b = m + 1; 392 } 393 } 394 return NULL; 395 } 396 397 void 398 scanit(const char *path, const char *subpath, 399 int *countp, pinfo_t ***list_tailp) 400 { 401 struct dirent *den; 402 pinfo_t *item; 403 char *npath; 404 char *spath; 405 DIR *dir; 406 struct stat st; 407 408 if ((dir = opendir(path)) != NULL) { 409 while ((den = readdir(dir)) != NULL) { 410 if (den->d_namlen == 1 && den->d_name[0] == '.') 411 continue; 412 if (den->d_namlen == 2 && den->d_name[0] == '.' && 413 den->d_name[1] == '.') 414 continue; 415 asprintf(&npath, "%s/%s", path, den->d_name); 416 if (lstat(npath, &st) < 0) { 417 free(npath); 418 continue; 419 } 420 if (S_ISDIR(st.st_mode)) { 421 if (subpath) { 422 asprintf(&spath, "%s/%s", 423 subpath, den->d_name); 424 scanit(npath, spath, 425 countp, list_tailp); 426 free(spath); 427 } else { 428 scanit(npath, den->d_name, 429 countp, list_tailp); 430 } 431 } else if (S_ISREG(st.st_mode)) { 432 item = calloc(1, sizeof(*item)); 433 if (subpath) { 434 asprintf(&item->spath, "%s/%s", 435 subpath, den->d_name); 436 } else { 437 item->spath = strdup(den->d_name); 438 } 439 **list_tailp = item; 440 *list_tailp = &item->next; 441 ++*countp; 442 ddprintf(0, "scan %s\n", item->spath); 443 } 444 free(npath); 445 } 446 closedir(dir); 447 } 448 } 449 450 /* 451 * This removes any .new files left over in the repo. These can wind 452 * being left around when dsynth is killed. 453 */ 454 static void 455 scandeletenew(const char *path) 456 { 457 struct dirent *den; 458 const char *ptr; 459 DIR *dir; 460 char *buf; 461 462 if ((dir = opendir(path)) == NULL) 463 dfatal_errno("Cannot scan directory %s", path); 464 while ((den = readdir(dir)) != NULL) { 465 if ((ptr = strrchr(den->d_name, '.')) != NULL && 466 strcmp(ptr, ".new") == 0) { 467 asprintf(&buf, "%s/%s", path, den->d_name); 468 if (remove(buf) < 0) 469 dfatal_errno("remove: Garbage %s\n", buf); 470 printf("Deleted Garbage %s\n", buf); 471 free(buf); 472 } 473 } 474 closedir(dir); 475 } 476 477 static void 478 rebuildTerminateSignal(int signo __unused) 479 { 480 if (RebuildRemovePath) 481 remove(RebuildRemovePath); 482 exit(1); 483 484 } 485