1/* 2 * FreeBSD install - a package for the installation and maintenance 3 * of non-core utilities. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * Jeremy D. Lea. 15 * 11 May 2002 16 * 17 * This is the version module. Based on pkg_version.pl by Bruce A. Mah. 18 * 19 */ 20 21#include <sys/cdefs.h> 22__FBSDID("$FreeBSD$"); 23 24#include "lib.h" 25#include "version.h" 26#include <err.h> 27#include <fetch.h> 28#include <signal.h> 29 30static FILE *IndexFile; 31static char IndexPath[PATH_MAX] = ""; 32static struct index_head Index = SLIST_HEAD_INITIALIZER(Index); 33 34static int pkg_do(char *); 35static void show_version(Package, const char *, const char *); 36 37/* 38 * This is the traditional pkg_perform, except that the argument is _not_ a 39 * list of packages. It is the index file from the command line. 40 * 41 * We loop over the installed packages, matching them with the -s flag if 42 * needed and calling pkg_do(). Beforehand we set up a few things, and after 43 * we tear them down... 44 * 45 * Returns 0 on success, non-zero on failure, corresponding to the number of 46 * failed attempts to access the INDEX. 47 */ 48int 49pkg_perform(char **indexarg) 50{ 51 char **pkgs, *pat[2], **patterns; 52 struct index_entry *ie; 53 int i, err_cnt = 0, rel_major_ver; 54 int MatchType; 55 56 struct utsname u; 57 58 if (uname(&u) == -1) { 59 warn("%s: failed to determine uname information", __func__); 60 return 1; 61 } else if ((rel_major_ver = (int) strtol(u.release, NULL, 10)) <= 0) { 62 warnx("%s: bad release version specified: %s", __func__, u.release); 63 return 1; 64 } 65 66 /* 67 * Try to find and open the INDEX. We only check IndexFile != NULL 68 * later, if we actually need the INDEX. 69 */ 70 if (*indexarg == NULL) { 71 snprintf(IndexPath, sizeof(IndexPath), "%s/INDEX-%d", PORTS_DIR, 72 rel_major_ver); 73 } else 74 strlcpy(IndexPath, *indexarg, sizeof(IndexPath)); 75 if (isURL(IndexPath)) 76 IndexFile = fetchGetURL(IndexPath, ""); 77 else 78 IndexFile = fopen(IndexPath, "r"); 79 80 /* Get either a list of matching or all packages */ 81 if (MatchName != NULL) { 82 pat[0] = MatchName; 83 pat[1] = NULL; 84 MatchType = RegexExtended ? MATCH_EREGEX : MATCH_REGEX; 85 patterns = pat; 86 } else { 87 MatchType = MATCH_ALL; 88 patterns = NULL; 89 } 90 91 if (LookUpOrigin != NULL) 92 pkgs = matchbyorigin(LookUpOrigin, &err_cnt); 93 else 94 pkgs = matchinstalled(MatchType, patterns, &err_cnt); 95 96 if (err_cnt != 0) 97 errx(2, "Unable to find package database directory!"); 98 if (pkgs == NULL) { 99 if (LookUpOrigin != NULL) { 100 warnx("no packages recorded with this origin"); 101 return (1); 102 } else { 103 switch (MatchType) { 104 case MATCH_ALL: 105 warnx("no packages installed"); 106 return (0); 107 case MATCH_EREGEX: 108 case MATCH_REGEX: 109 warnx("no packages match pattern"); 110 return (1); 111 default: 112 break; 113 } 114 } 115 } 116 117 for (i = 0; pkgs[i] != NULL; i++) 118 err_cnt += pkg_do(pkgs[i]); 119 120 /* If we opened the INDEX in pkg_do(), clean up. */ 121 while (!SLIST_EMPTY(&Index)) { 122 ie = SLIST_FIRST(&Index); 123 SLIST_REMOVE_HEAD(&Index, next); 124 if (ie->name != NULL) 125 free(ie->name); 126 if (ie->origin != NULL) 127 free(ie->origin); 128 free(ie); 129 } 130 if (IndexFile != NULL) 131 fclose(IndexFile); 132 133 return err_cnt; 134} 135 136/* 137 * Traditional pkg_do(). We take the package name we are passed and 138 * first slurp in the CONTENTS file, getting name and origin, then 139 * we look for it's corresponding Makefile. If that fails we pull in 140 * the INDEX, and check there. 141 */ 142static int 143pkg_do(char *pkg) 144{ 145 char *ch, tmp[PATH_MAX], tmp2[PATH_MAX], *latest = NULL; 146 Package plist; 147 struct index_entry *ie; 148 FILE *fp; 149 size_t len; 150 151 /* Suck in the contents list. */ 152 plist.head = plist.tail = NULL; 153 plist.name = plist.origin = NULL; 154 snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, pkg, CONTENTS_FNAME); 155 fp = fopen(tmp, "r"); 156 if (!fp) { 157 warnx("the package info for package '%s' is corrupt", pkg); 158 return 1; 159 } 160 read_plist(&plist, fp); 161 fclose(fp); 162 if (plist.name == NULL) { 163 warnx("%s does not appear to be a valid package!", pkg); 164 return 1; 165 } 166 167 /* 168 * First we check if the installed package has an origin, and try 169 * looking for it's Makefile. If we find the Makefile we get the 170 * latest version from there. If we fail, we start looking in the 171 * INDEX, first matching the origin and then the package name. 172 */ 173 if (plist.origin != NULL && !UseINDEXOnly) { 174 snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, plist.origin); 175 if (isdir(tmp) && chdir(tmp) != FAIL && isfile("Makefile")) { 176 if ((latest = vpipe("/usr/bin/make -V PKGNAME", tmp)) == NULL) 177 warnx("Failed to get PKGNAME from %s/Makefile!", tmp); 178 else 179 show_version(plist, latest, "port"); 180 } 181 } 182 if (latest == NULL) { 183 /* Report package as not found in INDEX if the INDEX is not required. */ 184 if (IndexFile == NULL && !UseINDEXOnly) 185 show_version(plist, NULL, plist.origin); 186 else { 187 /* We only pull in the INDEX once, if needed. */ 188 if (SLIST_EMPTY(&Index)) { 189 if (!IndexFile) 190 errx(2, "Unable to open %s in %s.", IndexPath, __func__); 191 while ((ch = fgetln(IndexFile, &len)) != NULL) { 192 /* 193 * Don't use strlcpy() because fgetln() doesn't 194 * return a valid C string. 195 */ 196 strncpy(tmp, ch, MIN(len, PATH_MAX)); 197 tmp[PATH_MAX-1] = '\0'; 198 /* The INDEX has pkgname|portdir|... */ 199 if ((ch = strchr(tmp, '|')) != NULL) 200 ch[0] = '\0'; 201 if (ch != NULL && (ch = strchr(&ch[1], '|')) != NULL) 202 ch[0] = '\0'; 203 /* Look backwards for the last two dirs = origin */ 204 while (ch != NULL && *--ch != '/') 205 if (ch[0] == '\0') 206 ch = NULL; 207 while (ch != NULL && *--ch != '/') 208 if (ch[0] == '\0') 209 ch = NULL; 210 if (ch == NULL) 211 errx(2, "The INDEX does not appear to be valid!"); 212 if ((ie = malloc(sizeof(struct index_entry))) == NULL) 213 errx(2, "Unable to allocate memory in %s.", __func__); 214 bzero(ie, sizeof(struct index_entry)); 215 ie->name = strdup(tmp); 216 ie->origin = strdup(&ch[1]); 217 /* Who really cares if we reverse the index... */ 218 SLIST_INSERT_HEAD(&Index, ie, next); 219 } 220 } 221 /* Now that we've slurped in the INDEX... */ 222 SLIST_FOREACH(ie, &Index, next) { 223 if (plist.origin != NULL) { 224 if (strcmp(plist.origin, ie->origin) == 0) 225 latest = strdup(ie->name); 226 } else { 227 strlcpy(tmp, ie->name, PATH_MAX); 228 strlcpy(tmp2, plist.name, PATH_MAX); 229 /* Chop off the versions and compare. */ 230 if ((ch = strrchr(tmp, '-')) == NULL) 231 errx(2, "The INDEX does not appear to be valid!"); 232 ch[0] = '\0'; 233 if ((ch = strrchr(tmp2, '-')) == NULL) 234 warnx("%s is not a valid package!", plist.name); 235 else 236 ch[0] = '\0'; 237 if (strcmp(tmp2, tmp) == 0) { 238 if (latest != NULL) { 239 /* Multiple matches */ 240 snprintf(tmp, PATH_MAX, "%s|%s", latest, ie->name); 241 free(latest); 242 latest = strdup(tmp); 243 } else 244 latest = strdup(ie->name); 245 } 246 } 247 } 248 if (latest == NULL) 249 show_version(plist, NULL, NULL); 250 else 251 show_version(plist, latest, "index"); 252 } 253 } 254 if (latest != NULL) 255 free(latest); 256 free_plist(&plist); 257 return 0; 258} 259 260#define OUTPUT(c) ((PreventChars != NULL && !strchr(PreventChars, (c))) || \ 261 (LimitChars != NULL && strchr(LimitChars, (c))) || \ 262 (PreventChars == NULL && LimitChars == NULL)) 263 264/* 265 * Do the work of comparing and outputing. Ugly, but well that's what 266 * You get when you try to match perl output in C ;-). 267 */ 268void 269show_version(Package plist, const char *latest, const char *source) 270{ 271 char *ch, tmp[PATH_MAX]; 272 const char *ver; 273 int cmp = 0; 274 275 if (!plist.name || strlen(plist.name) == 0) 276 return; 277 if (ShowOrigin != FALSE && plist.origin != NULL) 278 strlcpy(tmp, plist.origin, PATH_MAX); 279 else { 280 strlcpy(tmp, plist.name, PATH_MAX); 281 if (!Verbose) { 282 if ((ch = strrchr(tmp, '-')) != NULL) 283 ch[0] = '\0'; 284 } 285 } 286 if (latest == NULL) { 287 if (source == NULL && OUTPUT('!')) { 288 printf("%-34s !", tmp); 289 if (Verbose) 290 printf(" Comparison failed"); 291 printf("\n"); 292 } else if (OUTPUT('?')) { 293 printf("%-34s ?", tmp); 294 if (Verbose) 295 printf(" orphaned: %s", plist.origin); 296 printf("\n"); 297 } 298 } else if (strchr(latest,'|') != NULL) { 299 if (OUTPUT('*')) { 300 printf("%-34s *", tmp); 301 if (Verbose) { 302 strlcpy(tmp, latest, PATH_MAX); 303 ch = strchr(tmp, '|'); 304 ch[0] = '\0'; 305 306 ver = strrchr(tmp, '-'); 307 ver = ver ? &ver[1] : tmp; 308 printf(" multiple versions (index has %s", ver); 309 do { 310 ver = strrchr(&ch[1], '-'); 311 ver = ver ? &ver[1] : &ch[1]; 312 if ((ch = strchr(&ch[1], '|')) != NULL) 313 ch[0] = '\0'; 314 printf(", %s", ver); 315 } while (ch != NULL); 316 printf(")"); 317 } 318 printf("\n"); 319 } 320 } else { 321 cmp = version_cmp(plist.name, latest); 322 ver = strrchr(latest, '-'); 323 ver = ver ? &ver[1] : latest; 324 if (cmp < 0 && OUTPUT('<')) { 325 if (Quiet) 326 printf("%s", tmp); 327 else { 328 printf("%-34s <", tmp); 329 if (Verbose) 330 printf(" needs updating (%s has %s)", source, ver); 331 } 332 printf("\n"); 333 } else if (cmp == 0 && OUTPUT('=')) { 334 if (Quiet) 335 printf("%s", tmp); 336 else { 337 printf("%-34s =", tmp); 338 if (Verbose) 339 printf(" up-to-date with %s", source); 340 } 341 printf("\n"); 342 } else if (cmp > 0 && OUTPUT('>')) { 343 if (Quiet) 344 printf("%s", tmp); 345 else { 346 printf("%-34s >", tmp); 347 if (Verbose) 348 printf(" succeeds %s (%s has %s)", source, source, ver); 349 } 350 printf("\n"); 351 } 352 } 353} 354 355int 356version_match(char *pattern, const char *pkgname) 357{ 358 int ret = 0; 359 int matchstream = 0; 360 FILE *fp = NULL; 361 Boolean isTMP = FALSE; 362 363 if (isURL(pkgname)) { 364 fp = fetchGetURL(pkgname, ""); 365 isTMP = TRUE; 366 matchstream = 1; 367 if (fp == NULL) 368 errx(2, "Unable to open %s.", pkgname); 369 } else if (pkgname[0] == '/') { 370 fp = fopen(pkgname, "r"); 371 isTMP = TRUE; 372 matchstream = 1; 373 if (fp == NULL) 374 errx(2, "Unable to open %s.", pkgname); 375 } else if (strcmp(pkgname, "-") == 0) { 376 fp = stdin; 377 matchstream = 1; 378 } else if (isURL(pattern)) { 379 fp = fetchGetURL(pattern, ""); 380 isTMP = TRUE; 381 matchstream = -1; 382 if (fp == NULL) 383 errx(2, "Unable to open %s.", pattern); 384 } else if (pattern[0] == '/') { 385 fp = fopen(pattern, "r"); 386 isTMP = TRUE; 387 matchstream = -1; 388 if (fp == NULL) 389 errx(2, "Unable to open %s.", pattern); 390 } else if (strcmp(pattern, "-") == 0) { 391 fp = stdin; 392 matchstream = -1; 393 } else { 394 ret = pattern_match(MATCH_GLOB, pattern, pkgname); 395 } 396 397 if (fp != NULL) { 398 size_t len; 399 char *line; 400 while ((line = fgetln(fp, &len)) != NULL) { 401 int match; 402 char *ch, ln[2048]; 403 size_t lnlen; 404 if (len > 0 && line[len-1] == '\n') 405 len --; 406 lnlen = len; 407 if (lnlen > sizeof(ln)-1) 408 lnlen = sizeof(ln)-1; 409 memcpy(ln, line, lnlen); 410 ln[lnlen] = '\0'; 411 if ((ch = strchr(ln, '|')) != NULL) 412 ch[0] = '\0'; 413 if (matchstream > 0) 414 match = pattern_match(MATCH_GLOB, pattern, ln); 415 else 416 match = pattern_match(MATCH_GLOB, ln, pkgname); 417 if (match == 1) { 418 ret = 1; 419 printf("%.*s\n", (int)len, line); 420 } 421 } 422 if (isTMP) 423 fclose(fp); 424 } 425 426 return ret; 427} 428 429void 430cleanup(int sig) 431{ 432 if (sig) 433 exit(1); 434} 435