1 /* install - copy files and set attributes
2 Copyright (C) 1989, 1990, 1991 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> */
56 #if defined (CONFIG_BROKETS)
57 /* We use <config.h> instead of "config.h" so that a compilation
58 using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
59 (which it would do because it found this file in $srcdir). */
69 #include <sys/types.h>
74 #include "modechange.h"
76 #if !defined (isascii) || defined (STDC_HEADERS)
81 #define ISDIGIT(c) (isascii (c) && isdigit (c))
86 struct passwd *getpwnam ();
87 struct group *getgrnam ();
98 /* True if C is an ASCII octal digit. */
99 #define isodigit(c) ((c) >= '0' && c <= '7')
101 /* Number of bytes of a file to copy at a time. */
102 #define READ_SIZE (32 * 1024)
111 static int change_attributes ();
112 static int copy_file ();
113 static int install_file_in_dir ();
114 static int install_file_in_file ();
115 static int isnumber ();
116 static void get_ids ();
117 static void strip ();
118 static void usage ();
120 /* The name this program was run with, for error messages. */
123 /* The user name that will own the files, or NULL to make the owner
124 the current user ID. */
125 static char *owner_name;
127 /* The user ID corresponding to `owner_name'. */
128 static uid_t owner_id;
130 /* The group name that will own the files, or NULL to make the group
131 the current group ID. */
132 static char *group_name;
134 /* The group ID corresponding to `group_name'. */
135 static gid_t group_id;
137 /* The permissions to which the files will be set. The umask has
141 /* If nonzero, strip executable files after copying them. */
142 static int strip_files;
144 /* If nonzero, install a directory instead of a regular file. */
147 /* If non-zero, display usage information and exit. */
148 static int show_help;
150 /* If non-zero, print the version on standard output and exit. */
151 static int show_version;
153 static struct option const long_options[] =
155 {"strip", no_argument, NULL, 's'},
156 {"directory", no_argument, NULL, 'd'},
157 {"group", required_argument, NULL, 'g'},
158 {"mode", required_argument, NULL, 'm'},
159 {"owner", required_argument, NULL, 'o'},
160 {"help", no_argument, &show_help, 1},
161 {"version", no_argument, &show_version, 1},
172 char *symbolic_mode = NULL;
174 program_name = argv[0];
182 while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
201 symbolic_mode = optarg;
213 printf ("%s\n", version_string);
220 /* Check for invalid combinations of arguments. */
221 if ((dir_arg && strip_files)
223 || (optind == argc - 1 && !dir_arg))
228 struct mode_change *change = mode_compile (symbolic_mode, 0);
229 if (change == MODE_INVALID)
230 error (1, 0, "invalid mode `%s'", symbolic_mode);
231 else if (change == MODE_MEMORY_EXHAUSTED)
232 error (1, 0, "virtual memory exhausted");
233 mode = mode_adjust (0, change);
240 for (; optind < argc; ++optind)
243 make_path (argv[optind], mode, mode, owner_id, group_id, NULL);
248 if (optind == argc - 2)
250 if (!isdir (argv[argc - 1]))
251 errors = install_file_in_file (argv[argc - 2], argv[argc - 1]);
253 errors = install_file_in_dir (argv[argc - 2], argv[argc - 1]);
257 if (!isdir (argv[argc - 1]))
259 for (; optind < argc - 1; ++optind)
261 errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
269 /* Copy file FROM onto file TO and give TO the appropriate
271 Return 0 if successful, 1 if an error occurs. */
274 install_file_in_file (from, to)
279 int no_need_to_chown;
281 if (copy_file (from, to, &to_created))
285 no_need_to_chown = (to_created
286 && owner_name == NULL
287 && group_name == NULL);
288 return change_attributes (to, no_need_to_chown);
291 /* Copy file FROM into directory TO_DIR, keeping its same name,
292 and give the copy the appropriate attributes.
293 Return 0 if successful, 1 if not. */
296 install_file_in_dir (from, to_dir)
304 from_base = basename (from);
305 to = xmalloc ((unsigned) (strlen (to_dir) + strlen (from_base) + 2));
306 stpcpy (stpcpy (stpcpy (to, to_dir), "/"), from_base);
307 ret = install_file_in_file (from, to);
312 /* A chunk of a file being copied. */
313 static char buffer[READ_SIZE];
315 /* Copy file FROM onto file TO, creating TO if necessary.
316 Return 0 if the copy is successful, 1 if not. If the copy is
317 successful, set *TO_CREATED to non-zero if TO was created (if it did
318 not exist or did, but was unlinked) and to zero otherwise. If the
319 copy fails, don't modify *TO_CREATED. */
322 copy_file (from, to, to_created)
330 struct stat from_stats, to_stats;
331 int target_created = 1;
333 if (stat (from, &from_stats))
335 error (0, errno, "%s", from);
338 if (!S_ISREG (from_stats.st_mode))
340 error (0, 0, "`%s' is not a regular file", from);
343 if (stat (to, &to_stats) == 0)
345 if (!S_ISREG (to_stats.st_mode))
347 error (0, 0, "`%s' is not a regular file", to);
350 if (from_stats.st_dev == to_stats.st_dev
351 && from_stats.st_ino == to_stats.st_ino)
353 error (0, 0, "`%s' and `%s' are the same file", from, to);
356 /* If unlink fails, try to proceed anyway. */
361 fromfd = open (from, O_RDONLY, 0);
364 error (0, errno, "%s", from);
368 /* Make sure to open the file in a mode that allows writing. */
369 tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
372 error (0, errno, "%s", to);
377 while ((bytes = read (fromfd, buffer, READ_SIZE)) > 0)
378 if (write (tofd, buffer, bytes) != bytes)
380 error (0, errno, "%s", to);
386 error (0, errno, "%s", from);
390 if (close (fromfd) < 0)
392 error (0, errno, "%s", from);
395 if (close (tofd) < 0)
397 error (0, errno, "%s", to);
401 *to_created = target_created;
410 /* Set the attributes of file or directory PATH.
411 If NO_NEED_TO_CHOWN is non-zero, don't call chown.
412 Return 0 if successful, 1 if not. */
415 change_attributes (path, no_need_to_chown)
417 int no_need_to_chown;
421 /* chown must precede chmod because on some systems,
422 chown clears the set[ug]id bits for non-superusers,
423 resulting in incorrect permissions.
424 On System V, users can give away files with chown and then not
425 be able to chmod them. So don't give files away.
427 We don't pass -1 to chown to mean "don't change the value"
428 because SVR3 and earlier non-BSD systems don't support that.
430 We don't normally ignore errors from chown because the idea of
431 the install command is that the file is supposed to end up with
432 precisely the attributes that the user specified (or defaulted).
433 If the file doesn't end up with the group they asked for, they'll
434 want to know. But AFS returns EPERM when you try to change a
435 file's group; thus the kludge. */
437 if (!no_need_to_chown && chown (path, owner_id, group_id)
443 if (chmod (path, mode))
447 error (0, err, "%s", path);
453 /* Strip the symbol table from the file PATH.
454 We could dig the magic number out of the file first to
455 determine whether to strip it, but the header files and
456 magic numbers vary so much from system to system that making
457 it portable would be very difficult. Not worth the effort. */
469 error (1, errno, "cannot fork");
472 execlp ("strip", "strip", path, (char *) NULL);
473 error (1, errno, "cannot run strip");
475 default: /* Parent. */
476 /* Parent process. */
477 while (pid != wait (&status)) /* Wait for kid to finish. */
483 /* Initialize the user and group ownership of the files to install. */
493 pw = getpwnam (owner_name);
496 if (!isnumber (owner_name))
497 error (1, 0, "invalid user `%s'", owner_name);
498 owner_id = atoi (owner_name);
501 owner_id = pw->pw_uid;
505 owner_id = getuid ();
509 gr = getgrnam (group_name);
512 if (!isnumber (group_name))
513 error (1, 0, "invalid group `%s'", group_name);
514 group_id = atoi (group_name);
517 group_id = gr->gr_gid;
521 group_id = getgid ();
524 /* Return nonzero if STR is an ASCII representation of a nonzero
525 decimal integer, zero if not. */
543 fprintf (status == 0 ? stdout : stderr, "\
544 Usage: %s [OPTION]... SOURCE DEST (1st format)\n\
545 or: %s [OPTION]... SOURCE... DIRECTORY (2nd format)\n\
546 or: %s [OPTION]... DIRECTORY... (3nd format)\n\
548 program_name, program_name, program_name);
553 -d, --directory create [leading] directories, mandatory for 3rd format\n\
554 -g, --group GROUP set group ownership, instead of process' current group\n\
555 -m, --mode MODE set permission mode (as in chmod), instead of 0755\n\
556 -o, --owner OWNER set ownership (super-user only)\n\
557 -s, --strip strip symbol tables, only for 1st and 2nd formats\n\
558 --help display this help and exit\n\
559 --version output version information and exit\n");
562 fprintf (stderr, "Try `%s --help' for more information.\n",