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 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 */ 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 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 */ 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 */ 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 */ 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 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 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 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 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 }