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;
483 if ((fout = wfopen(new_statusfile, "w")) == NULL) {
486 if (getenv(udpkg_quiet) == NULL) {
487 printf("(Updating database...)\n");
489 if ((fin = wfopen(statusfile, "r")) != NULL) {
490 while (((line = get_line_from_file(fin)) != NULL) && !feof(fin)) {
491 line[strlen(line) - 1] = '\0'; /* trim newline */
492 /* If we see a package header, find out if it's a package
493 * that we have processed. if so, we skip that block for
494 * now (write it at the end).
496 * we also look at packages in the status cache and update
497 * their status fields
499 if (strstr(line, "Package: ") == line) {
500 for (pkg = pkgs; pkg != 0 && strcmp(line + 9,
501 pkg->package) != 0; pkg = pkg->next) ;
503 locpkg.package = line + 9;
504 statpkg = tfind(&locpkg, &status, package_compare);
506 /* note: statpkg should be non-zero, unless the status
507 * file was changed while we are processing (no locking
508 * is currently done...
511 statpkg = *(package_t **)statpkg;
517 if (strstr(line, "Status: ") == line && statpkg != 0) {
518 snprintf(line, sizeof(line), "Status: %s",
519 status_print(statpkg->status));
527 // Print out packages we processed.
528 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
529 fprintf(fout, "Package: %s\nStatus: %s\n",
530 pkg->package, status_print(pkg->status));
532 fprintf(fout, "Depends: %s\n", pkg->depends);
534 fprintf(fout, "Provides: %s\n", pkg->provides);
535 if (pkg->installer_menu_item)
536 fprintf(fout, "installer-menu-item: %i\n", pkg->installer_menu_item);
537 if (pkg->description)
538 fprintf(fout, "Description: %s\n", pkg->description);
545 r = rename(statusfile, bak_statusfile);
547 r = rename(new_statusfile, statusfile);
553 static int is_file(const char *fn)
557 if (stat(fn, &statbuf) < 0) {
560 return S_ISREG(statbuf.st_mode);
563 static int dpkg_doconfigure(package_t *pkg)
569 DPRINTF("Configuring %s\n", pkg->package);
570 pkg->status &= status_statusmask;
571 snprintf(postinst, sizeof(postinst), "%s%s.postinst", infodir, pkg->package);
573 if (is_file(postinst)) {
574 snprintf(buf, sizeof(buf), "%s configure", postinst);
575 if ((r = do_system(buf)) != 0) {
576 fprintf(stderr, "postinst exited with status %d\n", r);
577 pkg->status |= status_statushalfconfigured;
581 pkg->status |= status_statusinstalled;
586 static int dpkg_dounpack(package_t *pkg)
590 char *src_file = NULL;
591 char *dst_file = NULL;
592 // char *lst_file = NULL;
593 char *adminscripts[] = { "prerm", "postrm", "preinst", "postinst",
594 "conffiles", "md5sums", "shlibs", "templates" };
596 DPRINTF("Unpacking %s\n", pkg->package);
600 deb_extract(dpkg_deb_extract, "/", pkg->file);
602 /* Installs the package scripts into the info directory */
603 for (i = 0; i < sizeof(adminscripts) / sizeof(adminscripts[0]); i++) {
604 struct stat src_stat_buf;
605 int src_fd = 0, dst_fd = 0;
607 /* The full path of the current location of the admin file */
608 src_file = xrealloc(src_file, strlen(dpkgcidir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
609 sprintf(src_file, "%s%s/%s", dpkgcidir, pkg->package, adminscripts[i]);
611 /* the full path of where we want the file to be copied to */
612 dst_file = xrealloc(dst_file, strlen(infodir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
613 sprintf(dst_file, "%s%s.%s", infodir, pkg->package, adminscripts[i]);
616 * copy admin file to permanent home
617 * NOTE: Maybe merge this behaviour into libb/copy_file.c
619 if (lstat(src_file, &src_stat_buf) == 0) {
620 if ((src_fd = open(src_file, O_RDONLY)) != -1) {
621 if ((dst_fd = open(dst_file, O_WRONLY | O_CREAT, 0644)) == -1) {
622 perror_msg("Opening %s", dst_file);
624 copy_file_chunk(src_fd, dst_fd, src_stat_buf.st_size);
628 error_msg("couldnt open [%s]\n", src_file);
634 * create the list file
635 * FIXME: currently this dumps the lst to stdout instead of a file
637 /* lst_file = (char *) xmalloc(strlen(infodir) + strlen(pkg->package) + 6);
638 strcpy(lst_file, infodir);
639 strcat(lst_file, pkg->package);
640 strcat(lst_file, ".list");
641 deb_extract(dpkg_deb_list, NULL, pkg->file);
644 pkg->status &= status_wantmask;
645 pkg->status |= status_wantinstall;
646 pkg->status &= status_flagmask;
647 pkg->status |= status_flagok;
648 pkg->status &= status_statusmask;
651 pkg->status |= status_statusunpacked;
653 pkg->status |= status_statushalfinstalled;
660 * Extract and parse the control.tar.gz from the specified package
662 static int dpkg_unpackcontrol(package_t *pkg)
668 /* clean the temp directory (dpkgcidir) be recreating it */
669 remove_dir(dpkgcidir);
670 if (mkdir(dpkgcidir, S_IRWXU) != 0) {
676 * Get the package name from the file name,
677 * first remove the directories
679 if ((tmp_name = strrchr(pkg->file, '/')) == NULL) {
680 tmp_name = pkg->file;
684 /* now remove trailing version numbers etc */
685 length = strcspn(tmp_name, "_.");
686 pkg->package = (char *) xcalloc(1, length + 1);
687 /* store the package name */
688 strncpy(pkg->package, tmp_name, length);
690 /* work out the full extraction path */
691 tmp_name = (char *) xcalloc(1, strlen(dpkgcidir) + strlen(pkg->package) + 9);
692 strcpy(tmp_name, dpkgcidir);
693 strcat(tmp_name, pkg->package);
695 /* extract control.tar.gz to the full extraction path */
696 deb_extract(dpkg_deb_control, tmp_name, pkg->file);
698 /* parse the extracted control file */
699 strcat(tmp_name, "/control");
700 if ((file = wfopen(tmp_name, "r")) == NULL) {
703 if (control_read(file, pkg) == EXIT_FAILURE) {
710 static int dpkg_unpack(package_t *pkgs, void *status)
715 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
716 if (dpkg_unpackcontrol(pkg) == EXIT_FAILURE) {
719 if ((r = dpkg_dounpack(pkg)) != 0 ) {
723 status_merge(status, pkgs);
724 remove_dir(dpkgcidir);
729 static int dpkg_configure(package_t *pkgs, void *status)
735 for (pkg = pkgs; pkg != 0 && r == 0; pkg = pkg->next) {
736 found = tfind(pkg, &status, package_compare);
739 fprintf(stderr, "Trying to configure %s, but it is not installed\n", pkg->package);
742 /* configure the package listed in the status file;
743 * not pkg, as we have info only for the latter
746 r = dpkg_doconfigure(*(package_t **)found);
749 status_merge(status, 0);
754 static int dpkg_install(package_t *pkgs, void *status)
756 package_t *p, *ordered = 0;
758 /* Stage 1: parse all the control information */
759 for (p = pkgs; p != 0; p = p->next) {
760 if (dpkg_unpackcontrol(p) == EXIT_FAILURE) {
761 return(EXIT_FAILURE);
765 /* Stage 2: resolve dependencies */
767 ordered = depends_resolve(pkgs, status);
772 /* Stage 3: install */
773 for (p = ordered; p != 0; p = p->next) {
774 p->status &= status_wantmask;
775 p->status |= status_wantinstall;
777 /* for now the flag is always set to ok... this is probably
780 p->status &= status_flagmask;
781 p->status |= status_flagok;
783 DPRINTF("Installing %s\n", p->package);
784 if (dpkg_dounpack(p) != 0) {
787 if (dpkg_doconfigure(p) != 0) {
793 status_merge(status, pkgs);
795 remove_dir(dpkgcidir);
801 * Not implemented yet
803 static int dpkg_remove(package_t *pkgs, void *status)
807 for (p = pkgs; p != 0; p = p->next)
810 status_merge(status, 0);
816 extern int dpkg_main(int argc, char **argv)
818 const int arg_install = 1;
819 const int arg_unpack = 2;
820 const int arg_configure = 4;
822 package_t *p, *packages = NULL;
827 while ((opt = getopt(argc, argv, "iruc")) != -1) {
830 optflag |= arg_install;
833 optflag |= arg_unpack;
836 optflag |= arg_configure;
843 while (optind < argc) {
844 p = (package_t *) xcalloc(1, sizeof(package_t));
845 if (optflag & arg_configure) {
846 p->package = xstrdup(argv[optind]);
848 p->file = xstrdup(argv[optind]);
856 create_path(dpkgcidir, 0755);
857 create_path(infodir, 0755);
859 status = status_read();
861 if (optflag & arg_install) {
862 return dpkg_install(packages, status);
864 else if (optflag & arg_unpack) {
865 return dpkg_unpack(packages, status);
867 else if (optflag & arg_configure) {
868 return dpkg_configure(packages, status);
870 return(EXIT_FAILURE);