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 */
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_want_unknown = 1;
52 static const int status_want_install = 2;
53 //static const int status_want_hold = 3;
54 //static const int status_want_deinstall = 4;
55 //static const int status_want_purge = 5;
57 static const int status_flag_ok = 1;
58 //static const int status_flag_reinstreq = 2;
59 //static const int status_flag_hold = 3;
60 //static const int status_flag_holdreinstreq = 4;
62 //static const int status_statusnoninstalled = 1;
63 static const int status_status_unpacked = 2;
64 static const int status_status_halfconfigured = 3;
65 static const int status_status_installed = 4;
66 static const int status_status_halfinstalled = 5;
67 //static const int status_statusconfigfiles = 6;
68 //static const int status_statuspostinstfailed = 7;
69 //static const int status_statusremovalfailed = 8;
71 static const char *status_words_want[] = { "unknown", "install", "hold", "deinstall", "purge", 0 };
72 static const char *status_words_flag[] = { "ok", "reinstreq", "hold", "hold-reinstreq", 0 };
73 static const char *status_words_status[] = { "not-installed", "unpacked", "half-configured", "installed",
74 "half-installed", "config-files", "post-inst-failed", "removal-failed", 0 };
76 static const int color_white = 0;
77 static const int color_grey = 1;
78 static const int color_black = 2;
81 typedef struct package_s {
88 int installer_menu_item;
89 unsigned char status_want;
90 unsigned char status_flag;
91 unsigned char status_status;
92 char color; /* for topo-sort */
93 struct package_s *requiredfor[DEPENDSMAX];
94 unsigned short requiredcount;
95 struct package_s *next;
99 static int do_system(const char *cmd)
101 DPRINTF("cmd is %s\n", cmd);
105 #define do_system(cmd) system(cmd)
108 static int package_compare(const void *p1, const void *p2)
110 return strcmp(((package_t *)p1)->package,
111 ((package_t *)p2)->package);
115 * NOTE: this was handled by a "rm -rf" shell command
116 * Maybe theis behaviour should be integrated into the rm applet
117 * (i dont appreciate the rm applets recursive action fn)-bug1
119 static int remove_dir(const char *dirname)
122 DIR *dp = opendir(dirname);
123 while ((fp = readdir(dp)) != NULL) {
127 filename = (char *) xcalloc(1, strlen(dirname) + strlen(fp->d_name) + 2);
128 strcpy(filename, dirname);
129 strcat(filename, fp->d_name);
130 lstat(filename, &statbuf);
132 if ((strcmp(fp->d_name, ".") != 0) && (strcmp(fp->d_name, "..") != 0)) {
133 if (S_ISDIR(statbuf.st_mode)) {
134 remove_dir(strcat(filename, "/"));
136 else if (remove(filename) == -1) {
137 perror_msg(filename);
148 static char **depends_split(const char *dependsstr)
150 static char *dependsvec[DEPENDSMAX];
155 if (dependsstr == 0) {
159 p = xstrdup(dependsstr);
160 while (*p != 0 && *p != '\n') {
166 if (dependsvec[i] == 0) {
171 *p = 0; /* eat the space... */
182 /* Topological sort algorithm:
183 * ordered is the output list, pkgs is the dependency graph, pkg is
186 * recursively add all the adjacent nodes to the ordered list, marking
187 * each one as visited along the way
189 * yes, this algorithm looks a bit odd when all the params have the
192 static void depends_sort_visit(package_t **ordered, package_t *pkgs,
197 /* mark node as processing */
198 pkg->color = color_grey;
200 /* visit each not-yet-visited node */
201 for (i = 0; i < pkg->requiredcount; i++)
202 if (pkg->requiredfor[i]->color == color_white)
203 depends_sort_visit(ordered, pkgs, pkg->requiredfor[i]);
206 /* add it to the list */
207 newnode = (struct package_t *)xmalloc(sizeof(struct package_t));
208 /* make a shallow copy */
210 newnode->next = *ordered;
214 pkg->next = *ordered;
217 /* mark node as done */
218 pkg->color = color_black;
221 static package_t *depends_sort(package_t *pkgs)
223 /* TODO: it needs to break cycles in the to-be-installed package
225 package_t *ordered = NULL;
228 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
229 pkg->color = color_white;
231 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
232 if (pkg->color == color_white) {
233 depends_sort_visit(&ordered, pkgs, pkg);
237 /* Leaks the old list... return the new one... */
242 /* resolve package dependencies --
243 * for each package in the list of packages to be installed, we parse its
244 * dependency info to determine if the dependent packages are either
245 * already installed, or are scheduled to be installed. If both tests fail
248 * The algorithm here is O(n^2*m) where n = number of packages to be
249 * installed and m is the # of dependencies per package. Not a terribly
250 * efficient algorithm, but given that at any one time you are unlikely
251 * to install a very large number of packages it doesn't really matter
253 static package_t *depends_resolve(package_t *pkgs, void *status)
255 package_t *pkg, *chk;
261 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
262 dependsvec = depends_split(pkg->depends);
264 while (dependsvec[i] != 0) {
265 /* Check for dependencies; first look for installed packages */
266 dependpkg.package = dependsvec[i];
267 if (((found = tfind(&dependpkg, &status, package_compare)) == 0) ||
268 ((chk = *(package_t **)found) && (chk->status_flag & status_flag_ok) &&
269 (chk->status_status & status_status_installed))) {
271 /* if it fails, we look through the list of packages we are going to
273 for (chk = pkgs; chk != 0; chk = chk->next) {
274 if (strcmp(chk->package, dependsvec[i]) == 0 || (chk->provides &&
275 strncmp(chk->provides, dependsvec[i], strlen(dependsvec[i])) == 0)) {
276 if (chk->requiredcount >= DEPENDSMAX) {
277 error_msg("Too many dependencies for %s", chk->package);
281 chk->requiredfor[chk->requiredcount++] = pkg;
287 error_msg("%s depends on %s, but it is not going to be installed", pkg->package, dependsvec[i]);
295 return depends_sort(pkgs);
299 /* Status file handling routines
301 * This is a fairly minimalistic implementation. there are two main functions
302 * that are supported:
304 * 1) reading the entire status file:
305 * the status file is read into memory as a binary-tree, with just the
306 * package and status info preserved
308 * 2) merging the status file
309 * control info from (new) packages is merged into the status file,
310 * replacing any pre-existing entries. when a merge happens, status info
311 * read using the status_read function is written back to the status file
313 static unsigned char status_parse(const char *line, const char **status_words)
315 unsigned char status_num;
318 while (status_words[i] != 0) {
319 if (strncmp(line, status_words[i], strlen(status_words[i])) == 0) {
320 status_num = (char)i;
326 error_msg("Invalid status word");
331 * Read a control file (or a stanza of a status file) and parse it,
332 * filling parsed fields into the package structure
334 static int control_read(FILE *file, package_t *p)
338 while ((line = get_line_from_file(file)) != NULL) {
339 line[strlen(line) - 1] = '\0';
341 if (strlen(line) == 0) {
344 else if (strstr(line, "Package: ") == line) {
345 p->package = xstrdup(line + 9);
347 else if (strstr(line, "Status: ") == line) {
349 word_pointer = strchr(line, ' ') + 1;
350 p->status_want = status_parse(word_pointer, status_words_want);
351 word_pointer = strchr(word_pointer, ' ') + 1;
352 p->status_flag = status_parse(word_pointer, status_words_flag);
353 word_pointer = strchr(word_pointer, ' ') + 1;
354 p->status_status = status_parse(word_pointer, status_words_status);
356 else if (strstr(line, "Depends: ") == line) {
357 p->depends = xstrdup(line + 9);
359 else if (strstr(line, "Provides: ") == line) {
360 p->provides = xstrdup(line + 10);
362 else if (strstr(line, "Description: ") == line) {
363 p->description = xstrdup(line + 13);
364 /* This is specific to the Debian Installer. Ifdef? */
366 else if (strstr(line, "installer-menu-item: ") == line) {
367 p->installer_menu_item = atoi(line + 21);
369 /* TODO: localized descriptions */
375 static void *status_read(void)
379 package_t *m = 0, *p = 0, *t = 0;
381 if (getenv(udpkg_quiet) == NULL) {
382 printf("(Reading database...)\n");
385 if ((f = wfopen(statusfile, "r")) == NULL) {
390 m = (package_t *)xcalloc(1, sizeof(package_t));
394 * If there is an item in the tree by this name,
395 * it must be a virtual package; insert real
396 * package in preference.
398 tdelete(m, &status, package_compare);
399 tsearch(m, &status, package_compare);
402 * A "Provides" triggers the insertion
403 * of a pseudo package into the status
406 p = (package_t *)xcalloc(1, sizeof(package_t));
407 p->package = xstrdup(m->provides);
408 t = *(package_t **)tsearch(p, &status, package_compare);
414 * Pseudo package status is the
415 * same as the status of the
416 * package providing it
417 * FIXME: (not quite right, if 2
418 * packages of different statuses
421 t->status_want = m->status_want;
422 t->status_flag = m->status_flag;
423 t->status_status = m->status_status;
435 static int status_merge(void *status, package_t *pkgs)
439 package_t *pkg = 0, *statpkg = 0;
442 if ((fout = wfopen(new_statusfile, "w")) == NULL) {
445 if (getenv(udpkg_quiet) == NULL) {
446 printf("(Updating database...)\n");
450 * Dont use wfopen here, handle errors ourself
452 if ((fin = fopen(statusfile, "r")) != NULL) {
453 while (((line = get_line_from_file(fin)) != NULL) && !feof(fin)) {
454 line[strlen(line) - 1] = '\0'; /* trim newline */
455 /* If we see a package header, find out if it's a package
456 * that we have processed. if so, we skip that block for
457 * now (write it at the end).
459 * we also look at packages in the status cache and update
460 * their status fields
462 if (strstr(line, "Package: ") == line) {
463 for (pkg = pkgs; pkg != 0 && strcmp(line + 9,
464 pkg->package) != 0; pkg = pkg->next) ;
466 locpkg.package = line + 9;
467 statpkg = tfind(&locpkg, &status, package_compare);
469 /* note: statpkg should be non-zero, unless the status
470 * file was changed while we are processing (no locking
471 * is currently done...
474 statpkg = *(package_t **)statpkg;
480 if (strstr(line, "Status: ") == line && statpkg != 0) {
481 snprintf(line, sizeof(line), "Status: %s %s %s",
482 status_words_want[statpkg->status_want - 1],
483 status_words_flag[statpkg->status_flag - 1],
484 status_words_status[statpkg->status_status - 1]);
486 fprintf(fout, "%s\n", line);
492 // Print out packages we processed.
493 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
494 fprintf(fout, "Package: %s\nStatus: %s %s %s\n",
495 pkg->package, status_words_want[pkg->status_want - 1],
496 status_words_flag[pkg->status_flag - 1],
497 status_words_status[pkg->status_status - 1]);
500 fprintf(fout, "Depends: %s\n", pkg->depends);
502 fprintf(fout, "Provides: %s\n", pkg->provides);
503 if (pkg->installer_menu_item)
504 fprintf(fout, "installer-menu-item: %i\n", pkg->installer_menu_item);
505 if (pkg->description)
506 fprintf(fout, "Description: %s\n", pkg->description);
512 * Its ok if renaming statusfile fails becasue it doesnt exist
514 if (rename(statusfile, bak_statusfile) == -1) {
515 struct stat stat_buf;
516 if (stat(statusfile, &stat_buf) == 0) {
517 error_msg("Couldnt create backup status file");
518 return(EXIT_FAILURE);
520 error_msg("No status file found, creating new one");
523 if (rename(new_statusfile, statusfile) == -1) {
524 error_msg("Couldnt create status file");
525 return(EXIT_FAILURE);
527 return(EXIT_SUCCESS);
530 static int is_file(const char *fn)
534 if (stat(fn, &statbuf) < 0) {
537 return S_ISREG(statbuf.st_mode);
540 static int dpkg_doconfigure(package_t *pkg)
546 DPRINTF("Configuring %s\n", pkg->package);
547 pkg->status_status = 0;
548 snprintf(postinst, sizeof(postinst), "%s%s.postinst", infodir, pkg->package);
550 if (is_file(postinst)) {
551 snprintf(buf, sizeof(buf), "%s configure", postinst);
552 if ((r = do_system(buf)) != 0) {
553 error_msg("postinst exited with status %d\n", r);
554 pkg->status_status = status_status_halfconfigured;
558 pkg->status_status = status_status_installed;
563 static int dpkg_dounpack(package_t *pkg)
567 char *cwd = xgetcwd(0);
568 char *src_file = NULL;
569 char *dst_file = NULL;
570 // char *lst_file = NULL;
571 char *adminscripts[] = { "prerm", "postrm", "preinst", "postinst",
572 "conffiles", "md5sums", "shlibs", "templates" };
574 DPRINTF("Unpacking %s\n", pkg->package);
579 deb_extract(dpkg_deb_extract, "/", pkg->file);
581 /* Installs the package scripts into the info directory */
582 for (i = 0; i < sizeof(adminscripts) / sizeof(adminscripts[0]); i++) {
583 struct stat src_stat_buf;
584 int src_fd = 0, dst_fd = 0;
586 /* The full path of the current location of the admin file */
587 src_file = xrealloc(src_file, strlen(dpkgcidir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
588 sprintf(src_file, "%s%s/%s", dpkgcidir, pkg->package, adminscripts[i]);
590 /* the full path of where we want the file to be copied to */
591 dst_file = xrealloc(dst_file, strlen(infodir) + strlen(pkg->package) + strlen(adminscripts[i]) + 1);
592 sprintf(dst_file, "%s%s.%s", infodir, pkg->package, adminscripts[i]);
595 * copy admin file to permanent home
596 * NOTE: Maybe merge this behaviour into libb/copy_file.c
598 if (lstat(src_file, &src_stat_buf) == 0) {
599 if ((src_fd = open(src_file, O_RDONLY)) != -1) {
600 if ((dst_fd = open(dst_file, O_WRONLY | O_CREAT, 0644)) == -1) {
602 perror_msg("Opening %s", dst_file);
604 copy_file_chunk(src_fd, dst_fd, src_stat_buf.st_size);
609 error_msg("couldnt open [%s]\n", src_file);
615 * create the list file
616 * FIXME: currently this dumps the lst to stdout instead of a file
618 /* lst_file = (char *) xmalloc(strlen(infodir) + strlen(pkg->package) + 6);
619 strcpy(lst_file, infodir);
620 strcat(lst_file, pkg->package);
621 strcat(lst_file, ".list");
622 deb_extract(dpkg_deb_list, NULL, pkg->file);
625 pkg->status_want = status_want_install;
626 pkg->status_flag = status_flag_ok;
628 if (status == TRUE) {
629 pkg->status_status = status_status_unpacked;
631 pkg->status_status = status_status_halfinstalled;
639 * Extract and parse the control.tar.gz from the specified package
641 static int dpkg_unpackcontrol(package_t *pkg)
647 /* clean the temp directory (dpkgcidir) be recreating it */
648 remove_dir(dpkgcidir);
649 if (create_path(dpkgcidir, S_IRWXU) == FALSE) {
654 * Get the package name from the file name,
655 * first remove the directories
657 if ((tmp_name = strrchr(pkg->file, '/')) == NULL) {
658 tmp_name = pkg->file;
662 /* now remove trailing version numbers etc */
663 length = strcspn(tmp_name, "_.");
664 pkg->package = (char *) xcalloc(1, length + 1);
665 /* store the package name */
666 strncpy(pkg->package, tmp_name, length);
668 /* work out the full extraction path */
669 tmp_name = (char *) xcalloc(1, strlen(dpkgcidir) + strlen(pkg->package) + 9);
670 strcpy(tmp_name, dpkgcidir);
671 strcat(tmp_name, pkg->package);
673 /* extract control.tar.gz to the full extraction path */
674 deb_extract(dpkg_deb_control, tmp_name, pkg->file);
676 /* parse the extracted control file */
677 strcat(tmp_name, "/control");
678 if ((file = wfopen(tmp_name, "r")) == NULL) {
681 if (control_read(file, pkg) == EXIT_FAILURE) {
688 static int dpkg_unpack(package_t *pkgs, void *status)
693 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
694 if (dpkg_unpackcontrol(pkg) == EXIT_FAILURE) {
697 if ((r = dpkg_dounpack(pkg)) != 0 ) {
701 status_merge(status, pkgs);
702 remove_dir(dpkgcidir);
707 static int dpkg_configure(package_t *pkgs, void *status)
713 for (pkg = pkgs; pkg != 0 && r == 0; pkg = pkg->next) {
714 found = tfind(pkg, &status, package_compare);
717 error_msg("Trying to configure %s, but it is not installed", pkg->package);
720 /* configure the package listed in the status file;
721 * not pkg, as we have info only for the latter
724 r = dpkg_doconfigure(*(package_t **)found);
727 status_merge(status, 0);
732 static int dpkg_install(package_t *pkgs, void *status)
734 package_t *p, *ordered = 0;
736 /* Stage 1: parse all the control information */
737 for (p = pkgs; p != 0; p = p->next) {
738 if (dpkg_unpackcontrol(p) == EXIT_FAILURE) {
739 return(EXIT_FAILURE);
743 /* Stage 2: resolve dependencies */
745 ordered = depends_resolve(pkgs, status);
750 /* Stage 3: install */
751 for (p = ordered; p != 0; p = p->next) {
752 p->status_want = status_want_install;
754 /* for now the flag is always set to ok... this is probably
757 p->status_flag = status_flag_ok;
759 DPRINTF("Installing %s\n", p->package);
760 if (dpkg_dounpack(p) != 0) {
764 if (dpkg_doconfigure(p) != 0) {
770 status_merge(status, pkgs);
772 remove_dir(dpkgcidir);
778 * Not implemented yet
780 static int dpkg_remove(package_t *pkgs, void *status)
784 for (p = pkgs; p != 0; p = p->next)
787 status_merge(status, 0);
793 extern int dpkg_main(int argc, char **argv)
795 const int arg_install = 1;
796 const int arg_unpack = 2;
797 const int arg_configure = 4;
799 package_t *p, *packages = NULL;
804 while ((opt = getopt(argc, argv, "iruc")) != -1) {
807 optflag |= arg_install;
810 optflag |= arg_unpack;
813 optflag |= arg_configure;
820 while (optind < argc) {
821 p = (package_t *) xcalloc(1, sizeof(package_t));
822 if (optflag & arg_configure) {
823 p->package = xstrdup(argv[optind]);
825 p->file = xstrdup(argv[optind]);
833 create_path(dpkgcidir, S_IRWXU);
834 create_path(infodir, S_IRWXU);
836 status = status_read();
838 if (optflag & arg_install) {
839 return dpkg_install(packages, status);
841 else if (optflag & arg_unpack) {
842 return dpkg_unpack(packages, status);
844 else if (optflag & arg_configure) {
845 return dpkg_configure(packages, status);
847 return(EXIT_FAILURE);