You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

663 lines
19 KiB

  1. /*
  2. * Debian Installer main menu program.
  3. *
  4. * Copyright 2000,2004 Joey Hess <joeyh@debian.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. */
  16. #include "main-menu.h"
  17. #include <cdebconf/debconfclient.h>
  18. #include <stdlib.h>
  19. #include <search.h>
  20. #include <stdio.h>
  21. #include <sys/stat.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <signal.h>
  25. #include <sys/types.h>
  26. #include <unistd.h>
  27. const int RAISE = 1;
  28. const int LOWER = 0;
  29. int last_successful_item = -1;
  30. /* Save default priority, to be able to return to it when we have to lower it */
  31. int default_priority = 1;
  32. /* Save priority set by main-menu to detect priority changes from the user */
  33. int local_priority = -1;
  34. static struct debconfclient *debconf;
  35. static int di_config_package(di_system_package *p,
  36. int (*virtfunc)(di_system_package *),
  37. int (*walkfunc)(di_system_package *));
  38. /*
  39. * qsort comparison function (sort by menu item values, fall back to
  40. * lexical sort to resolve ties deterministically).
  41. */
  42. int package_array_compare (const void *v1, const void *v2) {
  43. di_system_package *p1, *p2;
  44. p1 = *(di_system_package **)v1;
  45. p2 = *(di_system_package **)v2;
  46. int r = p1->installer_menu_item - p2->installer_menu_item;
  47. if (r) return r;
  48. return strcmp(p1->p.package, p2->p.package);
  49. }
  50. int isdefault(di_system_package *p) {
  51. int check;
  52. check = di_system_dpkg_package_control_file_exec(&p->p, "menutest", 0, NULL);
  53. if (check <= 0 || p->p.status == di_package_status_unpacked || p->p.status == di_package_status_half_configured)
  54. return true;
  55. return false;
  56. }
  57. bool isinstallable(di_system_package *p) {
  58. int check;
  59. check = di_system_dpkg_package_control_file_exec(&p->p, "isinstallable", 0, NULL);
  60. if (check <= 0)
  61. return true;
  62. return false;
  63. }
  64. /* Return nonzero if all of the virtual packages that P provides are
  65. provided by installed packages. */
  66. int provides_installed_virtual_packages(di_package *p) {
  67. di_slist_node *node1, *node2;
  68. int provides = 0;
  69. for (node1 = p->depends.head; node1; node1 = node1->next) {
  70. di_package_dependency *d = node1->data;
  71. if (d->type == di_package_dependency_type_provides) {
  72. int installed = 0;
  73. provides = 1;
  74. for (node2 = d->ptr->depends.head; node2; node2 = node2->next) {
  75. d = node2->data;
  76. if (d->type == di_package_dependency_type_reverse_provides
  77. && d->ptr->status == di_package_status_installed) {
  78. installed = 1;
  79. break;
  80. }
  81. }
  82. if (!installed)
  83. return 0;
  84. }
  85. }
  86. return provides;
  87. }
  88. /* Expects a topologically ordered linked list of packages. */
  89. static di_system_package *
  90. get_default_menu_item(di_slist *list)
  91. {
  92. di_system_package *p;
  93. di_slist_node *node;
  94. /* Traverse the list, return the first menu item that isn't installed */
  95. for (node = list->head; node != NULL; node = node->next) {
  96. p = node->data;
  97. //di_log(DI_LOG_LEVEL_DEBUG, "find default; on: %s", p->p.package);
  98. if (!p->installer_menu_item ||
  99. p->p.status == di_package_status_installed ||
  100. !isinstallable(p)) {
  101. //di_log(DI_LOG_LEVEL_DEBUG, "not menu item; or not installed");
  102. continue;
  103. }
  104. if (p->installer_menu_item < last_successful_item &&
  105. p->installer_menu_item < NEVERDEFAULT) {
  106. //di_log(DI_LOG_LEVEL_DEBUG, "not in range to be default");
  107. continue;
  108. }
  109. /* If menutest says this item should be default, make it so */
  110. if (!isdefault(p)) {
  111. //di_log(DI_LOG_LEVEL_DEBUG, "isdefalt says no");
  112. continue;
  113. }
  114. /* If all of the virtual packages provided by a
  115. package have already been satisfied, do not default
  116. to it. */
  117. if (!provides_installed_virtual_packages(&p->p)) {
  118. //di_log(DI_LOG_LEVEL_DEBUG, "success on this one");
  119. return p;
  120. }
  121. //di_log(DI_LOG_LEVEL_DEBUG, "not default");
  122. }
  123. /* Severely broken, there are no menu items in the sorted list */
  124. return NULL;
  125. }
  126. /* Return the text of the menu entry for PACKAGE, translated to
  127. LANGUAGE if possible. */
  128. static size_t menu_entry(struct debconfclient *debconf, char *language, di_system_package *package, char *buf, size_t size)
  129. {
  130. char question[256];
  131. snprintf(question, sizeof(question), "debian-installer/%s/title", package->p.package);
  132. if (language) {
  133. /* 256 + strlen("Description-.UTF-8") */
  134. char field[256+18];
  135. char *lang, *end, *und;
  136. strcpy(field, "Description-");
  137. lang = language;
  138. do {
  139. end = strchr(lang, ':');
  140. if (end == NULL)
  141. end = lang + strlen(lang);
  142. strncpy(field+12, lang, end-lang);
  143. strcpy(field+12+(end-lang), ".UTF-8");
  144. if (!debconf_metaget(debconf, question, field)) {
  145. strncpy(buf, debconf->value, size);
  146. return strlen (buf);
  147. }
  148. // Was the language of the form xx_YY ?
  149. und = strchr(lang, '_');
  150. if (und != NULL && (und - lang) < (end - lang)) {
  151. strncpy(field+12, lang, und-lang);
  152. strcpy(field+12+(und-lang), ".UTF-8");
  153. if (!debconf_metaget(debconf, question, field)) {
  154. strncpy(buf, debconf->value, size);
  155. return strlen (buf);
  156. }
  157. }
  158. lang = end+1;
  159. } while (*end != '\0');
  160. }
  161. if (!debconf_metaget(debconf, question, "Description")) {
  162. strncpy(buf, debconf->value, size);
  163. return strlen (buf);
  164. }
  165. /* The following fallback case can go away once all packages
  166. have transitioned to the new form. */
  167. di_log(DI_LOG_LEVEL_INFO, "Falling back to the package description for %s", package->p.package);
  168. if (package->p.short_description)
  169. strncpy(buf, package->p.short_description, size);
  170. return strlen (buf);
  171. }
  172. /* Displays the main menu via debconf and returns the selected menu item. */
  173. di_system_package *show_main_menu(di_packages *packages, di_packages_allocator *allocator) {
  174. char *language = NULL;
  175. di_system_package **package_array, *p;
  176. di_slist *list;
  177. di_slist_node *node;
  178. di_system_package *menudefault = NULL, *ret = NULL;
  179. int i = 0, num = 0;
  180. char buf[256], *menu, *s;
  181. int menu_size, menu_used, size;
  182. if (debconf_get(debconf,"debian-installer/language") == 0 && debconf->value)
  183. language = strdup(debconf->value);
  184. for (node = packages->list.head; node; node = node->next) {
  185. p = node->data;
  186. if (((di_system_package *)p)->installer_menu_item)
  187. num++;
  188. }
  189. package_array = di_new (di_system_package *, num + 1);
  190. package_array[num] = NULL;
  191. for (node = packages->list.head; node; node = node->next) {
  192. p = node->data;
  193. if (p->installer_menu_item)
  194. package_array[i++] = node->data;
  195. }
  196. /* Sort by menu number. */
  197. qsort(package_array, num, sizeof (di_system_package *), package_array_compare);
  198. /* Order menu so depended-upon packages come first. */
  199. /* The menu number is really only used to break ties. */
  200. list = di_system_packages_resolve_dependencies_array_permissive (packages, (di_package **) package_array, allocator);
  201. /*
  202. * Generate list of menu choices for debconf.
  203. */
  204. menu = di_malloc(1024);
  205. menu[0] = '\0';
  206. menu_size = 1024;
  207. menu_used = 1;
  208. for (node = list->head; node != NULL; node = node->next) {
  209. p = node->data;
  210. if (!p->installer_menu_item ||
  211. !isinstallable(p))
  212. continue;
  213. size = menu_entry(debconf, language, p, buf, sizeof (buf));
  214. if (menu_used + size + 2 > menu_size)
  215. {
  216. menu_size += 1024;
  217. menu = di_realloc(menu, menu_size);
  218. }
  219. if (*menu)
  220. strcat(menu, ", ");
  221. strcat(menu, buf);
  222. menu_used += size + 2;
  223. }
  224. menudefault = get_default_menu_item(list);
  225. di_slist_free(list);
  226. /* Make debconf show the menu and get the user's choice. */
  227. debconf_settitle(debconf, "debian-installer/main-menu-title");
  228. debconf_capb(debconf);
  229. debconf_subst(debconf, MAIN_MENU, "MENU", menu);
  230. if (menudefault) {
  231. menu_entry(debconf, language, menudefault, buf, sizeof (buf));
  232. debconf_set(debconf, MAIN_MENU, buf);
  233. }
  234. debconf_input(debconf, "medium", MAIN_MENU);
  235. debconf_go(debconf);
  236. debconf_get(debconf, MAIN_MENU);
  237. s = strdup(debconf->value);
  238. /* Figure out which menu item was selected. */
  239. for (i = 0; i < num; i++) {
  240. p = package_array[i];
  241. menu_entry(debconf, language, p, buf, sizeof (buf));
  242. if (strcmp(buf, s) == 0) {
  243. ret = p;
  244. break;
  245. }
  246. }
  247. if (! ret) {
  248. /* This could happen because of a debconf protocol problem
  249. * (for example, leading whitespace in menu items can
  250. * be stripped and confuse the comparisons), or other
  251. * problem. */
  252. di_log(DI_LOG_LEVEL_WARNING, "Internal error! Cannot find \"%s\" in menu.", s);
  253. }
  254. free(language);
  255. free(package_array);
  256. return ret;
  257. }
  258. static int check_special(di_system_package *p);
  259. /*
  260. * Satisfy the dependencies of a virtual package. Its dependencies
  261. * that actually provide the package are presented in a debconf select
  262. * question for the user to pick and choose. Other dependencies are
  263. * just fed recursively through di_config_package.
  264. */
  265. static int satisfy_virtual(di_system_package *p) {
  266. di_slist_node *node;
  267. di_system_package *dep, *defpkg = NULL;
  268. char *language = NULL;
  269. char buf[256], *menu, *s = NULL;
  270. size_t menu_size, menu_used, size;
  271. int is_menu_item = 0;
  272. if (debconf_get(debconf, "debian-installer/language") == 0 && debconf->value)
  273. language = strdup(debconf->value);
  274. menu = di_malloc(1024);
  275. menu[0] = '\0';
  276. menu_size = 1024;
  277. menu_used = 1;
  278. /* Compile a list of providing package. The default choice will be the
  279. * package with highest priority. If we have ties, menu items are
  280. * preferred. If we still have ties, the default choice is arbitrary */
  281. for (node = p->p.depends.head; node; node = node->next) {
  282. di_package_dependency *d = node->data;
  283. dep = (di_system_package *)d->ptr;
  284. if (d->type == di_package_dependency_type_depends) {
  285. /* Non-providing dependency */
  286. di_log(DI_LOG_LEVEL_DEBUG, "non-providing dependency from %s to %s", p->p.package, dep->p.package);
  287. if (dep->p.status != di_package_status_installed &&
  288. di_config_package(dep, satisfy_virtual, check_special) == -1)
  289. return -1;
  290. continue;
  291. }
  292. if (d->type != di_package_dependency_type_reverse_provides)
  293. continue;
  294. if (dep->p.status == di_package_status_installed) {
  295. /* This means that a providing package is already
  296. * configure. So we short-circuit. */
  297. menu_used = 0;
  298. break;
  299. }
  300. if (defpkg == NULL || dep->p.priority > defpkg->p.priority ||
  301. (dep->p.priority == defpkg->p.priority &&
  302. dep->installer_menu_item < defpkg->installer_menu_item))
  303. defpkg = dep;
  304. /* This only makes sense if one of the dependencies
  305. * is a menu item */
  306. if (dep->installer_menu_item)
  307. is_menu_item = 1;
  308. size = menu_entry(debconf, language, dep, buf, sizeof (buf));
  309. if (menu_used + size + 2 > menu_size)
  310. {
  311. menu_size += 1024;
  312. menu = di_realloc(menu, menu_size);
  313. }
  314. if (dep == defpkg) {
  315. /* We want the default to be the first item */
  316. char *temp;
  317. temp = di_malloc (menu_size);
  318. strcpy(temp, menu);
  319. strcpy(menu, buf);
  320. strcat(menu, ", ");
  321. strcat(menu, temp);
  322. di_free(temp);
  323. } else {
  324. strcat(menu, buf);
  325. strcat(menu, ", ");
  326. }
  327. menu_used += size + 2;
  328. }
  329. if (menu_used >= 2)
  330. menu[menu_used-2] = '\0';
  331. if (menu_used > 1) {
  332. if (is_menu_item) {
  333. char *priority = "medium";
  334. /* Only let the user choose if one of them is a menu item */
  335. if (defpkg != NULL) {
  336. menu_entry(debconf, language, defpkg, buf, sizeof(buf));
  337. debconf_set(debconf, MISSING_PROVIDE, buf);
  338. } else {
  339. /* TODO: How to figure out a default? */
  340. priority = "critical";
  341. }
  342. debconf_capb(debconf, "backup");
  343. debconf_subst(debconf, MISSING_PROVIDE, "CHOICES", menu);
  344. debconf_input(debconf, priority, MISSING_PROVIDE);
  345. if (debconf_go(debconf) != 0)
  346. return 0;
  347. debconf_capb(debconf);
  348. debconf_get(debconf, MISSING_PROVIDE);
  349. s = strdup(debconf->value);
  350. }
  351. /* Go through the dependencies again */
  352. for (node = p->p.depends.head; node; node = node->next) {
  353. di_package_dependency *d = node->data;
  354. dep = (di_system_package *)d->ptr;
  355. menu_entry(debconf, language, dep, buf, sizeof(buf));
  356. if (!is_menu_item || strcmp(s, buf) == 0) {
  357. /* Ick. If we have a menu item it has to match the
  358. * debconf choice, otherwise we configure all of
  359. * the providing packages */
  360. if (di_config_package(dep, satisfy_virtual, check_special) == -1)
  361. return -1;
  362. if (is_menu_item)
  363. break;
  364. }
  365. }
  366. }
  367. free(menu);
  368. free(s);
  369. free(language);
  370. /* It doesn't make sense to configure virtual packages,
  371. * since they are, well, virtual. */
  372. p->p.status = di_package_status_installed;
  373. return 1;
  374. }
  375. static void update_language (void) {
  376. debconf_get(debconf, "debian-installer/language");
  377. if (*debconf->value != 0)
  378. setenv("LANGUAGE", debconf->value, 1);
  379. }
  380. static int
  381. check_special(di_system_package *p)
  382. {
  383. di_slist_node *node;
  384. /*
  385. * A language selection package must provide the virtual
  386. * package 'language-selected'.
  387. * The LANGUAGE environment variable must be updated
  388. */
  389. for (node = p->p.depends.head; node; node = node->next) {
  390. di_package_dependency *d = node->data;
  391. if (d->type == di_package_dependency_type_provides && strcmp(d->ptr->package, "language-selected") == 0) {
  392. update_language();
  393. break;
  394. }
  395. }
  396. return 0;
  397. }
  398. static void set_package_title(di_system_package *p) {
  399. char *title;
  400. if (!p->installer_menu_item)
  401. return;
  402. asprintf(&title, "debian-installer/%s/title", p->p.package);
  403. if (debconf_settitle(debconf, title))
  404. di_log(DI_LOG_LEVEL_WARNING, "Unable to set title for %s.", p->p.package);
  405. free(title);
  406. }
  407. static int do_menu_item(di_system_package *p) {
  408. di_log(DI_LOG_LEVEL_DEBUG, "Menu item '%s' selected", p->p.package);
  409. return di_config_package(p, satisfy_virtual, check_special);
  410. }
  411. static char *debconf_priorities[] =
  412. {
  413. "low",
  414. "medium",
  415. "high",
  416. "critical"
  417. };
  418. #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
  419. static void modify_debconf_priority (int raise_or_lower) {
  420. int pri, i;
  421. const char *template = "debconf/priority";
  422. debconf_get(debconf, template);
  423. pri = 1;
  424. if ( debconf->value ) {
  425. for (i = 0; (size_t)i < ARRAY_SIZE(debconf_priorities); ++i) {
  426. if (0 == strcmp(debconf->value,
  427. debconf_priorities[i]) ) {
  428. pri = i;
  429. break;
  430. }
  431. }
  432. }
  433. if (raise_or_lower == LOWER)
  434. --pri;
  435. else if (raise_or_lower == RAISE)
  436. ++pri;
  437. if (0 > pri)
  438. pri = 0;
  439. if (pri > default_priority)
  440. pri = default_priority;
  441. if (local_priority != pri) {
  442. di_log(DI_LOG_LEVEL_INFO, "Modifying debconf priority limit from '%s' to '%s'",
  443. debconf->value ? debconf->value : "(null)",
  444. debconf_priorities[pri] ? debconf_priorities[pri] : "(null)");
  445. local_priority = pri;
  446. debconf_set(debconf, template, debconf_priorities[pri]);
  447. }
  448. }
  449. static void adjust_default_priority (void) {
  450. int pri, i;
  451. const char *template = "debconf/priority";
  452. debconf_get(debconf, template);
  453. pri = 1;
  454. if ( debconf->value ) {
  455. for (i = 0; (size_t)i < ARRAY_SIZE(debconf_priorities); ++i) {
  456. if (0 == strcmp(debconf->value,
  457. debconf_priorities[i]) ) {
  458. pri = i;
  459. break;
  460. }
  461. }
  462. }
  463. if ( pri != local_priority ) {
  464. di_log(DI_LOG_LEVEL_INFO, "Priority changed externally, setting main-menu default to '%s' (%s)",
  465. debconf_priorities[pri] ? debconf_priorities[pri] : "(null)", debconf->value);
  466. local_priority = pri;
  467. default_priority = pri;
  468. }
  469. }
  470. void notify_user_of_failure (di_system_package *p) {
  471. char *language = NULL;
  472. char buf[256];
  473. debconf_capb(debconf);
  474. debconf_get(debconf,"debian-installer/language");
  475. if (debconf->value)
  476. language = strdup(debconf->value);
  477. menu_entry(debconf, language, p, buf, sizeof (buf));
  478. debconf_subst(debconf, ITEM_FAILURE, "ITEM", buf);
  479. debconf_input(debconf, "critical", ITEM_FAILURE);
  480. debconf_go(debconf);
  481. debconf_capb(debconf, "backup");
  482. }
  483. int main (int argc __attribute__ ((unused)), char **argv) {
  484. di_system_package *p;
  485. di_packages *packages;
  486. di_packages_allocator *allocator;
  487. int ret;
  488. debconf = debconfclient_new();
  489. di_system_init(basename(argv[0]));
  490. /* Tell udpkg to shut up. */
  491. setenv("UDPKG_QUIET", "y", 1);
  492. allocator = di_system_packages_allocator_alloc ();
  493. packages = di_system_packages_status_read_file(DI_SYSTEM_DPKG_STATUSFILE, allocator);
  494. while ((p=show_main_menu(packages, allocator))) {
  495. ret = do_menu_item(p);
  496. adjust_default_priority();
  497. switch (ret) {
  498. case EXIT_OK:
  499. /* Success */
  500. if (p->installer_menu_item < NEVERDEFAULT) {
  501. last_successful_item = p->installer_menu_item;
  502. modify_debconf_priority(RAISE);
  503. //di_log(DI_LOG_LEVEL_DEBUG, "Installed package '%s', raising last_successful_item to %d", p->p.package, p->installer_menu_item);
  504. }
  505. else {
  506. // di_log(DI_LOG_LEVEL_DEBUG, "Installed package '%s' but no raise since %d >= %i", p->p.package, p->installer_menu_item, NEVERDEFAULT);
  507. }
  508. break;
  509. case EXIT_BACKUP:
  510. di_log(DI_LOG_LEVEL_INFO, "Menu item '%s' succeeded but requested to be left unconfigured.", p->p.package);
  511. modify_debconf_priority(LOWER);
  512. break;
  513. case EXIT_QUIT:
  514. case EXIT_RESTART:
  515. di_log(DI_LOG_LEVEL_INFO, "Menu item '%s' requested exit with %d.", p->p.package, ret);
  516. return ret;
  517. default:
  518. di_log(DI_LOG_LEVEL_WARNING, "Menu item '%s' failed.", p->p.package);
  519. notify_user_of_failure(p);
  520. modify_debconf_priority(LOWER);
  521. }
  522. di_packages_free (packages);
  523. di_packages_allocator_free (allocator);
  524. allocator = di_system_packages_allocator_alloc ();
  525. packages = di_system_packages_status_read_file(DI_SYSTEM_DPKG_STATUSFILE, allocator);
  526. /* tell cdebconf to save the database */
  527. debconf->command(debconf, "X_SAVE", NULL);
  528. }
  529. return EXIT_FAILURE;
  530. }
  531. /*
  532. * Configure all dependencies, special case for virtual packages.
  533. * This is done depth-first.
  534. */
  535. static int di_config_package(di_system_package *p,
  536. int (*virtfunc)(di_system_package *),
  537. int (*walkfunc)(di_system_package *)) {
  538. char *configcommand;
  539. int ret;
  540. di_slist_node *node;
  541. di_system_package *dep;
  542. di_log(DI_LOG_LEVEL_DEBUG, "configure %s, status: %d\n", p->p.package, p->p.status);
  543. if (p->p.type == di_package_type_virtual_package) {
  544. di_log(DI_LOG_LEVEL_DEBUG, "virtual package %s\n", p->p.package);
  545. if (virtfunc)
  546. return virtfunc(p);
  547. else
  548. return -1;
  549. }
  550. else if (p->p.type == di_package_type_non_existent)
  551. return 0;
  552. for (node = p->p.depends.head; node; node = node->next) {
  553. di_package_dependency *d = node->data;
  554. dep = (di_system_package *)d->ptr;
  555. if (dep->p.status == di_package_status_installed)
  556. continue;
  557. if (d->type != di_package_dependency_type_depends)
  558. continue;
  559. /* Recursively configure this package */
  560. if (di_config_package(dep, virtfunc, walkfunc) == -1)
  561. return -1;
  562. }
  563. set_package_title(p);
  564. if (asprintf(&configcommand, "exec udpkg --configure --force-configure %s", p->p.package) == -1)
  565. return -1;
  566. ret = di_exec_shell_log(configcommand);
  567. ret = di_exec_mangle_status(ret);
  568. free(configcommand);
  569. switch (ret) {
  570. case EXIT_OK:
  571. case EXIT_QUIT:
  572. case EXIT_RESTART:
  573. p->p.status = di_package_status_installed;
  574. if (walkfunc != NULL)
  575. walkfunc(p);
  576. break;
  577. default:
  578. di_log(DI_LOG_LEVEL_WARNING, "Configuring '%s' failed with error code %d", p->p.package, ret);
  579. ret = -1;
  580. case EXIT_BACKUP:
  581. p->p.status = di_package_status_half_configured;
  582. break;
  583. }
  584. return ret;
  585. }
  586. /* vim: noexpandtab sw=8
  587. */