1 /*
2  * install - (BSD style) install files
3  *
4  * Gunnar Ritter, Freiburg i. Br., Germany, March 2003.
5  */
6 /*
7  * Copyright (c) 2003 Gunnar Ritter
8  *
9  * This software is provided 'as-is', without any express or implied
10  * warranty. In no event will the authors be held liable for any damages
11  * arising from the use of this software.
12  *
13  * Permission is granted to anyone to use this software for any purpose,
14  * including commercial applications, and to alter it and redistribute
15  * it freely, subject to the following restrictions:
16  *
17  * 1. The origin of this software must not be misrepresented; you must not
18  *    claim that you wrote the original software. If you use this software
19  *    in a product, an acknowledgment in the product documentation would be
20  *    appreciated but is not required.
21  *
22  * 2. Altered source versions must be plainly marked as such, and must not be
23  *    misrepresented as being the original software.
24  *
25  * 3. This notice may not be removed or altered from any source distribution.
26  */
27 
28 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
29 #define	USED	__attribute__ ((used))
30 #elif defined __GNUC__
31 #define	USED	__attribute__ ((unused))
32 #else
33 #define	USED
34 #endif
35 static const char sccsid[] USED = "@(#)/usr/ucb/install.sl	1.12 (gritter) 5/29/05";
36 
37 #include	<sys/types.h>
38 #include	<sys/stat.h>
39 #include	<fcntl.h>
40 #include	<unistd.h>
41 #include	<stdio.h>
42 #include	<string.h>
43 #include	<stdlib.h>
44 #include	<errno.h>
45 #include	<libgen.h>
46 #include	<limits.h>
47 #include	<pwd.h>
48 #include	<grp.h>
49 
50 enum	okay {
51 	OKAY = 0,
52 	STOP = 1
53 };
54 
55 static int	mflag;			/* -m option present */
56 static int	sflag;			/* strip files */
57 static mode_t	mode = 0755;		/* mode to set */
58 static int	dflag;			/* create directories */
59 static int	gflag;			/* set group */
60 static gid_t	group;			/* group to set */
61 static int	oflag;			/* set owner */
62 static uid_t	owner;			/* owner to set */
63 static int	errcnt;			/* count of errors */
64 static char	*progname;		/* argv[0] to main */
65 
66 void *
srealloc(void * op,size_t size)67 srealloc(void *op, size_t size)
68 {
69 	void	*np;
70 
71 	if ((np = realloc(op, size)) == NULL) {
72 		write(2, "no memory\n", 10);
73 		_exit(077);
74 	}
75 	return np;
76 }
77 
78 void *
smalloc(size_t size)79 smalloc(size_t size)
80 {
81 	return srealloc(NULL, size);
82 }
83 
84 uid_t
getowner(const char * string)85 getowner(const char *string)
86 {
87 	struct passwd	*pwd;
88 	char	*x;
89 	long	val;
90 
91 	if ((pwd = getpwnam(string)) != NULL)
92 		return pwd->pw_uid;
93 	val = strtoul(string, &x, 10);
94 	if (*x != '\0' || *string == '+' || *string == '-') {
95 		fprintf(stderr, "%s: unknown user %s.\n", progname, string);
96 		exit(1);
97 	}
98 	return val;
99 }
100 
101 gid_t
getgroup(const char * string)102 getgroup(const char *string)
103 {
104 	struct group	*grp;
105 	char	*x;
106 	long	val;
107 
108 	if ((grp = getgrnam(string)) != NULL)
109 		return grp->gr_gid;
110 	val = strtoul(string, &x, 10);
111 	if (*x != '\0' || *string == '+' || *string == '-') {
112 		fprintf(stderr, "%s: unknown group %s.\n", progname, string);
113 		exit(1);
114 	}
115 	return val;
116 }
117 
118 void
getpath(const char * path,char ** file,char ** filend,size_t * sz,size_t * slen)119 getpath(const char *path, char **file, char **filend, size_t *sz, size_t *slen)
120 {
121 	*sz = 14 + strlen(path) + 2;
122 	*file = smalloc(*sz);
123 	*filend = *file;
124 	if (path[0] == '/' && path[1] == '\0')
125 		*(*filend)++ = '/';
126 	else {
127 		const char	*cp = path;
128 		while ((*(*filend)++ = *cp++) != '\0');
129 		(*filend)[-1] = '/';
130 	}
131 	*slen = *filend - *file;
132 }
133 
134 void
setpath(const char * base,char ** file,char ** filend,size_t slen,size_t * sz,size_t * ss)135 setpath(const char *base, char **file, char **filend,
136 		size_t slen, size_t *sz, size_t *ss)
137 {
138 	if (slen + (*ss = strlen(base)) >= *sz) {
139 		*sz += slen + *ss + 15;
140 		*file = srealloc(*file, *sz);
141 		*filend = &(*file)[slen];
142 	}
143 	strcpy(*filend, base);
144 }
145 
146 void
fdcopy(const char * src,const struct stat * ssp,const int sfd,const char * tgt,const struct stat * dsp,const int dfd)147 fdcopy(const char *src, const struct stat *ssp, const int sfd,
148 		const char *tgt, const struct stat *dsp, const int dfd)
149 {
150 	char	*buf;
151 	size_t	bufsize;
152 	ssize_t	rsz, wo, wt;
153 
154 	if ((bufsize = ssp->st_blksize) < dsp->st_blksize)
155 		if ((bufsize = dsp->st_blksize) <= 0)
156 			bufsize = 512;
157 	buf = smalloc(bufsize);
158 	while ((rsz = read(sfd, buf, bufsize)) > 0) {
159 		wt = 0;
160 		do {
161 			if ((wo = write(dfd, buf + wt, rsz - wt)) < 0) {
162 				fprintf(stderr, "%s: write: %s: %s\n",
163 						progname, tgt,
164 						strerror(errno));
165 				errcnt |= 01;
166 				unlink(tgt);
167 				free(buf);
168 				return;
169 			}
170 			wt += wo;
171 		} while (wt < rsz);
172 	}
173 	if (rsz < 0) {
174 		fprintf(stderr, "%s: read: %s: %s\n",
175 				progname, src, strerror(errno));
176 		errcnt |= 01;
177 		unlink(tgt);
178 	}
179 	free(buf);
180 }
181 
182 static void
usage(void)183 usage(void)
184 {
185 	fprintf(stderr, "\
186 usage: %s [-cs] [-g group] [-m mode] [-o owner] file ...  destination\n\
187        %s  -d   [-g group] [-m mode] [-o owner] dir\n",
188        		progname, progname);
189 	exit(2);
190 }
191 
192 static void
strip(const char * file)193 strip(const char *file)
194 {
195 	const char	cpr[] = "strip ";
196 	char	*cmd, *cp;
197 	const char	*sp;
198 
199 	cp = cmd = smalloc(strlen(cpr) + strlen(file) + 1);
200 	for (sp = cpr; *sp; sp++)
201 		*cp++ = *sp;
202 	for (sp = file; *sp; sp++)
203 		*cp++ = *sp;
204 	*cp = '\0';
205 	system(cmd);
206 	free(cmd);
207 }
208 
209 static enum okay
chgown(const char * fn,struct stat * sp)210 chgown(const char *fn, struct stat *sp)
211 {
212 	struct stat	st;
213 
214 	if (sp == NULL) {
215 		if (stat(fn, &st) < 0) {
216 			fprintf(stderr, "%s: stat: %s: %s\n",
217 					progname, fn, strerror(errno));
218 			errcnt |= 1;
219 			return STOP;
220 		}
221 		sp = &st;
222 	}
223 	if (!oflag)
224 		owner = sp->st_uid;
225 	if (!gflag)
226 		group = sp->st_gid;
227 	if (chown(fn, owner, group) < 0) {
228 		fprintf(stderr, "%s: chown: %s: %s\n", progname, fn,
229 				strerror(errno));
230 		errcnt |= 01;
231 		return STOP;
232 	}
233 	return OKAY;
234 }
235 
236 static enum okay
check(const char * src,const char * tgt,const struct stat * dsp,struct stat * ssp)237 check(const char *src, const char *tgt, const struct stat *dsp,
238 		struct stat *ssp)
239 {
240 	if (stat(src, ssp) < 0) {
241 		fprintf(stderr, "%s: %s: %s\n", progname, src,
242 				strerror(errno));
243 		errcnt |= 01;
244 		return STOP;
245 	}
246 	if ((ssp->st_mode&S_IFMT) != S_IFREG && strcmp(src, "/dev/null")) {
247 		fprintf(stderr, "%s: %s isn't a regular file.\n",
248 				progname, src);
249 		errcnt |= 01;
250 		return STOP;
251 	}
252 	if (dsp && (ssp->st_dev == dsp->st_dev && ssp->st_ino == dsp->st_ino)) {
253 		fprintf(stderr, "%s: %s and %s are the same file.\n",
254 				progname, src, tgt);
255 		errcnt |= 01;
256 		return STOP;
257 	}
258 	return OKAY;
259 }
260 
261 static void
cp(const char * src,const char * tgt,struct stat * dsp)262 cp(const char *src, const char *tgt, struct stat *dsp)
263 {
264 	struct stat	sst, nst;
265 	int	sfd, dfd;
266 
267 	if (check(src, tgt, dsp, &sst) != OKAY)
268 		return;
269 	unlink(tgt);
270 	if ((dfd = creat(tgt, 0700)) < 0 || fchmod(dfd, 0700) < 0 ||
271 			fstat(dfd, &nst) < 0) {
272 		fprintf(stderr, "%s: %s: %s\n", progname, src,
273 				strerror(errno));
274 		errcnt |= 01;
275 		if (dfd >= 0)
276 			close(dfd);
277 		return;
278 	}
279 	if ((sfd = open(src, O_RDONLY)) < 0) {
280 		fprintf(stderr, "%s: open: %s: %s\n", progname, src,
281 				strerror(errno));
282 		errcnt |= 01;
283 		return;
284 	}
285 	fdcopy(src, &sst, sfd, tgt, &nst, dfd);
286 	close(dfd);
287 	close(sfd);
288 	if (sflag)
289 		strip(tgt);
290 	if (oflag || gflag)
291 		chgown(tgt, &nst);
292 	if (chmod(tgt, mode) < 0) {
293 		fprintf(stderr, "%s: %s: %s\n", progname, tgt, strerror(errno));
294 		errcnt |= 01;
295 	}
296 }
297 
298 static void
installf(int ac,char ** av)299 installf(int ac, char **av)
300 {
301 	struct stat	dst, ust;
302 
303 	if (lstat(av[ac-1], &dst) == 0) {
304 		if ((dst.st_mode&S_IFMT) != S_IFLNK ||
305 				stat(av[ac-1], &ust) < 0)
306 			ust = dst;
307 		if ((ust.st_mode&S_IFMT) == S_IFDIR) {
308 			char	*copy, *cend;
309 			size_t	sz, slen, ss;
310 			int	i;
311 
312 			getpath(av[ac-1], &copy, &cend, &sz, &slen);
313 			for (i = 0; i < ac-1; i++) {
314 				setpath(basename(av[i]), &copy, &cend,
315 						slen, &sz, &ss);
316 				cp(av[i], copy, stat(copy, &dst) < 0 ?
317 						NULL : &dst);
318 			}
319 		} else if (ac > 2)
320 			usage();
321 		else
322 			cp(av[0], av[1], &ust);
323 	} else if (ac > 2)
324 		usage();
325 	else
326 		cp(av[0], av[1], NULL);
327 }
328 
329 static enum okay
makedir(const char * dir)330 makedir(const char *dir)
331 {
332 	struct stat	st;
333 
334 	if (mkdir(dir, 0777) < 0) {
335 		if (errno == EEXIST) {
336 			if (stat(dir, &st) < 0 ||
337 					(st.st_mode&S_IFMT) != S_IFDIR){
338 				fprintf(stderr, "%s: %s is not a directory\n",
339 						progname, dir);
340 				errcnt |= 01;
341 				return STOP;
342 			}
343 		} else {
344 			fprintf(stderr, "%s: mkdir: %s: %s\n",
345 					progname, dir, strerror(errno));
346 			errcnt |= 01;
347 			return STOP;
348 		}
349 	}
350 	return OKAY;
351 }
352 static void
installd(char * dir)353 installd(char *dir)
354 {
355 	struct stat	st;
356 	int	sgid_bit;
357 	char	*slash;
358 	char	c;
359 
360 	slash = dir;
361 	do {
362 		while (*slash == '/')
363 			slash++;
364 		while (*slash != '/' && *slash != '\0')
365 			slash++;
366 		c = *slash;
367 		*slash = '\0';
368 		if (makedir(dir) != OKAY)
369 			return;
370 		if (c == '\0') {
371 			if (oflag || gflag)
372 				if (chgown(dir, NULL) != OKAY)
373 					return;
374 			if (mflag) {
375 				sgid_bit = stat(dir, &st) == 0 &&
376 					st.st_mode&S_ISGID ? S_ISGID : 0;
377 				if (chmod(dir, mode | sgid_bit) < 0) {
378 					fprintf(stderr, "%s: chmod: %s: %s\n",
379 							progname, dir,
380 							strerror(errno));
381 					errcnt |= 01;
382 					return;
383 				}
384 			}
385 		}
386 		*slash = c;
387 	} while (c != '\0');
388 }
389 
390 int
main(int argc,char ** argv)391 main(int argc, char **argv)
392 {
393 	const char	optstring[] = "csg:m:o:d";
394 	int	i;
395 
396 	progname = basename(argv[0]);
397 	while ((i = getopt(argc, argv, optstring)) != EOF) {
398 		switch (i) {
399 		case 'c':
400 			/* no-op */
401 			break;
402 		case 's':
403 			sflag = 1;
404 			break;
405 		case 'g':
406 			gflag = 1;
407 			group = getgroup(optarg);
408 			break;
409 		case 'm':
410 			mflag = 1;
411 			mode = strtol(optarg, NULL, 8);
412 			break;
413 		case 'o':
414 			oflag = 1;
415 			owner = getowner(optarg);
416 			break;
417 		case 'd':
418 			dflag = 1;
419 			break;
420 		default:
421 			usage();
422 		}
423 	}
424 	if (dflag) {
425 		if (argc == optind || argc > optind + 1)
426 			usage();
427 		if (mflag)
428 			mode &= ~(mode_t)S_ISGID;
429 		installd(argv[optind]);
430 	} else {
431 		if (argc < optind + 2)
432 			usage();
433 		installf(argc - optind, &argv[optind]);
434 	}
435 	return errcnt;
436 }
437