1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 1996 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30/*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40#pragma ident	"%Z%%M%	%I%	%E% SMI"
41
42#include <sys/param.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <sys/file.h>
46#include <fcntl.h>
47#include <grp.h>
48#include <pwd.h>
49#include <stdio.h>
50#include <ctype.h>
51#include <errno.h>
52#include <locale.h>
53
54#define	DEF_GROUP	"staff"		/* default group */
55#define	DEF_OWNER	"root"		/* default owner */
56#define	DEF_MODE	0755		/* default mode */
57
58char *group = DEF_GROUP;
59char *owner = DEF_OWNER;
60int mode    = DEF_MODE;
61int sflag = 0;
62struct passwd *pp;
63struct group *gp;
64extern int errno;
65int copy();
66void usage();
67
68int
69main(int argc, char **argv)
70{
71	extern char	*optarg;
72	extern int	optind;
73	struct stat	stb;
74	char	*dirname;
75	int	ch;
76	int	i;
77	int	rc;
78	int	dflag = 0;
79	int	gflag = 0;
80	int	oflag = 0;
81	int	mflag = 0;
82
83	(void) setlocale(LC_ALL, "");
84
85#if !defined(TEXT_DOMAIN)
86#define TEXT_DOMAIN "SYS_TEST"
87#endif
88	(void) textdomain(TEXT_DOMAIN);
89
90	while ((ch = getopt(argc, argv, "dcg:o:m:s")) != EOF)
91		switch((char)ch) {
92		case 'c':
93			break;	/* always do "copy" */
94		case 'd':
95			dflag++;
96			break;
97		case 'g':
98			gflag++;
99			group = optarg;
100			break;
101		case 'm':
102			mflag++;
103			mode = atoo(optarg);
104			break;
105		case 'o':
106			oflag++;
107			owner = optarg;
108			break;
109		case 's':
110			sflag++;
111			break;
112		case '?':
113		default:
114			usage();
115		}
116	argc -= optind;
117	argv += optind;
118
119	/* get group and owner id's */
120	if (!(gp = getgrnam(group))) {
121		fprintf(stderr, gettext("install: unknown group %s.\n"), group);
122		exit(1);
123	}
124	if (!(pp = getpwnam(owner))) {
125		fprintf(stderr, gettext("install: unknown user %s.\n"), owner);
126		exit(1);
127	}
128
129	if (dflag) {		/* install a directory */
130		int exists = 0;
131
132		if (argc != 1)
133			usage();
134		dirname = argv[0];
135		if (mkdirp(dirname, 0777) < 0) {
136			exists = errno == EEXIST;
137			if (!exists) {
138				fprintf(stderr, gettext("install: mkdir: %s: %s\n"), dirname, strerror(errno));
139				exit(1);
140			}
141		}
142		if (stat(dirname, &stb) < 0) {
143			fprintf(stderr, gettext("install: stat: %s: %s\n"), dirname, strerror(errno));
144			exit(1);
145		}
146		if ((stb.st_mode&S_IFMT) != S_IFDIR) {
147			fprintf(stderr, gettext("install: %s is not a directory\n"), dirname);
148		}
149		/* make sure directory setgid bit is inherited */
150		mode = (mode & ~S_ISGID) | (stb.st_mode & S_ISGID);
151		if (mflag && chmod(dirname, mode)) {
152			fprintf(stderr, gettext("install: chmod: %s: %s\n"), dirname, strerror(errno));
153			if (!exists)
154				(void) unlink(dirname);
155			exit(1) ;
156		}
157		if (oflag && chown(dirname, pp->pw_uid, -1) && errno != EPERM) {
158			fprintf(stderr, gettext("install: chown: %s: %s\n"), dirname, strerror(errno));
159			if (!exists)
160				(void) unlink(dirname);
161			exit(1) ;
162		}
163		if (gflag && chown(dirname, -1, gp->gr_gid) && errno != EPERM) {
164			fprintf(stderr, gettext("install: chgrp: %s: %s\n"), dirname, strerror(errno));
165			if (!exists)
166				(void) unlink(dirname);
167			exit(1) ;
168		}
169		exit(0);
170	}
171
172	if (argc < 2)
173		usage();
174
175        if (argc > 2) {		/* last arg must be a directory */
176                if (stat(argv[argc-1], &stb) < 0)
177                        usage();
178                if ((stb.st_mode&S_IFMT) != S_IFDIR)
179                        usage();
180        }
181        rc = 0;
182        for (i = 0; i < argc-1; i++)
183                rc |= install(argv[i], argv[argc-1]);
184        return (rc);
185}
186
187int
188install(from, to)
189	char *from, *to;
190{
191	int to_fd;
192	int devnull;
193	int status = 0;
194	char *path;
195	struct stat from_sb, to_sb;
196	static char pbuf[MAXPATHLEN];
197	char buf[MAXPATHLEN + 10];
198
199	/* check source */
200	if (stat(from, &from_sb)) {
201		fprintf(stderr, gettext("install: %s: %s\n"), from, strerror(errno));
202		return (1);
203	}
204	/* special case for removing files */
205	devnull = !strcmp(from, "/dev/null");
206	if (!devnull && !((from_sb.st_mode&S_IFMT) == S_IFREG)) {
207		fprintf(stderr, gettext("install: %s isn't a regular file.\n"), from);
208		return (1);
209	}
210
211	/* build target path, find out if target is same as source */
212	if (!stat(path = to, &to_sb)) {
213		if ((to_sb.st_mode&S_IFMT) == S_IFDIR) {
214			char *C, *strrchr();
215
216			(void) sprintf(path = pbuf, "%s/%s", to, (C = strrchr(from, '/')) ? ++C : from);
217			if (stat(path, &to_sb))
218				goto nocompare;
219		}
220		if ((to_sb.st_mode&S_IFMT) != S_IFREG) {
221			fprintf(stderr, gettext("install: %s isn't a regular file.\n"), path);
222			return (1);
223		}
224		if (to_sb.st_dev == from_sb.st_dev && to_sb.st_ino == from_sb.st_ino) {
225			fprintf(stderr, gettext("install: %s and %s are the same file.\n"), from, path);
226			return (1);
227		}
228		/* unlink now... avoid ETXTBSY errors later */
229		(void) unlink(path);
230	}
231
232nocompare:
233	/* open target, set mode, owner, group */
234	if ((to_fd = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0)) < 0) {
235		fprintf(stderr, gettext("install: %s: %s\n"), path, strerror(errno));
236		return (1);
237	}
238	if (fchmod(to_fd, mode)) {
239		fprintf(stderr, gettext("install: chmod: %s: %s\n"), path, strerror(errno));
240		status = 1;
241		close(to_fd);
242		goto inst_done;
243	}
244	if (!devnull) {
245		status = copy(from, to_fd, path);  /* copy */
246		close(to_fd);
247	}
248	if (sflag) {
249		sprintf(buf, "strip %s", path);
250		system(buf);
251	}
252	if (chown(path, pp->pw_uid, gp->gr_gid) && errno != EPERM) {
253		fprintf(stderr, gettext("install: chown: %s: %s\n"), path, strerror(errno));
254		status = 1;
255	}
256
257inst_done:
258	if (status)
259		(void) unlink(path);
260	return (status);
261}
262
263/*
264 * copy --
265 *	copy from one file to another
266 */
267int
268copy(from_name, to_fd, to_name)
269	int to_fd;
270	char *from_name, *to_name;
271{
272	int n, from_fd;
273	int status = 0;
274	char buf[MAXBSIZE];
275
276	if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
277		fprintf(stderr, gettext("install: open: %s: %s\n"), from_name, strerror(errno));
278		return (1);
279	}
280	while ((n = read(from_fd, buf, sizeof(buf))) > 0)
281		if (write(to_fd, buf, n) != n) {
282			fprintf(stderr, gettext("install: write: %s: %s\n"), to_name, strerror(errno));
283		status = 1;
284		goto copy_done;
285		}
286	if (n == -1) {
287		fprintf(stderr, gettext("install: read: %s: %s\n"), from_name, strerror(errno));
288		status = 1;
289		goto copy_done;
290	}
291
292copy_done:
293	(void) close(from_fd);
294	return (status);
295}
296
297/*
298 * atoo --
299 *      octal string to int
300 */
301int
302atoo(str)
303        char   *str;
304{
305        int    val;
306
307        for (val = 0; isdigit(*str); ++str)
308                val = val * 8 + *str - '0';
309        return(val);
310}
311
312
313/*
314 * usage --
315 *	print a usage message and die
316 */
317void
318usage()
319{
320	fputs(gettext("usage: install [-cs] [-g group] [-m mode] [-o owner] file ...  destination\n"), stderr);
321	fputs(gettext("       install  -d   [-g group] [-m mode] [-o owner] dir\n"), stderr);
322	exit(1);
323}
324
325/*
326 * mkdirp --
327 *	make a directory and parents if needed
328 */
329int
330mkdirp(dir, mode)
331	char *dir;
332	int mode;
333{
334	int err;
335	char *slash;
336	char *strrchr();
337	extern int errno;
338
339	if (mkdir(dir, mode) == 0)
340		return (0);
341	if (errno != ENOENT)
342		return (-1);
343	slash = strrchr(dir, '/');
344	if (slash == NULL)
345		return (-1);
346	*slash = '\0';
347	err = mkdirp(dir, 0777);
348	*slash = '/';
349	if (err)
350		return (err);
351	return mkdir(dir, mode);
352}
353