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
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]
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
31 static int compare_manifests(FILE *rulesfile
, char *control
, char *test
,
32 boolean_t prog_fmt
, uint_t flags
);
33 static void extract_fname_ftype(char *line
, char *fname
, char *type
);
34 static int report_add(char *fname
, char *type
);
35 static int report_delete(char *fname
, char *type
);
36 static int evaluate_differences(char *control_line
, char *test_line
,
37 boolean_t prog_fmt
, int flags
);
38 static void report_error(char *fname
, char *type
, char *ctrl_val
,
39 char *test_val
, boolean_t prog_fmt
);
40 static int read_manifest_line(FILE *fd
, char *buf
, int buf_size
, int start_pos
,
41 char **line
, char *fname
);
42 static void parse_line(char *line
, char *fname
, char *type
, char *size
,
43 char *mode
, char *acl
, char *mtime
, char *uid
, char *gid
, char *contents
,
44 char *devnode
, char *dest
);
45 static void init_default_flags(uint_t
*flags
);
46 static void get_token(char *line
, int *curr_pos
, int line_len
, char *buf
,
50 bart_compare(int argc
, char **argv
)
52 char *control_fname
, *test_fname
;
54 FILE *rules_fd
= NULL
;
56 boolean_t prog_fmt
= B_FALSE
;
58 init_default_flags(&glob_flags
);
60 while ((c
= getopt(argc
, argv
, "pr:i:")) != EOF
) {
70 if (strcmp(optarg
, "-") == 0)
73 rules_fd
= fopen(optarg
, "r");
74 if (rules_fd
== NULL
) {
81 process_glob_ignores(optarg
, &glob_flags
);
90 /* Make sure we have the right number of args */
91 if ((optind
+ 2) != argc
)
94 control_fname
= argv
[0];
96 /* At this point, the filenames are sane, so do the comparison */
97 return (compare_manifests(rules_fd
, control_fname
, test_fname
,
98 prog_fmt
, glob_flags
));
102 compare_manifests(FILE *rulesfile
, char *control
, char *test
,
103 boolean_t prog_fmt
, uint_t flags
)
105 FILE *control_fd
, *test_fd
;
106 char *control_line
, *test_line
, control_buf
[BUF_SIZE
],
107 test_buf
[BUF_SIZE
], control_fname
[PATH_MAX
],
108 control_type
[TYPE_SIZE
], test_fname
[PATH_MAX
],
109 test_type
[TYPE_SIZE
];
110 int control_pos
, test_pos
, ret
, fname_cmp
, return_status
;
112 return_status
= EXIT
;
114 return_status
= read_rules(rulesfile
, "", flags
, 0);
116 control_fd
= fopen(control
, "r");
117 if (control_fd
== NULL
) {
122 test_fd
= fopen(test
, "r");
123 if (test_fd
== NULL
) {
128 control_pos
= read_manifest_line(control_fd
, control_buf
,
129 BUF_SIZE
, 0, &control_line
, control
);
130 test_pos
= read_manifest_line(test_fd
, test_buf
, BUF_SIZE
, 0,
133 while ((control_pos
!= -1) && (test_pos
!= -1)) {
134 ret
= strcmp(control_line
, test_line
);
136 /* Lines compare OK, just read the next lines.... */
137 control_pos
= read_manifest_line(control_fd
,
138 control_buf
, BUF_SIZE
, control_pos
, &control_line
,
140 test_pos
= read_manifest_line(test_fd
, test_buf
,
141 BUF_SIZE
, test_pos
, &test_line
, test
);
146 * Something didn't compare properly.
148 extract_fname_ftype(control_line
, control_fname
, control_type
);
149 extract_fname_ftype(test_line
, test_fname
, test_type
);
150 fname_cmp
= strcmp(control_fname
, test_fname
);
152 if (fname_cmp
== 0) {
154 * Filenames were the same, see what was
155 * different and continue.
157 if (evaluate_differences(control_line
, test_line
,
158 prog_fmt
, flags
) != 0)
159 return_status
= WARNING_EXIT
;
161 control_pos
= read_manifest_line(control_fd
,
162 control_buf
, BUF_SIZE
, control_pos
, &control_line
,
164 test_pos
= read_manifest_line(test_fd
, test_buf
,
165 BUF_SIZE
, test_pos
, &test_line
, test
);
166 } else if (fname_cmp
> 0) {
167 /* Filenames were different, a files was ADDED */
168 if (report_add(test_fname
, test_type
)) {
169 report_error(test_fname
, ADD_KEYWORD
, NULL
,
171 return_status
= WARNING_EXIT
;
173 test_pos
= read_manifest_line(test_fd
, test_buf
,
174 BUF_SIZE
, test_pos
, &test_line
, test
);
175 } else if (fname_cmp
< 0) {
176 /* Filenames were different, a files was DELETED */
177 if (report_delete(control_fname
, control_type
)) {
178 report_error(control_fname
, DELETE_KEYWORD
,
179 NULL
, NULL
, prog_fmt
);
180 return_status
= WARNING_EXIT
;
182 control_pos
= read_manifest_line(control_fd
,
183 control_buf
, BUF_SIZE
, control_pos
, &control_line
,
189 * Entering this while loop means files were DELETED from the test
192 while (control_pos
!= -1) {
193 (void) sscanf(control_line
, "%1023s", control_fname
);
194 if (report_delete(control_fname
, control_type
)) {
195 report_error(control_fname
, DELETE_KEYWORD
, NULL
,
197 return_status
= WARNING_EXIT
;
199 control_pos
= read_manifest_line(control_fd
, control_buf
,
200 BUF_SIZE
, control_pos
, &control_line
, control
);
204 * Entering this while loop means files were ADDED to the test
207 while (test_pos
!= -1) {
208 (void) sscanf(test_line
, "%1023s", test_fname
);
209 if (report_add(test_fname
, test_type
)) {
210 report_error(test_fname
, ADD_KEYWORD
, NULL
,
212 return_status
= WARNING_EXIT
;
214 test_pos
= read_manifest_line(test_fd
, test_buf
,
215 BUF_SIZE
, test_pos
, &test_line
, test
);
218 (void) fclose(control_fd
);
219 (void) fclose(test_fd
);
221 /* For programmatic mode, add a newline for cosmetic reasons */
222 if (prog_fmt
&& (return_status
!= 0))
225 return (return_status
);
229 parse_line(char *line
, char *fname
, char *type
, char *size
, char *mode
,
230 char *acl
, char *mtime
, char *uid
, char *gid
, char *contents
, char *devnode
,
235 line_len
= strlen(line
);
238 get_token(line
, &pos
, line_len
, fname
, PATH_MAX
);
239 get_token(line
, &pos
, line_len
, type
, TYPE_SIZE
);
240 get_token(line
, &pos
, line_len
, size
, MISC_SIZE
);
241 get_token(line
, &pos
, line_len
, mode
, MISC_SIZE
);
242 get_token(line
, &pos
, line_len
, acl
, ACL_SIZE
);
243 get_token(line
, &pos
, line_len
, mtime
, MISC_SIZE
);
244 get_token(line
, &pos
, line_len
, uid
, MISC_SIZE
);
245 get_token(line
, &pos
, line_len
, gid
, MISC_SIZE
);
247 /* Reset these fields... */
253 /* Handle filetypes which have a last field..... */
255 get_token(line
, &pos
, line_len
, contents
, PATH_MAX
);
256 else if ((type
[0] == 'B') || (type
[0] == 'C'))
257 get_token(line
, &pos
, line_len
, devnode
, PATH_MAX
);
258 else if (type
[0] == 'L')
259 get_token(line
, &pos
, line_len
, dest
, PATH_MAX
);
263 get_token(char *line
, int *curr_pos
, int line_len
, char *buf
, int buf_size
)
267 while (isspace(line
[*curr_pos
]) && (*curr_pos
< line_len
))
270 while (!isspace(line
[*curr_pos
]) &&
271 (*curr_pos
< line_len
) && (cnt
< (buf_size
-1))) {
272 buf
[cnt
] = line
[*curr_pos
];
280 * Utility function: extract fname and type from this line
283 extract_fname_ftype(char *line
, char *fname
, char *type
)
288 line_len
= strlen(line
);
290 get_token(line
, &pos
, line_len
, fname
, PATH_MAX
);
291 get_token(line
, &pos
, line_len
, type
, TYPE_SIZE
);
295 * Utility function: tells us whether or not this addition should be reported
297 * Returns 0 if the discrepancy is ignored, non-zero if the discrepancy is
301 report_add(char *fname
, char *type
)
303 struct rule
*rule_ptr
;
305 rule_ptr
= check_rules(fname
, type
[0]);
306 if ((rule_ptr
!= NULL
) && (rule_ptr
->attr_list
& ATTR_ADD
))
313 * Utility function: tells us whether or not this deletion should be reported
315 * Returns 0 if the discrepancy is ignored, non-zero if the discrepancy is
319 report_delete(char *fname
, char *type
)
321 struct rule
*rule_ptr
;
323 rule_ptr
= check_rules(fname
, type
[0]);
325 if ((rule_ptr
!= NULL
) && (rule_ptr
->attr_list
& ATTR_DELETE
))
332 * This function takes in the two entries, which have been flagged as
333 * different, breaks them up and reports discrepancies. Note, discrepancies
334 * are affected by the 'CHECK' and 'IGNORE' stanzas which may apply to
337 * Returns the number of discrepancies reported.
340 evaluate_differences(char *control_line
, char *test_line
,
341 boolean_t prog_fmt
, int flags
)
343 char ctrl_fname
[PATH_MAX
], test_fname
[PATH_MAX
],
344 ctrl_type
[TYPE_SIZE
], test_type
[TYPE_SIZE
],
345 ctrl_size
[MISC_SIZE
], ctrl_mode
[MISC_SIZE
],
346 ctrl_acl
[ACL_SIZE
], ctrl_mtime
[MISC_SIZE
],
347 ctrl_uid
[MISC_SIZE
], ctrl_gid
[MISC_SIZE
],
348 ctrl_dest
[PATH_MAX
], ctrl_contents
[PATH_MAX
],
349 ctrl_devnode
[PATH_MAX
], test_size
[MISC_SIZE
],
350 test_mode
[MISC_SIZE
], test_acl
[ACL_SIZE
],
351 test_mtime
[MISC_SIZE
], test_uid
[MISC_SIZE
],
352 test_gid
[MISC_SIZE
], test_dest
[PATH_MAX
],
353 test_contents
[PATH_MAX
], test_devnode
[PATH_MAX
],
356 struct rule
*rule_ptr
;
360 parse_line(control_line
, ctrl_fname
, ctrl_type
, ctrl_size
, ctrl_mode
,
361 ctrl_acl
, ctrl_mtime
, ctrl_uid
, ctrl_gid
, ctrl_contents
,
362 ctrl_devnode
, ctrl_dest
);
365 * Now we know the fname and type, let's get the rule that matches this
366 * manifest entry. If there is a match, make sure to setup the
367 * correct reporting flags.
369 rule_ptr
= check_rules(ctrl_fname
, ctrl_type
[0]);
370 if (rule_ptr
!= NULL
)
371 flags
= rule_ptr
->attr_list
;
373 parse_line(test_line
, test_fname
, test_type
, test_size
, test_mode
,
374 test_acl
, test_mtime
, test_uid
, test_gid
, test_contents
,
375 test_devnode
, test_dest
);
378 * Report the errors based upon which keywords have been set by
381 if ((flags
& ATTR_TYPE
) && (ctrl_type
[0] != test_type
[0])) {
382 report_error(ctrl_fname
, TYPE_KEYWORD
, ctrl_type
,
383 test_type
, prog_fmt
);
387 if ((flags
& ATTR_SIZE
) && (strcmp(ctrl_size
, test_size
) != 0)) {
388 report_error(ctrl_fname
, SIZE_KEYWORD
, ctrl_size
,
389 test_size
, prog_fmt
);
393 if ((flags
& ATTR_MODE
) && (strcmp(ctrl_mode
, test_mode
) != 0)) {
394 report_error(ctrl_fname
, MODE_KEYWORD
, ctrl_mode
,
395 test_mode
, prog_fmt
);
399 if ((flags
& ATTR_ACL
) && (strcmp(ctrl_acl
, test_acl
) != 0)) {
400 report_error(ctrl_fname
, ACL_KEYWORD
, ctrl_acl
,
405 if ((flags
& ATTR_MTIME
) && (ctrl_type
[0] == test_type
[0])) {
406 if (strcmp(ctrl_mtime
, test_mtime
) != 0) {
407 switch (ctrl_type
[0]) {
419 report_error(ctrl_fname
, tag
, ctrl_mtime
,
420 test_mtime
, prog_fmt
);
425 if ((ctrl_type
[0] == 'F') && (flags
& ATTR_MTIME
) &&
426 (strcmp(ctrl_mtime
, test_mtime
) != 0)) {
427 report_error(ctrl_fname
, MTIME_KEYWORD
, ctrl_mtime
, test_mtime
,
432 if ((ctrl_type
[0] == 'D') && (flags
& ATTR_DIRMTIME
) &&
433 (strcmp(ctrl_mtime
, test_mtime
) != 0)) {
434 report_error(ctrl_fname
, DIRMTIME_KEYWORD
, ctrl_mtime
,
435 test_mtime
, prog_fmt
);
439 if ((ctrl_type
[0] == 'L') && (flags
& ATTR_LNMTIME
) &&
440 (strcmp(ctrl_mtime
, test_mtime
) != 0)) {
441 report_error(ctrl_fname
, LNMTIME_KEYWORD
, ctrl_mtime
,
442 test_mtime
, prog_fmt
);
445 } else if ((flags
& ATTR_MTIME
) &&
446 (strcmp(ctrl_mtime
, test_mtime
) != 0)) {
447 report_error(ctrl_fname
, MTIME_KEYWORD
, ctrl_mtime
,
448 test_mtime
, prog_fmt
);
452 if ((flags
& ATTR_UID
) && (strcmp(ctrl_uid
, test_uid
) != 0)) {
453 report_error(ctrl_fname
, UID_KEYWORD
, ctrl_uid
,
458 if ((flags
& ATTR_GID
) && (strcmp(ctrl_gid
, test_gid
) != 0)) {
459 report_error(ctrl_fname
, GID_KEYWORD
, ctrl_gid
,
464 if ((flags
& ATTR_DEVNODE
) &&
465 (strcmp(ctrl_devnode
, test_devnode
) != 0)) {
466 report_error(ctrl_fname
, DEVNODE_KEYWORD
, ctrl_devnode
,
467 test_devnode
, prog_fmt
);
471 if ((flags
& ATTR_DEST
) && (strcmp(ctrl_dest
, test_dest
) != 0)) {
472 report_error(ctrl_fname
, DEST_KEYWORD
, ctrl_dest
,
473 test_dest
, prog_fmt
);
477 if ((flags
& ATTR_CONTENTS
) &&
478 (strcmp(ctrl_contents
, test_contents
)) != 0) {
479 report_error(ctrl_fname
, CONTENTS_KEYWORD
, ctrl_contents
,
480 test_contents
, prog_fmt
);
488 * Function responsible for reporting errors.
491 report_error(char *fname
, char *type
, char *ctrl_val
, char *test_val
,
494 static char last_fname
[PATH_MAX
] = "";
498 if (strcmp(fname
, last_fname
) != 0) {
499 (void) printf("%s:\n", fname
);
500 (void) strlcpy(last_fname
, fname
, sizeof (last_fname
));
503 if (strcmp(type
, ADD_KEYWORD
) == 0 ||
504 strcmp(type
, DELETE_KEYWORD
) == 0)
505 (void) printf(" %s\n", type
);
507 (void) printf(" %s control:%s test:%s\n", type
,
510 /* Programmatic mode */
511 if (strcmp(fname
, last_fname
) != 0) {
512 /* Ensure a line is not printed for the initial case */
513 if (strlen(last_fname
) != 0)
515 (void) strlcpy(last_fname
, fname
, sizeof (last_fname
));
516 (void) printf("%s ", fname
);
519 (void) printf("%s ", type
);
520 if (strcmp(type
, ADD_KEYWORD
) != 0 &&
521 strcmp(type
, DELETE_KEYWORD
) != 0) {
522 (void) printf("%s ", ctrl_val
);
523 (void) printf("%s ", test_val
);
529 * Function responsible for reading in a line from the manifest.
530 * Takes in the file ptr and a buffer, parses the buffer and sets the 'line'
531 * ptr correctly. In the case when the buffer is fully parsed, this function
532 * reads more data from the file ptr and refills the buffer.
535 read_manifest_line(FILE *fd
, char *buf
, int buf_size
, int start_pos
,
536 char **line
, char *fname
)
538 int end_pos
, len
, iscomment
= 0, filepos
;
541 * Initialization case: make sure the manifest version is OK
543 if (start_pos
== 0) {
547 (void) fread((void *) buf
, (size_t)buf_size
, (size_t)1, fd
);
552 if (strncmp(buf
, MANIFEST_VER
,
553 strlen(MANIFEST_VER
)) != 0)
554 (void) fprintf(stderr
, MISSING_VER
, fname
);
555 if ((*line
[0] == '!') || (*line
[0] == '#'))
559 while ((buf
[end_pos
] != '\n') &&
560 (buf
[end_pos
] != '\0') &&
561 (end_pos
< buf_size
))
564 if (end_pos
>= buf_size
)
568 *line
= &(buf
[end_pos
]);
570 if ((*line
[0] == '!') || (*line
[0] == '#'))
575 while ((buf
[end_pos
] != '\n') && (buf
[end_pos
] != '\0') &&
576 (end_pos
< buf_size
))
579 if (end_pos
< buf_size
) {
580 if (buf
[end_pos
] == '\n') {
585 if (buf
[end_pos
] == '\0')
589 (void) fprintf(stderr
, MANIFEST_ERR
);
593 end_pos
= (start_pos
+1);
594 *line
= &(buf
[end_pos
]);
596 /* Read the buffer until EOL or the buffer is empty */
597 while ((buf
[end_pos
] != '\n') && (buf
[end_pos
] != '\0') &&
598 (end_pos
< buf_size
))
601 if (end_pos
< buf_size
) {
602 /* Found the end of the line, normal exit */
603 if (buf
[end_pos
] == '\n') {
608 /* No more input to read */
609 if (buf
[end_pos
] == '\0')
614 * The following code takes the remainder of the buffer and
615 * puts it at the beginning. The space after the remainder, which
616 * is now at the beginning, is blanked.
617 * At this point, read in more data and continue to find the EOL....
619 len
= end_pos
- (start_pos
+ 1);
620 (void) memcpy(buf
, &(buf
[start_pos
+1]), (size_t)len
);
621 (void) memset(&buf
[len
], '\0', (buf_size
- len
));
622 (void) fread((void *) &buf
[len
], (size_t)(buf_size
-len
), (size_t)1, fd
);
626 /* Read the buffer until EOL or the buffer is empty */
627 while ((buf
[end_pos
] != '\n') && (buf
[end_pos
] != '\0') &&
628 (end_pos
< buf_size
))
631 if (end_pos
< buf_size
) {
632 /* Found the end of the line, normal exit */
633 if (buf
[end_pos
] == '\n') {
638 /* No more input to read */
639 if (buf
[end_pos
] == '\0')
643 (void) fprintf(stderr
, MANIFEST_ERR
);
650 init_default_flags(uint_t
*flags
)
652 /* Default behavior: everything is checked *except* dirmtime */
653 *flags
= ATTR_ALL
& ~(ATTR_DIRMTIME
);