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