sourcecheck.c 12 KB
Newer Older
1
/*  This file is part of "reprepro"
2
 *  Copyright (C) 2010,2011,2016 Bernhard R. Link
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
 */
#include <config.h>

Bernhard Link's avatar
Bernhard Link committed
18
#include <sys/types.h>
19 20
#include <assert.h>
#include <string.h>
21
#include <stdlib.h>
Bernhard Link's avatar
Bernhard Link committed
22
#include <stdio.h>
23 24 25 26

#include "error.h"
#include "distribution.h"
#include "trackingt.h"
27
#include "package.h"
28 29 30 31 32
#include "sourcecheck.h"

/* This is / will be the implementation of the
 *	unusedsources
 *	withoutsource
33 34
 *	reportcruft
 *	removecruft (to be implemented)
35 36 37 38 39 40
 * commands.
 *
 * Currently those only work with tracking enabled, but
 * are in this file as the implementation without tracking
 * will need similar infrastructure */

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

/* TODO: some tree might be more efficient, check how bad the comparisons are here */
struct info_source {
	struct info_source *next;
	char *name;
	struct info_source_version {
		struct info_source_version *next;
		char *version;
		bool used;
	} version;
};

static void free_source_info(struct info_source *s) {
	while (s != NULL) {
		struct info_source *h = s;
		s = s->next;

		while (h->version.next != NULL) {
			struct info_source_version *v = h->version.next;
			h->version.next = v->next;
			free(v->version);
			free(v);
		}
		free(h->version.version);
		free(h->name);
		free(h);
	}
}

70
static retvalue collect_source_versions(struct distribution *d, struct info_source **out) {
71 72
	struct info_source *root = NULL, *last = NULL;
	struct target *t;
73
	struct package_cursor cursor;
74 75 76
	retvalue result = RET_NOTHING, r;

	for (t = d->targets ; t != NULL ; t = t->next) {
77
		if (t->architecture != architecture_source)
78
			continue;
79
		r = package_openiterator(t, true, &cursor);
80 81 82 83
		if (RET_WAS_ERROR(r)) {
			RET_UPDATE(result, r);
			break;
		}
84
		while (package_next(&cursor)) {
85 86 87
			struct info_source **into = NULL;
			struct info_source_version *v;

88
			r = package_getversion(&cursor.current);
89 90 91 92 93 94
			if (!RET_IS_OK(r)) {
				RET_UPDATE(result, r);
				continue;
			}
			if (last != NULL) {
				int c;
95
				c = strcmp(cursor.current.name, last->name);
96 97 98 99 100 101 102 103
				if (c < 0) {
					/* start at the beginning */
					last = NULL;
				} else while (c > 0) {
					into = &last->next;
					if (last->next == NULL)
						break;
					last = last->next;
104
					c = strcmp(cursor.current.name, last->name);
105 106 107 108 109 110 111 112 113 114 115 116
					if (c == 0) {
						into = NULL;
						break;
					}
			       }
			}
			/* if into != NULL, place there,
			 * if last != NULL, already found */
			if (last == NULL) {
				into = &root;
				while ((last = *into) != NULL) {
					int c;
117
					c = strcmp(cursor.current.name, last->name);
118 119 120 121 122 123 124 125 126 127
					if (c == 0) {
						into = NULL;
						break;
					}
					if (c < 0)
						break;
					into = &last->next;
				}
			}
			if (into != NULL) {
128
				last = zNEW(struct info_source);
129 130 131 132
				if (FAILEDTOALLOC(last)) {
					result = RET_ERROR_OOM;
					break;
				}
133
				last->name = strdup(cursor.current.name);
134 135 136 137 138
				if (FAILEDTOALLOC(last->name)) {
					free(last);
					result = RET_ERROR_OOM;
					break;
				}
139 140 141 142 143 144 145 146
				last->version.version = package_dupversion(
						&cursor.current);
				if (FAILEDTOALLOC(last->version.version)) {
					result = RET_ERROR_OOM;
					free(last->name);
					free(last);
					break;
				}
147 148 149 150 151 152
				last->next = *into;
				*into = last;
				RET_UPDATE(result, RET_OK);
				continue;
			}
			assert (last != NULL);
153
			assert (strcmp(cursor.current.name, last->name)==0);
154 155

			v = &last->version;
156
			while (strcmp(v->version, cursor.current.version) != 0) {
157
				if (v->next == NULL) {
158
					v->next = zNEW(struct info_source_version);
159 160 161 162 163
					if (FAILEDTOALLOC(v->next)) {
						result = RET_ERROR_OOM;
						break;
					}
					v = v->next;
164 165 166 167 168 169
					v->version = package_dupversion(
							&cursor.current);
					if (FAILEDTOALLOC(v->version)) {
						result = RET_ERROR_OOM;
						break;
					}
170 171 172 173 174 175
					RET_UPDATE(result, RET_OK);
					break;
				}
				v = v->next;
			}
		}
176
		r = package_closeiterator(&cursor);
177 178 179 180 181 182 183 184 185 186 187 188 189 190
		if (RET_WAS_ERROR(r)) {
			RET_UPDATE(result, r);
			break;
		}
	}
	if (RET_IS_OK(result))
		*out = root;
	else {
		assert (result != RET_NOTHING || root == NULL);
		free_source_info(root);
	}
	return result;
}

