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