(add_tabstop): Give correct size when reallocating tab_list buffer.
[platform/upstream/coreutils.git] / src / install.c
1 /* install - copy files and set attributes
2    Copyright (C) 1989, 1990, 1991, 1995 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 #include <config.h>
56 #include <stdio.h>
57 #include <getopt.h>
58 #include <sys/types.h>
59 #include <pwd.h>
60 #include <grp.h>
61
62 #include "system.h"
63 #include "version.h"
64 #include "safe-stat.h"
65 #include "modechange.h"
66 #include "makepath.h"
67 #include "error.h"
68
69 #ifdef _POSIX_VERSION
70 #include <sys/wait.h>
71 #else
72 struct passwd *getpwnam ();
73 struct group *getgrnam ();
74 uid_t getuid ();
75 gid_t getgid ();
76 int wait ();
77 #endif
78
79 #ifdef _POSIX_SOURCE
80 #define endgrent()
81 #define endpwent()
82 #endif
83
84 /* True if C is an ASCII octal digit. */
85 #define isodigit(c) ((c) >= '0' && c <= '7')
86
87 /* Number of bytes of a file to copy at a time. */
88 #define READ_SIZE (32 * 1024)
89
90 char *basename ();
91 char *stpcpy ();
92 char *xmalloc ();
93 int safe_read ();
94 int full_write ();
95 int isdir ();
96
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 ();
105
106 /* The name this program was run with, for error messages. */
107 char *program_name;
108
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;
112
113 /* The user ID corresponding to `owner_name'. */
114 static uid_t owner_id;
115
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;
119
120 /* The group ID corresponding to `group_name'. */
121 static gid_t group_id;
122
123 /* The permissions to which the files will be set.  The umask has
124    no effect. */
125 static int mode;
126
127 /* If nonzero, strip executable files after copying them. */
128 static int strip_files;
129
130 /* If nonzero, install a directory instead of a regular file. */
131 static int dir_arg;
132
133 /* If non-zero, display usage information and exit.  */
134 static int show_help;
135
136 /* If non-zero, print the version on standard output and exit.  */
137 static int show_version;
138
139 static struct option const long_options[] =
140 {
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},
148   {NULL, 0, NULL, 0}
149 };
150
151 void
152 main (argc, argv)
153      int argc;
154      char **argv;
155 {
156   int optc;
157   int errors = 0;
158   char *symbolic_mode = NULL;
159
160   program_name = argv[0];
161   owner_name = NULL;
162   group_name = NULL;
163   mode = 0755;
164   strip_files = 0;
165   dir_arg = 0;
166   umask (0);
167
168   while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
169                               (int *) 0)) != EOF)
170     {
171       switch (optc)
172         {
173         case 0:
174           break;
175         case 'c':
176           break;
177         case 's':
178           strip_files = 1;
179           break;
180         case 'd':
181           dir_arg = 1;
182           break;
183         case 'g':
184           group_name = optarg;
185           break;
186         case 'm':
187           symbolic_mode = optarg;
188           break;
189         case 'o':
190           owner_name = optarg;
191           break;
192         default:
193           usage (1);
194         }
195     }
196
197   if (show_version)
198     {
199       printf ("%s\n", version_string);
200       exit (0);
201     }
202
203   if (show_help)
204     usage (0);
205
206   /* Check for invalid combinations of arguments. */
207   if (dir_arg && strip_files)
208     error (1, 0,
209            "the strip option may not be used when installing a directory");
210
211   if (optind == argc || (optind == argc - 1 && !dir_arg))
212     {
213       error (0, 0, "too few arguments");
214       usage (1);
215     }
216
217   if (symbolic_mode)
218     {
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);
225     }
226
227   get_ids ();
228
229   if (dir_arg)
230     {
231       for (; optind < argc; ++optind)
232         {
233           errors |=
234             make_path (argv[optind], mode, mode, owner_id, group_id, 0, NULL);
235         }
236     }
237   else
238     {
239       if (optind == argc - 2)
240         {
241           if (!isdir (argv[argc - 1]))
242             errors = install_file_in_file (argv[argc - 2], argv[argc - 1]);
243           else
244             errors = install_file_in_dir (argv[argc - 2], argv[argc - 1]);
245         }
246       else
247         {
248           if (!isdir (argv[argc - 1]))
249             usage (1);
250           for (; optind < argc - 1; ++optind)
251             {
252               errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
253             }
254         }
255     }
256
257   exit (errors);
258 }
259
260 /* Copy file FROM onto file TO and give TO the appropriate
261    attributes.
262    Return 0 if successful, 1 if an error occurs. */
263
264 static int
265 install_file_in_file (from, to)
266      char *from;
267      char *to;
268 {
269   int to_created;
270   int no_need_to_chown;
271
272   if (copy_file (from, to, &to_created))
273     return 1;
274   if (strip_files)
275     strip (to);
276   no_need_to_chown = (to_created
277                       && owner_name == NULL
278                       && group_name == NULL);
279   return change_attributes (to, no_need_to_chown);
280 }
281
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. */
285
286 static int
287 install_file_in_dir (from, to_dir)
288      char *from;
289      char *to_dir;
290 {
291   char *from_base;
292   char *to;
293   int ret;
294
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);
299   free (to);
300   return ret;
301 }
302
303 /* A chunk of a file being copied. */
304 static char buffer[READ_SIZE];
305
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.  */
311
312 static int
313 copy_file (from, to, to_created)
314      char *from;
315      char *to;
316      int *to_created;
317 {
318   int fromfd, tofd;
319   int bytes;
320   int ret = 0;
321   struct stat from_stats, to_stats;
322   int target_created = 1;
323
324   if (safe_stat (from, &from_stats))
325     {
326       error (0, errno, "%s", from);
327       return 1;
328     }
329   if (!S_ISREG (from_stats.st_mode))
330     {
331       error (0, 0, "`%s' is not a regular file", from);
332       return 1;
333     }
334   if (safe_stat (to, &to_stats) == 0)
335     {
336       if (!S_ISREG (to_stats.st_mode))
337         {
338           error (0, 0, "`%s' is not a regular file", to);
339           return 1;
340         }
341       if (from_stats.st_dev == to_stats.st_dev
342           && from_stats.st_ino == to_stats.st_ino)
343         {
344           error (0, 0, "`%s' and `%s' are the same file", from, to);
345           return 1;
346         }
347       /* If unlink fails, try to proceed anyway.  */
348       if (unlink (to))
349         target_created = 0;
350     }
351
352   fromfd = open (from, O_RDONLY, 0);
353   if (fromfd == -1)
354     {
355       error (0, errno, "%s", from);
356       return 1;
357     }
358
359   /* Make sure to open the file in a mode that allows writing. */
360   tofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
361   if (tofd == -1)
362     {
363       error (0, errno, "%s", to);
364       close (fromfd);
365       return 1;
366     }
367
368   while ((bytes = safe_read (fromfd, buffer, READ_SIZE)) > 0)
369     if (full_write (tofd, buffer, bytes) < 0)
370       {
371         error (0, errno, "%s", to);
372         goto copy_error;
373       }
374
375   if (bytes == -1)
376     {
377       error (0, errno, "%s", from);
378       goto copy_error;
379     }
380
381   if (close (fromfd) < 0)
382     {
383       error (0, errno, "%s", from);
384       ret = 1;
385     }
386   if (close (tofd) < 0)
387     {
388       error (0, errno, "%s", to);
389       ret = 1;
390     }
391   if (ret == 0)
392     *to_created = target_created;
393   return ret;
394
395  copy_error:
396   close (fromfd);
397   close (tofd);
398   return 1;
399 }
400
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. */
404
405 static int
406 change_attributes (path, no_need_to_chown)
407      char *path;
408      int no_need_to_chown;
409 {
410   int err = 0;
411
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.
417
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.
420
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.  */
427
428   if (!no_need_to_chown && chown (path, owner_id, group_id)
429 #ifdef AFS
430       && errno != EPERM
431 #endif
432       )
433     err = errno;
434   if (chmod (path, mode))
435     err = errno;
436   if (err)
437     {
438       error (0, err, "%s", path);
439       return 1;
440     }
441   return 0;
442 }
443
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. */
449
450 static void
451 strip (path)
452      char *path;
453 {
454   int pid, status;
455
456   pid = fork ();
457   switch (pid)
458     {
459     case -1:
460       error (1, errno, "cannot fork");
461       break;
462     case 0:                     /* Child. */
463       execlp ("strip", "strip", path, (char *) NULL);
464       error (1, errno, "cannot run strip");
465       break;
466     default:                    /* Parent. */
467       /* Parent process. */
468       while (pid != wait (&status))     /* Wait for kid to finish. */
469         /* Do nothing. */ ;
470       break;
471     }
472 }
473
474 /* Initialize the user and group ownership of the files to install. */
475
476 static void
477 get_ids ()
478 {
479   struct passwd *pw;
480   struct group *gr;
481
482   if (owner_name)
483     {
484       pw = getpwnam (owner_name);
485       if (pw == NULL)
486         {
487           if (!isnumber (owner_name))
488             error (1, 0, "invalid user `%s'", owner_name);
489           owner_id = atoi (owner_name);
490         }
491       else
492         owner_id = pw->pw_uid;
493       endpwent ();
494     }
495   else
496     owner_id = getuid ();
497
498   if (group_name)
499     {
500       gr = getgrnam (group_name);
501       if (gr == NULL)
502         {
503           if (!isnumber (group_name))
504             error (1, 0, "invalid group `%s'", group_name);
505           group_id = atoi (group_name);
506         }
507       else
508         group_id = gr->gr_gid;
509       endgrent ();
510     }
511   else
512     group_id = getgid ();
513 }
514
515 /* Return nonzero if STR is an ASCII representation of a nonzero
516    decimal integer, zero if not. */
517
518 static int
519 isnumber (str)
520      char *str;
521 {
522   if (*str == 0)
523     return 0;
524   for (; *str; str++)
525     if (!ISDIGIT (*str))
526       return 0;
527   return 1;
528 }
529
530 static void
531 usage (status)
532      int status;
533 {
534   if (status != 0)
535     fprintf (stderr, "Try `%s --help' for more information.\n",
536              program_name);
537   else
538     {
539       printf ("\
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\
543 ",
544               program_name, program_name, program_name);
545       printf ("\
546 \n\
547   -c                  (ignored)\n\
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");
555     }
556   exit (status);
557 }