14 #define DEPENDSMAX 64 /* maximum number of depends we can handle */
16 /* Should we do full dependency checking? */
19 /* Should we do debugging? */
23 #define SYSTEM(x) do_system(x)
24 #define DPRINTF(fmt,args...) fprintf(stderr, fmt, ##args)
26 #define SYSTEM(x) system(x)
27 #define DPRINTF(fmt,args...) /* nothing */
31 extern int deb_extract(int optflags, const char *dir_name, const char *deb_filename);
32 static const int dpkg_deb_contents = 1;
33 static const int dpkg_deb_control = 2;
34 // const int dpkg_deb_info = 4;
35 static const int dpkg_deb_extract = 8;
36 static const int dpkg_deb_verbose_extract = 16;
37 static const int dpkg_deb_list = 32;
39 static const char statusfile[] = "/var/lib/dpkg/status.udeb";
40 static const char new_statusfile[] = "/var/lib/dpkg/status.udeb.new";
41 static const char bak_statusfile[] = "/var/lib/dpkg/status.udeb.bak";
43 static const char dpkgcidir[] = "/var/lib/dpkg/tmp.ci/";
45 static const char infodir[] = "/var/lib/dpkg/info/";
46 static const char udpkg_quiet[] = "UDPKG_QUIET";
48 //static const int status_wantstart = 0;
49 //static const int status_wantunknown = (1 << 0);
50 static const int status_wantinstall = (1 << 1);
51 //static const int status_wanthold = (1 << 2);
52 //static const int status_wantdeinstall = (1 << 3);
53 //static const int status_wantpurge = (1 << 4);
54 static const int status_wantmask = 31;
56 //static const int status_flagstart = 5;
57 static const int status_flagok = (1 << 5); /* 32 */
58 //static const int status_flagreinstreq = (1 << 6);
59 //static const int status_flaghold = (1 << 7);
60 //static const int status_flagholdreinstreq = (1 << 8);
61 static const int status_flagmask = 480;
63 //static const int status_statusstart = 9;
64 //static const int status_statusnoninstalled = (1 << 9); /* 512 */
65 static const int status_statusunpacked = (1 << 10);
66 static const int status_statushalfconfigured = (1 << 11);
67 static const int status_statusinstalled = (1 << 12);
68 static const int status_statushalfinstalled = (1 << 13);
69 //static const int status_statusconfigfiles = (1 << 14);
70 //static const int status_statuspostinstfailed = (1 << 15);
71 //static const int status_statusremovalfailed = (1 << 16);
72 static const int status_statusmask = 130560; /* i assume status_statusinstalled is supposed to be included */
74 static const char *statuswords[][10] = {
75 { (char *) 0, "unknown", "install", "hold", "deinstall", "purge", 0 },
76 { (char *) 5, "ok", "reinstreq", "hold", "hold-reinstreq", 0 },
77 { (char *) 9, "not-installed", "unpacked", "half-configured",
78 "installed", "half-installed", "config-files",
79 "post-inst-failed", "removal-failed", 0 }
82 const int color_white = 0;
83 const int color_grey = 1;
84 const int color_black = 2;
87 typedef struct package_s {
94 int installer_menu_item;
96 char color; /* for topo-sort */
97 struct package_s *requiredfor[DEPENDSMAX];
98 unsigned short requiredcount;
99 struct package_s *next;
103 static int do_system(const char *cmd)
105 DPRINTF("cmd is %s\n", cmd);
109 #define do_system(cmd) system(cmd)
112 static int package_compare(const void *p1, const void *p2)
114 return strcmp(((package_t *)p1)->package,
115 ((package_t *)p2)->package);
118 static int remove_dpkgcidir()
120 char *rm_dpkgcidir = NULL;
122 rm_dpkgcidir = (char *) xmalloc(strlen(dpkgcidir) + 8);
123 strcpy(rm_dpkgcidir, "rm -rf ");
124 strcat(rm_dpkgcidir, dpkgcidir);
126 if (SYSTEM(rm_dpkgcidir) != 0) {
136 static char **depends_split(const char *dependsstr)
138 static char *dependsvec[DEPENDSMAX];
143 if (dependsstr == 0) {
147 p = strdup(dependsstr);
148 while (*p != 0 && *p != '\n') {
154 if (dependsvec[i] == 0) {
159 *p = 0; /* eat the space... */
170 /* Topological sort algorithm:
171 * ordered is the output list, pkgs is the dependency graph, pkg is
174 * recursively add all the adjacent nodes to the ordered list, marking
175 * each one as visited along the way
177 * yes, this algorithm looks a bit odd when all the params have the
180 static void depends_sort_visit(package_t **ordered, package_t *pkgs,
185 /* mark node as processing */
186 pkg->color = color_grey;
188 /* visit each not-yet-visited node */
189 for (i = 0; i < pkg->requiredcount; i++)
190 if (pkg->requiredfor[i]->color == color_white)
191 depends_sort_visit(ordered, pkgs, pkg->requiredfor[i]);
194 /* add it to the list */
195 newnode = (struct package_t *)xmalloc(sizeof(struct package_t));
196 /* make a shallow copy */
198 newnode->next = *ordered;
202 pkg->next = *ordered;
205 /* mark node as done */
206 pkg->color = color_black;
209 static package_t *depends_sort(package_t *pkgs)
211 /* TODO: it needs to break cycles in the to-be-installed package
213 package_t *ordered = NULL;
216 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
217 pkg->color = color_white;
219 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
220 if (pkg->color == color_white) {
221 depends_sort_visit(&ordered, pkgs, pkg);
225 /* Leaks the old list... return the new one... */
230 /* resolve package dependencies --
231 * for each package in the list of packages to be installed, we parse its
232 * dependency info to determine if the dependent packages are either
233 * already installed, or are scheduled to be installed. If both tests fail
236 * The algorithm here is O(n^2*m) where n = number of packages to be
237 * installed and m is the # of dependencies per package. Not a terribly
238 * efficient algorithm, but given that at any one time you are unlikely
239 * to install a very large number of packages it doesn't really matter
241 static package_t *depends_resolve(package_t *pkgs, void *status)
243 package_t *pkg, *chk;
249 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
250 dependsvec = depends_split(pkg->depends);
252 while (dependsvec[i] != 0) {
253 /* Check for dependencies; first look for installed packages */
254 dependpkg.package = dependsvec[i];
255 if ((found = tfind(&dependpkg, &status, package_compare)) == 0 ||
256 ((chk = *(package_t **)found) &&
257 (chk->status & (status_flagok | status_statusinstalled)) !=
258 (status_flagok | status_statusinstalled))) {
260 /* if it fails, we look through the list of packages we are going to
262 for (chk = pkgs; chk != 0; chk = chk->next) {
263 if (strcmp(chk->package, dependsvec[i]) == 0 || (chk->provides &&
264 strncmp(chk->provides, dependsvec[i], strlen(dependsvec[i])) == 0)) {
265 if (chk->requiredcount >= DEPENDSMAX) {
266 fprintf(stderr, "Too many dependencies for %s\n", chk->package);
270 chk->requiredfor[chk->requiredcount++] = pkg;
276 fprintf(stderr, "%s depends on %s, but it is not going to be installed\n", pkg->package, dependsvec[i]);
284 return depends_sort(pkgs);
288 /* Status file handling routines
290 * This is a fairly minimalistic implementation. there are two main functions
291 * that are supported:
293 * 1) reading the entire status file:
294 * the status file is read into memory as a binary-tree, with just the
295 * package and status info preserved
297 * 2) merging the status file
298 * control info from (new) packages is merged into the status file,
299 * replacing any pre-existing entries. when a merge happens, status info
300 * read using the status_read function is written back to the status file
302 static unsigned long status_parse(const char *line)
308 for (i = 0; i < 3; i++) {
309 if ((p = strchr(line, ' ')) != NULL) {
313 while (statuswords[i][j] != 0) {
314 if (strcmp(line, statuswords[i][j]) == 0) {
315 l |= (1 << ((int)statuswords[i][0] + j - 1));
321 if (statuswords[i][j] == 0) {
330 static const char *status_print(unsigned long flags)
332 /* this function returns a static buffer... */
333 static char buf[256];
337 for (i = 0; i < 3; i++) {
339 while (statuswords[i][j] != 0) {
340 if ((flags & (1 << ((int)statuswords[i][0] + j - 1))) != 0) {
341 strcat(buf, statuswords[i][j]);
342 if (i < 2) strcat(buf, " ");
347 if (statuswords[i][j] == 0) {
348 fprintf(stderr, "corrupted status flag!!\n");
357 * Read a control file (or a stanza of a status file) and parse it,
358 * filling parsed fields into the package structure
360 static int control_read(FILE *file, package_t *p)
364 while ((line = get_line_from_file(file)) != NULL) {
365 line[strlen(line)] = 0;
367 if (strlen(line) == 0) {
370 if (strstr(line, "Package: ") == line) {
371 p->package = strdup(line + 9);
373 if (strstr(line, "Status: ") == line) {
374 p->status = status_parse(line + 8);
376 if (strstr(line, "Depends: ") == line) {
377 p->depends = strdup(line + 9);
379 if (strstr(line, "Provides: ") == line) {
380 p->provides = strdup(line + 10);
382 if (strstr(line, "Description: ") == line) {
383 p->description = strdup(line + 13);
384 /* This is specific to the Debian Installer. Ifdef? */
386 if (strstr(line, "installer-menu-item: ") == line) {
387 p->installer_menu_item = atoi(line + 21);
389 /* TODO: localized descriptions */
395 static void *status_read(void)
399 package_t *m = 0, *p = 0, *t = 0;
401 if ((f = fopen(statusfile, "r")) == NULL) {
406 if (getenv(udpkg_quiet) == NULL) {
407 printf("(Reading database...)\n");
411 m = (package_t *)xmalloc(sizeof(package_t));
412 memset(m, 0, sizeof(package_t));
416 * If there is an item in the tree by this name,
417 * it must be a virtual package; insert real
418 * package in preference.
420 tdelete(m, &status, package_compare);
421 tsearch(m, &status, package_compare);
424 * A "Provides" triggers the insertion
425 * of a pseudo package into the status
428 p = (package_t *)xmalloc(sizeof(package_t));
429 memset(p, 0, sizeof(package_t));
430 p->package = strdup(m->provides);
432 t = *(package_t **)tsearch(p, &status, package_compare);
439 * Pseudo package status is the
440 * same as the status of the
441 * package providing it
442 * FIXME: (not quite right, if 2
443 * packages of different statuses
446 t->status = m->status;
458 static int status_merge(void *status, package_t *pkgs)
462 package_t *pkg = 0, *statpkg = 0;
466 if ((fin = fopen(statusfile, "r")) == NULL) {
470 if ((fout = fopen(new_statusfile, "w")) == NULL) {
471 perror(new_statusfile);
474 if (getenv(udpkg_quiet) == NULL) {
475 printf("(Updating database...)\n");
478 while (((line = get_line_from_file(fin)) != NULL) && !feof(fin)) {
479 line[strlen(line)] = 0; /* trim newline */
480 /* If we see a package header, find out if it's a package
481 * that we have processed. if so, we skip that block for
482 * now (write it at the end).
484 * we also look at packages in the status cache and update
485 * their status fields
487 if (strstr(line, "Package: ") == line) {
488 for (pkg = pkgs; pkg != 0 && strncmp(line + 9,
489 pkg->package, strlen(line) - 9) != 0;
492 locpkg.package = line + 9;
493 statpkg = tfind(&locpkg, &status, package_compare);
495 /* note: statpkg should be non-zero, unless the status
496 * file was changed while we are processing (no locking
497 * is currently done...
500 statpkg = *(package_t **)statpkg;
506 if (strstr(line, "Status: ") == line && statpkg != 0) {
507 snprintf(line, sizeof(line), "Status: %s",
508 status_print(statpkg->status));
515 // Print out packages we processed.
516 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
517 fprintf(fout, "Package: %s\nStatus: %s\n",
518 pkg->package, status_print(pkg->status));
520 fprintf(fout, "Depends: %s\n", pkg->depends);
522 fprintf(fout, "Provides: %s\n", pkg->provides);
523 if (pkg->installer_menu_item)
524 fprintf(fout, "installer-menu-item: %i\n", pkg->installer_menu_item);
525 if (pkg->description)
526 fprintf(fout, "Description: %s\n", pkg->description);
533 r = rename(statusfile, bak_statusfile);
535 r = rename(new_statusfile, statusfile);
541 static int is_file(const char *fn)
545 if (stat(fn, &statbuf) < 0) {
548 return S_ISREG(statbuf.st_mode);
551 static int dpkg_doconfigure(package_t *pkg)
557 DPRINTF("Configuring %s\n", pkg->package);
558 pkg->status &= status_statusmask;
559 snprintf(postinst, sizeof(postinst), "%s%s.postinst", infodir, pkg->package);
561 if (is_file(postinst)) {
562 snprintf(buf, sizeof(buf), "%s configure", postinst);
563 if ((r = do_system(buf)) != 0) {
564 fprintf(stderr, "postinst exited with status %d\n", r);
565 pkg->status |= status_statushalfconfigured;
569 pkg->status |= status_statusinstalled;
574 static int dpkg_dounpack(package_t *pkg)
579 char *src_file = NULL;
580 char *dst_file = NULL;
581 char *lst_file = NULL;
582 char *adminscripts[] = { "/prerm", "/postrm", "/preinst", "/postinst",
583 "/conffiles", "/md5sums", "/shlibs", "/templates" };
584 char buf[1024], buf2[1024];
586 DPRINTF("Unpacking %s\n", pkg->package);
590 deb_extract(dpkg_deb_extract, "/", pkg->file);
592 /* Installs the package scripts into the info directory */
593 for (i = 0; i < sizeof(adminscripts) / sizeof(adminscripts[0]); i++) {
594 /* The full path of the current location of the admin file */
595 src_file = xrealloc(src_file, strlen(dpkgcidir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
596 strcpy(src_file, dpkgcidir);
597 strcat(src_file, pkg->package);
598 strcat(src_file, adminscripts[i]);
600 /* the full path of where we want the file to be copied to */
601 dst_file = xrealloc(dst_file, strlen(infodir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
602 strcpy(dst_file, infodir);
603 strcat(dst_file, pkg->package);
604 strcat(dst_file, adminscripts[i]);
606 /* copy admin file to permanent home */
607 if (copy_file(src_file, dst_file, TRUE, FALSE, FALSE) < 0) {
608 error_msg_and_die("Cannot copy %s to %s ", buf, buf2);
611 /* create the list file */
612 lst_file = (char *) xmalloc(strlen(infodir) + strlen(pkg->package) + 6);
613 strcpy(lst_file, infodir);
614 strcat(lst_file, pkg->package);
615 strcat(lst_file, ".list");
616 outfp = freopen(lst_file, "w", stdout);
617 deb_extract(dpkg_deb_list, NULL, pkg->file);
618 stdout = freopen(NULL, "w", outfp);
626 pkg->status &= status_wantmask;
627 pkg->status |= status_wantinstall;
628 pkg->status &= status_flagmask;
629 pkg->status |= status_flagok;
630 pkg->status &= status_statusmask;
633 pkg->status |= status_statusunpacked;
635 pkg->status |= status_statushalfinstalled;
642 * Extract and parse the control.tar.gz from the specified package
644 static int dpkg_unpackcontrol(package_t *pkg)
650 /* clean the temp directory (dpkgcidir) be recreating it */
652 if (mkdir(dpkgcidir, S_IRWXU) != 0) {
658 * Get the package name from the file name,
659 * first remove the directories
661 if ((tmp_name = strrchr(pkg->file, '/')) == NULL) {
662 tmp_name = pkg->file;
666 /* now remove trailing version numbers etc */
667 length = strcspn(tmp_name, "_.");
668 pkg->package = (char *) xmalloc(length + 1);
669 /* store the package name */
670 strncpy(pkg->package, tmp_name, length);
672 /* work out the full extraction path */
673 tmp_name = (char *) xmalloc(strlen(dpkgcidir) + strlen(pkg->package) + 9);
674 memset(tmp_name, 0, strlen(dpkgcidir) + strlen(pkg->package) + 9);
675 strcpy(tmp_name, dpkgcidir);
676 strcat(tmp_name, pkg->package);
678 /* extract control.tar.gz to the full extraction path */
679 deb_extract(dpkg_deb_control, tmp_name, pkg->file);
681 /* parse the extracted control file */
682 strcat(tmp_name, "/control");
683 if ((file = fopen(tmp_name, "r")) == NULL) {
686 if (control_read(file, pkg) == EXIT_FAILURE) {
693 static int dpkg_unpack(package_t *pkgs, void *status)
698 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
699 if (dpkg_unpackcontrol(pkg) == EXIT_FAILURE) {
702 if ((r = dpkg_dounpack(pkg)) != 0 ) {
706 status_merge(status, pkgs);
712 static int dpkg_configure(package_t *pkgs, void *status)
718 for (pkg = pkgs; pkg != 0 && r == 0; pkg = pkg->next) {
719 found = tfind(pkg, &status, package_compare);
722 fprintf(stderr, "Trying to configure %s, but it is not installed\n", pkg->package);
725 /* configure the package listed in the status file;
726 * not pkg, as we have info only for the latter
729 r = dpkg_doconfigure(*(package_t **)found);
732 status_merge(status, 0);
737 static int dpkg_install(package_t *pkgs, void *status)
739 package_t *p, *ordered = 0;
741 /* Stage 1: parse all the control information */
742 for (p = pkgs; p != 0; p = p->next) {
743 if (dpkg_unpackcontrol(p) == EXIT_FAILURE) {
749 /* Stage 2: resolve dependencies */
751 ordered = depends_resolve(pkgs, status);
756 /* Stage 3: install */
757 for (p = ordered; p != 0; p = p->next) {
758 p->status &= status_wantmask;
759 p->status |= status_wantinstall;
761 /* for now the flag is always set to ok... this is probably
764 p->status &= status_flagmask;
765 p->status |= status_flagok;
767 DPRINTF("Installing %s\n", p->package);
768 if (dpkg_dounpack(p) != 0) {
771 if (dpkg_doconfigure(p) != 0) {
777 status_merge(status, pkgs);
784 static int dpkg_remove(package_t *pkgs, void *status)
788 for (p = pkgs; p != 0; p = p->next)
791 status_merge(status, 0);
796 extern int dpkg_main(int argc, char **argv)
800 package_t *p, *packages = NULL;
801 char *cwd = getcwd(0, 0);
806 /* Nasty little hack to "parse" long options. */
813 p = (package_t *)xmalloc(sizeof(package_t));
814 memset(p, 0, sizeof(package_t));
820 p->file = xmalloc(strlen(cwd) + strlen(*argv) + 2);
821 sprintf(p->file, "%s/%s", cwd, *argv);
823 p->package = strdup(*argv);
831 status = status_read();
835 return dpkg_install(packages, status);
837 return dpkg_remove(packages, status);
839 return dpkg_unpack(packages, status);
841 return dpkg_configure(packages, status);