merge with 3.8.3e
[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 make_path ();
109 int isdir ();
110
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 ();
119
120 /* The name this program was run with, for error messages. */
121 char *program_name;
122
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;
126
127 /* The user ID corresponding to `owner_name'. */
128 static uid_t owner_id;
129
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;
133
134 /* The group ID corresponding to `group_name'. */
135 static gid_t group_id;
136
137 /* The permissions to which the files will be set.  The umask has
138    no effect. */
139 static int mode;
140
141 /* If nonzero, strip executable files after copying them. */
142 static int strip_files;
143
144 /* If nonzero, install a directory instead of a regular file. */
145 static int dir_arg;
146
147 /* If non-zero, display usage information and exit.  */
148 static int show_help;
149
150 /* If non-zero, print the version on standard output and exit.  */
151 static int show_version;
152
153 static struct option const long_options[] =
154 {
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},
162   {NULL, 0, NULL, 0}
163 };
164
165 void
166 main (argc, argv)
167      int argc;
168      char **argv;
169 {
170   int optc;
171   int errors = 0;
172   char *symbolic_mode = NULL;
173
174   program_name = argv[0];
175   owner_name = NULL;
176   group_name = NULL;
177   mode = 0755;
178   strip_files = 0;
179   dir_arg = 0;
180   umask (0);
181
182   while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
183                               (int *) 0)) != EOF)
184     {
185       switch (optc)
186         {
187         case 0:
188           break;
189         case 'c':
190           break;
191         case 's':
192           strip_files = 1;
193           break;
194         case 'd':
195           dir_arg = 1;
196           break;
197         case 'g':
198           group_name = optarg;
199           break;
200         case 'm':
201           symbolic_mode = optarg;
202           break;
203         case 'o':
204           owner_name = optarg;
205           break;
206         default:
207           usage (1);
208         }
209     }
210
211   if (show_version)
212     {
213       printf ("%s\n", version_string);
214       exit (0);
215     }
216
217   if (show_help)
218     usage (0);
219
220   /* Check for invalid combinations of arguments. */
221   if ((dir_arg && strip_files)
222       || (optind == argc)
223       || (optind == argc - 1 && !dir_arg))
224     usage (1);
225
226   if (symbolic_mode)
227     {
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);
234     }
235
236   get_ids ();
237
238   if (dir_arg)
239     {
240       for (; optind < argc; ++optind)
241         {
242           errors |=
243             make_path (argv[optind], mode, mode, owner_id, group_id, NULL);
244         }
245     }
246   else
247     {
248       if (optind == argc - 2)
249         {
250           if (!isdir (argv[argc - 1]))
251             errors = install_file_in_file (argv[argc - 2], argv[argc - 1]);
252           else
253             errors = install_file_in_dir (argv[argc - 2], argv[argc - 1]);
254         }
255       else
256         {
257           if (!isdir (argv[argc - 1]))
258             usage (1);
259           for (; optind < argc - 1; ++optind)
260             {
261               errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
262             }
263         }
264     }
265
266   exit (errors);
267 }
268
269 /* Copy file FROM onto file TO and give TO the appropriate
270    attributes.
271    Return 0 if successful, 1 if an error occurs. */
272
273 static int
274 install_file_in_file (from, to)
275      char *from;
276      char *to;
277 {
278   int to_created;
279   int no_need_to_chown;
280
281   if (copy_file (from, to, &to_created))
282     return 1;
283   if (strip_files)
284     strip (to);
285   no_need_to_chown = (to_created
286                       && owner_name == NULL
287                       && group_name == NULL);
288   return change_attributes (to, no_need_to_chown);
289 }
290
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. */
294
295 static int
296 install_file_in_dir (from, to_dir)
297      char *from;
298      char *to_dir;
299 {
300   char *from_base;
301   char *to;
302   int ret;
303
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);
308   free (to);
309   return ret;
310 }
311
312 /* A chunk of a file being copied. */
313 static char buffer[READ_SIZE];
314
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.  */
320
321 static int
322 copy_file (from, to, to_created)
323      char *from;
324      char *to;
325      int *to_created;
326 {
327   int fromfd, tofd;
328   int bytes;
329   int ret = 0;
330   struct stat from_stats, to_stats;
331   int target_created = 1;
332
333   if (stat (from, &from_stats))
334     {
335       error (0, errno, "%s", from);
336       return 1;
337     }
338   if (!S_ISREG (from_stats.st_mode))
339     {
340       error (0, 0, "`%s' is not a regular file", from);
341       return 1;
342     }
343   if (stat (to, &to_stats) == 0)
344     {
345       if (!S_ISREG (to_stats.st_mode))
346         {
347           error (0, 0, "`%s' is not a regular file", to);
348           return 1;
349         }
350       if (from_stats.st_dev == to_stats.st_dev
351           && from_stats.st_ino == to_stats.st_ino)
352         {
353           error (0, 0, "`%s' and `%s' are the same file", from, to);
354           return 1;
355         }
356       /* If unlink fails, try to proceed anyway.  */
357       if (unlink (to))
358         target_created = 0;
359     }
360
361   fromfd = open (from, O_RDONLY, 0);
362   if (fromfd == -1)
363     {
364       error (0, errno, "%s", from);
365       return 1;
366     }
367
368   /* Make sure to open the file in a mode that allows writing. */
369   tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
370   if (tofd == -1)
371     {
372       error (0, errno, "%s", to);
373       close (fromfd);
374       return 1;
375     }
376
377   while ((bytes = read (fromfd, buffer, READ_SIZE)) > 0)
378     if (write (tofd, buffer, bytes) != bytes)
379       {
380         error (0, errno, "%s", to);
381         goto copy_error;
382       }
383
384   if (bytes == -1)
385     {
386       error (0, errno, "%s", from);
387       goto copy_error;
388     }
389
390   if (close (fromfd) < 0)
391     {
392       error (0, errno, "%s", from);
393       ret = 1;
394     }
395   if (close (tofd) < 0)
396     {
397       error (0, errno, "%s", to);
398       ret = 1;
399     }
400   if (ret == 0)
401     *to_created = target_created;
402   return ret;
403
404  copy_error:
405   close (fromfd);
406   close (tofd);
407   return 1;
408 }
409
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. */
413
414 static int
415 change_attributes (path, no_need_to_chown)
416      char *path;
417      int no_need_to_chown;
418 {
419   int err = 0;
420
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.
426
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.
429
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.  */
436
437   if (!no_need_to_chown && chown (path, owner_id, group_id)
438 #ifdef AFS
439       && errno != EPERM
440 #endif
441       )
442     err = errno;
443   if (chmod (path, mode))
444     err = errno;
445   if (err)
446     {
447       error (0, err, "%s", path);
448       return 1;
449     }
450   return 0;
451 }
452
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. */
458
459 static void
460 strip (path)
461      char *path;
462 {
463   int pid, status;
464
465   pid = fork ();
466   switch (pid)
467     {
468     case -1:
469       error (1, errno, "cannot fork");
470       break;
471     case 0:                     /* Child. */
472       execlp ("strip", "strip", path, (char *) NULL);
473       error (1, errno, "cannot run strip");
474       break;
475     default:                    /* Parent. */
476       /* Parent process. */
477       while (pid != wait (&status))     /* Wait for kid to finish. */
478         /* Do nothing. */ ;
479       break;
480     }
481 }
482
483 /* Initialize the user and group ownership of the files to install. */
484
485 static void
486 get_ids ()
487 {
488   struct passwd *pw;
489   struct group *gr;
490
491   if (owner_name)
492     {
493       pw = getpwnam (owner_name);
494       if (pw == NULL)
495         {
496           if (!isnumber (owner_name))
497             error (1, 0, "invalid user `%s'", owner_name);
498           owner_id = atoi (owner_name);
499         }
500       else
501         owner_id = pw->pw_uid;
502       endpwent ();
503     }
504   else
505     owner_id = getuid ();
506
507   if (group_name)
508     {
509       gr = getgrnam (group_name);
510       if (gr == NULL)
511         {
512           if (!isnumber (group_name))
513             error (1, 0, "invalid group `%s'", group_name);
514           group_id = atoi (group_name);
515         }
516       else
517         group_id = gr->gr_gid;
518       endgrent ();
519     }
520   else
521     group_id = getgid ();
522 }
523
524 /* Return nonzero if STR is an ASCII representation of a nonzero
525    decimal integer, zero if not. */
526
527 static int
528 isnumber (str)
529      char *str;
530 {
531   if (*str == 0)
532     return 0;
533   for (; *str; str++)
534     if (!ISDIGIT (*str))
535       return 0;
536   return 1;
537 }
538
539 static void
540 usage (status)
541      int status;
542 {
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\
547 \n",
548             program_name, program_name, program_name);
549
550   if (status == 0)
551     fprintf (stdout, "\
552   -c                  (ignored)\n\
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");
560
561   else
562     fprintf (stderr, "Try `%s --help' for more information.\n",
563              program_name);
564
565   exit (status);
566 }