4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
31 * argument handling and top level dispatch
34 * main argument handling and main loop
35 * usage (static) print out usage message
36 * confirm prompt the user for a confirmation and get it
37 * nomem fatal error handler for malloc failures
38 * findfiles (static) locate our baseline and rules files
39 * cleanup (static) unlock baseline and delete temp file
40 * check_access (static) do we have adequate access to a file/directory
41 * whoami (static) get uid/gid/umask
44 #pragma ident "%Z%%M% %I% %E% SMI"
61 * local routines in this module:
63 static errmask_t
findfiles(); /* find rule and baseline files */
64 static void cleanup(int); /* cleanup locks and temps */
65 static errmask_t
check_access(char *, int *); /* check access to file */
66 static void whoami(); /* gather information about me */
67 static void usage(void); /* general usage */
71 * globals exported to the rest of the program
73 bool_t opt_mtime
; /* preserve modification times on propagations */
74 bool_t opt_notouch
; /* don't actually make any changes */
75 bool_t opt_quiet
; /* disable reconciliation command output */
76 bool_t opt_verbose
; /* enable analysis descriptions */
77 side_t opt_force
; /* designated winner for conflicts */
78 side_t opt_oneway
; /* one way only propagation */
79 side_t opt_onesided
; /* permit one-sided evaluation */
80 bool_t opt_everything
; /* everything must agree (modes/uid/gid) */
81 bool_t opt_yes
; /* pre-confirm massive deletions are OK */
82 bool_t opt_acls
; /* always scan for acls on all files */
83 bool_t opt_errors
; /* simulate errors on specified files */
84 bool_t opt_halt
; /* halt on propagation errors */
85 dbgmask_t opt_debug
; /* debug mask */
87 uid_t my_uid
; /* default UID for files I create */
88 gid_t my_gid
; /* default GID for files I create */
90 static char *file_rules
; /* name of rules file */
91 static char *file_base
; /* name of baseline file */
93 static int new_baseline
; /* are we creating a new baseline */
94 static int new_rules
; /* are we creating a new rules file */
95 static int my_umask
; /* default UMASK for files I create */
96 static int lockfd
; /* file descriptor for locking baseline */
98 static char *rlist
[MAX_RLIST
];
99 static int num_restrs
= 0;
106 * argument processing and primary dispatch
109 * error codes per filesync.1 (ERR_* in filesync.h)
112 * read filesync.1 in order to understand the argument processing
114 * most of the command line options just set some opt_ global
115 * variable that is later looked at by the code that actually
116 * implements the features. Only file names are really processed
120 main(int argc
, char **argv
)
123 errmask_t errs
= ERR_OK
;
129 /* keep the error messages simple */
130 argv
[0] = "filesync";
132 /* gather together all of the options */
133 while ((c
= getopt(argc
, argv
, "AaehmnqvyD:E:r:s:d:f:o:")) != EOF
)
135 case 'a': /* always scan for acls */
138 case 'e': /* everything agrees */
139 opt_everything
= TRUE
;
141 case 'h': /* halt on error */
144 case 'm': /* preserve modtimes */
147 case 'n': /* notouch */
150 case 'q': /* quiet */
153 case 'v': /* verbose */
159 case 'D': /* debug options */
160 if (!isdigit(optarg
[0])) {
164 opt_debug
|= strtol(optarg
, (char **)NULL
, 0);
167 case 'E': /* error simulation */
168 if (dbg_set_error(optarg
)) {
175 case 'f': /* force conflict resolution */
198 case 'o': /* one way propagation */
201 opt_oneway
= OPT_SRC
;
204 opt_oneway
= OPT_DST
;
215 case 'r': /* restricted reconciliation */
216 if (num_restrs
< MAX_RLIST
)
217 rlist
[ num_restrs
++ ] = optarg
;
219 fprintf(stderr
, gettext(ERR_tomany
),
226 if ((srcname
= qualify(optarg
)) == 0)
231 if ((dstname
= qualify(optarg
)) == 0)
241 if (opt_debug
& DBG_MISC
)
242 fprintf(stderr
, "MISC: DBG=%s\n", showflags(dbgmap
, opt_debug
));
244 /* if we have file names, we need a source and destination */
247 fprintf(stderr
, gettext(ERR_nosrc
));
251 fprintf(stderr
, gettext(ERR_nodst
));
256 /* check for simple usage errors */
257 if (errs
& ERR_INVAL
) {
262 /* locate our baseline and rules files */
266 /* figure out file creation defaults */
269 /* read in our initial baseline */
270 if (!new_baseline
&& (c
= read_baseline(file_base
)))
273 /* read in the rules file if we need or have rules */
274 if (optind
>= argc
&& new_rules
) {
275 fprintf(stderr
, ERR_nonames
);
277 } else if (!new_rules
)
278 errs
|= read_rules(file_rules
);
280 /* if anything has failed with our setup, go no further */
287 * figure out whether or not we are willing to do a one-sided
288 * analysis (where we don't even look at the other side. This
289 * is an "I'm just curious what has changed" query, and we are
290 * only willing to do it if:
291 * we aren't actually going to do anything
292 * we have a baseline we can compare against
293 * otherwise, we are going to insist on being able to access
294 * both the source and destination.
296 if (opt_notouch
&& !new_baseline
)
297 opt_onesided
= opt_oneway
;
300 * there are two interested usage scenarios:
301 * file names specified
302 * create new rules for the specified files
303 * evaulate and reconcile only the specified files
304 * no file names specified
305 * use already existing rules
306 * consider restricting them to specified subdirs/files
309 /* figure out what base pair we're working on */
310 bp
= add_base(srcname
, dstname
);
312 /* perverse default rules to avoid trouble */
314 errs
|= add_ignore(0, SUFX_RULES
);
315 errs
|= add_ignore(0, SUFX_BASE
);
318 /* create include rules for each file/dir arg */
319 while (optind
< argc
)
320 errs
|= add_include(bp
, argv
[ optind
++ ]);
323 * evaluate the specified base on each side,
324 * being careful to limit evaulation to new rules
326 errs
|= evaluate(bp
, OPT_SRC
, TRUE
);
327 errs
|= evaluate(bp
, OPT_DST
, TRUE
);
329 /* note any possible evaluation restrictions */
330 for (i
= 0; i
< num_restrs
; i
++)
331 errs
|= add_restr(rlist
[i
]);
334 * we can only prune the baseline file if we have done
335 * a complete (unrestricted) analysis.
340 /* evaulate each base on each side */
341 for (bp
= bases
; bp
; bp
= bp
->b_next
) {
342 errs
|= evaluate(bp
, OPT_SRC
, FALSE
);
343 errs
|= evaluate(bp
, OPT_DST
, FALSE
);
347 /* if anything serious happened, skip reconciliation */
348 if (errs
& ERR_FATAL
) {
353 /* analyze and deal with the differenecs */
356 /* see if there is any dead-wood in the baseline */
360 if (c
> 0 && opt_verbose
)
361 fprintf(stdout
, V_prunes
, c
);
364 /* print out a final summary */
367 /* update the rules and baseline files (if needed) */
368 (void) umask(my_umask
);
369 errs
|= write_baseline(file_base
);
370 errs
|= write_rules(file_rules
);
372 if (opt_debug
& DBG_MISC
)
373 fprintf(stderr
, "MISC: EXIT=%s\n", showflags(errmap
, errs
));
375 /* just returning ERR_RESOLVABLE upsets some people */
376 if (errs
== ERR_RESOLVABLE
&& !opt_notouch
)
390 * print out a usage message
399 * the -D and -E switches are for development/test/support
400 * use only and do not show up in the general usage message.
405 fprintf(stderr
, "%s\t%s %s\n", gettext(ERR_usage
), "filesync",
406 gettext(USE_simple
));
407 fprintf(stderr
, "\t%s %s\n", "filesync", gettext(USE_all
));
408 fprintf(stderr
, "\t-a .......... %s\n", gettext(USE_a
));
409 fprintf(stderr
, "\t-e .......... %s\n", gettext(USE_e
));
410 fprintf(stderr
, "\t-h .......... %s\n", gettext(USE_h
));
411 fprintf(stderr
, "\t-m .......... %s\n", gettext(USE_m
));
412 fprintf(stderr
, "\t-n .......... %s\n", gettext(USE_n
));
413 fprintf(stderr
, "\t-q .......... %s\n", gettext(USE_q
));
414 fprintf(stderr
, "\t-v .......... %s\n", gettext(USE_v
));
415 fprintf(stderr
, "\t-y .......... %s\n", gettext(USE_y
));
416 fprintf(stderr
, "\t-s dir ...... %s\n", gettext(USE_s
));
417 fprintf(stderr
, "\t-d dir ...... %s\n", gettext(USE_d
));
418 fprintf(stderr
, "\t-r dir ...... %s\n", gettext(USE_r
));
419 fprintf(stderr
, "\t-f [sdon].... %s\n", gettext(USE_f
));
420 fprintf(stderr
, "\t-o src/dst... %s\n", gettext(USE_o
));
428 * to confirm that the user is willing to do something dangerous
431 * warning message to be printed
437 * if this is a "notouch" or if the user has pre-confirmed,
438 * we should not obtain the confirmation and just return that
439 * the user has confirmed.
442 confirm(char *message
)
444 char ansbuf
[ MAX_LINE
];
446 /* if user pre-confirmed, we don't have to ask */
447 if (opt_yes
|| opt_notouch
)
450 ttyo
= fopen("/dev/tty", "w");
451 ttyi
= fopen("/dev/tty", "r");
452 if (ttyi
== NULL
|| ttyo
== NULL
)
455 /* explain the problem and prompt for confirmation */
456 fprintf(ttyo
, message
);
457 fprintf(ttyo
, gettext(WARN_proceed
));
459 /* if the user doesn't kill us, we can continue */
460 (void) fgets(ansbuf
, sizeof (ansbuf
), ttyi
);
462 /* close the files and return */
470 fprintf(stderr
, gettext(ERR_nomem
), reason
);
479 * to locate our baseline and rules files
486 * settings of file_base and file_rules
489 * in order to keep multiple filesyncs from running in parallel
490 * we put an advisory lock on the baseline file. If the baseline
491 * file does not exist we create one. The unlocking (and deletion
492 * of extraneous baselines) is handled in cleanup.
495 findfiles(void) /* find rule and baseline files */
497 char namebuf
[MAX_PATH
];
501 /* figure out where the files should be located */
502 s
= getenv("FILESYNC");
503 where
= (s
&& *s
) ? expand(s
) : expand(DFLT_PRFX
);
505 /* see if we got a viable name */
507 fprintf(stderr
, gettext(ERR_nofsync
));
511 /* try to form the name of the rules file */
512 strcpy(namebuf
, where
);
513 strcat(namebuf
, SUFX_RULES
);
515 errs
= check_access(namebuf
, &new_rules
);
517 /* if we cannot find a proper rules file, look in the old place */
518 if (new_rules
&& errs
== 0) {
519 strcpy(namebuf
, where
);
520 strcat(namebuf
, SUFX_OLD
);
521 file_rules
= strdup(namebuf
);
522 errs
= check_access(namebuf
, &new_rules
);
524 /* if we couldn't find that either, go with new name */
525 if (new_rules
&& errs
== 0)
530 /* try to form the name of the baseline file */
531 strcpy(namebuf
, where
);
532 strcat(namebuf
, SUFX_BASE
);
533 file_base
= strdup(namebuf
);
534 errs
|= check_access(namebuf
, &new_baseline
);
536 if (opt_debug
& DBG_FILES
) {
537 fprintf(stderr
, "FILE: %s rules file: %s\n",
538 new_rules
? "new" : "existing", file_rules
);
540 fprintf(stderr
, "FILE: %s base file: %s\n",
541 new_baseline
? "new" : "existing", file_base
);
545 * in order to lock out other filesync programs we need some
546 * file we can lock. We do an advisory lock on the baseline
547 * file. If no baseline file exists, we create an empty one.
550 lockfd
= creat(file_base
, 0666);
552 lockfd
= open(file_base
, O_RDWR
);
555 fprintf(stderr
, new_baseline
? ERR_creat
: ERR_open
,
556 TXT_base
, file_base
);
559 ret
= lockf(lockfd
, F_TLOCK
, 0L);
561 fprintf(stderr
, ERR_lock
, TXT_base
, file_base
);
563 } else if (opt_debug
& DBG_FILES
)
564 fprintf(stderr
, "FILE: locking baseline file %s\n",
576 * to clean up temporary files and locking prior to exit
585 * if there are no errors, the baseline file is assumed to be good.
586 * Otherwise, if we created a temporary baseline file (just for
587 * locking) we will delete it.
590 cleanup(errmask_t errmask
)
592 /* unlock the baseline file */
593 if (opt_debug
& DBG_FILES
)
594 fprintf(stderr
, "FILE: unlock baseline file %s\n", file_base
);
595 (void) lockf(lockfd
, F_ULOCK
, 0);
597 /* see if we need to delete a temporary copy */
598 if (errmask
&& new_baseline
) {
599 if (opt_debug
& DBG_FILES
)
600 fprintf(stderr
, "FILE: unlink temp baseline file %s\n",
602 (void) unlink(file_base
);
611 * to determine whether or not we can access an existing file
612 * or create a new one
615 * name of file (in a clobberable buffer)
616 * pointer to new file flag
620 * setting of the new file flag
623 * it is kind of a kluge that this routine clobbers the name,
624 * but it is only called from one place, it needs a modified
625 * copy of the name, and the one caller doesn't mind.
628 check_access(char *name
, int *newflag
)
631 /* start out by asking for what we want */
632 if (access(name
, R_OK
|W_OK
) == 0) {
637 /* if the problem is isn't non-existence, lose */
638 if (errno
!= ENOENT
) {
640 fprintf(stderr
, gettext(ERR_rdwri
), name
);
645 * the file doesn't exist, so there is still hope if we can
646 * write in the directory that should contain the file
650 /* truncate the file name to its containing directory */
651 for (s
= name
; s
[1]; s
++);
652 while (s
> name
&& *s
!= '/')
661 /* then see if we have write access to the directory */
662 if (access(name
, W_OK
) == 0)
665 fprintf(stderr
, gettext(ERR_dirwac
), name
);
674 * to figure out who I am and what the default modes/ownership
675 * is on files that I create.
684 if (opt_debug
& DBG_MISC
)
685 fprintf(stderr
, "MISC: my_uid=%u, my_gid=%u, my_umask=%03o\n",
686 my_uid
, my_gid
, my_umask
);