191
static retvalue process_binaries(struct distribution *d, struct info_source *sources, retvalue (*action)(struct package *, void *), void *privdata) {
192
	struct target *t;
193
	struct package_cursor cursor;
194 195 196
	retvalue result = RET_NOTHING, r;

	for (t = d->targets ; t != NULL ; t = t->next) {
197
		if (t->architecture == architecture_source)
198
			continue;
199
		r = package_openiterator(t, true, &cursor);
200 201 202 203
		if (RET_WAS_ERROR(r)) {
			RET_UPDATE(result, r);
			break;
		}
204
		while (package_next(&cursor)) {
205 206 207
			struct info_source *s;
			struct info_source_version *v;

208
			r = package_getsource(&cursor.current);
209 210 211 212
			if (!RET_IS_OK(r)) {
				RET_UPDATE(result, r);
				continue;
			}
213 214 215
			const char *source = cursor.current.source;
			const char *version = cursor.current.sourceversion;

216
			s = sources;
217
			while (s != NULL && strcmp(s->name, source) < 0) {
218 219
				s = s->next;
			}
220
			if (s != NULL && strcmp(source, s->name) == 0) {
221 222 223 224 225 226 227 228
				v = &s->version;
				while (v != NULL && strcmp(version, v->version) != 0)
					v = v->next;
			} else
				v = NULL;
			if (v != NULL) {
				v->used = true;
			} else if (action != NULL) {
229
				r = action(&cursor.current, privdata);
230 231 232
				RET_UPDATE(result, r);
			}
		}
233
		r = package_closeiterator(&cursor);
234 235 236 237 238 239 240 241
		if (RET_WAS_ERROR(r)) {
			RET_UPDATE(result, r);
			break;
		}
	}
	return result;
}

242 243 244 245
static retvalue listunusedsources(struct distribution *d, const struct trackedpackage *pkg) {
	bool hasbinary = false, hassource = false;
	int i;

246 247
	for (i = 0 ; i < pkg->filekeys.count ; i++) {
		if (pkg->refcounts[i] == 0)
248
			continue;
249
		if (pkg->filetypes[i] == 's')
250
			hassource = true;
251
		if (pkg->filetypes[i] == 'b')
252
			hasbinary = true;
253
		if (pkg->filetypes[i] == 'a')
254 255
			hasbinary = true;
	}
256 257 258
	if (hassource && ! hasbinary) {
		printf("%s %s %s\n", d->codename, pkg->sourcename,
				pkg->sourceversion);
259 260 261 262 263
		return RET_OK;
	}
	return RET_NOTHING;
}

