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