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