tpm.c - pm - barely a pack manager
(HTM) git clone git://z3bra.org/pm
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
tpm.c (20146B)
---
1 #include <errno.h>
2 #include <dirent.h>
3 #include <fcntl.h>
4 #include <limits.h>
5 #include <regex.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/mman.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13
14 #include <archive.h>
15 #include <archive_entry.h>
16
17 #include "arg.h"
18 #include "config.h"
19
20 #define log(l,...) if(verbose>=l){printf(__VA_ARGS__);}
21
22 struct pack {
23 char *path;
24 char *name;
25 char *version;
26 };
27
28 /* possible actions */
29 enum {
30 ACTION_INVALID = -1,
31 ACTION_INSTALL = 0,
32 ACTION_DELETE = 1,
33 ACTION_UPDATE = 2,
34 ACTION_INSPECT = 3,
35 ACTION_DEFAULT = ACTION_INSPECT,
36 };
37
38 /* error codes */
39 enum {
40 ERR_INVALID_ACTION = 1,
41 ERR_METADATA = 2,
42 ERR_PACK_LOAD = 3,
43 ERR_DELETE = 4,
44 ERR_INSPECT = 5,
45 ERR_UNPACK = 6,
46 };
47
48 enum {
49 LOG_NONE = 0,
50 LOG_INFO = 1,
51 LOG_VERBOSE = 2,
52 LOG_DEBUG = 3
53 };
54
55 void usage(char *name);
56 int is_empty(char *dir);
57 int mkdir_parents(char *dir, mode_t mode);
58 char *base_name(char *path);
59 int re_match(const char *re, const char *str);
60
61 struct pack *pack_load_tarball(char *path);
62 struct pack *pack_load_metadata(const char *datadir, char *name);
63 void pack_free(struct pack *p);
64 #ifdef REPOAWARE
65 int pack_find(char *, char *);
66 #endif
67 int pack_extract(const char *rootfs, const char *datadir, struct pack *p);
68 int pack_install(const char *rootfs, const char *datadir, struct pack *p);
69 int pack_delete(const char *rootfs, const char *datadir, struct pack *p);
70
71 int inspect_installed(const char *datadir, const char *name);
72 int inspect_version(const char *datadir, const char *name, char version[]);
73 int inspect_collision(const char *rootfs, struct pack *p);
74 int inspect_files(int fd, const char *datadir, const char *name);
75 int inspect_system(int fd, const char *datadir);
76
77 int write_metadata(const char *datadir, struct pack *pack);
78 int write_entry(struct archive *a, struct archive *w);
79 int delete_node(char *path);
80 int delete_content(const char *rootfs, char *map, size_t size);
81 int delete_metadata(const char *datadir, char *name);
82
83 /* action wrappers around CLI arguments */
84 int install(const char *rootfs, const char *datadir, char *path);
85 int update(const char *rootfs, const char *datadir, char *path);
86 int delete(const char *rootfs, const char *datadir, char *name);
87 int inspect(const char *datadir, const char *name);
88
89 int verbose = LOG_NONE;
90 int overwrite = 0;
91
92 void
93 usage(char *name)
94 {
95 fprintf(stderr, "usage: %s -adfiuv [PACK..]\n" , name);
96 }
97
98
99 /*
100 * Returns 0 if a directory is empty, -1 otherwise
101 */
102 int
103 is_empty(char *path)
104 {
105 DIR *d;
106 struct dirent *p;
107
108 if (!(d = opendir(path))) {
109 perror(path);
110 return -1;
111 }
112
113 while ((p = readdir(d))) {
114 if (strcmp(p->d_name, ".") && strcmp(p->d_name, "..")) {
115 closedir(d);
116 return -1;
117 }
118 }
119
120 closedir(d);
121 return 0;
122 }
123
124
125 /*
126 * Recursive mkdir, taken from the ii project
127 * http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html
128 */
129 int
130 mkdir_parents(char *path, mode_t mode)
131 {
132 char tmp[PATH_MAX] = "";
133 char *p = NULL;
134 size_t len;
135
136 snprintf(tmp, sizeof(tmp), "%s", path);
137 len = strlen(tmp);
138 if(tmp[len - 1] == '/')
139 tmp[len - 1] = 0;
140 for(p = tmp + 1; *p; p++)
141 if(*p == '/') {
142 *p = 0;
143 mkdir(tmp, mode);
144 *p = '/';
145 }
146 return mkdir(tmp, mode);
147 }
148
149
150 /*
151 * Return a pointer to the basename, or NULL if path ends with '/'
152 */
153 char *
154 base_name(char *path)
155 {
156 char *b = strrchr(path, '/');
157 return b ? b + 1 : path;
158 }
159
160
161 /*
162 * Check wether a string matches a regular expression
163 */
164 int
165 re_match(const char *re, const char *str)
166 {
167 int retval = -1;
168 regex_t preg;
169 regmatch_t sub[1];
170
171 if (regcomp(&preg, re, REG_EXTENDED)) {
172 fprintf(stderr, "%s: Not a valid expression\n", re);
173 return -1;
174 }
175
176 retval = regexec(&preg, str, 1, sub, 0);
177 regfree(&preg);
178
179 return retval;
180 }
181
182
183 /*
184 * Load a pack from a tarball and return a pack structure
185 */
186 struct pack *
187 pack_load_tarball(char *path)
188 {
189 struct stat st;
190 struct pack *pack = NULL;
191 char *fn, *regex = PACK_FORMAT;
192 regex_t preg;
193 regmatch_t sub[3];
194 size_t i, nmatch = 3, sublen[3];
195
196 fn = base_name(path);
197 if (stat(path, &st) < 0) {
198 perror(path);
199 return NULL;
200 }
201
202 regcomp(&preg, regex, REG_EXTENDED);
203 regexec(&preg, fn, nmatch, sub, 0);
204
205 if (!(pack = malloc(sizeof(struct pack)))) {
206 perror(path);
207 regfree(&preg);
208 return NULL;
209 }
210
211 for (i=0; i<nmatch; i++)
212 sublen[i] = sub[i].rm_eo - sub[i].rm_so;
213
214 pack->path = strdup(path);
215 pack->name = malloc(sublen[1] + 1);
216 pack->version = malloc(sublen[2] + 1);
217
218 strncpy(pack->name, fn+sub[1].rm_so, sublen[1]);
219 strncpy(pack->version, fn+sub[2].rm_so, sublen[2]);
220
221 pack->name[sublen[1]] = 0;
222 pack->version[sublen[2]] = 0;
223
224 regfree(&preg);
225
226 return pack;
227 }
228
229
230 /*
231 * Load a pack from a metadata directory and return a pack structure
232 */
233 struct pack *
234 pack_load_metadata(const char *datadir, char *name)
235 {
236 struct pack *pack = NULL;
237 char tmp[PATH_MAX] = "";
238
239 snprintf(tmp, PATH_MAX, "%s/%s", datadir, name);
240
241 if (inspect_installed(datadir, name)) {
242 fprintf(stderr, "%s: Not installed\n", name);
243 return NULL;
244 }
245
246 if (!(pack = malloc(sizeof(struct pack)))) {
247 perror("malloc");
248 return NULL;
249 }
250
251 pack->name = strdup(name);
252 pack->path = strdup(tmp);
253 pack->version = malloc(LINE_MAX);
254 inspect_version(datadir, pack->name, pack->version);
255
256 return pack;
257 }
258
259
260 /*
261 * Free a pack structure
262 */
263 void
264 pack_free(struct pack *p)
265 {
266 if (!p)
267 return;
268
269 if (p->path)
270 free(p->path);
271 if (p->name)
272 free(p->name);
273 if (p->version)
274 free(p->version);
275
276 free(p);
277 }
278
279
280 #ifdef REPOAWARE
281 /*
282 * Find a pack filename using an external tool writing the path to the
283 * pack to stdout.
284 * The tool should be called as:
285 *
286 * tool <name>
287 */
288 int
289 pack_find(char *name, char *out)
290 {
291 int fd[2], status;
292 size_t len = 0;
293
294 pipe(fd);
295 if (!fork()) {
296 close(1);
297 close(fd[0]);
298 dup2(fd[1], 1);
299 execlp(REPO_EXEC, REPO_EXEC, name, NULL);
300 }
301
302 close(fd[1]);
303
304 wait(&status);
305 if (status)
306 exit(1);
307
308 len = read(fd[0], out, PATH_MAX);
309 close(fd[0]);
310
311 if (len < 1)
312 return -1;
313
314 out[len - 1] = 0;
315
316 return 0;
317 }
318 #endif
319
320
321 /*
322 * Extract a tarball to the given directory
323 */
324 int
325 pack_extract(const char *rootfs, const char *datadir, struct pack *p)
326 {
327 int r, fd;
328 struct archive *a;
329 struct archive *w;
330 struct archive_entry *e;
331 char cwd[PATH_MAX] = "";
332 char tmp[PATH_MAX] = "";
333
334 int mask = ARCHIVE_EXTRACT_PERM
335 |ARCHIVE_EXTRACT_SECURE_NODOTDOT;
336
337 snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, p->name);
338
339 log(LOG_DEBUG, "+ %s\n", tmp);
340 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
341 perror(tmp);
342 return -1;
343 }
344
345 a = archive_read_new();
346 archive_read_support_filter_gzip(a);
347 archive_read_support_filter_bzip2(a);
348 archive_read_support_filter_xz(a);
349 archive_read_support_format_tar(a);
350
351 /* try and open the tarball of our pack */
352 if ((r = archive_read_open_filename(a, p->path, 0)) != ARCHIVE_OK) {
353 fprintf(stderr, "%s: %s\n", p->path, archive_error_string(a));
354 archive_read_free(a);
355 close(fd);
356 return r;
357 }
358
359 w = archive_write_disk_new();
360 archive_write_disk_set_options(w, mask);
361
362 getcwd(cwd, PATH_MAX);
363 chdir(rootfs);
364 log(LOG_VERBOSE, "extracting pack\n");
365 while ((r = archive_read_next_header(a, &e)) != ARCHIVE_EOF) {
366 if (r != ARCHIVE_OK) {
367 fprintf(stderr, "%s: %s\n", archive_entry_pathname(e),
368 archive_error_string(a));
369 break;
370 }
371
372 if ((r = archive_write_header(w, e)) != ARCHIVE_OK) {
373 fprintf(stderr, "%s: %s\n", archive_entry_pathname(e),
374 archive_error_string(w));
375 break;
376 }
377
378 log(LOG_DEBUG, "+ %s%s\n", rootfs, archive_entry_pathname(e));
379 if ((r = write_entry(a, w)) != ARCHIVE_OK) {
380 fprintf(stderr, "%s: %s\n", archive_entry_pathname(e),
381 archive_error_string(w));
382 break;
383 }
384
385 dprintf(fd, "%s\n", archive_entry_pathname(e));
386 archive_write_finish_entry(w);
387 }
388 close(fd);
389 chdir(cwd);
390
391 archive_write_free(w);
392 archive_read_free(a);
393
394 return r == ARCHIVE_EOF ? ARCHIVE_OK : r;
395 }
396
397
398 /*
399 * Install a pack to rootfs, writing metadata to datadir
400 * If overwrite is set to 1, it will overwrite all files
401 */
402 int
403 pack_install(const char *rootfs, const char *datadir, struct pack *p)
404 {
405 int r;
406 char tmp[PATH_MAX] = "";
407
408 if (overwrite == 0) {
409 snprintf(tmp, PATH_MAX, "%s/%s", datadir, p->name);
410 if (inspect_installed(datadir, p->name) == 0) {
411 fprintf(stderr, "%s: Already installed\n", p->name);
412 return -1;
413 }
414 if (inspect_collision(rootfs, p) != 0)
415 return -1;
416 }
417
418 log(LOG_VERBOSE, "writing metadata\n");
419 if (write_metadata(datadir, p) < 0) {
420 delete_metadata(datadir, p->name);
421 return -1;
422 }
423
424 r = pack_extract(rootfs, datadir, p);
425
426 if (r != 0) {
427 fprintf(stderr, "%s: Extraction failed\n", p->path);
428 pack_delete(rootfs, datadir, p);
429 }
430
431 return r;
432 }
433
434
435 /*
436 * Delete a pack from the system.
437 * This will read the file datadir/name/files, change directory to rootfs
438 * and call delete_content()
439 */
440 int
441 pack_delete(const char *rootfs, const char *datadir, struct pack *p)
442 {
443 int fd, r = 0;
444 char *addr = NULL;
445 char tmp[PATH_MAX];
446 struct stat st;
447
448 snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, p->name);
449 if (stat(tmp, &st) < 0) {
450 perror(tmp);
451 return -1;
452 }
453 if ((fd = open(tmp, O_RDONLY)) < 0) {
454 perror(tmp);
455 return ERR_DELETE;
456 }
457
458 if (st.st_size > 0) {
459 addr = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
460 if (addr == MAP_FAILED) {
461 perror("mmap");
462 close(fd);
463 return ERR_DELETE;
464 }
465
466 log(LOG_VERBOSE, "cleaning installed files\n");
467 /* ignore errors so everything gets deleted */
468 if (delete_content(rootfs, addr, st.st_size) < 0) {
469 close(fd);
470 r = ERR_DELETE;
471 }
472 munmap(addr, st.st_size);
473 }
474 close(fd);
475
476 log(LOG_VERBOSE, "cleaning metadata\n");
477 if (delete_metadata(datadir, p->name) < 0) {
478 fprintf(stderr, "%s/%s: %s\n", datadir, p->name, strerror(errno));
479 r = ERR_DELETE;
480 }
481
482 return r;
483 }
484
485
486 /*
487 * Returns 0 if "name" is installed, non-zero otherwise
488 */
489 int
490 inspect_installed(const char *datadir, const char *name)
491 {
492 char tmp[PATH_MAX] = "";
493 struct stat st;
494
495 snprintf(tmp, PATH_MAX, "%s/%s", datadir, name);
496 log(LOG_VERBOSE, "checking existence of %s\n", tmp);
497 return stat(tmp, &st) || !S_ISDIR(st.st_mode);
498 }
499
500
501 /*
502 * Read the version file for a pack and fill the given pointer with it
503 */
504 int
505 inspect_version(const char *datadir, const char *name, char version[LINE_MAX])
506 {
507 FILE *stream;
508 char tmp[PATH_MAX] = "", *lf = NULL;
509
510 snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, name);
511 if ((stream = fopen(tmp, "r")) == NULL) {
512 snprintf(version, 10, "(unknown)");
513 return 1;
514 } else {
515 fgets(version, LINE_MAX, stream);
516 if ((lf = strchr(version, '\n')) != NULL)
517 *lf = 0;
518 }
519
520 fclose(stream);
521
522 return 0;
523 }
524
525
526 /*
527 * Check for collisions between the filesystem and the tarball
528 */
529 int
530 inspect_collision(const char *rootfs, struct pack *p)
531 {
532 int r = 0;
533 char cwd[PATH_MAX] = "";
534 struct stat st;
535 struct archive *a;
536 struct archive_entry *e;
537
538 a = archive_read_new();
539 archive_read_support_filter_gzip(a);
540 archive_read_support_filter_bzip2(a);
541 archive_read_support_filter_xz(a);
542 archive_read_support_format_tar(a);
543
544 r = archive_read_open_filename(a, p->path, 0);
545 if (r != ARCHIVE_OK) {
546 fprintf(stderr, "%s: %s\n", p->path, archive_error_string(a));
547 return -1;
548 }
549
550 getcwd(cwd, PATH_MAX);
551 chdir(rootfs);
552 while (archive_read_next_header(a, &e) == ARCHIVE_OK) {
553 if (stat(archive_entry_pathname(e), &st) == 0 && !S_ISDIR(st.st_mode)) {
554 fprintf(stderr, "%s: file exists\n", archive_entry_pathname(e));
555 r++;
556 break;
557 }
558 archive_read_data_skip(a);
559 }
560
561 archive_read_free(a);
562 chdir(cwd);
563
564 return r;
565 }
566
567
568 /*
569 * Write files installed by a pack to a file descriptor
570 */
571 int
572 inspect_files(int fd, const char *datadir, const char *name)
573 {
574 int meta;
575 char tmp[PATH_MAX] = "";
576 size_t len;
577
578 snprintf(tmp, PATH_MAX, "%s/%s/files", datadir, name);
579 if ((meta = open(tmp, O_RDONLY)) < 0) {
580 perror(tmp);
581 return -1;
582 }
583
584 while ((len = read(meta, tmp, PATH_MAX)) > 0) {
585 tmp[len] = 0;
586 dprintf(fd, "%s", tmp);
587 }
588
589 return 0;
590 }
591
592
593 /*
594 * Write packs installed in datadir to a file descriptor
595 */
596 int
597 inspect_system(int fd, const char *datadir)
598 {
599 DIR *d;
600 struct dirent *p;
601 char ver[LINE_MAX];
602
603 if (!(d = opendir(datadir))) {
604 perror(datadir);
605 return -1;
606 }
607
608 while ((p = readdir(d)))
609 if (p->d_name[0] != '.') {
610 inspect_version(datadir, p->d_name, ver);
611 dprintf(fd, "%s", p->d_name);
612 if (verbose)
613 dprintf(fd, "\t%s", ver);
614 write(fd, "\n", 1);
615 }
616
617 closedir(d);
618 return 0;
619 }
620
621
622 /*
623 * Write metadata about a pack file:
624 * + datadir/packname/version - version of the pack installed
625 */
626 int
627 write_metadata(const char *datadir, struct pack *p)
628 {
629 int fd, r;
630 struct stat st;
631 char tmp[PATH_MAX];
632
633 snprintf(tmp, PATH_MAX, "%s/%s", datadir, p->name);
634
635 if (stat(tmp, &st) < 0 && errno == ENOENT) {
636 log(LOG_DEBUG, "+ %s\n", tmp);
637 if ((r = mkdir_parents(tmp, 0755)) < 0)
638 return r;
639 }
640
641 snprintf(tmp, PATH_MAX, "%s/%s/version", datadir, p->name);
642
643 log(LOG_DEBUG, "+ %s\n", tmp);
644 if ((fd = open(tmp, O_CREAT|O_WRONLY|O_TRUNC, 0644)) < 0) {
645 perror(tmp);
646 return -1;
647 }
648
649 r = write(fd, p->version, strnlen(p->version, LINE_MAX));
650 r += write(fd, "\n", 1);
651
652 if (r < 1) {
653 perror(tmp);
654 close(fd);
655 return -1;
656 }
657
658 close(fd);
659 return 0;
660 }
661
662
663 /*
664 * Write an archive entry on the disk, thus creating the file
665 */
666 int
667 write_entry(struct archive *a, struct archive *w)
668 {
669 int r;
670 const void *buf;
671 size_t len;
672 off_t off;
673
674 for (;;) {
675 r = archive_read_data_block(a, &buf, &len, &off);
676 switch (r) {
677 case ARCHIVE_EOF: return 0;
678 case ARCHIVE_OK: break;
679 default: return r;
680 }
681
682 r = archive_write_data_block(w, buf, len, off);
683 if (r != ARCHIVE_OK)
684 return r;
685 }
686 }
687
688
689 /*
690 * Deletes a node, be it a file or a directory.
691 * In case the node doesn't exists, we're done
692 */
693 int
694 delete_node(char *path)
695 {
696 int r = 0;
697 struct stat st;
698 size_t len = 0;
699
700 len = strnlen(path, PATH_MAX);
701 /* remove potential trailing '/' */
702 if (path[len - 1] == '/')
703 path[--len] = 0;
704
705 log(LOG_DEBUG, "- %s\n", path);
706
707 /*
708 * if path doesn't exist anymore, it's all good :)
709 * we use lstat here so that dangling links can be delt with too
710 */
711 if (lstat(path, &st) < 0 && errno == ENOENT)
712 return 0;
713
714 if (S_ISDIR(st.st_mode) && is_empty(path) == 0) {
715 if ((r = rmdir(path)) < 0) {
716 perror(path);
717 return r;
718 }
719 }
720
721 if (!S_ISDIR(st.st_mode) && (r = unlink(path)) < 0) {
722 perror(path);
723 return r;
724 }
725
726 return 0;
727 }
728
729
730 /*
731 * Delete all entries listed in the given file.
732 * if the entry doesn't exist, it will be ignored
733 */
734 int
735 delete_content(const char *rootfs, char *map, size_t size)
736 {
737 char *path = NULL;
738 char tmp[PATH_MAX] = "";
739 size_t off;
740
741 if (size < 1)
742 return -1;
743
744 do {
745 /*
746 * it is assumed here that the file is POSIX and thus that
747 * the last char will be \n.
748 * This might sound stupid; but this is what pack_extract() will do.
749 * We read the mmap file backward until we encounter \n or the
750 * beginning of the file. If it's an \n, we replace it by 0 and
751 * process to the deletion of the inode, either with rmdir or
752 * unlink.
753 *
754 * When setting the path, the current offset is either at the
755 * start of the file, or on a 0 (freshly replaced \n). In case
756 * We are on a 0, the path should be set to the string just
757 * after (&map[off] + 1). This doesn't apply to start of file.
758 */
759 for (off=size-1; off>0 && map[off] != '\n'; off--);
760 map[off] = (off > 0 ? 0 : *map);
761 path = (off < size-1 ? &map[off] + (off>0?1:0) : NULL);
762
763 if (path != NULL && strnlen(path, PATH_MAX) > 0) {
764 snprintf(tmp, PATH_MAX, "%s%s", rootfs, path);
765 if (delete_node(tmp) < 0)
766 return ERR_DELETE;
767 }
768 } while (off > 0);
769
770 return 0;
771 }
772
773
774 /*
775 * Delete the metadata stored for a given pack name
776 */
777 int
778 delete_metadata(const char *datadir, char *name)
779 {
780 int i;
781 char path[PATH_MAX] = "";
782 char *meta[] = { "files", "version", NULL };
783
784 for (i = 0; meta[i] != NULL; i++) {
785 snprintf(path, PATH_MAX, "%s/%s/%s", datadir, name, meta[i]);
786 log(LOG_DEBUG, "- %s\n", path);
787 if (unlink(path) < 0) {
788 perror(path);
789 return ERR_DELETE;
790 }
791 }
792
793 /* remove metadata directory, no matter what */
794 snprintf(path, PATH_MAX, "%s/%s", datadir, name);
795 log(LOG_DEBUG, "- %s\n", path);
796 return rmdir(path);
797 }
798
799
800 /*
801 * Install a pack from the given path. This wraps load/install of a pack
802 */
803 int
804 install(const char *rootfs, const char *datadir, char *name)
805 {
806 int r = 0;
807 char path[PATH_MAX];
808 struct pack *p = NULL;
809
810 if (re_match(PACK_FORMAT, name) != 0) {
811 #ifdef REPOAWARE
812 pack_find(name, path);
813 #else
814 fprintf(stderr, "%s: invalid filename\n", name);
815 exit(1);
816 #endif
817 } else {
818 snprintf(path, PATH_MAX, "%s", name);
819 }
820
821 if ((p = pack_load_tarball(path)) == NULL)
822 return ERR_PACK_LOAD;
823
824 r += pack_install(rootfs, datadir, p);
825
826 if (r == 0)
827 log(LOG_INFO, "installed %s (%s)\n", p->name, p->version);
828
829 pack_free(p);
830
831 return r;
832 }
833
834
835 /*
836 * Update a pack. This should be as easy as delete/install.
837 * Deletion is required in case the file structure changes
838 */
839 int
840 update(const char *rootfs, const char *datadir, char *name)
841 {
842 int r = 0, tmp = overwrite;
843 char path[PATH_MAX], ver[LINE_MAX];
844 struct pack *p = NULL;
845
846 if (re_match(PACK_FORMAT, name) != 0) {
847 #ifdef REPOAWARE
848 pack_find(name, path);
849 #else
850 fprintf(stderr, "%s: invalid filename\n", name);
851 exit(1);
852 #endif
853 } else {
854 snprintf(path, PATH_MAX, "%s", name);
855 }
856
857 if ((p = pack_load_tarball(path)) == NULL)
858 return ERR_PACK_LOAD;
859
860 if (inspect_installed(datadir, p->name)) {
861 fprintf(stderr, "%s: not installed\n", p->name);
862 pack_free(p);
863 return ERR_DELETE;
864 }
865
866 inspect_version(datadir, p->name, ver);
867 if (!overwrite && !strncmp(p->version, ver, LINE_MAX)) {
868 fprintf(stderr, "%s: already at version %s\n", p->name, p->version);
869 pack_free(p);
870 return ERR_DELETE;
871 }
872
873 if (pack_delete(rootfs, datadir, p) != 0) {
874 pack_free(p);
875 return ERR_DELETE;
876 }
877
878 overwrite = 1;
879 r += pack_install(rootfs, datadir, p);
880 overwrite = tmp;
881
882 if (r == 0)
883 log(LOG_INFO, "updated %s (%s -> %s)\n", p->name, ver, p->version);
884
885 pack_free(p);
886
887 return r;
888 }
889
890
891 /*
892 * Delete a currently installed pack. This wraps load/delete functions
893 */
894 int
895 delete(const char *rootfs, const char *datadir, char *name)
896 {
897 int r = 0;
898 struct pack *p = NULL;
899
900 if (inspect_installed(datadir, name)) {
901 fprintf(stderr, "%s: not installed\n", name);
902 return ERR_DELETE;
903 }
904
905 if ((p = pack_load_metadata(datadir, name)) == NULL)
906 return ERR_PACK_LOAD;
907
908 r += pack_delete(rootfs, datadir, p);
909
910 if (r == 0)
911 log(LOG_INFO, "deleted %s (%s)\n", p->name, p->version);
912
913 pack_free(p);
914
915 return r;
916 }
917
918
919 /*
920 * Inspect the system, either by looking into a pack, checking installed
921 * files, or listing packs actually installed
922 */
923 int
924 inspect(const char *datadir, const char *packname)
925 {
926 /* name is NULL, list packs installed */
927 if (!packname)
928 return inspect_system(1, datadir);
929
930 /* otherwise, list files installed by a pack */
931 return inspect_files(1, datadir, packname);
932 }
933
934
935 int
936 main (int argc, char **argv)
937 {
938 int r = 0;
939 char *n = NULL, *argv0;
940 uint8_t action = ACTION_DEFAULT;
941 char rootfs[PATH_MAX] = "";
942 char datadir[PATH_MAX] = "";
943
944 strncpy(rootfs, PACK_ROOT, PATH_MAX -1);
945 strncat(rootfs, "/", PATH_MAX - 1);
946 strncpy(datadir, PACK_DATA, PATH_MAX);
947
948 ARGBEGIN{
949 case 'a':
950 action = ACTION_INSTALL;
951 break;
952 case 'd':
953 action = ACTION_DELETE;
954 break;
955 case 'f':
956 overwrite = 1;
957 break;
958 case 'i':
959 action = ACTION_INSPECT;
960 if (argc > 1)
961 n = ARGF();
962 break;
963 case 'u':
964 action = ACTION_UPDATE;
965 break;
966 case 'v':
967 verbose++;
968 break;
969 default:
970 action = ACTION_INVALID;
971 }ARGEND;
972
973 switch (action) {
974 case ACTION_INSTALL:
975 case ACTION_UPDATE:
976 case ACTION_DELETE:
977 while(*argv) {
978 if (action == ACTION_INSTALL)
979 r += install(rootfs, datadir, *argv);
980 if (action == ACTION_UPDATE)
981 r += update(rootfs, datadir, *argv);
982 if (action == ACTION_DELETE)
983 r += delete(rootfs, datadir, *argv);
984 argv++;
985 }
986 break;
987 case ACTION_INSPECT:
988 if (inspect(datadir, n) != 0)
989 return ERR_INSPECT;
990 break;
991 default:
992 usage(argv0);
993 return ERR_INVALID_ACTION;
994 }
995
996 return r;
997 }