install.c (9124B)
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 * 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 * 79 smalloc(size_t size) 80 { 81 return srealloc(NULL, size); 82 } 83 84 uid_t 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 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 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 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 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 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 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 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 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 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 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 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 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 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 }