.
[coreutils.git] / src / install.c
blobc0ec2ae121d449b1177bb2033c056d6e53f0ae10
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)
7 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, 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.
25 Options:
26 -g, --group=GROUP
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.
31 -m, --mode=MODE
32 Set the permission mode for the installed file or directory
33 to MODE, which is an octal number (default is 0755).
35 -o, --owner=OWNER
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
38 a numeric user ID.
40 -c No effect. For compatibility with old Unix versions of install.
42 -s, --strip
43 Strip the symbol tables from installed files.
45 -d, --directory
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> */
55 #include <config.h>
56 #include <stdio.h>
57 #include <getopt.h>
58 #include <sys/types.h>
59 #include <pwd.h>
60 #include <grp.h>
62 #include "system.h"
63 #include "version.h"
64 #include "modechange.h"
65 #include "makepath.h"
66 #include "error.h"
68 #ifdef _POSIX_VERSION
69 #include <sys/wait.h>
70 #else
71 struct passwd *getpwnam ();
72 struct group *getgrnam ();
73 uid_t getuid ();
74 gid_t getgid ();
75 int wait ();
76 #endif
78 #ifdef _POSIX_SOURCE
79 #define endgrent()
80 #define endpwent()
81 #endif
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)
89 char *basename ();
90 char *stpcpy ();
91 char *xmalloc ();
92 int safe_read ();
93 int full_write ();
94 int isdir ();
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. */
106 char *program_name;
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
123 no effect. */
124 static int mode;
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. */
130 static int dir_arg;
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},
147 {NULL, 0, NULL, 0}
150 void
151 main (argc, argv)
152 int argc;
153 char **argv;
155 int optc;
156 int errors = 0;
157 char *symbolic_mode = NULL;
159 program_name = argv[0];
160 owner_name = NULL;
161 group_name = NULL;
162 mode = 0755;
163 strip_files = 0;
164 dir_arg = 0;
165 umask (0);
167 while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
168 (int *) 0)) != EOF)
170 switch (optc)
172 case 0:
173 break;
174 case 'c':
175 break;
176 case 's':
177 strip_files = 1;
178 break;
179 case 'd':
180 dir_arg = 1;
181 break;
182 case 'g':
183 group_name = optarg;
184 break;
185 case 'm':
186 symbolic_mode = optarg;
187 break;
188 case 'o':
189 owner_name = optarg;
190 break;
191 default:
192 usage (1);
196 if (show_version)
198 printf ("install - %s\n", version_string);
199 exit (0);
202 if (show_help)
203 usage (0);
205 /* Check for invalid combinations of arguments. */
206 if (dir_arg && strip_files)
207 error (1, 0,
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");
213 usage (1);
216 if (symbolic_mode)
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);
226 get_ids ();
228 if (dir_arg)
230 for (; optind < argc; ++optind)
232 errors |=
233 make_path (argv[optind], mode, mode, owner_id, group_id, 0, NULL);
236 else
238 if (optind == argc - 2)
240 if (!isdir (argv[argc - 1]))
241 errors = install_file_in_file (argv[argc - 2], argv[argc - 1]);
242 else
243 errors = install_file_in_dir (argv[argc - 2], argv[argc - 1]);
245 else
247 if (!isdir (argv[argc - 1]))
248 usage (1);
249 for (; optind < argc - 1; ++optind)
251 errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
256 exit (errors);
259 /* Copy file FROM onto file TO and give TO the appropriate
260 attributes.
261 Return 0 if successful, 1 if an error occurs. */
263 static int
264 install_file_in_file (from, to)
265 char *from;
266 char *to;
268 int to_created;
269 int no_need_to_chown;
271 if (copy_file (from, to, &to_created))
272 return 1;
273 if (strip_files)
274 strip (to);
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. */
285 static int
286 install_file_in_dir (from, to_dir)
287 char *from;
288 char *to_dir;
290 char *from_base;
291 char *to;
292 int ret;
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);
298 free (to);
299 return ret;
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. */
311 static int
312 copy_file (from, to, to_created)
313 char *from;
314 char *to;
315 int *to_created;
317 int fromfd, tofd;
318 int bytes;
319 int ret = 0;
320 struct stat from_stats, to_stats;
321 int target_created = 1;
323 if (stat (from, &from_stats))
325 error (0, errno, "%s", from);
326 return 1;
328 if (!S_ISREG (from_stats.st_mode))
330 error (0, 0, "`%s' is not a regular file", from);
331 return 1;
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);
338 return 1;
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);
344 return 1;
346 /* If unlink fails, try to proceed anyway. */
347 if (unlink (to))
348 target_created = 0;
351 fromfd = open (from, O_RDONLY, 0);
352 if (fromfd == -1)
354 error (0, errno, "%s", from);
355 return 1;
358 /* Make sure to open the file in a mode that allows writing. */
359 tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
360 if (tofd == -1)
362 error (0, errno, "%s", to);
363 close (fromfd);
364 return 1;
367 while ((bytes = safe_read (fromfd, buffer, READ_SIZE)) > 0)
368 if (full_write (tofd, buffer, bytes) < 0)
370 error (0, errno, "%s", to);
371 goto copy_error;
374 if (bytes == -1)
376 error (0, errno, "%s", from);
377 goto copy_error;
380 if (close (fromfd) < 0)
382 error (0, errno, "%s", from);
383 ret = 1;
385 if (close (tofd) < 0)
387 error (0, errno, "%s", to);
388 ret = 1;
390 if (ret == 0)
391 *to_created = target_created;
392 return ret;
394 copy_error:
395 close (fromfd);
396 close (tofd);
397 return 1;
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. */
404 static int
405 change_attributes (path, no_need_to_chown)
406 char *path;
407 int no_need_to_chown;
409 int err = 0;
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)
428 #ifdef AFS
429 && errno != EPERM
430 #endif
432 err = errno;
433 if (chmod (path, mode))
434 err = errno;
435 if (err)
437 error (0, err, "%s", path);
438 return 1;
440 return 0;
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. */
449 static void
450 strip (path)
451 char *path;
453 int pid, status;
455 pid = fork ();
456 switch (pid)
458 case -1:
459 error (1, errno, "cannot fork");
460 break;
461 case 0: /* Child. */
462 execlp ("strip", "strip", path, (char *) NULL);
463 error (1, errno, "cannot run strip");
464 break;
465 default: /* Parent. */
466 /* Parent process. */
467 while (pid != wait (&status)) /* Wait for kid to finish. */
468 /* Do nothing. */ ;
469 break;
473 /* Initialize the user and group ownership of the files to install. */
475 static void
476 get_ids ()
478 struct passwd *pw;
479 struct group *gr;
481 if (owner_name)
483 pw = getpwnam (owner_name);
484 if (pw == NULL)
486 if (!isnumber (owner_name))
487 error (1, 0, "invalid user `%s'", owner_name);
488 owner_id = atoi (owner_name);
490 else
491 owner_id = pw->pw_uid;
492 endpwent ();
494 else
495 owner_id = getuid ();
497 if (group_name)
499 gr = getgrnam (group_name);
500 if (gr == NULL)
502 if (!isnumber (group_name))
503 error (1, 0, "invalid group `%s'", group_name);
504 group_id = atoi (group_name);
506 else
507 group_id = gr->gr_gid;
508 endgrent ();
510 else
511 group_id = getgid ();
514 /* Return nonzero if STR is an ASCII representation of a nonzero
515 decimal integer, zero if not. */
517 static int
518 isnumber (str)
519 char *str;
521 if (*str == 0)
522 return 0;
523 for (; *str; str++)
524 if (!ISDIGIT (*str))
525 return 0;
526 return 1;
529 static void
530 usage (status)
531 int status;
533 if (status != 0)
534 fprintf (stderr, "Try `%s --help' for more information.\n",
535 program_name);
536 else
538 printf ("\
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);
544 printf ("\
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\
549 -c (ignored)\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");
558 exit (status);