master xplshn/aruu / cmd / extra / install.c
  1/* see LICENSE file for copyright and license details */
  2#include <sys/stat.h>
  3#include <sys/types.h>
  4
  5#include <errno.h>
  6#include <fcntl.h>
  7#include <grp.h>
  8#include <libgen.h>
  9#include <limits.h>
 10#include <pwd.h>
 11#include <stdio.h>
 12#include <stdlib.h>
 13#include <string.h>
 14#include <unistd.h>
 15
 16#include "fs.h"
 17#include "util.h"
 18#include "wexec.h"
 19
 20static int    dflag, Dflag, pflag, sflag;
 21static mode_t mode = 0755;
 22static uid_t  uid  = (uid_t)-1;
 23static gid_t  gid  = (gid_t)-1;
 24static int    status;
 25
 26static void
 27usage(void)
 28{
 29  eprintf(
 30      "usage: %s [-Dps] [-o owner] [-g group] [-m mode] [-t dir] source ... dest\n"
 31      "       %s -d [-Dp] [-o owner] [-g group] [-m mode] dir ...\n",
 32      argv0,
 33      argv0
 34  );
 35}
 36
 37static void
 38lookup_owner(const char *owner, const char *group)
 39{
 40  struct passwd *pw;
 41  struct group  *gr;
 42
 43  if (owner) {
 44    errno = 0;
 45    pw    = getpwnam(owner);
 46    if (pw) {
 47      uid = pw->pw_uid;
 48    } else {
 49      if (errno)
 50        eprintf("getpwnam %s:", owner);
 51      uid = (uid_t)estrtonum(owner, 0, UINT_MAX);
 52    }
 53  }
 54  if (group) {
 55    errno = 0;
 56    gr    = getgrnam(group);
 57    if (gr) {
 58      gid = gr->gr_gid;
 59    } else {
 60      if (errno)
 61        eprintf("getgrnam %s:", group);
 62      gid = (gid_t)estrtonum(group, 0, UINT_MAX);
 63    }
 64  }
 65}
 66
 67static void
 68chown_if_requested(const char *path)
 69{
 70  if (uid == (uid_t)-1 && gid == (gid_t)-1)
 71    return;
 72  if (chown(path, uid, gid) < 0) {
 73    weprintf("chown %s:", path);
 74    status = 1;
 75  }
 76}
 77
 78static void
 79preserve_time(const char *src, const char *dst)
 80{
 81  struct stat     st;
 82  struct timespec times[2];
 83
 84  if (stat(src, &st) < 0) {
 85    weprintf("stat %s:", src);
 86    status = 1;
 87    return;
 88  }
 89  times[0] = st.st_atim;
 90  times[1] = st.st_mtim;
 91  if (utimensat(AT_FDCWD, dst, times, 0) < 0) {
 92    weprintf("utimensat %s:", dst);
 93    status = 1;
 94  }
 95}
 96
 97static void
 98strip_binary(const char *path)
 99{
100  char *args[] = {"strip", "-p", (char *)path, NULL};
101
102  if (wexecvp("strip", args) != 0) {
103    weprintf("strip %s:", path);
104    status = 1;
105  }
106}
107
108// leading path components of dst, not dst itself
109static void
110make_leading_dirs(const char *dst)
111{
112  char  buf[PATH_MAX];
113  char *dir;
114
115  estrlcpy(buf, dst, sizeof(buf));
116  dir = dirname(buf);
117  if (mkdirp(dir, 0755, 0755) < 0 && errno != EEXIST) {
118    weprintf("mkdir %s:", dir);
119    status = 1;
120  }
121}
122
123static void
124install_dirs(char **argv)
125{
126  for (; *argv; argv++) {
127    if (mkdirp(*argv, mode, 0755) < 0 && errno != EEXIST) {
128      weprintf("mkdir %s:", *argv);
129      status = 1;
130      continue;
131    }
132    if (chmod(*argv, mode) < 0) {
133      weprintf("chmod %s:", *argv);
134      status = 1;
135    }
136    chown_if_requested(*argv);
137  }
138}
139
140static void
141install_one(const char *src, const char *dst)
142{
143  if (Dflag)
144    make_leading_dirs(dst);
145
146  cp_fflag  = 1;
147  cp_pflag  = 0;
148  cp_rflag  = 0;
149  cp_follow = 'L';
150  cp(src, dst, 0);
151  if (cp_status) {
152    status = 1;
153    return;
154  }
155
156  if (sflag)
157    strip_binary(dst);
158
159  if (chmod(dst, mode) < 0) {
160    weprintf("chmod %s:", dst);
161    status = 1;
162  }
163  chown_if_requested(dst);
164  if (pflag)
165    preserve_time(src, dst);
166}
167
168// ?man install: copy files and set attributes
169// ?man arguments: source ... dest
170// ?man install copies each source to dest, or into dest when dest is a
171// ?man directory or -t is given, then applies mode/owner/group; with
172// ?man -d, every argument is a directory to create instead
173int
174main(int argc, char *argv[])
175{
176  const char *owner = NULL, *group = NULL, *modestr = NULL, *targetdir = NULL;
177  const char *dest;
178  struct stat st;
179  int         isdir;
180  int         i;
181
182  ARGBEGIN
183  {
184    // ?man -c: ignored; accepted for compatibility with older install callers
185    case 'c':
186      break;
187    // ?man -d: create directories instead of installing files
188    case 'd':
189      dflag = 1;
190      break;
191    // ?man -D: create dest's leading directories before installing
192    case 'D':
193      Dflag = 1;
194      break;
195    // ?man -p: preserve source modification and access times
196    case 'p':
197      pflag = 1;
198      break;
199    // ?man -s: strip the installed file's symbol table
200    case 's':
201      sflag = 1;
202      break;
203    // ?man -o:owner set the installed file's owner (name or uid)
204    case 'o':
205      owner = EARGF(usage());
206      break;
207    // ?man -g:group set the installed file's group (name or gid)
208    case 'g':
209      group = EARGF(usage());
210      break;
211    // ?man -m:mode set the installed file's permissions
212    case 'm':
213      modestr = EARGF(usage());
214      break;
215    // ?man -t:dir install every source into dir
216    case 't':
217      targetdir = EARGF(usage());
218      break;
219    default:
220      usage();
221  }
222  ARGEND;
223
224  if (argc < 1 || (dflag && (targetdir || sflag)))
225    usage();
226  if (modestr)
227    mode = parsemode(modestr, 0, 0);
228  lookup_owner(owner, group);
229
230  if (dflag) {
231    install_dirs(argv);
232    goto shut;
233  }
234
235  if (targetdir) {
236    isdir = 1;
237    dest  = targetdir;
238    if (Dflag && mkdirp(targetdir, 0755, 0755) < 0 && errno != EEXIST) {
239      weprintf("mkdir %s:", targetdir);
240      status = 1;
241    }
242  } else {
243    if (argc < 2)
244      usage();
245    dest  = argv[--argc];
246    isdir = stat(dest, &st) == 0 && S_ISDIR(st.st_mode);
247    if (!isdir && argc > 1)
248      eprintf("%s: not a directory\n", dest);
249  }
250
251  for (i = 0; i < argc; i++) {
252    if (isdir) {
253      char        buf[PATH_MAX], nb[PATH_MAX];
254      const char *base;
255
256      estrlcpy(nb, argv[i], sizeof(nb));
257      base = basename(nb);
258      estrlcpy(buf, dest, sizeof(buf));
259      estrlcat(buf, "/", sizeof(buf));
260      estrlcat(buf, base, sizeof(buf));
261      install_one(argv[i], buf);
262    } else {
263      install_one(argv[i], dest);
264    }
265  }
266
267shut:
268  return fshut(stdout, "<stdout>") || status;
269}