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