master xplshn/aruu / cmd / pseudo / xinstall.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <dirent.h>
  4#include <errno.h>
  5#include <fcntl.h>
  6#include <grp.h>
  7#include <pwd.h>
  8#include <stdlib.h>
  9#include <string.h>
 10#include <sys/stat.h>
 11#include <sys/wait.h>
 12#include <unistd.h>
 13
 14#include "util.h"
 15
 16static int    Dflag = 0;
 17static gid_t  group;
 18static uid_t  owner;
 19static mode_t mode = 0755;
 20
 21static void
 22make_dir(char *dir, int was_missing)
 23{
 24  if (!mkdir(dir, was_missing ? 0755 : mode)) {
 25    if (!was_missing && (lchown(dir, owner, group) < 0))
 26      eprintf("lchmod %s:", dir);
 27  } else if (errno != EEXIST) {
 28    eprintf("mkdir %s:", dir);
 29  }
 30}
 31
 32static void
 33make_dirs(char *dir, int was_missing)
 34{
 35  char *p;
 36  for (p = strchr(dir + (dir[0] == '/'), '/'); p; p = strchr(p + 1, '/')) {
 37    *p = '\0';
 38    make_dir(dir, was_missing);
 39    *p = '/';
 40  }
 41  make_dir(dir, was_missing);
 42}
 43
 44static int
 45install(const char *s1, const char *s2, int depth)
 46{
 47  int f1, f2;
 48
 49  (void)depth;
 50
 51  if ((f1 = open(s1, O_RDONLY)) < 0)
 52    eprintf("open %s:", s1);
 53  if ((f2 = creat(s2, 0600)) < 0) {
 54    if (unlink(s2) < 0 && errno != ENOENT)
 55      eprintf("unlink %s:", s2);
 56    if ((f2 = creat(s2, 0600)) < 0)
 57      eprintf("creat %s:", s2);
 58  }
 59  if (concat(f1, s1, f2, s2) < 0)
 60    goto fail;
 61  if (fchmod(f2, mode) < 0) {
 62    weprintf("fchmod %s:", s2);
 63    goto fail;
 64  }
 65  if (fchown(f2, owner, group) < 0) {
 66    weprintf("fchown %s:", s2);
 67    goto fail;
 68  }
 69
 70  close(f1);
 71  close(f2);
 72
 73  return 0;
 74
 75fail:
 76  unlink(s2);
 77  exit(1);
 78}
 79
 80static void
 81usage(void)
 82{
 83  eprintf(
 84      "usage: %s [-g group] [-o owner] [-m mode] (-d dir ... | [-D] "
 85      "(-t dest source ... | source ... dest))\n",
 86      argv0
 87  );
 88}
 89
 90// ?man xinstall: copy files and set attributes
 91// ?man arguments: (-d dir ... | (-t dest source ... | source ... dest))
 92// ?man copy files and set their permissions and ownership
 93int
 94main(int argc, char *argv[])
 95{
 96  int            dflag = 0;
 97  char          *gflag = 0;
 98  char          *oflag = 0;
 99  char          *mflag = 0;
100  char          *tflag = 0;
101  struct group  *gr;
102  struct passwd *pw;
103  struct stat    st;
104  char          *p;
105
106  ARGBEGIN
107  {
108    // ?man -c: print count or perform stdout action
109    case 'c':
110      /* no-op for compatibility */
111      break;
112    // ?man -d: specify directory
113    case 'd':
114      dflag = 1;
115      break;
116    // ?man -D: specify option flag
117    case 'D':
118      Dflag = 1;
119      break;
120    // ?man -s: silent mode or print summary
121    case 's':
122      /* no-op for compatibility */
123      break;
124    // ?man -g:str: specify option flag
125    case 'g':
126      gflag = EARGF(usage());
127      break;
128    // ?man -o:str: specify output file
129    case 'o':
130      oflag = EARGF(usage());
131      break;
132    // ?man -m:str: specify mode or limit
133    case 'm':
134      mflag = EARGF(usage());
135      break;
136    // ?man -t:str: sort or specify timestamp
137    case 't':
138      tflag = EARGF(usage());
139      break;
140    default:
141      usage();
142  }
143  ARGEND
144
145  if (argc < 1 + (!tflag & !dflag) || dflag & (Dflag | !!tflag))
146    usage();
147
148  if (gflag) {
149    errno = 0;
150    gr    = getgrnam(gflag);
151    if (gr) {
152      group = gr->gr_gid;
153    } else {
154      if (errno)
155        eprintf("getgrnam %s:", gflag);
156      group = estrtonum(gflag, 0, UINT_MAX);
157    }
158  } else {
159    group = getgid();
160  }
161
162  if (oflag) {
163    errno = 0;
164    pw    = getpwnam(oflag);
165    if (pw) {
166      owner = pw->pw_uid;
167    } else {
168      if (errno)
169        eprintf("getpwnam %s:", oflag);
170      owner = estrtonum(oflag, 0, UINT_MAX);
171    }
172  } else {
173    owner = getuid();
174  }
175
176  if (mflag)
177    mode = parsemode(mflag, mode, 0);
178
179  if (dflag) {
180    for (; *argv; argc--, argv++)
181      make_dirs(*argv, 0);
182    return 0;
183  }
184
185  if (tflag) {
186    argv         = memmove(argv - 1, argv, argc * sizeof(*argv));
187    argv[argc++] = tflag;
188  }
189  if (tflag || argc > 2) {
190    if (stat(argv[argc - 1], &st) < 0) {
191      if ((errno == ENOENT) && Dflag) {
192        make_dirs(argv[argc - 1], 1);
193      } else {
194        eprintf("stat %s:", argv[argc - 1]);
195      }
196    } else if (!S_ISDIR(st.st_mode)) {
197      eprintf("%s: not a directory\n", argv[argc - 1]);
198    }
199  }
200  if (stat(argv[argc - 1], &st) < 0) {
201    if (errno != ENOENT)
202      eprintf("stat %s:", argv[argc - 1]);
203    if (tflag || Dflag || argc > 2) {
204      if ((p = strrchr(argv[argc - 1], '/')) != NULL) {
205        *p = '\0';
206        make_dirs(argv[argc - 1], 1);
207        *p = '/';
208      }
209    }
210  }
211  enmasse(argc, argv, install);
212
213  return 0;
214}