summaryrefslogtreecommitdiff
path: root/usr/src/cmd/filesync/rules.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/filesync/rules.c')
-rw-r--r--usr/src/cmd/filesync/rules.c638
1 files changed, 638 insertions, 0 deletions
diff --git a/usr/src/cmd/filesync/rules.c b/usr/src/cmd/filesync/rules.c
new file mode 100644
index 0000000000..1d4b87dd16
--- /dev/null
+++ b/usr/src/cmd/filesync/rules.c
@@ -0,0 +1,638 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved
+ *
+ * module:
+ * rules.c
+ *
+ * purpose:
+ * to read and write the rules file and manage rules lists
+ *
+ * contents:
+ * reading rules file
+ * read_rules
+ * (static) read_command
+ * writing rules file
+ * write_rules
+ * (static) rw_header, rw_base
+ * adding rules
+ * add_ignore, add_include
+ * (static) add_rule
+ * adding/checking restrictions
+ * add_restr, check_restr
+ */
+#ident "%W% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "filesync.h"
+#include "database.h"
+#include "messages.h"
+#include "debug.h"
+
+/*
+ * routines:
+ */
+static errmask_t rw_base(FILE *file, struct base *bp);
+static errmask_t rw_header(FILE *file);
+static errmask_t add_rule(struct base *, int, const char *);
+static char *read_cmd(char *);
+
+/*
+ * globals
+ */
+static int rules_added;
+static int restr_added;
+
+/*
+ * locals
+ */
+#define RULE_MAJOR 1 /* rules file format major rev */
+#define RULE_MINOR 1 /* rules file format minor rev */
+#define RULE_TAG "PACKINGRULES" /* magic string for rules files */
+
+/*
+ * routine:
+ * read_rules
+ *
+ * purpose:
+ * to read in the rules file
+ *
+ * parameters:
+ * name of rules file
+ *
+ * returns:
+ * error mask
+ *
+ * notes:
+ * later when I implement a proper (comment preserving) update
+ * function I'm going to wish I had figured out how to build the
+ * input functions for this function in a way that would make
+ * the more usable for that too.
+ */
+errmask_t
+read_rules(char *name)
+{ FILE *file;
+ errmask_t errs = 0;
+ int flags;
+ int major, minor;
+ char *s, *s1, *s2;
+ struct base *bp;
+ char *errstr = "???";
+
+ file = fopen(name, "r");
+ if (file == NULL) {
+ fprintf(stderr, gettext(ERR_open), gettext(TXT_rules),
+ name);
+ return (ERR_FILES);
+ }
+
+ lex_linenum = 0;
+
+ if (opt_debug & DBG_FILES)
+ fprintf(stderr, "FILE: READ RULES %s\n", name);
+
+ bp = &omnibase; /* default base before any others */
+
+ while (!feof(file)) {
+ /* find the first token on the line */
+ s = lex(file);
+
+ /* skip blank lines and comments */
+ if (s == 0 || *s == 0 || *s == '#' || *s == '*')
+ continue;
+
+ /* see if the first token is a known keyword */
+ if (strcmp(s, "BASE") == 0) {
+
+ /* get the source & destination tokens */
+ errstr = gettext(TXT_srcdst);
+ s1 = lex(0);
+ if (s1 == 0)
+ goto bad;
+ s1 = strdup(s1);
+
+ s2 = lex(0);
+ if (s2 == 0)
+ goto bad;
+ s2 = strdup(s2);
+
+ /* creat the new base pair */
+ bp = add_base(s1, s2);
+ bp->b_flags |= F_LISTED;
+
+ free(s1);
+ free(s2);
+ continue;
+ }
+
+ if (strcmp(s, "LIST") == 0) {
+
+ /* make sure we are associated with a real base */
+ if (bp == &omnibase) {
+ errstr = gettext(TXT_nobase);
+ goto bad;
+ }
+
+ /* skip to the next token */
+ s = lex(0);
+ errstr = gettext(TXT_noargs);
+ if (s == 0)
+ goto bad;
+
+ /* see if it is a program or a name */
+ if (*s == '!') {
+ errs |= add_rule(bp, R_PROGRAM,
+ read_cmd(&s[1]));
+ } else {
+ do {
+ flags = wildcards(s) ? R_WILD : 0;
+ errs |= add_rule(bp, flags, s);
+ s = lex(0);
+ } while (s != 0);
+ }
+ continue;
+ }
+
+ if (strcmp(s, "IGNORE") == 0) {
+
+ /* skip to the next token */
+ s = lex(0);
+ errstr = gettext(TXT_noargs);
+ if (s == 0)
+ goto bad;
+
+ flags = R_IGNORE;
+
+ /* see if it is a program or a name */
+ if (*s == '!') {
+ errs |= add_rule(bp, R_PROGRAM|flags,
+ read_cmd(&s[1]));
+ } else {
+ do {
+ if (wildcards(s))
+ flags |= R_WILD;
+ errs |= add_rule(bp, flags, s);
+ s = lex(0);
+ } while (s != 0);
+ }
+ continue;
+ }
+
+ if (strcmp(s, "VERSION") == 0 || strcmp(s, RULE_TAG) == 0) {
+ s = lex(0);
+ errstr = gettext(TXT_noargs);
+ if (s == 0)
+ goto bad;
+
+ major = strtol(s, &s1, 10);
+ errstr = gettext(TXT_badver);
+ if (*s1 != '.')
+ goto bad;
+ minor = strtol(&s1[1], 0, 10);
+
+ if (major != RULE_MAJOR || minor > RULE_MINOR) {
+ fprintf(stderr, gettext(ERR_badver),
+ major, minor, gettext(TXT_rules), name);
+ errs |= ERR_FILES;
+ }
+ continue;
+ }
+
+ bad: /* log the error and continue processing to find others */
+ fprintf(stderr, gettext(ERR_badinput),
+ lex_linenum, errstr, name);
+ errs |= ERR_FILES;
+ }
+
+
+ (void) fclose(file);
+ return (errs);
+}
+
+/*
+ * routine:
+ * read_cmd
+ *
+ * purpose:
+ * to lex a runnable command (! lines) into a buffer
+ *
+ * parameters:
+ * first token
+ *
+ * returns:
+ * pointer to a command line in a static buffer
+ * (it is assumed the caller will copy it promptly)
+ *
+ * notes:
+ * this is necessary because lex has already choped off
+ * the first token for us
+ */
+static char *read_cmd(char * s)
+{
+ static char cmdbuf[ MAX_LINE ];
+
+ cmdbuf[0] = 0;
+
+ do {
+ if (*s) {
+ strcat(cmdbuf, s);
+ strcat(cmdbuf, " ");
+ }
+ } while ((s = lex(0)) != 0);
+
+ return (cmdbuf);
+}
+
+/*
+ * routine:
+ * write_rules
+ *
+ * purpose:
+ * to rewrite the rules file, appending the new rules
+ *
+ * parameters:
+ * name of output file
+ *
+ * returns:
+ * error mask
+ *
+ */
+errmask_t
+write_rules(char *name)
+{ FILE *newfile;
+ errmask_t errs = 0;
+ struct base *bp;
+ char tmpname[ MAX_PATH ];
+
+ /* if no-touch is specified, we don't update files */
+ if (opt_notouch || rules_added == 0)
+ return (0);
+
+ /* create a temporary output file */
+ sprintf(tmpname, "%s-TMP", name);
+
+ /* create our output file */
+ newfile = fopen(tmpname, "w+");
+ if (newfile == NULL) {
+ fprintf(stderr, gettext(ERR_creat), gettext(TXT_rules),
+ name);
+ return (ERR_FILES);
+ }
+
+ if (opt_debug & DBG_FILES)
+ fprintf(stderr, "FILE: UPDATE RULES %s\n", name);
+
+ errs |= rw_header(newfile);
+ errs |= rw_base(newfile, &omnibase);
+ for (bp = bases; bp; bp = bp->b_next)
+ errs |= rw_base(newfile, bp);
+
+ if (ferror(newfile)) {
+ fprintf(stderr, gettext(ERR_write), gettext(TXT_rules),
+ tmpname);
+ errs |= ERR_FILES;
+ }
+
+ if (fclose(newfile)) {
+ fprintf(stderr, gettext(ERR_fclose), gettext(TXT_rules),
+ tmpname);
+ errs |= ERR_FILES;
+ }
+
+ /* now switch the new file for the old one */
+ if (errs == 0)
+ if (rename(tmpname, name) != 0) {
+ fprintf(stderr, gettext(ERR_rename),
+ gettext(TXT_rules), tmpname, name);
+ errs |= ERR_FILES;
+ }
+
+ return (errs);
+}
+
+/*
+ * routine:
+ * rw_header
+ *
+ * purpose:
+ * to write out a rules header
+ *
+ * parameters:
+ * FILE* for the output file
+ *
+ * returns:
+ * error mask
+ *
+ * notes:
+ */
+static errmask_t rw_header(FILE *file)
+{
+ time_t now;
+ struct tm *local;
+
+ /* figure out what time it is */
+ (void) time(&now);
+ local = localtime(&now);
+
+ fprintf(file, "%s %d.%d\n", RULE_TAG, RULE_MAJOR, RULE_MINOR);
+ fprintf(file, "#\n");
+ fprintf(file, "# filesync rules, last written by %s, %s",
+ cuserid((char *) 0), asctime(local));
+ fprintf(file, "#\n");
+
+ return (0);
+}
+
+/*
+ * routine:
+ * rw_base
+ *
+ * purpose:
+ * to write out the summary for one base-pair
+ *
+ * parameters:
+ * FILE * for the output file
+ *
+ * returns:
+ * error mask
+ *
+ * notes:
+ */
+static errmask_t rw_base(FILE *file, struct base *bp)
+{ struct rule *rp;
+
+ fprintf(file, "\n");
+
+ /* global rules don't appear within a base */
+ if (bp->b_ident)
+ fprintf(file, "BASE %s %s\n", noblanks(bp->b_src_spec),
+ noblanks(bp->b_dst_spec));
+
+ for (rp = bp->b_includes; rp; rp = rp->r_next)
+ if (rp->r_flags & R_PROGRAM)
+ fprintf(file, "LIST !%s\n", rp->r_file);
+ else
+ fprintf(file, "LIST %s\n", noblanks(rp->r_file));
+
+ for (rp = bp->b_excludes; rp; rp = rp->r_next)
+ if (rp->r_flags & R_PROGRAM)
+ fprintf(file, "IGNORE !%s\n", rp->r_file);
+ else
+ fprintf(file, "IGNORE %s\n", noblanks(rp->r_file));
+
+ return (0);
+}
+
+/*
+ * routine:
+ * add_rule
+ *
+ * purpose:
+ * to add a new rule
+ *
+ * parameters:
+ * pointer to list base
+ * rule flags
+ * associated name/arguments
+ *
+ * returns:
+ * error flags
+ *
+ * notes:
+ * we always copy the argument string because most of them
+ * were read from a file and are just in a transient buffer
+ */
+static errmask_t add_rule(struct base *bp, int flags, const char *args)
+{ struct rule *rp;
+ struct rule **list;
+
+ rp = malloc(sizeof (struct rule));
+ if (rp == 0)
+ nomem("rule struture");
+
+ /* initialize the new base */
+ memset((void *) rp, 0, sizeof (struct rule));
+ rp->r_flags = flags;
+ rp->r_file = strdup(args);
+
+ /* figure out which list to put it on */
+ if (flags&R_IGNORE)
+ list = &bp->b_excludes;
+ else if (flags&R_RESTRICT)
+ list = &bp->b_restrictions;
+ else
+ list = &bp->b_includes;
+
+ while (*list)
+ list = &((*list)->r_next);
+ *list = rp;
+
+ if (flags & R_NEW)
+ rules_added++;
+
+ if (opt_debug & DBG_RULE) {
+ fprintf(stderr, "RULE: base=%d, ", bp->b_ident);
+ fprintf(stderr, "flags=%s, ",
+ showflags(rflags, rp->r_flags));
+ fprintf(stderr, "arg=%s\n", rp->r_file);
+ }
+
+ return (0);
+}
+
+/*
+ * routine:
+ * add_ignore, add_include
+ *
+ * purpose:
+ * wrappers for add_rule that permit outsiders (like main.c)
+ * not to know what is inside of a base, file, or list entry
+ *
+ * parameters:
+ * base under which rules should be added
+ * argument associated with rule
+ *
+ * returns:
+ * error flags
+ *
+ * notes:
+ * basically these routines figure out what the right
+ * flags are for a rule, and what list to put it on,
+ * and then call a common handler.
+ */
+errmask_t
+add_ignore(struct base *bp, char *name)
+{ int flags = R_IGNORE | R_NEW;
+
+ if (bp == 0)
+ bp = &omnibase;
+
+ if (wildcards(name))
+ flags |= R_WILD;
+
+ return (add_rule(bp, flags, name));
+}
+
+errmask_t
+add_include(struct base *bp, char *name)
+{ int flags = R_NEW;
+
+ if (bp == 0)
+ bp = &omnibase;
+
+ if (wildcards(name))
+ flags |= R_WILD;
+
+ bp->b_flags |= F_LISTED;
+
+ return (add_rule(bp, flags, name));
+}
+
+/*
+ * routine:
+ * add_restr
+ *
+ * purpose:
+ * to add a restriction to a base
+ *
+ * parameters:
+ * address of base
+ * restriction string
+ *
+ * returns:
+ * error mask
+ *
+ * notes:
+ * a restriction is specified on the command line and
+ * tells us to limit our analysis/reconcilation to
+ * specified files and/or directories. We deal with
+ * these by adding a restriction rule to any base that
+ * looks like it might fit the restriction. We need to
+ * treat this as a rule because the restriction string
+ * may extend beyond the base directory and part-way into
+ * its tree ... meaning that individual file names under
+ * the base will have to be checked against the restriction.
+ */
+errmask_t
+add_restr(char *restr)
+{ const char *s;
+ errmask_t errs = 0;
+ struct base *bp;
+
+ for (bp = bases; bp; bp = bp->b_next) {
+ /*
+ * see if this restriction could apply to this base.
+ * It could match either the source or destination
+ * directory name for this base. If it matches neither
+ * then the restriction does not apply to this base.
+ */
+ s = prefix(restr, bp->b_src_name);
+ if (s == 0)
+ s = prefix(restr, bp->b_dst_name);
+ if (s == 0)
+ continue;
+
+ /*
+ * if there is more restriction string after the
+ * base, we will need to note the remainder of the
+ * string so that we can match individual files
+ * against it.
+ */
+ if (*s == '/')
+ s++;
+
+ errs |= add_rule(bp, R_RESTRICT, s);
+ restr_added++;
+ }
+
+ return (errs);
+}
+
+/*
+ * routine:
+ * check_restr
+ *
+ * purpose:
+ * to see if an argument falls within restrictions
+ *
+ * parameters:
+ * pointer to relevent base
+ * file name
+ *
+ * returns:
+ * TRUE name is within restrictions
+ * FALSE name is outside of restrictions
+ * MAYBE name is on the path to a restriction
+ *
+ * notes:
+ * if no restrictions have been specified, we evaluate
+ * everything. If any restrictions have been specified,
+ * we process only files that match one of the restrictions.
+ *
+ * add_restr has ensured that if the restriction includes
+ * a portion that must be matched by individual files under
+ * the base, that the restriction rule will contain that
+ * portion of the restriction which must be matched against
+ * individual file names.
+ */
+bool_t
+check_restr(struct base *bp, const char *name)
+{ struct rule *rp;
+
+ /* if there are no restrictions, everything is OK */
+ if (restr_added == 0)
+ return (TRUE);
+
+ /* now we have to run through the list */
+ for (rp = bp->b_restrictions; rp; rp = rp->r_next) {
+ /* see if current path is under the restriction */
+ if (prefix(name, rp->r_file))
+ return (TRUE);
+
+ /* see if current path is on the way to restr */
+ if (prefix(rp->r_file, name))
+ /*
+ * this is kinky, but walker really needs
+ * to know the difference between a directory
+ * that we are unreservedly scanning, and one
+ * that we are scanning only to find something
+ * beneath it.
+ */
+ return (MAYBE);
+ }
+
+ /*
+ * there are restrictions in effect and this file doesn't seem
+ * to meet any of them
+ */
+ if (opt_debug & DBG_RULE)
+ fprintf(stderr, "RULE: FAIL RESTRICTION base=%d, file=%s\n",
+ bp->b_ident, name);
+
+ return (FALSE);
+}