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], ©, &cend, &sz, &slen);
313 for (i = 0; i < ac-1; i++) {
314 setpath(basename(av[i]), ©, &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