(main): Include program name in --version output.
[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 #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
77
78 #ifdef _POSIX_SOURCE
79 #define endgrent()
80 #define endpwent()
81 #endif
82
83 /* True if C is an ASCII octal digit. */
84 #define isodigit(c) ((c) >= '0' && c <= '7')
85
86 /* Number of bytes of a file to copy at a time. */
87 #define READ_SIZE (32 * 1024)
88
89 char *basename ();
90 char *stpcpy ();
91 char *xmalloc ();
92 int safe_read ();
93 int full_write ();
94 int isdir ();
95
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 ();
104
105 /* The name this program was run with, for error messages. */
106 char *program_name;
107
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;
111
112 /* The user ID corresponding to `owner_name'. */
113 static uid_t owner_id;
114
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;
118
119 /* The group ID corresponding to `group_name'. */
120 static gid_t group_id;
121
122 /* The permissions to which the files will be set.  The umask has
123    no effect. */
124 static int mode;
125
126 /* If nonzero, strip executable files after copying them. */
127 static int strip_files;
128
129 /* If nonzero, install a directory instead of a regular file. */
130 static int dir_arg;
131
132 /* If non-zero, display usage information and exit.  */
133 static int show_help;
134
135 /* If non-zero, print the version on standard output and exit.  */
136 static int show_version;
137
138 static struct option const long_options[] =
139 {
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}
148 };
149
150 void
151 main (argc, argv)
152      int argc;
153      char **argv;
154 {
155   int optc;
156   int errors = 0;
157   char *symbolic_mode = NULL;
158
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);
166
167   while ((optc = getopt_long (argc, argv, "csdg:m:o:", long_options,
168                               (int *) 0)) != EOF)
169     {
170       switch (optc)
171         {
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);
193         }
194     }
195
196   if (show_version)
197     {
198       printf ("install - %s\n", version_string);
199       exit (0);
200     }
201
202   if (show_help)
203     usage (0);
204
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");
209
210   if (optind == argc || (optind == argc - 1 && !dir_arg))
211     {
212       error (0, 0, "too few arguments");
213       usage (1);
214     }
215
216   if (symbolic_mode)
217     {
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);
224     }
225
226   get_ids ();
227
228   if (dir_arg)
229     {
230       for (; optind < argc; ++optind)
231         {
232           errors |=
233             make_path (argv[optind], mode, mode, owner_id, group_id, 0, NULL);
234         }
235     }
236   else
237     {
238       if (optind == argc - 2)
239         {
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]);
244         }
245       else
246         {
247           if (!isdir (argv[argc - 1]))
248             usage (1);
249           for (; optind < argc - 1; ++optind)
250             {
251               errors |= install_file_in_dir (argv[optind], argv[argc - 1]);
252             }
253         }
254     }
255
256   exit (errors);
257 }
258
259 /* Copy file FROM onto file TO and give TO the appropriate
260    attributes.
261    Return 0 if successful, 1 if an error occurs. */
262
263 static int
264 install_file_in_file (from, to)
265      char *from;
266      char *to;
267 {
268   int to_created;
269   int no_need_to_chown;
270
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);
279 }
280
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. */
284
285 static int
286 install_file_in_dir (from, to_dir)
287      char *from;
288      char *to_dir;
289 {
290   char *from_base;
291   char *to;
292   int ret;
293
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;
300 }
301
302 /* A chunk of a file being copied. */
303 static char buffer[READ_SIZE];
304
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.  */
310
311 static int
312 copy_file (from, to, to_created)
313      char *from;
314      char *to;
315      int *to_created;
316 {
317   int fromfd, tofd;
318   int bytes;
319   int ret = 0;
320   struct stat from_stats, to_stats;
321   int target_created = 1;
322
323   if (stat (from, &from_stats))
324     {
325       error (0, errno, "%s", from);
326       return 1;
327     }
328   if (!S_ISREG (from_stats.st_mode))
329     {
330       error (0, 0, "`%s' is not a regular file", from);
331       return 1;
332     }
333   if (stat (to, &to_stats) == 0)
334     {
335       if (!S_ISREG (to_stats.st_mode))
336         {
337           error (0, 0, "`%s' is not a regular file", to);
338           return 1;
339         }
340       if (from_stats.st_dev == to_stats.st_dev
341           && from_stats.st_ino == to_stats.st_ino)
342         {
343           error (0, 0, "`%s' and `%s' are the same file", from, to);
344           return 1;
345         }
346       /* If unlink fails, try to proceed anyway.  */
347       if (unlink (to))
348         target_created = 0;
349     }
350
351   fromfd = open (from, O_RDONLY, 0);
352   if (fromfd == -1)
353     {
354       error (0, errno, "%s", from);
355       return 1;
356     }
357
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)
361     {
362       error (0, errno, "%s", to);
363       close (fromfd);
364       return 1;
365     }
366
367   while ((bytes = safe_read (fromfd, buffer, READ_SIZE)) > 0)
368     if (full_write (tofd, buffer, bytes) < 0)
369       {
370         error (0, errno, "%s", to);
371         goto copy_error;
372       }
373
374   if (bytes == -1)
375     {
376       error (0, errno, "%s", from);
377       goto copy_error;
378     }
379
380   if (close (fromfd) < 0)
381     {
382       error (0, errno, "%s", from);
383       ret = 1;
384     }
385   if (close (tofd) < 0)
386     {
387       error (0, errno, "%s", to);
388       ret = 1;
389     }
390   if (ret == 0)
391     *to_created = target_created;
392   return ret;
393
394  copy_error:
395   close (fromfd);
396   close (tofd);
397   return 1;
398 }
399
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. */
403
404 static int
405 change_attributes (path, no_need_to_chown)
406      char *path;
407      int no_need_to_chown;
408 {
409   int err = 0;
410
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.
416
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.
419
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.  */
426
427   if (!no_need_to_chown && chown (path, owner_id, group_id)
428 #ifdef AFS
429       && errno != EPERM
430 #endif
431       )
432     err = errno;
433   if (chmod (path, mode))
434     err = errno;
435   if (err)
436     {
437       error (0, err, "%s", path);
438       return 1;
439     }
440   return 0;
441 }
442
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. */
448
449 static void
450 strip (path)
451      char *path;
452 {
453   int pid, status;
454
455   pid = fork ();
456   switch (pid)
457     {
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;
470     }
471 }
472
473 /* Initialize the user and group ownership of the files to install. */
474
475 static void
476 get_ids ()
477 {
478   struct passwd *pw;
479   struct group *gr;
480
481   if (owner_name)
482     {
483       pw = getpwnam (owner_name);
484       if (pw == NULL)
485         {
486           if (!isnumber (owner_name))
487             error (1, 0, "invalid user `%s'", owner_name);
488           owner_id = atoi (owner_name);
489         }
490       else
491         owner_id = pw->pw_uid;
492       endpwent ();
493     }
494   else
495     owner_id = getuid ();
496
497   if (group_name)
498     {
499       gr = getgrnam (group_name);
500       if (gr == NULL)
501         {
502           if (!isnumber (group_name))
503             error (1, 0, "invalid group `%s'", group_name);
504           group_id = atoi (group_name);
505         }
506       else
507         group_id = gr->gr_gid;
508       endgrent ();
509     }
510   else
511     group_id = getgid ();
512 }
513
514 /* Return nonzero if STR is an ASCII representation of a nonzero
515    decimal integer, zero if not. */
516
517 static int
518 isnumber (str)
519      char *str;
520 {
521   if (*str == 0)
522     return 0;
523   for (; *str; str++)
524     if (!ISDIGIT (*str))
525       return 0;
526   return 1;
527 }
528
529 static void
530 usage (status)
531      int status;
532 {
533   if (status != 0)
534     fprintf (stderr, "Try `%s --help' for more information.\n",
535              program_name);
536   else
537     {
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\
542 ",
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\
548 \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");
557     }
558   exit (status);
559 }