264
retvalue unusedsources(struct distribution *alldistributions) {
265 266 267
	struct distribution *d;
	retvalue result = RET_NOTHING, r;

268 269
	for (d = alldistributions ; d != NULL ; d = d->next) {
		if (!d->selected)
270
			continue;
271
		if (!atomlist_in(&d->architectures, architecture_source))
272
			continue;
273
		if (d->tracking != dt_NONE) {
274
			r = tracking_foreach_ro(d, listunusedsources);
275
			RET_UPDATE(result, r);
276
			if (RET_WAS_ERROR(r))
277
				return r;
278 279
			continue;
		}
280 281 282 283
		struct info_source *sources = NULL;
		const struct info_source *s;
		const struct info_source_version *v;

284
		r = collect_source_versions(d, &sources);
285 286
		if (!RET_IS_OK(r))
			continue;
287

288
		r = process_binaries(d, sources, NULL, NULL);
289
		RET_UPDATE(result, r);
290 291 292 293 294 295 296 297 298
		for (s = sources ; s != NULL ; s = s->next) {
			for (v = &s->version ; v != NULL ; v = v->next) {
				if (v->used)
					continue;
				printf("%s %s %s\n", d->codename,
						s->name, v->version);
			}
		}
		free_source_info(sources);
299 300 301 302 303 304 305 306
	}
	return result;
}

static retvalue listsourcemissing(struct distribution *d, const struct trackedpackage *pkg) {
	bool hasbinary = false, hassource = false;
	int i;

307 308
	for (i = 0 ; i < pkg->filekeys.count ; i++) {
		if (pkg->refcounts[i] == 0)
309
			continue;
310
		if (pkg->filetypes[i] == 's')
311
			hassource = true;
312
		if (pkg->filetypes[i] == 'b')
313
			hasbinary = true;
314
		if (pkg->filetypes[i] == 'a')
315 316
			hasbinary = true;
	}
317 318 319
	if (hasbinary && ! hassource) {
		for (i = 0 ; i < pkg->filekeys.count ; i++) {
			if (pkg->refcounts[i] == 0)
320
				continue;
321
			if (pkg->filetypes[i] != 'b' && pkg->filetypes[i] != 'a')
322
				continue;
323 324 325
			printf("%s %s %s %s\n", d->codename, pkg->sourcename,
					pkg->sourceversion,
					pkg->filekeys.values[i]);
326 327 328 329 330 331
		}
		return RET_OK;
	}
	return RET_NOTHING;
}

332
static retvalue listmissing(struct package *package, UNUSED(void*data)) {
333 334 335
	retvalue r;
	struct strlist list;

336
	r = package->target->getfilekeys(package->control, &list);
337 338 339
	if (!RET_IS_OK(r))
		return r;
	assert (list.count == 1);
340 341
	printf("%s %s %s %s\n", package->target->distribution->codename,
			package->source, package->sourceversion, list.values[0]);
342 343 344 345
	strlist_done(&list);
	return RET_OK;
}

346
retvalue sourcemissing(struct distribution *alldistributions) {
347 348 349
	struct distribution *d;
	retvalue result = RET_NOTHING, r;

350 351
	for (d = alldistributions ; d != NULL ; d = d->next) {
		if (!d->selected)
352
			continue;
353 354
		if (!atomlist_in(&d->architectures, architecture_source)) {
			if (verbose >= 0)
355 356 357 358 359
				fprintf(stderr,
"Not processing distribution '%s', as it has no source packages.\n",
						d->codename);
			continue;
		}
360
		if (d->tracking != dt_NONE) {
361
			r = tracking_foreach_ro(d, listsourcemissing);
362
			RET_UPDATE(result, r);
363
			if (RET_WAS_ERROR(r))
364 365 366 367
				return r;
		} else {
			struct info_source *sources = NULL;

368
			r = collect_source_versions(d, &sources);
369 370 371
			if (!RET_IS_OK(r))
				continue;

372
			r = process_binaries(d, sources, listmissing, NULL);
373 374
			RET_UPDATE(result, r);
			free_source_info(sources);
375 376 377 378 379
		}

	}
	return result;
}
380 381 382 383 384

