1 /* install - copy files and set attributes
2 Copyright (C) 1989, 1990, 1991, 1995 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 2, or (at your option)
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, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* Copy files and set their permission modes and, if possible,
19 their owner and group. Used similarly to `cp'; typically
20 used in Makefiles to copy programs into their destination
21 directories. It can also be used to create the destination
22 directories and any leading directories, and to set the final
23 directory's modes. It refuses to copy files onto themselves.
27 Set the group ownership of the installed file or directory
28 to the group ID of GROUP (default is process's current
29 group). GROUP may also be a numeric group ID.
32 Set the permission mode for the installed file or directory
33 to MODE, which is an octal number (default is 0755).
36 If run as root, set the ownership of the installed file to
37 the user ID of OWNER (default is root). OWNER may also be
40 -c No effect. For compatibility with old Unix versions of install.
43 Strip the symbol tables from installed files.
46 Create a directory and its leading directories, if they
47 do not already exist. Set the owner, group and mode
48 as given on the command line. Any leading directories
49 that are created are also given those attributes.
50 This is different from the SunOS 4.0 install, which gives
51 directories that it creates the default attributes.
53 David MacKenzie <djm@gnu.ai.mit.edu> */
58 #include <sys/types.h>
64 #include "modechange.h"
71 struct passwd *getpwnam ();
72 struct group *getgrnam ();
83 /* True if C is an ASCII octal digit. */
84 #define isodigit(c) ((c) >= '0' && c <= '7')
86 /* Number of bytes of a file to copy at a time. */
87 #define READ_SIZE (32 * 1024)
96 static int change_attributes ();
97 static int copy_file ();
98 static int install_file_in_dir ();
99 static int install_file_in_file ();
100 static int isnumber ();
101 static void get_ids ();
102 static void strip ();
103 static void usage ();
105 /* The name this program was run with, for error messages. */
108 /* The user name that will own the files, or NULL to make the owner
109 the current user ID. */
110 static char *owner_name;
112 /* The user ID corresponding to `owner_name'. */
113 static uid_t owner_id;
115 /* The group name that will own the files, or NULL to make the group
116 the current group ID. */
117 static char *group_name;
119 /* The group ID corresponding to `group_name'. */
120 static gid_t group_id;
122 /* The permissions to which the files will be set. The umask has
126 /* If nonzero, strip executable files after copying them. */
127 static int strip_files;
129 /* If nonzero, install a directory instead of a regular file. */
132 /* If non-zero, display usage information and exit. */
133 static int show_help;
135 /* If non-zero, print the version on standard output and exit. */
136 static int show_version;
138 static struct option const long_options[] =
140 {"strip", no_argument, NULL, 's'},
141 {"directory", no_argument, NULL, 'd'},
142 {"group", required_argument, NULL, 'g'},
143 {"mode", required_argument, NULL, 'm'},
144 {"owner", required_argument, NULL, 'o'},
145 {"help", no_argument, &show_help, 1},
146 {"version", no_argument, &show_version, 1},
157 char *symbolic_mode = NULL;
159 program_name = argv[0];
167 while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
186 symbolic_mode = optarg;
198 printf ("install - %s\n", version_string);
205 /* Check for invalid combinations of arguments. */
206 if (dir_arg && strip_files)
208 "the strip option may not be used when installing a directory");
210 if (optind == argc || (optind == argc - 1 && !dir_arg))
212 error (0, 0, "too few arguments");
218 struct mode_change *change = mode_compile (symbolic_mode, 0);
219 if (change == MODE_INVALID)
220 error (1, 0, "invalid mode `%s'", symbolic_mode);
221 else if (change == MODE_MEMORY_EXHAUSTED)
222 error (1, 0, "virtual memory exhausted");
223 mode = mode_adjust (0, change);
230 for (; optind < argc; ++optind)
233 make_path (argv[optind], mode, mode, owner_id, group_id, 0, NULL);
238 if (optind == argc - 2)
240 if (!isdir (argv[argc - 1]))
241 errors = install_file_in_file (argv[argc - 2], argv[argc - 1]);
243 errors = install_file_in_dir (argv[argc - 2], argv[argc - 1]);
247 if (!isdir (argv[argc - 1]))
249 for (; optind < argc - 1; ++optind)
251 errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
259 /* Copy file FROM onto file TO and give TO the appropriate
261 Return 0 if successful, 1 if an error occurs. */
264 install_file_in_file (from, to)
269 int no_need_to_chown;
271 if (copy_file (from, to, &to_created))
275 no_need_to_chown = (to_created
276 && owner_name == NULL
277 && group_name == NULL);
278 return change_attributes (to, no_need_to_chown);
281 /* Copy file FROM into directory TO_DIR, keeping its same name,
282 and give the copy the appropriate attributes.
283 Return 0 if successful, 1 if not. */
286 install_file_in_dir (from, to_dir)
294 from_base = basename (from);
295 to = xmalloc ((unsigned) (strlen (to_dir) + strlen (from_base) + 2));
296 stpcpy (stpcpy (stpcpy (to, to_dir), "/"), from_base);
297 ret = install_file_in_file (from, to);
302 /* A chunk of a file being copied. */
303 static char buffer[READ_SIZE];
305 /* Copy file FROM onto file TO, creating TO if necessary.
306 Return 0 if the copy is successful, 1 if not. If the copy is
307 successful, set *TO_CREATED to non-zero if TO was created (if it did
308 not exist or did, but was unlinked) and to zero otherwise. If the
309 copy fails, don't modify *TO_CREATED. */
312 copy_file (from, to, to_created)
320 struct stat from_stats, to_stats;
321 int target_created = 1;
323 if (stat (from, &from_stats))
325 error (0, errno, "%s", from);
328 if (!S_ISREG (from_stats.st_mode))
330 error (0, 0, "`%s' is not a regular file", from);
333 if (stat (to, &to_stats) == 0)
335 if (!S_ISREG (to_stats.st_mode))
337 error (0, 0, "`%s' is not a regular file", to);
340 if (from_stats.st_dev == to_stats.st_dev
341 && from_stats.st_ino == to_stats.st_ino)
343 error (0, 0, "`%s' and `%s' are the same file", from, to);
346 /* If unlink fails, try to proceed anyway. */
351 fromfd = open (from, O_RDONLY, 0);
354 error (0, errno, "%s", from);
358 /* Make sure to open the file in a mode that allows writing. */
359 tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
362 error (0, errno, "%s", to);
367 while ((bytes = safe_read (fromfd, buffer, READ_SIZE)) > 0)
368 if (full_write (tofd, buffer, bytes) < 0)
370 error (0, errno, "%s", to);
376 error (0, errno, "%s", from);
380 if (close (fromfd) < 0)
382 error (0, errno, "%s", from);
385 if (close (tofd) < 0)
387 error (0, errno, "%s", to);
391 *to_created = target_created;
400 /* Set the attributes of file or directory PATH.
401 If NO_NEED_TO_CHOWN is non-zero, don't call chown.
402 Return 0 if successful, 1 if not. */
405 change_attributes (path, no_need_to_chown)
407 int no_need_to_chown;
411 /* chown must precede chmod because on some systems,
412 chown clears the set[ug]id bits for non-superusers,
413 resulting in incorrect permissions.
414 On System V, users can give away files with chown and then not
415 be able to chmod them. So don't give files away.
417 We don't pass -1 to chown to mean "don't change the value"
418 because SVR3 and earlier non-BSD systems don't support that.
420 We don't normally ignore errors from chown because the idea of
421 the install command is that the file is supposed to end up with
422 precisely the attributes that the user specified (or defaulted).
423 If the file doesn't end up with the group they asked for, they'll
424 want to know. But AFS returns EPERM when you try to change a
425 file's group; thus the kludge. */
427 if (!no_need_to_chown && chown (path, owner_id, group_id)
433 if (chmod (path, mode))
437 error (0, err, "%s", path);
443 /* Strip the symbol table from the file PATH.
444 We could dig the magic number out of the file first to
445 determine whether to strip it, but the header files and
446 magic numbers vary so much from system to system that making
447 it portable would be very difficult. Not worth the effort. */
459 error (1, errno, "cannot fork");
462 execlp ("strip", "strip", path, (char *) NULL);
463 error (1, errno, "cannot run strip");
465 default: /* Parent. */
466 /* Parent process. */
467 while (pid != wait (&status)) /* Wait for kid to finish. */
473 /* Initialize the user and group ownership of the files to install. */
483 pw = getpwnam (owner_name);
486 if (!isnumber (owner_name))
487 error (1, 0, "invalid user `%s'", owner_name);
488 owner_id = atoi (owner_name);
491 owner_id = pw->pw_uid;
495 owner_id = getuid ();
499 gr = getgrnam (group_name);
502 if (!isnumber (group_name))
503 error (1, 0, "invalid group `%s'", group_name);
504 group_id = atoi (group_name);
507 group_id = gr->gr_gid;
511 group_id = getgid ();
514 /* Return nonzero if STR is an ASCII representation of a nonzero
515 decimal integer, zero if not. */
534 fprintf (stderr, "Try `%s --help' for more information.\n",
539 Usage: %s [OPTION]... SOURCE DEST (1st format)\n\
540 or: %s [OPTION]... SOURCE... DIRECTORY (2nd format)\n\
541 or: %s -d [OPTION]... DIRECTORY... (3nd format)\n\
543 program_name, program_name, program_name);
545 In first two formats, copy SOURCE to DEST or multiple SOURCE(s) to\n\
546 DIRECTORY, while setting permission modes and owner/group. In third\n\
547 format, make all components of the given DIRECTORY(ies).\n\
550 -d, --directory create [leading] directories, mandatory for 3rd format\n\
551 -g, --group=GROUP set group ownership, instead of process' current group\n\
552 -m, --mode=MODE set permission mode (as in chmod), instead of rw-r--r--\n\
553 -o, --owner=OWNER set ownership (super-user only)\n\
554 -s, --strip strip symbol tables, only for 1st and 2nd formats\n\
555 --help display this help and exit\n\
556 --version output version information and exit\n");