i recode anna to reduce lowmem complexity. - now, lowmem add to the asklist only packages with a want_install status (except for menu-item packages ) at the end of choose modules function. - lowmem also takes into account the packages listed in the INCLUDE_FILE - and all asklist computing is done when question is shown (in expert or lowmem mode)
you can test this version
#include <sys/utsname.h> #include "anna.h" #include "util.h" #include "retriever.h" struct debconfclient *debconf = NULL; static char *running_kernel = NULL; static const char *subarchitecture; static int quiet = 0; /* TODO: instead of copy/paste these two priority function from cdebconf (same in main-menu), may be we can add priority.h in /usr/include/cdebconf */ static int priority_code(const char *p) { if (p == 0) return -1; if (strncmp(p, "low", 3) == 0) return 0; if (strncmp(p, "medium", 6) == 0) return 1; if (strncmp(p, "high", 4) == 0) return 2; if (strncmp(p, "critical", 8) == 0) return 3; return -1; } /** * @brief compares two priorities * @param const char *p1, const char *p2 - priorities to compare * @return int - <0 if p1<p2, ==0 if p1==p2, >0 if p1>p2 */ int priority_compare(const char *p1, const char *p2) { int i1, i2; i1 = priority_code(p1); i2 = priority_code(p2); if (i1 > i2) return 1; else if (i1 == i2) return 0; else return -1; } di_packages *get_packages (void) { di_packages_allocator *packages_allocator = di_system_packages_allocator_alloc(); di_packages *packages = retriever_packages(packages_allocator); while (packages == NULL) { int r=retriever_error("packages"); di_log(DI_LOG_LEVEL_WARNING, "bad d-i Packages file"); if (r != 1) { /* Failed to handle error. */ return NULL; } else { /* Error handled, retry. */ packages_allocator = di_system_packages_allocator_alloc(); packages = retriever_packages(packages_allocator); } } return packages; } /* Go through the available packages to see if it contains at least * one package that is valid for the subarchitecture and corresponds * to the kernel version we are running */ int packages_ok (di_packages *packages) { di_slist_node *node; di_package *package; bool kernel_packages_present = false; for (node = packages->list.head; node; node = node->next) { package = node->data; if (!di_system_package_check_subarchitecture(package, subarchitecture)) continue; if (((di_system_package *)package)->kernel_version) { if (running_kernel && strcmp(running_kernel, ((di_system_package *)package)->kernel_version) == 0) { kernel_packages_present = true; break; } } } if (!kernel_packages_present) { di_log(DI_LOG_LEVEL_WARNING, "no packages for kernel in archive"); debconf_input(debconf, "critical", "anna/no_kernel_modules"); if (debconf_go(debconf) == 30) return 0; debconf_get(debconf, "anna/no_kernel_modules"); if (strcmp(debconf->value, "false") == 0) return 0; } return 1; } static int choose_modules(di_packages *status, di_packages **packages) { char *choose_modules_question = "anna/choose_modules" ; char *template = "debconf/priority"; char *question_priority = "medium"; char *choices; int package_count = 0; di_package *package, *status_package, **package_array; di_slist_node *node, *node1; bool standard_modules = true; bool expert_mode = false; bool lowmem_mode = false; /* Test debconf priority to know if packages with unknown status will be shown */ debconf_get(debconf, template); if (priority_compare(debconf->value, question_priority) <= 0) { expert_mode = true; di_log (DI_LOG_LEVEL_DEBUG, "debconf_priority=%s, unknown status packages will be shown", debconf->value); } /* Test lowmem level to know if packages with want_install status will be shown */ if ( get_lowmem_level() >= 2) { lowmem_mode = true; choose_modules_question="anna/choose_modules_lowmem"; /* force priority to show question even in a non expert mode */ question_priority = "high"; di_log (DI_LOG_LEVEL_DEBUG, "lowmem_mode, want_install status packages will be shown"); } for (node = status->list.head; node; node = node->next) { status_package = node->data; package = di_packages_get_package(*packages, status_package->package, 0); if (!package) continue; package->status = status_package->status; if (status_package->status == di_package_status_unpacked || status_package->status == di_package_status_installed) { for (node1 = package->depends.head; node1; node1 = node1->next) { di_package_dependency *d = node1->data; if (d->type == di_package_dependency_type_reverse_enhances) { package->status_want = di_package_status_want_install; di_log (DI_LOG_LEVEL_DEBUG, "install %s, enhances installed packages %s", package->package, status_package->package); } } } } debconf_get(debconf, "anna/standard_modules"); if (strcmp(debconf->value, "false") == 0) standard_modules = false; for (node = (*packages)->list.head; node; node = node->next) { package = node->data; package->status_want = di_package_status_want_deinstall; if (package->type != di_package_type_real_package) continue; if (is_installed(package, status)) continue; if (!di_system_package_check_subarchitecture(package, subarchitecture)) continue; if (((di_system_package *)package)->kernel_version) { if (running_kernel && strcmp(running_kernel, ((di_system_package *)package)->kernel_version) == 0) { package->status_want = di_package_status_want_unknown; di_log (DI_LOG_LEVEL_DEBUG, "ask for %s, matches kernel", package->package); } else { continue; } for (node1 = package->depends.head; node1; node1 = node1->next) { di_package_dependency *d = node1->data; if (d->type == di_package_dependency_type_provides && d->ptr && is_queued(d->ptr)) { package->status_want = di_package_status_want_install; di_log (DI_LOG_LEVEL_DEBUG, "install %s, queued by anna-install", package->package); continue; } } } if (package->priority >= di_package_priority_standard) { if (standard_modules || ((di_system_package *)package)->kernel_version) { package->status_want = di_package_status_want_install; di_log (DI_LOG_LEVEL_DEBUG, "install %s, priority >= standard", package->package); } else { package->status_want = di_package_status_want_unknown; di_log (DI_LOG_LEVEL_DEBUG, "ask for %s, priority >= standard", package->package); } } else if (is_queued(package)) { package->status_want = di_package_status_want_install; di_log (DI_LOG_LEVEL_DEBUG, "install %s, queued by anna-install", package->package); } else if (((di_system_package *)package)->installer_menu_item /* we don't want to see installed packages in choices list*/ && package->status != di_package_status_installed) { package->status_want = di_package_status_want_unknown; di_log (DI_LOG_LEVEL_DEBUG, "ask for %s, is menu item", package->package); } } /* Include packages in udeb_include */ take_includes(*packages); /* Drop packages in udeb_exclude */ drop_excludes(*packages); di_system_packages_resolve_dependencies_mark_anna(*packages, subarchitecture, running_kernel); /* Test if question will be shown */ if ( expert_mode || lowmem_mode ) { /* Slight over-allocation, but who cares */ package_array = di_new0(di_package *, di_hash_table_size((*packages)->table)); /* Now build the asklist, figuring out which packages have been * pulled into instlist */ for (node = (*packages)->list.head; node; node = node->next) { package = node->data; if ( expert_mode && package->status_want == di_package_status_want_unknown) { package_array[package_count++] = package; } /* In lowmem_mode, we add packages with want_install * status to asklist. * To keep the installation process, we do not put * in the asklist packages which are a menu item */ if ( lowmem_mode && package->status_want == di_package_status_want_install && ((di_system_package *)package)->installer_menu_item == 0) { package->status_want = di_package_status_want_unknown; package_array[package_count++] = package; } } qsort(package_array, package_count, sizeof(di_package *), package_name_compare); choices = list_to_choices(package_array); debconf_subst(debconf, choose_modules_question, "CHOICES", choices); debconf_input(debconf, question_priority, choose_modules_question); di_free(choices); di_free(package_array); if (debconf_go(debconf) == 30) return 1; debconf_get(debconf, choose_modules_question); if (debconf->value != NULL) { char *choices = debconf->value; for (node = (*packages)->list.head; node; node = node->next) { package = node->data; /* Not very safe, but at least easy ;) */ if (strstr(choices, package->package) != NULL) package->status_want = di_package_status_want_install; } } } return 0; } void resume_progress_bar (int progress_step, int pkg_count, di_package *package) { debconf_progress_start(debconf, 0, 2*pkg_count, "anna/progress_title"); debconf_progress_set(debconf, progress_step); debconf_subst(debconf, "anna/progress_step_retr", "PACKAGE", package->package); debconf_progress_info(debconf, "anna/progress_step_retr"); } static int install_modules(di_packages *status, di_packages *packages) { di_slist_node *node; di_package *package; char *f, *fp, *dest_file; int ret = 0, pkg_count = 0; int progress_step=0; di_system_packages_resolve_dependencies_mark_anna(packages, subarchitecture, running_kernel); for (node = packages->list.head; node; node = node->next) { package = node->data; if (package->status_want == di_package_status_want_install && !is_installed(package, status)) pkg_count++; else package->status_want = di_package_status_want_unknown; } /* Short-circuit if there's no packages to install. */ if (pkg_count <= 0) return 0; if (!quiet) debconf_progress_start(debconf, 0, pkg_count, "anna/progress_title"); for (node = packages->list.head; node; node = node->next) { package = node->data; if (package->type == di_package_type_real_package && package->status_want == di_package_status_want_install) { if (!package->filename) { di_log(DI_LOG_LEVEL_ERROR, "no Filename field for %s, ignoring", package->package); continue; } for (f = fp = package->filename; *fp != 0; fp++) { if (*fp == '/') f = ++fp; } if (asprintf(&dest_file, "%s/%s", DOWNLOAD_DIR, f) == -1) return 5; if (!quiet) { debconf_subst(debconf, "anna/progress_step_retr", "PACKAGE", package->package); debconf_progress_info(debconf, "anna/progress_step_retr"); } for (;;) { if (retriever_retrieve(package, dest_file)) { di_log(DI_LOG_LEVEL_WARNING, "package retrieval failed"); if (!quiet) /* error handling may use a progress bar, so stop the current one */ debconf_progress_stop(debconf); if (retriever_error("retrieve") != 1) { /* Failed to handle error. */ free(dest_file); ret = 6; goto OUT; } else { /* Handled error, retry. */ if (!quiet) resume_progress_bar(progress_step, pkg_count, package); continue; } } if (! md5sum(package->md5sum, dest_file)) { di_log(DI_LOG_LEVEL_WARNING, "bad md5sum"); if (!quiet) /* error handling may use a progress bar, so stop the current one */ debconf_progress_stop(debconf); if (retriever_error("retrieve") != 1) { /* Failed to handle error. */ unlink(dest_file); free(dest_file); ret = 7; goto OUT; } else { /* Handled error, retry. */ if (!quiet) resume_progress_bar(progress_step, pkg_count, package); continue; } } break; } if (!unpack_package(dest_file)) { if (!quiet) debconf_progress_stop(debconf); debconf_subst(debconf, "anna/install_failed", "PACKAGE", package->package); debconf_input(debconf, "critical", "anna/install_failed"); debconf_go(debconf); unlink(dest_file); free(dest_file); ret = 8; break; } if (!((di_system_package *)package)->installer_menu_item && !configure_package(package->package)) { if (!quiet) debconf_progress_stop(debconf); debconf_subst(debconf, "anna/install_failed", "PACKAGE", package->package); debconf_input(debconf, "critical", "anna/install_failed"); debconf_go(debconf); unlink(dest_file); free(dest_file); ret = 8; break; } unlink(dest_file); free(dest_file); if (!quiet) { debconf_progress_step(debconf, 1); progress_step++; } } } if (!quiet) debconf_progress_stop(debconf); OUT: return ret; } int main(int argc, char **argv) { int ret; di_packages *packages, *status; di_packages_allocator *status_allocator; struct utsname uts; const char *quiet_env; debconf = debconfclient_new(); debconf_capb(debconf, "backup"); di_system_init("anna"); subarchitecture = di_system_subarch_analyze(); if (uname(&uts) == 0) { running_kernel = strdup(uts.release); } quiet_env = getenv("ANNA_QUIET"); if (quiet_env && strcmp(quiet_env, "1") == 0) quiet = 1; status_allocator = di_system_packages_allocator_alloc(); status = di_system_packages_status_read_file(DI_SYSTEM_DPKG_STATUSFILE, status_allocator); if (argc <= 1) { fprintf(stderr, "need parameters\n"); exit(1); } else if (strcmp(argv[1], "install") == 0) { di_slist_node *node; di_package *package = NULL; int i; if (get_retriever() == NULL) { fprintf(stderr, "no default retriever set\n"); exit(1); } retriever_config(); packages = get_packages(); if (! packages) { retriever_cleanup(); return 10; } for (node = packages->list.head; node; node = node->next) { package = node->data; package->status_want = di_package_status_want_deinstall; } for (i = 2; i < argc; i++) { int installed = 0; int found = 0; for (node = status->list.head; node; node = node->next) { di_slist_node *node1; package = node->data; if (strcmp(package->package, argv[i]) == 0) { installed = 1; continue; } for (node1 = package->depends.head; node1; node1 = node1->next) { di_package_dependency *d = node1->data; if (d->type == di_package_dependency_type_provides && d->ptr && strcmp(d->ptr->package, argv[i]) == 0) { installed = 1; continue; } } } if (installed) { di_log (DI_LOG_LEVEL_DEBUG, "skipping already installed %s", argv[i]); continue; } for (node = packages->list.head; node; node = node->next) { package = node->data; if (!di_system_package_check_subarchitecture(package, subarchitecture)) continue; if (strcmp(package->package, argv[i]) == 0) { package->status_want = di_package_status_want_install; found = 1; continue; } if (((di_system_package *)package)->kernel_version) { if (running_kernel && strcmp(running_kernel, ((di_system_package *)package)->kernel_version) != 0) { continue; } di_slist_node *node1; for (node1 = package->depends.head; node1; node1 = node1->next) { di_package_dependency *d = node1->data; if (d->type == di_package_dependency_type_provides && d->ptr && strcmp(d->ptr->package, argv[i]) == 0) { package->status_want = di_package_status_want_install; found = 1; continue; } } } } if (! found) { fprintf(stderr, "unknown udeb %s\n", argv[i]); exit(1); } } ret = install_modules(status, packages); } else { if (argc == 2) { set_retriever(argv[1], 0); } else if (strcmp(argv[2], "default") == 0) { set_retriever(argv[1], 1); } retriever_config(); packages = get_packages(); if (! packages || ! packages_ok(packages)) { retriever_cleanup(); return 10; } if (choose_modules(status, &packages) != 0) { ret = 10; } else { ret = install_modules(status, packages); } } retriever_cleanup(); return ret; }