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 (c) 1995 Sun Microsystems, Inc. All Rights Reserved
24 *
25 * module:
26 * rules.c
27 *
28 * purpose:
29 * to read and write the rules file and manage rules lists
30 *
31 * contents:
32 * reading rules file
33 * read_rules
34 * (static) read_command
35 * writing rules file
36 * write_rules
37 * (static) rw_header, rw_base
38 * adding rules
39 * add_ignore, add_include
40 * (static) add_rule
41 * adding/checking restrictions
42 * add_restr, check_restr
43 */
44 #pragma ident "%Z%%M% %I% %E% SMI"
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <ctype.h>
51
52 #include "filesync.h"
53 #include "database.h"
54 #include "messages.h"
55 #include "debug.h"
56
57 /*
58 * routines:
59 */
60 static errmask_t rw_base(FILE *file, struct base *bp);
61 static errmask_t rw_header(FILE *file);
62 static errmask_t add_rule(struct base *, int, const char *);
63 static char *read_cmd(char *);
64
65 /*
66 * globals
67 */
68 static int rules_added;
69 static int restr_added;
70
71 /*
72 * locals
73 */
74 #define RULE_MAJOR 1 /* rules file format major rev */
75 #define RULE_MINOR 1 /* rules file format minor rev */
76 #define RULE_TAG "PACKINGRULES" /* magic string for rules files */
77
78 /*
79 * routine:
80 * read_rules
81 *
82 * purpose:
83 * to read in the rules file
84 *
85 * parameters:
86 * name of rules file
87 *
88 * returns:
89 * error mask
90 *
91 * notes:
92 * later when I implement a proper (comment preserving) update
93 * function I'm going to wish I had figured out how to build the
94 * input functions for this function in a way that would make
95 * the more usable for that too.
96 */
97 errmask_t
read_rules(char * name)98 read_rules(char *name)
99 { FILE *file;
100 errmask_t errs = 0;
101 int flags;
102 int major, minor;
103 char *s, *s1, *s2;
104 struct base *bp;
105 char *errstr = "???";
106
107 file = fopen(name, "r");
108 if (file == NULL) {
109 fprintf(stderr, gettext(ERR_open), gettext(TXT_rules),
110 name);
111 return (ERR_FILES);
112 }
113
114 lex_linenum = 0;
115
116 if (opt_debug & DBG_FILES)
117 fprintf(stderr, "FILE: READ RULES %s\n", name);
118
119 bp = &omnibase; /* default base before any others */
120
121 while (!feof(file)) {
122 /* find the first token on the line */
123 s = lex(file);
124
125 /* skip blank lines and comments */
126 if (s == 0 || *s == 0 || *s == '#' || *s == '*')
127 continue;
128
129 /* see if the first token is a known keyword */
130 if (strcmp(s, "BASE") == 0) {
131
132 /* get the source & destination tokens */
133 errstr = gettext(TXT_srcdst);
134 s1 = lex(0);
135 if (s1 == 0)
136 goto bad;
137 s1 = strdup(s1);
138
139 s2 = lex(0);
140 if (s2 == 0)
141 goto bad;
142 s2 = strdup(s2);
143
144 /* creat the new base pair */
145 bp = add_base(s1, s2);
146 bp->b_flags |= F_LISTED;
147
148 free(s1);
149 free(s2);
150 continue;
151 }
152
153 if (strcmp(s, "LIST") == 0) {
154
155 /* make sure we are associated with a real base */
156 if (bp == &omnibase) {
157 errstr = gettext(TXT_nobase);
158 goto bad;
159 }
160
161 /* skip to the next token */
162 s = lex(0);
163 errstr = gettext(TXT_noargs);
164 if (s == 0)
165 goto bad;
166
167 /* see if it is a program or a name */
168 if (*s == '!') {
169 errs |= add_rule(bp, R_PROGRAM,
170 read_cmd(&s[1]));
171 } else {
172 do {
173 flags = wildcards(s) ? R_WILD : 0;
174 errs |= add_rule(bp, flags, s);
175 s = lex(0);
176 } while (s != 0);
177 }
178 continue;
179 }
180
181 if (strcmp(s, "IGNORE") == 0) {
182
183 /* skip to the next token */
184 s = lex(0);
185 errstr = gettext(TXT_noargs);
186 if (s == 0)
187 goto bad;
188
189 flags = R_IGNORE;
190
191 /* see if it is a program or a name */
192 if (*s == '!') {
193 errs |= add_rule(bp, R_PROGRAM|flags,
194 read_cmd(&s[1]));
195 } else {
196 do {
197 if (wildcards(s))
198 flags |= R_WILD;
199 errs |= add_rule(bp, flags, s);
200 s = lex(0);
201 } while (s != 0);
202 }
203 continue;
204 }
205
206 if (strcmp(s, "VERSION") == 0 || strcmp(s, RULE_TAG) == 0) {
207 s = lex(0);
208 errstr = gettext(TXT_noargs);
209 if (s == 0)
210 goto bad;
211
212 major = strtol(s, &s1, 10);
213 errstr = gettext(TXT_badver);
214 if (*s1 != '.')
215 goto bad;
216 minor = strtol(&s1[1], 0, 10);
217
218 if (major != RULE_MAJOR || minor > RULE_MINOR) {
219 fprintf(stderr, gettext(ERR_badver),
220 major, minor, gettext(TXT_rules), name);
221 errs |= ERR_FILES;
222 }
223 continue;
224 }
225
226 bad: /* log the error and continue processing to find others */
227 fprintf(stderr, gettext(ERR_badinput),
228 lex_linenum, errstr, name);
229 errs |= ERR_FILES;
230 }
231
232
233 (void) fclose(file);
234 return (errs);
235 }
236
237 /*
238 * routine:
239 * read_cmd
240 *
241 * purpose:
242 * to lex a runnable command (! lines) into a buffer
243 *
244 * parameters:
245 * first token
246 *
247 * returns:
248 * pointer to a command line in a static buffer
249 * (it is assumed the caller will copy it promptly)
250 *
251 * notes:
252 * this is necessary because lex has already choped off
253 * the first token for us
254 */
read_cmd(char * s)255 static char *read_cmd(char * s)
256 {
257 static char cmdbuf[ MAX_LINE ];
258
259 cmdbuf[0] = 0;
260
261 do {
262 if (*s) {
263 strcat(cmdbuf, s);
264 strcat(cmdbuf, " ");
265 }
266 } while ((s = lex(0)) != 0);
267
268 return (cmdbuf);
269 }
270
271 /*
272 * routine:
273 * write_rules
274 *
275 * purpose:
276 * to rewrite the rules file, appending the new rules
277 *
278 * parameters:
279 * name of output file
280 *
281 * returns:
282 * error mask
283 *
284 */
285 errmask_t
write_rules(char * name)286 write_rules(char *name)
287 { FILE *newfile;
288 errmask_t errs = 0;
289 struct base *bp;
290 char tmpname[ MAX_PATH ];
291
292 /* if no-touch is specified, we don't update files */
293 if (opt_notouch || rules_added == 0)
294 return (0);
295
296 /* create a temporary output file */
297 sprintf(tmpname, "%s-TMP", name);
298
299 /* create our output file */
300 newfile = fopen(tmpname, "w+");
301 if (newfile == NULL) {
302 fprintf(stderr, gettext(ERR_creat), gettext(TXT_rules),
303 name);
304 return (ERR_FILES);
305 }
306
307 if (opt_debug & DBG_FILES)
308 fprintf(stderr, "FILE: UPDATE RULES %s\n", name);
309
310 errs |= rw_header(newfile);
311 errs |= rw_base(newfile, &omnibase);
312 for (bp = bases; bp; bp = bp->b_next)
313 errs |= rw_base(newfile, bp);
314
315 if (ferror(newfile)) {
316 fprintf(stderr, gettext(ERR_write), gettext(TXT_rules),
317 tmpname);
318 errs |= ERR_FILES;
319 }
320
321 if (fclose(newfile)) {
322 fprintf(stderr, gettext(ERR_fclose), gettext(TXT_rules),
323 tmpname);
324 errs |= ERR_FILES;
325 }
326
327 /* now switch the new file for the old one */
328 if (errs == 0)
329 if (rename(tmpname, name) != 0) {
330 fprintf(stderr, gettext(ERR_rename),
331 gettext(TXT_rules), tmpname, name);
332 errs |= ERR_FILES;
333 }
334
335 return (errs);
336 }
337
338 /*
339 * routine:
340 * rw_header
341 *
342 * purpose:
343 * to write out a rules header
344 *
345 * parameters:
346 * FILE* for the output file
347 *
348 * returns:
349 * error mask
350 *
351 * notes:
352 */
rw_header(FILE * file)353 static errmask_t rw_header(FILE *file)
354 {
355 time_t now;
356 struct tm *local;
357
358 /* figure out what time it is */
359 (void) time(&now);
360 local = localtime(&now);
361
362 fprintf(file, "%s %d.%d\n", RULE_TAG, RULE_MAJOR, RULE_MINOR);
363 fprintf(file, "#\n");
364 fprintf(file, "# filesync rules, last written by %s, %s",
365 cuserid((char *) 0), asctime(local));
366 fprintf(file, "#\n");
367
368 return (0);
369 }
370
371 /*
372 * routine:
373 * rw_base
374 *
375 * purpose:
376 * to write out the summary for one base-pair
377 *
378 * parameters:
379 * FILE * for the output file
380 *
381 * returns:
382 * error mask
383 *
384 * notes:
385 */
rw_base(FILE * file,struct base * bp)386 static errmask_t rw_base(FILE *file, struct base *bp)
387 { struct rule *rp;
388
389 fprintf(file, "\n");
390
391 /* global rules don't appear within a base */
392 if (bp->b_ident)
393 fprintf(file, "BASE %s %s\n", noblanks(bp->b_src_spec),
394 noblanks(bp->b_dst_spec));
395
396 for (rp = bp->b_includes; rp; rp = rp->r_next)
397 if (rp->r_flags & R_PROGRAM)
398 fprintf(file, "LIST !%s\n", rp->r_file);
399 else
400 fprintf(file, "LIST %s\n", noblanks(rp->r_file));
401
402 for (rp = bp->b_excludes; rp; rp = rp->r_next)
403 if (rp->r_flags & R_PROGRAM)
404 fprintf(file, "IGNORE !%s\n", rp->r_file);
405 else
406 fprintf(file, "IGNORE %s\n", noblanks(rp->r_file));
407
408 return (0);
409 }
410
411 /*
412 * routine:
413 * add_rule
414 *
415 * purpose:
416 * to add a new rule
417 *
418 * parameters:
419 * pointer to list base
420 * rule flags
421 * associated name/arguments
422 *
423 * returns:
424 * error flags
425 *
426 * notes:
427 * we always copy the argument string because most of them
428 * were read from a file and are just in a transient buffer
429 */
add_rule(struct base * bp,int flags,const char * args)430 static errmask_t add_rule(struct base *bp, int flags, const char *args)
431 { struct rule *rp;
432 struct rule **list;
433
434 rp = malloc(sizeof (struct rule));
435 if (rp == 0)
436 nomem("rule struture");
437
438 /* initialize the new base */
439 memset((void *) rp, 0, sizeof (struct rule));
440 rp->r_flags = flags;
441 rp->r_file = strdup(args);
442
443 /* figure out which list to put it on */
444 if (flags&R_IGNORE)
445 list = &bp->b_excludes;
446 else if (flags&R_RESTRICT)
447 list = &bp->b_restrictions;
448 else
449 list = &bp->b_includes;
450
451 while (*list)
452 list = &((*list)->r_next);
453 *list = rp;
454
455 if (flags & R_NEW)
456 rules_added++;
457
458 if (opt_debug & DBG_RULE) {
459 fprintf(stderr, "RULE: base=%d, ", bp->b_ident);
460 fprintf(stderr, "flags=%s, ",
461 showflags(rflags, rp->r_flags));
462 fprintf(stderr, "arg=%s\n", rp->r_file);
463 }
464
465 return (0);
466 }
467
468 /*
469 * routine:
470 * add_ignore, add_include
471 *
472 * purpose:
473 * wrappers for add_rule that permit outsiders (like main.c)
474 * not to know what is inside of a base, file, or list entry
475 *
476 * parameters:
477 * base under which rules should be added
478 * argument associated with rule
479 *
480 * returns:
481 * error flags
482 *
483 * notes:
484 * basically these routines figure out what the right
485 * flags are for a rule, and what list to put it on,
486 * and then call a common handler.
487 */
488 errmask_t
add_ignore(struct base * bp,char * name)489 add_ignore(struct base *bp, char *name)
490 { int flags = R_IGNORE | R_NEW;
491
492 if (bp == 0)
493 bp = &omnibase;
494
495 if (wildcards(name))
496 flags |= R_WILD;
497
498 return (add_rule(bp, flags, name));
499 }
500
501 errmask_t
add_include(struct base * bp,char * name)502 add_include(struct base *bp, char *name)
503 { int flags = R_NEW;
504
505 if (bp == 0)
506 bp = &omnibase;
507
508 if (wildcards(name))
509 flags |= R_WILD;
510
511 bp->b_flags |= F_LISTED;
512
513 return (add_rule(bp, flags, name));
514 }
515
516 /*
517 * routine:
518 * add_restr
519 *
520 * purpose:
521 * to add a restriction to a base
522 *
523 * parameters:
524 * address of base
525 * restriction string
526 *
527 * returns:
528 * error mask
529 *
530 * notes:
531 * a restriction is specified on the command line and
532 * tells us to limit our analysis/reconcilation to
533 * specified files and/or directories. We deal with
534 * these by adding a restriction rule to any base that
535 * looks like it might fit the restriction. We need to
536 * treat this as a rule because the restriction string
537 * may extend beyond the base directory and part-way into
538 * its tree ... meaning that individual file names under
539 * the base will have to be checked against the restriction.
540 */
541 errmask_t
add_restr(char * restr)542 add_restr(char *restr)
543 { const char *s;
544 errmask_t errs = 0;
545 struct base *bp;
546
547 for (bp = bases; bp; bp = bp->b_next) {
548 /*
549 * see if this restriction could apply to this base.
550 * It could match either the source or destination
551 * directory name for this base. If it matches neither
552 * then the restriction does not apply to this base.
553 */
554 s = prefix(restr, bp->b_src_name);
555 if (s == 0)
556 s = prefix(restr, bp->b_dst_name);
557 if (s == 0)
558 continue;
559
560 /*
561 * if there is more restriction string after the
562 * base, we will need to note the remainder of the
563 * string so that we can match individual files
564 * against it.
565 */
566 if (*s == '/')
567 s++;
568
569 errs |= add_rule(bp, R_RESTRICT, s);
570 restr_added++;
571 }
572
573 return (errs);
574 }
575
576 /*
577 * routine:
578 * check_restr
579 *
580 * purpose:
581 * to see if an argument falls within restrictions
582 *
583 * parameters:
584 * pointer to relevant base
585 * file name
586 *
587 * returns:
588 * TRUE name is within restrictions
589 * FALSE name is outside of restrictions
590 * MAYBE name is on the path to a restriction
591 *
592 * notes:
593 * if no restrictions have been specified, we evaluate
594 * everything. If any restrictions have been specified,
595 * we process only files that match one of the restrictions.
596 *
597 * add_restr has ensured that if the restriction includes
598 * a portion that must be matched by individual files under
599 * the base, that the restriction rule will contain that
600 * portion of the restriction which must be matched against
601 * individual file names.
602 */
603 bool_t
check_restr(struct base * bp,const char * name)604 check_restr(struct base *bp, const char *name)
605 { struct rule *rp;
606
607 /* if there are no restrictions, everything is OK */
608 if (restr_added == 0)
609 return (TRUE);
610
611 /* now we have to run through the list */
612 for (rp = bp->b_restrictions; rp; rp = rp->r_next) {
613 /* see if current path is under the restriction */
614 if (prefix(name, rp->r_file))
615 return (TRUE);
616
617 /* see if current path is on the way to restr */
618 if (prefix(rp->r_file, name))
619 /*
620 * this is kinky, but walker really needs
621 * to know the difference between a directory
622 * that we are unreservedly scanning, and one
623 * that we are scanning only to find something
624 * beneath it.
625 */
626 return (MAYBE);
627 }
628
629 /*
630 * there are restrictions in effect and this file doesn't seem
631 * to meet any of them
632 */
633 if (opt_debug & DBG_RULE)
634 fprintf(stderr, "RULE: FAIL RESTRICTION base=%d, file=%s\n",
635 bp->b_ident, name);
636
637 return (FALSE);
638 }
639