174295Ssobomax/*
274295Ssobomax * FreeBSD install - a package for the installation and maintainance
374295Ssobomax * of non-core utilities.
474295Ssobomax *
574295Ssobomax * Redistribution and use in source and binary forms, with or without
674295Ssobomax * modification, are permitted provided that the following conditions
774295Ssobomax * are met:
874295Ssobomax * 1. Redistributions of source code must retain the above copyright
974295Ssobomax *    notice, this list of conditions and the following disclaimer.
1074295Ssobomax * 2. Redistributions in binary form must reproduce the above copyright
1174295Ssobomax *    notice, this list of conditions and the following disclaimer in the
1274295Ssobomax *    documentation and/or other materials provided with the distribution.
1374295Ssobomax *
1474295Ssobomax * Maxim Sobolev
1574295Ssobomax * 14 March 2001
1674295Ssobomax *
1774295Ssobomax * Routines used to do various operations with dependencies
1874295Ssobomax * among installed packages.
1974295Ssobomax *
2074295Ssobomax */
2174295Ssobomax
2293520Sobrien#include <sys/cdefs.h>
2393520Sobrien__FBSDID("$FreeBSD$");
2493520Sobrien
2574295Ssobomax#include "lib.h"
2674295Ssobomax#include <err.h>
2774295Ssobomax#include <stdio.h>
2874295Ssobomax
29170947Spavvoid list_deps(const char *pkgname, char **pkgs, char *listed,
30170947Spav               char *check_loop, char **newpkgs, int *nrnewpkgs,
31170947Spav               int *err_cnt);
32170947Spav
3374295Ssobomax/*
3474295Ssobomax * Sort given NULL-terminated list of installed packages (pkgs) in
3574295Ssobomax * such a way that if package A depends on package B then after
3674295Ssobomax * sorting A will be listed before B no matter how they were
3774295Ssobomax * originally positioned in the list.
38170947Spav *
39170947Spav * Works by performing a recursive depth-first search on the
40170947Spav * required-by lists.
4174295Ssobomax */
42170947Spav
4374295Ssobomaxint
4474295Ssobomaxsortdeps(char **pkgs)
4574295Ssobomax{
46170947Spav    int i, err_cnt=0;
47170947Spav    int nrpkgs, nrnewpkgs;
48170947Spav    char *listed, *check_loop, **newpkgs;
49170947Spav    char *cp;
5074295Ssobomax
5190987Ssobomax    if (pkgs[0] == NULL || pkgs[1] == NULL)
5290987Ssobomax	return (0);
5390987Ssobomax
54170947Spav    nrpkgs = 0;
55170947Spav    while (pkgs[nrpkgs]) nrpkgs++;
56170947Spav    listed = alloca(nrpkgs);
57170947Spav    if (listed == NULL) {
58170947Spav	warnx("%s(): alloca() failed", __func__);
59170947Spav	return 1;
6074295Ssobomax    }
61170947Spav    bzero(listed,nrpkgs);
62170947Spav    check_loop = alloca(nrpkgs);
63170947Spav    if (check_loop == NULL) {
64170947Spav	warnx("%s(): alloca() failed", __func__);
65170947Spav	return 1;
66170947Spav    }
67170947Spav    bzero(check_loop,nrpkgs);
68170947Spav    newpkgs = alloca(nrpkgs*sizeof(char*));
69170947Spav    if (newpkgs == NULL) {
70170947Spav	warnx("%s(): alloca() failed", __func__);
71170947Spav	return 1;
72170947Spav    }
73170947Spav    nrnewpkgs = 0;
74170947Spav
75170947Spav    for (i = 0; pkgs[i]; i++) if (!listed[i]) {
76170947Spav	check_loop[i] = 1;
77170947Spav	cp = strchr(pkgs[i], ':');
78170947Spav	if (cp != NULL)
79170947Spav	    *cp = '\0';
80170947Spav	list_deps(pkgs[i],pkgs,listed,check_loop,newpkgs,&nrnewpkgs,&err_cnt);
81170947Spav	if (cp != NULL)
82170947Spav	    *cp = ':';
83170947Spav	listed[i] = 1;
84170947Spav	newpkgs[nrnewpkgs] = pkgs[i];
85170947Spav	nrnewpkgs++;
86170947Spav    }
87170947Spav
88170947Spav    if (nrnewpkgs != nrpkgs) {
89170947Spav	fprintf(stderr,"This shouldn't happen, and indicates a huge error in the code.\n");
90170947Spav	exit(1);
91170947Spav    }
92170947Spav    for (i = 0; i < nrnewpkgs; i++) pkgs[i] = newpkgs[i];
93170947Spav
9474295Ssobomax    return err_cnt;
9574295Ssobomax}
9674295Ssobomax
9774295Ssobomax/*
98170947Spav * This recursive function lists the dependencies (that is, the
99170947Spav * "required-by"s) for pkgname, putting them into newpkgs.
100170947Spav */
101170947Spav
102170947Spavvoid list_deps(const char *pkgname, char **pkgs, char *listed,
103170947Spav               char *check_loop, char **newpkgs, int *nrnewpkgs,
104170947Spav               int *err_cnt) {
105170947Spav    char **rb, **rbtmp;
106170947Spav    char *cp;
107170947Spav    int errcode, i, j;
10883663Ssobomax    struct reqr_by_entry *rb_entry;
10983663Ssobomax    struct reqr_by_head *rb_list;
11083663Ssobomax
111170947Spav    if (isinstalledpkg(pkgname) <= 0)
112170947Spav	return;
11396076Ssobomax
114170947Spav    errcode = requiredby(pkgname, &rb_list, FALSE, TRUE);
11583663Ssobomax    if (errcode < 0)
116170947Spav	return;
117170947Spav    /*
118170947Spav     * We put rb_list into an argv style NULL terminated list,
119170947Spav     * because requiredby uses some static storage, and list_deps
120170947Spav     * is a recursive function.
121170947Spav     */
12283663Ssobomax
123170947Spav    rbtmp = rb = alloca((errcode + 1) * sizeof(*rb));
124170947Spav    if (rb == NULL) {
125170947Spav	warnx("%s(): alloca() failed", __func__);
126170947Spav	(*err_cnt)++;
127170947Spav	return;
128170947Spav    }
12996076Ssobomax    STAILQ_FOREACH(rb_entry, rb_list, link) {
130170947Spav	*rbtmp = alloca(strlen(rb_entry->pkgname) + 1);
131170947Spav	if (*rbtmp == NULL) {
132170947Spav	    warnx("%s(): alloca() failed", __func__);
133170947Spav	    (*err_cnt)++;
134170947Spav	    return;
13596076Ssobomax	}
136170947Spav	strcpy(*rbtmp, rb_entry->pkgname);
137170947Spav	rbtmp++;
13896076Ssobomax    }
139170947Spav    *rbtmp = NULL;
14083663Ssobomax
141170947Spav    for (i = 0; rb[i]; i++)
142170947Spav	for (j = 0; pkgs[j]; j++) if (!listed[j]) {
143170947Spav	    cp = strchr(pkgs[j], ':');
144170947Spav	    if (cp != NULL)
145170947Spav		*cp = '\0';
146170947Spav	    if (strcmp(rb[i], pkgs[j]) == 0) { /*match */
147170947Spav		/*
148170947Spav		 * Try to avoid deadlock if package A depends on B which in
149170947Spav		 * turn depends on C and C due to an error depends on A.
150170947Spav		 * It Should Never Happen[tm] in real life.
151170947Spav		 */
152170947Spav		if (check_loop[j]) {
153170947Spav		    warnx("dependency loop detected for package %s", pkgs[j]);
154170947Spav		    (*err_cnt)++;
155170947Spav		}
156170947Spav		else {
157170947Spav		    check_loop[j] = 1;
158170947Spav		    list_deps(pkgs[j],pkgs,listed,check_loop,newpkgs,nrnewpkgs,err_cnt);
159170947Spav		    listed[j] = 1;
160170947Spav		    newpkgs[*nrnewpkgs] = pkgs[j];
161170947Spav		    (*nrnewpkgs)++;
162170947Spav		}
163170947Spav	    }
164170947Spav	    if (cp != NULL)
165170947Spav		*cp = ':';
166170947Spav	}
16783663Ssobomax}
16883663Ssobomax
16983663Ssobomax/*
17083663Ssobomax * Load +REQUIRED_BY file and return a list with names of
17183663Ssobomax * packages that require package reffered to by `pkgname'.
17283663Ssobomax *
17383663Ssobomax * Optionally check that packages listed there are actually
17483663Ssobomax * installed and filter out those that don't (filter == TRUE).
17583663Ssobomax *
17683663Ssobomax * strict argument controls whether the caller want warnings
17783663Ssobomax * to be emitted when there are some non-fatal conditions,
17883663Ssobomax * i.e. package doesn't have +REQUIRED_BY file or some packages
17983663Ssobomax * listed in +REQUIRED_BY don't exist.
18083663Ssobomax *
18183663Ssobomax * Result returned in the **list, while return value is equal
18283663Ssobomax * to the number of entries in the resulting list. Print error
18383663Ssobomax * message and return -1 on error.
18483663Ssobomax */
18583663Ssobomaxint
18683663Ssobomaxrequiredby(const char *pkgname, struct reqr_by_head **list, Boolean strict, Boolean filter)
18783663Ssobomax{
18874295Ssobomax    FILE *fp;
18996613Ssobomax    char fbuf[FILENAME_MAX], fname[FILENAME_MAX];
19074295Ssobomax    int retval;
19183663Ssobomax    struct reqr_by_entry *rb_entry;
19283663Ssobomax    static struct reqr_by_head rb_list = STAILQ_HEAD_INITIALIZER(rb_list);
19374295Ssobomax
19483663Ssobomax    *list = &rb_list;
19583663Ssobomax    /* Deallocate any previously allocated space */
19683663Ssobomax    while (!STAILQ_EMPTY(&rb_list)) {
19783663Ssobomax	rb_entry = STAILQ_FIRST(&rb_list);
19883663Ssobomax	STAILQ_REMOVE_HEAD(&rb_list, link);
19983663Ssobomax	free(rb_entry);
20083663Ssobomax    }
20183663Ssobomax
202131280Seik    if (isinstalledpkg(pkgname) <= 0) {
20383663Ssobomax	if (strict == TRUE)
20483663Ssobomax	    warnx("no such package '%s' installed", pkgname);
20583663Ssobomax	return -1;
20683663Ssobomax    }
20783663Ssobomax
20896613Ssobomax    snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, pkgname,
20996613Ssobomax	     REQUIRED_BY_FNAME);
21074295Ssobomax    fp = fopen(fname, "r");
21174295Ssobomax    if (fp == NULL) {
21283663Ssobomax	/* Probably pkgname doesn't have any packages that depend on it */
21383663Ssobomax	if (strict == TRUE)
21483663Ssobomax	    warnx("couldn't open dependency file '%s'", fname);
21574295Ssobomax	return 0;
21674295Ssobomax    }
21774295Ssobomax
21874295Ssobomax    retval = 0;
21974295Ssobomax    while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
22083663Ssobomax	if (fbuf[strlen(fbuf) - 1] == '\n')
22183663Ssobomax	    fbuf[strlen(fbuf) - 1] = '\0';
222131280Seik	if (filter == TRUE && isinstalledpkg(fbuf) <= 0) {
22383663Ssobomax	    if (strict == TRUE)
22483663Ssobomax		warnx("package '%s' is recorded in the '%s' but isn't "
22583663Ssobomax		      "actually installed", fbuf, fname);
22683663Ssobomax	    continue;
22783663Ssobomax	}
22883663Ssobomax	retval++;
22983663Ssobomax	rb_entry = malloc(sizeof(*rb_entry));
23083663Ssobomax	if (rb_entry == NULL) {
23196392Salfred	    warnx("%s(): malloc() failed", __func__);
23283663Ssobomax	    retval = -1;
23374295Ssobomax	    break;
23474295Ssobomax	}
23583663Ssobomax	strlcpy(rb_entry->pkgname, fbuf, sizeof(rb_entry->pkgname));
23683663Ssobomax	STAILQ_INSERT_TAIL(&rb_list, rb_entry, link);
23774295Ssobomax    }
23883663Ssobomax    fclose(fp);
23974295Ssobomax
24074295Ssobomax    return retval;
24174295Ssobomax}
242