1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 1995-2003 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate /* 28*0Sstevel@tonic-gate * module: 29*0Sstevel@tonic-gate * main.c 30*0Sstevel@tonic-gate * 31*0Sstevel@tonic-gate * purpose: 32*0Sstevel@tonic-gate * argument handling and top level dispatch 33*0Sstevel@tonic-gate * 34*0Sstevel@tonic-gate * contents: 35*0Sstevel@tonic-gate * main argument handling and main loop 36*0Sstevel@tonic-gate * usage (static) print out usage message 37*0Sstevel@tonic-gate * confirm prompt the user for a confirmation and get it 38*0Sstevel@tonic-gate * nomem fatal error handler for malloc failures 39*0Sstevel@tonic-gate * findfiles (static) locate our baseline and rules files 40*0Sstevel@tonic-gate * cleanup (static) unlock baseline and delete temp file 41*0Sstevel@tonic-gate * check_access (static) do we have adequate access to a file/directory 42*0Sstevel@tonic-gate * whoami (static) get uid/gid/umask 43*0Sstevel@tonic-gate */ 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate #include <unistd.h> 48*0Sstevel@tonic-gate #include <stdlib.h> 49*0Sstevel@tonic-gate #include <fcntl.h> 50*0Sstevel@tonic-gate #include <stdio.h> 51*0Sstevel@tonic-gate #include <string.h> 52*0Sstevel@tonic-gate #include <ctype.h> 53*0Sstevel@tonic-gate #include <errno.h> 54*0Sstevel@tonic-gate #include <sys/stat.h> 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate #include "filesync.h" 57*0Sstevel@tonic-gate #include "database.h" 58*0Sstevel@tonic-gate #include "messages.h" 59*0Sstevel@tonic-gate #include "debug.h" 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate /* 62*0Sstevel@tonic-gate * local routines in this module: 63*0Sstevel@tonic-gate */ 64*0Sstevel@tonic-gate static errmask_t findfiles(); /* find rule and baseline files */ 65*0Sstevel@tonic-gate static void cleanup(int); /* cleanup locks and temps */ 66*0Sstevel@tonic-gate static errmask_t check_access(char *, int *); /* check access to file */ 67*0Sstevel@tonic-gate static void whoami(); /* gather information about me */ 68*0Sstevel@tonic-gate static void usage(void); /* general usage */ 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate /* 72*0Sstevel@tonic-gate * globals exported to the rest of the program 73*0Sstevel@tonic-gate */ 74*0Sstevel@tonic-gate bool_t opt_mtime; /* preserve modification times on propagations */ 75*0Sstevel@tonic-gate bool_t opt_notouch; /* don't actually make any changes */ 76*0Sstevel@tonic-gate bool_t opt_quiet; /* disable reconciliation command output */ 77*0Sstevel@tonic-gate bool_t opt_verbose; /* enable analysis descriptions */ 78*0Sstevel@tonic-gate side_t opt_force; /* designated winner for conflicts */ 79*0Sstevel@tonic-gate side_t opt_oneway; /* one way only propagation */ 80*0Sstevel@tonic-gate side_t opt_onesided; /* permit one-sided evaluation */ 81*0Sstevel@tonic-gate bool_t opt_everything; /* everything must agree (modes/uid/gid) */ 82*0Sstevel@tonic-gate bool_t opt_yes; /* pre-confirm massive deletions are OK */ 83*0Sstevel@tonic-gate bool_t opt_acls; /* always scan for acls on all files */ 84*0Sstevel@tonic-gate bool_t opt_errors; /* simulate errors on specified files */ 85*0Sstevel@tonic-gate bool_t opt_halt; /* halt on propagation errors */ 86*0Sstevel@tonic-gate dbgmask_t opt_debug; /* debug mask */ 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gate uid_t my_uid; /* default UID for files I create */ 89*0Sstevel@tonic-gate gid_t my_gid; /* default GID for files I create */ 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate static char *file_rules; /* name of rules file */ 92*0Sstevel@tonic-gate static char *file_base; /* name of baseline file */ 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gate static int new_baseline; /* are we creating a new baseline */ 95*0Sstevel@tonic-gate static int new_rules; /* are we creating a new rules file */ 96*0Sstevel@tonic-gate static int my_umask; /* default UMASK for files I create */ 97*0Sstevel@tonic-gate static int lockfd; /* file descriptor for locking baseline */ 98*0Sstevel@tonic-gate 99*0Sstevel@tonic-gate static char *rlist[MAX_RLIST]; 100*0Sstevel@tonic-gate static int num_restrs = 0; 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate /* 103*0Sstevel@tonic-gate * routine: 104*0Sstevel@tonic-gate * main 105*0Sstevel@tonic-gate * 106*0Sstevel@tonic-gate * purpose: 107*0Sstevel@tonic-gate * argument processing and primary dispatch 108*0Sstevel@tonic-gate * 109*0Sstevel@tonic-gate * returns: 110*0Sstevel@tonic-gate * error codes per filesync.1 (ERR_* in filesync.h) 111*0Sstevel@tonic-gate * 112*0Sstevel@tonic-gate * notes: 113*0Sstevel@tonic-gate * read filesync.1 in order to understand the argument processing 114*0Sstevel@tonic-gate * 115*0Sstevel@tonic-gate * most of the command line options just set some opt_ global 116*0Sstevel@tonic-gate * variable that is later looked at by the code that actually 117*0Sstevel@tonic-gate * implements the features. Only file names are really processed 118*0Sstevel@tonic-gate * in this routine. 119*0Sstevel@tonic-gate */ 120*0Sstevel@tonic-gate void 121*0Sstevel@tonic-gate main(int argc, char **argv) 122*0Sstevel@tonic-gate { int i; 123*0Sstevel@tonic-gate int c; 124*0Sstevel@tonic-gate errmask_t errs = ERR_OK; 125*0Sstevel@tonic-gate int do_prune = 0; 126*0Sstevel@tonic-gate char *srcname = 0; 127*0Sstevel@tonic-gate char *dstname = 0; 128*0Sstevel@tonic-gate struct base *bp; 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gate /* keep the error messages simple */ 131*0Sstevel@tonic-gate argv[0] = "filesync"; 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate /* gather together all of the options */ 134*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "AaehmnqvyD:E:r:s:d:f:o:")) != EOF) 135*0Sstevel@tonic-gate switch (c) { 136*0Sstevel@tonic-gate case 'a': /* always scan for acls */ 137*0Sstevel@tonic-gate opt_acls = TRUE; 138*0Sstevel@tonic-gate break; 139*0Sstevel@tonic-gate case 'e': /* everything agrees */ 140*0Sstevel@tonic-gate opt_everything = TRUE; 141*0Sstevel@tonic-gate break; 142*0Sstevel@tonic-gate case 'h': /* halt on error */ 143*0Sstevel@tonic-gate opt_halt = TRUE; 144*0Sstevel@tonic-gate break; 145*0Sstevel@tonic-gate case 'm': /* preserve modtimes */ 146*0Sstevel@tonic-gate opt_mtime = TRUE; 147*0Sstevel@tonic-gate break; 148*0Sstevel@tonic-gate case 'n': /* notouch */ 149*0Sstevel@tonic-gate opt_notouch = TRUE; 150*0Sstevel@tonic-gate break; 151*0Sstevel@tonic-gate case 'q': /* quiet */ 152*0Sstevel@tonic-gate opt_quiet = TRUE; 153*0Sstevel@tonic-gate break; 154*0Sstevel@tonic-gate case 'v': /* verbose */ 155*0Sstevel@tonic-gate opt_verbose = TRUE; 156*0Sstevel@tonic-gate break; 157*0Sstevel@tonic-gate case 'y': /* yes */ 158*0Sstevel@tonic-gate opt_yes = TRUE; 159*0Sstevel@tonic-gate break; 160*0Sstevel@tonic-gate case 'D': /* debug options */ 161*0Sstevel@tonic-gate if (!isdigit(optarg[0])) { 162*0Sstevel@tonic-gate dbg_usage(); 163*0Sstevel@tonic-gate exit(ERR_INVAL); 164*0Sstevel@tonic-gate } 165*0Sstevel@tonic-gate opt_debug |= strtol(optarg, (char **) NULL, 0); 166*0Sstevel@tonic-gate break; 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate case 'E': /* error simulation */ 169*0Sstevel@tonic-gate if (dbg_set_error(optarg)) { 170*0Sstevel@tonic-gate err_usage(); 171*0Sstevel@tonic-gate exit(ERR_INVAL); 172*0Sstevel@tonic-gate } 173*0Sstevel@tonic-gate opt_errors = TRUE; 174*0Sstevel@tonic-gate break; 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate case 'f': /* force conflict resolution */ 177*0Sstevel@tonic-gate switch (optarg[0]) { 178*0Sstevel@tonic-gate case 's': 179*0Sstevel@tonic-gate opt_force = OPT_SRC; 180*0Sstevel@tonic-gate break; 181*0Sstevel@tonic-gate case 'd': 182*0Sstevel@tonic-gate opt_force = OPT_DST; 183*0Sstevel@tonic-gate break; 184*0Sstevel@tonic-gate case 'o': 185*0Sstevel@tonic-gate opt_force = OPT_OLD; 186*0Sstevel@tonic-gate break; 187*0Sstevel@tonic-gate case 'n': 188*0Sstevel@tonic-gate opt_force = OPT_NEW; 189*0Sstevel@tonic-gate break; 190*0Sstevel@tonic-gate default: 191*0Sstevel@tonic-gate fprintf(stderr, 192*0Sstevel@tonic-gate gettext(ERR_badopt), 193*0Sstevel@tonic-gate c, optarg); 194*0Sstevel@tonic-gate errs |= ERR_INVAL; 195*0Sstevel@tonic-gate break; 196*0Sstevel@tonic-gate } 197*0Sstevel@tonic-gate break; 198*0Sstevel@tonic-gate 199*0Sstevel@tonic-gate case 'o': /* one way propagation */ 200*0Sstevel@tonic-gate switch (optarg[0]) { 201*0Sstevel@tonic-gate case 's': 202*0Sstevel@tonic-gate opt_oneway = OPT_SRC; 203*0Sstevel@tonic-gate break; 204*0Sstevel@tonic-gate case 'd': 205*0Sstevel@tonic-gate opt_oneway = OPT_DST; 206*0Sstevel@tonic-gate break; 207*0Sstevel@tonic-gate default: 208*0Sstevel@tonic-gate fprintf(stderr, 209*0Sstevel@tonic-gate gettext(ERR_badopt), 210*0Sstevel@tonic-gate c, optarg); 211*0Sstevel@tonic-gate errs |= ERR_INVAL; 212*0Sstevel@tonic-gate break; 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate break; 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate case 'r': /* restricted reconciliation */ 217*0Sstevel@tonic-gate if (num_restrs < MAX_RLIST) 218*0Sstevel@tonic-gate rlist[ num_restrs++ ] = optarg; 219*0Sstevel@tonic-gate else { 220*0Sstevel@tonic-gate fprintf(stderr, gettext(ERR_tomany), 221*0Sstevel@tonic-gate MAX_RLIST); 222*0Sstevel@tonic-gate errs |= ERR_INVAL; 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate break; 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate case 's': 227*0Sstevel@tonic-gate if ((srcname = qualify(optarg)) == 0) 228*0Sstevel@tonic-gate errs |= ERR_MISSING; 229*0Sstevel@tonic-gate break; 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate case 'd': 232*0Sstevel@tonic-gate if ((dstname = qualify(optarg)) == 0) 233*0Sstevel@tonic-gate errs |= ERR_MISSING; 234*0Sstevel@tonic-gate break; 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate default: 237*0Sstevel@tonic-gate case '?': 238*0Sstevel@tonic-gate errs |= ERR_INVAL; 239*0Sstevel@tonic-gate break; 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate if (opt_debug & DBG_MISC) 243*0Sstevel@tonic-gate fprintf(stderr, "MISC: DBG=%s\n", showflags(dbgmap, opt_debug)); 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate /* if we have file names, we need a source and destination */ 246*0Sstevel@tonic-gate if (optind < argc) { 247*0Sstevel@tonic-gate if (srcname == 0) { 248*0Sstevel@tonic-gate fprintf(stderr, gettext(ERR_nosrc)); 249*0Sstevel@tonic-gate errs |= ERR_INVAL; 250*0Sstevel@tonic-gate } 251*0Sstevel@tonic-gate if (dstname == 0) { 252*0Sstevel@tonic-gate fprintf(stderr, gettext(ERR_nodst)); 253*0Sstevel@tonic-gate errs |= ERR_INVAL; 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate } 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate /* check for simple usage errors */ 258*0Sstevel@tonic-gate if (errs & ERR_INVAL) { 259*0Sstevel@tonic-gate usage(); 260*0Sstevel@tonic-gate exit(errs); 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate /* locate our baseline and rules files */ 264*0Sstevel@tonic-gate if (c = findfiles()) 265*0Sstevel@tonic-gate exit(c); 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate /* figure out file creation defaults */ 268*0Sstevel@tonic-gate whoami(); 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate /* read in our initial baseline */ 271*0Sstevel@tonic-gate if (!new_baseline && (c = read_baseline(file_base))) 272*0Sstevel@tonic-gate errs |= c; 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate /* read in the rules file if we need or have rules */ 275*0Sstevel@tonic-gate if (optind >= argc && new_rules) { 276*0Sstevel@tonic-gate fprintf(stderr, ERR_nonames); 277*0Sstevel@tonic-gate errs |= ERR_INVAL; 278*0Sstevel@tonic-gate } else if (!new_rules) 279*0Sstevel@tonic-gate errs |= read_rules(file_rules); 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate /* if anything has failed with our setup, go no further */ 282*0Sstevel@tonic-gate if (errs) { 283*0Sstevel@tonic-gate cleanup(errs); 284*0Sstevel@tonic-gate exit(errs); 285*0Sstevel@tonic-gate } 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate /* 288*0Sstevel@tonic-gate * figure out whether or not we are willing to do a one-sided 289*0Sstevel@tonic-gate * analysis (where we don't even look at the other side. This 290*0Sstevel@tonic-gate * is an "I'm just curious what has changed" query, and we are 291*0Sstevel@tonic-gate * only willing to do it if: 292*0Sstevel@tonic-gate * we aren't actually going to do anything 293*0Sstevel@tonic-gate * we have a baseline we can compare against 294*0Sstevel@tonic-gate * otherwise, we are going to insist on being able to access 295*0Sstevel@tonic-gate * both the source and destination. 296*0Sstevel@tonic-gate */ 297*0Sstevel@tonic-gate if (opt_notouch && !new_baseline) 298*0Sstevel@tonic-gate opt_onesided = opt_oneway; 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate /* 301*0Sstevel@tonic-gate * there are two interested usage scenarios: 302*0Sstevel@tonic-gate * file names specified 303*0Sstevel@tonic-gate * create new rules for the specified files 304*0Sstevel@tonic-gate * evaulate and reconcile only the specified files 305*0Sstevel@tonic-gate * no file names specified 306*0Sstevel@tonic-gate * use already existing rules 307*0Sstevel@tonic-gate * consider restricting them to specified subdirs/files 308*0Sstevel@tonic-gate */ 309*0Sstevel@tonic-gate if (optind < argc) { 310*0Sstevel@tonic-gate /* figure out what base pair we're working on */ 311*0Sstevel@tonic-gate bp = add_base(srcname, dstname); 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate /* perverse default rules to avoid trouble */ 314*0Sstevel@tonic-gate if (new_rules) { 315*0Sstevel@tonic-gate errs |= add_ignore(0, SUFX_RULES); 316*0Sstevel@tonic-gate errs |= add_ignore(0, SUFX_BASE); 317*0Sstevel@tonic-gate } 318*0Sstevel@tonic-gate 319*0Sstevel@tonic-gate /* create include rules for each file/dir arg */ 320*0Sstevel@tonic-gate while (optind < argc) 321*0Sstevel@tonic-gate errs |= add_include(bp, argv[ optind++ ]); 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate /* 324*0Sstevel@tonic-gate * evaluate the specified base on each side, 325*0Sstevel@tonic-gate * being careful to limit evaulation to new rules 326*0Sstevel@tonic-gate */ 327*0Sstevel@tonic-gate errs |= evaluate(bp, OPT_SRC, TRUE); 328*0Sstevel@tonic-gate errs |= evaluate(bp, OPT_DST, TRUE); 329*0Sstevel@tonic-gate } else { 330*0Sstevel@tonic-gate /* note any possible evaluation restrictions */ 331*0Sstevel@tonic-gate for (i = 0; i < num_restrs; i++) 332*0Sstevel@tonic-gate errs |= add_restr(rlist[i]); 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate /* 335*0Sstevel@tonic-gate * we can only prune the baseline file if we have done 336*0Sstevel@tonic-gate * a complete (unrestricted) analysis. 337*0Sstevel@tonic-gate */ 338*0Sstevel@tonic-gate if (i == 0) 339*0Sstevel@tonic-gate do_prune = 1; 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate /* evaulate each base on each side */ 342*0Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 343*0Sstevel@tonic-gate errs |= evaluate(bp, OPT_SRC, FALSE); 344*0Sstevel@tonic-gate errs |= evaluate(bp, OPT_DST, FALSE); 345*0Sstevel@tonic-gate } 346*0Sstevel@tonic-gate } 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate /* if anything serious happened, skip reconciliation */ 349*0Sstevel@tonic-gate if (errs & ERR_FATAL) { 350*0Sstevel@tonic-gate cleanup(errs); 351*0Sstevel@tonic-gate exit(errs); 352*0Sstevel@tonic-gate } 353*0Sstevel@tonic-gate 354*0Sstevel@tonic-gate /* analyze and deal with the differenecs */ 355*0Sstevel@tonic-gate errs |= analyze(); 356*0Sstevel@tonic-gate 357*0Sstevel@tonic-gate /* see if there is any dead-wood in the baseline */ 358*0Sstevel@tonic-gate if (do_prune) { 359*0Sstevel@tonic-gate c = prune(); 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate if (c > 0 && opt_verbose) 362*0Sstevel@tonic-gate fprintf(stdout, V_prunes, c); 363*0Sstevel@tonic-gate } 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate /* print out a final summary */ 366*0Sstevel@tonic-gate summary(); 367*0Sstevel@tonic-gate 368*0Sstevel@tonic-gate /* update the rules and baseline files (if needed) */ 369*0Sstevel@tonic-gate (void) umask(my_umask); 370*0Sstevel@tonic-gate errs |= write_baseline(file_base); 371*0Sstevel@tonic-gate errs |= write_rules(file_rules); 372*0Sstevel@tonic-gate 373*0Sstevel@tonic-gate if (opt_debug & DBG_MISC) 374*0Sstevel@tonic-gate fprintf(stderr, "MISC: EXIT=%s\n", showflags(errmap, errs)); 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate /* just returning ERR_RESOLVABLE upsets some people */ 377*0Sstevel@tonic-gate if (errs == ERR_RESOLVABLE && !opt_notouch) 378*0Sstevel@tonic-gate errs = 0; 379*0Sstevel@tonic-gate 380*0Sstevel@tonic-gate /* all done */ 381*0Sstevel@tonic-gate cleanup(0); 382*0Sstevel@tonic-gate exit(errs); 383*0Sstevel@tonic-gate } 384*0Sstevel@tonic-gate 385*0Sstevel@tonic-gate 386*0Sstevel@tonic-gate /* 387*0Sstevel@tonic-gate * routine: 388*0Sstevel@tonic-gate * usage 389*0Sstevel@tonic-gate * 390*0Sstevel@tonic-gate * purpose: 391*0Sstevel@tonic-gate * print out a usage message 392*0Sstevel@tonic-gate * 393*0Sstevel@tonic-gate * parameters: 394*0Sstevel@tonic-gate * none 395*0Sstevel@tonic-gate * 396*0Sstevel@tonic-gate * returns: 397*0Sstevel@tonic-gate * none 398*0Sstevel@tonic-gate * 399*0Sstevel@tonic-gate * note: 400*0Sstevel@tonic-gate * the -D and -E switches are for development/test/support 401*0Sstevel@tonic-gate * use only and do not show up in the general usage message. 402*0Sstevel@tonic-gate */ 403*0Sstevel@tonic-gate static void 404*0Sstevel@tonic-gate usage(void) 405*0Sstevel@tonic-gate { 406*0Sstevel@tonic-gate fprintf(stderr, "%s\t%s %s\n", gettext(ERR_usage), "filesync", 407*0Sstevel@tonic-gate gettext(USE_simple)); 408*0Sstevel@tonic-gate fprintf(stderr, "\t%s %s\n", "filesync", gettext(USE_all)); 409*0Sstevel@tonic-gate fprintf(stderr, "\t-a .......... %s\n", gettext(USE_a)); 410*0Sstevel@tonic-gate fprintf(stderr, "\t-e .......... %s\n", gettext(USE_e)); 411*0Sstevel@tonic-gate fprintf(stderr, "\t-h .......... %s\n", gettext(USE_h)); 412*0Sstevel@tonic-gate fprintf(stderr, "\t-m .......... %s\n", gettext(USE_m)); 413*0Sstevel@tonic-gate fprintf(stderr, "\t-n .......... %s\n", gettext(USE_n)); 414*0Sstevel@tonic-gate fprintf(stderr, "\t-q .......... %s\n", gettext(USE_q)); 415*0Sstevel@tonic-gate fprintf(stderr, "\t-v .......... %s\n", gettext(USE_v)); 416*0Sstevel@tonic-gate fprintf(stderr, "\t-y .......... %s\n", gettext(USE_y)); 417*0Sstevel@tonic-gate fprintf(stderr, "\t-s dir ...... %s\n", gettext(USE_s)); 418*0Sstevel@tonic-gate fprintf(stderr, "\t-d dir ...... %s\n", gettext(USE_d)); 419*0Sstevel@tonic-gate fprintf(stderr, "\t-r dir ...... %s\n", gettext(USE_r)); 420*0Sstevel@tonic-gate fprintf(stderr, "\t-f [sdon].... %s\n", gettext(USE_f)); 421*0Sstevel@tonic-gate fprintf(stderr, "\t-o src/dst... %s\n", gettext(USE_o)); 422*0Sstevel@tonic-gate } 423*0Sstevel@tonic-gate 424*0Sstevel@tonic-gate /* 425*0Sstevel@tonic-gate * routine: 426*0Sstevel@tonic-gate * confirm 427*0Sstevel@tonic-gate * 428*0Sstevel@tonic-gate * purpose: 429*0Sstevel@tonic-gate * to confirm that the user is willing to do something dangerous 430*0Sstevel@tonic-gate * 431*0Sstevel@tonic-gate * parameters: 432*0Sstevel@tonic-gate * warning message to be printed 433*0Sstevel@tonic-gate * 434*0Sstevel@tonic-gate * returns: 435*0Sstevel@tonic-gate * void 436*0Sstevel@tonic-gate * 437*0Sstevel@tonic-gate * notes: 438*0Sstevel@tonic-gate * if this is a "notouch" or if the user has pre-confirmed, 439*0Sstevel@tonic-gate * we should not obtain the confirmation and just return that 440*0Sstevel@tonic-gate * the user has confirmed. 441*0Sstevel@tonic-gate */ 442*0Sstevel@tonic-gate void 443*0Sstevel@tonic-gate confirm(char *message) 444*0Sstevel@tonic-gate { FILE *ttyi, *ttyo; 445*0Sstevel@tonic-gate char ansbuf[ MAX_LINE ]; 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate /* if user pre-confirmed, we don't have to ask */ 448*0Sstevel@tonic-gate if (opt_yes || opt_notouch) 449*0Sstevel@tonic-gate return; 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate ttyo = fopen("/dev/tty", "w"); 452*0Sstevel@tonic-gate ttyi = fopen("/dev/tty", "r"); 453*0Sstevel@tonic-gate if (ttyi == NULL || ttyo == NULL) 454*0Sstevel@tonic-gate exit(ERR_OTHER); 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate /* explain the problem and prompt for confirmation */ 457*0Sstevel@tonic-gate fprintf(ttyo, message); 458*0Sstevel@tonic-gate fprintf(ttyo, gettext(WARN_proceed)); 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate /* if the user doesn't kill us, we can continue */ 461*0Sstevel@tonic-gate (void) fgets(ansbuf, sizeof (ansbuf), ttyi); 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate /* close the files and return */ 464*0Sstevel@tonic-gate (void) fclose(ttyi); 465*0Sstevel@tonic-gate (void) fclose(ttyo); 466*0Sstevel@tonic-gate } 467*0Sstevel@tonic-gate 468*0Sstevel@tonic-gate void 469*0Sstevel@tonic-gate nomem(char *reason) 470*0Sstevel@tonic-gate { 471*0Sstevel@tonic-gate fprintf(stderr, gettext(ERR_nomem), reason); 472*0Sstevel@tonic-gate exit(ERR_OTHER); 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate 475*0Sstevel@tonic-gate /* 476*0Sstevel@tonic-gate * routine: 477*0Sstevel@tonic-gate * findfiles 478*0Sstevel@tonic-gate * 479*0Sstevel@tonic-gate * purpose: 480*0Sstevel@tonic-gate * to locate our baseline and rules files 481*0Sstevel@tonic-gate * 482*0Sstevel@tonic-gate * parameters: 483*0Sstevel@tonic-gate * none 484*0Sstevel@tonic-gate * 485*0Sstevel@tonic-gate * returns: 486*0Sstevel@tonic-gate * error mask 487*0Sstevel@tonic-gate * settings of file_base and file_rules 488*0Sstevel@tonic-gate * 489*0Sstevel@tonic-gate * side-effects: 490*0Sstevel@tonic-gate * in order to keep multiple filesyncs from running in parallel 491*0Sstevel@tonic-gate * we put an advisory lock on the baseline file. If the baseline 492*0Sstevel@tonic-gate * file does not exist we create one. The unlocking (and deletion 493*0Sstevel@tonic-gate * of extraneous baselines) is handled in cleanup. 494*0Sstevel@tonic-gate */ 495*0Sstevel@tonic-gate static errmask_t 496*0Sstevel@tonic-gate findfiles(void) /* find rule and baseline files */ 497*0Sstevel@tonic-gate { char *s, *where; 498*0Sstevel@tonic-gate char namebuf[MAX_PATH]; 499*0Sstevel@tonic-gate int ret; 500*0Sstevel@tonic-gate errmask_t errs = 0; 501*0Sstevel@tonic-gate 502*0Sstevel@tonic-gate /* figure out where the files should be located */ 503*0Sstevel@tonic-gate s = getenv("FILESYNC"); 504*0Sstevel@tonic-gate where = (s && *s) ? expand(s) : expand(DFLT_PRFX); 505*0Sstevel@tonic-gate 506*0Sstevel@tonic-gate /* see if we got a viable name */ 507*0Sstevel@tonic-gate if (where == 0) { 508*0Sstevel@tonic-gate fprintf(stderr, gettext(ERR_nofsync)); 509*0Sstevel@tonic-gate return (ERR_FILES); 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate /* try to form the name of the rules file */ 513*0Sstevel@tonic-gate strcpy(namebuf, where); 514*0Sstevel@tonic-gate strcat(namebuf, SUFX_RULES); 515*0Sstevel@tonic-gate s = strdup(namebuf); 516*0Sstevel@tonic-gate errs = check_access(namebuf, &new_rules); 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate /* if we cannot find a proper rules file, look in the old place */ 519*0Sstevel@tonic-gate if (new_rules && errs == 0) { 520*0Sstevel@tonic-gate strcpy(namebuf, where); 521*0Sstevel@tonic-gate strcat(namebuf, SUFX_OLD); 522*0Sstevel@tonic-gate file_rules = strdup(namebuf); 523*0Sstevel@tonic-gate errs = check_access(namebuf, &new_rules); 524*0Sstevel@tonic-gate 525*0Sstevel@tonic-gate /* if we couldn't find that either, go with new name */ 526*0Sstevel@tonic-gate if (new_rules && errs == 0) 527*0Sstevel@tonic-gate file_rules = s; 528*0Sstevel@tonic-gate } else 529*0Sstevel@tonic-gate file_rules = s; 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate /* try to form the name of the baseline file */ 532*0Sstevel@tonic-gate strcpy(namebuf, where); 533*0Sstevel@tonic-gate strcat(namebuf, SUFX_BASE); 534*0Sstevel@tonic-gate file_base = strdup(namebuf); 535*0Sstevel@tonic-gate errs |= check_access(namebuf, &new_baseline); 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate if (opt_debug & DBG_FILES) { 538*0Sstevel@tonic-gate fprintf(stderr, "FILE: %s rules file: %s\n", 539*0Sstevel@tonic-gate new_rules ? "new" : "existing", file_rules); 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate fprintf(stderr, "FILE: %s base file: %s\n", 542*0Sstevel@tonic-gate new_baseline ? "new" : "existing", file_base); 543*0Sstevel@tonic-gate } 544*0Sstevel@tonic-gate 545*0Sstevel@tonic-gate /* 546*0Sstevel@tonic-gate * in order to lock out other filesync programs we need some 547*0Sstevel@tonic-gate * file we can lock. We do an advisory lock on the baseline 548*0Sstevel@tonic-gate * file. If no baseline file exists, we create an empty one. 549*0Sstevel@tonic-gate */ 550*0Sstevel@tonic-gate if (new_baseline) 551*0Sstevel@tonic-gate lockfd = creat(file_base, 0666); 552*0Sstevel@tonic-gate else 553*0Sstevel@tonic-gate lockfd = open(file_base, O_RDWR); 554*0Sstevel@tonic-gate 555*0Sstevel@tonic-gate if (lockfd < 0) { 556*0Sstevel@tonic-gate fprintf(stderr, new_baseline ? ERR_creat : ERR_open, 557*0Sstevel@tonic-gate TXT_base, file_base); 558*0Sstevel@tonic-gate errs |= ERR_FILES; 559*0Sstevel@tonic-gate } else { 560*0Sstevel@tonic-gate ret = lockf(lockfd, F_TLOCK, 0L); 561*0Sstevel@tonic-gate if (ret < 0) { 562*0Sstevel@tonic-gate fprintf(stderr, ERR_lock, TXT_base, file_base); 563*0Sstevel@tonic-gate errs |= ERR_FILES; 564*0Sstevel@tonic-gate } else if (opt_debug & DBG_FILES) 565*0Sstevel@tonic-gate fprintf(stderr, "FILE: locking baseline file %s\n", 566*0Sstevel@tonic-gate file_base); 567*0Sstevel@tonic-gate } 568*0Sstevel@tonic-gate 569*0Sstevel@tonic-gate return (errs); 570*0Sstevel@tonic-gate } 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate /* 573*0Sstevel@tonic-gate * routine: 574*0Sstevel@tonic-gate * cleanup 575*0Sstevel@tonic-gate * 576*0Sstevel@tonic-gate * purpose: 577*0Sstevel@tonic-gate * to clean up temporary files and locking prior to exit 578*0Sstevel@tonic-gate * 579*0Sstevel@tonic-gate * paremeters: 580*0Sstevel@tonic-gate * error mask 581*0Sstevel@tonic-gate * 582*0Sstevel@tonic-gate * returns: 583*0Sstevel@tonic-gate * void 584*0Sstevel@tonic-gate * 585*0Sstevel@tonic-gate * notes: 586*0Sstevel@tonic-gate * if there are no errors, the baseline file is assumed to be good. 587*0Sstevel@tonic-gate * Otherwise, if we created a temporary baseline file (just for 588*0Sstevel@tonic-gate * locking) we will delete it. 589*0Sstevel@tonic-gate */ 590*0Sstevel@tonic-gate static void 591*0Sstevel@tonic-gate cleanup(errmask_t errmask) 592*0Sstevel@tonic-gate { 593*0Sstevel@tonic-gate /* unlock the baseline file */ 594*0Sstevel@tonic-gate if (opt_debug & DBG_FILES) 595*0Sstevel@tonic-gate fprintf(stderr, "FILE: unlock baseline file %s\n", file_base); 596*0Sstevel@tonic-gate (void) lockf(lockfd, F_ULOCK, 0); 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate /* see if we need to delete a temporary copy */ 599*0Sstevel@tonic-gate if (errmask && new_baseline) { 600*0Sstevel@tonic-gate if (opt_debug & DBG_FILES) 601*0Sstevel@tonic-gate fprintf(stderr, "FILE: unlink temp baseline file %s\n", 602*0Sstevel@tonic-gate file_base); 603*0Sstevel@tonic-gate (void) unlink(file_base); 604*0Sstevel@tonic-gate } 605*0Sstevel@tonic-gate } 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate /* 608*0Sstevel@tonic-gate * routine: 609*0Sstevel@tonic-gate * check_access 610*0Sstevel@tonic-gate * 611*0Sstevel@tonic-gate * purpose: 612*0Sstevel@tonic-gate * to determine whether or not we can access an existing file 613*0Sstevel@tonic-gate * or create a new one 614*0Sstevel@tonic-gate * 615*0Sstevel@tonic-gate * parameters: 616*0Sstevel@tonic-gate * name of file (in a clobberable buffer) 617*0Sstevel@tonic-gate * pointer to new file flag 618*0Sstevel@tonic-gate * 619*0Sstevel@tonic-gate * returns: 620*0Sstevel@tonic-gate * error mask 621*0Sstevel@tonic-gate * setting of the new file flag 622*0Sstevel@tonic-gate * 623*0Sstevel@tonic-gate * note: 624*0Sstevel@tonic-gate * it is kind of a kluge that this routine clobbers the name, 625*0Sstevel@tonic-gate * but it is only called from one place, it needs a modified 626*0Sstevel@tonic-gate * copy of the name, and the one caller doesn't mind. 627*0Sstevel@tonic-gate */ 628*0Sstevel@tonic-gate static errmask_t 629*0Sstevel@tonic-gate check_access(char *name, int *newflag) 630*0Sstevel@tonic-gate { char *s; 631*0Sstevel@tonic-gate 632*0Sstevel@tonic-gate /* start out by asking for what we want */ 633*0Sstevel@tonic-gate if (access(name, R_OK|W_OK) == 0) { 634*0Sstevel@tonic-gate *newflag = 0; 635*0Sstevel@tonic-gate return (0); 636*0Sstevel@tonic-gate } 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gate /* if the problem is isn't non-existance, lose */ 639*0Sstevel@tonic-gate if (errno != ENOENT) { 640*0Sstevel@tonic-gate *newflag = 0; 641*0Sstevel@tonic-gate fprintf(stderr, gettext(ERR_rdwri), name); 642*0Sstevel@tonic-gate return (ERR_FILES); 643*0Sstevel@tonic-gate } 644*0Sstevel@tonic-gate 645*0Sstevel@tonic-gate /* 646*0Sstevel@tonic-gate * the file doesn't exist, so there is still hope if we can 647*0Sstevel@tonic-gate * write in the directory that should contain the file 648*0Sstevel@tonic-gate */ 649*0Sstevel@tonic-gate *newflag = 1; 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gate /* truncate the file name to its containing directory */ 652*0Sstevel@tonic-gate for (s = name; s[1]; s++); 653*0Sstevel@tonic-gate while (s > name && *s != '/') 654*0Sstevel@tonic-gate s--; 655*0Sstevel@tonic-gate if (s > name) 656*0Sstevel@tonic-gate *s = 0; 657*0Sstevel@tonic-gate else if (*s == '/') 658*0Sstevel@tonic-gate s[1] = 0; 659*0Sstevel@tonic-gate else 660*0Sstevel@tonic-gate name = "."; 661*0Sstevel@tonic-gate 662*0Sstevel@tonic-gate /* then see if we have write access to the directory */ 663*0Sstevel@tonic-gate if (access(name, W_OK) == 0) 664*0Sstevel@tonic-gate return (0); 665*0Sstevel@tonic-gate 666*0Sstevel@tonic-gate fprintf(stderr, gettext(ERR_dirwac), name); 667*0Sstevel@tonic-gate return (ERR_FILES); 668*0Sstevel@tonic-gate } 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate /* 671*0Sstevel@tonic-gate * routine: 672*0Sstevel@tonic-gate * whoami 673*0Sstevel@tonic-gate * 674*0Sstevel@tonic-gate * purpose: 675*0Sstevel@tonic-gate * to figure out who I am and what the default modes/ownership 676*0Sstevel@tonic-gate * is on files that I create. 677*0Sstevel@tonic-gate */ 678*0Sstevel@tonic-gate static void 679*0Sstevel@tonic-gate whoami() 680*0Sstevel@tonic-gate { 681*0Sstevel@tonic-gate my_uid = geteuid(); 682*0Sstevel@tonic-gate my_gid = getegid(); 683*0Sstevel@tonic-gate my_umask = umask(0); 684*0Sstevel@tonic-gate 685*0Sstevel@tonic-gate if (opt_debug & DBG_MISC) 686*0Sstevel@tonic-gate fprintf(stderr, "MISC: my_uid=%ld, my_gid=%ld, my_umask=%03o\n", 687*0Sstevel@tonic-gate my_uid, my_gid, my_umask); 688*0Sstevel@tonic-gate } 689