static retvalue listcruft(struct distribution *d, const struct trackedpackage *pkg) {
	bool hasbinary = false, hassource = false;
	int i;

385 386
	for (i = 0 ; i < pkg->filekeys.count ; i++) {
		if (pkg->refcounts[i] == 0)
387
			continue;
388
		if (pkg->filetypes[i] == 's')
389
			hassource = true;
390
		if (pkg->filetypes[i] == 'b')
391
			hasbinary = true;
392
		if (pkg->filetypes[i] == 'a')
393 394
			hasbinary = true;
	}
395 396 397
	if (hasbinary && ! hassource) {
		printf("binaries-without-source %s %s %s\n", d->codename,
				pkg->sourcename, pkg->sourceversion);
398
		return RET_OK;
399 400 401
	} else if (hassource && ! hasbinary) {
		printf("source-without-binaries %s %s %s\n", d->codename,
				pkg->sourcename, pkg->sourceversion);
402 403 404 405 406
		return RET_OK;
	}
	return RET_NOTHING;
}

407
static retvalue listmissingonce(struct package *package, void *data) {
408 409 410 411
	struct info_source **already = data;
	struct info_source *s;

	for (s = *already ; s != NULL ; s = s->next) {
412
		if (strcmp(s->name, package->source) != 0)
413
			continue;
414
		if (strcmp(s->version.version, package->sourceversion) != 0)
415 416 417
			continue;
		return RET_NOTHING;
	}
418
	s = zNEW(struct info_source);
419 420
	if (FAILEDTOALLOC(s))
		return RET_ERROR_OOM;
421 422
	s->name = strdup(package->source);
	s->version.version = strdup(package->sourceversion);
423 424 425 426 427 428 429 430 431
	if (FAILEDTOALLOC(s->name) || FAILEDTOALLOC(s->version.version)) {
		free(s->name);
		free(s->version.version);
		free(s);
		return RET_ERROR_OOM;
	}
	s->next = *already;
	*already = s;
	printf("binaries-without-source %s %s %s\n",
432 433
			package->target->distribution->codename,
			package->source, package->sourceversion);
434 435 436
	return RET_OK;
}

437
retvalue reportcruft(struct distribution *alldistributions) {
438 439 440
	struct distribution *d;
	retvalue result = RET_NOTHING, r;

441 442
	for (d = alldistributions ; d != NULL ; d = d->next) {
		if (!d->selected)
443
			continue;
444 445
		if (!atomlist_in(&d->architectures, architecture_source)) {
			if (verbose >= 0)
446 447 448 449 450
				fprintf(stderr,
"Not processing distribution '%s', as it has no source packages.\n",
						d->codename);
			continue;
		}
451
		if (d->tracking != dt_NONE) {
452
			r = tracking_foreach_ro(d, listcruft);
453
			RET_UPDATE(result, r);
454
			if (RET_WAS_ERROR(r))
455
				return r;
456 457
			continue;
		}
458 459 460 461
		struct info_source *sources = NULL;
		struct info_source *list = NULL;
		const struct info_source *s;
		const struct info_source_version *v;
462

463
		r = collect_source_versions( d, &sources);
464 465 466
		if (!RET_IS_OK(r))
			continue;

467
		r = process_binaries( d, sources,
468
				listmissingonce, &list);
469
		RET_UPDATE(result, r);
470 471 472 473 474
		for (s = sources ; s != NULL ; s = s->next) {
			for (v = &s->version ; v != NULL ; v = v->next) {
				if (v->used)
					continue;
				printf("source-without-binaries %s %s %s\n",
475
					d->codename, s->name, v->version);
476 477 478 479
			}
		}
		free_source_info(list);
		free_source_info(sources);
480 481 482
	}
	return result;
}