.
[platform/upstream/coreutils.git] / src / install.c
1 /* install - copy files and set attributes
2    Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
3
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.
8
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.
13
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.  */
17
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.
24
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.
30
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).
34
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.
39
40    -c   No effect.  For compatibility with old Unix versions of install.
41
42    -s, --strip
43         Strip the symbol tables from installed files.
44
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.
52
53    David MacKenzie <djm@gnu.ai.mit.edu> */
54
55 #ifdef HAVE_CONFIG_H
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).  */
60 #include <config.h>
61 #else
62 #include "config.h"
63 #endif
64 #endif
65
66 #include <stdio.h>
67 #include <getopt.h>
68 #include <ctype.h>
69 #include <sys/types.h>
70 #include <pwd.h>
71 #include <grp.h>
72 #include "system.h"
73 #include "version.h"
74 #include "modechange.h"
75
76 #if !defined (isascii) || defined (STDC_HEADERS)
77 #undef isascii
78 #define isascii(c) 1
79 #endif
80
81 #define ISDIGIT(c) (isascii (c) && isdigit (c))
82
83 #ifdef _POSIX_VERSION
84 #include <sys/wait.h>
85 #else
86 struct passwd *getpwnam ();
87 struct group *getgrnam ();
88 uid_t getuid ();
89 gid_t getgid ();
90 int wait ();
91 #endif
92
93 #ifdef _POSIX_SOURCE
94 #define endgrent()
95 #define endpwent()
96 #endif
97
98 /* True if C is an ASCII octal digit. */
99 #define isodigit(c) ((c) >= '0' && c <= '7')
100
101 /* Number of bytes of a file to copy at a time. */
102 #define READ_SIZE (32 * 1024)
103
104 char *basename ();
105 char *stpcpy ();
106 char *xmalloc ();
107 void error ();
108 int safe_read ();
109 int full_write ();
110 int make_path ();
111 int isdir ();
112
113 static int change_attributes ();
114 static int copy_file ();
115 static int install_file_in_dir ();
116 static int install_file_in_file ();
117 static int isnumber ();
118 static void get_ids ();
119 static void strip ();
120 static void usage ();
121
122 /* The name this program was run with, for error messages. */
123 char *program_name;
124
125 /* The user name that will own the files, or NULL to make the owner
126    the current user ID. */
127 static char *owner_name;
128
129 /* The user ID corresponding to `owner_name'. */
130 static uid_t owner_id;
131
132 /* The group name that will own the files, or NULL to make the group
133    the current group ID. */
134 static char *group_name;
135
136 /* The group ID corresponding to `group_name'. */
137 static gid_t group_id;
138
139 /* The permissions to which the files will be set.  The umask has
140    no effect. */
141 static int mode;
142
143 /* If nonzero, strip executable files after copying them. */
144 static int strip_files;
145
146 /* If nonzero, install a directory instead of a regular file. */
147 static int dir_arg;
148
149 /* If non-zero, display usage information and exit.  */
150 static int show_help;
151
152 /* If non-zero, print the version on standard output and exit.  */
153 static int show_version;
154
155 static struct option const long_options[] =
156 {
157   {"strip", no_argument, NULL, 's'},
158   {"directory", no_argument, NULL, 'd'},
159   {"group", required_argument, NULL, 'g'},
160   {"mode", required_argument, NULL, 'm'},
161   {"owner", required_argument, NULL, 'o'},
162   {"help", no_argument, &show_help, 1},
163   {"version", no_argument, &show_version, 1},
164   {NULL, 0, NULL, 0}
165 };
166
167 void
168 main (argc, argv)
169      int argc;
170      char **argv;
171 {
172   int optc;
173   int errors = 0;
174   char *symbolic_mode = NULL;
175
176   program_name = argv[0];
177   owner_name = NULL;
178   group_name = NULL;
179   mode = 0755;
180   strip_files = 0;
181   dir_arg = 0;
182   umask (0);
183
184   while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
185                               (int *) 0)) != EOF)
186     {
187       switch (optc)
188         {
189         case 0:
190           break;
191         case 'c':
192           break;
193         case 's':
194           strip_files = 1;
195           break;
196         case 'd':
197           dir_arg = 1;
198           break;
199         case 'g':
200           group_name = optarg;
201           break;
202         case 'm':
203           symbolic_mode = optarg;
204           break;
205         case 'o':
206           owner_name = optarg;
207           break;
208         default:
209           usage (1);
210         }
211     }
212
213   if (show_version)
214     {
215       printf ("%s\n", version_string);
216       exit (0);
217     }
218
219   if (show_help)
220     usage (0);
221
222   /* Check for invalid combinations of arguments. */
223   if ((dir_arg && strip_files)
224       || (optind == argc)
225       || (optind == argc - 1 && !dir_arg))
226     usage (1);
227
228   if (symbolic_mode)
229     {
230       struct mode_change *change = mode_compile (symbolic_mode, 0);
231       if (change == MODE_INVALID)
232         error (1, 0, "invalid mode `%s'", symbolic_mode);
233       else if (change == MODE_MEMORY_EXHAUSTED)
234         error (1, 0, "virtual memory exhausted");
235       mode = mode_adjust (0, change);
236     }
237
238   get_ids ();
239
240   if (dir_arg)
241     {
242       for (; optind < argc; ++optind)
243         {
244           errors |=
245             make_path (argv[optind], mode, mode, owner_id, group_id, NULL);
246         }
247     }
248   else
249     {
250       if (optind == argc - 2)
251         {
252           if (!isdir (argv[argc - 1]))
253             errors = install_file_in_file (argv[argc - 2], argv[argc - 1]);
254           else
255             errors = install_file_in_dir (argv[argc - 2], argv[argc - 1]);
256         }
257       else
258         {
259           if (!isdir (argv[argc - 1]))
260             usage (1);
261           for (; optind < argc - 1; ++optind)
262             {
263               errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
264             }
265         }
266     }
267
268   exit (errors);
269 }
270
271 /* Copy file FROM onto file TO and give TO the appropriate
272    attributes.
273    Return 0 if successful, 1 if an error occurs. */
274
275 static int
276 install_file_in_file (from, to)
277      char *from;
278      char *to;
279 {
280   int to_created;
281   int no_need_to_chown;
282
283   if (copy_file (from, to, &to_created))
284     return 1;
285   if (strip_files)
286     strip (to);
287   no_need_to_chown = (to_created
288                       && owner_name == NULL
289                       && group_name == NULL);
290   return change_attributes (to, no_need_to_chown);
291 }
292
293 /* Copy file FROM into directory TO_DIR, keeping its same name,
294    and give the copy the appropriate attributes.
295    Return 0 if successful, 1 if not. */
296
297 static int
298 install_file_in_dir (from, to_dir)
299      char *from;
300      char *to_dir;
301 {
302   char *from_base;
303   char *to;
304   int ret;
305
306   from_base = basename (from);
307   to = xmalloc ((unsigned) (strlen (to_dir) + strlen (from_base) + 2));
308   stpcpy (stpcpy (stpcpy (to, to_dir), "/"), from_base);
309   ret = install_file_in_file (from, to);
310   free (to);
311   return ret;
312 }
313
314 /* A chunk of a file being copied. */
315 static char buffer[READ_SIZE];
316
317 /* Copy file FROM onto file TO, creating TO if necessary.
318    Return 0 if the copy is successful, 1 if not.  If the copy is
319    successful, set *TO_CREATED to non-zero if TO was created (if it did
320    not exist or did, but was unlinked) and to zero otherwise.  If the
321    copy fails, don't modify *TO_CREATED.  */
322
323 static int
324 copy_file (from, to, to_created)
325      char *from;
326      char *to;
327      int *to_created;
328 {
329   int fromfd, tofd;
330   int bytes;
331   int ret = 0;
332   struct stat from_stats, to_stats;
333   int target_created = 1;
334
335   if (stat (from, &from_stats))
336     {
337       error (0, errno, "%s", from);
338       return 1;
339     }
340   if (!S_ISREG (from_stats.st_mode))
341     {
342       error (0, 0, "`%s' is not a regular file", from);
343       return 1;
344     }
345   if (stat (to, &to_stats) == 0)
346     {
347       if (!S_ISREG (to_stats.st_mode))
348         {
349           error (0, 0, "`%s' is not a regular file", to);
350           return 1;
351         }
352       if (from_stats.st_dev == to_stats.st_dev
353           && from_stats.st_ino == to_stats.st_ino)
354         {
355           error (0, 0, "`%s' and `%s' are the same file", from, to);
356           return 1;
357         }
358       /* If unlink fails, try to proceed anyway.  */
359       if (unlink (to))
360         target_created = 0;
361     }
362
363   fromfd = open (from, O_RDONLY, 0);
364   if (fromfd == -1)
365     {
366       error (0, errno, "%s", from);
367       return 1;
368     }
369
370   /* Make sure to open the file in a mode that allows writing. */
371   tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
372   if (tofd == -1)
373     {
374       error (0, errno, "%s", to);
375       close (fromfd);
376       return 1;
377     }
378
379   while ((bytes = safe_read (fromfd, buffer, READ_SIZE)) > 0)
380     if (full_write (tofd, buffer, bytes) < 0)
381       {
382         error (0, errno, "%s", to);
383         goto copy_error;
384       }
385
386   if (bytes == -1)
387     {
388       error (0, errno, "%s", from);
389       goto copy_error;
390     }
391
392   if (close (fromfd) < 0)
393     {
394       error (0, errno, "%s", from);
395       ret = 1;
396     }
397   if (close (tofd) < 0)
398     {
399       error (0, errno, "%s", to);
400       ret = 1;
401     }
402   if (ret == 0)
403     *to_created = target_created;
404   return ret;
405
406  copy_error:
407   close (fromfd);
408   close (tofd);
409   return 1;
410 }
411
412 /* Set the attributes of file or directory PATH.
413    If NO_NEED_TO_CHOWN is non-zero, don't call chown.
414    Return 0 if successful, 1 if not. */
415
416 static int
417 change_attributes (path, no_need_to_chown)
418      char *path;
419      int no_need_to_chown;
420 {
421   int err = 0;
422
423   /* chown must precede chmod because on some systems,
424      chown clears the set[ug]id bits for non-superusers,
425      resulting in incorrect permissions.
426      On System V, users can give away files with chown and then not
427      be able to chmod them.  So don't give files away.
428
429      We don't pass -1 to chown to mean "don't change the value"
430      because SVR3 and earlier non-BSD systems don't support that.
431
432      We don't normally ignore errors from chown because the idea of
433      the install command is that the file is supposed to end up with
434      precisely the attributes that the user specified (or defaulted).
435      If the file doesn't end up with the group they asked for, they'll
436      want to know.  But AFS returns EPERM when you try to change a
437      file's group; thus the kludge.  */
438
439   if (!no_need_to_chown && chown (path, owner_id, group_id)
440 #ifdef AFS
441       && errno != EPERM
442 #endif
443       )
444     err = errno;
445   if (chmod (path, mode))
446     err = errno;
447   if (err)
448     {
449       error (0, err, "%s", path);
450       return 1;
451     }
452   return 0;
453 }
454
455 /* Strip the symbol table from the file PATH.
456    We could dig the magic number out of the file first to
457    determine whether to strip it, but the header files and
458    magic numbers vary so much from system to system that making
459    it portable would be very difficult.  Not worth the effort. */
460
461 static void
462 strip (path)
463      char *path;
464 {
465   int pid, status;
466
467   pid = fork ();
468   switch (pid)
469     {
470     case -1:
471       error (1, errno, "cannot fork");
472       break;
473     case 0:                     /* Child. */
474       execlp ("strip", "strip", path, (char *) NULL);
475       error (1, errno, "cannot run strip");
476       break;
477     default:                    /* Parent. */
478       /* Parent process. */
479       while (pid != wait (&status))     /* Wait for kid to finish. */
480         /* Do nothing. */ ;
481       break;
482     }
483 }
484
485 /* Initialize the user and group ownership of the files to install. */
486
487 static void
488 get_ids ()
489 {
490   struct passwd *pw;
491   struct group *gr;
492
493   if (owner_name)
494     {
495       pw = getpwnam (owner_name);
496       if (pw == NULL)
497         {
498           if (!isnumber (owner_name))
499             error (1, 0, "invalid user `%s'", owner_name);
500           owner_id = atoi (owner_name);
501         }
502       else
503         owner_id = pw->pw_uid;
504       endpwent ();
505     }
506   else
507     owner_id = getuid ();
508
509   if (group_name)
510     {
511       gr = getgrnam (group_name);
512       if (gr == NULL)
513         {
514           if (!isnumber (group_name))
515             error (1, 0, "invalid group `%s'", group_name);
516           group_id = atoi (group_name);
517         }
518       else
519         group_id = gr->gr_gid;
520       endgrent ();
521     }
522   else
523     group_id = getgid ();
524 }
525
526 /* Return nonzero if STR is an ASCII representation of a nonzero
527    decimal integer, zero if not. */
528
529 static int
530 isnumber (str)
531      char *str;
532 {
533   if (*str == 0)
534     return 0;
535   for (; *str; str++)
536     if (!ISDIGIT (*str))
537       return 0;
538   return 1;
539 }
540
541 static void
542 usage (status)
543      int status;
544 {
545   if (status != 0)
546     fprintf (stderr, "Try `%s --help' for more information.\n",
547              program_name);
548   else
549     {
550       printf ("\
551 Usage: %s [OPTION]... SOURCE DEST           (1st format)\n\
552   or:  %s [OPTION]... SOURCE... DIRECTORY   (2nd format)\n\
553   or:  %s [OPTION]... DIRECTORY...          (3nd format)\n\
554 ",
555               program_name, program_name, program_name);
556       printf ("\
557 \n\
558   -c                  (ignored)\n\
559   -d, --directory     create [leading] directories, mandatory for 3rd format\n\
560   -g, --group=GROUP   set group ownership, instead of process' current group\n\
561   -m, --mode=MODE     set permission mode (as in chmod), instead of 0755\n\
562   -o, --owner=OWNER   set ownership (super-user only)\n\
563   -s, --strip         strip symbol tables, only for 1st and 2nd formats\n\
564       --help          display this help and exit\n\
565       --version       output version information and exit\n");
566     }
567   exit (status);
568 }