1 /*
2 * CDDL HEADER START
3 *
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.
8 *
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.
13 *
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]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
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>
38
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"
45
46 #define MAX_PROTO_REFS 5
47 #define MAX_EXCEPTION_FILES 5
48 #define MAX_DEPTH 50
49
50 /*
51 * default flag values
52 */
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;
62
63 static elem_list first_list;
64 static char *first_file_name;
65
66 static elem_list second_list;
67 static char *second_file_name;
68
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;
75
76 static char *myname;
77
78 /*
79 * default flag values
80 */
81 static int verbose = 0;
82
83 static void
usage(void)84 usage(void)
85 {
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);
111 }
112
113
114 static void
open_output_files(void)115 open_output_files(void)
116 {
117 if ((need_add_fp =
118 fopen((need_add_file = tempnam(NULL, "add")), "w")) == NULL) {
119 perror(need_add_file);
120 exit(1);
121 }
122
123 if ((need_rm_fp =
124 fopen((need_rm_file = tempnam(NULL, "rm")), "w")) == NULL) {
125 perror(need_rm_file);
126 exit(1);
127 }
128
129 if ((differ_fp =
130 fopen((differ_file = tempnam(NULL, "diff")), "w")) == NULL) {
131 perror(differ_file);
132 exit(1);
133 }
134 }
135
136 static void
close_output_files(void)137 close_output_files(void)
138 {
139 (void) fclose(need_add_fp);
140 (void) fclose(need_rm_fp);
141 (void) fclose(differ_fp);
142 }
143
144 static void
print_file(char * file)145 print_file(char *file)
146 {
147 FILE *fp;
148 int count;
149 char buff[BUF_SIZE];
150
151 if ((fp = fopen(file, "r")) == NULL) {
152 perror(need_add_file);
153 }
154
155 while (count = fread(buff, sizeof (char), BUF_SIZE, fp))
156 (void) fwrite(buff, sizeof (char), count, stdout);
157 (void) fclose(fp);
158 }
159
160 static void
print_header(void)161 print_header(void)
162 {
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 "-----------------------------------------------------");
168 }
169
170 static void
print_results(void)171 print_results(void)
172 {
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);
201 }
202
203 static void
clean_up(void)204 clean_up(void)
205 {
206 (void) unlink(need_add_file);
207 (void) unlink(need_rm_file);
208 (void) unlink(differ_file);
209 }
210
211 /*
212 * elem_compare(a,b)
213 *
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).
222 *
223 * Returns:
224 * 0 - elements are identical
225 * >0 - elements differ
226 * check flags to see which fields differ.
227 */
228 static int
elem_compare(elem * a,elem * b,int different_types)229 elem_compare(elem *a, elem *b, int different_types)
230 {
231 int res = 0;
232 elem *i, *j;
233
234 /*
235 * if these are hard links to other files - those are the
236 * files that should be compared.
237 */
238 i = a->link_parent ? a->link_parent : a;
239 j = b->link_parent ? b->link_parent : b;
240
241 /*
242 * We do not compare inodes - they always differ.
243 * We do not compare names because we assume that was
244 * checked before.
245 */
246
247 /*
248 * Special rules for comparison:
249 *
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.
254 */
255 if (i->file_type != j->file_type) {
256 if (different_types) {
257 /*
258 * Check to see if filetypes are FILE_T vs.
259 * EDIT_T/VOLATILE_T/LINK_T comparisons.
260 */
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;
275 }
276
277 /*
278 * if symlink - check the symlink value and then
279 * return. symlink is the only field of concern
280 * in SYMLINKS.
281 */
282 if (check_sym && ((res == 0) && (i->file_type == SYM_LINK_T))) {
283 if ((!i->symsrc) || (!j->symsrc))
284 res |= SYM_F;
285 else {
286 /*
287 * if either symlink starts with a './' strip it off,
288 * its irrelevant.
289 */
290 if ((i->symsrc[0] == '.') && (i->symsrc[1] == '/'))
291 i->symsrc += 2;
292 if ((j->symsrc[0] == '.') && (j->symsrc[1] == '/'))
293 j->symsrc += 2;
294
295 if (strncmp(i->symsrc, j->symsrc, MAXNAME) != 0)
296 res |= SYM_F;
297 }
298 return (res);
299 }
300
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;
312
313 return (res);
314 }
315
316 static void
print_elem(FILE * fp,elem * e)317 print_elem(FILE *fp, elem *e)
318 {
319 elem p;
320 pkg_list *l;
321 char maj[TYPESIZE], min[TYPESIZE];
322 char perm[12], ref_cnt[12];
323
324 /*
325 * If this is a LINK to another file, then adopt
326 * the permissions of that file.
327 */
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;
334 }
335
336 if (!check_majmin || e->major == -1) {
337 maj[0] = '-';
338 maj[1] = '\0';
339 } else {
340 (void) sprintf(maj, "%d", e->major);
341 }
342
343 if (!check_majmin || e->minor == -1) {
344 min[0] = '-';
345 min[1] = '\0';
346 } else {
347 (void) sprintf(min, "%d", e->minor);
348 }
349
350 if (!check_perm) {
351 perm[0] = '-';
352 perm[1] = '\0';
353 } else {
354 (void) snprintf(perm, sizeof (perm), "%o", e->perm);
355 }
356
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);
362 }
363
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);
370 /*
371 * dump package list - if any.
372 */
373 if (!e->pkgs)
374 (void) fputs(" proto", fp);
375
376 for (l = e->pkgs; l; l = l->next) {
377 (void) fputc(' ', fp);
378 (void) fputs(l->pkg_name, fp);
379 }
380 (void) fputc('\n', fp);
381 }
382
383 /*
384 * do_compare(a,b)
385 *
386 * Args:
387 * different_types - see elem_compare() for explanation.
388 */
389 static void
do_compare(elem * a,elem * b,int different_types)390 do_compare(elem *a, elem *b, int different_types)
391 {
392 int rc;
393
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);
400
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);
417 }
418 }
419
420 static void
check_second_vs_first(int verbose)421 check_second_vs_first(int verbose)
422 {
423 int i;
424 elem *cur;
425
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)) {
432 /*
433 * this entry is filtered, we don't
434 * need to do any more processing.
435 */
436 if (verbose) {
437 (void) printf(
438 "Filtered: Need Deletion "
439 "of:\n\t");
440 print_elem(stdout, cur);
441 }
442 continue;
443 }
444 /*
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.
450 */
451 if ((second_list.type == PROTODIR_LIST) &&
452 (cur->arch != P_ISA) &&
453 (first_list.type != PROTODIR_LIST)) {
454 /*
455 * do a lookup for same file, but as
456 * type ISA.
457 */
458 elem *e;
459
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;
467 }
468 }
469
470 print_elem(need_rm_fp, cur);
471 }
472 }
473 }
474 }
475
476 static void
check_first_vs_second(int verbose)477 check_first_vs_second(int verbose)
478 {
479 int i;
480 elem *e;
481 elem *cur;
482
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)) {
487 /*
488 * this entry is filtered, we don't need to do
489 * any more processing.
490 */
491 if (verbose) {
492 (void) printf("Filtered: Need "
493 "Addition of:\n\t");
494 print_elem(stdout, cur);
495 }
496 continue;
497 }
498
499 /*
500 * Search package database for file.
501 */
502 e = find_elem(&second_list, cur, NO_FOLLOW_LINK);
503
504 /*
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.
509 */
510 if (!e && (first_list.type == PROTODIR_LIST) &&
511 (cur->arch != P_ISA) &&
512 (second_list.type != PROTODIR_LIST)) {
513 /*
514 * do a lookup for same file, but as type ISA.
515 */
516 e = find_elem_isa(&second_list, cur,
517 NO_FOLLOW_LINK);
518 }
519
520 if (!e && (first_list.type != PROTODIR_LIST) &&
521 (cur->arch == P_ISA) &&
522 (second_list.type == PROTODIR_LIST)) {
523 /*
524 * do a lookup for same file, but as any
525 * type but ISA
526 */
527 e = find_elem_mach(&second_list, cur,
528 NO_FOLLOW_LINK);
529 }
530
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;
537 }
538 }
539 }
540 }
541
542 static int
read_in_file(const char * file_name,elem_list * list)543 read_in_file(const char *file_name, elem_list *list)
544 {
545 struct stat st_buf;
546 int count = 0;
547
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);
553 }
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);
565 }
566 } else {
567 perror(file_name);
568 usage();
569 exit(1);
570 }
571
572 return (count);
573 }
574
575 /* ARGSUSED */
576 static int
set_values(const char * fname,const struct stat * sbp,int otype,struct FTW * ftw)577 set_values(const char *fname, const struct stat *sbp, int otype,
578 struct FTW *ftw)
579 {
580 elem *ep;
581 uid_t uid;
582 gid_t gid;
583 elem keyelem;
584 mode_t perm;
585
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);
599 }
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);
605 }
606 /*
607 * Do nothing if this is a hard or symbolic link,
608 * since links don't have this information.
609 *
610 * Assume it's a file on the exception list if it's
611 * not found in the packaging. Those are root:bin 755.
612 */
613 if (ep != NULL &&
614 (ep->file_type == SYM_LINK_T || ep->file_type == LINK_T)) {
615 return (0);
616 }
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);
625 }
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);
634 }
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;
639
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);
651 }
652 }
653 if (lchown(fname, uid, gid) == -1) {
654 perror(fname);
655 return (1);
656 }
657 }
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);
665 }
666 }
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);
678 }
679 }
680
681 int
main(int argc,char ** argv)682 main(int argc, char **argv)
683 {
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;
692
693 if ((myname = argv[0]) == NULL)
694 myname = "protocmp";
695
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;
733 }
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;
749 }
750 break;
751 case '?':
752 default:
753 errflg++;
754 break;
755 }
756 }
757
758 if (argc == optind || n_proto_refs == 0) {
759 usage();
760 exit(1);
761 }
762
763 if (set_group || set_user || set_perm) {
764 if (optind != argc - 1) {
765 usage();
766 exit(1);
767 }
768 if (stat(argv[optind], &st_buf) == -1) {
769 perror(argv[optind]);
770 exit(1);
771 }
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);
776 }
777 }
778
779 init_list(&first_list, HASH_SIZE);
780 init_list(&second_list, HASH_SIZE);
781 init_list(&exception_list, HASH_SIZE);
782
783 for (i = 0; i < n_exception_files; i++) {
784 (void) read_in_exceptions(exception_files[i], verbose);
785 }
786
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);
790 }
791
792 if (set_group || set_user || set_perm) {
793 if (chdir(argv[optind]) == -1) {
794 perror(argv[optind]);
795 exit(1);
796 }
797 i = nftw(".", set_values, MAX_DEPTH, FTW_PHYS|FTW_DEPTH);
798 if (i == -1) {
799 perror("nftw");
800 i = 1;
801 }
802 exit(i);
803 }
804
805 for (i = optind; i < argc; i++) {
806 second_file_name = argv[i];
807 (void) read_in_file(second_file_name, &second_list);
808 }
809
810 open_output_files();
811
812 if (verbose)
813 (void) puts("comparing build to packages...");
814
815 check_first_vs_second(list_filtered_exceptions);
816
817 if (verbose)
818 (void) puts("checking over packages...");
819 check_second_vs_first(list_filtered_exceptions);
820
821 close_output_files();
822
823 print_results();
824
825 clean_up();
826
827 return (0);
828 }
829