1 /* install 1.11 - install files. Author: Kees J. Bot
21 /* First line used on a self-decompressing executable. */
22 char ZCAT
[]= "#!/usr/bin/zexec /usr/bin/zcat\n";
23 char GZCAT
[]= "#!/usr/bin/zexec /usr/bin/gzcat\n";
25 /* Compression filters. */
26 char *COMPRESS
[]= { "compress", nil
};
27 char *GZIP
[]= { "gzip", "-#", nil
};
29 int excode
= 0; /* Exit code. */
31 void report(char *label
)
33 if (label
== nil
|| label
[0] == 0)
34 fprintf(stderr
, "install: %s\n", strerror(errno
));
36 fprintf(stderr
, "install: %s: %s\n", label
, strerror(errno
));
40 void fatal(char *label
)
46 void *allocate(void *mem
, size_t size
)
47 /* Safe malloc/realloc. */
49 mem
= mem
== nil
? malloc(size
) : realloc(mem
, size
);
51 if (mem
== nil
) fatal(nil
);
55 void deallocate(void *mem
)
57 if (mem
!= nil
) free(mem
);
60 int lflag
= 0; /* Make a hard link if possible. */
61 int cflag
= 0; /* Copy if you can't link, otherwise symlink. */
62 int dflag
= 0; /* Create a directory. */
63 int strip
= 0; /* Strip the copy. */
64 char **compress
= nil
; /* Compress utility to make a compressed executable. */
65 char *zcat
= nil
; /* Line one to decompress. */
67 long stack
= -1; /* Amount of heap + stack. */
68 int wordpow
= 1; /* Must be multiplied with wordsize ** wordpow */
69 /* So 8kb for an 8086 and 16kb for the rest. */
71 pid_t
filter(int fd
, char **command
)
72 /* Let a command filter the output to fd. */
82 switch ((pid
= fork())) {
93 signal(SIGPIPE
, SIG_DFL
);
94 execvp(command
[0], command
);
97 /* Connect fd to the pipe. */
104 int mkdirp(char *dir
, int mode
, int owner
, int group
)
111 while (*sep
== '/') sep
++;
119 while (*sep
!= '/' && *sep
!= 0) sep
++;
121 while (*sep
== '/') sep
++;
123 keep
= *pref
; *pref
= 0;
125 if (strcmp(dir
, ".") == 0 || strcmp(dir
, "..") == 0) continue;
127 if (mkdir(dir
, mode
) < 0) {
128 if (errno
!= EEXIST
|| *sep
== 0) {
129 /* On purpose not doing: *pref= keep; */
133 if (chown(dir
, owner
, group
) < 0 && errno
!= EPERM
)
136 } while (*pref
= keep
, *sep
!= 0);
140 void makedir(char *dir
, int mode
, int owner
, int group
)
141 /* Install a directory, and set it's modes. */
145 if (stat(dir
, &st
) < 0) {
146 if (errno
!= ENOENT
) { report(dir
); return; }
148 /* The target doesn't exist, make it. */
149 if (mode
== -1) mode
= 0755;
150 if (owner
== -1) owner
= getuid();
151 if (group
== -1) group
= getgid();
153 if (mkdirp(dir
, mode
, owner
, group
) < 0) {
157 /* The target does exist, change mode and ownership. */
158 if (mode
== -1) mode
= (st
.st_mode
& 07777) | 0555;
160 if ((st
.st_mode
& 07777) != mode
) {
161 if (chmod(dir
, mode
) < 0) { report(dir
); return; }
163 if (owner
== -1) owner
= st
.st_uid
;
164 if (group
== -1) group
= st
.st_gid
;
165 if (st
.st_uid
!= owner
|| st
.st_gid
!= group
) {
166 if (chown(dir
, owner
, group
) < 0 && errno
!= EPERM
) {
169 /* Set the mode again, chown may have wrecked it. */
170 (void) chmod(dir
, mode
);
175 int setstack(struct exec
*hdr
)
176 /* Set the stack size in a header. Return true if something changed. */
181 while (wordpow
> 0) {
182 total
*= hdr
->a_cpu
== A_I8086
? 2 : 4;
185 total
+= hdr
->a_data
+ hdr
->a_bss
;
187 if (!(hdr
->a_flags
& A_SEP
)) {
190 if (hdr
->a_flags
& A_PAL
) total
+= hdr
->a_hdrlen
;
193 if (hdr
->a_cpu
== A_I8086
&& total
> 0x10000L
)
196 if (hdr
->a_total
!= total
) {
197 /* Need to change stack allocation. */
205 void copylink(char *source
, char *dest
, int mode
, int owner
, int group
)
207 struct stat sst
, dst
;
209 int r
, same
= 0, change
= 0, docopy
= 1;
211 # define hdr ((struct exec *) buf)
215 /* Source must exist as a plain file, dest may exist as a plain file. */
217 if (stat(source
, &sst
) < 0) { report(source
); return; }
220 mode
= sst
.st_mode
& 07777;
221 if (!lflag
|| cflag
) {
223 if (mode
& 0111) mode
|= 0111;
226 if (owner
== -1) owner
= sst
.st_uid
;
227 if (group
== -1) group
= sst
.st_gid
;
229 if (!S_ISREG(sst
.st_mode
)) {
230 fprintf(stderr
, "install: %s is not a regular file\n", source
);
236 if (errno
!= ENOENT
) { report(dest
); return; }
238 if (!S_ISREG(dst
.st_mode
)) {
239 fprintf(stderr
, "install: %s is not a regular file\n",
245 /* Are the files the same? */
246 if (sst
.st_dev
== dst
.st_dev
&& sst
.st_ino
== dst
.st_ino
) {
247 if (!lflag
&& cflag
) {
249 "install: %s and %s are the same, can't copy\n",
258 if (lflag
&& !same
) {
259 /* Try to link the files. */
261 if (r
>= 0 && unlink(dest
) < 0) {
262 report(dest
); return;
265 if (link(source
, dest
) >= 0) {
268 if (!cflag
|| errno
!= EXDEV
) {
270 "install: can't link %s to %s: %s\n",
271 source
, dest
, strerror(errno
));
278 if (docopy
&& !same
) {
279 /* Copy the files, stripping if necessary. */
280 long count
= LONG_MAX
;
283 if ((sfd
= open(source
, O_RDONLY
)) < 0) {
284 report(source
); return;
287 /* Open for write is less simple, its mode may be 444. */
288 dfd
= open(dest
, O_WRONLY
|O_CREAT
|O_TRUNC
, mode
| 0600);
289 if (dfd
< 0 && errno
== EACCES
) {
290 (void) chmod(dest
, mode
| 0600);
291 dfd
= open(dest
, O_WRONLY
|O_TRUNC
);
300 while (count
> 0 && (n
= read(sfd
, buf
, sizeof(buf
))) > 0) {
301 if (first
&& n
>= A_MINHDR
&& !BADMAG(*hdr
)) {
304 + hdr
->a_text
+ hdr
->a_data
;
306 hdr
->a_flags
&= ~A_NSYM
;
310 if (stack
!= -1 && setstack(hdr
)) change
= 1;
312 if (compress
!= nil
) {
313 /* Write first #! line. */
314 (void) write(dfd
, zcat
, strlen(zcat
));
316 /* Put a compressor in between. */
317 if ((pid
= filter(dfd
, compress
)) < 0) {
325 if (count
< n
) n
= count
;
327 if (write(dfd
, buf
, n
) < 0) {
331 if (pid
!= 0) (void) waitpid(pid
, nil
, 0);
337 if (n
< 0) report(source
);
340 if (pid
!= 0 && waitpid(pid
, &status
, 0) < 0 || status
!= 0) {
347 /* The file has been linked into place. Set the
350 if ((dfd
= open(dest
, O_RDWR
)) < 0) {
355 if ((n
= read(dfd
, buf
, sizeof(*hdr
))) < 0) {
356 report(dest
); return;
359 if (n
>= A_MINHDR
&& !BADMAG(*hdr
) && setstack(hdr
)) {
360 if (lseek(dfd
, (off_t
) 0, SEEK_SET
) == -1
361 || write(dfd
, buf
, n
) < 0
373 if (stat(dest
, &dst
) < 0) { report(dest
); return; }
375 if ((dst
.st_mode
& 07777) != mode
) {
376 if (chmod(dest
, mode
) < 0) { report(dest
); return; }
378 if (dst
.st_uid
!= owner
|| dst
.st_gid
!= group
) {
379 if (chown(dest
, owner
, group
) < 0 && errno
!= EPERM
) {
380 report(dest
); return;
382 /* Set the mode again, chown may have wrecked it. */
383 (void) chmod(dest
, mode
);
388 ubuf
.actime
= dst
.st_atime
;
389 ubuf
.modtime
= sst
.st_mtime
;
391 if (utime(dest
, &ubuf
) < 0 && errno
!= EPERM
) {
392 report(dest
); return;
401 install [-lcsz#] [-o owner] [-g group] [-m mode] [-S stack] [file1] file2\n\
402 install [-lcsz#] [-o owner] [-g group] [-m mode] [-S stack] file ... dir\n\
403 install -d [-o owner] [-g group] [-m mode] directory\n");
407 void main(int argc
, char **argv
)
410 int mode
= -1; /* Mode of target. */
411 int owner
= -1; /* Owner. */
412 int group
= -1; /* Group. */
415 gid_t groups
[NGROUPS_MAX
];
420 /* Only those in group 0 are allowed to set owner and group. */
421 if (getgid() == 0) super
= 1;
423 ngroups
= getgroups(NGROUPS_MAX
, groups
);
424 for (g
= 0; g
< ngroups
; g
++) if (groups
[g
] == 0) super
= 1;
431 /* May use a filter. */
432 signal(SIGPIPE
, SIG_IGN
);
434 while (i
< argc
&& argv
[i
][0] == '-') {
435 char *p
= argv
[i
++]+1;
442 if (strcmp(p
, "-") == 0) break;
446 case 'l': lflag
= 1; break;
447 case 'c': cflag
= 1; break;
448 case 's': strip
= 1; break;
449 case 'd': dflag
= 1; break;
451 if (compress
== nil
) {
458 if (i
== argc
) usage();
460 if (*p
== 0) usage();
462 num
= strtoul(p
, &end
, 10);
464 if ((uid_t
) num
!= num
) usage();
467 if ((pw
= getpwnam(p
)) == nil
) {
469 "install: %s: unknown user\n",
479 if (i
== argc
) usage();
481 if (*p
== 0) usage();
483 num
= strtoul(p
, &end
, 10);
485 if ((gid_t
) num
!= num
) usage();
488 if ((gr
= getgrnam(p
)) == nil
) {
490 "install: %s: unknown user\n",
500 if (i
== argc
) usage();
502 if (*p
== 0) usage();
504 num
= strtoul(p
, &end
, 010);
505 if (*end
!= 0 || (num
& 07777) != num
) usage();
507 if ((mode
& S_ISUID
) && super
&& owner
== -1) {
508 /* Setuid what? Root most likely. */
511 if ((mode
& S_ISGID
) && super
&& group
== -1) {
518 if (i
== argc
) usage();
520 if (*p
== 0) usage();
522 stack
= strtol(p
, &end
, 0);
524 if (end
== p
|| stack
< 0) usage();
529 case 'M': num
= 1024 * 1024L; break;
531 case 'K': num
= 1024; break;
533 case 'W': num
= 4; wp
++; break;
535 case 'B': num
= 1; break;
538 if (stack
> LONG_MAX
/ num
) usage();
542 while (wp
> 0) { stack
/= 4; wordpow
++; wp
--; }
545 if ((unsigned) (p
[-1] - '1') <= ('9' - '1')) {
555 /* Some options don't mix. */
556 if (dflag
&& (cflag
|| lflag
|| strip
)) usage();
558 /* Don't let the user umask interfere. */
562 /* install directory */
563 if ((argc
- i
) != 1) usage();
565 makedir(argv
[i
], mode
, owner
, group
);
569 if ((argc
- i
) < 1) usage();
570 if ((lflag
|| cflag
) && (argc
- i
) == 1) usage();
572 if (stat(argv
[argc
-1], &st
) >= 0 && S_ISDIR(st
.st_mode
)) {
573 /* install file ... dir */
577 if ((argc
- i
) == 1) usage();
580 if ((base
= strrchr(argv
[i
], '/')) == nil
)
584 target
= allocate(target
, strlen(argv
[argc
-1])
585 + 1 + strlen(base
) + 1);
586 strcpy(target
, argv
[argc
-1]);
588 strcat(target
, base
);
590 copylink(argv
[i
++], target
, mode
, owner
, group
);
593 /* install [file1] file2 */
595 copylink(argv
[i
], argv
[argc
-1], mode
, owner
, group
);