rules.c revision 3517:79d66aa80b8b
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 */
60static errmask_t rw_base(FILE *file, struct base *bp);
61static errmask_t rw_header(FILE *file);
62static errmask_t add_rule(struct base *, int, const char *);
63static char *read_cmd(char *);
64
65/*
66 * globals
67 */
68static int rules_added;
69static 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 */
97errmask_t
98read_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 */
255static 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 */
285errmask_t
286write_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 */
353static 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 */
386static 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 */
430static 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 */
488errmask_t
489add_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
501errmask_t
502add_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 */
541errmask_t
542add_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 */
603bool_t
604check_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