1/*	$NetBSD: perform.c,v 1.4 2021/04/10 19:49:59 nia Exp $	*/
2
3#if HAVE_CONFIG_H
4#include "config.h"
5#endif
6#include <nbcompat.h>
7#if HAVE_SYS_CDEFS_H
8#include <sys/cdefs.h>
9#endif
10__RCSID("$NetBSD: perform.c,v 1.4 2021/04/10 19:49:59 nia Exp $");
11
12/*-
13 * Copyright (c) 2008 Joerg Sonnenberger <joerg@NetBSD.org>.
14 * All rights reserved.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 *
20 * 1. Redistributions of source code must retain the above copyright
21 *    notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 *    notice, this list of conditions and the following disclaimer in
24 *    the documentation and/or other materials provided with the
25 *    distribution.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
30 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
31 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
33 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
35 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
36 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
37 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 */
40
41/*
42 * FreeBSD install - a package for the installation and maintainance
43 * of non-core utilities.
44 *
45 * Redistribution and use in source and binary forms, with or without
46 * modification, are permitted provided that the following conditions
47 * are met:
48 * 1. Redistributions of source code must retain the above copyright
49 *    notice, this list of conditions and the following disclaimer.
50 * 2. Redistributions in binary form must reproduce the above copyright
51 *    notice, this list of conditions and the following disclaimer in the
52 *    documentation and/or other materials provided with the distribution.
53 *
54 * Jordan K. Hubbard
55 * 23 Aug 1993
56 *
57 * This is the main body of the info module.
58 *
59 */
60
61#include "lib.h"
62#include "info.h"
63
64#if HAVE_SYS_TYPES_H
65#include <sys/types.h>
66#endif
67#if HAVE_SYS_STAT_H
68#include <sys/stat.h>
69#endif
70#if HAVE_SYS_QUEUE_H
71#include <sys/queue.h>
72#endif
73#if HAVE_SYS_WAIT_H
74#include <sys/wait.h>
75#endif
76
77#ifndef BOOTSTRAP
78#include <archive.h>
79#include <archive_entry.h>
80#endif
81#if HAVE_ERR_H
82#include <err.h>
83#endif
84#include <ctype.h>
85#include <dirent.h>
86#include <errno.h>
87#include <fcntl.h>
88#include <limits.h>
89#include <stddef.h>
90#include <signal.h>
91
92#define	LOAD_CONTENTS		(1 << 0)
93#define	LOAD_COMMENT		(1 << 1)
94#define	LOAD_DESC		(1 << 2)
95#define	LOAD_INSTALL		(1 << 3)
96#define	LOAD_DEINSTALL		(1 << 4)
97#define	LOAD_DISPLAY		(1 << 5)
98#define	LOAD_MTREE		(1 << 6)
99#define	LOAD_BUILD_VERSION	(1 << 7)
100#define	LOAD_BUILD_INFO		(1 << 8)
101#define	LOAD_SIZE_PKG		(1 << 9)
102#define	LOAD_SIZE_ALL		(1 << 10)
103#define	LOAD_PRESERVE		(1 << 11)
104#define	LOAD_REQUIRED_BY	(1 << 12)
105#define	LOAD_INSTALLED_INFO	(1 << 13)
106
107static const struct pkg_meta_desc {
108	size_t entry_offset;
109	const char *entry_filename;
110	int entry_mask;
111	int required_file;
112} pkg_meta_descriptors[] = {
113	{ offsetof(struct pkg_meta, meta_contents), CONTENTS_FNAME,
114	    LOAD_CONTENTS, 1},
115	{ offsetof(struct pkg_meta, meta_comment), COMMENT_FNAME,
116	    LOAD_COMMENT, 1 },
117	{ offsetof(struct pkg_meta, meta_desc), DESC_FNAME,
118	    LOAD_DESC, 1 },
119	{ offsetof(struct pkg_meta, meta_install), INSTALL_FNAME,
120	    LOAD_INSTALL, 0 },
121	{ offsetof(struct pkg_meta, meta_deinstall), DEINSTALL_FNAME,
122	    LOAD_DEINSTALL, 0 },
123	{ offsetof(struct pkg_meta, meta_display), DISPLAY_FNAME,
124	    LOAD_DISPLAY, 0 },
125	{ offsetof(struct pkg_meta, meta_mtree), MTREE_FNAME,
126	    LOAD_MTREE, 0 },
127	{ offsetof(struct pkg_meta, meta_build_version), BUILD_VERSION_FNAME,
128	    LOAD_BUILD_VERSION, 0 },
129	{ offsetof(struct pkg_meta, meta_build_info), BUILD_INFO_FNAME,
130	    LOAD_BUILD_INFO, 0 },
131	{ offsetof(struct pkg_meta, meta_size_pkg), SIZE_PKG_FNAME,
132	    LOAD_SIZE_PKG, 0 },
133	{ offsetof(struct pkg_meta, meta_size_all), SIZE_ALL_FNAME,
134	    LOAD_SIZE_ALL, 0 },
135	{ offsetof(struct pkg_meta, meta_preserve), PRESERVE_FNAME,
136	    LOAD_PRESERVE, 0 },
137	{ offsetof(struct pkg_meta, meta_required_by), REQUIRED_BY_FNAME,
138	    LOAD_REQUIRED_BY, 0 },
139	{ offsetof(struct pkg_meta, meta_installed_info), INSTALLED_INFO_FNAME,
140	    LOAD_INSTALLED_INFO, 0 },
141	{ 0, NULL, 0, 0 },
142};
143
144static int desired_meta_data;
145
146static void
147free_pkg_meta(struct pkg_meta *meta)
148{
149	const struct pkg_meta_desc *descr;
150
151	for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr)
152		free(*(char **)((char *)meta + descr->entry_offset));
153
154	free(meta);
155}
156
157#ifndef BOOTSTRAP
158static struct pkg_meta *
159read_meta_data_from_archive(struct archive *archive,
160    struct archive_entry *entry)
161{
162	struct pkg_meta *meta;
163	const char *fname;
164	const struct pkg_meta_desc *descr, *last_descr;
165	char **target;
166	int64_t size;
167	int r, found_required;
168
169	found_required = 0;
170
171	meta = xcalloc(1, sizeof(*meta));
172
173	last_descr = 0;
174	if (entry != NULL) {
175		r = ARCHIVE_OK;
176		goto has_entry;
177	}
178
179	while ((r = archive_read_next_header(archive, &entry)) == ARCHIVE_OK) {
180has_entry:
181		fname = archive_entry_pathname(entry);
182
183		for (descr = pkg_meta_descriptors; descr->entry_filename;
184		     ++descr) {
185			if (strcmp(descr->entry_filename, fname) == 0)
186				break;
187		}
188		if (descr->entry_filename == NULL)
189			break;
190
191		if (descr->required_file)
192			++found_required;
193
194		target = (char **)((char *)meta + descr->entry_offset);
195		if (*target)
196			errx(2, "duplicate entry, package corrupt");
197		if (descr < last_descr)
198			warnx("misordered package, continuing");
199		else
200			last_descr = descr;
201
202		if ((descr->entry_mask & desired_meta_data) == 0) {
203			if (archive_read_data_skip(archive))
204				errx(2, "cannot read package meta data");
205			continue;
206		}
207
208		size = archive_entry_size(entry);
209		if (size > SSIZE_MAX - 1)
210			errx(2, "package meta data too large to process");
211		*target = xmalloc(size + 1);
212		if (archive_read_data(archive, *target, size) != size)
213			errx(2, "cannot read package meta data");
214		(*target)[size] = '\0';
215	}
216
217	for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) {
218		if (descr->required_file)
219			--found_required;
220	}
221
222	meta->is_installed = 0;
223	if (found_required != 0 || (r != ARCHIVE_OK && r != ARCHIVE_EOF)) {
224		free_pkg_meta(meta);
225		meta = NULL;
226	}
227
228	return meta;
229}
230#endif
231
232static struct pkg_meta *
233read_meta_data_from_pkgdb(const char *pkg)
234{
235	struct pkg_meta *meta;
236	const struct pkg_meta_desc *descr;
237	char **target;
238	char *fname;
239	int fd;
240	struct stat st;
241
242	meta = xcalloc(1, sizeof(*meta));
243
244	for (descr = pkg_meta_descriptors; descr->entry_filename; ++descr) {
245		if ((descr->entry_mask & desired_meta_data) == 0)
246			continue;
247
248		fname = pkgdb_pkg_file(pkg, descr->entry_filename);
249		fd = open(fname, O_RDONLY, 0);
250		free(fname);
251		if (fd == -1) {
252			if (errno == ENOENT && descr->required_file == 0)
253				continue;
254			err(2, "cannot read meta data file %s of package %s",
255			    descr->entry_filename, pkg);
256		}
257		target = (char **)((char *)meta + descr->entry_offset);
258
259		if (fstat(fd, &st) == -1)
260			err(2, "cannot stat meta data");
261		if ((st.st_mode & S_IFMT) != S_IFREG)
262			errx(1, "meta data is not regular file");
263		if (st.st_size > SSIZE_MAX - 1)
264			err(2, "meta data file too large to process");
265		*target = xmalloc(st.st_size + 1);
266		if (read(fd, *target, st.st_size) != st.st_size)
267			err(2, "cannot read meta data");
268		(*target)[st.st_size] = '\0';
269		close(fd);
270	}
271
272	meta->is_installed = 1;
273
274	return meta;
275}
276
277static void
278build_full_reqby(lpkg_head_t *reqby, struct pkg_meta *meta, int limit)
279{
280	char *iter, *eol, *next;
281	lpkg_t *lpp;
282	struct pkg_meta *meta_dep;
283
284	if (limit == 65536)
285		errx(1, "Cycle in the dependency tree, bailing out");
286
287	if (meta->is_installed == 0 || meta->meta_required_by == NULL)
288		return;
289
290	for (iter = meta->meta_required_by; *iter != '\0'; iter = next) {
291		eol = iter + strcspn(iter, "\n");
292		if (*eol == '\n')
293			next = eol + 1;
294		else
295			next = eol;
296		if (iter == eol)
297			continue;
298		TAILQ_FOREACH(lpp, reqby, lp_link) {
299			if (strlen(lpp->lp_name) + iter != eol)
300				continue;
301			if (memcmp(lpp->lp_name, iter, eol - iter) == 0)
302				break;
303		}
304		if (lpp != NULL)
305			continue;
306		*eol = '\0';
307		lpp = alloc_lpkg(iter);
308		if (next != eol)
309			*eol = '\n';
310
311		meta_dep = read_meta_data_from_pkgdb(lpp->lp_name);
312		if (meta_dep == NULL)
313			continue;
314		build_full_reqby(reqby, meta_dep, limit + 1);
315		free_pkg_meta(meta_dep);
316
317		TAILQ_INSERT_HEAD(reqby, lpp, lp_link);
318	}
319}
320
321static lfile_head_t files;
322
323static int
324pkg_do(const char *pkg)
325{
326	struct pkg_meta *meta;
327	int     code = 0;
328	const char   *binpkgfile = NULL;
329	char *pkgdir;
330
331	if (IS_URL(pkg) || (fexists(pkg) && isfile(pkg))) {
332#ifdef BOOTSTRAP
333		errx(2, "Binary packages not supported during bootstrap");
334#else
335		struct archive *archive;
336		struct archive_entry *entry;
337		char *archive_name, *pkgname;
338
339		archive = open_archive(pkg, &archive_name);
340		if (archive == NULL) {
341			warnx("can't find package `%s', skipped", pkg);
342			return -1;
343		}
344		pkgname = NULL;
345		entry = NULL;
346		pkg_verify_signature(archive_name, &archive, &entry, &pkgname);
347		if (archive == NULL)
348			return -1;
349		free(pkgname);
350
351		meta = read_meta_data_from_archive(archive, entry);
352		archive_read_free(archive);
353		if (!IS_URL(pkg))
354			binpkgfile = pkg;
355#endif
356	} else {
357		/*
358	         * It's not an uninstalled package, try and find it among the
359	         * installed
360	         */
361		pkgdir = pkgdb_pkg_dir(pkg);
362		if (!fexists(pkgdir) || !(isdir(pkgdir) || islinktodir(pkgdir))) {
363			switch (add_installed_pkgs_by_basename(pkg, &pkgs)) {
364			case 1:
365				return 0;
366			case 0:
367				/* No match */
368				warnx("can't find package `%s'", pkg);
369				return 1;
370			case -1:
371				errx(EXIT_FAILURE, "Error during search in pkgdb for %s", pkg);
372			}
373		}
374		free(pkgdir);
375		meta = read_meta_data_from_pkgdb(pkg);
376	}
377
378	if (meta == NULL) {
379		warnx("invalid package `%s' skipped", pkg);
380		return 1;
381	}
382
383	/*
384         * Index is special info type that has to override all others to make
385         * any sense.
386         */
387	if (Flags & SHOW_INDEX) {
388		char    tmp[MaxPathSize];
389
390		(void) snprintf(tmp, sizeof(tmp), "%-19s ", pkg);
391		show_index(meta->meta_comment, tmp);
392	} else if (Flags & SHOW_BI_VAR) {
393		if (strcspn(BuildInfoVariable, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
394		    == strlen(BuildInfoVariable)) {
395			if (meta->meta_installed_info)
396				show_var(meta->meta_installed_info, BuildInfoVariable);
397		} else {
398			if (meta->meta_build_info)
399				show_var(meta->meta_build_info, BuildInfoVariable);
400			else
401				warnx("Build information missing");
402		}
403	} else {
404		package_t plist;
405
406		/* Read the contents list */
407		parse_plist(&plist, meta->meta_contents);
408
409		/* Start showing the package contents */
410		if (!Quiet && !(Flags & SHOW_SUMMARY)) {
411			printf("%sInformation for %s:\n\n", InfoPrefix, pkg);
412			if (meta->meta_preserve) {
413				printf("*** PACKAGE MAY NOT BE DELETED ***\n");
414			}
415		}
416		if (Flags & SHOW_SUMMARY) {
417			show_summary(meta, &plist, binpkgfile);
418		}
419		if (Flags & SHOW_COMMENT) {
420			show_file(meta->meta_comment, "Comment:\n", TRUE);
421		}
422		if (Flags & SHOW_DEPENDS) {
423			show_depends("Requires:\n", &plist);
424		}
425		if (Flags & SHOW_BLD_DEPENDS) {
426			show_bld_depends("Built using:\n", &plist);
427		}
428		if ((Flags & SHOW_REQBY) && meta->meta_required_by) {
429			show_file(meta->meta_required_by, "Required by:\n", TRUE);
430		}
431		if ((Flags & SHOW_FULL_REQBY) && meta->is_installed) {
432			lpkg_head_t reqby;
433			TAILQ_INIT(&reqby);
434			build_full_reqby(&reqby, meta, 0);
435			show_list(&reqby, "Full required by list:\n");
436		}
437		if (Flags & SHOW_DESC) {
438			show_file(meta->meta_desc, "Description:\n", TRUE);
439		}
440		if ((Flags & SHOW_DISPLAY) && meta->meta_display) {
441			show_file(meta->meta_display, "Install notice:\n",
442				  TRUE);
443		}
444		if (Flags & SHOW_PLIST) {
445			show_plist("Packing list:\n", &plist, PLIST_SHOW_ALL);
446		}
447		if ((Flags & SHOW_INSTALL) && meta->meta_install) {
448			show_file(meta->meta_install, "Install script:\n",
449				  TRUE);
450		}
451		if ((Flags & SHOW_DEINSTALL) && meta->meta_deinstall) {
452			show_file(meta->meta_deinstall, "De-Install script:\n",
453				  TRUE);
454		}
455		if ((Flags & SHOW_MTREE) && meta->meta_mtree) {
456			show_file(meta->meta_mtree, "mtree file:\n", TRUE);
457		}
458		if (Flags & SHOW_PREFIX) {
459			show_plist("Prefix(s):\n", &plist, PLIST_CWD);
460		}
461		if (Flags & SHOW_FILES) {
462			show_files("Files:\n", &plist);
463		}
464		if ((Flags & SHOW_BUILD_VERSION) && meta->meta_build_version) {
465			show_file(meta->meta_build_version, "Build version:\n",
466				  TRUE);
467		}
468		if (Flags & SHOW_BUILD_INFO) {
469			if (meta->meta_build_info) {
470				show_file(meta->meta_build_info, "Build information:\n",
471					  TRUE);
472			}
473			if (meta->meta_installed_info) {
474				show_file(meta->meta_installed_info, "Installed information:\n",
475					  TRUE);
476			}
477		}
478		if ((Flags & SHOW_PKG_SIZE) && meta->meta_size_pkg) {
479			show_file(meta->meta_size_pkg, "Size of this package in bytes: ",
480				  TRUE);
481		}
482		if ((Flags & SHOW_ALL_SIZE) && meta->meta_size_all) {
483			show_file(meta->meta_size_all, "Size in bytes including required pkgs: ",
484				  TRUE);
485		}
486		if (!Quiet && !(Flags & SHOW_SUMMARY)) {
487			if (meta->meta_preserve) {
488				printf("*** PACKAGE MAY NOT BE DELETED ***\n\n");
489			}
490			puts(InfoPrefix);
491		}
492		free_plist(&plist);
493	}
494	free_pkg_meta(meta);
495	return code;
496}
497
498struct print_matching_arg {
499	const char *pattern;
500	int got_match;
501};
502
503static int
504print_matching_pkg(const char *pkgname, void *cookie)
505{
506	struct print_matching_arg *arg= cookie;
507
508	if (pkg_match(arg->pattern, pkgname)) {
509		if (!Quiet)
510			puts(pkgname);
511		arg->got_match = 1;
512	}
513
514	return 0;
515}
516
517/*
518 * Returns 0 if at least one package matching pkgname.
519 * Returns 1 otherwise.
520 *
521 * If -q was not specified, print all matching packages to stdout.
522 */
523int
524CheckForPkg(const char *pkgname)
525{
526	struct print_matching_arg arg;
527
528	arg.pattern = pkgname;
529	arg.got_match = 0;
530
531	if (iterate_pkg_db(print_matching_pkg, &arg) == -1) {
532		warnx("cannot iterate pkgdb");
533		return 1;
534	}
535
536	if (arg.got_match == 0 && !ispkgpattern(pkgname)) {
537		char *pattern;
538
539		pattern = xasprintf("%s-[0-9]*", pkgname);
540
541		arg.pattern = pattern;
542		arg.got_match = 0;
543
544		if (iterate_pkg_db(print_matching_pkg, &arg) == -1) {
545			free(pattern);
546			warnx("cannot iterate pkgdb");
547			return 1;
548		}
549		free(pattern);
550	}
551
552	if (arg.got_match)
553		return 0;
554	else
555		return 1;
556}
557
558/*
559 * Returns 0 if at least one package matching pkgname.
560 * Returns 1 otherwise.
561 *
562 * If -q was not specified, print best match to stdout.
563 */
564int
565CheckForBestPkg(const char *pkgname)
566{
567	char *pattern, *best_match;
568
569	best_match = find_best_matching_installed_pkg(pkgname, 1);
570	if (best_match == NULL) {
571		if (ispkgpattern(pkgname))
572			return 1;
573
574		pattern = xasprintf("%s-[0-9]*", pkgname);
575		best_match = find_best_matching_installed_pkg(pattern, 1);
576		free(pattern);
577	}
578
579	if (best_match == NULL)
580		return 1;
581	if (!Quiet)
582		puts(best_match);
583	free(best_match);
584	return 0;
585}
586
587static int
588perform_single_pkg(const char *pkg, void *cookie)
589{
590	int *err_cnt = cookie;
591
592	if (Which == WHICH_ALL || !is_automatic_installed(pkg))
593		*err_cnt += pkg_do(pkg);
594
595	return 0;
596}
597
598int
599pkg_perform(lpkg_head_t *pkghead)
600{
601	int     err_cnt = 0;
602
603	TAILQ_INIT(&files);
604
605	desired_meta_data = 0;
606	if ((Flags & (SHOW_INDEX | SHOW_BI_VAR)) == 0)
607		desired_meta_data |= LOAD_PRESERVE;
608	if ((Flags & (SHOW_INDEX | SHOW_BI_VAR)) == 0)
609		desired_meta_data |= LOAD_CONTENTS;
610	if (Flags & (SHOW_COMMENT | SHOW_INDEX | SHOW_SUMMARY))
611		desired_meta_data |= LOAD_COMMENT;
612	if (Flags & (SHOW_BI_VAR | SHOW_BUILD_INFO | SHOW_SUMMARY))
613		desired_meta_data |= LOAD_BUILD_INFO | LOAD_INSTALLED_INFO;
614	if (Flags & (SHOW_SUMMARY | SHOW_PKG_SIZE))
615		desired_meta_data |= LOAD_SIZE_PKG;
616	if (Flags & SHOW_ALL_SIZE)
617		desired_meta_data |= LOAD_SIZE_ALL;
618	if (Flags & (SHOW_SUMMARY | SHOW_DESC))
619		desired_meta_data |= LOAD_DESC;
620	if (Flags & (SHOW_REQBY | SHOW_FULL_REQBY))
621		desired_meta_data |= LOAD_REQUIRED_BY;
622	if (Flags & SHOW_DISPLAY)
623		desired_meta_data |= LOAD_DISPLAY;
624	if (Flags & SHOW_INSTALL)
625		desired_meta_data |= LOAD_INSTALL;
626	if (Flags & SHOW_DEINSTALL)
627		desired_meta_data |= LOAD_DEINSTALL;
628	if (Flags & SHOW_MTREE)
629		desired_meta_data |= LOAD_MTREE;
630	if (Flags & SHOW_BUILD_VERSION)
631		desired_meta_data |= LOAD_BUILD_VERSION;
632
633	if (Which != WHICH_LIST) {
634		if (File2Pkg) {
635			/* Show all files with the package they belong to */
636			if (pkgdb_dump() == -1)
637				err_cnt = 1;
638		} else {
639			if (iterate_pkg_db(perform_single_pkg, &err_cnt) == -1)
640				err_cnt = 1;
641		}
642	} else {
643		/* Show info on individual pkg(s) */
644		lpkg_t *lpp;
645
646		while ((lpp = TAILQ_FIRST(pkghead)) != NULL) {
647			TAILQ_REMOVE(pkghead, lpp, lp_link);
648			err_cnt += pkg_do(lpp->lp_name);
649			free_lpkg(lpp);
650		}
651	}
652	return err_cnt;
653}
654