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