maint: revert "build: update gnulib submodule to latest"
[coreutils/ericb.git] / src / install.c
blob94534f81e6eb8756cb826d64220af62f8741e528
1 /* install - copy files and set attributes
2 Copyright (C) 1989-1991, 1995-2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 #include <signal.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #include <selinux/selinux.h>
27 #include <sys/wait.h>
29 #include "system.h"
30 #include "backupfile.h"
31 #include "error.h"
32 #include "cp-hash.h"
33 #include "copy.h"
34 #include "filenamecat.h"
35 #include "full-read.h"
36 #include "mkancesdirs.h"
37 #include "mkdir-p.h"
38 #include "modechange.h"
39 #include "prog-fprintf.h"
40 #include "quote.h"
41 #include "quotearg.h"
42 #include "savewd.h"
43 #include "stat-time.h"
44 #include "utimens.h"
45 #include "xstrtol.h"
47 /* The official name of this program (e.g., no `g' prefix). */
48 #define PROGRAM_NAME "install"
50 #define AUTHORS proper_name ("David MacKenzie")
52 static int selinux_enabled = 0;
53 static bool use_default_selinux_context = true;
55 #if ! HAVE_ENDGRENT
56 # define endgrent() ((void) 0)
57 #endif
59 #if ! HAVE_ENDPWENT
60 # define endpwent() ((void) 0)
61 #endif
63 #if ! HAVE_LCHOWN
64 # define lchown(name, uid, gid) chown (name, uid, gid)
65 #endif
67 #if ! HAVE_MATCHPATHCON_INIT_PREFIX
68 # define matchpathcon_init_prefix(a, p) /* empty */
69 #endif
71 /* The user name that will own the files, or NULL to make the owner
72 the current user ID. */
73 static char *owner_name;
75 /* The user ID corresponding to `owner_name'. */
76 static uid_t owner_id;
78 /* The group name that will own the files, or NULL to make the group
79 the current group ID. */
80 static char *group_name;
82 /* The group ID corresponding to `group_name'. */
83 static gid_t group_id;
85 #define DEFAULT_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
87 /* The file mode bits to which non-directory files will be set. The umask has
88 no effect. */
89 static mode_t mode = DEFAULT_MODE;
91 /* Similar, but for directories. */
92 static mode_t dir_mode = DEFAULT_MODE;
94 /* The file mode bits that the user cares about. This should be a
95 superset of DIR_MODE and a subset of CHMOD_MODE_BITS. This matters
96 for directories, since otherwise directories may keep their S_ISUID
97 or S_ISGID bits. */
98 static mode_t dir_mode_bits = CHMOD_MODE_BITS;
100 /* Compare files before installing (-C) */
101 static bool copy_only_if_needed;
103 /* If true, strip executable files after copying them. */
104 static bool strip_files;
106 /* If true, install a directory instead of a regular file. */
107 static bool dir_arg;
109 /* Program used to strip binaries, "strip" is default */
110 static char const *strip_program = "strip";
112 /* For long options that have no equivalent short option, use a
113 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
114 enum
116 PRESERVE_CONTEXT_OPTION = CHAR_MAX + 1,
117 STRIP_PROGRAM_OPTION
120 static struct option const long_options[] =
122 {"backup", optional_argument, NULL, 'b'},
123 {"compare", no_argument, NULL, 'C'},
124 {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
125 {"directory", no_argument, NULL, 'd'},
126 {"group", required_argument, NULL, 'g'},
127 {"mode", required_argument, NULL, 'm'},
128 {"no-target-directory", no_argument, NULL, 'T'},
129 {"owner", required_argument, NULL, 'o'},
130 {"preserve-timestamps", no_argument, NULL, 'p'},
131 {"preserve-context", no_argument, NULL, PRESERVE_CONTEXT_OPTION},
132 {"strip", no_argument, NULL, 's'},
133 {"strip-program", required_argument, NULL, STRIP_PROGRAM_OPTION},
134 {"suffix", required_argument, NULL, 'S'},
135 {"target-directory", required_argument, NULL, 't'},
136 {"verbose", no_argument, NULL, 'v'},
137 {GETOPT_HELP_OPTION_DECL},
138 {GETOPT_VERSION_OPTION_DECL},
139 {NULL, 0, NULL, 0}
142 /* Compare content of opened files using file descriptors A_FD and B_FD. Return
143 true if files are equal. */
144 static bool
145 have_same_content (int a_fd, int b_fd)
147 enum { CMP_BLOCK_SIZE = 4096 };
148 static char a_buff[CMP_BLOCK_SIZE];
149 static char b_buff[CMP_BLOCK_SIZE];
151 size_t size;
152 while (0 < (size = full_read (a_fd, a_buff, sizeof a_buff))) {
153 if (size != full_read (b_fd, b_buff, sizeof b_buff))
154 return false;
156 if (memcmp (a_buff, b_buff, size) != 0)
157 return false;
160 return size == 0;
163 /* Return true for mode with non-permission bits. */
164 static bool
165 extra_mode (mode_t input)
167 mode_t mask = S_IRWXUGO | S_IFMT;
168 return !! (input & ~ mask);
171 /* Return true if copy of file SRC_NAME to file DEST_NAME is necessary. */
172 static bool
173 need_copy (const char *src_name, const char *dest_name,
174 const struct cp_options *x)
176 struct stat src_sb, dest_sb;
177 int src_fd, dest_fd;
178 bool content_match;
180 if (extra_mode (mode))
181 return true;
183 /* compare files using stat */
184 if (lstat (src_name, &src_sb) != 0)
185 return true;
187 if (lstat (dest_name, &dest_sb) != 0)
188 return true;
190 if (!S_ISREG (src_sb.st_mode) || !S_ISREG (dest_sb.st_mode)
191 || extra_mode (src_sb.st_mode) || extra_mode (dest_sb.st_mode))
192 return true;
194 if (src_sb.st_size != dest_sb.st_size
195 || (dest_sb.st_mode & CHMOD_MODE_BITS) != mode)
196 return true;
198 if (owner_id == (uid_t) -1)
200 errno = 0;
201 uid_t ruid = getuid ();
202 if ((ruid == (uid_t) -1 && errno) || dest_sb.st_uid != ruid)
203 return true;
205 else if (dest_sb.st_uid != owner_id)
206 return true;
208 if (group_id == (uid_t) -1)
210 errno = 0;
211 gid_t rgid = getgid ();
212 if ((rgid == (uid_t) -1 && errno) || dest_sb.st_gid != rgid)
213 return true;
215 else if (dest_sb.st_gid != group_id)
216 return true;
218 /* compare SELinux context if preserving */
219 if (selinux_enabled && x->preserve_security_context)
221 security_context_t file_scontext = NULL;
222 security_context_t to_scontext = NULL;
223 bool scontext_match;
225 if (getfilecon (src_name, &file_scontext) == -1)
226 return true;
228 if (getfilecon (dest_name, &to_scontext) == -1)
230 freecon (file_scontext);
231 return true;
234 scontext_match = STREQ (file_scontext, to_scontext);
236 freecon (file_scontext);
237 freecon (to_scontext);
238 if (!scontext_match)
239 return true;
242 /* compare files content */
243 src_fd = open (src_name, O_RDONLY | O_BINARY);
244 if (src_fd < 0)
245 return true;
247 dest_fd = open (dest_name, O_RDONLY | O_BINARY);
248 if (dest_fd < 0)
250 close (src_fd);
251 return true;
254 content_match = have_same_content (src_fd, dest_fd);
256 close (src_fd);
257 close (dest_fd);
258 return !content_match;
261 static void
262 cp_option_init (struct cp_options *x)
264 cp_options_default (x);
265 x->copy_as_regular = true;
266 x->reflink_mode = REFLINK_NEVER;
267 x->dereference = DEREF_ALWAYS;
268 x->unlink_dest_before_opening = true;
269 x->unlink_dest_after_failed_open = false;
270 x->hard_link = false;
271 x->interactive = I_UNSPECIFIED;
272 x->move_mode = false;
273 x->one_file_system = false;
274 x->preserve_ownership = false;
275 x->preserve_links = false;
276 x->preserve_mode = false;
277 x->preserve_timestamps = false;
278 x->reduce_diagnostics=false;
279 x->data_copy_required = true;
280 x->require_preserve = false;
281 x->require_preserve_context = false;
282 x->require_preserve_xattr = false;
283 x->recursive = false;
284 x->sparse_mode = SPARSE_AUTO;
285 x->symbolic_link = false;
286 x->backup_type = no_backups;
288 /* Create destination files initially writable so we can run strip on them.
289 Although GNU strip works fine on read-only files, some others
290 would fail. */
291 x->set_mode = true;
292 x->mode = S_IRUSR | S_IWUSR;
293 x->stdin_tty = false;
295 x->open_dangling_dest_symlink = false;
296 x->update = false;
297 x->preserve_security_context = false;
298 x->preserve_xattr = false;
299 x->verbose = false;
300 x->dest_info = NULL;
301 x->src_info = NULL;
304 #ifdef ENABLE_MATCHPATHCON
305 /* Modify file context to match the specified policy.
306 If an error occurs the file will remain with the default directory
307 context. */
308 static void
309 setdefaultfilecon (char const *file)
311 struct stat st;
312 security_context_t scontext = NULL;
313 static bool first_call = true;
315 if (selinux_enabled != 1)
317 /* Indicate no context found. */
318 return;
320 if (lstat (file, &st) != 0)
321 return;
323 if (first_call && IS_ABSOLUTE_FILE_NAME (file))
325 /* Calling matchpathcon_init_prefix (NULL, "/first_component/")
326 is an optimization to minimize the expense of the following
327 matchpathcon call. Do it only once, just before the first
328 matchpathcon call. We *could* call matchpathcon_fini after
329 the final matchpathcon call, but that's not necessary, since
330 by then we're about to exit, and besides, the buffers it
331 would free are still reachable. */
332 char const *p0;
333 char const *p = file + 1;
334 while (ISSLASH (*p))
335 ++p;
337 /* Record final leading slash, for when FILE starts with two or more. */
338 p0 = p - 1;
340 if (*p)
342 char *prefix;
345 ++p;
347 while (*p && !ISSLASH (*p));
349 prefix = malloc (p - p0 + 2);
350 if (prefix)
352 stpcpy (stpncpy (prefix, p0, p - p0), "/");
353 matchpathcon_init_prefix (NULL, prefix);
354 free (prefix);
358 first_call = false;
360 /* If there's an error determining the context, or it has none,
361 return to allow default context */
362 if ((matchpathcon (file, st.st_mode, &scontext) != 0) ||
363 STREQ (scontext, "<<none>>"))
365 if (scontext != NULL)
366 freecon (scontext);
367 return;
370 if (lsetfilecon (file, scontext) < 0 && errno != ENOTSUP)
371 error (0, errno,
372 _("warning: %s: failed to change context to %s"),
373 quotearg_colon (file), scontext);
375 freecon (scontext);
376 return;
378 #else
379 static void
380 setdefaultfilecon (char const *file)
382 (void) file;
384 #endif
386 /* FILE is the last operand of this command. Return true if FILE is a
387 directory. But report an error there is a problem accessing FILE,
388 or if FILE does not exist but would have to refer to an existing
389 directory if it referred to anything at all. */
391 static bool
392 target_directory_operand (char const *file)
394 char const *b = last_component (file);
395 size_t blen = strlen (b);
396 bool looks_like_a_dir = (blen == 0 || ISSLASH (b[blen - 1]));
397 struct stat st;
398 int err = (stat (file, &st) == 0 ? 0 : errno);
399 bool is_a_dir = !err && S_ISDIR (st.st_mode);
400 if (err && err != ENOENT)
401 error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
402 if (is_a_dir < looks_like_a_dir)
403 error (EXIT_FAILURE, err, _("target %s is not a directory"), quote (file));
404 return is_a_dir;
407 /* Report that directory DIR was made, if OPTIONS requests this. */
408 static void
409 announce_mkdir (char const *dir, void *options)
411 struct cp_options const *x = options;
412 if (x->verbose)
413 prog_fprintf (stdout, _("creating directory %s"), quote (dir));
416 /* Make ancestor directory DIR, whose last file name component is
417 COMPONENT, with options OPTIONS. Assume the working directory is
418 COMPONENT's parent. */
419 static int
420 make_ancestor (char const *dir, char const *component, void *options)
422 int r = mkdir (component, DEFAULT_MODE);
423 if (r == 0)
424 announce_mkdir (dir, options);
425 return r;
428 /* Process a command-line file name, for the -d option. */
429 static int
430 process_dir (char *dir, struct savewd *wd, void *options)
432 return (make_dir_parents (dir, wd,
433 make_ancestor, options,
434 dir_mode, announce_mkdir,
435 dir_mode_bits, owner_id, group_id, false)
436 ? EXIT_SUCCESS
437 : EXIT_FAILURE);
440 /* Copy file FROM onto file TO, creating TO if necessary.
441 Return true if successful. */
443 static bool
444 copy_file (const char *from, const char *to, const struct cp_options *x)
446 bool copy_into_self;
448 if (copy_only_if_needed && !need_copy (from, to, x))
449 return true;
451 /* Allow installing from non-regular files like /dev/null.
452 Charles Karney reported that some Sun version of install allows that
453 and that sendmail's installation process relies on the behavior.
454 However, since !x->recursive, the call to "copy" will fail if FROM
455 is a directory. */
457 return copy (from, to, false, x, &copy_into_self, NULL);
460 /* Set the attributes of file or directory NAME.
461 Return true if successful. */
463 static bool
464 change_attributes (char const *name)
466 bool ok = false;
467 /* chown must precede chmod because on some systems,
468 chown clears the set[ug]id bits for non-superusers,
469 resulting in incorrect permissions.
470 On System V, users can give away files with chown and then not
471 be able to chmod them. So don't give files away.
473 We don't normally ignore errors from chown because the idea of
474 the install command is that the file is supposed to end up with
475 precisely the attributes that the user specified (or defaulted).
476 If the file doesn't end up with the group they asked for, they'll
477 want to know. */
479 if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1)
480 && lchown (name, owner_id, group_id) != 0)
481 error (0, errno, _("cannot change ownership of %s"), quote (name));
482 else if (chmod (name, mode) != 0)
483 error (0, errno, _("cannot change permissions of %s"), quote (name));
484 else
485 ok = true;
487 if (use_default_selinux_context)
488 setdefaultfilecon (name);
490 return ok;
493 /* Set the timestamps of file DEST to match those of SRC_SB.
494 Return true if successful. */
496 static bool
497 change_timestamps (struct stat const *src_sb, char const *dest)
499 struct timespec timespec[2];
500 timespec[0] = get_stat_atime (src_sb);
501 timespec[1] = get_stat_mtime (src_sb);
503 if (utimens (dest, timespec))
505 error (0, errno, _("cannot set time stamps for %s"), quote (dest));
506 return false;
508 return true;
511 /* Strip the symbol table from the file NAME.
512 We could dig the magic number out of the file first to
513 determine whether to strip it, but the header files and
514 magic numbers vary so much from system to system that making
515 it portable would be very difficult. Not worth the effort. */
517 static void
518 strip (char const *name)
520 int status;
521 pid_t pid = fork ();
523 switch (pid)
525 case -1:
526 error (EXIT_FAILURE, errno, _("fork system call failed"));
527 break;
528 case 0: /* Child. */
529 execlp (strip_program, strip_program, name, NULL);
530 error (EXIT_FAILURE, errno, _("cannot run %s"), strip_program);
531 break;
532 default: /* Parent. */
533 if (waitpid (pid, &status, 0) < 0)
534 error (EXIT_FAILURE, errno, _("waiting for strip"));
535 else if (! WIFEXITED (status) || WEXITSTATUS (status))
536 error (EXIT_FAILURE, 0, _("strip process terminated abnormally"));
537 break;
541 /* Initialize the user and group ownership of the files to install. */
543 static void
544 get_ids (void)
546 struct passwd *pw;
547 struct group *gr;
549 if (owner_name)
551 pw = getpwnam (owner_name);
552 if (pw == NULL)
554 unsigned long int tmp;
555 if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK
556 || UID_T_MAX < tmp)
557 error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name));
558 owner_id = tmp;
560 else
561 owner_id = pw->pw_uid;
562 endpwent ();
564 else
565 owner_id = (uid_t) -1;
567 if (group_name)
569 gr = getgrnam (group_name);
570 if (gr == NULL)
572 unsigned long int tmp;
573 if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK
574 || GID_T_MAX < tmp)
575 error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name));
576 group_id = tmp;
578 else
579 group_id = gr->gr_gid;
580 endgrent ();
582 else
583 group_id = (gid_t) -1;
586 void
587 usage (int status)
589 if (status != EXIT_SUCCESS)
590 fprintf (stderr, _("Try `%s --help' for more information.\n"),
591 program_name);
592 else
594 printf (_("\
595 Usage: %s [OPTION]... [-T] SOURCE DEST\n\
596 or: %s [OPTION]... SOURCE... DIRECTORY\n\
597 or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
598 or: %s [OPTION]... -d DIRECTORY...\n\
600 program_name, program_name, program_name, program_name);
601 fputs (_("\
603 This install program copies files (often just compiled) into destination\n\
604 locations you choose. If you want to download and install a ready-to-use\n\
605 package on a GNU/Linux system, you should instead be using a package manager\n\
606 like yum(1) or apt-get(1).\n\
608 In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\
609 the existing DIRECTORY, while setting permission modes and owner/group.\n\
610 In the 4th form, create all components of the given DIRECTORY(ies).\n\
612 "), stdout);
613 fputs (_("\
614 Mandatory arguments to long options are mandatory for short options too.\n\
615 "), stdout);
616 fputs (_("\
617 --backup[=CONTROL] make a backup of each existing destination file\n\
618 -b like --backup but does not accept an argument\n\
619 -c (ignored)\n\
620 -C, --compare compare each pair of source and destination files, and\n\
621 in some cases, do not modify the destination at all\n\
622 -d, --directory treat all arguments as directory names; create all\n\
623 components of the specified directories\n\
624 "), stdout);
625 fputs (_("\
626 -D create all leading components of DEST except the last,\n\
627 then copy SOURCE to DEST\n\
628 -g, --group=GROUP set group ownership, instead of process' current group\n\
629 -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\
630 -o, --owner=OWNER set ownership (super-user only)\n\
631 "), stdout);
632 fputs (_("\
633 -p, --preserve-timestamps apply access/modification times of SOURCE files\n\
634 to corresponding destination files\n\
635 -s, --strip strip symbol tables\n\
636 --strip-program=PROGRAM program used to strip binaries\n\
637 -S, --suffix=SUFFIX override the usual backup suffix\n\
638 -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
639 -T, --no-target-directory treat DEST as a normal file\n\
640 -v, --verbose print the name of each directory as it is created\n\
641 "), stdout);
642 fputs (_("\
643 --preserve-context preserve SELinux security context\n\
644 -Z, --context=CONTEXT set SELinux security context of files and directories\
646 "), stdout);
648 fputs (HELP_OPTION_DESCRIPTION, stdout);
649 fputs (VERSION_OPTION_DESCRIPTION, stdout);
650 fputs (_("\
652 The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
653 The version control method may be selected via the --backup option or through\n\
654 the VERSION_CONTROL environment variable. Here are the values:\n\
656 "), stdout);
657 fputs (_("\
658 none, off never make backups (even if --backup is given)\n\
659 numbered, t make numbered backups\n\
660 existing, nil numbered if numbered backups exist, simple otherwise\n\
661 simple, never always make simple backups\n\
662 "), stdout);
663 emit_ancillary_info ();
665 exit (status);
668 /* Copy file FROM onto file TO and give TO the appropriate
669 attributes.
670 Return true if successful. */
672 static bool
673 install_file_in_file (const char *from, const char *to,
674 const struct cp_options *x)
676 struct stat from_sb;
677 if (x->preserve_timestamps && stat (from, &from_sb) != 0)
679 error (0, errno, _("cannot stat %s"), quote (from));
680 return false;
682 if (! copy_file (from, to, x))
683 return false;
684 if (strip_files)
685 strip (to);
686 if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode))
687 && ! change_timestamps (&from_sb, to))
688 return false;
689 return change_attributes (to);
692 /* Copy file FROM onto file TO, creating any missing parent directories of TO.
693 Return true if successful. */
695 static bool
696 install_file_in_file_parents (char const *from, char *to,
697 struct cp_options *x)
699 bool save_working_directory =
700 ! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
701 int status = EXIT_SUCCESS;
703 struct savewd wd;
704 savewd_init (&wd);
705 if (! save_working_directory)
706 savewd_finish (&wd);
708 if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
710 error (0, errno, _("cannot create directory %s"), to);
711 status = EXIT_FAILURE;
714 if (save_working_directory)
716 int restore_result = savewd_restore (&wd, status);
717 int restore_errno = errno;
718 savewd_finish (&wd);
719 if (EXIT_SUCCESS < restore_result)
720 return false;
721 if (restore_result < 0 && status == EXIT_SUCCESS)
723 error (0, restore_errno, _("cannot create directory %s"), to);
724 return false;
728 return (status == EXIT_SUCCESS && install_file_in_file (from, to, x));
731 /* Copy file FROM into directory TO_DIR, keeping its same name,
732 and give the copy the appropriate attributes.
733 Return true if successful. */
735 static bool
736 install_file_in_dir (const char *from, const char *to_dir,
737 const struct cp_options *x)
739 const char *from_base = last_component (from);
740 char *to = file_name_concat (to_dir, from_base, NULL);
741 bool ret = install_file_in_file (from, to, x);
742 free (to);
743 return ret;
747 main (int argc, char **argv)
749 int optc;
750 int exit_status = EXIT_SUCCESS;
751 const char *specified_mode = NULL;
752 bool make_backups = false;
753 char *backup_suffix_string;
754 char *version_control_string = NULL;
755 bool mkdir_and_install = false;
756 struct cp_options x;
757 char const *target_directory = NULL;
758 bool no_target_directory = false;
759 int n_files;
760 char **file;
761 bool strip_program_specified = false;
762 security_context_t scontext = NULL;
763 /* set iff kernel has extra selinux system calls */
764 selinux_enabled = (0 < is_selinux_enabled ());
766 initialize_main (&argc, &argv);
767 set_program_name (argv[0]);
768 setlocale (LC_ALL, "");
769 bindtextdomain (PACKAGE, LOCALEDIR);
770 textdomain (PACKAGE);
772 atexit (close_stdin);
774 cp_option_init (&x);
776 owner_name = NULL;
777 group_name = NULL;
778 strip_files = false;
779 dir_arg = false;
780 umask (0);
782 /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
783 we'll actually use backup_suffix_string. */
784 backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
786 while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z:", long_options,
787 NULL)) != -1)
789 switch (optc)
791 case 'b':
792 make_backups = true;
793 if (optarg)
794 version_control_string = optarg;
795 break;
796 case 'c':
797 break;
798 case 'C':
799 copy_only_if_needed = true;
800 break;
801 case 's':
802 strip_files = true;
803 #ifdef SIGCHLD
804 /* System V fork+wait does not work if SIGCHLD is ignored. */
805 signal (SIGCHLD, SIG_DFL);
806 #endif
807 break;
808 case STRIP_PROGRAM_OPTION:
809 strip_program = xstrdup (optarg);
810 strip_program_specified = true;
811 break;
812 case 'd':
813 dir_arg = true;
814 break;
815 case 'D':
816 mkdir_and_install = true;
817 break;
818 case 'v':
819 x.verbose = true;
820 break;
821 case 'g':
822 group_name = optarg;
823 break;
824 case 'm':
825 specified_mode = optarg;
826 break;
827 case 'o':
828 owner_name = optarg;
829 break;
830 case 'p':
831 x.preserve_timestamps = true;
832 break;
833 case 'S':
834 make_backups = true;
835 backup_suffix_string = optarg;
836 break;
837 case 't':
838 if (target_directory)
839 error (EXIT_FAILURE, 0,
840 _("multiple target directories specified"));
841 else
843 struct stat st;
844 if (stat (optarg, &st) != 0)
845 error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
846 if (! S_ISDIR (st.st_mode))
847 error (EXIT_FAILURE, 0, _("target %s is not a directory"),
848 quote (optarg));
850 target_directory = optarg;
851 break;
852 case 'T':
853 no_target_directory = true;
854 break;
856 case PRESERVE_CONTEXT_OPTION:
857 if ( ! selinux_enabled)
859 error (0, 0, _("WARNING: ignoring --preserve-context; "
860 "this kernel is not SELinux-enabled"));
861 break;
863 x.preserve_security_context = true;
864 use_default_selinux_context = false;
865 break;
866 case 'Z':
867 if ( ! selinux_enabled)
869 error (0, 0, _("WARNING: ignoring --context (-Z); "
870 "this kernel is not SELinux-enabled"));
871 break;
873 scontext = optarg;
874 use_default_selinux_context = false;
875 break;
876 case_GETOPT_HELP_CHAR;
877 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
878 default:
879 usage (EXIT_FAILURE);
883 /* Check for invalid combinations of arguments. */
884 if (dir_arg && strip_files)
885 error (EXIT_FAILURE, 0,
886 _("the strip option may not be used when installing a directory"));
887 if (dir_arg && target_directory)
888 error (EXIT_FAILURE, 0,
889 _("target directory not allowed when installing a directory"));
891 if (x.preserve_security_context && scontext != NULL)
892 error (EXIT_FAILURE, 0,
893 _("cannot force target context to %s and preserve it"),
894 quote (scontext));
896 if (backup_suffix_string)
897 simple_backup_suffix = xstrdup (backup_suffix_string);
899 x.backup_type = (make_backups
900 ? xget_version (_("backup type"),
901 version_control_string)
902 : no_backups);
904 if (scontext && setfscreatecon (scontext) < 0)
905 error (EXIT_FAILURE, errno,
906 _("failed to set default file creation context to %s"),
907 quote (scontext));
909 n_files = argc - optind;
910 file = argv + optind;
912 if (n_files <= ! (dir_arg || target_directory))
914 if (n_files <= 0)
915 error (0, 0, _("missing file operand"));
916 else
917 error (0, 0, _("missing destination file operand after %s"),
918 quote (file[0]));
919 usage (EXIT_FAILURE);
922 if (no_target_directory)
924 if (target_directory)
925 error (EXIT_FAILURE, 0,
926 _("cannot combine --target-directory (-t) "
927 "and --no-target-directory (-T)"));
928 if (2 < n_files)
930 error (0, 0, _("extra operand %s"), quote (file[2]));
931 usage (EXIT_FAILURE);
934 else if (! (dir_arg || target_directory))
936 if (2 <= n_files && target_directory_operand (file[n_files - 1]))
937 target_directory = file[--n_files];
938 else if (2 < n_files)
939 error (EXIT_FAILURE, 0, _("target %s is not a directory"),
940 quote (file[n_files - 1]));
943 if (specified_mode)
945 struct mode_change *change = mode_compile (specified_mode);
946 if (!change)
947 error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode));
948 mode = mode_adjust (0, false, 0, change, NULL);
949 dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits);
950 free (change);
953 if (strip_program_specified && !strip_files)
954 error (0, 0, _("WARNING: ignoring --strip-program option as -s option was "
955 "not specified"));
957 if (copy_only_if_needed && x.preserve_timestamps)
959 error (0, 0, _("options --compare (-C) and --preserve-timestamps are "
960 "mutually exclusive"));
961 usage (EXIT_FAILURE);
964 if (copy_only_if_needed && strip_files)
966 error (0, 0, _("options --compare (-C) and --strip are mutually "
967 "exclusive"));
968 usage (EXIT_FAILURE);
971 if (copy_only_if_needed && extra_mode (mode))
972 error (0, 0, _("the --compare (-C) option is ignored when you"
973 " specify a mode with non-permission bits"));
975 get_ids ();
977 if (dir_arg)
978 exit_status = savewd_process_files (n_files, file, process_dir, &x);
979 else
981 /* FIXME: it's a little gross that this initialization is
982 required by copy.c::copy. */
983 hash_init ();
985 if (!target_directory)
987 if (! (mkdir_and_install
988 ? install_file_in_file_parents (file[0], file[1], &x)
989 : install_file_in_file (file[0], file[1], &x)))
990 exit_status = EXIT_FAILURE;
992 else
994 int i;
995 dest_info_init (&x);
996 for (i = 0; i < n_files; i++)
997 if (! install_file_in_dir (file[i], target_directory, &x))
998 exit_status = EXIT_FAILURE;
1002 exit (exit_status);