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