xref: /dflybsd-src/usr.bin/dsynth/repo.c (revision d78d3a2272f5ecf9e0b570e362128240417a1b85)
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 int pinfocmp(const void *s1, const void *s2);
46 static void scanit(const char *path, const char *subpath,
47 			int *countp, pinfo_t ***list_tailp);
48 pinfo_t *pinfofind(pinfo_t **ary, int count, char *spath);
49 static void scandeletenew(const char *path);
50 
51 void
52 DoRebuildRepo(int ask)
53 {
54 	char *buf;
55 
56 	if (ask) {
57 		if (askyn("Rebuild the repository? ") == 0)
58 			return;
59 	}
60 
61 	/*
62 	 * Scan the repository for temporary .new files and delete them.
63 	 */
64 	scandeletenew(RepositoryPath);
65 
66 	/*
67 	 * Issue the repo command to rebuild the repo
68 	 */
69 	asprintf(&buf, "pkg repo -o %s %s", PackagesPath, RepositoryPath);
70 	printf("Rebuilding repository\n");
71 	if (system(buf)) {
72 		printf("Rebuild failed\n");
73 	} else {
74 		printf("Rebuild succeeded\n");
75 	}
76 }
77 
78 void
79 DoUpgradePkgs(pkg_t *pkgs __unused, int ask __unused)
80 {
81 	dfatal("Not Implemented");
82 }
83 
84 void
85 PurgeDistfiles(pkg_t *pkgs)
86 {
87 	pinfo_t *list;
88 	pinfo_t *item;
89 	pinfo_t **list_tail;
90 	pinfo_t **ary;
91 	char *dstr;
92 	char *buf;
93 	int count;
94 	int delcount;
95 	int i;
96 
97 	printf("Scanning distfiles... ");
98 	fflush(stdout);
99 	count = 0;
100 	list = NULL;
101 	list_tail = &list;
102 	scanit(DistFilesPath, NULL, &count, &list_tail);
103 	printf("Checking %d distfiles\n", count);
104 	fflush(stdout);
105 
106 	ary = calloc(count, sizeof(pinfo_t *));
107 	for (i = 0; i < count; ++i) {
108 		ary[i] = list;
109 		list = list->next;
110 	}
111 	ddassert(list == NULL);
112 	qsort(ary, count, sizeof(pinfo_t *), pinfocmp);
113 
114 	for (; pkgs; pkgs = pkgs->bnext) {
115 		if (pkgs->distfiles == NULL || pkgs->distfiles[0] == 0)
116 			continue;
117 		ddprintf(0, "distfiles %s\n", pkgs->distfiles);
118 		dstr = strtok(pkgs->distfiles, " \t");
119 		while (dstr) {
120 			for (;;) {
121 				if (pkgs->distsubdir) {
122 					asprintf(&buf, "%s/%s",
123 						 pkgs->distsubdir, dstr);
124 					item = pinfofind(ary, count, buf);
125 					ddprintf(0, "TEST %s %p\n", buf, item);
126 					free(buf);
127 					buf = NULL;
128 				} else {
129 					item = pinfofind(ary, count, dstr);
130 					ddprintf(0, "TEST %s %p\n", dstr, item);
131 				}
132 				if (item) {
133 					item->foundit = 1;
134 					break;
135 				}
136 				if (strrchr(dstr, ':') == NULL)
137 					break;
138 				*strrchr(dstr, ':') = 0;
139 			}
140 			dstr = strtok(NULL, " \t");
141 		}
142 	}
143 
144 	delcount = 0;
145 	for (i = 0; i < count; ++i) {
146 		item = ary[i];
147 		if (item->foundit == 0) {
148 			++delcount;
149 		}
150 	}
151 	if (askyn("Delete %d of %d items? ", delcount, count)) {
152 		printf("Deleting %d/%d obsolete source distfiles\n",
153 		       delcount, count);
154 		for (i = 0; i < count; ++i) {
155 			item = ary[i];
156 			if (item->foundit == 0) {
157 				asprintf(&buf, "%s/%s",
158 					 DistFilesPath, item->spath);
159 				if (remove(buf) < 0)
160 					printf("Cannot delete %s\n", buf);
161 				free(buf);
162 			}
163 		}
164 	}
165 
166 
167 	free(ary);
168 }
169 
170 void
171 RemovePackages(pkg_t *pkgs __unused)
172 {
173 	dfatal("Not Implemented");
174 }
175 
176 static int
177 pinfocmp(const void *s1, const void *s2)
178 {
179 	const pinfo_t *item1 = *(const pinfo_t *const*)s1;
180 	const pinfo_t *item2 = *(const pinfo_t *const*)s2;
181 
182 	return (strcmp(item1->spath, item2->spath));
183 }
184 
185 pinfo_t *
186 pinfofind(pinfo_t **ary, int count, char *spath)
187 {
188 	pinfo_t *item;
189 	int res;
190 	int b;
191 	int e;
192 	int m;
193 
194 	b = 0;
195 	e = count;
196 	while (b != e) {
197 		m = b + (e - b) / 2;
198 		item = ary[m];
199 		res = strcmp(spath, item->spath);
200 		if (res == 0)
201 			return item;
202 		if (res < 0) {
203 			e = m;
204 		} else {
205 			b = m + 1;
206 		}
207 	}
208 	return NULL;
209 }
210 
211 void
212 scanit(const char *path, const char *subpath,
213        int *countp, pinfo_t ***list_tailp)
214 {
215 	struct dirent *den;
216 	pinfo_t *item;
217 	char *npath;
218 	char *spath;
219 	DIR *dir;
220 	struct stat st;
221 
222 	if ((dir = opendir(path)) != NULL) {
223 		while ((den = readdir(dir)) != NULL) {
224 			if (den->d_namlen == 1 && den->d_name[0] == '.')
225 				continue;
226 			if (den->d_namlen == 2 && den->d_name[0] == '.' &&
227 			    den->d_name[1] == '.')
228 				continue;
229 			asprintf(&npath, "%s/%s", path, den->d_name);
230 			if (lstat(npath, &st) < 0) {
231 				free(npath);
232 				continue;
233 			}
234 			if (S_ISDIR(st.st_mode)) {
235 				if (subpath) {
236 					asprintf(&spath, "%s/%s",
237 						 subpath, den->d_name);
238 					scanit(npath, spath,
239 					       countp, list_tailp);
240 					free(spath);
241 				} else {
242 					scanit(npath, den->d_name,
243 					       countp, list_tailp);
244 				}
245 			} else if (S_ISREG(st.st_mode)) {
246 				item = calloc(1, sizeof(*item));
247 				if (subpath) {
248 					asprintf(&item->spath, "%s/%s",
249 						 subpath, den->d_name);
250 				} else {
251 					item->spath = strdup(den->d_name);
252 				}
253 				**list_tailp = item;
254 				*list_tailp = &item->next;
255 				++*countp;
256 				ddprintf(0, "scan   %s\n", item->spath);
257 			}
258 			free(npath);
259 		}
260 		closedir(dir);
261 	}
262 }
263 
264 /*
265  * This removes any .new files left over in the repo.  These can wind
266  * being left around when dsynth is killed.
267  */
268 static void
269 scandeletenew(const char *path)
270 {
271 	struct dirent *den;
272 	const char *ptr;
273 	DIR *dir;
274 	char *buf;
275 
276 	if ((dir = opendir(path)) == NULL)
277 		dfatal_errno("Cannot scan directory %s", path);
278 	while ((den = readdir(dir)) != NULL) {
279 		if ((ptr = strrchr(den->d_name, '.')) != NULL &&
280 		    strcmp(ptr, ".new") == 0) {
281 			asprintf(&buf, "%s/%s", path, den->d_name);
282 			if (remove(buf) < 0)
283 				dfatal_errno("remove: Garbage %s\n", buf);
284 			printf("Deleted Garbage %s\n", buf);
285 			free(buf);
286 		}
287 	}
288 	closedir(dir);
289 }
290