1 /* $Rev$ */
2
3 #include <sys/types.h>
4 #include <sys/wait.h>
5 #include <sys/time.h>
6 #include <sys/resource.h>
7 #include <pwd.h>
8 #include <grp.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #include "install.h"
16
17 #define wait_exitcode(w) ((w) >> 8)
18
19 #define EXT_INST_COPY ext_tools[0]
20 #define EXT_INST_CHECK ext_tools[1]
21 #define EXT_INST_DIR ext_tools[2]
22 #define EXT_INST_LINK ext_tools[3]
23 #define EXT_INST_SOSUFFIX ext_tools[4]
24 char *ext_tools[] = {
25 "./inst-copy",
26 "./inst-check",
27 "./inst-dir",
28 "./inst-link",
29 "./mk-sosuffix",
30 };
31
32 #define MAX_PATHLEN 1024
33 #define MAX_MSGLEN 8192
34
35 #define str_same(a,b) (strcmp((a),(b)) == 0)
36
37 extern const char progname[];
38 extern char **environ;
39
40 char src_name[MAX_PATHLEN];
41 char dst_name[MAX_PATHLEN];
42 char dir_name[MAX_PATHLEN];
43 char src_tmp[MAX_PATHLEN];
44 char dst_tmp[MAX_PATHLEN];
45 char dir_tmp[MAX_PATHLEN];
46 char tmp_buf[MAX_PATHLEN];
47 char msg_buf[MAX_MSGLEN];
48
49 char *task_args[16];
50 int task_pipe[2];
51 int uid;
52 int gid;
53
54 char uidbuf[16];
55 char gidbuf[16];
56 char permbuf[16];
57
58 unsigned long install_failed;
59
60 /* error functions */
fails_sys(const char * s)61 int fails_sys(const char *s)
62 {
63 printf("failed: %s: %s\n", s, install_error(errno));
64 return 0;
65 }
fails(const char * s)66 int fails(const char *s)
67 {
68 printf("failed: %s\n", s);
69 return 0;
70 }
fail()71 void fail()
72 {
73 printf("failed: %s\n", install_error(errno));
74 }
fail_noread()75 void fail_noread()
76 {
77 printf("failed: no bytes read\n");
78 }
79
80 /* portability functions */
mem_copy(void * dst,const void * src,unsigned long len)81 void mem_copy(void *dst, const void *src, unsigned long len)
82 {
83 char ch;
84 char *cdst;
85 const char *csrc;
86
87 csrc = (const char *) src;
88 cdst = (char *) dst;
89 for (;;) {
90 if (!len) break; ch = *csrc; *cdst = ch; ++cdst; ++csrc; --len;
91 if (!len) break; ch = *csrc; *cdst = ch; ++cdst; ++csrc; --len;
92 if (!len) break; ch = *csrc; *cdst = ch; ++cdst; ++csrc; --len;
93 if (!len) break; ch = *csrc; *cdst = ch; ++cdst; ++csrc; --len;
94 }
95 }
base_name(const char * dir,char ** out)96 int base_name(const char *dir, char **out)
97 {
98 static char path[MAX_PATHLEN];
99 const char *s;
100 const char *t;
101 const char *u;
102 unsigned int len;
103 unsigned int nlen;
104
105 len = strlen(dir);
106
107 if (!len) {
108 path[0] = '.';
109 path[1] = 0;
110 *out = path;
111 return 1;
112 }
113
114 if (len >= MAX_PATHLEN) return 0;
115
116 s = dir;
117 t = s + (len - 1);
118 while ((t > s) && (t[0] == '/')) --t;
119
120 if ((t == s) && (t[0] == '/')) {
121 path[0] = '/';
122 path[1] = 0;
123 *out = path;
124 return 1;
125 }
126 u = t;
127 while ((u > s) && (*(u - 1) != '/')) --u;
128
129 nlen = (t - u) + 1;
130 mem_copy(path, u, nlen);
131 path[nlen] = 0;
132
133 *out = path;
134 return 1;
135 }
str_ends(const char * s,const char * end)136 int str_ends(const char *s, const char *end)
137 {
138 register unsigned long slen;
139 register unsigned long elen;
140 slen = strlen(s);
141 elen = strlen(end);
142 if (elen > slen) elen = slen;
143 s += (slen - elen);
144 return str_same(s, end);
145 }
146
147 /* utilities */
lookup_uid(const char * user,int * uid)148 int lookup_uid(const char *user, int *uid)
149 {
150 struct passwd *pwd;
151
152 pwd = getpwnam(user);
153 if (!pwd) return 0;
154
155 *uid = pwd->pw_uid;
156 return 1;
157 }
lookup_gid(const char * group,int * gid)158 int lookup_gid(const char *group, int *gid)
159 {
160 struct group *grp;
161
162 grp = getgrnam(group);
163 if (!grp) return 0;
164
165 *gid = grp->gr_gid;
166 return 1;
167 }
lookup(struct install_item * ins,int * uid,int * gid)168 int lookup(struct install_item *ins, int *uid, int *gid)
169 {
170 if (ins->owner) {
171 if (!lookup_uid(ins->owner, uid)) return 0;
172 } else *uid = -1;
173 if (ins->group) {
174 if (!lookup_gid(ins->group, gid)) return 0;
175 } else *gid = -1;
176 return 1;
177 }
task_pipes()178 int task_pipes()
179 {
180 if (pipe(task_pipe) == -1) { fail(); return -1; }
181 return 0;
182 }
task_close()183 int task_close()
184 {
185 if (close(task_pipe[0]) == -1) { fail(); return -1; }
186 return 0;
187 }
task()188 int task()
189 {
190 int pid;
191 switch (pid = fork()) {
192 case 0:
193 if (close(1) == -1) return 113;
194 if (dup(task_pipe[1]) == -1) return 114;
195 if (close(task_pipe[0]) == -1) return 115;
196 if (execve(task_args[0], task_args, 0) == -1) return 116;
197 break;
198 case -1:
199 fails_sys("fork");
200 return -1;
201 default:
202 if (close(task_pipe[1]) == -1) return 116;
203 break;
204 }
205 return pid;
206 }
task_read(char * buf,unsigned int len)207 int task_read(char *buf, unsigned int len)
208 {
209 return read(task_pipe[0], buf, len);
210 }
task_echo(char * buf,unsigned int len)211 void task_echo(char *buf, unsigned int len)
212 {
213 int r;
214 for (;;) {
215 r = task_read(buf, len);
216 if (r == -1) { fails_sys("read"); break; }
217 if (r == 0) break;
218 buf[r - 1] = 0;
219 printf("%s\n", buf);
220 }
221 }
libname(char * name,char * buf)222 int libname(char *name, char *buf)
223 {
224 char *s;
225 char rbuf[MAX_PATHLEN];
226 int r;
227 int fd;
228 int ret;
229 int clean;
230
231 ret = 1;
232
233 fd = open(name, O_RDONLY);
234 if (fd == -1) return fails_sys(name);
235
236 r = read(fd, rbuf, MAX_PATHLEN);
237 if (r == 0) { ret = 0; goto END; }
238 if (r == -1) { fails_sys(name); ret = 0; goto END; }
239
240 s = rbuf;
241 clean = 0;
242 while (r) {
243 switch (*s) {
244 case ' ': case '\t': case '\n':
245 s[0] = 0;
246 clean = 1;
247 break;
248 default:
249 break;
250 }
251 if (clean) break;
252 --r; ++s;
253 }
254 mem_copy(buf, rbuf, s - rbuf);
255 buf[s - rbuf] = 0;
256
257 END:
258 if (close(fd) == -1) fails_sys(name);
259 return ret;
260 }
uidgidperm_to_text(int uid,int gid,int perm)261 int uidgidperm_to_text(int uid, int gid, int perm)
262 {
263 if (snprintf(uidbuf, 16, "%d", uid) < 0) return fails_sys("snprintf");
264 if (snprintf(gidbuf, 16, "%d", gid) < 0) return fails_sys("snprintf");
265 if (snprintf(permbuf, 16, "%o", perm) < 0) return fails_sys("snprintf");
266 return 1;
267 }
268
269 /* install operator callbacks */
inst_copy(struct install_item * ins,unsigned int fl)270 int inst_copy(struct install_item *ins, unsigned int fl)
271 {
272 int pid;
273 int stat;
274
275 task_args[0] = EXT_INST_COPY;
276 task_args[1] = ins->src;
277 task_args[2] = ins->dst;
278 task_args[3] = uidbuf;
279 task_args[4] = gidbuf;
280 task_args[5] = permbuf;
281 if (fl & INSTALL_DRYRUN) {
282 task_args[6] = "dryrun";
283 task_args[7] = 0;
284 } else task_args[6] = 0;
285
286 pid = task();
287 if (pid == -1) return 0;
288 if (waitpid(pid, &stat, 0) == -1) return fails_sys("task");
289
290 task_echo(msg_buf, MAX_MSGLEN);
291 return (wait_exitcode(stat) == 0);
292 }
inst_link(struct install_item * ins,unsigned int fl)293 int inst_link(struct install_item *ins, unsigned int fl)
294 {
295 int pid;
296 int stat;
297
298 task_args[0] = EXT_INST_LINK;
299 task_args[1] = ins->dir;
300 task_args[2] = ins->src;
301 task_args[3] = ins->dst;
302 if (fl & INSTALL_DRYRUN) {
303 task_args[4] = "dryrun";
304 task_args[5] = 0;
305 } else task_args[4] = 0;
306
307 pid = task();
308 if (pid == -1) return 0;
309 if (waitpid(pid, &stat, 0) == -1) return fails_sys("task");
310
311 task_echo(msg_buf, MAX_MSGLEN);
312 return (wait_exitcode(stat) == 0);
313 }
inst_mkdir(struct install_item * ins,unsigned int fl)314 int inst_mkdir(struct install_item *ins, unsigned int fl)
315 {
316 int pid;
317 int stat;
318
319 task_args[0] = EXT_INST_DIR;
320 task_args[1] = ins->dir;
321 task_args[2] = uidbuf;
322 task_args[3] = gidbuf;
323 task_args[4] = permbuf;
324 if (fl & INSTALL_DRYRUN) {
325 task_args[5] = "dryrun";
326 task_args[6] = 0;
327 } else task_args[5] = 0;
328
329 pid = task();
330 if (pid == -1) return 0;
331 if (waitpid(pid, &stat, 0) == -1) return fails_sys("task");
332
333 task_echo(msg_buf, MAX_MSGLEN);
334 return (wait_exitcode(stat) == 0);
335 }
inst_liblink(struct install_item * ins,unsigned int fl)336 int inst_liblink(struct install_item *ins, unsigned int fl)
337 {
338 return inst_link(ins, fl);
339 }
340
341 /* name translation callbacks */
ntran_copy(struct install_item * ins)342 int ntran_copy(struct install_item *ins)
343 {
344 if (!ins->src) return fails("src file undefined");
345 if (!ins->dir) return fails("directory unefined");
346 if (!ins->dst) ins->dst = ins->src;
347
348 if (str_ends(ins->src, ".vlb")) {
349 if (!libname(ins->src, src_name)) return 0;
350 ins->src = src_name;
351 }
352 if (str_ends(ins->dst, ".vlb")) {
353 if (!libname(ins->dst, dst_name)) return 0;
354 ins->dst = dst_name;
355 }
356
357 if (!base_name(ins->dst, &ins->dst)) return fails("invalid path");
358 if (snprintf(dst_tmp, MAX_PATHLEN, "%s/%s", ins->dir, ins->dst) < 0)
359 return fails_sys("snprintf");
360
361 ins->dst = dst_tmp;
362 return 1;
363 }
ntran_link(struct install_item * ins)364 int ntran_link(struct install_item *ins)
365 {
366 if (!ins->src) return fails("src file undefined");
367 if (!ins->dir) return fails("directory unefined");
368 if (!ins->dst) return fails("dst name undefined");
369 return 1;
370 }
ntran_mkdir(struct install_item * ins)371 int ntran_mkdir(struct install_item *ins)
372 {
373 if (!ins->dst) ins->dst = ins->src;
374 return 1;
375 }
ntran_liblink(struct install_item * ins)376 int ntran_liblink(struct install_item *ins)
377 {
378 int pid;
379 int stat;
380 int r;
381
382 if (!ins->src) return fails("src file undefined");
383 if (!ins->dir) return fails("directory unefined");
384 if (!ins->dst) return fails("dst name undefined");
385
386 if (str_ends(ins->src, ".vlb")) {
387 if (!libname(ins->src, src_tmp)) return 0;
388 ins->src = src_tmp;
389 if (!base_name(ins->src, &ins->src)) return fails("invalid path");
390 mem_copy(src_name, ins->src, MAX_PATHLEN);
391 ins->src = src_name;
392 }
393
394 task_args[0] = EXT_INST_SOSUFFIX;
395 task_args[1] = 0;
396 pid = task();
397 if (pid == -1) return 0;
398 if (waitpid(pid, &stat, 0) == -1) return fails_sys("task");
399
400 r = task_read(tmp_buf, MAX_PATHLEN);
401 if (r == -1) return fails_sys("read");
402 if (r == 0) { fail_noread(); return 0; }
403 tmp_buf[r - 1] = 0;
404
405 if (!base_name(ins->dst, &ins->dst)) return fails("invalid path");
406 if (snprintf(dst_tmp, MAX_PATHLEN, "%s.%s", ins->dst, tmp_buf) < 0)
407 return fails_sys("snprintf");
408 ins->dst = dst_tmp;
409
410 if (task_close() == -1) return 0;
411 if (task_pipes() == -1) return 0;
412 return 1;
413 }
ntran_chk_link(struct install_item * ins)414 int ntran_chk_link(struct install_item *ins)
415 {
416 if (!ntran_link(ins)) return 0;
417 if (snprintf(dst_name, MAX_PATHLEN, "%s/%s", ins->dir, ins->dst) < 0)
418 return fails_sys("sprintf");
419 ins->dst = dst_name;
420 return 1;
421 }
ntran_chk_liblink(struct install_item * ins)422 int ntran_chk_liblink(struct install_item *ins)
423 {
424 if (!ntran_liblink(ins)) return 0;
425 if (snprintf(dst_name, MAX_PATHLEN, "%s/%s", ins->dir, ins->dst) < 0)
426 return fails_sys("sprintf");
427 ins->dst = dst_name;
428 return 1;
429 }
430
431 /* instchk operator callbacks */
instchk_copy(struct install_item * ins,unsigned int fl)432 int instchk_copy(struct install_item *ins, unsigned int fl)
433 {
434 int pid;
435 int stat;
436
437 task_args[0] = EXT_INST_CHECK;
438 task_args[1] = ins->dst;
439 task_args[2] = uidbuf;
440 task_args[3] = gidbuf;
441 task_args[4] = permbuf;
442 task_args[5] = "file";
443 task_args[6] = 0;
444
445 pid = task();
446 if (pid == -1) return 0;
447 if (waitpid(pid, &stat, 0) == -1) return fails_sys("task");
448 task_echo(msg_buf, MAX_MSGLEN);
449
450 if (wait_exitcode(stat)) ++install_failed;
451 return (wait_exitcode(stat) == 0);
452 }
instchk_link(struct install_item * ins,unsigned int fl)453 int instchk_link(struct install_item *ins, unsigned int fl)
454 {
455 int pid;
456 int stat;
457
458 task_args[0] = EXT_INST_CHECK;
459 task_args[1] = ins->dst;
460 task_args[2] = uidbuf;
461 task_args[3] = gidbuf;
462 task_args[4] = permbuf;
463 task_args[5] = "symlink";
464 task_args[6] = 0;
465
466 pid = task();
467 if (pid == -1) return 0;
468 if (waitpid(pid, &stat, 0) == -1) return fails_sys("task");
469 task_echo(msg_buf, MAX_MSGLEN);
470
471 if (wait_exitcode(stat)) ++install_failed;
472 return (wait_exitcode(stat) == 0);
473 }
instchk_mkdir(struct install_item * ins,unsigned int fl)474 int instchk_mkdir(struct install_item *ins, unsigned int fl)
475 {
476 int pid;
477 int stat;
478
479 task_args[0] = EXT_INST_CHECK;
480 task_args[1] = ins->dir;
481 task_args[2] = uidbuf;
482 task_args[3] = gidbuf;
483 task_args[4] = permbuf;
484 task_args[5] = "directory";
485 task_args[6] = 0;
486
487 pid = task();
488 if (pid == -1) return 0;
489 if (waitpid(pid, &stat, 0) == -1) return fails_sys("task");
490 task_echo(msg_buf, MAX_MSGLEN);
491
492 if (wait_exitcode(stat)) ++install_failed;
493 return (wait_exitcode(stat) == 0);
494 }
instchk_liblink(struct install_item * ins,unsigned int fl)495 int instchk_liblink(struct install_item *ins, unsigned int fl)
496 {
497 return instchk_link(ins, fl);
498 }
499
500 /* deinstall operator callbacks */
deinst_copy(struct install_item * ins,unsigned int fl)501 int deinst_copy(struct install_item *ins, unsigned int fl)
502 {
503 printf("unlink %s\n", ins->dst);
504 if (fl & INSTALL_DRYRUN) return 1;
505 if (unlink(ins->dst) == -1) return fails_sys("unlink");
506 return 1;
507 }
deinst_link(struct install_item * ins,unsigned int fl)508 int deinst_link(struct install_item *ins, unsigned int fl)
509 {
510 printf("unlink %s/%s\n", ins->dir, ins->dst);
511 if (snprintf(tmp_buf, MAX_PATHLEN, "%s/%s", ins->dir, ins->dst) < 0)
512 return fails_sys("snprintf");
513 ins->dst = tmp_buf;
514
515 if (fl & INSTALL_DRYRUN) return 1;
516 if (unlink(ins->dst) == -1) return fails_sys("unlink");
517 return 1;
518 }
deinst_mkdir(struct install_item * ins,unsigned int fl)519 int deinst_mkdir(struct install_item *ins, unsigned int fl)
520 {
521 printf("rmdir %s\n", ins->dir);
522 if (fl & INSTALL_DRYRUN) return 1;
523 if (rmdir(ins->dir) == -1) return fails_sys("rmdir");
524 return 1;
525 }
deinst_liblink(struct install_item * ins,unsigned int fl)526 int deinst_liblink(struct install_item *ins, unsigned int fl)
527 {
528 return deinst_link(ins, fl);
529 }
530
531 /* operator callback tables */
532 struct instop {
533 int (*oper)(struct install_item *, unsigned int);
534 int (*trans)(struct install_item *);
535 };
536 struct instop install_opers[] = {
537 { inst_copy, ntran_copy },
538 { inst_link, ntran_link },
539 { inst_mkdir, ntran_mkdir },
540 { inst_liblink, ntran_liblink },
541 };
542 struct instop instchk_opers[] = {
543 { instchk_copy, ntran_copy },
544 { instchk_link, ntran_chk_link },
545 { instchk_mkdir, ntran_mkdir },
546 { instchk_liblink, ntran_chk_liblink },
547 };
548 struct instop deinst_opers[] = {
549 { deinst_copy, ntran_copy },
550 { deinst_link, ntran_link },
551 { deinst_mkdir, ntran_mkdir },
552 { deinst_liblink, ntran_liblink },
553 };
554
555 /* interface */
check_tools()556 int check_tools()
557 {
558 unsigned int i;
559 int r;
560 r = 1;
561 for (i = 0; i < (sizeof(ext_tools) / sizeof(const char *)); ++i) {
562 if (access(ext_tools[i], X_OK) == -1) {
563 printf("%s: fatal: %s missing or not executable\n", progname,
564 ext_tools[i]);
565 r = 0;
566 }
567 }
568 return r;
569 }
570
install(struct install_item * ins,unsigned int fl)571 int install(struct install_item *ins, unsigned int fl)
572 {
573 int r;
574
575 r = 1;
576 if (task_pipes() == -1) return 0;
577 if (!lookup(ins, &uid, &gid)) { fail(); goto CLEANUP; }
578 if (!uidgidperm_to_text(uid, gid, ins->perm)) goto CLEANUP;
579
580 r = install_opers[ins->op].trans(ins);
581 if (!r) goto CLEANUP;
582 r = install_opers[ins->op].oper(ins, fl);
583
584 CLEANUP:
585 fflush(0);
586 task_close();
587 return r;
588 }
589
install_check(struct install_item * ins)590 int install_check(struct install_item *ins)
591 {
592 int r;
593
594 r = 1;
595 if (task_pipes() == -1) return 0;
596 if (!lookup(ins, &uid, &gid)) { fail(); goto CLEANUP; }
597 if (!uidgidperm_to_text(uid, gid, ins->perm)) goto CLEANUP;
598
599 r = instchk_opers[ins->op].trans(ins);
600 if (!r) goto CLEANUP;
601 r = instchk_opers[ins->op].oper(ins, 0);
602
603 CLEANUP:
604 fflush(0);
605 task_close();
606 return r;
607 }
608
deinstall(struct install_item * ins,unsigned int fl)609 int deinstall(struct install_item *ins, unsigned int fl)
610 {
611 int r;
612
613 r = 1;
614 if (task_pipes() == -1) return 0;
615 if (!lookup(ins, &uid, &gid)) { fail(); goto CLEANUP; }
616 if (!uidgidperm_to_text(uid, gid, ins->perm)) goto CLEANUP;
617
618 r = deinst_opers[ins->op].trans(ins);
619 if (!r) goto CLEANUP;
620 r = deinst_opers[ins->op].oper(ins, fl);
621
622 CLEANUP:
623 fflush(0);
624 task_close();
625 return r;
626 }
627