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 "safe-stat.h"
65 #include "modechange.h"
72 struct passwd *getpwnam ();
73 struct group *getgrnam ();
84 /* True if C is an ASCII octal digit. */
85 #define isodigit(c) ((c) >= '0' && c <= '7')
87 /* Number of bytes of a file to copy at a time. */
88 #define READ_SIZE (32 * 1024)
97 static int change_attributes ();
98 static int copy_file ();
99 static int install_file_in_dir ();
100 static int install_file_in_file ();
101 static int isnumber ();
102 static void get_ids ();
103 static void strip ();
104 static void usage ();
106 /* The name this program was run with, for error messages. */
109 /* The user name that will own the files, or NULL to make the owner
110 the current user ID. */
111 static char *owner_name;
113 /* The user ID corresponding to `owner_name'. */
114 static uid_t owner_id;
116 /* The group name that will own the files, or NULL to make the group
117 the current group ID. */
118 static char *group_name;
120 /* The group ID corresponding to `group_name'. */
121 static gid_t group_id;
123 /* The permissions to which the files will be set. The umask has
127 /* If nonzero, strip executable files after copying them. */
128 static int strip_files;
130 /* If nonzero, install a directory instead of a regular file. */
133 /* If non-zero, display usage information and exit. */
134 static int show_help;
136 /* If non-zero, print the version on standard output and exit. */
137 static int show_version;
139 static struct option const long_options[] =
141 {"strip", no_argument, NULL, 's'},
142 {"directory", no_argument, NULL, 'd'},
143 {"group", required_argument, NULL, 'g'},
144 {"mode", required_argument, NULL, 'm'},
145 {"owner", required_argument, NULL, 'o'},
146 {"help", no_argument, &show_help, 1},
147 {"version", no_argument, &show_version, 1},
158 char *symbolic_mode = NULL;
160 program_name = argv[0];
168 while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
187 symbolic_mode = optarg;
199 printf ("%s\n", version_string);
206 /* Check for invalid combinations of arguments. */
207 if (dir_arg && strip_files)
209 "the strip option may not be used when installing a directory");
211 if (optind == argc || (optind == argc - 1 && !dir_arg))
213 error (0, 0, "too few arguments");
219 struct mode_change *change = mode_compile (symbolic_mode, 0);
220 if (change == MODE_INVALID)
221 error (1, 0, "invalid mode `%s'", symbolic_mode);
222 else if (change == MODE_MEMORY_EXHAUSTED)
223 error (1, 0, "virtual memory exhausted");
224 mode = mode_adjust (0, change);
231 for (; optind < argc; ++optind)
234 make_path (argv[optind], mode, mode, owner_id, group_id, 0, NULL);
239 if (optind == argc - 2)
241 if (!isdir (argv[argc - 1]))
242 errors = install_file_in_file (argv[argc - 2], argv[argc - 1]);
244 errors = install_file_in_dir (argv[argc - 2], argv[argc - 1]);
248 if (!isdir (argv[argc - 1]))
250 for (; optind < argc - 1; ++optind)
252 errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
260 /* Copy file FROM onto file TO and give TO the appropriate
262 Return 0 if successful, 1 if an error occurs. */
265 install_file_in_file (from, to)
270 int no_need_to_chown;
272 if (copy_file (from, to, &to_created))
276 no_need_to_chown = (to_created
277 && owner_name == NULL
278 && group_name == NULL);
279 return change_attributes (to, no_need_to_chown);
282 /* Copy file FROM into directory TO_DIR, keeping its same name,
283 and give the copy the appropriate attributes.
284 Return 0 if successful, 1 if not. */
287 install_file_in_dir (from, to_dir)
295 from_base = basename (from);
296 to = xmalloc ((unsigned) (strlen (to_dir) + strlen (from_base) + 2));
297 stpcpy (stpcpy (stpcpy (to, to_dir), "/"), from_base);
298 ret = install_file_in_file (from, to);
303 /* A chunk of a file being copied. */
304 static char buffer[READ_SIZE];
306 /* Copy file FROM onto file TO, creating TO if necessary.
307 Return 0 if the copy is successful, 1 if not. If the copy is
308 successful, set *TO_CREATED to non-zero if TO was created (if it did
309 not exist or did, but was unlinked) and to zero otherwise. If the
310 copy fails, don't modify *TO_CREATED. */
313 copy_file (from, to, to_created)
321 struct stat from_stats, to_stats;
322 int target_created = 1;
324 if (safe_stat (from, &from_stats))
326 error (0, errno, "%s", from);
329 if (!S_ISREG (from_stats.st_mode))
331 error (0, 0, "`%s' is not a regular file", from);
334 if (safe_stat (to, &to_stats) == 0)
336 if (!S_ISREG (to_stats.st_mode))
338 error (0, 0, "`%s' is not a regular file", to);
341 if (from_stats.st_dev == to_stats.st_dev
342 && from_stats.st_ino == to_stats.st_ino)
344 error (0, 0, "`%s' and `%s' are the same file", from, to);
347 /* If unlink fails, try to proceed anyway. */
352 fromfd = open (from, O_RDONLY, 0);
355 error (0, errno, "%s", from);
359 /* Make sure to open the file in a mode that allows writing. */
360 tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
363 error (0, errno, "%s", to);
368 while ((bytes = safe_read (fromfd, buffer, READ_SIZE)) > 0)
369 if (full_write (tofd, buffer, bytes) < 0)
371 error (0, errno, "%s", to);
377 error (0, errno, "%s", from);
381 if (close (fromfd) < 0)
383 error (0, errno, "%s", from);
386 if (close (tofd) < 0)
388 error (0, errno, "%s", to);
392 *to_created = target_created;
401 /* Set the attributes of file or directory PATH.
402 If NO_NEED_TO_CHOWN is non-zero, don't call chown.
403 Return 0 if successful, 1 if not. */
406 change_attributes (path, no_need_to_chown)
408 int no_need_to_chown;
412 /* chown must precede chmod because on some systems,
413 chown clears the set[ug]id bits for non-superusers,
414 resulting in incorrect permissions.
415 On System V, users can give away files with chown and then not
416 be able to chmod them. So don't give files away.
418 We don't pass -1 to chown to mean "don't change the value"
419 because SVR3 and earlier non-BSD systems don't support that.
421 We don't normally ignore errors from chown because the idea of
422 the install command is that the file is supposed to end up with
423 precisely the attributes that the user specified (or defaulted).
424 If the file doesn't end up with the group they asked for, they'll
425 want to know. But AFS returns EPERM when you try to change a
426 file's group; thus the kludge. */
428 if (!no_need_to_chown && chown (path, owner_id, group_id)
434 if (chmod (path, mode))
438 error (0, err, "%s", path);
444 /* Strip the symbol table from the file PATH.
445 We could dig the magic number out of the file first to
446 determine whether to strip it, but the header files and
447 magic numbers vary so much from system to system that making
448 it portable would be very difficult. Not worth the effort. */
460 error (1, errno, "cannot fork");
463 execlp ("strip", "strip", path, (char *) NULL);
464 error (1, errno, "cannot run strip");
466 default: /* Parent. */
467 /* Parent process. */
468 while (pid != wait (&status)) /* Wait for kid to finish. */
474 /* Initialize the user and group ownership of the files to install. */
484 pw = getpwnam (owner_name);
487 if (!isnumber (owner_name))
488 error (1, 0, "invalid user `%s'", owner_name);
489 owner_id = atoi (owner_name);
492 owner_id = pw->pw_uid;
496 owner_id = getuid ();
500 gr = getgrnam (group_name);
503 if (!isnumber (group_name))
504 error (1, 0, "invalid group `%s'", group_name);
505 group_id = atoi (group_name);
508 group_id = gr->gr_gid;
512 group_id = getgid ();
515 /* Return nonzero if STR is an ASCII representation of a nonzero
516 decimal integer, zero if not. */
535 fprintf (stderr, "Try `%s --help' for more information.\n",
540 Usage: %s [OPTION]... SOURCE DEST (1st format)\n\
541 or: %s [OPTION]... SOURCE... DIRECTORY (2nd format)\n\
542 or: %s [OPTION]... DIRECTORY... (3nd format)\n\
544 program_name, program_name, program_name);
548 -d, --directory create [leading] directories, mandatory for 3rd format\n\
549 -g, --group=GROUP set group ownership, instead of process' current group\n\
550 -m, --mode=MODE set permission mode (as in chmod), instead of 0755\n\
551 -o, --owner=OWNER set ownership (super-user only)\n\
552 -s, --strip strip symbol tables, only for 1st and 2nd formats\n\
553 --help display this help and exit\n\
554 --version output version information and exit\n");