dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / tools / protocmp / protocmp.c
blob4cd1b56eb24bdd1fc4324c11db1bdb8f8aa0e4e4
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <dirent.h>
34 #include <sys/param.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <ftw.h>
39 #include "list.h"
40 #include "protocmp.h"
41 #include "proto_list.h"
42 #include "protodir.h"
43 #include "exception_list.h"
44 #include "stdusers.h"
46 #define MAX_PROTO_REFS 5
47 #define MAX_EXCEPTION_FILES 5
48 #define MAX_DEPTH 50
51 * default flag values
53 static int check_group = 1;
54 static int set_group = 0;
55 static int check_user = 1;
56 static int set_user = 0;
57 static int check_perm = 1;
58 static int set_perm = 0;
59 static int check_link = 1;
60 static int check_sym = 1;
61 static int check_majmin = 1;
63 static elem_list first_list;
64 static char *first_file_name;
66 static elem_list second_list;
67 static char *second_file_name;
69 static FILE *need_add_fp;
70 static char *need_add_file;
71 static FILE *need_rm_fp;
72 static char *need_rm_file;
73 static FILE *differ_fp;
74 static char *differ_file;
76 static char *myname;
79 * default flag values
81 static int verbose = 0;
83 static void
84 usage(void)
86 (void) fputs("usage: protocmp [-gupGUPlmsLv] "
87 "[-e <exception-list> ...] "
88 "-d <protolist|pkg dir>\n\t[-d <protolist|pkg dir> ...] "
89 "[<protolist|pkg dir>...]|<root>]\n",
90 stderr);
91 (void) fputs(" where:\n", stderr);
92 (void) fputs("\t-g : don't compare group\n", stderr);
93 (void) fputs("\t-u : don't compare owner\n", stderr);
94 (void) fputs("\t-p : don't compare permissions\n", stderr);
95 (void) fputs("\t-G : set group\n", stderr);
96 (void) fputs("\t-U : set owner\n", stderr);
97 (void) fputs("\t-P : set permissions\n", stderr);
98 (void) fputs("\t-l : don't compare link counts\n", stderr);
99 (void) fputs("\t-m : don't compare major/minor numbers\n",
100 stderr);
101 (void) fputs("\t-s : don't compare symlink values\n", stderr);
102 (void) fputs("\t-d <protolist|pkg dir>:\n", stderr);
103 (void) fputs("\t proto list or packaging to check\n", stderr);
104 (void) fputs("\t-e <file>: exceptions file\n", stderr);
105 (void) fputs("\t-L : list filtered exceptions\n", stderr);
106 (void) fputs("\t-v : verbose output\n", stderr);
107 (void) fputs("\n"
108 "If any of the -[GUP] flags are given, then the final argument must be the\n"
109 "proto root directory itself on which to set permissions according to the\n"
110 "packaging data specified via -d options.\n", stderr);
114 static void
115 open_output_files(void)
117 if ((need_add_fp =
118 fopen((need_add_file = tempnam(NULL, "add")), "w")) == NULL) {
119 perror(need_add_file);
120 exit(1);
123 if ((need_rm_fp =
124 fopen((need_rm_file = tempnam(NULL, "rm")), "w")) == NULL) {
125 perror(need_rm_file);
126 exit(1);
129 if ((differ_fp =
130 fopen((differ_file = tempnam(NULL, "diff")), "w")) == NULL) {
131 perror(differ_file);
132 exit(1);
136 static void
137 close_output_files(void)
139 (void) fclose(need_add_fp);
140 (void) fclose(need_rm_fp);
141 (void) fclose(differ_fp);
144 static void
145 print_file(char *file)
147 FILE *fp;
148 int count;
149 char buff[BUF_SIZE];
151 if ((fp = fopen(file, "r")) == NULL) {
152 perror(need_add_file);
155 while (count = fread(buff, sizeof (char), BUF_SIZE, fp))
156 (void) fwrite(buff, sizeof (char), count, stdout);
157 (void) fclose(fp);
160 static void
161 print_header(void)
163 (void) printf("%c %-30s %-20s %-4s %-5s %-5s %-5s %-2s %2s %2s %-9s\n",
164 'T', "File Name", "Reloc/Sym name", "perm", "owner", "group",
165 "inode", "lnk", "maj", "min", "package(s)");
166 (void) puts("-------------------------------------------------------"
167 "-----------------------------------------------------");
170 static void
171 print_results(void)
173 (void) puts("*******************************************************");
174 (void) puts("*");
175 (void) printf("* Entries found in %s, but not found in %s\n",
176 first_file_name, second_file_name);
177 (void) puts("*");
178 (void) puts("*******************************************************");
179 print_header();
180 print_file(need_add_file);
181 (void) puts("*******************************************************");
182 (void) puts("*");
183 (void) printf("* Entries found in %s, but not found in %s\n",
184 second_file_name, first_file_name);
185 (void) puts("*");
186 (void) puts("*******************************************************");
187 print_header();
188 print_file(need_rm_file);
189 (void) puts("*******************************************************");
190 (void) puts("*");
191 (void) printf("* Entries that differ between %s and %s\n",
192 first_file_name, second_file_name);
193 (void) puts("*");
194 (void) printf("* filea == %s\n", first_file_name);
195 (void) printf("* fileb == %s\n", second_file_name);
196 (void) puts("*");
197 (void) puts("*******************************************************");
198 (void) fputs("Unit ", stdout);
199 print_header();
200 print_file(differ_file);
203 static void
204 clean_up(void)
206 (void) unlink(need_add_file);
207 (void) unlink(need_rm_file);
208 (void) unlink(differ_file);
212 * elem_compare(a,b)
214 * Args:
215 * a - element a
216 * b - element b
217 * different_types -
218 * value = 0 -> comparing two elements of same
219 * type (eg: protodir elem vs. protodir elem).
220 * value != 0 -> comparing two elements of different type
221 * (eg: protodir elem vs. protolist elem).
223 * Returns:
224 * 0 - elements are identical
225 * >0 - elements differ
226 * check flags to see which fields differ.
228 static int
229 elem_compare(elem *a, elem *b, int different_types)
231 int res = 0;
232 elem *i, *j;
235 * if these are hard links to other files - those are the
236 * files that should be compared.
238 i = a->link_parent ? a->link_parent : a;
239 j = b->link_parent ? b->link_parent : b;
242 * We do not compare inodes - they always differ.
243 * We do not compare names because we assume that was
244 * checked before.
248 * Special rules for comparison:
250 * 1) if directory - ignore ref_cnt.
251 * 2) if sym_link - only check file_type & symlink
252 * 3) elem type of FILE_T, EDIT_T, & VOLATILE_T are equivilant when
253 * comparing a protodir entry to a protolist entry.
255 if (i->file_type != j->file_type) {
256 if (different_types) {
258 * Check to see if filetypes are FILE_T vs.
259 * EDIT_T/VOLATILE_T/LINK_T comparisons.
261 if ((i->file_type == FILE_T) &&
262 ((j->file_type == EDIT_T) ||
263 (j->file_type == VOLATILE_T) ||
264 (j->file_type == LINK_T))) {
265 /*EMPTY*/
266 } else if ((j->file_type == FILE_T) &&
267 ((i->file_type == EDIT_T) ||
268 (i->file_type == VOLATILE_T) ||
269 (i->file_type == LINK_T))) {
270 /*EMPTY*/
271 } else
272 res |= TYPE_F;
273 } else
274 res |= TYPE_F;
278 * if symlink - check the symlink value and then
279 * return. symlink is the only field of concern
280 * in SYMLINKS.
282 if (check_sym && ((res == 0) && (i->file_type == SYM_LINK_T))) {
283 if ((!i->symsrc) || (!j->symsrc))
284 res |= SYM_F;
285 else {
287 * if either symlink starts with a './' strip it off,
288 * its irrelevant.
290 if ((i->symsrc[0] == '.') && (i->symsrc[1] == '/'))
291 i->symsrc += 2;
292 if ((j->symsrc[0] == '.') && (j->symsrc[1] == '/'))
293 j->symsrc += 2;
295 if (strncmp(i->symsrc, j->symsrc, MAXNAME) != 0)
296 res |= SYM_F;
298 return (res);
301 if ((i->file_type != DIR_T) && check_link &&
302 (i->ref_cnt != j->ref_cnt))
303 res |= REF_F;
304 if (check_user && (strncmp(i->owner, j->owner, TYPESIZE) != 0))
305 res |= OWNER_F;
306 if (check_group && (strncmp(i->group, j->group, TYPESIZE) != 0))
307 res |= GROUP_F;
308 if (check_perm && (i->perm != j->perm))
309 res |= PERM_F;
310 if (check_majmin && ((i->major != j->major) || (i->minor != j->minor)))
311 res |= MAJMIN_F;
313 return (res);
316 static void
317 print_elem(FILE *fp, elem *e)
319 elem p;
320 pkg_list *l;
321 char maj[TYPESIZE], min[TYPESIZE];
322 char perm[12], ref_cnt[12];
325 * If this is a LINK to another file, then adopt
326 * the permissions of that file.
328 if (e->link_parent) {
329 p = *((elem *)e->link_parent);
330 (void) strcpy(p.name, e->name);
331 p.symsrc = e->symsrc;
332 p.file_type = e->file_type;
333 e = &p;
336 if (!check_majmin || e->major == -1) {
337 maj[0] = '-';
338 maj[1] = '\0';
339 } else {
340 (void) sprintf(maj, "%d", e->major);
343 if (!check_majmin || e->minor == -1) {
344 min[0] = '-';
345 min[1] = '\0';
346 } else {
347 (void) sprintf(min, "%d", e->minor);
350 if (!check_perm) {
351 perm[0] = '-';
352 perm[1] = '\0';
353 } else {
354 (void) snprintf(perm, sizeof (perm), "%o", e->perm);
357 if (!check_link) {
358 ref_cnt[0] = '-';
359 ref_cnt[1] = '\0';
360 } else {
361 (void) snprintf(ref_cnt, sizeof (ref_cnt), "%d", e->ref_cnt);
364 (void) fprintf(fp, "%c %-30s %-20s %4s %-5s %-5s %6d %2s %2s %2s ",
365 e->file_type, e->name,
366 check_sym && e->symsrc != NULL ? e->symsrc : "-", perm,
367 check_user ? e->owner : "-",
368 check_group ? e->group : "-",
369 e->inode, ref_cnt, maj, min);
371 * dump package list - if any.
373 if (!e->pkgs)
374 (void) fputs(" proto", fp);
376 for (l = e->pkgs; l; l = l->next) {
377 (void) fputc(' ', fp);
378 (void) fputs(l->pkg_name, fp);
380 (void) fputc('\n', fp);
384 * do_compare(a,b)
386 * Args:
387 * different_types - see elem_compare() for explanation.
389 static void
390 do_compare(elem *a, elem *b, int different_types)
392 int rc;
394 if ((rc = elem_compare(a, b, different_types)) != 0) {
395 (void) fputs("filea: ", differ_fp);
396 print_elem(differ_fp, a);
397 (void) fputs("fileb: ", differ_fp);
398 print_elem(differ_fp, b);
399 (void) fputs(" differ: ", differ_fp);
401 if (rc & SYM_F)
402 (void) fputs("symlink", differ_fp);
403 if (rc & PERM_F)
404 (void) fputs("perm ", differ_fp);
405 if (rc & REF_F)
406 (void) fputs("ref_cnt ", differ_fp);
407 if (rc & TYPE_F)
408 (void) fputs("file_type ", differ_fp);
409 if (rc & OWNER_F)
410 (void) fputs("owner ", differ_fp);
411 if (rc & GROUP_F)
412 (void) fputs("group ", differ_fp);
413 if (rc & MAJMIN_F)
414 (void) fputs("major/minor ", differ_fp);
415 (void) putc('\n', differ_fp);
416 (void) putc('\n', differ_fp);
420 static void
421 check_second_vs_first(int verbose)
423 int i;
424 elem *cur;
426 for (i = 0; i < second_list.num_of_buckets; i++) {
427 for (cur = second_list.list[i]; cur; cur = cur->next) {
428 if (!(cur->flag & VISITED_F)) {
429 if ((first_list.type != second_list.type) &&
430 find_elem(&exception_list, cur,
431 FOLLOW_LINK)) {
433 * this entry is filtered, we don't
434 * need to do any more processing.
436 if (verbose) {
437 (void) printf(
438 "Filtered: Need Deletion "
439 "of:\n\t");
440 print_elem(stdout, cur);
442 continue;
445 * It is possible for arch specific files to be
446 * found in a protodir but listed as arch
447 * independent in a protolist file. If this is
448 * a protodir vs. a protolist we will make
449 * that check.
451 if ((second_list.type == PROTODIR_LIST) &&
452 (cur->arch != P_ISA) &&
453 (first_list.type != PROTODIR_LIST)) {
455 * do a lookup for same file, but as
456 * type ISA.
458 elem *e;
460 e = find_elem_isa(&first_list, cur,
461 NO_FOLLOW_LINK);
462 if (e) {
463 do_compare(e, cur,
464 first_list.type -
465 second_list.type);
466 continue;
470 print_elem(need_rm_fp, cur);
476 static void
477 check_first_vs_second(int verbose)
479 int i;
480 elem *e;
481 elem *cur;
483 for (i = 0; i < first_list.num_of_buckets; i++) {
484 for (cur = first_list.list[i]; cur; cur = cur->next) {
485 if ((first_list.type != second_list.type) &&
486 find_elem(&exception_list, cur, FOLLOW_LINK)) {
488 * this entry is filtered, we don't need to do
489 * any more processing.
491 if (verbose) {
492 (void) printf("Filtered: Need "
493 "Addition of:\n\t");
494 print_elem(stdout, cur);
496 continue;
500 * Search package database for file.
502 e = find_elem(&second_list, cur, NO_FOLLOW_LINK);
505 * It is possible for arch specific files to be found
506 * in a protodir but listed as arch independent in a
507 * protolist file. If this is a protodir vs. a
508 * protolist we will make that check.
510 if (!e && (first_list.type == PROTODIR_LIST) &&
511 (cur->arch != P_ISA) &&
512 (second_list.type != PROTODIR_LIST)) {
514 * do a lookup for same file, but as type ISA.
516 e = find_elem_isa(&second_list, cur,
517 NO_FOLLOW_LINK);
520 if (!e && (first_list.type != PROTODIR_LIST) &&
521 (cur->arch == P_ISA) &&
522 (second_list.type == PROTODIR_LIST)) {
524 * do a lookup for same file, but as any
525 * type but ISA
527 e = find_elem_mach(&second_list, cur,
528 NO_FOLLOW_LINK);
531 if (e == NULL)
532 print_elem(need_add_fp, cur);
533 else {
534 do_compare(cur, e,
535 first_list.type - second_list.type);
536 e->flag |= VISITED_F;
542 static int
543 read_in_file(const char *file_name, elem_list *list)
545 struct stat st_buf;
546 int count = 0;
548 if (stat(file_name, &st_buf) == 0) {
549 if (S_ISREG(st_buf.st_mode)) {
550 if (verbose) {
551 (void) printf("file(%s): trying to process "
552 "as protolist...\n", file_name);
554 count = read_in_protolist(file_name, list, verbose);
555 } else if (S_ISDIR(st_buf.st_mode)) {
556 if (verbose)
557 (void) printf("directory(%s): trying to "
558 "process as protodir...\n", file_name);
559 count = read_in_protodir(file_name, list, verbose);
560 } else {
561 (void) fprintf(stderr,
562 "%s not a file or a directory.\n", file_name);
563 usage();
564 exit(1);
566 } else {
567 perror(file_name);
568 usage();
569 exit(1);
572 return (count);
575 /* ARGSUSED */
576 static int
577 set_values(const char *fname, const struct stat *sbp, int otype,
578 struct FTW *ftw)
580 elem *ep;
581 uid_t uid;
582 gid_t gid;
583 elem keyelem;
584 mode_t perm;
586 if (fname[0] == '\0' || fname[1] == '\0' || fname[2] == '\0')
587 return (0);
588 /* skip leading "./" */
589 fname += 2;
590 switch (otype) {
591 case FTW_F:
592 case FTW_D:
593 case FTW_DP:
594 if (strlcpy(keyelem.name, fname, sizeof (keyelem.name)) >=
595 sizeof (keyelem.name)) {
596 (void) fprintf(stderr, "%s: %s: name too long\n",
597 myname, fname);
598 return (1);
600 keyelem.arch = P_ISA;
601 ep = find_elem(&first_list, &keyelem, NO_FOLLOW_LINK);
602 if (ep == NULL) {
603 ep = find_elem_mach(&first_list, &keyelem,
604 NO_FOLLOW_LINK);
607 * Do nothing if this is a hard or symbolic link,
608 * since links don't have this information.
610 * Assume it's a file on the exception list if it's
611 * not found in the packaging. Those are root:bin 755.
613 if (ep != NULL &&
614 (ep->file_type == SYM_LINK_T || ep->file_type == LINK_T)) {
615 return (0);
617 if (!set_group) {
618 gid = -1;
619 } else if (ep == NULL) {
620 gid = 0;
621 } else if ((gid = stdfind(ep->group, groupnames)) == -1) {
622 (void) fprintf(stderr, "%s: %s: group '%s' unknown\n",
623 myname, fname, ep->group);
624 return (1);
626 if (!set_user) {
627 uid = -1;
628 } else if (ep == NULL) {
629 uid = 2;
630 } else if ((uid = stdfind(ep->owner, usernames)) == -1) {
631 (void) fprintf(stderr, "%s: %s: user '%s' unknown\n",
632 myname, fname, ep->owner);
633 return (1);
635 if ((set_group && gid != -1 && gid != sbp->st_gid) ||
636 (set_user && uid != -1 && uid != sbp->st_uid)) {
637 if (verbose) {
638 const char *owner, *group;
640 owner = ep == NULL ? "root" : ep->owner;
641 group = ep == NULL ? "bin" : ep->group;
642 if (set_group && set_user) {
643 (void) printf("chown %s:%s %s\n",
644 owner, group, fname);
645 } else if (set_user) {
646 (void) printf("chown %s %s\n", owner,
647 fname);
648 } else {
649 (void) printf("chgrp %s %s\n", group,
650 fname);
653 if (lchown(fname, uid, gid) == -1) {
654 perror(fname);
655 return (1);
658 perm = ep == NULL ? 0755 : ep->perm;
659 if (set_perm && ((perm ^ sbp->st_mode) & ~S_IFMT) != 0) {
660 if (verbose)
661 (void) printf("chmod %lo %s\n", perm, fname);
662 if (chmod(fname, perm) == -1) {
663 perror(fname);
664 return (1);
667 return (0);
668 case FTW_DNR:
669 case FTW_NS:
670 (void) fprintf(stderr, "%s: %s: permission denied\n",
671 myname, fname);
672 return (1);
673 case FTW_SL:
674 case FTW_SLN:
675 return (0);
676 default:
677 return (1);
682 main(int argc, char **argv)
684 int errflg = 0;
685 int i, c;
686 int list_filtered_exceptions = 0;
687 int n_proto_refs = 0;
688 int n_exception_files = 0;
689 char *proto_refs[MAX_PROTO_REFS];
690 char *exception_files[MAX_EXCEPTION_FILES];
691 struct stat st_buf;
693 if ((myname = argv[0]) == NULL)
694 myname = "protocmp";
696 while ((c = getopt(argc, argv, "gupGUPlmsLe:vd:")) != EOF) {
697 switch (c) {
698 case 's':
699 check_sym = 0;
700 break;
701 case 'm':
702 check_majmin = 0;
703 break;
704 case 'g':
705 check_group = 0;
706 break;
707 case 'G':
708 set_group = 1;
709 break;
710 case 'u':
711 check_user = 0;
712 break;
713 case 'U':
714 set_user = 1;
715 break;
716 case 'l':
717 check_link = 0;
718 break;
719 case 'p':
720 check_perm = 0;
721 break;
722 case 'P':
723 set_perm = 1;
724 break;
725 case 'e':
726 if (n_exception_files >= MAX_EXCEPTION_FILES) {
727 errflg++;
728 (void) fprintf(stderr,
729 "Only %d exception files supported\n",
730 MAX_EXCEPTION_FILES);
731 } else {
732 exception_files[n_exception_files++] = optarg;
734 break;
735 case 'L':
736 list_filtered_exceptions++;
737 break;
738 case 'v':
739 verbose++;
740 break;
741 case 'd':
742 if (n_proto_refs >= MAX_PROTO_REFS) {
743 errflg++;
744 (void) fprintf(stderr,
745 "Only %d proto references supported\n",
746 MAX_PROTO_REFS);
747 } else {
748 proto_refs[n_proto_refs++] = optarg;
750 break;
751 case '?':
752 default:
753 errflg++;
754 break;
758 if (argc == optind || n_proto_refs == 0) {
759 usage();
760 exit(1);
763 if (set_group || set_user || set_perm) {
764 if (optind != argc - 1) {
765 usage();
766 exit(1);
768 if (stat(argv[optind], &st_buf) == -1) {
769 perror(argv[optind]);
770 exit(1);
772 if (!S_ISDIR(st_buf.st_mode)) {
773 (void) fprintf(stderr, "%s: %s: not a directory\n",
774 myname, argv[optind]);
775 exit(1);
779 init_list(&first_list, HASH_SIZE);
780 init_list(&second_list, HASH_SIZE);
781 init_list(&exception_list, HASH_SIZE);
783 for (i = 0; i < n_exception_files; i++) {
784 (void) read_in_exceptions(exception_files[i], verbose);
787 for (i = 0; i < n_proto_refs; i++) {
788 first_file_name = proto_refs[i];
789 (void) read_in_file(first_file_name, &first_list);
792 if (set_group || set_user || set_perm) {
793 if (chdir(argv[optind]) == -1) {
794 perror(argv[optind]);
795 exit(1);
797 i = nftw(".", set_values, MAX_DEPTH, FTW_PHYS|FTW_DEPTH);
798 if (i == -1) {
799 perror("nftw");
800 i = 1;
802 exit(i);
805 for (i = optind; i < argc; i++) {
806 second_file_name = argv[i];
807 (void) read_in_file(second_file_name, &second_list);
810 open_output_files();
812 if (verbose)
813 (void) puts("comparing build to packages...");
815 check_first_vs_second(list_filtered_exceptions);
817 if (verbose)
818 (void) puts("checking over packages...");
819 check_second_vs_first(list_filtered_exceptions);
821 close_output_files();
823 print_results();
825 clean_up();
827 return (0);