diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/tools/protocmp/protocmp.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/tools/protocmp/protocmp.c')
-rw-r--r-- | usr/src/tools/protocmp/protocmp.c | 830 |
1 files changed, 830 insertions, 0 deletions
diff --git a/usr/src/tools/protocmp/protocmp.c b/usr/src/tools/protocmp/protocmp.c new file mode 100644 index 0000000000..fb9cd643a3 --- /dev/null +++ b/usr/src/tools/protocmp/protocmp.c @@ -0,0 +1,830 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <sys/param.h> +#include <errno.h> +#include <unistd.h> +#include <ftw.h> + +#include "list.h" +#include "protocmp.h" +#include "proto_list.h" +#include "protodir.h" +#include "exception_list.h" +#include "stdusers.h" + +#define MAX_PROTO_REFS 5 +#define MAX_EXCEPTION_FILES 5 +#define MAX_DEPTH 50 + +/* + * default flag values + */ +static int check_group = 1; +static int set_group = 0; +static int check_user = 1; +static int set_user = 0; +static int check_perm = 1; +static int set_perm = 0; +static int check_link = 1; +static int check_sym = 1; +static int check_majmin = 1; + +static elem_list first_list; +static char *first_file_name; + +static elem_list second_list; +static char *second_file_name; + +static FILE *need_add_fp; +static char *need_add_file; +static FILE *need_rm_fp; +static char *need_rm_file; +static FILE *differ_fp; +static char *differ_file; + +static char *myname; + +/* + * default flag values + */ +static int verbose = 0; + +static void +usage(void) +{ + (void) fputs("usage: protocmp [-gupGUPlmsLv] " + "[-e <exception-list> ...] " + "-d <protolist|pkg dir>\n\t[-d <protolist|pkg dir> ...] " + "[<protolist|pkg dir>...]|<root>]\n", + stderr); + (void) fputs(" where:\n", stderr); + (void) fputs("\t-g : don't compare group\n", stderr); + (void) fputs("\t-u : don't compare owner\n", stderr); + (void) fputs("\t-p : don't compare permissions\n", stderr); + (void) fputs("\t-G : set group\n", stderr); + (void) fputs("\t-U : set owner\n", stderr); + (void) fputs("\t-P : set permissions\n", stderr); + (void) fputs("\t-l : don't compare link counts\n", stderr); + (void) fputs("\t-m : don't compare major/minor numbers\n", + stderr); + (void) fputs("\t-s : don't compare symlink values\n", stderr); + (void) fputs("\t-d <protolist|pkg dir>:\n", stderr); + (void) fputs("\t proto list or packaging to check\n", stderr); + (void) fputs("\t-e <file>: exceptions file\n", stderr); + (void) fputs("\t-L : list filtered exceptions\n", stderr); + (void) fputs("\t-v : verbose output\n", stderr); + (void) fputs("\n" +"If any of the -[GUP] flags are given, then the final argument must be the\n" +"proto root directory itself on which to set permissions according to the\n" +"packaging data specified via -d options.\n", stderr); +} + + +static void +open_output_files(void) +{ + if ((need_add_fp = + fopen((need_add_file = tempnam(NULL, "add")), "w")) == NULL) { + perror(need_add_file); + exit(1); + } + + if ((need_rm_fp = + fopen((need_rm_file = tempnam(NULL, "rm")), "w")) == NULL) { + perror(need_rm_file); + exit(1); + } + + if ((differ_fp = + fopen((differ_file = tempnam(NULL, "diff")), "w")) == NULL) { + perror(differ_file); + exit(1); + } +} + +static void +close_output_files(void) +{ + (void) fclose(need_add_fp); + (void) fclose(need_rm_fp); + (void) fclose(differ_fp); +} + +static void +print_file(char *file) +{ + FILE *fp; + int count; + char buff[BUF_SIZE]; + + if ((fp = fopen(file, "r")) == NULL) { + perror(need_add_file); + } + + while (count = fread(buff, sizeof (char), BUF_SIZE, fp)) + (void) fwrite(buff, sizeof (char), count, stdout); + (void) fclose(fp); +} + +static void +print_header(void) +{ + (void) printf("%c %-30s %-20s %-4s %-5s %-5s %-5s %-2s %2s %2s %-9s\n", + 'T', "File Name", "Reloc/Sym name", "perm", "owner", "group", + "inode", "lnk", "maj", "min", "package(s)"); + (void) puts("-------------------------------------------------------" + "-----------------------------------------------------"); +} + +static void +print_results(void) +{ + (void) puts("*******************************************************"); + (void) puts("*"); + (void) printf("* Entries found in %s, but not found in %s\n", + first_file_name, second_file_name); + (void) puts("*"); + (void) puts("*******************************************************"); + print_header(); + print_file(need_add_file); + (void) puts("*******************************************************"); + (void) puts("*"); + (void) printf("* Entries found in %s, but not found in %s\n", + second_file_name, first_file_name); + (void) puts("*"); + (void) puts("*******************************************************"); + print_header(); + print_file(need_rm_file); + (void) puts("*******************************************************"); + (void) puts("*"); + (void) printf("* Entries that differ between %s and %s\n", + first_file_name, second_file_name); + (void) puts("*"); + (void) printf("* filea == %s\n", first_file_name); + (void) printf("* fileb == %s\n", second_file_name); + (void) puts("*"); + (void) puts("*******************************************************"); + (void) fputs("Unit ", stdout); + print_header(); + print_file(differ_file); +} + +static void +clean_up(void) +{ + (void) unlink(need_add_file); + (void) unlink(need_rm_file); + (void) unlink(differ_file); +} + +/* + * elem_compare(a,b) + * + * Args: + * a - element a + * b - element b + * different_types - + * value = 0 -> comparing two elements of same + * type (eg: protodir elem vs. protodir elem). + * value != 0 -> comparing two elements of different type + * (eg: protodir elem vs. protolist elem). + * + * Returns: + * 0 - elements are identical + * >0 - elements differ + * check flags to see which fields differ. + */ +static int +elem_compare(elem *a, elem *b, int different_types) +{ + int res = 0; + elem *i, *j; + + /* + * if these are hard links to other files - those are the + * files that should be compared. + */ + i = a->link_parent ? a->link_parent : a; + j = b->link_parent ? b->link_parent : b; + + /* + * We do not compare inodes - they always differ. + * We do not compare names because we assume that was + * checked before. + */ + + /* + * Special rules for comparison: + * + * 1) if directory - ignore ref_cnt. + * 2) if sym_link - only check file_type & symlink + * 3) elem type of FILE_T, EDIT_T, & VOLATILE_T are equivilant when + * comparing a protodir entry to a protolist entry. + */ + if (i->file_type != j->file_type) { + if (different_types) { + /* + * Check to see if filetypes are FILE_T vs. + * EDIT_T/VOLATILE_T/LINK_T comparisons. + */ + if ((i->file_type == FILE_T) && + ((j->file_type == EDIT_T) || + (j->file_type == VOLATILE_T) || + (j->file_type == LINK_T))) { + /*EMPTY*/ + } else if ((j->file_type == FILE_T) && + ((i->file_type == EDIT_T) || + (i->file_type == VOLATILE_T) || + (i->file_type == LINK_T))) { + /*EMPTY*/ + } else + res |= TYPE_F; + } else + res |= TYPE_F; + } + + /* + * if symlink - check the symlink value and then + * return. symlink is the only field of concern + * in SYMLINKS. + */ + if (check_sym && ((res == 0) && (i->file_type == SYM_LINK_T))) { + if ((!i->symsrc) || (!j->symsrc)) + res |= SYM_F; + else { + /* + * if either symlink starts with a './' strip it off, + * its irrelavant. + */ + if ((i->symsrc[0] == '.') && (i->symsrc[1] == '/')) + i->symsrc += 2; + if ((j->symsrc[0] == '.') && (j->symsrc[1] == '/')) + j->symsrc += 2; + + if (strncmp(i->symsrc, j->symsrc, MAXNAME) != 0) + res |= SYM_F; + } + return (res); + } + + if ((i->file_type != DIR_T) && check_link && + (i->ref_cnt != j->ref_cnt)) + res |= REF_F; + if (check_user && (strncmp(i->owner, j->owner, TYPESIZE) != 0)) + res |= OWNER_F; + if (check_group && (strncmp(i->group, j->group, TYPESIZE) != 0)) + res |= GROUP_F; + if (check_perm && (i->perm != j->perm)) + res |= PERM_F; + if (check_majmin && ((i->major != j->major) || (i->minor != j->minor))) + res |= MAJMIN_F; + + return (res); +} + +static void +print_elem(FILE *fp, elem *e) +{ + elem p; + pkg_list *l; + char maj[TYPESIZE], min[TYPESIZE]; + char perm[12], ref_cnt[12]; + + /* + * If this is a LINK to another file, then adopt + * the permissions of that file. + */ + if (e->link_parent) { + p = *((elem *)e->link_parent); + (void) strcpy(p.name, e->name); + p.symsrc = e->symsrc; + p.file_type = e->file_type; + e = &p; + } + + if (!check_majmin || e->major == -1) { + maj[0] = '-'; + maj[1] = '\0'; + } else { + (void) sprintf(maj, "%d", e->major); + } + + if (!check_majmin || e->minor == -1) { + min[0] = '-'; + min[1] = '\0'; + } else { + (void) sprintf(min, "%d", e->minor); + } + + if (!check_perm) { + perm[0] = '-'; + perm[1] = '\0'; + } else { + (void) snprintf(perm, sizeof (perm), "%o", e->perm); + } + + if (!check_link) { + ref_cnt[0] = '-'; + ref_cnt[1] = '\0'; + } else { + (void) snprintf(ref_cnt, sizeof (ref_cnt), "%d", e->ref_cnt); + } + + (void) fprintf(fp, "%c %-30s %-20s %4s %-5s %-5s %6d %2s %2s %2s ", + e->file_type, e->name, + check_sym && e->symsrc != NULL ? e->symsrc : "-", perm, + check_user ? e->owner : "-", + check_group ? e->group : "-", + e->inode, ref_cnt, maj, min); + /* + * dump package list - if any. + */ + if (!e->pkgs) + (void) fputs(" proto", fp); + + for (l = e->pkgs; l; l = l->next) { + (void) fputc(' ', fp); + (void) fputs(l->pkg_name, fp); + } + (void) fputc('\n', fp); +} + +/* + * do_compare(a,b) + * + * Args: + * different_types - see elem_compare() for explanation. + */ +static void +do_compare(elem *a, elem *b, int different_types) +{ + int rc; + + if ((rc = elem_compare(a, b, different_types)) != 0) { + (void) fputs("filea: ", differ_fp); + print_elem(differ_fp, a); + (void) fputs("fileb: ", differ_fp); + print_elem(differ_fp, b); + (void) fputs(" differ: ", differ_fp); + + if (rc & SYM_F) + (void) fputs("symlink", differ_fp); + if (rc & PERM_F) + (void) fputs("perm ", differ_fp); + if (rc & REF_F) + (void) fputs("ref_cnt ", differ_fp); + if (rc & TYPE_F) + (void) fputs("file_type ", differ_fp); + if (rc & OWNER_F) + (void) fputs("owner ", differ_fp); + if (rc & GROUP_F) + (void) fputs("group ", differ_fp); + if (rc & MAJMIN_F) + (void) fputs("major/minor ", differ_fp); + (void) putc('\n', differ_fp); + (void) putc('\n', differ_fp); + } +} + +static void +check_second_vs_first(int verbose) +{ + int i; + elem *cur; + + for (i = 0; i < second_list.num_of_buckets; i++) { + for (cur = second_list.list[i]; cur; cur = cur->next) { + if (!(cur->flag & VISITED_F)) { + if ((first_list.type != second_list.type) && + find_elem(&exception_list, cur, + FOLLOW_LINK)) { + /* + * this entry is filtered, we don't + * need to do any more processing. + */ + if (verbose) { + (void) printf( + "Filtered: Need Deletion " + "of:\n\t"); + print_elem(stdout, cur); + } + continue; + } + /* + * It is possible for arch specific files to be + * found in a protodir but listed as arch + * independent in a protolist file. If this is + * a protodir vs. a protolist we will make + * that check. + */ + if ((second_list.type == PROTODIR_LIST) && + (cur->arch != P_ISA) && + (first_list.type != PROTODIR_LIST)) { + /* + * do a lookup for same file, but as + * type ISA. + */ + elem *e; + + e = find_elem_isa(&first_list, cur, + NO_FOLLOW_LINK); + if (e) { + do_compare(e, cur, + first_list.type - + second_list.type); + continue; + } + } + + print_elem(need_rm_fp, cur); + } + } + } +} + +static void +check_first_vs_second(int verbose) +{ + int i; + elem *e; + elem *cur; + + for (i = 0; i < first_list.num_of_buckets; i++) { + for (cur = first_list.list[i]; cur; cur = cur->next) { + if ((first_list.type != second_list.type) && + find_elem(&exception_list, cur, FOLLOW_LINK)) { + /* + * this entry is filtered, we don't need to do + * any more processing. + */ + if (verbose) { + (void) printf("Filtered: Need " + "Addition of:\n\t"); + print_elem(stdout, cur); + } + continue; + } + + /* + * Search package database for file. + */ + e = find_elem(&second_list, cur, NO_FOLLOW_LINK); + + /* + * It is possible for arch specific files to be found + * in a protodir but listed as arch independent in a + * protolist file. If this is a protodir vs. a + * protolist we will make that check. + */ + if (!e && (first_list.type == PROTODIR_LIST) && + (cur->arch != P_ISA) && + (second_list.type != PROTODIR_LIST)) { + /* + * do a lookup for same file, but as type ISA. + */ + e = find_elem_isa(&second_list, cur, + NO_FOLLOW_LINK); + } + + if (!e && (first_list.type != PROTODIR_LIST) && + (cur->arch == P_ISA) && + (second_list.type == PROTODIR_LIST)) { + /* + * do a lookup for same file, but as any + * type but ISA + */ + e = find_elem_mach(&second_list, cur, + NO_FOLLOW_LINK); + } + + if (e == NULL) + print_elem(need_add_fp, cur); + else { + do_compare(cur, e, + first_list.type - second_list.type); + e->flag |= VISITED_F; + } + } + } +} + +static int +read_in_file(const char *file_name, elem_list *list) +{ + struct stat st_buf; + int count = 0; + + if (stat(file_name, &st_buf) == 0) { + if (S_ISREG(st_buf.st_mode)) { + if (verbose) { + (void) printf("file(%s): trying to process " + "as protolist...\n", file_name); + } + count = read_in_protolist(file_name, list, verbose); + } else if (S_ISDIR(st_buf.st_mode)) { + if (verbose) + (void) printf("directory(%s): trying to " + "process as protodir...\n", file_name); + count = read_in_protodir(file_name, list, verbose); + } else { + (void) fprintf(stderr, + "%s not a file or a directory.\n", file_name); + usage(); + exit(1); + } + } else { + perror(file_name); + usage(); + exit(1); + } + + return (count); +} + +/* ARGSUSED */ +static int +set_values(const char *fname, const struct stat *sbp, int otype, + struct FTW *ftw) +{ + elem *ep; + uid_t uid; + gid_t gid; + elem keyelem; + mode_t perm; + + if (fname[0] == '\0' || fname[1] == '\0' || fname[2] == '\0') + return (0); + /* skip leading "./" */ + fname += 2; + switch (otype) { + case FTW_F: + case FTW_D: + case FTW_DP: + if (strlcpy(keyelem.name, fname, sizeof (keyelem.name)) >= + sizeof (keyelem.name)) { + (void) fprintf(stderr, "%s: %s: name too long\n", + myname, fname); + return (1); + } + keyelem.arch = P_ISA; + ep = find_elem(&first_list, &keyelem, NO_FOLLOW_LINK); + if (ep == NULL) { + ep = find_elem_mach(&first_list, &keyelem, + NO_FOLLOW_LINK); + } + /* + * Do nothing if this is a hard or symbolic link, + * since links don't have this information. + * + * Assume it's a file on the exception list if it's + * not found in the packaging. Those are root:bin 755. + */ + if (ep != NULL && + (ep->file_type == SYM_LINK_T || ep->file_type == LINK_T)) { + return (0); + } + if (!set_group) { + gid = -1; + } else if (ep == NULL) { + gid = 0; + } else if ((gid = stdfind(ep->group, groupnames)) == -1) { + (void) fprintf(stderr, "%s: %s: group '%s' unknown\n", + myname, fname, ep->group); + return (1); + } + if (!set_user) { + uid = -1; + } else if (ep == NULL) { + uid = 2; + } else if ((uid = stdfind(ep->owner, usernames)) == -1) { + (void) fprintf(stderr, "%s: %s: user '%s' unknown\n", + myname, fname, ep->owner); + return (1); + } + if ((set_group && gid != -1 && gid != sbp->st_gid) || + (set_user && uid != -1 && uid != sbp->st_uid)) { + if (verbose) { + const char *owner, *group; + + owner = ep == NULL ? "root" : ep->owner; + group = ep == NULL ? "bin" : ep->group; + if (set_group && set_user) { + (void) printf("chown %s:%s %s\n", + owner, group, fname); + } else if (set_user) { + (void) printf("chown %s %s\n", owner, + fname); + } else { + (void) printf("chgrp %s %s\n", group, + fname); + } + } + if (lchown(fname, uid, gid) == -1) { + perror(fname); + return (1); + } + } + perm = ep == NULL ? 0755 : ep->perm; + if (set_perm && ((perm ^ sbp->st_mode) & ~S_IFMT) != 0) { + if (verbose) + (void) printf("chmod %lo %s\n", perm, fname); + if (chmod(fname, perm) == -1) { + perror(fname); + return (1); + } + } + return (0); + case FTW_DNR: + case FTW_NS: + (void) fprintf(stderr, "%s: %s: permission denied\n", + myname, fname); + return (1); + case FTW_SL: + case FTW_SLN: + return (0); + default: + return (1); + } +} + +int +main(int argc, char **argv) +{ + int errflg = 0; + int i, c; + int list_filtered_exceptions = NULL; + int n_proto_refs = 0; + int n_exception_files = 0; + char *proto_refs[MAX_PROTO_REFS]; + char *exception_files[MAX_EXCEPTION_FILES]; + struct stat st_buf; + + if ((myname = argv[0]) == NULL) + myname = "protocmp"; + + while ((c = getopt(argc, argv, "gupGUPlmsLe:vd:")) != EOF) { + switch (c) { + case 's': + check_sym = 0; + break; + case 'm': + check_majmin = 0; + break; + case 'g': + check_group = 0; + break; + case 'G': + set_group = 1; + break; + case 'u': + check_user = 0; + break; + case 'U': + set_user = 1; + break; + case 'l': + check_link = 0; + break; + case 'p': + check_perm = 0; + break; + case 'P': + set_perm = 1; + break; + case 'e': + if (n_exception_files >= MAX_EXCEPTION_FILES) { + errflg++; + (void) fprintf(stderr, + "Only %d exception files supported\n", + MAX_EXCEPTION_FILES); + } else { + exception_files[n_exception_files++] = optarg; + } + break; + case 'L': + list_filtered_exceptions++; + break; + case 'v': + verbose++; + break; + case 'd': + if (n_proto_refs >= MAX_PROTO_REFS) { + errflg++; + (void) fprintf(stderr, + "Only %d proto references supported\n", + MAX_PROTO_REFS); + } else { + proto_refs[n_proto_refs++] = optarg; + } + break; + case '?': + default: + errflg++; + break; + } + } + + if (argc == optind || n_proto_refs == 0) { + usage(); + exit(1); + } + + if (set_group || set_user || set_perm) { + if (optind != argc - 1) { + usage(); + exit(1); + } + if (stat(argv[optind], &st_buf) == -1) { + perror(argv[optind]); + exit(1); + } + if (!S_ISDIR(st_buf.st_mode)) { + (void) fprintf(stderr, "%s: %s: not a directory\n", + myname, argv[optind]); + exit(1); + } + } + + init_list(&first_list, HASH_SIZE); + init_list(&second_list, HASH_SIZE); + init_list(&exception_list, HASH_SIZE); + + for (i = 0; i < n_exception_files; i++) { + (void) read_in_exceptions(exception_files[i], verbose); + } + + for (i = 0; i < n_proto_refs; i++) { + first_file_name = proto_refs[i]; + (void) read_in_file(first_file_name, &first_list); + } + + if (set_group || set_user || set_perm) { + if (chdir(argv[optind]) == -1) { + perror(argv[optind]); + exit(1); + } + i = nftw(".", set_values, MAX_DEPTH, FTW_PHYS|FTW_DEPTH); + if (i == -1) { + perror("nftw"); + i = 1; + } + exit(i); + } + + for (i = optind; i < argc; i++) { + second_file_name = argv[i]; + (void) read_in_file(second_file_name, &second_list); + } + + open_output_files(); + + if (verbose) + (void) puts("comparing build to packages..."); + + check_first_vs_second(list_filtered_exceptions); + + if (verbose) + (void) puts("checking over packages..."); + check_second_vs_first(list_filtered_exceptions); + + close_output_files(); + + print_results(); + + clean_up(); + + return (0); +} |