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