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