source: trunk/athena/bin/install/install.c @ 9437

Revision 9437, 9.2 KB checked in by ghudson, 28 years ago (diff)
Convert to Athena coding standards and build system. Also fix a couple of bugs: the target file was not always properly removed on error, and "strip" was run via system() without regard to quoting problems.
Line 
1/*
2 * Copyright (c) 1987 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley.  The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18/* Copyright 1996 by the Massachusetts Institute of Technology.
19 *
20 * Permission to use, copy, modify, and distribute this
21 * software and its documentation for any purpose and without
22 * fee is hereby granted, provided that the above copyright
23 * notice appear in all copies and that both that copyright
24 * notice and this permission notice appear in supporting
25 * documentation, and that the name of M.I.T. not be used in
26 * advertising or publicity pertaining to distribution of the
27 * software without specific, written prior permission.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose.  It is provided "as is"
30 * without express or implied warranty.
31 */
32
33static char rcsid[] = "$Id: install.c,v 1.5 1996-12-16 08:29:02 ghudson Exp $";
34
35#include <sys/types.h>
36#include <sys/param.h>
37#include <sys/stat.h>
38#include <sys/time.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <grp.h>
42#include <pwd.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <ctype.h>
47#include <limits.h>
48
49#define MAXARGS 1024
50
51#ifndef MAXBSIZE
52#define MAXBSIZE 10240
53#endif
54
55#ifndef MIN
56#define MIN(a,b) (((a) < (b)) ? (a) : (b))
57#endif
58
59static uid_t uid;
60static gid_t gid;
61
62static int docopy = 0;
63static int dostrip = 0;
64static int domove = 0;
65static int dotime = 0;
66static int multiple = 0;
67static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
68
69static char *group;
70static char *owner;
71static char pathbuf[MAXPATHLEN];
72
73static void install(const char *from_name, const char *to_name, int isdir);
74static void copy(int from_fd, const char *from_name, int to_fd,
75                 const char *to_name);
76static void strip(const char *name);
77static int isnumber(const char *s);
78static int atoo(const char *str);
79static void bad(const char *head, const char *str);
80static void usage(void);
81
82int main(int cmdline_argc, char **cmdline_argv)
83{
84  extern char *optarg;
85  extern int optind;
86  struct stat from_sb, to_sb;
87  int ch, no_target;
88  char *to_name;
89  struct passwd *pp;
90  struct group *gp;
91  int argc = 1;
92  char *args[MAXARGS], **argv = args;
93  char *inst_env;
94
95  /* Copy any command-line arguments from INSTOPT into argv. */
96  inst_env = getenv("INSTOPT");
97  if (inst_env)
98    {
99      while (isspace(*inst_env))
100        inst_env++;
101      while (*inst_env)
102        {
103          argv[argc++] = inst_env;
104          while (*inst_env && !isspace(*inst_env))
105            inst_env++;
106          if (*inst_env)
107            *inst_env++ = 0;
108          while (isspace(*inst_env))
109            inst_env++;
110        }
111    }
112
113  if (argc + cmdline_argc > MAXARGS)
114    {
115      fprintf(stderr, "install: too many command-line arguments.\n");
116      return 1;
117    }
118
119  /* Copy the original arguments into argv. */
120  argv[0] = *cmdline_argv++;
121  while (--cmdline_argc)
122    argv[argc++] = *cmdline_argv++;
123
124  while ((ch = getopt(argc, argv, "cdstg:m:o:")) != EOF)
125    {
126      switch(ch)
127        {
128        case 'c':
129          docopy = 1;
130          break;
131        case 'd':
132          domove = 1;
133          break;
134        case 'g':
135          group = optarg;
136          break;
137        case 'm':
138          mode = atoo(optarg);
139          break;
140        case 'o':
141          owner = optarg;
142          break;
143        case 's':
144          dostrip = 1;
145          break;
146        case 't':
147          dotime = 1;
148          break;
149        case '?':
150        default:
151          usage();
152        }
153    }
154  argc -= optind;
155  argv += optind;
156  if (argc < 2)
157    usage();
158
159  /* Check for multiple specifications of copy and move. */
160  if (domove && docopy)
161    {
162      fprintf(stderr, "install: cannot specify both -c and -d\n");
163      return 1;
164    }
165
166  /* If neither copy nor move specified, do copy. */
167  if (!domove)
168    docopy = 1;
169
170  /* Get group and owner ids. */
171  if (owner)
172    {
173      to_name = strchr(owner, '.');
174      if (to_name)
175        {
176          *to_name++ = '\0';
177          if (!group)
178            group = to_name;
179          else
180            {
181              fputs("install: multiple specification of the group\n", stderr);
182              return 1;
183            }
184        }
185      if (!isnumber(owner))
186        {
187          pp = getpwnam(owner);
188          if (!pp)
189            {
190              fprintf(stderr, "install: unknown user %s.\n", owner);
191              return 1;
192            }
193          else
194            uid = pp->pw_uid;
195        }
196      else
197        uid = atoi(owner);
198    }
199  else
200    uid = -1;
201
202  if (group)
203    {
204      if (!isnumber(group))
205        {
206          gp = getgrnam(group);
207          if (!gp)
208            {
209              fprintf(stderr, "install: unknown group %s.\n", group);
210              return 1;
211            }
212          else
213            gid = gp->gr_gid;
214        }
215      else
216        gid = atoi(group);
217    }
218  else
219    gid = -1;
220
221  to_name = argv[argc - 1];
222  no_target = stat(to_name, &to_sb);
223  if (!no_target && S_ISDIR(to_sb.st_mode))
224    {
225      for (; *argv != to_name; argv++)
226        install(*argv, to_name, 1);
227      return 0;
228    }
229
230  /* can't do file1 file2 directory/file */
231  if (argc != 2)
232    usage();
233
234  if (!no_target)
235    {
236      if (stat(*argv, &from_sb))
237        {
238          fprintf(stderr, "install: can't find %s.\n", *argv);
239          return 1;
240        }
241      if (!S_ISREG(to_sb.st_mode))
242        {
243          fprintf(stderr, "install: %s isn't a regular file.\n", to_name);
244          exit(1);
245        }
246      if (to_sb.st_dev == from_sb.st_dev && to_sb.st_ino == from_sb.st_ino)
247        {
248          fprintf(stderr, "install: %s and %s are the same file.\n", *argv,
249                  to_name);
250          return 1;
251        }
252
253      /* Unlink now, avoid ETXTBSY errors later. */
254      unlink(to_name);
255    }
256  install(*argv, to_name, 0);
257  return 0;
258}
259
260/* install -- build a path name and install the file */
261static void install(const char *from_name, const char *to_name, int isdir)
262{
263  struct stat from_sb;
264  struct timeval timep[2];
265  int devnull, from_fd, to_fd;
266  const char *c;
267
268  /* If trying to install "/dev/null" to a directory, fail. */
269  if (isdir || strcmp(from_name, "/dev/null"))
270    {
271      if (stat(from_name, &from_sb))
272        {
273          fprintf(stderr, "install: can't find %s.\n", from_name);
274          exit(1);
275        }
276      if (!S_ISREG(from_sb.st_mode))
277        {
278          fprintf(stderr, "install: %s isn't a regular file.\n", from_name);
279          exit(1);
280        }
281
282      /* Build the target path. */
283      if (isdir)
284        {
285          c = strrchr(from_name, '/');
286          c = (c) ? c + 1 : from_name;
287          sprintf(pathbuf, "%s/%s", to_name, c);
288        }
289      else
290        strcpy(pathbuf, to_name);
291      devnull = 0;
292    }
293  else
294    devnull = 1;
295
296  /* Unlink now, avoid ETXTBSY errors later. */
297  unlink(pathbuf);
298
299  /* Create target. */
300  to_fd = open(pathbuf, O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU);
301  if (to_fd < 0)
302    {
303      fprintf(stderr, "install: %s: %s\n", pathbuf, strerror(errno));
304      exit(1);
305    }
306  if (!devnull)
307    {
308      from_fd = open(from_name, O_RDONLY, 0);
309      if (from_fd < 0)
310        {
311          unlink(pathbuf);
312          bad("install: open: ", from_name);
313          exit(1);
314        }
315      copy(from_fd, from_name, to_fd, pathbuf);
316      close(from_fd);
317      close(to_fd);
318      if (dostrip)
319        strip(pathbuf);
320      if (!docopy)
321        unlink(from_name);
322    }
323  if (dotime)
324    {
325      timep[0].tv_sec = from_sb.st_atime;
326      timep[1].tv_sec = from_sb.st_mtime;
327      timep[0].tv_usec = timep[1].tv_usec = 0;
328      if (utimes(pathbuf, timep))
329        bad("install: utimes", pathbuf);
330    }
331  /* set owner, group, mode. and time for target */
332  if (chmod(pathbuf, mode))
333    bad("install: fchmod", pathbuf);
334  if ((uid != -1 || gid != -1))
335    {
336      uid = (uid != -1) ? uid : from_sb.st_uid;
337      gid = (gid != -1) ? gid : from_sb.st_gid;
338      if (chown(pathbuf, uid, gid) < 0)
339        bad("install: chown: ", to_name);
340    }
341}
342
343/* copy -- copy from one file to another */
344static void copy(int from_fd, const char *from_name, int to_fd,
345                 const char *to_name)
346{
347  int n;
348  char buf[MAXBSIZE];
349
350  while ((n = read(from_fd, buf, sizeof(buf))) > 0)
351    {
352      if (write(to_fd, buf, n) != n)
353        bad("install: write: ", to_name);
354    }
355  if (n == -1)
356      bad("install: read: ", from_name);
357}
358
359static void strip(const char *name)
360{
361  pid_t pid;
362
363  fflush(NULL);
364  pid = fork();
365  if (pid == 0)
366    {
367      execlp(STRIP, STRIP, name, NULL);
368      fprintf(stderr, "Cannot execute %s: %s\n", STRIP, strerror(errno));
369      exit(1);
370    }
371  else if (pid > 0)
372    {
373      while (waitpid(pid, NULL, 0) < 0 && errno == EINTR)
374        ;
375    }
376}
377
378/* isnumber -- determine whether string is a number */
379static int isnumber(const char *s)
380{
381  while(*s)
382    {
383      if (!isdigit(*s))
384        return 0;
385      else
386        s++;
387    }
388  return 1;
389}
390
391/* atoo -- octal string to int */
392static int atoo(const char *str)
393{
394  int val;
395
396  for (val = 0; isdigit(*str); ++str)
397    val = val * 8 + *str - '0';
398  return val;
399}
400
401/* bad -- remove created target and die */
402static void bad(const char *head, const char *str)
403{
404  fprintf(stderr, "%s%s: %s\n", head, str, strerror(errno));
405  unlink(pathbuf);
406  exit(1);
407}
408
409/* usage -- print a usage message and die */
410static void usage()
411{
412  fputs("usage: install [-cds] [-g group] [-m mode] [-o owner]"
413        " file1 file2;\n\tor file1 ... fileN directory\n", stderr);
414  exit(1);
415}
Note: See TracBrowser for help on using the repository browser.