14 //#define PACKAGE "udpkg"
15 //#define VERSION "0.1"
18 * Should we do full dependency checking?
23 * Should we do debugging?
28 #define SYSTEM(x) do_system(x)
29 #define DPRINTF(fmt,args...) fprintf(stderr, fmt, ##args)
31 #define SYSTEM(x) system(x)
32 #define DPRINTF(fmt,args...) /* nothing */
36 #define DEPENDSMAX 64 /* maximum number of depends we can handle */
38 #define ADMINDIR "/var/lib/dpkg"
39 #define STATUSFILE ADMINDIR ## "/status.udeb"
40 #define DPKGCIDIR ADMINDIR ## "/tmp.ci/"
41 static const char infodir[] = "/var/lib/dpkg/info/";
42 static const char udpkg_quiet[] = "UDPKG_QUIET";
44 //static const int status_wantstart = 0;
45 //static const int status_wantunknown = (1 << 0);
46 static const int status_wantinstall = (1 << 1);
47 //static const int status_wanthold = (1 << 2);
48 //static const int status_wantdeinstall = (1 << 3);
49 //static const int status_wantpurge = (1 << 4);
50 static const int status_wantmask = 31;
52 //static const int status_flagstart = 5;
53 static const int status_flagok = (1 << 5); /* 32 */
54 //static const int status_flagreinstreq = (1 << 6);
55 //static const int status_flaghold = (1 << 7);
56 //static const int status_flagholdreinstreq = (1 << 8);
57 static const int status_flagmask = 480;
59 //static const int status_statusstart = 9;
60 //static const int status_statusnoninstalled = (1 << 9); /* 512 */
61 static const int status_statusunpacked = (1 << 10);
62 static const int status_statushalfconfigured = (1 << 11);
63 static const int status_statusinstalled = (1 << 12);
64 static const int status_statushalfinstalled = (1 << 13);
65 //static const int status_statusconfigfiles = (1 << 14);
66 //static const int status_statuspostinstfailed = (1 << 15);
67 //static const int status_statusremovalfailed = (1 << 16);
68 static const int status_statusmask = 130560; /* i assume status_statusinstalled is supposed to be included */
70 static const char *statuswords[][10] = {
71 { (char *) 0, "unknown", "install", "hold", "deinstall", "purge", 0 },
72 { (char *) 5, "ok", "reinstreq", "hold", "hold-reinstreq", 0 },
73 { (char *) 9, "not-installed", "unpacked", "half-configured",
74 "installed", "half-installed", "config-files",
75 "post-inst-failed", "removal-failed", 0 }
78 const int color_white = 0;
79 const int color_grey = 1;
80 const int color_black = 2;
83 typedef struct package_s {
90 int installer_menu_item;
92 char color; /* for topo-sort */
93 struct package_s *requiredfor[DEPENDSMAX];
94 unsigned short requiredcount;
95 struct package_s *next;
98 static int package_compare(const void *p1, const void *p2)
100 return strcmp(((package_t *)p1)->package,
101 ((package_t *)p2)->package);
107 static char **depends_split(const char *dependsstr)
109 static char *dependsvec[DEPENDSMAX];
117 p = strdup(dependsstr);
118 while (*p != 0 && *p != '\n')
127 else if (dependsvec[i] == 0)
131 *p = 0; /* eat the space... */
140 static void depends_sort_visit(package_t **ordered, package_t *pkgs,
143 /* Topological sort algorithm:
144 * ordered is the output list, pkgs is the dependency graph, pkg is
147 * recursively add all the adjacent nodes to the ordered list, marking
148 * each one as visited along the way
150 * yes, this algorithm looks a bit odd when all the params have the
155 /* mark node as processing */
156 pkg->color = color_grey;
158 /* visit each not-yet-visited node */
159 for (i = 0; i < pkg->requiredcount; i++)
160 if (pkg->requiredfor[i]->color == color_white)
161 depends_sort_visit(ordered, pkgs, pkg->requiredfor[i]);
164 /* add it to the list */
165 newnode = (struct package_t *)malloc(sizeof(struct package_t));
166 /* make a shallow copy */
168 newnode->next = *ordered;
171 pkg->next = *ordered;
174 /* mark node as done */
175 pkg->color = color_black;
178 static package_t *depends_sort(package_t *pkgs)
180 /* TODO: it needs to break cycles in the to-be-installed package
182 package_t *ordered = NULL;
185 for (pkg = pkgs; pkg != 0; pkg = pkg->next)
186 pkg->color = color_white;
188 for (pkg = pkgs; pkg != 0; pkg = pkg->next)
189 if (pkg->color == color_white)
190 depends_sort_visit(&ordered, pkgs, pkg);
192 /* Leaks the old list... return the new one... */
197 /* resolve package dependencies --
198 * for each package in the list of packages to be installed, we parse its
199 * dependency info to determine if the dependent packages are either
200 * already installed, or are scheduled to be installed. If both tests fail
203 * The algorithm here is O(n^2*m) where n = number of packages to be
204 * installed and m is the # of dependencies per package. Not a terribly
205 * efficient algorithm, but given that at any one time you are unlikely
206 * to install a very large number of packages it doesn't really matter
208 static package_t *depends_resolve(package_t *pkgs, void *status)
210 package_t *pkg, *chk;
216 for (pkg = pkgs; pkg != 0; pkg = pkg->next)
218 dependsvec = depends_split(pkg->depends);
220 while (dependsvec[i] != 0)
222 /* Check for dependencies; first look for installed packages */
223 dependpkg.package = dependsvec[i];
224 if ((found = tfind(&dependpkg, &status, package_compare)) == 0 ||
225 ((chk = *(package_t **)found) &&
226 (chk->status & (status_flagok | status_statusinstalled)) !=
227 (status_flagok | status_statusinstalled)))
229 /* if it fails, we look through the list of packages we are going to
231 for (chk = pkgs; chk != 0; chk = chk->next)
233 if (strcmp(chk->package, dependsvec[i]) == 0 ||
235 strncmp(chk->provides, dependsvec[i], strlen(dependsvec[i])) == 0))
237 if (chk->requiredcount >= DEPENDSMAX)
239 fprintf(stderr, "Too many dependencies for %s\n",
244 chk->requiredfor[chk->requiredcount++] = pkg;
250 fprintf(stderr, "%s depends on %s, but it is not going to be installed\n", pkg->package, dependsvec[i]);
258 return depends_sort(pkgs);
262 /* Status file handling routines
264 * This is a fairly minimalistic implementation. there are two main functions
265 * that are supported:
267 * 1) reading the entire status file:
268 * the status file is read into memory as a binary-tree, with just the
269 * package and status info preserved
271 * 2) merging the status file
272 * control info from (new) packages is merged into the status file,
273 * replacing any pre-existing entries. when a merge happens, status info
274 * read using the status_read function is written back to the status file
277 static unsigned long status_parse(const char *line)
282 for (i = 0; i < 3; i++)
284 p = strchr(line, ' ');
287 while (statuswords[i][j] != 0)
289 if (strcmp(line, statuswords[i][j]) == 0)
291 l |= (1 << ((int)statuswords[i][0] + j - 1));
296 if (statuswords[i][j] == 0) return 0; /* parse error */
302 static const char *status_print(unsigned long flags)
304 /* this function returns a static buffer... */
305 static char buf[256];
309 for (i = 0; i < 3; i++)
312 while (statuswords[i][j] != 0)
314 if ((flags & (1 << ((int)statuswords[i][0] + j - 1))) != 0)
316 strcat(buf, statuswords[i][j]);
317 if (i < 2) strcat(buf, " ");
322 if (statuswords[i][j] == 0)
324 fprintf(stderr, "corrupted status flag!!\n");
332 * Read a control file (or a stanza of a status file) and parse it,
333 * filling parsed fields into the package structure
335 static void control_read(FILE *f, package_t *p)
338 while (fgets(buf, BUFSIZE, f) && !feof(f))
340 buf[strlen(buf)-1] = 0;
343 else if (strstr(buf, "Package: ") == buf)
345 p->package = strdup(buf+9);
347 else if (strstr(buf, "Status: ") == buf)
349 p->status = status_parse(buf+8);
351 else if (strstr(buf, "Depends: ") == buf)
353 p->depends = strdup(buf+9);
355 else if (strstr(buf, "Provides: ") == buf)
357 p->provides = strdup(buf+10);
359 /* This is specific to the Debian Installer. Ifdef? */
360 else if (strstr(buf, "installer-menu-item: ") == buf)
362 p->installer_menu_item = atoi(buf+21);
364 else if (strstr(buf, "Description: ") == buf)
366 p->description = strdup(buf+13);
368 /* TODO: localized descriptions */
372 static void *status_read(void)
376 package_t *m = 0, *p = 0, *t = 0;
378 if ((f = fopen(STATUSFILE, "r")) == NULL)
383 if (getenv(udpkg_quiet) == NULL)
384 printf("(Reading database...)\n");
387 m = (package_t *)malloc(sizeof(package_t));
388 memset(m, 0, sizeof(package_t));
393 * If there is an item in the tree by this name,
394 * it must be a virtual package; insert real
395 * package in preference.
397 tdelete(m, &status, package_compare);
398 tsearch(m, &status, package_compare);
402 * A "Provides" triggers the insertion
403 * of a pseudo package into the status
406 p = (package_t *)malloc(sizeof(package_t));
407 memset(p, 0, sizeof(package_t));
408 p->package = strdup(m->provides);
410 t = *(package_t **)tsearch(p, &status, package_compare);
418 * Pseudo package status is the
419 * same as the status of the
420 * package providing it
421 * FIXME: (not quite right, if 2
422 * packages of different statuses
425 t->status = m->status;
438 static int status_merge(void *status, package_t *pkgs)
442 package_t *pkg = 0, *statpkg = 0;
446 if ((fin = fopen(STATUSFILE, "r")) == NULL)
451 if ((fout = fopen(STATUSFILE ".new", "w")) == NULL)
453 perror(STATUSFILE ".new");
456 if (getenv(udpkg_quiet) == NULL)
457 printf("(Updating database...)\n");
458 while (fgets(buf, BUFSIZE, fin) && !feof(fin))
460 buf[strlen(buf)-1] = 0; /* trim newline */
461 /* If we see a package header, find out if it's a package
462 * that we have processed. if so, we skip that block for
463 * now (write it at the end).
465 * we also look at packages in the status cache and update
466 * their status fields
468 if (strstr(buf, "Package: ") == buf)
470 for (pkg = pkgs; pkg != 0 && strncmp(buf + 9,
471 pkg->package, strlen(buf) - 9)!=0;
474 locpkg.package = buf+9;
475 statpkg = tfind(&locpkg, &status, package_compare);
477 /* note: statpkg should be non-zero, unless the status
478 * file was changed while we are processing (no locking
479 * is currently done...
481 if (statpkg != 0) statpkg = *(package_t **)statpkg;
483 if (pkg != 0) continue;
485 if (strstr(buf, "Status: ") == buf && statpkg != 0)
487 snprintf(buf, sizeof(buf), "Status: %s",
488 status_print(statpkg->status));
494 // Print out packages we processed.
495 for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
496 fprintf(fout, "Package: %s\nStatus: %s\n",
497 pkg->package, status_print(pkg->status));
499 fprintf(fout, "Depends: %s\n", pkg->depends);
501 fprintf(fout, "Provides: %s\n", pkg->provides);
502 if (pkg->installer_menu_item)
503 fprintf(fout, "installer-menu-item: %i\n", pkg->installer_menu_item);
504 if (pkg->description)
505 fprintf(fout, "Description: %s\n", pkg->description);
512 r = rename(STATUSFILE, STATUSFILE ".bak");
513 if (r == 0) r = rename(STATUSFILE ".new", STATUSFILE);
518 * Main udpkg implementation routines
522 static int do_system(const char *cmd)
524 DPRINTF("cmd is %s\n", cmd);
528 #define do_system(cmd) system(cmd)
531 static int is_file(const char *fn)
535 if (stat(fn, &statbuf) < 0) return 0;
536 return S_ISREG(statbuf.st_mode);
539 static int dpkg_doconfigure(package_t *pkg)
544 DPRINTF("Configuring %s\n", pkg->package);
545 pkg->status &= status_statusmask;
546 snprintf(postinst, sizeof(postinst), "%s%s.postinst", infodir, pkg->package);
547 if (is_file(postinst))
549 snprintf(buf, sizeof(buf), "%s configure", postinst);
550 if ((r = do_system(buf)) != 0)
552 fprintf(stderr, "postinst exited with status %d\n", r);
553 pkg->status |= status_statushalfconfigured;
558 pkg->status |= status_statusinstalled;
563 static int dpkg_dounpack(package_t *pkg)
568 char buf[1024], buf2[1024];
570 char *adminscripts[] = { "prerm", "postrm", "preinst", "postinst",
571 "conffiles", "md5sums", "shlibs",
574 DPRINTF("Unpacking %s\n", pkg->package);
578 snprintf(buf, sizeof(buf), "ar -p %s data.tar.gz|zcat|tar -xf -", pkg->file);
579 if (SYSTEM(buf) == 0)
581 /* Installs the package scripts into the info directory */
582 for (i = 0; i < sizeof(adminscripts) / sizeof(adminscripts[0]);
585 snprintf(buf, sizeof(buf), "%s%s/%s",
586 DPKGCIDIR, pkg->package, adminscripts[i]);
587 snprintf(buf2, sizeof(buf), "%s%s.%s",
588 infodir, pkg->package, adminscripts[i]);
589 if (copy_file(buf, buf2, TRUE, FALSE, FALSE) < 0)
591 fprintf(stderr, "Cannot copy %s to %s: %s\n",
592 buf, buf2, strerror(errno));
598 /* ugly hack to create the list file; should
599 * probably do something more elegant
601 * why oh why does dpkg create the list file
604 snprintf(buf, sizeof(buf),
605 "ar -p %s data.tar.gz|zcat|tar -tf -",
607 snprintf(buf2, sizeof(buf2),
608 "%s%s.list", infodir, pkg->package);
609 if ((infp = popen(buf, "r")) == NULL ||
610 (outfp = fopen(buf2, "w")) == NULL)
612 fprintf(stderr, "Cannot create %s\n",
617 while (fgets(buf, sizeof(buf), infp) &&
622 if (*p == '/' && *(p+1) == '\n')
628 if (p[strlen(p)-2] == '/')
630 p[strlen(p)-2] = '\n';
639 pkg->status &= status_wantmask;
640 pkg->status |= status_wantinstall;
641 pkg->status &= status_flagmask;
642 pkg->status |= status_flagok;
643 pkg->status &= status_statusmask;
645 pkg->status |= status_statusunpacked;
647 pkg->status |= status_statushalfinstalled;
653 static int dpkg_doinstall(package_t *pkg)
655 DPRINTF("Installing %s\n", pkg->package);
656 return (dpkg_dounpack(pkg) || dpkg_doconfigure(pkg));
659 static int dpkg_unpackcontrol(package_t *pkg)
667 p = strrchr(pkg->file, '/');
668 if (p) p++; else p = pkg->file;
669 p = pkg->package = strdup(p);
670 while (*p != 0 && *p != '_' && *p != '.') p++;
674 snprintf(buf, sizeof(buf), "%s%s", DPKGCIDIR, pkg->package);
675 DPRINTF("dir = %s\n", buf);
676 if (mkdir(buf, S_IRWXU) == 0 && chdir(buf) == 0)
678 snprintf(buf, sizeof(buf), "ar -p %s control.tar.gz|zcat|tar -xf -",
680 if (SYSTEM(buf) == 0)
682 if ((f = fopen("control", "r")) != NULL) {
683 control_read(f, pkg);
694 static int dpkg_unpack(package_t *pkgs)
698 void *status = status_read();
700 if (SYSTEM("rm -rf -- " DPKGCIDIR) != 0 ||
701 mkdir(DPKGCIDIR, S_IRWXU) != 0)
707 for (pkg = pkgs; pkg != 0; pkg = pkg->next)
709 dpkg_unpackcontrol(pkg);
710 r = dpkg_dounpack(pkg);
713 status_merge(status, pkgs);
714 SYSTEM("rm -rf -- " DPKGCIDIR);
718 static int dpkg_configure(package_t *pkgs)
723 void *status = status_read();
724 for (pkg = pkgs; pkg != 0 && r == 0; pkg = pkg->next)
726 found = tfind(pkg, &status, package_compare);
729 fprintf(stderr, "Trying to configure %s, but it is not installed\n", pkg->package);
734 /* configure the package listed in the status file;
735 * not pkg, as we have info only for the latter */
736 r = dpkg_doconfigure(*(package_t **)found);
739 status_merge(status, 0);
743 static int dpkg_install(package_t *pkgs)
745 package_t *p, *ordered = 0;
746 void *status = status_read();
747 if (SYSTEM("rm -rf -- " DPKGCIDIR) != 0 ||
748 mkdir(DPKGCIDIR, S_IRWXU) != 0)
754 /* Stage 1: parse all the control information */
755 for (p = pkgs; p != 0; p = p->next)
756 if (dpkg_unpackcontrol(p) != 0)
759 /* force loop break, and prevents further ops */
763 /* Stage 2: resolve dependencies */
765 ordered = depends_resolve(pkgs, status);
770 /* Stage 3: install */
771 for (p = ordered; p != 0; p = p->next)
773 p->status &= status_wantmask;
774 p->status |= status_wantinstall;
776 /* for now the flag is always set to ok... this is probably
779 p->status &= status_flagmask;
780 p->status |= status_flagok;
782 if (dpkg_doinstall(p) != 0)
789 status_merge(status, pkgs);
790 SYSTEM("rm -rf -- " DPKGCIDIR);
794 static int dpkg_remove(package_t *pkgs)
797 void *status = status_read();
798 for (p = pkgs; p != 0; p = p->next)
801 status_merge(status, 0);
805 extern int dpkg_main(int argc, char **argv)
809 package_t *p, *packages = NULL;
810 char *cwd = getcwd(0, 0);
814 /* Nasty little hack to "parse" long options. */
822 p = (package_t *)malloc(sizeof(package_t));
823 memset(p, 0, sizeof(package_t));
828 p->file = malloc(strlen(cwd) + strlen(*argv) + 2);
829 sprintf(p->file, "%s/%s", cwd, *argv);
832 p->package = strdup(*argv);
841 case 'i': return dpkg_install(packages); break;
842 case 'r': return dpkg_remove(packages); break;
843 case 'u': return dpkg_unpack(packages); break;
844 case 'c': return dpkg_configure(packages); break;
847 /* if it falls through to here, some of the command line options were