main.c revision 1.4
1/* $NetBSD: main.c,v 1.4 2019/10/13 21:56:14 joerg 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: main.c,v 1.4 2019/10/13 21:56:14 joerg Exp $"); 11 12/*- 13 * Copyright (c) 1999-2019 The NetBSD Foundation, Inc. 14 * All rights reserved. 15 * 16 * This code is derived from software contributed to The NetBSD Foundation 17 * by Hubert Feyrer <hubert@feyrer.de> and 18 * by Joerg Sonnenberger <joerg@NetBSD.org>. 19 * 20 * Redistribution and use in source and binary forms, with or without 21 * modification, are permitted provided that the following conditions 22 * are met: 23 * 1. Redistributions of source code must retain the above copyright 24 * notice, this list of conditions and the following disclaimer. 25 * 2. Redistributions in binary form must reproduce the above copyright 26 * notice, this list of conditions and the following disclaimer in the 27 * documentation and/or other materials provided with the distribution. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39 * POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42#if HAVE_SYS_TYPES_H 43#include <sys/types.h> 44#endif 45#if HAVE_SYS_STAT_H 46#include <sys/stat.h> 47#endif 48#if HAVE_DIRENT_H 49#include <dirent.h> 50#endif 51#if HAVE_ERR_H 52#include <err.h> 53#endif 54#if HAVE_ERRNO_H 55#include <errno.h> 56#endif 57#if HAVE_FCNTL_H 58#include <fcntl.h> 59#endif 60#ifndef NETBSD 61#include <nbcompat/md5.h> 62#include <nbcompat/sha2.h> 63#else 64#include <md5.h> 65#include <sha2.h> 66#endif 67#if HAVE_LIMITS_H 68#include <limits.h> 69#endif 70#if HAVE_STDIO_H 71#include <stdio.h> 72#endif 73#if HAVE_STRING_H 74#include <string.h> 75#endif 76 77#ifndef BOOTSTRAP 78#include <archive.h> 79#include <fetch.h> 80#endif 81 82#include "admin.h" 83#include "lib.h" 84 85#define DEFAULT_SFX ".t[bg]z" /* default suffix for ls{all,best} */ 86 87struct pkgdb_count { 88 size_t files; 89 size_t directories; 90 size_t packages; 91}; 92 93static const char Options[] = "C:K:SVbd:qs:v"; 94 95int quiet, verbose; 96 97static void set_unset_variable(char **, Boolean); 98static void digest_input(char **); 99 100/* print usage message and exit */ 101void 102usage(void) 103{ 104 (void) fprintf(stderr, "usage: %s [-bqSVv] [-C config] [-d lsdir] [-K pkg_dbdir] [-s sfx] command [args ...]\n" 105 "Where 'commands' and 'args' are:\n" 106 " rebuild - rebuild pkgdb from +CONTENTS files\n" 107 " rebuild-tree - rebuild +REQUIRED_BY files from forward deps\n" 108 " check [pkg ...] - check md5 checksum of installed files\n" 109 " add pkg ... - add pkg files to database\n" 110 " set variable=value pkg ... - set installation variable for package\n" 111 " unset variable pkg ... - unset installation variable for package\n" 112 " lsall /path/to/pkgpattern - list all pkgs matching the pattern\n" 113 " lsbest /path/to/pkgpattern - list pkgs matching the pattern best\n" 114 " dump - dump database\n" 115 " pmatch pattern pkg - returns true if pkg matches pattern, otherwise false\n" 116 " fetch-pkg-vulnerabilities [-s] - fetch new vulnerability file\n" 117 " check-pkg-vulnerabilities [-s] <file> - check syntax and checksums of the vulnerability file\n" 118 " audit [-eis] [-t type] ... - check installed packages for vulnerabilities\n" 119 " audit-pkg [-eis] [-t type] ... - check listed packages for vulnerabilities\n" 120 " audit-batch [-eis] [-t type] ... - check packages in listed files for vulnerabilities\n" 121 " audit-history [-t type] ... - print all advisories for package names\n" 122 " check-license <condition> - check if condition is acceptable\n" 123 " check-single-license <license> - check if license is acceptable\n" 124 " config-var name - print current value of the configuration variable\n" 125 " check-signature ... - verify the signature of packages\n" 126 " x509-sign-package pkg spkg key cert - create X509 signature\n" 127 " gpg-sign-package pkg spkg - create GPG signature\n", 128 getprogname()); 129 exit(EXIT_FAILURE); 130} 131 132/* 133 * add1pkg(<pkg>) 134 * adds the files listed in the +CONTENTS of <pkg> into the 135 * pkgdb.byfile.db database file in the current package dbdir. It 136 * returns the number of files added to the database file. 137 */ 138static int 139add_pkg(const char *pkgdir, void *vp) 140{ 141 FILE *f; 142 plist_t *p; 143 package_t Plist; 144 char *contents; 145 char *PkgName, *dirp; 146 char file[MaxPathSize]; 147 struct pkgdb_count *count; 148 149 if (!pkgdb_open(ReadWrite)) 150 err(EXIT_FAILURE, "cannot open pkgdb"); 151 152 count = vp; 153 ++count->packages; 154 155 contents = pkgdb_pkg_file(pkgdir, CONTENTS_FNAME); 156 if ((f = fopen(contents, "r")) == NULL) 157 errx(EXIT_FAILURE, "%s: can't open `%s'", pkgdir, CONTENTS_FNAME); 158 free(contents); 159 160 read_plist(&Plist, f); 161 if ((p = find_plist(&Plist, PLIST_NAME)) == NULL) { 162 errx(EXIT_FAILURE, "Package `%s' has no @name, aborting.", pkgdir); 163 } 164 165 PkgName = p->name; 166 dirp = NULL; 167 for (p = Plist.head; p; p = p->next) { 168 switch(p->type) { 169 case PLIST_FILE: 170 if (dirp == NULL) { 171 errx(EXIT_FAILURE, "@cwd not yet found, please send-pr!"); 172 } 173 (void) snprintf(file, sizeof(file), "%s/%s", dirp, p->name); 174 if (!(isfile(file) || islinktodir(file))) { 175 if (isbrokenlink(file)) { 176 warnx("%s: Symlink `%s' exists and is in %s but target does not exist!", 177 PkgName, file, CONTENTS_FNAME); 178 } else { 179 warnx("%s: File `%s' is in %s but not on filesystem!", 180 PkgName, file, CONTENTS_FNAME); 181 } 182 } else { 183 pkgdb_store(file, PkgName); 184 ++count->files; 185 } 186 break; 187 case PLIST_PKGDIR: 188 add_pkgdir(PkgName, dirp, p->name); 189 ++count->directories; 190 break; 191 case PLIST_CWD: 192 if (strcmp(p->name, ".") != 0) 193 dirp = p->name; 194 else 195 dirp = pkgdb_pkg_dir(pkgdir); 196 break; 197 case PLIST_IGNORE: 198 p = p->next; 199 break; 200 case PLIST_SHOW_ALL: 201 case PLIST_SRC: 202 case PLIST_CMD: 203 case PLIST_CHMOD: 204 case PLIST_CHOWN: 205 case PLIST_CHGRP: 206 case PLIST_COMMENT: 207 case PLIST_NAME: 208 case PLIST_UNEXEC: 209 case PLIST_DISPLAY: 210 case PLIST_PKGDEP: 211 case PLIST_DIR_RM: 212 case PLIST_OPTION: 213 case PLIST_PKGCFL: 214 case PLIST_BLDDEP: 215 break; 216 } 217 } 218 free_plist(&Plist); 219 fclose(f); 220 pkgdb_close(); 221 222 return 0; 223} 224 225static void 226rebuild(void) 227{ 228 char *cachename; 229 struct pkgdb_count count; 230 231 count.files = 0; 232 count.directories = 0; 233 count.packages = 0; 234 235 cachename = pkgdb_get_database(); 236 if (unlink(cachename) != 0 && errno != ENOENT) 237 err(EXIT_FAILURE, "unlink %s", cachename); 238 239 setbuf(stdout, NULL); 240 241 iterate_pkg_db(add_pkg, &count); 242 243 printf("\n"); 244 printf("Stored %" PRIzu " file%s and %" PRIzu " explicit director%s" 245 " from %"PRIzu " package%s in %s.\n", 246 count.files, count.files == 1 ? "" : "s", 247 count.directories, count.directories == 1 ? "y" : "ies", 248 count.packages, count.packages == 1 ? "" : "s", 249 cachename); 250} 251 252static int 253lspattern(const char *pkg, void *vp) 254{ 255 const char *dir = vp; 256 printf("%s/%s\n", dir, pkg); 257 return 0; 258} 259 260static int 261lsbasepattern(const char *pkg, void *vp) 262{ 263 puts(pkg); 264 return 0; 265} 266 267static int 268remove_required_by(const char *pkgname, void *cookie) 269{ 270 char *path; 271 272 path = pkgdb_pkg_file(pkgname, REQUIRED_BY_FNAME); 273 274 if (unlink(path) == -1 && errno != ENOENT) 275 err(EXIT_FAILURE, "Cannot remove %s", path); 276 277 free(path); 278 279 return 0; 280} 281 282static void 283add_required_by(const char *pattern, const char *required_by) 284{ 285 char *best_installed, *path; 286 int fd; 287 size_t len; 288 289 best_installed = find_best_matching_installed_pkg(pattern); 290 if (best_installed == NULL) { 291 warnx("Dependency %s of %s unresolved", pattern, required_by); 292 return; 293 } 294 295 path = pkgdb_pkg_file(best_installed, REQUIRED_BY_FNAME); 296 free(best_installed); 297 298 if ((fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) 299 errx(EXIT_FAILURE, "Cannot write to %s", path); 300 free(path); 301 302 len = strlen(required_by); 303 if (write(fd, required_by, len) != (ssize_t)len || 304 write(fd, "\n", 1) != 1 || 305 close(fd) == -1) 306 errx(EXIT_FAILURE, "Cannot write to %s", path); 307} 308 309 310static int 311add_depends_of(const char *pkgname, void *cookie) 312{ 313 FILE *fp; 314 plist_t *p; 315 package_t plist; 316 char *path; 317 318 path = pkgdb_pkg_file(pkgname, CONTENTS_FNAME); 319 if ((fp = fopen(path, "r")) == NULL) 320 errx(EXIT_FAILURE, "Cannot read %s of package %s", 321 CONTENTS_FNAME, pkgname); 322 free(path); 323 read_plist(&plist, fp); 324 fclose(fp); 325 326 for (p = plist.head; p; p = p->next) { 327 if (p->type == PLIST_PKGDEP) 328 add_required_by(p->name, pkgname); 329 } 330 331 free_plist(&plist); 332 333 return 0; 334} 335 336static void 337rebuild_tree(void) 338{ 339 if (iterate_pkg_db(remove_required_by, NULL) == -1) 340 errx(EXIT_FAILURE, "cannot iterate pkgdb"); 341 if (iterate_pkg_db(add_depends_of, NULL) == -1) 342 errx(EXIT_FAILURE, "cannot iterate pkgdb"); 343} 344 345int 346main(int argc, char *argv[]) 347{ 348 Boolean use_default_sfx = TRUE; 349 Boolean show_basename_only = FALSE; 350 char lsdir[MaxPathSize]; 351 char sfx[MaxPathSize]; 352 char *lsdirp = NULL; 353 int ch; 354 355 setprogname(argv[0]); 356 357 if (argc < 2) 358 usage(); 359 360 while ((ch = getopt(argc, argv, Options)) != -1) 361 switch (ch) { 362 case 'C': 363 config_file = optarg; 364 break; 365 366 case 'K': 367 pkgdb_set_dir(optarg, 3); 368 break; 369 370 case 'S': 371 sfx[0] = 0x0; 372 use_default_sfx = FALSE; 373 break; 374 375 case 'V': 376 show_version(); 377 /* NOTREACHED */ 378 379 case 'b': 380 show_basename_only = TRUE; 381 break; 382 383 case 'd': 384 (void) strlcpy(lsdir, optarg, sizeof(lsdir)); 385 lsdirp = lsdir; 386 break; 387 388 case 'q': 389 quiet = 1; 390 break; 391 392 case 's': 393 (void) strlcpy(sfx, optarg, sizeof(sfx)); 394 use_default_sfx = FALSE; 395 break; 396 397 case 'v': 398 ++verbose; 399 break; 400 401 default: 402 usage(); 403 /* NOTREACHED */ 404 } 405 406 argc -= optind; 407 argv += optind; 408 409 if (argc <= 0) { 410 usage(); 411 } 412 413 /* 414 * config-var is reading the config file implicitly, 415 * so skip it here. 416 */ 417 if (strcasecmp(argv[0], "config-var") != 0) 418 pkg_install_config(); 419 420 if (use_default_sfx) 421 (void) strlcpy(sfx, DEFAULT_SFX, sizeof(sfx)); 422 423 if (strcasecmp(argv[0], "pmatch") == 0) { 424 425 char *pattern, *pkg; 426 427 argv++; /* "pmatch" */ 428 429 if (argv[0] == NULL || argv[1] == NULL) { 430 usage(); 431 } 432 433 pattern = argv[0]; 434 pkg = argv[1]; 435 436 if (pkg_match(pattern, pkg)){ 437 return 0; 438 } else { 439 return 1; 440 } 441 442 } else if (strcasecmp(argv[0], "rebuild") == 0) { 443 444 rebuild(); 445 printf("Done.\n"); 446 447 448 } else if (strcasecmp(argv[0], "rebuild-tree") == 0) { 449 450 rebuild_tree(); 451 printf("Done.\n"); 452 453 } else if (strcasecmp(argv[0], "check") == 0) { 454 argv++; /* "check" */ 455 456 check(argv); 457 458 if (!quiet) { 459 printf("Done.\n"); 460 } 461 462 } else if (strcasecmp(argv[0], "lsall") == 0) { 463 argv++; /* "lsall" */ 464 465 while (*argv != NULL) { 466 /* args specified */ 467 int rc; 468 const char *basep, *dir; 469 470 dir = lsdirp ? lsdirp : dirname_of(*argv); 471 basep = basename_of(*argv); 472 473 if (show_basename_only) 474 rc = match_local_files(dir, use_default_sfx, 1, basep, lsbasepattern, NULL); 475 else 476 rc = match_local_files(dir, use_default_sfx, 1, basep, lspattern, __UNCONST(dir)); 477 if (rc == -1) 478 errx(EXIT_FAILURE, "Error from match_local_files(\"%s\", \"%s\", ...)", 479 dir, basep); 480 481 argv++; 482 } 483 484 } else if (strcasecmp(argv[0], "lsbest") == 0) { 485 argv++; /* "lsbest" */ 486 487 while (*argv != NULL) { 488 /* args specified */ 489 const char *basep, *dir; 490 char *p; 491 492 dir = lsdirp ? lsdirp : dirname_of(*argv); 493 basep = basename_of(*argv); 494 495 p = find_best_matching_file(dir, basep, use_default_sfx, 1); 496 497 if (p) { 498 if (show_basename_only) 499 printf("%s\n", p); 500 else 501 printf("%s/%s\n", dir, p); 502 free(p); 503 } 504 505 argv++; 506 } 507 } else if (strcasecmp(argv[0], "list") == 0 || 508 strcasecmp(argv[0], "dump") == 0) { 509 510 pkgdb_dump(); 511 512 } else if (strcasecmp(argv[0], "add") == 0) { 513 struct pkgdb_count count; 514 515 count.files = 0; 516 count.directories = 0; 517 count.packages = 0; 518 519 for (++argv; *argv != NULL; ++argv) 520 add_pkg(*argv, &count); 521 } else if (strcasecmp(argv[0], "set") == 0) { 522 argv++; /* "set" */ 523 set_unset_variable(argv, FALSE); 524 } else if (strcasecmp(argv[0], "unset") == 0) { 525 argv++; /* "unset" */ 526 set_unset_variable(argv, TRUE); 527 } else if (strcasecmp(argv[0], "digest") == 0) { 528 argv++; /* "digest" */ 529 digest_input(argv); 530 } else if (strcasecmp(argv[0], "config-var") == 0) { 531 argv++; 532 if (argv == NULL || argv[1] != NULL) 533 errx(EXIT_FAILURE, "config-var takes exactly one argument"); 534 pkg_install_show_variable(argv[0]); 535 } else if (strcasecmp(argv[0], "check-license") == 0) { 536 if (argv[1] == NULL) 537 errx(EXIT_FAILURE, "check-license takes exactly one argument"); 538 539 load_license_lists(); 540 541 switch (acceptable_pkg_license(argv[1])) { 542 case 0: 543 puts("no"); 544 return 0; 545 case 1: 546 puts("yes"); 547 return 0; 548 case -1: 549 errx(EXIT_FAILURE, "invalid license condition"); 550 } 551 } else if (strcasecmp(argv[0], "check-single-license") == 0) { 552 if (argv[1] == NULL) 553 errx(EXIT_FAILURE, "check-license takes exactly one argument"); 554 load_license_lists(); 555 556 switch (acceptable_license(argv[1])) { 557 case 0: 558 puts("no"); 559 return 0; 560 case 1: 561 puts("yes"); 562 return 0; 563 case -1: 564 errx(EXIT_FAILURE, "invalid license"); 565 } 566 } 567#ifndef BOOTSTRAP 568 else if (strcasecmp(argv[0], "findbest") == 0) { 569 struct url *url; 570 char *output; 571 int rc; 572 573 process_pkg_path(); 574 575 rc = 0; 576 for (++argv; *argv != NULL; ++argv) { 577 url = find_best_package(NULL, *argv, 1); 578 if (url == NULL) { 579 rc = 1; 580 continue; 581 } 582 output = fetchStringifyURL(url); 583 puts(output); 584 fetchFreeURL(url); 585 free(output); 586 } 587 588 return rc; 589 } else if (strcasecmp(argv[0], "fetch-pkg-vulnerabilities") == 0) { 590 fetch_pkg_vulnerabilities(--argc, ++argv); 591 } else if (strcasecmp(argv[0], "check-pkg-vulnerabilities") == 0) { 592 check_pkg_vulnerabilities(--argc, ++argv); 593 } else if (strcasecmp(argv[0], "audit") == 0) { 594 audit_pkgdb(--argc, ++argv); 595 } else if (strcasecmp(argv[0], "audit-pkg") == 0) { 596 audit_pkg(--argc, ++argv); 597 } else if (strcasecmp(argv[0], "audit-batch") == 0) { 598 audit_batch(--argc, ++argv); 599 } else if (strcasecmp(argv[0], "audit-history") == 0) { 600 audit_history(--argc, ++argv); 601 } else if (strcasecmp(argv[0], "check-signature") == 0) { 602 struct archive *pkg; 603 int rc; 604 605 rc = 0; 606 for (--argc, ++argv; argc > 0; --argc, ++argv) { 607 char *archive_name; 608 609 pkg = open_archive(*argv, &archive_name); 610 if (pkg == NULL) { 611 warnx("%s could not be opened", *argv); 612 continue; 613 } 614 if (pkg_full_signature_check(archive_name, &pkg)) 615 rc = 1; 616 free(archive_name); 617 if (pkg != NULL) 618 archive_read_free(pkg); 619 } 620 return rc; 621 } else if (strcasecmp(argv[0], "x509-sign-package") == 0) { 622#ifdef HAVE_SSL 623 --argc; 624 ++argv; 625 if (argc != 4) 626 errx(EXIT_FAILURE, "x509-sign-package takes exactly four arguments"); 627 pkg_sign_x509(argv[0], argv[1], argv[2], argv[3]); 628#else 629 errx(EXIT_FAILURE, "OpenSSL support is not included"); 630#endif 631 } else if (strcasecmp(argv[0], "gpg-sign-package") == 0) { 632 --argc; 633 ++argv; 634 if (argc != 2) 635 errx(EXIT_FAILURE, "gpg-sign-package takes exactly two arguments"); 636 pkg_sign_gpg(argv[0], argv[1]); 637 } 638#endif 639 else { 640 usage(); 641 } 642 643 return 0; 644} 645 646struct set_installed_info_arg { 647 char *variable; 648 char *value; 649 int got_match; 650}; 651 652static int 653set_installed_info_var(const char *name, void *cookie) 654{ 655 struct set_installed_info_arg *arg = cookie; 656 char *filename; 657 int retval; 658 659 filename = pkgdb_pkg_file(name, INSTALLED_INFO_FNAME); 660 661 retval = var_set(filename, arg->variable, arg->value); 662 663 free(filename); 664 arg->got_match = 1; 665 666 return retval; 667} 668 669static void 670set_unset_variable(char **argv, Boolean unset) 671{ 672 struct set_installed_info_arg arg; 673 char *eq; 674 char *variable; 675 int ret = 0; 676 677 if (argv[0] == NULL || argv[1] == NULL) 678 usage(); 679 680 variable = NULL; 681 682 if (unset) { 683 arg.variable = argv[0]; 684 arg.value = NULL; 685 } else { 686 eq = NULL; 687 if ((eq=strchr(argv[0], '=')) == NULL) 688 usage(); 689 690 variable = xmalloc(eq-argv[0]+1); 691 strlcpy(variable, argv[0], eq-argv[0]+1); 692 693 arg.variable = variable; 694 arg.value = eq+1; 695 696 if (strcmp(variable, AUTOMATIC_VARNAME) == 0 && 697 strcasecmp(arg.value, "yes") != 0 && 698 strcasecmp(arg.value, "no") != 0) { 699 errx(EXIT_FAILURE, 700 "unknown value `%s' for " AUTOMATIC_VARNAME, 701 arg.value); 702 } 703 } 704 if (strpbrk(arg.variable, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != NULL) { 705 free(variable); 706 errx(EXIT_FAILURE, 707 "variable name must not contain uppercase letters"); 708 } 709 710 argv++; 711 while (*argv != NULL) { 712 arg.got_match = 0; 713 if (match_installed_pkgs(*argv, set_installed_info_var, &arg) == -1) 714 errx(EXIT_FAILURE, "Cannot process pkdbdb"); 715 if (arg.got_match == 0) { 716 char *pattern; 717 718 if (ispkgpattern(*argv)) { 719 warnx("no matching pkg for `%s'", *argv); 720 ret++; 721 } else { 722 pattern = xasprintf("%s-[0-9]*", *argv); 723 724 if (match_installed_pkgs(pattern, set_installed_info_var, &arg) == -1) 725 errx(EXIT_FAILURE, "Cannot process pkdbdb"); 726 727 if (arg.got_match == 0) { 728 warnx("cannot find package %s", *argv); 729 ++ret; 730 } 731 free(pattern); 732 } 733 } 734 735 argv++; 736 } 737 738 if (ret > 0) 739 exit(EXIT_FAILURE); 740 741 free(variable); 742 743 return; 744} 745 746static void 747digest_input(char **argv) 748{ 749 char digest[SHA256_DIGEST_STRING_LENGTH]; 750 int failures = 0; 751 752 while (*argv != NULL) { 753 if (SHA256_File(*argv, digest)) { 754 puts(digest); 755 } else { 756 warn("cannot process %s", *argv); 757 ++failures; 758 } 759 argv++; 760 } 761 if (failures) 762 exit(EXIT_FAILURE); 763} 764