d05198cbff6fe36f71718f0df5172be111a98ac4
[oweals/busybox.git] / archival / dpkg.c
1 #include <dirent.h>
2 #include <getopt.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <search.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <utime.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13
14 #include "busybox.h"
15
16
17 #define DEPENDSMAX      64      /* maximum number of depends we can handle */
18
19 /* Should we do full dependency checking? */
20 #define DODEPENDS 1
21
22 /* Should we do debugging? */
23 #define DODEBUG 1
24
25 #ifdef DODEBUG
26 #define SYSTEM(x) do_system(x)
27 #define DPRINTF(fmt,args...) fprintf(stderr, fmt, ##args)
28 #else
29 #define SYSTEM(x) system(x)
30 #define DPRINTF(fmt,args...) /* nothing */
31 #endif
32
33 /* from dpkg-deb.c */
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;
41
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";
45
46 static const char dpkgcidir[] = "/var/lib/dpkg/tmp.ci/";
47
48 static const char infodir[] = "/var/lib/dpkg/info/";
49 static const char udpkg_quiet[] = "UDPKG_QUIET";
50
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;
58
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;
65
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 */
76
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 }
83 };
84
85 static const int color_white    = 0;
86 static const int color_grey     = 1;
87 static const int color_black    = 2;
88
89 /* data structures */
90 typedef struct package_s {
91         char *file;
92         char *package;
93         char *version;
94         char *depends;
95         char *provides;
96         char *description;
97         int installer_menu_item;
98         unsigned long status;
99         char color; /* for topo-sort */
100         struct package_s *requiredfor[DEPENDSMAX]; 
101         unsigned short requiredcount;
102         struct package_s *next;
103 } package_t;
104
105 #ifdef DODEBUG
106 static int do_system(const char *cmd)
107 {
108         DPRINTF("cmd is %s\n", cmd);
109         return system(cmd);
110 }
111 #else
112 #define do_system(cmd) system(cmd)
113 #endif
114
115 static int package_compare(const void *p1, const void *p2)
116 {
117         return strcmp(((package_t *)p1)->package, 
118                 ((package_t *)p2)->package);
119 }
120
121 /*
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
125  */
126 static int remove_dir(const char *dirname)
127 {
128         struct dirent *fp;
129         DIR *dp = opendir(dirname);
130         while ((fp = readdir(dp)) != NULL) {
131                 struct stat statbuf;
132                 char *filename;
133
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);
138
139                 if ((strcmp(fp->d_name, ".") != 0) && (strcmp(fp->d_name, "..") != 0)) {
140                         if (S_ISDIR(statbuf.st_mode)) {
141                                 remove_dir(strcat(filename, "/"));
142                         }
143                         else if (remove(filename) == -1) {
144                                 perror_msg(filename);
145                         }
146                 }
147         }
148         remove(dirname);
149         return EXIT_SUCCESS;
150 }
151
152 #ifdef DODEPENDS
153 #include <ctype.h>
154
155 static char **depends_split(const char *dependsstr)
156 {
157         static char *dependsvec[DEPENDSMAX];
158         char *p;
159         int i = 0;
160
161         dependsvec[0] = 0;
162         if (dependsstr == 0) {
163                 goto end;
164         }
165
166         p = xstrdup(dependsstr);
167         while (*p != 0 && *p != '\n') {
168                 if (*p != ' ') {
169                         if (*p == ',') {
170                                 *p = 0;
171                                 dependsvec[++i] = 0;
172                         } else {
173                                 if (dependsvec[i] == 0) {
174                                         dependsvec[i] = p;
175                                 }
176                         }
177                 } else {
178                         *p = 0; /* eat the space... */
179                 }
180                 p++;
181         }
182         *p = 0;
183
184 end:
185         dependsvec[i+1] = 0;
186         return dependsvec;
187 }
188
189 /* Topological sort algorithm:
190  * ordered is the output list, pkgs is the dependency graph, pkg is 
191  * the current node
192  *
193  * recursively add all the adjacent nodes to the ordered list, marking
194  * each one as visited along the way
195  *
196  * yes, this algorithm looks a bit odd when all the params have the
197  * same type :-)
198  */
199 static void depends_sort_visit(package_t **ordered, package_t *pkgs,
200                 package_t *pkg)
201 {
202         unsigned short i;
203
204         /* mark node as processing */
205         pkg->color = color_grey;
206
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]);
211
212 #if 0
213         /* add it to the list */
214         newnode = (struct package_t *)xmalloc(sizeof(struct package_t));
215         /* make a shallow copy */
216         *newnode = *pkg;
217         newnode->next = *ordered;
218         *ordered = newnode;
219 #endif
220
221         pkg->next = *ordered;
222         *ordered = pkg;
223
224         /* mark node as done */
225         pkg->color = color_black;
226 }
227
228 static package_t *depends_sort(package_t *pkgs)
229 {
230         /* TODO: it needs to break cycles in the to-be-installed package 
231          * graph... */
232         package_t *ordered = NULL;
233         package_t *pkg;
234
235         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
236                 pkg->color = color_white;
237         }
238         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
239                 if (pkg->color == color_white) {
240                         depends_sort_visit(&ordered, pkgs, pkg);
241                 }
242         }
243
244         /* Leaks the old list... return the new one... */
245         return ordered;
246 }
247
248
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
253  * than bail.
254  *
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
259  */
260 static package_t *depends_resolve(package_t *pkgs, void *status)
261 {
262         package_t *pkg, *chk;
263         package_t dependpkg;
264         char **dependsvec;
265         int i;
266         void *found;
267
268         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
269                 dependsvec = depends_split(pkg->depends);
270                 i = 0;
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))) {
278
279                                 /* if it fails, we look through the list of packages we are going to 
280                                  * install */
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);
286                                                         return 0;
287                                                 }
288                                                 if (chk != pkg) {
289                                                         chk->requiredfor[chk->requiredcount++] = pkg;
290                                                 }
291                                                 break;
292                                         }
293                                 }
294                                 if (chk == 0) {
295                                         fprintf(stderr, "%s depends on %s, but it is not going to be installed\n", pkg->package, dependsvec[i]);
296                                         return 0;
297                                 }
298                         }
299                         i++;
300                 }
301         }
302
303         return depends_sort(pkgs);
304 }
305 #endif
306
307 /* Status file handling routines
308  * 
309  * This is a fairly minimalistic implementation. there are two main functions 
310  * that are supported:
311  * 
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
315  *
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
320  */
321 static unsigned long status_parse(const char *line)
322 {
323         char *p;
324         int i, j;
325         unsigned long l = 0;
326
327         for (i = 0; i < 3; i++) {
328                 if ((p = strchr(line, ' ')) != NULL) {
329                         *p = 0;
330                 }
331                 j = 1;
332                 while (statuswords[i][j] != 0) {
333                         if (strcmp(line, statuswords[i][j]) == 0) {
334                                 l |= (1 << ((int)statuswords[i][0] + j - 1));
335                                 break;
336                         }
337                         j++;
338                 }
339                 /* parse error */
340                 if (statuswords[i][j] == 0) {
341                         return 0;
342                 }
343                 line = p+1;
344         }
345
346         return l;
347 }
348
349 static const char *status_print(unsigned long flags)
350 {
351         /* this function returns a static buffer... */
352         static char buf[256];
353         int i, j;
354
355         buf[0] = 0;
356         for (i = 0; i < 3; i++) {
357                 j = 1;
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, " ");
362                                 break;
363                         }
364                         j++;
365                 }
366                 if (statuswords[i][j] == 0) {
367                         fprintf(stderr, "corrupted status flag!!\n");
368                         return NULL;
369                 }
370         }
371
372         return buf;
373 }
374
375 /*
376  * Read a control file (or a stanza of a status file) and parse it,
377  * filling parsed fields into the package structure
378  */
379 static int control_read(FILE *file, package_t *p)
380 {
381         char *line;
382
383         while ((line = get_line_from_file(file)) != NULL) {
384                 line[strlen(line) - 1] = '\0';
385
386                 if (strlen(line) == 0) {
387                         break;
388                 } else
389                         if (strstr(line, "Package: ") == line) {
390                                 p->package = xstrdup(line + 9);
391                 } else
392                         if (strstr(line, "Status: ") == line) {
393                                 p->status = status_parse(line + 8);
394                 } else
395                         if (strstr(line, "Depends: ") == line) {
396                                 p->depends = xstrdup(line + 9);
397                 } else
398                         if (strstr(line, "Provides: ") == line) {
399                                 p->provides = xstrdup(line + 10);
400                 } else
401                         if (strstr(line, "Description: ") == line) {
402                                 p->description = xstrdup(line + 13);
403                 /* This is specific to the Debian Installer. Ifdef? */
404                 } else
405                         if (strstr(line, "installer-menu-item: ") == line) {
406                                 p->installer_menu_item = atoi(line + 21);
407                 }
408                 /* TODO: localized descriptions */
409         }
410         free(line);
411         return EXIT_SUCCESS;
412 }
413
414 static void *status_read(void)
415 {
416         FILE *f;
417         void *status = 0;
418         package_t *m = 0, *p = 0, *t = 0;
419
420         if ((f = fopen(statusfile, "r")) == NULL) {
421                 perror_msg(statusfile);
422                 return 0;
423         }
424
425         if (getenv(udpkg_quiet) == NULL) {
426                 printf("(Reading database...)\n");
427         }
428
429         while (!feof(f)) {
430                 m = (package_t *)xcalloc(1, sizeof(package_t));
431                 control_read(f, m);
432                 if (m->package) {
433                         /*
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.
437                          */
438                         tdelete(m, &status, package_compare);
439                         tsearch(m, &status, package_compare);
440                         if (m->provides) {
441                                 /* 
442                                  * A "Provides" triggers the insertion
443                                  * of a pseudo package into the status
444                                  * binary-tree.
445                                  */
446                                 p = (package_t *)xcalloc(1, sizeof(package_t));
447                                 p->package = xstrdup(m->provides);
448
449                                 t = *(package_t **)tsearch(p, &status, package_compare);
450                                 if (t != p) {
451                                         free(p->package);
452                                         free(p);
453                                 }
454                                 else {
455                                         /*
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
461                                          * provide it).
462                                          */
463                                         t->status = m->status;
464                                 }
465                         }
466                 }
467                 else {
468                         free(m);
469                 }
470         }
471         fclose(f);
472         return status;
473 }
474
475 static int status_merge(void *status, package_t *pkgs)
476 {
477         FILE *fin, *fout;
478         char *line = NULL;
479         package_t *pkg = 0, *statpkg = 0;
480         package_t locpkg;
481         int r = 0;
482
483         if ((fout = wfopen(new_statusfile, "w")) == NULL) {
484                 return 0;
485         }
486         if (getenv(udpkg_quiet) == NULL) {
487                 printf("(Updating database...)\n");
488         }
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).
495                          *
496                          * we also look at packages in the status cache and update
497                          * their status fields
498                          */
499                         if (strstr(line, "Package: ") == line) {
500                                 for (pkg = pkgs; pkg != 0 && strcmp(line + 9,
501                                                 pkg->package) != 0; pkg = pkg->next) ;
502
503                                 locpkg.package = line + 9;
504                                 statpkg = tfind(&locpkg, &status, package_compare);
505                         
506                                 /* note: statpkg should be non-zero, unless the status
507                                  * file was changed while we are processing (no locking
508                                  * is currently done...
509                                  */
510                                 if (statpkg != 0) {
511                                         statpkg = *(package_t **)statpkg;
512                                 }
513                         }
514                         if (pkg != 0) {
515                                 continue;
516                         }
517                         if (strstr(line, "Status: ") == line && statpkg != 0) {
518                                 snprintf(line, sizeof(line), "Status: %s",
519                                         status_print(statpkg->status));
520                         }
521                         fputs(line, fout);
522                         fputc('\n', fout);
523                 }
524         }
525         free(line);
526
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));
531                 if (pkg->depends)
532                         fprintf(fout, "Depends: %s\n", pkg->depends);
533                 if (pkg->provides)
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);
539                 fputc('\n', fout);
540         }
541         
542         fclose(fin);
543         fclose(fout);
544
545         r = rename(statusfile, bak_statusfile);
546         if (r == 0) {
547                 r = rename(new_statusfile, statusfile);
548         }
549
550         return 0;
551 }
552
553 static int is_file(const char *fn)
554 {
555         struct stat statbuf;
556
557         if (stat(fn, &statbuf) < 0) {
558                 return 0;
559         }
560         return S_ISREG(statbuf.st_mode);
561 }
562
563 static int dpkg_doconfigure(package_t *pkg)
564 {
565         int r;
566         char postinst[1024];
567         char buf[1024];
568
569         DPRINTF("Configuring %s\n", pkg->package);
570         pkg->status &= status_statusmask;
571         snprintf(postinst, sizeof(postinst), "%s%s.postinst", infodir, pkg->package);
572
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;
578                         return 1;
579                 }
580         }
581         pkg->status |= status_statusinstalled;
582         
583         return 0;
584 }
585
586 static int dpkg_dounpack(package_t *pkg)
587 {
588         int r = 0, i;
589         char *cwd;
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" };
595
596         DPRINTF("Unpacking %s\n", pkg->package);
597
598         cwd = getcwd(0, 0);
599         chdir("/");
600         deb_extract(dpkg_deb_extract, "/", pkg->file);
601
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;
606
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]);
610
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]);
614
615                 /*
616                  * copy admin file to permanent home
617                  * NOTE: Maybe merge this behaviour into libb/copy_file.c
618                  */
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);
623                                 }
624                                 copy_file_chunk(src_fd, dst_fd, src_stat_buf.st_size);
625                                 close(src_fd);
626                                 close(dst_fd);
627                         } else {
628                                 error_msg("couldnt open [%s]\n", src_file);
629                         }
630                 }
631         }
632
633         /* 
634          * create the list file 
635          * FIXME: currently this dumps the lst to stdout instead of a file
636          */
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);
642 */
643
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;
649
650         if (r == 0) {
651                 pkg->status |= status_statusunpacked;
652         } else {
653                 pkg->status |= status_statushalfinstalled;
654         }
655         chdir(cwd);
656         return r;
657 }
658
659 /*
660  * Extract and parse the control.tar.gz from the specified package
661  */
662 static int dpkg_unpackcontrol(package_t *pkg)
663 {
664         char *tmp_name;
665         FILE *file;
666         int length;
667
668         /* clean the temp directory (dpkgcidir) be recreating it */
669         remove_dir(dpkgcidir);
670         if (mkdir(dpkgcidir, S_IRWXU) != 0) {
671                 perror("mkdir");
672                 return EXIT_FAILURE;
673         }
674
675         /*
676          * Get the package name from the file name,
677          * first remove the directories
678          */
679         if ((tmp_name = strrchr(pkg->file, '/')) == NULL) {
680                 tmp_name = pkg->file;
681         } else {
682                 tmp_name++;
683         }
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);
689
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);
694
695         /* extract control.tar.gz to the full extraction path */
696         deb_extract(dpkg_deb_control, tmp_name, pkg->file);
697
698         /* parse the extracted control file */
699         strcat(tmp_name, "/control");
700         if ((file = wfopen(tmp_name, "r")) == NULL) {
701                 return EXIT_FAILURE;
702         }
703         if (control_read(file, pkg) == EXIT_FAILURE) {
704                 return EXIT_FAILURE;
705         }
706
707         return EXIT_SUCCESS;
708 }
709
710 static int dpkg_unpack(package_t *pkgs, void *status)
711 {
712         int r = 0;
713         package_t *pkg;
714
715         for (pkg = pkgs; pkg != 0; pkg = pkg->next) {
716                 if (dpkg_unpackcontrol(pkg) == EXIT_FAILURE) {
717                         return EXIT_FAILURE;
718                 }
719                 if ((r = dpkg_dounpack(pkg)) != 0 ) {
720                         break;
721                 }
722         }
723         status_merge(status, pkgs);
724         remove_dir(dpkgcidir);
725
726         return r;
727 }
728
729 static int dpkg_configure(package_t *pkgs, void *status)
730 {
731         int r = 0;
732         void *found;
733         package_t *pkg;
734
735         for (pkg = pkgs; pkg != 0 && r == 0; pkg = pkg->next) {
736                 found = tfind(pkg, &status, package_compare);
737
738                 if (found == 0) {
739                         fprintf(stderr, "Trying to configure %s, but it is not installed\n", pkg->package);
740                         r = 1;
741                 } 
742                 /* configure the package listed in the status file;
743                  * not pkg, as we have info only for the latter
744                  */
745                 else {
746                         r = dpkg_doconfigure(*(package_t **)found);
747                 }
748         }
749         status_merge(status, 0);
750
751         return r;
752 }
753
754 static int dpkg_install(package_t *pkgs, void *status)
755 {
756         package_t *p, *ordered = 0;
757
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);
762                 }
763         }
764
765         /* Stage 2: resolve dependencies */
766 #ifdef DODEPENDS
767         ordered = depends_resolve(pkgs, status);
768 #else
769         ordered = pkgs;
770 #endif
771         
772         /* Stage 3: install */
773         for (p = ordered; p != 0; p = p->next) {
774                 p->status &= status_wantmask;
775                 p->status |= status_wantinstall;
776
777                 /* for now the flag is always set to ok... this is probably
778                  * not what we want
779                  */
780                 p->status &= status_flagmask;
781                 p->status |= status_flagok;
782
783                 DPRINTF("Installing %s\n", p->package);
784                 if (dpkg_dounpack(p) != 0) {
785                         perror_msg(p->file);
786                 }
787                 if (dpkg_doconfigure(p) != 0) {
788                         perror_msg(p->file);
789                 }
790         }
791
792         if (ordered != 0) {
793                 status_merge(status, pkgs);
794         }
795         remove_dir(dpkgcidir);
796
797         return 0;
798 }
799
800 /*
801  * Not implemented yet
802  *
803 static int dpkg_remove(package_t *pkgs, void *status)
804 {
805         package_t *p;
806
807         for (p = pkgs; p != 0; p = p->next)
808         {
809         }
810         status_merge(status, 0);
811
812         return 0;
813 }
814 */
815
816 extern int dpkg_main(int argc, char **argv)
817 {
818         const int arg_install = 1;
819         const int arg_unpack = 2;
820         const int arg_configure = 4;
821
822         package_t *p, *packages = NULL;
823         void *status = NULL;
824         char opt = 0;
825         int optflag = 0;
826
827         while ((opt = getopt(argc, argv, "iruc")) != -1) {
828                 switch (opt) {
829                         case 'i':
830                                 optflag |= arg_install;
831                                 break;
832                         case 'u':
833                                 optflag |= arg_unpack;
834                                 break;
835                         case 'c':
836                                 optflag |= arg_configure;
837                                 break;
838                         default:
839                                 show_usage();
840                 }
841         }
842
843         while (optind < argc) {
844                 p = (package_t *) xcalloc(1, sizeof(package_t));
845                 if (optflag & arg_configure) {
846                         p->package = xstrdup(argv[optind]);
847                 } else {
848                         p->file = xstrdup(argv[optind]);
849                 }
850                 p->next = packages;
851                 packages = p;
852
853                 optind++;
854         }
855
856         create_path(dpkgcidir, 0755);
857         create_path(infodir, 0755);
858
859         status = status_read();
860
861         if (optflag & arg_install) {
862                 return dpkg_install(packages, status);
863         }
864         else if (optflag & arg_unpack) {
865                 return dpkg_unpack(packages, status);
866         }
867         else if (optflag & arg_configure) {
868                 return dpkg_configure(packages, status);
869         }
870         return(EXIT_FAILURE);
871 }