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