source: MondoRescue/branches/3.3/mindi-busybox/archival/dpkg.c@ 3626

Last change on this file since 3626 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 59.8 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * mini dpkg implementation for busybox.
4 * this is not meant as a replacement for dpkg
5 *
6 * written by glenn mcgrath with the help of others
7 * copyright (c) 2001 by glenn mcgrath
8 *
9 * parts of the version comparison code is plucked from the real dpkg
10 * application which is licensed GPLv2 and
11 * copyright (c) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
12 *
13 * started life as a busybox implementation of udpkg
14 *
15 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
16 */
17/*
18 * known difference between busybox dpkg and the official dpkg that i don't
19 * consider important, its worth keeping a note of differences anyway, just to
20 * make it easier to maintain.
21 * - the first value for the confflile: field isnt placed on a new line.
22 * - when installing a package the status: field is placed at the end of the
23 * section, rather than just after the package: field.
24 *
25 * bugs that need to be fixed
26 * - (unknown, please let me know when you find any)
27 */
28
29//config:config DPKG
30//config: bool "dpkg"
31//config: default n
32//config: select FEATURE_SEAMLESS_GZ
33//config: help
34//config: dpkg is a medium-level tool to install, build, remove and manage
35//config: Debian packages.
36//config:
37//config: This implementation of dpkg has a number of limitations,
38//config: you should use the official dpkg if possible.
39
40//applet:IF_DPKG(APPLET(dpkg, BB_DIR_USR_BIN, BB_SUID_DROP))
41//kbuild:lib-$(CONFIG_DPKG) += dpkg.o
42
43//usage:#define dpkg_trivial_usage
44//usage: "[-ilCPru] [-F OPT] PACKAGE"
45//usage:#define dpkg_full_usage "\n\n"
46//usage: "Install, remove and manage Debian packages\n"
47//usage: IF_LONG_OPTS(
48//usage: "\n -i,--install Install the package"
49//usage: "\n -l,--list List of installed packages"
50//usage: "\n --configure Configure an unpackaged package"
51//usage: "\n -P,--purge Purge all files of a package"
52//usage: "\n -r,--remove Remove all but the configuration files for a package"
53//usage: "\n --unpack Unpack a package, but don't configure it"
54//usage: "\n --force-depends Ignore dependency problems"
55//usage: "\n --force-confnew Overwrite existing config files when installing"
56//usage: "\n --force-confold Keep old config files when installing"
57//usage: )
58//usage: IF_NOT_LONG_OPTS(
59//usage: "\n -i Install the package"
60//usage: "\n -l List of installed packages"
61//usage: "\n -C Configure an unpackaged package"
62//usage: "\n -P Purge all files of a package"
63//usage: "\n -r Remove all but the configuration files for a package"
64//usage: "\n -u Unpack a package, but don't configure it"
65//usage: "\n -F depends Ignore dependency problems"
66//usage: "\n -F confnew Overwrite existing config files when installing"
67//usage: "\n -F confold Keep old config files when installing"
68//usage: )
69
70#include "libbb.h"
71#include <fnmatch.h>
72#include "bb_archive.h"
73
74/* note: if you vary hash_prime sizes be aware,
75 * 1) tweaking these will have a big effect on how much memory this program uses.
76 * 2) for computational efficiency these hash tables should be at least 20%
77 * larger than the maximum number of elements stored in it.
78 * 3) all _hash_prime's must be a prime number or chaos is assured, if your looking
79 * for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt
80 * 4) if you go bigger than 15 bits you may get into trouble (untested) as its
81 * sometimes cast to an unsigned, if you go to 16 bit you will overlap
82 * int's and chaos is assured, 16381 is the max prime for 14 bit field
83 */
84
85/* NAME_HASH_PRIME, Stores package names and versions,
86 * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME,
87 * as there a lot of duplicate version numbers */
88#define NAME_HASH_PRIME 16381
89
90/* PACKAGE_HASH_PRIME, Maximum number of unique packages,
91 * It must not be smaller than STATUS_HASH_PRIME,
92 * Currently only packages from status_hashtable are stored in here, but in
93 * future this may be used to store packages not only from a status file,
94 * but an available_hashtable, and even multiple packages files.
95 * Package can be stored more than once if they have different versions.
96 * e.g. The same package may have different versions in the status file
97 * and available file */
98#define PACKAGE_HASH_PRIME 10007
99typedef struct edge_s {
100 unsigned operator:4; /* was:3 */
101 unsigned type:4;
102 unsigned name:16; /* was:14 */
103 unsigned version:16; /* was:14 */
104} edge_t;
105
106typedef struct common_node_s {
107 unsigned name:16; /* was:14 */
108 unsigned version:16; /* was:14 */
109 unsigned num_of_edges:16; /* was:14 */
110 edge_t **edge;
111} common_node_t;
112
113/* Currently it doesnt store packages that have state-status of not-installed
114 * So it only really has to be the size of the maximum number of packages
115 * likely to be installed at any one time, so there is a bit of leeway here */
116#define STATUS_HASH_PRIME 8191
117typedef struct status_node_s {
118 unsigned package:16; /* was:14 */ /* has to fit PACKAGE_HASH_PRIME */
119 unsigned status:16; /* was:14 */ /* has to fit STATUS_HASH_PRIME */
120} status_node_t;
121
122
123/* Globals */
124struct globals {
125 char *name_hashtable[NAME_HASH_PRIME + 1];
126 common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1];
127 status_node_t *status_hashtable[STATUS_HASH_PRIME + 1];
128};
129#define G (*ptr_to_globals)
130#define name_hashtable (G.name_hashtable )
131#define package_hashtable (G.package_hashtable)
132#define status_hashtable (G.status_hashtable )
133#define INIT_G() do { \
134 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
135} while (0)
136
137
138/* Even numbers are for 'extras', like ored dependencies or null */
139enum edge_type_e {
140 EDGE_NULL = 0,
141 EDGE_PRE_DEPENDS = 1,
142 EDGE_OR_PRE_DEPENDS = 2,
143 EDGE_DEPENDS = 3,
144 EDGE_OR_DEPENDS = 4,
145 EDGE_REPLACES = 5,
146 EDGE_PROVIDES = 7,
147 EDGE_CONFLICTS = 9,
148 EDGE_SUGGESTS = 11,
149 EDGE_RECOMMENDS = 13,
150 EDGE_ENHANCES = 15
151};
152enum operator_e {
153 VER_NULL = 0,
154 VER_EQUAL = 1,
155 VER_LESS = 2,
156 VER_LESS_EQUAL = 3,
157 VER_MORE = 4,
158 VER_MORE_EQUAL = 5,
159 VER_ANY = 6
160};
161
162typedef struct deb_file_s {
163 char *control_file;
164 char *filename;
165 unsigned package:16; /* was:14 */
166} deb_file_t;
167
168
169static void make_hash(const char *key, unsigned *start, unsigned *decrement, const int hash_prime)
170{
171 unsigned long hash_num = key[0];
172 int len = strlen(key);
173 int i;
174
175 /* Maybe i should have uses a "proper" hashing algorithm here instead
176 * of making one up myself, seems to be working ok though. */
177 for (i = 1; i < len; i++) {
178 /* shifts the ascii based value and adds it to previous value
179 * shift amount is mod 24 because long int is 32 bit and data
180 * to be shifted is 8, don't want to shift data to where it has
181 * no effect */
182 hash_num += (key[i] + key[i-1]) << ((key[i] * i) % 24);
183 }
184 *start = (unsigned) hash_num % hash_prime;
185 *decrement = (unsigned) 1 + (hash_num % (hash_prime - 1));
186}
187
188/* this adds the key to the hash table */
189static int search_name_hashtable(const char *key)
190{
191 unsigned probe_address;
192 unsigned probe_decrement;
193
194 make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME);
195 while (name_hashtable[probe_address] != NULL) {
196 if (strcmp(name_hashtable[probe_address], key) == 0) {
197 return probe_address;
198 }
199 probe_address -= probe_decrement;
200 if ((int)probe_address < 0) {
201 probe_address += NAME_HASH_PRIME;
202 }
203 }
204 name_hashtable[probe_address] = xstrdup(key);
205 return probe_address;
206}
207
208/* this DOESNT add the key to the hashtable
209 * TODO make it consistent with search_name_hashtable
210 */
211static unsigned search_status_hashtable(const char *key)
212{
213 unsigned probe_address;
214 unsigned probe_decrement;
215
216 make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME);
217 while (status_hashtable[probe_address] != NULL) {
218 if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) {
219 break;
220 }
221 probe_address -= probe_decrement;
222 if ((int)probe_address < 0) {
223 probe_address += STATUS_HASH_PRIME;
224 }
225 }
226 return probe_address;
227}
228
229static int order(char x)
230{
231 return (x == '~' ? -1
232 : x == '\0' ? 0
233 : isdigit(x) ? 0
234 : isalpha(x) ? x
235 : (unsigned char)x + 256
236 );
237}
238
239/* This code is taken from dpkg and modified slightly to work with busybox */
240static int version_compare_part(const char *val, const char *ref)
241{
242 if (!val) val = "";
243 if (!ref) ref = "";
244
245 while (*val || *ref) {
246 int first_diff;
247
248 while ((*val && !isdigit(*val)) || (*ref && !isdigit(*ref))) {
249 int vc = order(*val);
250 int rc = order(*ref);
251 if (vc != rc)
252 return vc - rc;
253 val++;
254 ref++;
255 }
256
257 while (*val == '0')
258 val++;
259 while (*ref == '0')
260 ref++;
261
262 first_diff = 0;
263 while (isdigit(*val) && isdigit(*ref)) {
264 if (first_diff == 0)
265 first_diff = *val - *ref;
266 val++;
267 ref++;
268 }
269 if (isdigit(*val))
270 return 1;
271 if (isdigit(*ref))
272 return -1;
273 if (first_diff)
274 return first_diff;
275 }
276 return 0;
277}
278
279/* if ver1 < ver2 return -1,
280 * if ver1 = ver2 return 0,
281 * if ver1 > ver2 return 1,
282 */
283static int version_compare(const unsigned ver1, const unsigned ver2)
284{
285 char *ch_ver1 = name_hashtable[ver1];
286 char *ch_ver2 = name_hashtable[ver2];
287 unsigned epoch1 = 0, epoch2 = 0;
288 char *colon;
289 char *deb_ver1, *deb_ver2;
290 char *upstream_ver1;
291 char *upstream_ver2;
292 int result;
293
294 /* Compare epoch */
295 colon = strchr(ch_ver1, ':');
296 if (colon) {
297 epoch1 = atoi(ch_ver1);
298 ch_ver1 = colon + 1;
299 }
300 colon = strchr(ch_ver2, ':');
301 if (colon) {
302 epoch2 = atoi(ch_ver2);
303 ch_ver2 = colon + 1;
304 }
305 if (epoch1 < epoch2) {
306 return -1;
307 }
308 if (epoch1 > epoch2) {
309 return 1;
310 }
311
312 /* Compare upstream version */
313 upstream_ver1 = xstrdup(ch_ver1);
314 upstream_ver2 = xstrdup(ch_ver2);
315
316 /* Chop off debian version, and store for later use */
317 deb_ver1 = strrchr(upstream_ver1, '-');
318 deb_ver2 = strrchr(upstream_ver2, '-');
319 if (deb_ver1) {
320 *deb_ver1++ = '\0';
321 }
322 if (deb_ver2) {
323 *deb_ver2++ = '\0';
324 }
325 result = version_compare_part(upstream_ver1, upstream_ver2);
326 if (result == 0) {
327 /* Compare debian versions */
328 result = version_compare_part(deb_ver1, deb_ver2);
329 }
330
331 free(upstream_ver1);
332 free(upstream_ver2);
333 return result;
334}
335
336static int test_version(const unsigned version1, const unsigned version2, const unsigned operator)
337{
338 const int version_result = version_compare(version1, version2);
339 switch (operator) {
340 case VER_ANY:
341 return TRUE;
342 case VER_EQUAL:
343 return (version_result == 0);
344 case VER_LESS:
345 return (version_result < 0);
346 case VER_LESS_EQUAL:
347 return (version_result <= 0);
348 case VER_MORE:
349 return (version_result > 0);
350 case VER_MORE_EQUAL:
351 return (version_result >= 0);
352 }
353 return FALSE;
354}
355
356static int search_package_hashtable(const unsigned name, const unsigned version, const unsigned operator)
357{
358 unsigned probe_address;
359 unsigned probe_decrement;
360
361 make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME);
362 while (package_hashtable[probe_address] != NULL) {
363 if (package_hashtable[probe_address]->name == name) {
364 if (operator == VER_ANY) {
365 return probe_address;
366 }
367 if (test_version(package_hashtable[probe_address]->version, version, operator)) {
368 return probe_address;
369 }
370 }
371 probe_address -= probe_decrement;
372 if ((int)probe_address < 0) {
373 probe_address += PACKAGE_HASH_PRIME;
374 }
375 }
376 return probe_address;
377}
378
379/*
380 * This function searches through the entire package_hashtable looking
381 * for a package which provides "needle". It returns the index into
382 * the package_hashtable for the providing package.
383 *
384 * needle is the index into name_hashtable of the package we are
385 * looking for.
386 *
387 * start_at is the index in the package_hashtable to start looking
388 * at. If start_at is -1 then start at the beginning. This is to allow
389 * for repeated searches since more than one package might provide
390 * needle.
391 *
392 * FIXME: I don't think this is very efficient, but I thought I'd keep
393 * it simple for now until it proves to be a problem.
394 */
395static int search_for_provides(int needle, int start_at)
396{
397 int i, j;
398 common_node_t *p;
399 for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) {
400 p = package_hashtable[i];
401 if (p == NULL)
402 continue;
403 for (j = 0; j < p->num_of_edges; j++)
404 if (p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle)
405 return i;
406 }
407 return -1;
408}
409
410/*
411 * Add an edge to a node
412 */
413static void add_edge_to_node(common_node_t *node, edge_t *edge)
414{
415 node->edge = xrealloc_vector(node->edge, 2, node->num_of_edges);
416 node->edge[node->num_of_edges++] = edge;
417}
418
419/*
420 * Create one new node and one new edge for every dependency.
421 *
422 * Dependencies which contain multiple alternatives are represented as
423 * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a
424 * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of
425 * the OR edge contains the full dependency string while the version
426 * field contains the number of EDGE nodes which follow as part of
427 * this alternative.
428 */
429static void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned edge_type)
430{
431 char *line = xstrdup(whole_line);
432 char *line2;
433 char *line_ptr1 = NULL;
434 char *line_ptr2 = NULL;
435 char *field;
436 char *field2;
437 char *version;
438 edge_t *edge;
439 edge_t *or_edge;
440 int offset_ch;
441
442 field = strtok_r(line, ",", &line_ptr1);
443 do {
444 /* skip leading spaces */
445 field += strspn(field, " ");
446 line2 = xstrdup(field);
447 field2 = strtok_r(line2, "|", &line_ptr2);
448 or_edge = NULL;
449 if ((edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS)
450 && (strcmp(field, field2) != 0)
451 ) {
452 or_edge = xzalloc(sizeof(edge_t));
453 or_edge->type = edge_type + 1;
454 or_edge->name = search_name_hashtable(field);
455 //or_edge->version = 0; // tracks the number of alternatives
456 add_edge_to_node(parent_node, or_edge);
457 }
458
459 do {
460 edge = xmalloc(sizeof(edge_t));
461 edge->type = edge_type;
462
463 /* Skip any extra leading spaces */
464 field2 += strspn(field2, " ");
465
466 /* Get dependency version info */
467 version = strchr(field2, '(');
468 if (version == NULL) {
469 edge->operator = VER_ANY;
470 /* Get the versions hash number, adding it if the number isnt already in there */
471 edge->version = search_name_hashtable("ANY");
472 } else {
473 /* Skip leading ' ' or '(' */
474 version += strspn(version, " (");
475 /* Calculate length of any operator characters */
476 offset_ch = strspn(version, "<=>");
477 /* Determine operator */
478 if (offset_ch > 0) {
479 if (strncmp(version, "=", offset_ch) == 0) {
480 edge->operator = VER_EQUAL;
481 } else if (strncmp(version, "<<", offset_ch) == 0) {
482 edge->operator = VER_LESS;
483 } else if (strncmp(version, "<=", offset_ch) == 0) {
484 edge->operator = VER_LESS_EQUAL;
485 } else if (strncmp(version, ">>", offset_ch) == 0) {
486 edge->operator = VER_MORE;
487 } else if (strncmp(version, ">=", offset_ch) == 0) {
488 edge->operator = VER_MORE_EQUAL;
489 } else {
490 bb_error_msg_and_die("illegal operator");
491 }
492 }
493 /* skip to start of version numbers */
494 version += offset_ch;
495 version += strspn(version, " ");
496
497 /* Truncate version at trailing ' ' or ')' */
498 version[strcspn(version, " )")] = '\0';
499 /* Get the versions hash number, adding it if the number isnt already in there */
500 edge->version = search_name_hashtable(version);
501 }
502
503 /* Get the dependency name */
504 field2[strcspn(field2, " (")] = '\0';
505 edge->name = search_name_hashtable(field2);
506
507 if (or_edge)
508 or_edge->version++;
509
510 add_edge_to_node(parent_node, edge);
511 field2 = strtok_r(NULL, "|", &line_ptr2);
512 } while (field2 != NULL);
513
514 free(line2);
515 field = strtok_r(NULL, ",", &line_ptr1);
516 } while (field != NULL);
517
518 free(line);
519}
520
521static void free_package(common_node_t *node)
522{
523 unsigned i;
524 if (node) {
525 for (i = 0; i < node->num_of_edges; i++) {
526 free(node->edge[i]);
527 }
528 free(node->edge);
529 free(node);
530 }
531}
532
533/*
534 * Gets the next package field from package_buffer, separated into the field name
535 * and field value, it returns the int offset to the first character of the next field
536 */
537static int read_package_field(const char *package_buffer, char **field_name, char **field_value)
538{
539 int offset_name_start = 0;
540 int offset_name_end = 0;
541 int offset_value_start = 0;
542 int offset_value_end = 0;
543 int offset = 0;
544 int next_offset;
545 int name_length;
546 int value_length;
547 int exit_flag = FALSE;
548
549 if (package_buffer == NULL) {
550 *field_name = NULL;
551 *field_value = NULL;
552 return -1;
553 }
554 while (1) {
555 next_offset = offset + 1;
556 switch (package_buffer[offset]) {
557 case '\0':
558 exit_flag = TRUE;
559 break;
560 case ':':
561 if (offset_name_end == 0) {
562 offset_name_end = offset;
563 offset_value_start = next_offset;
564 }
565 /* TODO: Name might still have trailing spaces if ':' isnt
566 * immediately after name */
567 break;
568 case '\n':
569 /* TODO: The char next_offset may be out of bounds */
570 if (package_buffer[next_offset] != ' ') {
571 exit_flag = TRUE;
572 break;
573 }
574 case '\t':
575 case ' ':
576 /* increment the value start point if its a just filler */
577 if (offset_name_start == offset) {
578 offset_name_start++;
579 }
580 if (offset_value_start == offset) {
581 offset_value_start++;
582 }
583 break;
584 }
585 if (exit_flag) {
586 /* Check that the names are valid */
587 offset_value_end = offset;
588 name_length = offset_name_end - offset_name_start;
589 value_length = offset_value_end - offset_value_start;
590 if (name_length == 0) {
591 break;
592 }
593 if ((name_length > 0) && (value_length > 0)) {
594 break;
595 }
596
597 /* If not valid, start fresh with next field */
598 exit_flag = FALSE;
599 offset_name_start = offset + 1;
600 offset_name_end = 0;
601 offset_value_start = offset + 1;
602 offset_value_end = offset + 1;
603 offset++;
604 }
605 offset++;
606 }
607 *field_name = NULL;
608 if (name_length) {
609 *field_name = xstrndup(&package_buffer[offset_name_start], name_length);
610 }
611 *field_value = NULL;
612 if (value_length > 0) {
613 *field_value = xstrndup(&package_buffer[offset_value_start], value_length);
614 }
615 return next_offset;
616}
617
618static unsigned fill_package_struct(char *control_buffer)
619{
620 static const char field_names[] ALIGN1 =
621 "Package\0""Version\0"
622 "Pre-Depends\0""Depends\0""Replaces\0""Provides\0"
623 "Conflicts\0""Suggests\0""Recommends\0""Enhances\0";
624
625 common_node_t *new_node = xzalloc(sizeof(common_node_t));
626 char *field_name;
627 char *field_value;
628 int field_start = 0;
629 int num = -1;
630 int buffer_length = strlen(control_buffer);
631
632 new_node->version = search_name_hashtable("unknown");
633 while (field_start < buffer_length) {
634 unsigned field_num;
635
636 field_start += read_package_field(&control_buffer[field_start],
637 &field_name, &field_value);
638
639 if (field_name == NULL) {
640 goto fill_package_struct_cleanup;
641 }
642
643 field_num = index_in_strings(field_names, field_name);
644 switch (field_num) {
645 case 0: /* Package */
646 new_node->name = search_name_hashtable(field_value);
647 break;
648 case 1: /* Version */
649 new_node->version = search_name_hashtable(field_value);
650 break;
651 case 2: /* Pre-Depends */
652 add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS);
653 break;
654 case 3: /* Depends */
655 add_split_dependencies(new_node, field_value, EDGE_DEPENDS);
656 break;
657 case 4: /* Replaces */
658 add_split_dependencies(new_node, field_value, EDGE_REPLACES);
659 break;
660 case 5: /* Provides */
661 add_split_dependencies(new_node, field_value, EDGE_PROVIDES);
662 break;
663 case 6: /* Conflicts */
664 add_split_dependencies(new_node, field_value, EDGE_CONFLICTS);
665 break;
666 case 7: /* Suggests */
667 add_split_dependencies(new_node, field_value, EDGE_SUGGESTS);
668 break;
669 case 8: /* Recommends */
670 add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS);
671 break;
672 case 9: /* Enhances */
673 add_split_dependencies(new_node, field_value, EDGE_ENHANCES);
674 break;
675 }
676 fill_package_struct_cleanup:
677 free(field_name);
678 free(field_value);
679 }
680
681 if (new_node->version == search_name_hashtable("unknown")) {
682 free_package(new_node);
683 return -1;
684 }
685 num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL);
686 free_package(package_hashtable[num]);
687 package_hashtable[num] = new_node;
688 return num;
689}
690
691/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */
692static unsigned get_status(const unsigned status_node, const int num)
693{
694 char *status_string = name_hashtable[status_hashtable[status_node]->status];
695 char *state_sub_string;
696 unsigned state_sub_num;
697 int len;
698 int i;
699
700 /* set tmp_string to point to the start of the word number */
701 for (i = 1; i < num; i++) {
702 /* skip past a word */
703 status_string += strcspn(status_string, " ");
704 /* skip past the separating spaces */
705 status_string += strspn(status_string, " ");
706 }
707 len = strcspn(status_string, " \n");
708 state_sub_string = xstrndup(status_string, len);
709 state_sub_num = search_name_hashtable(state_sub_string);
710 free(state_sub_string);
711 return state_sub_num;
712}
713
714static void set_status(const unsigned status_node_num, const char *new_value, const int position)
715{
716 const unsigned new_value_num = search_name_hashtable(new_value);
717 unsigned want = get_status(status_node_num, 1);
718 unsigned flag = get_status(status_node_num, 2);
719 unsigned status = get_status(status_node_num, 3);
720 char *new_status;
721
722 switch (position) {
723 case 1:
724 want = new_value_num;
725 break;
726 case 2:
727 flag = new_value_num;
728 break;
729 case 3:
730 status = new_value_num;
731 break;
732 default:
733 bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen");
734 }
735
736 new_status = xasprintf("%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]);
737 status_hashtable[status_node_num]->status = search_name_hashtable(new_status);
738 free(new_status);
739}
740
741static const char *describe_status(int status_num)
742{
743 int status_want, status_state;
744 if (status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0)
745 return "is not installed or flagged to be installed";
746
747 status_want = get_status(status_num, 1);
748 status_state = get_status(status_num, 3);
749
750 if (status_state == search_name_hashtable("installed")) {
751 if (status_want == search_name_hashtable("install"))
752 return "is installed";
753 if (status_want == search_name_hashtable("deinstall"))
754 return "is marked to be removed";
755 if (status_want == search_name_hashtable("purge"))
756 return "is marked to be purged";
757 }
758 if (status_want == search_name_hashtable("unknown"))
759 return "is in an indeterminate state";
760 if (status_want == search_name_hashtable("install"))
761 return "is marked to be installed";
762
763 return "is not installed or flagged to be installed";
764}
765
766static void index_status_file(const char *filename)
767{
768 FILE *status_file;
769 char *control_buffer;
770 char *status_line;
771 status_node_t *status_node = NULL;
772 unsigned status_num;
773
774 status_file = xfopen_for_read(filename);
775 while ((control_buffer = xmalloc_fgetline_str(status_file, "\n\n")) != NULL) {
776 const unsigned package_num = fill_package_struct(control_buffer);
777 if (package_num != -1) {
778 status_node = xmalloc(sizeof(status_node_t));
779 /* fill_package_struct doesnt handle the status field */
780 status_line = strstr(control_buffer, "Status:");
781 if (status_line != NULL) {
782 status_line += 7;
783 status_line += strspn(status_line, " \n\t");
784 status_line = xstrndup(status_line, strcspn(status_line, "\n"));
785 status_node->status = search_name_hashtable(status_line);
786 free(status_line);
787 }
788 status_node->package = package_num;
789 status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]);
790 status_hashtable[status_num] = status_node;
791 }
792 free(control_buffer);
793 }
794 fclose(status_file);
795}
796
797static void write_buffer_no_status(FILE *new_status_file, const char *control_buffer)
798{
799 char *name;
800 char *value;
801 int start = 0;
802 while (1) {
803 start += read_package_field(&control_buffer[start], &name, &value);
804 if (name == NULL) {
805 break;
806 }
807 if (strcmp(name, "Status") != 0) {
808 fprintf(new_status_file, "%s: %s\n", name, value);
809 }
810 }
811}
812
813/* This could do with a cleanup */
814static void write_status_file(deb_file_t **deb_file)
815{
816 FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status");
817 FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb");
818 char *package_name;
819 char *status_from_file;
820 char *control_buffer = NULL;
821 char *tmp_string;
822 int status_num;
823 int field_start = 0;
824 int write_flag;
825 int i = 0;
826
827 /* Update previously known packages */
828 while ((control_buffer = xmalloc_fgetline_str(old_status_file, "\n\n")) != NULL) {
829 tmp_string = strstr(control_buffer, "Package:");
830 if (tmp_string == NULL) {
831 continue;
832 }
833
834 tmp_string += 8;
835 tmp_string += strspn(tmp_string, " \n\t");
836 package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
837 write_flag = FALSE;
838 tmp_string = strstr(control_buffer, "Status:");
839 if (tmp_string != NULL) {
840 /* Separate the status value from the control buffer */
841 tmp_string += 7;
842 tmp_string += strspn(tmp_string, " \n\t");
843 status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
844 } else {
845 status_from_file = NULL;
846 }
847
848 /* Find this package in the status hashtable */
849 status_num = search_status_hashtable(package_name);
850 if (status_hashtable[status_num] != NULL) {
851 const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status];
852 if (strcmp(status_from_file, status_from_hashtable) != 0) {
853 /* New status isnt exactly the same as old status */
854 const int state_status = get_status(status_num, 3);
855 if ((strcmp("installed", name_hashtable[state_status]) == 0)
856 || (strcmp("unpacked", name_hashtable[state_status]) == 0)
857 ) {
858 /* We need to add the control file from the package */
859 i = 0;
860 while (deb_file[i] != NULL) {
861 if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) {
862 /* Write a status file entry with a modified status */
863 /* remove trailing \n's */
864 write_buffer_no_status(new_status_file, deb_file[i]->control_file);
865 set_status(status_num, "ok", 2);
866 fprintf(new_status_file, "Status: %s\n\n",
867 name_hashtable[status_hashtable[status_num]->status]);
868 write_flag = TRUE;
869 break;
870 }
871 i++;
872 }
873 /* This is temperary, debugging only */
874 if (deb_file[i] == NULL) {
875 bb_error_msg_and_die("ALERT: cannot find a control file, "
876 "your status file may be broken, status may be "
877 "incorrect for %s", package_name);
878 }
879 }
880 else if (strcmp("not-installed", name_hashtable[state_status]) == 0) {
881 /* Only write the Package, Status, Priority and Section lines */
882 fprintf(new_status_file, "Package: %s\n", package_name);
883 fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
884
885 while (1) {
886 char *field_name;
887 char *field_value;
888 field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
889 if (field_name == NULL) {
890 break;
891 }
892 if ((strcmp(field_name, "Priority") == 0)
893 || (strcmp(field_name, "Section") == 0)
894 ) {
895 fprintf(new_status_file, "%s: %s\n", field_name, field_value);
896 }
897 }
898 write_flag = TRUE;
899 fputs("\n", new_status_file);
900 }
901 else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
902 /* only change the status line */
903 while (1) {
904 char *field_name;
905 char *field_value;
906 field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
907 if (field_name == NULL) {
908 break;
909 }
910 /* Setup start point for next field */
911 if (strcmp(field_name, "Status") == 0) {
912 fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
913 } else {
914 fprintf(new_status_file, "%s: %s\n", field_name, field_value);
915 }
916 }
917 write_flag = TRUE;
918 fputs("\n", new_status_file);
919 }
920 }
921 }
922 /* If the package from the status file wasnt handle above, do it now*/
923 if (!write_flag) {
924 fprintf(new_status_file, "%s\n\n", control_buffer);
925 }
926
927 free(status_from_file);
928 free(package_name);
929 free(control_buffer);
930 }
931
932 /* Write any new packages */
933 for (i = 0; deb_file[i] != NULL; i++) {
934 status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]);
935 if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) {
936 write_buffer_no_status(new_status_file, deb_file[i]->control_file);
937 set_status(status_num, "ok", 2);
938 fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
939 }
940 }
941 fclose(old_status_file);
942 fclose(new_status_file);
943
944 /* Create a separate backfile to dpkg */
945 if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) {
946 if (errno != ENOENT)
947 bb_error_msg_and_die("can't create backup status file");
948 /* Its ok if renaming the status file fails because status
949 * file doesnt exist, maybe we are starting from scratch */
950 bb_error_msg("no status file found, creating new one");
951 }
952
953 xrename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status");
954}
955
956/* This function returns TRUE if the given package can satisfy a
957 * dependency of type depend_type.
958 *
959 * A pre-depends is satisfied only if a package is already installed,
960 * which a regular depends can be satisfied by a package which we want
961 * to install.
962 */
963static int package_satisfies_dependency(int package, int depend_type)
964{
965 int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]);
966
967 /* status could be unknown if package is a pure virtual
968 * provides which cannot satisfy any dependency by itself.
969 */
970 if (status_hashtable[status_num] == NULL)
971 return 0;
972
973 switch (depend_type) {
974 case EDGE_PRE_DEPENDS: return get_status(status_num, 3) == search_name_hashtable("installed");
975 case EDGE_DEPENDS: return get_status(status_num, 1) == search_name_hashtable("install");
976 }
977 return 0;
978}
979
980static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count - ?? */)
981{
982 int *conflicts = NULL;
983 int conflicts_num = 0;
984 int i = deb_start;
985 int j;
986
987 /* Check for conflicts
988 * TODO: TEST if conflicts with other packages to be installed
989 *
990 * Add install packages and the packages they provide
991 * to the list of files to check conflicts for
992 */
993
994 /* Create array of package numbers to check against
995 * installed package for conflicts*/
996 while (deb_file[i] != NULL) {
997 const unsigned package_num = deb_file[i]->package;
998 conflicts = xrealloc_vector(conflicts, 2, conflicts_num);
999 conflicts[conflicts_num] = package_num;
1000 conflicts_num++;
1001 /* add provides to conflicts list */
1002 for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) {
1003 if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) {
1004 const int conflicts_package_num = search_package_hashtable(
1005 package_hashtable[package_num]->edge[j]->name,
1006 package_hashtable[package_num]->edge[j]->version,
1007 package_hashtable[package_num]->edge[j]->operator);
1008 if (package_hashtable[conflicts_package_num] == NULL) {
1009 /* create a new package */
1010 common_node_t *new_node = xzalloc(sizeof(common_node_t));
1011 new_node->name = package_hashtable[package_num]->edge[j]->name;
1012 new_node->version = package_hashtable[package_num]->edge[j]->version;
1013 package_hashtable[conflicts_package_num] = new_node;
1014 }
1015 conflicts = xrealloc_vector(conflicts, 2, conflicts_num);
1016 conflicts[conflicts_num] = conflicts_package_num;
1017 conflicts_num++;
1018 }
1019 }
1020 i++;
1021 }
1022
1023 /* Check conflicts */
1024 i = 0;
1025 while (deb_file[i] != NULL) {
1026 const common_node_t *package_node = package_hashtable[deb_file[i]->package];
1027 int status_num = 0;
1028 status_num = search_status_hashtable(name_hashtable[package_node->name]);
1029
1030 if (get_status(status_num, 3) == search_name_hashtable("installed")) {
1031 i++;
1032 continue;
1033 }
1034
1035 for (j = 0; j < package_node->num_of_edges; j++) {
1036 const edge_t *package_edge = package_node->edge[j];
1037
1038 if (package_edge->type == EDGE_CONFLICTS) {
1039 const unsigned package_num =
1040 search_package_hashtable(package_edge->name,
1041 package_edge->version,
1042 package_edge->operator);
1043 int result = 0;
1044 if (package_hashtable[package_num] != NULL) {
1045 status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
1046
1047 if (get_status(status_num, 1) == search_name_hashtable("install")) {
1048 result = test_version(package_hashtable[deb_file[i]->package]->version,
1049 package_edge->version, package_edge->operator);
1050 }
1051 }
1052
1053 if (result) {
1054 bb_error_msg_and_die("package %s conflicts with %s",
1055 name_hashtable[package_node->name],
1056 name_hashtable[package_edge->name]);
1057 }
1058 }
1059 }
1060 i++;
1061 }
1062
1063
1064 /* Check dependendcies */
1065 for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
1066 int status_num = 0;
1067 int number_of_alternatives = 0;
1068 const edge_t * root_of_alternatives = NULL;
1069 const common_node_t *package_node = package_hashtable[i];
1070
1071 /* If the package node does not exist then this
1072 * package is a virtual one. In which case there are
1073 * no dependencies to check.
1074 */
1075 if (package_node == NULL) continue;
1076
1077 status_num = search_status_hashtable(name_hashtable[package_node->name]);
1078
1079 /* If there is no status then this package is a
1080 * virtual one provided by something else. In which
1081 * case there are no dependencies to check.
1082 */
1083 if (status_hashtable[status_num] == NULL) continue;
1084
1085 /* If we don't want this package installed then we may
1086 * as well ignore it's dependencies.
1087 */
1088 if (get_status(status_num, 1) != search_name_hashtable("install")) {
1089 continue;
1090 }
1091
1092 /* This code is tested only for EDGE_DEPENDS, since I
1093 * have no suitable pre-depends available. There is no
1094 * reason that it shouldn't work though :-)
1095 */
1096 for (j = 0; j < package_node->num_of_edges; j++) {
1097 const edge_t *package_edge = package_node->edge[j];
1098 unsigned package_num;
1099
1100 if (package_edge->type == EDGE_OR_PRE_DEPENDS
1101 || package_edge->type == EDGE_OR_DEPENDS
1102 ) {
1103 /* start an EDGE_OR_ list */
1104 number_of_alternatives = package_edge->version;
1105 root_of_alternatives = package_edge;
1106 continue;
1107 }
1108 if (number_of_alternatives == 0) { /* not in the middle of an EDGE_OR_ list */
1109 number_of_alternatives = 1;
1110 root_of_alternatives = NULL;
1111 }
1112
1113 package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator);
1114
1115 if (package_edge->type == EDGE_PRE_DEPENDS
1116 || package_edge->type == EDGE_DEPENDS
1117 ) {
1118 int result=1;
1119 status_num = 0;
1120
1121 /* If we are inside an alternative then check
1122 * this edge is the right type.
1123 *
1124 * EDGE_DEPENDS == OR_DEPENDS -1
1125 * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1
1126 */
1127 if (root_of_alternatives && package_edge->type != root_of_alternatives->type - 1)
1128 bb_error_msg_and_die("fatal error, package dependencies corrupt: %d != %d - 1",
1129 package_edge->type, root_of_alternatives->type);
1130
1131 if (package_hashtable[package_num] != NULL)
1132 result = !package_satisfies_dependency(package_num, package_edge->type);
1133
1134 if (result) { /* check for other package which provide what we are looking for */
1135 int provider = -1;
1136
1137 while ((provider = search_for_provides(package_edge->name, provider)) > -1) {
1138 if (package_hashtable[provider] == NULL) {
1139 puts("Have a provider but no package information for it");
1140 continue;
1141 }
1142 result = !package_satisfies_dependency(provider, package_edge->type);
1143
1144 if (result == 0)
1145 break;
1146 }
1147 }
1148
1149 /* It must be already installed, or to be installed */
1150 number_of_alternatives--;
1151 if (result && number_of_alternatives == 0) {
1152 if (root_of_alternatives)
1153 bb_error_msg_and_die(
1154 "package %s %sdepends on %s, which %s",
1155 name_hashtable[package_node->name],
1156 package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
1157 name_hashtable[root_of_alternatives->name],
1158 "cannot be satisfied");
1159 bb_error_msg_and_die(
1160 "package %s %sdepends on %s, which %s",
1161 name_hashtable[package_node->name],
1162 package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
1163 name_hashtable[package_edge->name],
1164 describe_status(status_num));
1165 }
1166 if (result == 0 && number_of_alternatives) {
1167 /* we've found a package which
1168 * satisfies the dependency,
1169 * so skip over the rest of
1170 * the alternatives.
1171 */
1172 j += number_of_alternatives;
1173 number_of_alternatives = 0;
1174 }
1175 }
1176 }
1177 }
1178 free(conflicts);
1179 return TRUE;
1180}
1181
1182static char **create_list(const char *filename)
1183{
1184 FILE *list_stream;
1185 char **file_list;
1186 char *line;
1187 int count;
1188
1189 /* don't use [xw]fopen here, handle error ourself */
1190 list_stream = fopen_for_read(filename);
1191 if (list_stream == NULL) {
1192 return NULL;
1193 }
1194
1195 file_list = NULL;
1196 count = 0;
1197 while ((line = xmalloc_fgetline(list_stream)) != NULL) {
1198 file_list = xrealloc_vector(file_list, 2, count);
1199 file_list[count++] = line;
1200 /*file_list[count] = NULL; - xrealloc_vector did it */
1201 }
1202 fclose(list_stream);
1203
1204 return file_list;
1205}
1206
1207/* maybe i should try and hook this into remove_file.c somehow */
1208static int remove_file_array(char **remove_names, char **exclude_names)
1209{
1210 struct stat path_stat;
1211 int remove_flag = 1; /* not removed anything yet */
1212 int i, j;
1213
1214 if (remove_names == NULL) {
1215 return 0;
1216 }
1217 for (i = 0; remove_names[i] != NULL; i++) {
1218 if (exclude_names != NULL) {
1219 for (j = 0; exclude_names[j] != NULL; j++) {
1220 if (strcmp(remove_names[i], exclude_names[j]) == 0) {
1221 goto skip;
1222 }
1223 }
1224 }
1225 /* TODO: why we are checking lstat? we can just try rm/rmdir */
1226 if (lstat(remove_names[i], &path_stat) < 0) {
1227 continue;
1228 }
1229 if (S_ISDIR(path_stat.st_mode)) {
1230 remove_flag &= rmdir(remove_names[i]); /* 0 if no error */
1231 } else {
1232 remove_flag &= unlink(remove_names[i]); /* 0 if no error */
1233 }
1234 skip:
1235 continue;
1236 }
1237 return (remove_flag == 0);
1238}
1239
1240static void run_package_script_or_die(const char *package_name, const char *script_type)
1241{
1242 char *script_path;
1243 int result;
1244
1245 script_path = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, script_type);
1246
1247 /* If the file doesnt exist is isnt fatal */
1248 result = access(script_path, F_OK) ? EXIT_SUCCESS : system(script_path);
1249 free(script_path);
1250 if (result)
1251 bb_error_msg_and_die("%s failed, exit code %d", script_type, result);
1252}
1253
1254/*
1255The policy manual defines what scripts get called when and with
1256what arguments. I realize that busybox does not support all of
1257these scenarios, but it does support some of them; it does not,
1258however, run them with any parameters in run_package_script_or_die().
1259Here are the scripts:
1260
1261preinst install
1262preinst install <old_version>
1263preinst upgrade <old_version>
1264preinst abort_upgrade <new_version>
1265postinst configure <most_recent_version>
1266postinst abort-upgade <new_version>
1267postinst abort-remove
1268postinst abort-remove in-favour <package> <version>
1269postinst abort-deconfigure in-favor <failed_install_package> removing <conflicting_package> <version>
1270prerm remove
1271prerm upgrade <new_version>
1272prerm failed-upgrade <old_version>
1273prerm remove in-favor <package> <new_version>
1274prerm deconfigure in-favour <package> <version> removing <package> <version>
1275postrm remove
1276postrm purge
1277postrm upgrade <new_version>
1278postrm failed-upgrade <old_version>
1279postrm abort-install
1280postrm abort-install <old_version>
1281postrm abort-upgrade <old_version>
1282postrm disappear <overwriter> <version>
1283*/
1284static const char *const all_control_files[] = {
1285 "preinst", "postinst", "prerm", "postrm",
1286 "list", "md5sums", "shlibs", "conffiles",
1287 "config", "templates"
1288};
1289
1290static char **all_control_list(const char *package_name)
1291{
1292 unsigned i = 0;
1293 char **remove_files;
1294
1295 /* Create a list of all /var/lib/dpkg/info/<package> files */
1296 remove_files = xzalloc(sizeof(all_control_files) + sizeof(char*));
1297 while (i < ARRAY_SIZE(all_control_files)) {
1298 remove_files[i] = xasprintf("/var/lib/dpkg/info/%s.%s",
1299 package_name, all_control_files[i]);
1300 i++;
1301 }
1302
1303 return remove_files;
1304}
1305
1306static void free_array(char **array)
1307{
1308 if (array) {
1309 unsigned i = 0;
1310 while (array[i]) {
1311 free(array[i]);
1312 i++;
1313 }
1314 free(array);
1315 }
1316}
1317
1318/* This function lists information on the installed packages. It loops through
1319 * the status_hashtable to retrieve the info. This results in smaller code than
1320 * scanning the status file. The resulting list, however, is unsorted.
1321 */
1322static void list_packages(const char *pattern)
1323{
1324 int i;
1325
1326 puts(" Name Version");
1327 puts("+++-==============-==============");
1328
1329 /* go through status hash, dereference package hash and finally strings */
1330 for (i = 0; i < STATUS_HASH_PRIME+1; i++) {
1331 if (status_hashtable[i]) {
1332 const char *stat_str; /* status string */
1333 const char *name_str; /* package name */
1334 const char *vers_str; /* version */
1335 char s1, s2; /* status abbreviations */
1336 int spccnt; /* space count */
1337 int j;
1338
1339 stat_str = name_hashtable[status_hashtable[i]->status];
1340 name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name];
1341 vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version];
1342
1343 if (pattern && fnmatch(pattern, name_str, 0) != 0)
1344 continue;
1345
1346 /* get abbreviation for status field 1 */
1347 s1 = stat_str[0] == 'i' ? 'i' : 'r';
1348
1349 /* get abbreviation for status field 2 */
1350 for (j = 0, spccnt = 0; stat_str[j] && spccnt < 2; j++) {
1351 if (stat_str[j] == ' ') spccnt++;
1352 }
1353 s2 = stat_str[j];
1354
1355 /* print out the line formatted like Debian dpkg */
1356 printf("%c%c %-14s %s\n", s1, s2, name_str, vers_str);
1357 }
1358 }
1359}
1360
1361static void remove_package(const unsigned package_num, int noisy)
1362{
1363 const char *package_name = name_hashtable[package_hashtable[package_num]->name];
1364 const char *package_version = name_hashtable[package_hashtable[package_num]->version];
1365 const unsigned status_num = search_status_hashtable(package_name);
1366 const int package_name_length = strlen(package_name);
1367 char **remove_files;
1368 char **exclude_files;
1369 char list_name[package_name_length + 25];
1370 char conffile_name[package_name_length + 30];
1371
1372 if (noisy)
1373 printf("Removing %s (%s)...\n", package_name, package_version);
1374
1375 /* Run prerm script */
1376 run_package_script_or_die(package_name, "prerm");
1377
1378 /* Create a list of files to remove, and a separate list of those to keep */
1379 sprintf(list_name, "/var/lib/dpkg/info/%s.%s", package_name, "list");
1380 remove_files = create_list(list_name);
1381
1382 sprintf(conffile_name, "/var/lib/dpkg/info/%s.%s", package_name, "conffiles");
1383 exclude_files = create_list(conffile_name);
1384
1385 /* Some directories can't be removed straight away, so do multiple passes */
1386 while (remove_file_array(remove_files, exclude_files))
1387 continue;
1388 free_array(exclude_files);
1389 free_array(remove_files);
1390
1391 /* Create a list of files in /var/lib/dpkg/info/<package>.* to keep */
1392 exclude_files = xzalloc(sizeof(exclude_files[0]) * 3);
1393 exclude_files[0] = xstrdup(conffile_name);
1394 exclude_files[1] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "postrm");
1395
1396 /* Create a list of all /var/lib/dpkg/info/<package> files */
1397 remove_files = all_control_list(package_name);
1398
1399 remove_file_array(remove_files, exclude_files);
1400 free_array(remove_files);
1401 free_array(exclude_files);
1402
1403 /* rename <package>.conffiles to <package>.list
1404 * The conffiles control file isn't required in Debian packages, so don't
1405 * error out if it's missing. */
1406 rename(conffile_name, list_name);
1407
1408 /* Change package status */
1409 set_status(status_num, "config-files", 3);
1410}
1411
1412static void purge_package(const unsigned package_num)
1413{
1414 const char *package_name = name_hashtable[package_hashtable[package_num]->name];
1415 const char *package_version = name_hashtable[package_hashtable[package_num]->version];
1416 const unsigned status_num = search_status_hashtable(package_name);
1417 char **remove_files;
1418 char **exclude_files;
1419 char list_name[strlen(package_name) + 25];
1420
1421 printf("Purging %s (%s)...\n", package_name, package_version);
1422
1423 /* Run prerm script */
1424 run_package_script_or_die(package_name, "prerm");
1425
1426 /* Create a list of files to remove */
1427 sprintf(list_name, "/var/lib/dpkg/info/%s.%s", package_name, "list");
1428 remove_files = create_list(list_name);
1429
1430 /* Some directories cant be removed straight away, so do multiple passes */
1431 while (remove_file_array(remove_files, NULL))
1432 continue;
1433 free_array(remove_files);
1434
1435 /* Create a list of all /var/lib/dpkg/info/<package> files */
1436 remove_files = all_control_list(package_name);
1437
1438 /* Delete all of them except the postrm script */
1439 exclude_files = xzalloc(sizeof(exclude_files[0]) * 2);
1440 exclude_files[0] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "postrm");
1441 remove_file_array(remove_files, exclude_files);
1442 free_array(exclude_files);
1443
1444 /* Run and remove postrm script */
1445 run_package_script_or_die(package_name, "postrm");
1446 remove_file_array(remove_files, NULL);
1447
1448 free_array(remove_files);
1449
1450 /* Change package status */
1451 set_status(status_num, "not-installed", 3);
1452}
1453
1454static archive_handle_t *init_archive_deb_ar(const char *filename)
1455{
1456 archive_handle_t *ar_handle;
1457
1458 /* Setup an ar archive handle that refers to the gzip sub archive */
1459 ar_handle = init_handle();
1460 ar_handle->filter = filter_accept_list_reassign;
1461 ar_handle->src_fd = xopen(filename, O_RDONLY);
1462
1463 return ar_handle;
1464}
1465
1466static void init_archive_deb_control(archive_handle_t *ar_handle)
1467{
1468 archive_handle_t *tar_handle;
1469
1470 /* Setup the tar archive handle */
1471 tar_handle = init_handle();
1472 tar_handle->src_fd = ar_handle->src_fd;
1473
1474 /* We don't care about data.tar.* or debian-binary, just control.tar.* */
1475 llist_add_to(&(ar_handle->accept), (char*)"control.tar");
1476#if ENABLE_FEATURE_SEAMLESS_GZ
1477 llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz");
1478#endif
1479#if ENABLE_FEATURE_SEAMLESS_BZ2
1480 llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2");
1481#endif
1482#if ENABLE_FEATURE_SEAMLESS_XZ
1483 llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz");
1484#endif
1485
1486 /* Assign the tar handle as a subarchive of the ar handle */
1487 ar_handle->dpkg__sub_archive = tar_handle;
1488}
1489
1490static void init_archive_deb_data(archive_handle_t *ar_handle)
1491{
1492 archive_handle_t *tar_handle;
1493
1494 /* Setup the tar archive handle */
1495 tar_handle = init_handle();
1496 tar_handle->src_fd = ar_handle->src_fd;
1497
1498 /* We don't care about control.tar.* or debian-binary, just data.tar.* */
1499 llist_add_to(&(ar_handle->accept), (char*)"data.tar");
1500#if ENABLE_FEATURE_SEAMLESS_GZ
1501 llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz");
1502#endif
1503#if ENABLE_FEATURE_SEAMLESS_BZ2
1504 llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2");
1505#endif
1506#if ENABLE_FEATURE_SEAMLESS_LZMA
1507 llist_add_to(&(ar_handle->accept), (char*)"data.tar.lzma");
1508#endif
1509#if ENABLE_FEATURE_SEAMLESS_XZ
1510 llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz");
1511#endif
1512
1513 /* Assign the tar handle as a subarchive of the ar handle */
1514 ar_handle->dpkg__sub_archive = tar_handle;
1515}
1516
1517static void FAST_FUNC data_extract_to_buffer(archive_handle_t *archive_handle)
1518{
1519 unsigned size = archive_handle->file_header->size;
1520
1521 archive_handle->dpkg__buffer = xzalloc(size + 1);
1522 xread(archive_handle->src_fd, archive_handle->dpkg__buffer, size);
1523}
1524
1525static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept)
1526{
1527 ar_handle->dpkg__sub_archive->action_data = data_extract_to_buffer;
1528 ar_handle->dpkg__sub_archive->accept = myaccept;
1529 ar_handle->dpkg__sub_archive->filter = filter_accept_list;
1530
1531 unpack_ar_archive(ar_handle);
1532 close(ar_handle->src_fd);
1533
1534 return ar_handle->dpkg__sub_archive->dpkg__buffer;
1535}
1536
1537static void append_control_file_to_llist(const char *package_name, const char *control_name, llist_t **ll)
1538{
1539 FILE *fp;
1540 char *filename, *line;
1541
1542 filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, control_name);
1543 fp = fopen_for_read(filename);
1544 free(filename);
1545 if (fp != NULL) {
1546 while ((line = xmalloc_fgetline(fp)) != NULL)
1547 llist_add_to(ll, line);
1548 fclose(fp);
1549 }
1550}
1551
1552static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle)
1553{
1554 int fd;
1555 char *name_ptr = archive_handle->file_header->name + 1;
1556
1557 /* Is this file marked as config file? */
1558 if (!find_list_entry(archive_handle->accept, name_ptr))
1559 return EXIT_SUCCESS; /* no */
1560
1561 fd = open(name_ptr, O_RDONLY);
1562 if (fd >= 0) {
1563 md5_ctx_t md5;
1564 char *md5line, *buf;
1565 int count;
1566
1567 /* Calculate MD5 of existing file */
1568 buf = xmalloc(4096);
1569 md5_begin(&md5);
1570 while ((count = safe_read(fd, buf, 4096)) > 0)
1571 md5_hash(&md5, buf, count);
1572 md5_end(&md5, buf); /* using buf as result storage */
1573 close(fd);
1574
1575 md5line = xmalloc(16 * 2 + 2 + strlen(name_ptr) + 1);
1576 sprintf(bin2hex(md5line, buf, 16), " %s", name_ptr);
1577 free(buf);
1578
1579 /* Is it changed after install? */
1580 if (find_list_entry(archive_handle->accept, md5line) == NULL) {
1581 printf("Warning: Creating %s as %s.dpkg-new\n", name_ptr, name_ptr);
1582 archive_handle->file_header->name = xasprintf("%s.dpkg-new", archive_handle->file_header->name);
1583 }
1584 free(md5line);
1585 }
1586 return EXIT_SUCCESS;
1587}
1588
1589static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle)
1590{
1591 char *name_ptr = archive_handle->file_header->name;
1592
1593 /* Skip all leading "/" */
1594 while (*name_ptr == '/')
1595 name_ptr++;
1596 /* Skip all leading "./" and "../" */
1597 while (name_ptr[0] == '.') {
1598 if (name_ptr[1] == '.')
1599 name_ptr++;
1600 if (name_ptr[1] != '/')
1601 break;
1602 name_ptr += 2;
1603 }
1604
1605 if (name_ptr[0] != '\0') {
1606 archive_handle->file_header->name = xasprintf("%s%s", archive_handle->dpkg__buffer, name_ptr);
1607 data_extract_all(archive_handle);
1608 if (fnmatch("*.dpkg-new", archive_handle->file_header->name, 0) == 0) {
1609 /* remove .dpkg-new suffix */
1610 archive_handle->file_header->name[strlen(archive_handle->file_header->name) - 9] = '\0';
1611 }
1612 }
1613}
1614
1615enum {
1616 /* Commands */
1617 OPT_configure = (1 << 0),
1618 OPT_install = (1 << 1),
1619 OPT_list_installed = (1 << 2),
1620 OPT_purge = (1 << 3),
1621 OPT_remove = (1 << 4),
1622 OPT_unpack = (1 << 5),
1623 OPTMASK_cmd = (1 << 6) - 1,
1624 /* Options */
1625 OPT_force = (1 << 6),
1626 OPT_force_ignore_depends = (1 << 7),
1627 OPT_force_confnew = (1 << 8),
1628 OPT_force_confold = (1 << 9),
1629};
1630
1631static void unpack_package(deb_file_t *deb_file)
1632{
1633 const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
1634 const unsigned status_num = search_status_hashtable(package_name);
1635 const unsigned status_package_num = status_hashtable[status_num]->package;
1636 char *info_prefix;
1637 char *list_filename;
1638 archive_handle_t *archive_handle;
1639 FILE *out_stream;
1640 llist_t *accept_list;
1641 llist_t *conffile_list;
1642 int i;
1643
1644 /* If existing version, remove it first */
1645 conffile_list = NULL;
1646 if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) {
1647 /* Package is already installed, remove old version first */
1648 printf("Preparing to replace %s %s (using %s)...\n", package_name,
1649 name_hashtable[package_hashtable[status_package_num]->version],
1650 deb_file->filename);
1651
1652 /* Read md5sums from old package */
1653 if (!(option_mask32 & OPT_force_confold))
1654 append_control_file_to_llist(package_name, "md5sums", &conffile_list);
1655
1656 remove_package(status_package_num, 0);
1657 } else {
1658 printf("Unpacking %s (from %s)...\n", package_name, deb_file->filename);
1659 }
1660
1661 /* Extract control.tar.gz to /var/lib/dpkg/info/<package>.filename */
1662 info_prefix = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "");
1663 archive_handle = init_archive_deb_ar(deb_file->filename);
1664 init_archive_deb_control(archive_handle);
1665
1666 accept_list = NULL;
1667 i = 0;
1668 while (i < ARRAY_SIZE(all_control_files)) {
1669 char *c = xasprintf("./%s", all_control_files[i]);
1670 llist_add_to(&accept_list, c);
1671 i++;
1672 }
1673 archive_handle->dpkg__sub_archive->accept = accept_list;
1674 archive_handle->dpkg__sub_archive->filter = filter_accept_list;
1675 archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix;
1676 archive_handle->dpkg__sub_archive->dpkg__buffer = info_prefix;
1677 archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_UNLINK_OLD;
1678 unpack_ar_archive(archive_handle);
1679
1680 /* Run the preinst prior to extracting */
1681 run_package_script_or_die(package_name, "preinst");
1682
1683 /* Don't overwrite existing config files */
1684 if (!(option_mask32 & OPT_force_confnew))
1685 append_control_file_to_llist(package_name, "conffiles", &conffile_list);
1686
1687 /* Extract data.tar.gz to the root directory */
1688 archive_handle = init_archive_deb_ar(deb_file->filename);
1689 init_archive_deb_data(archive_handle);
1690 archive_handle->dpkg__sub_archive->accept = conffile_list;
1691 /* Why ARCHIVE_REMEMBER_NAMES?
1692 * We want names collected in ->passed list even if conffile_list
1693 * is NULL (otherwise get_header_tar may optimize name saving out):
1694 */
1695 archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_REMEMBER_NAMES | ARCHIVE_UNLINK_OLD;
1696 archive_handle->dpkg__sub_archive->filter = filter_rename_config;
1697 archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix;
1698 archive_handle->dpkg__sub_archive->dpkg__buffer = (char*)"/"; /* huh? */
1699 unpack_ar_archive(archive_handle);
1700
1701 /* Create the list file */
1702 list_filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "list");
1703 out_stream = xfopen_for_write(list_filename);
1704 archive_handle->dpkg__sub_archive->passed = llist_rev(archive_handle->dpkg__sub_archive->passed);
1705 while (archive_handle->dpkg__sub_archive->passed) {
1706 char *filename = llist_pop(&archive_handle->dpkg__sub_archive->passed);
1707 /* the leading . has been stripped by data_extract_all_prefix already */
1708 fprintf(out_stream, "%s\n", filename);
1709 free(filename);
1710 }
1711 fclose(out_stream);
1712
1713 /* change status */
1714 set_status(status_num, "install", 1);
1715 set_status(status_num, "unpacked", 3);
1716
1717 free(info_prefix);
1718 free(list_filename);
1719}
1720
1721static void configure_package(deb_file_t *deb_file)
1722{
1723 const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
1724 const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version];
1725 const int status_num = search_status_hashtable(package_name);
1726
1727 printf("Setting up %s (%s)...\n", package_name, package_version);
1728
1729 /* Run the postinst script */
1730 /* TODO: handle failure gracefully */
1731 run_package_script_or_die(package_name, "postinst");
1732
1733 /* Change status to reflect success */
1734 set_status(status_num, "install", 1);
1735 set_status(status_num, "installed", 3);
1736}
1737
1738int dpkg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1739int dpkg_main(int argc UNUSED_PARAM, char **argv)
1740{
1741 deb_file_t **deb_file = NULL;
1742 status_node_t *status_node;
1743 char *str_f;
1744 int opt;
1745 int package_num;
1746 int deb_count = 0;
1747 int state_status;
1748 int status_num;
1749 int i;
1750#if ENABLE_LONG_OPTS
1751 static const char dpkg_longopts[] ALIGN1 =
1752// FIXME: we use -C non-compatibly, should be:
1753// "-C|--audit Check for broken package(s)"
1754 "configure\0" No_argument "C"
1755 "force\0" Required_argument "F"
1756 "install\0" No_argument "i"
1757 "list\0" No_argument "l"
1758 "purge\0" No_argument "P"
1759 "remove\0" No_argument "r"
1760 "unpack\0" No_argument "u"
1761 "force-depends\0" No_argument "\xff"
1762 "force-confnew\0" No_argument "\xfe"
1763 "force-confold\0" No_argument "\xfd"
1764 ;
1765#endif
1766
1767 INIT_G();
1768
1769 IF_LONG_OPTS(applet_long_options = dpkg_longopts);
1770 opt = getopt32(argv, "CilPruF:", &str_f);
1771 argv += optind;
1772 //if (opt & OPT_configure) ... // -C
1773 if (opt & OPT_force) { // -F (--force in official dpkg)
1774 if (strcmp(str_f, "depends") == 0)
1775 opt |= OPT_force_ignore_depends;
1776 else if (strcmp(str_f, "confnew") == 0)
1777 opt |= OPT_force_confnew;
1778 else if (strcmp(str_f, "confold") == 0)
1779 opt |= OPT_force_confold;
1780 else
1781 bb_show_usage();
1782 option_mask32 = opt;
1783 }
1784 //if (opt & OPT_install) ... // -i
1785 //if (opt & OPT_list_installed) ... // -l
1786 //if (opt & OPT_purge) ... // -P
1787 //if (opt & OPT_remove) ... // -r
1788 //if (opt & OPT_unpack) ... // -u (--unpack in official dpkg)
1789 if (!(opt & OPTMASK_cmd) /* no cmd */
1790 || ((opt & OPTMASK_cmd) & ((opt & OPTMASK_cmd)-1)) /* more than one cmd */
1791 ) {
1792 bb_show_usage();
1793 }
1794
1795/* puts("(Reading database ... xxxxx files and directories installed.)"); */
1796 index_status_file("/var/lib/dpkg/status");
1797
1798 /* if the list action was given print the installed packages and exit */
1799 if (opt & OPT_list_installed) {
1800 list_packages(argv[0]); /* param can be NULL */
1801 return EXIT_SUCCESS;
1802 }
1803
1804 /* Read arguments and store relevant info in structs */
1805 while (*argv) {
1806 /* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */
1807 deb_file = xrealloc_vector(deb_file, 2, deb_count);
1808 deb_file[deb_count] = xzalloc(sizeof(deb_file[0][0]));
1809 if (opt & (OPT_install | OPT_unpack)) {
1810 /* -i/-u: require filename */
1811 archive_handle_t *archive_handle;
1812 llist_t *control_list = NULL;
1813
1814 /* Extract the control file */
1815 llist_add_to(&control_list, (char*)"./control");
1816 archive_handle = init_archive_deb_ar(argv[0]);
1817 init_archive_deb_control(archive_handle);
1818 deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list);
1819 if (deb_file[deb_count]->control_file == NULL) {
1820 bb_error_msg_and_die("can't extract control file");
1821 }
1822 deb_file[deb_count]->filename = xstrdup(argv[0]);
1823 package_num = fill_package_struct(deb_file[deb_count]->control_file);
1824
1825 if (package_num == -1) {
1826 bb_error_msg("invalid control file in %s", argv[0]);
1827 argv++;
1828 continue;
1829 }
1830 deb_file[deb_count]->package = (unsigned) package_num;
1831
1832 /* Add the package to the status hashtable */
1833 if (opt & (OPT_unpack | OPT_install)) {
1834 /* Try and find a currently installed version of this package */
1835 status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]);
1836 /* If no previous entry was found initialise a new entry */
1837 if (status_hashtable[status_num] == NULL
1838 || status_hashtable[status_num]->status == 0
1839 ) {
1840 status_node = xmalloc(sizeof(status_node_t));
1841 status_node->package = deb_file[deb_count]->package;
1842 /* reinstreq isnt changed to "ok" until the package control info
1843 * is written to the status file*/
1844 status_node->status = search_name_hashtable("install reinstreq not-installed");
1845 status_hashtable[status_num] = status_node;
1846 } else {
1847 set_status(status_num, "install", 1);
1848 set_status(status_num, "reinstreq", 2);
1849 }
1850 }
1851 } else if (opt & (OPT_configure | OPT_purge | OPT_remove)) {
1852 /* -C/-p/-r: require package name */
1853 deb_file[deb_count]->package = search_package_hashtable(
1854 search_name_hashtable(argv[0]),
1855 search_name_hashtable("ANY"), VER_ANY);
1856 if (package_hashtable[deb_file[deb_count]->package] == NULL) {
1857 bb_error_msg_and_die("package %s is uninstalled or unknown", argv[0]);
1858 }
1859 package_num = deb_file[deb_count]->package;
1860 status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
1861 state_status = get_status(status_num, 3);
1862
1863 /* check package status is "installed" */
1864 if (opt & OPT_remove) {
1865 if (strcmp(name_hashtable[state_status], "not-installed") == 0
1866 || strcmp(name_hashtable[state_status], "config-files") == 0
1867 ) {
1868 bb_error_msg_and_die("%s is already removed", name_hashtable[package_hashtable[package_num]->name]);
1869 }
1870 set_status(status_num, "deinstall", 1);
1871 } else if (opt & OPT_purge) {
1872 /* if package status is "conf-files" then its ok */
1873 if (strcmp(name_hashtable[state_status], "not-installed") == 0) {
1874 bb_error_msg_and_die("%s is already purged", name_hashtable[package_hashtable[package_num]->name]);
1875 }
1876 set_status(status_num, "purge", 1);
1877 }
1878 }
1879 deb_count++;
1880 argv++;
1881 }
1882 if (!deb_count)
1883 bb_error_msg_and_die("no package files specified");
1884 deb_file[deb_count] = NULL;
1885
1886 /* Check that the deb file arguments are installable */
1887 if (!(opt & OPT_force_ignore_depends)) {
1888 if (!check_deps(deb_file, 0 /*, deb_count*/)) {
1889 bb_error_msg_and_die("dependency check failed");
1890 }
1891 }
1892
1893 /* TODO: install or remove packages in the correct dependency order */
1894 for (i = 0; i < deb_count; i++) {
1895 /* Remove or purge packages */
1896 if (opt & OPT_remove) {
1897 remove_package(deb_file[i]->package, 1);
1898 }
1899 else if (opt & OPT_purge) {
1900 purge_package(deb_file[i]->package);
1901 }
1902 else if (opt & OPT_unpack) {
1903 unpack_package(deb_file[i]);
1904 }
1905 else if (opt & OPT_install) {
1906 unpack_package(deb_file[i]);
1907 /* package is configured in second pass below */
1908 }
1909 else if (opt & OPT_configure) {
1910 configure_package(deb_file[i]);
1911 }
1912 }
1913 /* configure installed packages */
1914 if (opt & OPT_install) {
1915 for (i = 0; i < deb_count; i++)
1916 configure_package(deb_file[i]);
1917 }
1918
1919 write_status_file(deb_file);
1920
1921 if (ENABLE_FEATURE_CLEAN_UP) {
1922 for (i = 0; i < deb_count; i++) {
1923 free(deb_file[i]->control_file);
1924 free(deb_file[i]->filename);
1925 free(deb_file[i]);
1926 }
1927
1928 free(deb_file);
1929
1930 for (i = 0; i < NAME_HASH_PRIME; i++) {
1931 free(name_hashtable[i]);
1932 }
1933
1934 for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
1935 free_package(package_hashtable[i]);
1936 }
1937
1938 for (i = 0; i < STATUS_HASH_PRIME; i++) {
1939 free(status_hashtable[i]);
1940 }
1941
1942 free(status_hashtable);
1943 free(package_hashtable);
1944 free(name_hashtable);
1945 }
1946
1947 return EXIT_SUCCESS;
1948}
Note: See TracBrowser for help on using the repository browser.