11 #include <sys/types.h>
17 #define DEPENDSMAX 64 /* maximum number of depends we can handle */
19 /* Should we do full dependency checking? */
22 /* Should we do debugging? */
26 #define SYSTEM(x) do_system(x)
27 #define DPRINTF(fmt,args...) fprintf(stderr, fmt, ##args)
29 #define SYSTEM(x) system(x)
30 #define DPRINTF(fmt,args...) /* nothing */
34 extern int deb_extract(int optflags, const char *dir_name, const char *deb_filename);
35 static const int dpkg_deb_contents = 1;
36 static const int dpkg_deb_control = 2;
37 // const int dpkg_deb_info = 4;
38 static const int dpkg_deb_extract = 8;
39 static const int dpkg_deb_verbose_extract = 16;
40 static const int dpkg_deb_list = 32;
42 static const char statusfile[] = "/var/lib/dpkg/status.udeb";
43 static const char new_statusfile[] = "/var/lib/dpkg/status.udeb.new";
44 static const char bak_statusfile[] = "/var/lib/dpkg/status.udeb.bak";
46 static const char dpkgcidir[] = "/var/lib/dpkg/tmp.ci/";
48 static const char infodir[] = "/var/lib/dpkg/info/";
49 static const char udpkg_quiet[] = "UDPKG_QUIET";
51 //static const int status_wantstart = 0;
52 //static const int status_wantunknown = (1 << 0);
53 static const int status_wantinstall = (1 << 1);
54 //static const int status_wanthold = (1 << 2);
55 //static const int status_wantdeinstall = (1 << 3);
56 //static const int status_wantpurge = (1 << 4);
57 static const int status_wantmask = 31;
59 //static const int status_flagstart = 5;
60 static const int status_flagok = (1 << 5); /* 32 */
61 //static const int status_flagreinstreq = (1 << 6);
62 //static const int status_flaghold = (1 << 7);
63 //static const int status_flagholdreinstreq = (1 << 8);
64 static const int status_flagmask = 480;
66 //static const int status_statusstart = 9;
67 //static const int status_statusnoninstalled = (1 << 9); /* 512 */
68 static const int status_statusunpacked = (1 << 10);
69 static const int status_statushalfconfigured = (1 << 11);
70 static const int status_statusinstalled = (1 << 12);
71 static const int status_statushalfinstalled = (1 << 13);
72 //static const int status_statusconfigfiles = (1 << 14);
73 //static const int status_statuspostinstfailed = (1 << 15);
74 //static const int status_statusremovalfailed = (1 << 16);
75 static const int status_statusmask = 130560; /* i assume status_statusinstalled is supposed to be included */
77 static const char *statuswords[][10] = {
78 { (char *) 0, "unknown", "install", "hold", "deinstall", "purge", 0 },
79 { (char *) 5, "ok", "reinstreq", "hold", "hold-reinstreq", 0 },
80 { (char *) 9, "not-installed", "unpacked", "half-configured",
81 "installed", "half-installed", "config-files",
82 "post-inst-failed", "removal-failed", 0 }
85 static const int color_white = 0;
86 static const int color_grey = 1;
87 static const int color_black = 2;
90 typedef struct package_s {
97 int installer_menu_item;
99 char color; /* for topo-sort */
100 struct package_s *requiredfor[DEPENDSMAX];
101 unsigned short requiredcount;
102 struct package_s *next;
106 static int do_system(const char *cmd)
108 DPRINTF("cmd is %s\n", cmd);
112 #define do_system(cmd) system(cmd)
115 static int package_compare(const void *p1, const void *p2)
117 return strcmp(((package_t *)p1)->package,
118 ((package_t *)p2)->package);
122 * NOTE: this was handled by a "rm -rf" shell command
123 * Maybe theis behaviour should be integrated into the rm applet
124 * (i dont appreciate the rm applets recursive action fn)-bug1
126 static int remove_dir(const char *dirname)
129 DIR *dp = opendir(dirname);
130 while ((fp = readdir(dp)) != NULL) {
134 filename = (char *) xcalloc(1, strlen(dirname) + strlen(fp->d_name) + 2);
135 strcpy(filename, dirname);
136 strcat(filename, fp->d_name);
137 lstat(filename, &statbuf);
139 if ((strcmp(fp->d_name, ".") != 0) && (strcmp(fp->d_name, "..") != 0)) {
140 if (S_ISDIR(statbuf.st_mode)) {
141 remove_dir(strcat(filename, "/"));
143 else if (remove(filename) == -1) {
144 perror_msg(filename);
155 static char **depends_split(const char *dependsstr)
157 static char *dependsvec[DEPENDSMAX];
162 if (dependsstr == 0) {
166 p = xstrdup(dependsstr);
167 while (*p != 0 && *p != '\n') {
173 if (dependsvec[i] == 0) {
178 *p = 0; /* eat the space... */
189 /* Topological sort algorithm:
190 * ordered is the output list, pkgs is the dependency graph, pkg is
193 * recursively add all the adjacent nodes to the ordered list, marking
194 * each one as visited along the way
196 * yes, this algorithm looks a bit odd when all the params have the
199 static void depends_sort_visit(package_t **ordered, package_t *pkgs,
204 /* mark node as processing */
205 pkg->color = color_grey;
207 /* visit each not-yet-visited node */
208 for (i = 0; i < pkg->requiredcount; i++)
209 if (pkg->requiredfor[i]->color == color_white)
210 depends_sort_visit(ordered, pkgs, pkg->requiredfor[i]);
213 /* add it to the list */
214 newnode = (struct package_t *)xmalloc(sizeof(struct package_t));
215 /* make a shallow copy */
217 newnode->next = *ordered;
221 pkg->next = *ordered;
224 /* mark node as done */
225 pkg->color = color_black;
228 static package_t *depends_sort(package_t *pkgs)
230 /* TODO: it needs to break cycles in the to-be-installed package
232 package_t *ordered = NULL;
235 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
236 pkg->color = color_white;
238 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
239 if (pkg->color == color_white) {
240 depends_sort_visit(&ordered, pkgs, pkg);
244 /* Leaks the old list... return the new one... */
249 /* resolve package dependencies --
250 * for each package in the list of packages to be installed, we parse its
251 * dependency info to determine if the dependent packages are either
252 * already installed, or are scheduled to be installed. If both tests fail
255 * The algorithm here is O(n^2*m) where n = number of packages to be
256 * installed and m is the # of dependencies per package. Not a terribly
257 * efficient algorithm, but given that at any one time you are unlikely
258 * to install a very large number of packages it doesn't really matter
260 static package_t *depends_resolve(package_t *pkgs, void *status)
262 package_t *pkg, *chk;
268 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
269 dependsvec = depends_split(pkg->depends);
271 while (dependsvec[i] != 0) {
272 /* Check for dependencies; first look for installed packages */
273 dependpkg.package = dependsvec[i];
274 if ((found = tfind(&dependpkg, &status, package_compare)) == 0 ||
275 ((chk = *(package_t **)found) &&
276 (chk->status & (status_flagok | status_statusinstalled)) !=
277 (status_flagok | status_statusinstalled))) {
279 /* if it fails, we look through the list of packages we are going to
281 for (chk = pkgs; chk != 0; chk = chk->next) {
282 if (strcmp(chk->package, dependsvec[i]) == 0 || (chk->provides &&
283 strncmp(chk->provides, dependsvec[i], strlen(dependsvec[i])) == 0)) {
284 if (chk->requiredcount >= DEPENDSMAX) {
285 fprintf(stderr, "Too many dependencies for %s\n", chk->package);
289 chk->requiredfor[chk->requiredcount++] = pkg;
295 fprintf(stderr, "%s depends on %s, but it is not going to be installed\n", pkg->package, dependsvec[i]);
303 return depends_sort(pkgs);
307 /* Status file handling routines
309 * This is a fairly minimalistic implementation. there are two main functions
310 * that are supported:
312 * 1) reading the entire status file:
313 * the status file is read into memory as a binary-tree, with just the
314 * package and status info preserved
316 * 2) merging the status file
317 * control info from (new) packages is merged into the status file,
318 * replacing any pre-existing entries. when a merge happens, status info
319 * read using the status_read function is written back to the status file
321 static unsigned long status_parse(const char *line)
327 for (i = 0; i < 3; i++) {
328 if ((p = strchr(line, ' ')) != NULL) {
332 while (statuswords[i][j] != 0) {
333 if (strcmp(line, statuswords[i][j]) == 0) {
334 l |= (1 << ((int)statuswords[i][0] + j - 1));
340 if (statuswords[i][j] == 0) {
349 static const char *status_print(unsigned long flags)
351 /* this function returns a static buffer... */
352 static char buf[256];
356 for (i = 0; i < 3; i++) {
358 while (statuswords[i][j] != 0) {
359 if ((flags & (1 << ((int)statuswords[i][0] + j - 1))) != 0) {
360 strcat(buf, statuswords[i][j]);
361 if (i < 2) strcat(buf, " ");
366 if (statuswords[i][j] == 0) {
367 fprintf(stderr, "corrupted status flag!!\n");
376 * Read a control file (or a stanza of a status file) and parse it,
377 * filling parsed fields into the package structure
379 static int control_read(FILE *file, package_t *p)
383 while ((line = get_line_from_file(file)) != NULL) {
384 line[strlen(line) - 1] = '\0';
386 if (strlen(line) == 0) {
389 if (strstr(line, "Package: ") == line) {
390 p->package = xstrdup(line + 9);
392 if (strstr(line, "Status: ") == line) {
393 p->status = status_parse(line + 8);
395 if (strstr(line, "Depends: ") == line) {
396 p->depends = xstrdup(line + 9);
398 if (strstr(line, "Provides: ") == line) {
399 p->provides = xstrdup(line + 10);
401 if (strstr(line, "Description: ") == line) {
402 p->description = xstrdup(line + 13);
403 /* This is specific to the Debian Installer. Ifdef? */
405 if (strstr(line, "installer-menu-item: ") == line) {
406 p->installer_menu_item = atoi(line + 21);
408 /* TODO: localized descriptions */
414 static void *status_read(void)
418 package_t *m = 0, *p = 0, *t = 0;
420 if ((f = fopen(statusfile, "r")) == NULL) {
421 perror_msg(statusfile);
425 if (getenv(udpkg_quiet) == NULL) {
426 printf("(Reading database...)\n");
430 m = (package_t *)xcalloc(1, sizeof(package_t));
434 * If there is an item in the tree by this name,
435 * it must be a virtual package; insert real
436 * package in preference.
438 tdelete(m, &status, package_compare);
439 tsearch(m, &status, package_compare);
442 * A "Provides" triggers the insertion
443 * of a pseudo package into the status
446 p = (package_t *)xcalloc(1, sizeof(package_t));
447 p->package = xstrdup(m->provides);
449 t = *(package_t **)tsearch(p, &status, package_compare);
456 * Pseudo package status is the
457 * same as the status of the
458 * package providing it
459 * FIXME: (not quite right, if 2
460 * packages of different statuses
463 t->status = m->status;
475 static int status_merge(void *status, package_t *pkgs)
479 package_t *pkg = 0, *statpkg = 0;
482 if ((fout = wfopen(new_statusfile, "w")) == NULL) {
485 if (getenv(udpkg_quiet) == NULL) {
486 printf("(Updating database...)\n");
489 * Dont use wfopen here, handle errors ourself
491 if ((fin = fopen(statusfile, "r")) != NULL) {
492 while (((line = get_line_from_file(fin)) != NULL) && !feof(fin)) {
493 line[strlen(line) - 1] = '\0'; /* trim newline */
494 /* If we see a package header, find out if it's a package
495 * that we have processed. if so, we skip that block for
496 * now (write it at the end).
498 * we also look at packages in the status cache and update
499 * their status fields
501 if (strstr(line, "Package: ") == line) {
502 for (pkg = pkgs; pkg != 0 && strcmp(line + 9,
503 pkg->package) != 0; pkg = pkg->next) ;
505 locpkg.package = line + 9;
506 statpkg = tfind(&locpkg, &status, package_compare);
508 /* note: statpkg should be non-zero, unless the status
509 * file was changed while we are processing (no locking
510 * is currently done...
513 statpkg = *(package_t **)statpkg;
519 if (strstr(line, "Status: ") == line && statpkg != 0) {
520 snprintf(line, sizeof(line), "Status: %s",
521 status_print(statpkg->status));
530 // Print out packages we processed.
531 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
532 fprintf(fout, "Package: %s\nStatus: %s\n",
533 pkg->package, status_print(pkg->status));
535 fprintf(fout, "Depends: %s\n", pkg->depends);
537 fprintf(fout, "Provides: %s\n", pkg->provides);
538 if (pkg->installer_menu_item)
539 fprintf(fout, "installer-menu-item: %i\n", pkg->installer_menu_item);
540 if (pkg->description)
541 fprintf(fout, "Description: %s\n", pkg->description);
547 * Its ok if renaming statusfile fails becasue it doesnt exist
549 if (rename(statusfile, bak_statusfile) == -1) {
550 struct stat stat_buf;
551 error_msg("Couldnt create backup status file");
552 if (stat(statusfile, &stat_buf) == 0) {
553 return(EXIT_FAILURE);
557 if (rename(new_statusfile, statusfile) == -1) {
558 error_msg("Couldnt create status file");
559 return(EXIT_FAILURE);
561 return(EXIT_SUCCESS);
564 static int is_file(const char *fn)
568 if (stat(fn, &statbuf) < 0) {
571 return S_ISREG(statbuf.st_mode);
574 static int dpkg_doconfigure(package_t *pkg)
580 DPRINTF("Configuring %s\n", pkg->package);
581 pkg->status &= status_statusmask;
582 snprintf(postinst, sizeof(postinst), "%s%s.postinst", infodir, pkg->package);
584 if (is_file(postinst)) {
585 snprintf(buf, sizeof(buf), "%s configure", postinst);
586 if ((r = do_system(buf)) != 0) {
587 fprintf(stderr, "postinst exited with status %d\n", r);
588 pkg->status |= status_statushalfconfigured;
592 pkg->status |= status_statusinstalled;
597 static int dpkg_dounpack(package_t *pkg)
601 char *src_file = NULL;
602 char *dst_file = NULL;
603 // char *lst_file = NULL;
604 char *adminscripts[] = { "prerm", "postrm", "preinst", "postinst",
605 "conffiles", "md5sums", "shlibs", "templates" };
607 DPRINTF("Unpacking %s\n", pkg->package);
611 deb_extract(dpkg_deb_extract, "/", pkg->file);
613 /* Installs the package scripts into the info directory */
614 for (i = 0; i < sizeof(adminscripts) / sizeof(adminscripts[0]); i++) {
615 struct stat src_stat_buf;
616 int src_fd = 0, dst_fd = 0;
618 /* The full path of the current location of the admin file */
619 src_file = xrealloc(src_file, strlen(dpkgcidir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
620 sprintf(src_file, "%s%s/%s", dpkgcidir, pkg->package, adminscripts[i]);
622 /* the full path of where we want the file to be copied to */
623 dst_file = xrealloc(dst_file, strlen(infodir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
624 sprintf(dst_file, "%s%s.%s", infodir, pkg->package, adminscripts[i]);
627 * copy admin file to permanent home
628 * NOTE: Maybe merge this behaviour into libb/copy_file.c
630 if (lstat(src_file, &src_stat_buf) == 0) {
631 if ((src_fd = open(src_file, O_RDONLY)) != -1) {
632 if ((dst_fd = open(dst_file, O_WRONLY | O_CREAT, 0644)) == -1) {
633 perror_msg("Opening %s", dst_file);
635 copy_file_chunk(src_fd, dst_fd, src_stat_buf.st_size);
639 error_msg("couldnt open [%s]\n", src_file);
645 * create the list file
646 * FIXME: currently this dumps the lst to stdout instead of a file
648 /* lst_file = (char *) xmalloc(strlen(infodir) + strlen(pkg->package) + 6);
649 strcpy(lst_file, infodir);
650 strcat(lst_file, pkg->package);
651 strcat(lst_file, ".list");
652 deb_extract(dpkg_deb_list, NULL, pkg->file);
655 pkg->status &= status_wantmask;
656 pkg->status |= status_wantinstall;
657 pkg->status &= status_flagmask;
658 pkg->status |= status_flagok;
659 pkg->status &= status_statusmask;
662 pkg->status |= status_statusunpacked;
664 pkg->status |= status_statushalfinstalled;
671 * Extract and parse the control.tar.gz from the specified package
673 static int dpkg_unpackcontrol(package_t *pkg)
679 /* clean the temp directory (dpkgcidir) be recreating it */
680 remove_dir(dpkgcidir);
681 if (create_path(dpkgcidir, S_IRWXU) == FALSE) {
686 * Get the package name from the file name,
687 * first remove the directories
689 if ((tmp_name = strrchr(pkg->file, '/')) == NULL) {
690 tmp_name = pkg->file;
694 /* now remove trailing version numbers etc */
695 length = strcspn(tmp_name, "_.");
696 pkg->package = (char *) xcalloc(1, length + 1);
697 /* store the package name */
698 strncpy(pkg->package, tmp_name, length);
700 /* work out the full extraction path */
701 tmp_name = (char *) xcalloc(1, strlen(dpkgcidir) + strlen(pkg->package) + 9);
702 strcpy(tmp_name, dpkgcidir);
703 strcat(tmp_name, pkg->package);
705 /* extract control.tar.gz to the full extraction path */
706 deb_extract(dpkg_deb_control, tmp_name, pkg->file);
708 /* parse the extracted control file */
709 strcat(tmp_name, "/control");
710 if ((file = wfopen(tmp_name, "r")) == NULL) {
713 if (control_read(file, pkg) == EXIT_FAILURE) {
720 static int dpkg_unpack(package_t *pkgs, void *status)
725 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
726 if (dpkg_unpackcontrol(pkg) == EXIT_FAILURE) {
729 if ((r = dpkg_dounpack(pkg)) != 0 ) {
733 status_merge(status, pkgs);
734 remove_dir(dpkgcidir);
739 static int dpkg_configure(package_t *pkgs, void *status)
745 for (pkg = pkgs; pkg != 0 && r == 0; pkg = pkg->next) {
746 found = tfind(pkg, &status, package_compare);
749 fprintf(stderr, "Trying to configure %s, but it is not installed\n", pkg->package);
752 /* configure the package listed in the status file;
753 * not pkg, as we have info only for the latter
756 r = dpkg_doconfigure(*(package_t **)found);
759 status_merge(status, 0);
764 static int dpkg_install(package_t *pkgs, void *status)
766 package_t *p, *ordered = 0;
768 /* Stage 1: parse all the control information */
769 for (p = pkgs; p != 0; p = p->next) {
770 if (dpkg_unpackcontrol(p) == EXIT_FAILURE) {
771 return(EXIT_FAILURE);
775 /* Stage 2: resolve dependencies */
777 ordered = depends_resolve(pkgs, status);
782 /* Stage 3: install */
783 for (p = ordered; p != 0; p = p->next) {
784 p->status &= status_wantmask;
785 p->status |= status_wantinstall;
787 /* for now the flag is always set to ok... this is probably
790 p->status &= status_flagmask;
791 p->status |= status_flagok;
793 DPRINTF("Installing %s\n", p->package);
794 if (dpkg_dounpack(p) != 0) {
797 if (dpkg_doconfigure(p) != 0) {
803 status_merge(status, pkgs);
805 remove_dir(dpkgcidir);
811 * Not implemented yet
813 static int dpkg_remove(package_t *pkgs, void *status)
817 for (p = pkgs; p != 0; p = p->next)
820 status_merge(status, 0);
826 extern int dpkg_main(int argc, char **argv)
828 const int arg_install = 1;
829 const int arg_unpack = 2;
830 const int arg_configure = 4;
832 package_t *p, *packages = NULL;
837 while ((opt = getopt(argc, argv, "iruc")) != -1) {
840 optflag |= arg_install;
843 optflag |= arg_unpack;
846 optflag |= arg_configure;
853 while (optind < argc) {
854 p = (package_t *) xcalloc(1, sizeof(package_t));
855 if (optflag & arg_configure) {
856 p->package = xstrdup(argv[optind]);
858 p->file = xstrdup(argv[optind]);
866 create_path(dpkgcidir, S_IRWXU);
867 create_path(infodir, S_IRWXU);
869 status = status_read();
871 if (optflag & arg_install) {
872 return dpkg_install(packages, status);
874 else if (optflag & arg_unpack) {
875 return dpkg_unpack(packages, status);
877 else if (optflag & arg_configure) {
878 return dpkg_configure(packages, status);
880 return(EXIT_FAILURE);