xinstall.c revision 1.4 1 /*
2 * Copyright (c) 1987 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
37 All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 /*static char sccsid[] = "from: @(#)xinstall.c 5.24 (Berkeley) 7/1/90";*/
42 static char rcsid[] = "$Id: xinstall.c,v 1.4 1994/10/02 21:32:31 cgd Exp $";
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/file.h>
48 #include <grp.h>
49 #include <pwd.h>
50 #include <stdio.h>
51 #include <ctype.h>
52 #include <errno.h>
53 #include <paths.h>
54 #include "pathnames.h"
55
56 static struct passwd *pp;
57 static struct group *gp;
58 static int docopy, dostrip, dodir, mode = 0755;
59 static char *group, *owner, pathbuf[MAXPATHLEN];
60
61 main(argc, argv)
62 int argc;
63 char **argv;
64 {
65 extern char *optarg;
66 extern int optind;
67 struct stat from_sb, to_sb;
68 mode_t *set, *setmode();
69 int ch, no_target;
70 char *to_name;
71
72 while ((ch = getopt(argc, argv, "cg:m:o:sd")) != EOF)
73 switch((char)ch) {
74 case 'c':
75 docopy = 1;
76 break;
77 case 'g':
78 group = optarg;
79 break;
80 case 'm':
81 if (!(set = setmode(optarg))) {
82 (void)fprintf(stderr,
83 "install: invalid file mode.\n");
84 exit(1);
85 }
86 mode = getmode(set, 0);
87 break;
88 case 'o':
89 owner = optarg;
90 break;
91 case 's':
92 dostrip = 1;
93 break;
94 case 'd':
95 dodir = 1;
96 break;
97 case '?':
98 default:
99 usage();
100 }
101 argc -= optind;
102 argv += optind;
103
104 /* copy and strip options make no sense when creating directories */
105 if ((docopy || dostrip) && dodir)
106 usage();
107
108 /* must have at least two arguments, except when creating directories */
109 if (argc < 2 && !dodir)
110 usage();
111
112 /* get group and owner id's */
113 if (group && !(gp = getgrnam(group))) {
114 fprintf(stderr, "install: unknown group %s.\n", group);
115 exit(1);
116 }
117 if (owner && !(pp = getpwnam(owner))) {
118 fprintf(stderr, "install: unknown user %s.\n", owner);
119 exit(1);
120 }
121
122 if (dodir) {
123 for (; *argv != NULL; ++argv)
124 build(*argv);
125 exit (0);
126 /* NOTREACHED */
127 }
128
129 no_target = stat(to_name = argv[argc - 1], &to_sb);
130 if (!no_target && S_ISDIR(to_sb.st_mode)) {
131 for (; *argv != to_name; ++argv)
132 install(*argv, to_name, 1);
133 exit(0);
134 }
135
136 /* can't do file1 file2 directory/file */
137 if (argc != 2)
138 usage();
139
140 if (!no_target) {
141 if (stat(*argv, &from_sb)) {
142 fprintf(stderr, "install: can't find %s.\n", *argv);
143 exit(1);
144 }
145 if (!S_ISREG(to_sb.st_mode)) {
146 fprintf(stderr, "install: %s isn't a regular file.\n", to_name);
147 exit(1);
148 }
149 if (to_sb.st_dev == from_sb.st_dev && to_sb.st_ino == from_sb.st_ino) {
150 fprintf(stderr, "install: %s and %s are the same file.\n", *argv, to_name);
151 exit(1);
152 }
153 /* unlink now... avoid ETXTBSY errors later */
154 (void)unlink(to_name);
155 }
156 install(*argv, to_name, 0);
157 exit(0);
158 }
159
160 /*
161 * install --
162 * build a path name and install the file
163 */
164 install(from_name, to_name, isdir)
165 char *from_name, *to_name;
166 int isdir;
167 {
168 struct stat from_sb;
169 int devnull, from_fd, to_fd;
170 char *C, *rindex();
171
172 /* if try to install NULL file to a directory, fails */
173 if (isdir || strcmp(from_name, _PATH_DEVNULL)) {
174 if (stat(from_name, &from_sb)) {
175 fprintf(stderr, "install: can't find %s.\n", from_name);
176 exit(1);
177 }
178 if (!S_ISREG(from_sb.st_mode)) {
179 fprintf(stderr, "install: %s isn't a regular file.\n", from_name);
180 exit(1);
181 }
182 /* build the target path */
183 if (isdir) {
184 (void)sprintf(pathbuf, "%s/%s", to_name, (C = rindex(from_name, '/')) ? ++C : from_name);
185 to_name = pathbuf;
186 }
187 devnull = 0;
188 } else
189 devnull = 1;
190
191 /* unlink now... avoid ETXTBSY errors later */
192 (void)unlink(to_name);
193
194 /* create target */
195 if ((to_fd = open(to_name, O_CREAT|O_WRONLY|O_TRUNC, 0600)) < 0) {
196 error(to_name);
197 exit(1);
198 }
199 if (!devnull) {
200 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
201 (void)unlink(to_name);
202 error(from_name);
203 exit(1);
204 }
205 copy(from_fd, from_name, to_fd, to_name);
206 (void)close(from_fd);
207 }
208 if (dostrip)
209 strip(to_name);
210 /*
211 * set owner, group, mode for target; do the chown first,
212 * chown may lose the setuid bits.
213 */
214 if ((group || owner) &&
215 fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1) ||
216 fchmod(to_fd, mode)) {
217 error(to_name);
218 bad(to_name);
219 }
220 (void)close(to_fd);
221 if (!docopy && !devnull && unlink(from_name)) {
222 error(from_name);
223 exit(1);
224 }
225 }
226
227 /*
228 * copy --
229 * copy from one file to another
230 */
231 copy(from_fd, from_name, to_fd, to_name)
232 register int from_fd, to_fd;
233 char *from_name, *to_name;
234 {
235 register int n;
236 char buf[MAXBSIZE];
237
238 while ((n = read(from_fd, buf, sizeof(buf))) > 0)
239 if (write(to_fd, buf, n) != n) {
240 error(to_name);
241 bad(to_name);
242 }
243 if (n == -1) {
244 error(from_name);
245 bad(to_name);
246 }
247 }
248
249 /*
250 * strip --
251 * use strip(1) to strip the target file
252 */
253 strip(to_name)
254 char *to_name;
255 {
256 int status;
257
258 switch (vfork()) {
259 case -1:
260 error("fork");
261 bad(to_name);
262 case 0:
263 execl(_PATH_STRIP, "strip", to_name, (char *)NULL);
264 error(_PATH_STRIP);
265 _exit(1);
266 default:
267 if (wait(&status) == -1 || status)
268 bad(to_name);
269 }
270 }
271
272 /*
273 * build --
274 * build directory heirarchy
275 */
276 build(path)
277 char *path;
278 {
279 register char *p;
280 struct stat sb;
281 int ch;
282
283 for (p = path;; ++p)
284 if (!*p || (p != path && *p == '/')) {
285 ch = *p;
286 *p = '\0';
287 if (stat(path, &sb)) {
288 if (errno != ENOENT || mkdir(path, 0777) < 0) {
289 error(path);
290 return(1);
291 }
292 }
293 if (!(*p = ch))
294 break;
295 }
296
297 if ((group || owner) &&
298 chown(path, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1) ||
299 chmod(path, mode)) {
300 error(path);
301 }
302
303 return(0);
304 }
305
306 /*
307 * error --
308 * print out an error message
309 */
310 error(s)
311 char *s;
312 {
313 extern int errno;
314 char *strerror();
315
316 (void)fprintf(stderr, "install: %s: %s\n", s, strerror(errno));
317 }
318
319 /*
320 * bad --
321 * remove created target and die
322 */
323 bad(fname)
324 char *fname;
325 {
326 (void)unlink(fname);
327 exit(1);
328 }
329
330 /*
331 * usage --
332 * print a usage message and die
333 */
334 usage()
335 {
336 (void)fprintf(stderr, "\
337 usage: install [-cs] [-g group] [-m mode] [-o owner] file1 file2\n\
338 install [-cs] [-g group] [-m mode] [-o owner] file1 ... fileN directory\n\
339 install -d [-g group] [-m mode] [-o owner] directory ...\n");
340 exit(1);
341 }
342