xref: /dragonfly/usr.bin/dsynth/repo.c (revision 47492050)
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