From: Davidlohr Bueso <dave@xxxxxxx> fdisk: properly handle SIGINTs for better user control Based on the TODO file, add the following feature: * catch SIGINT (Ctrl-C) and return to main menu. From Red Hat bugzilla #545488: While using fdisk normally, if you accidentally pressed the wrong button (to start a sequence of questions for some operation, e.g. 'c' to create partition). The tool tries too hard to keep asking you for valid input. You can't provide a blank or invalid input to get it to break out of the current dialog sequence and get back to the main menu. Summary of changes: * move main menu's logic out of main() into main_menu() * setup signal handler to either exit (if already in main menu) or go back to the main menu All fdisk regression tests passed. Some examples: root@cowboy:~/projects/util-linux-git/fdisk# ./fdisk /dev/sda Command (m for help): x Expert command (m for help): ^C Command (m for help): a Partition number (1-5): ^C Command (m for help): d Partition number (1-5): ^C Command (m for help): ^Croot@cowboy:~/projects/util-linux-git/fdisk# Signed-off-by: Davidlohr Bueso <dave@xxxxxxx> --- TODO | 9 -- fdisk/fdisk.c | 261 ++++++++++++++++++++++++++++++++++----------------------- fdisk/fdisk.h | 10 ++ 3 files changed, 165 insertions(+), 115 deletions(-) diff --git a/TODO b/TODO index e827ac3..844e072 100644 --- a/TODO +++ b/TODO @@ -45,15 +45,6 @@ fdisk(s) * use off_t instead "long long" - * catch SIGINT (Ctrl-C) and return to main menu. - From Red Hat bugzilla #545488: - - While using fdisk normally, if you accidentally pressed the wrong button (to - start a sequence of questions for some operation, e.g. 'c' to create - partition). The tool tries too hard to keep asking you for valid input. You - can't provide a blank or invalid input to get it to break out of the current - dialog sequence and get back to the main menu. - * fdisk/* refactoring * add GPT support diff --git a/fdisk/fdisk.c b/fdisk/fdisk.c index 1f841d1..df8bb49 100644 --- a/fdisk/fdisk.c +++ b/fdisk/fdisk.c @@ -6,6 +6,9 @@ * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation: either version 1 or * (at your option) any later version. + * + * Nov. 2010 - Davidlohr Bueso <dave@xxxxxxx> + * -catch SIGINTs and return to main menu or exit */ #include <unistd.h> @@ -21,6 +24,7 @@ #include <sys/time.h> #include <time.h> #include <limits.h> +#include <signal.h> #include "nls.h" #include "blkdev.h" @@ -72,6 +76,8 @@ static void delete_partition(int i); s |= (sector >> 2) & 0xc0; \ } +static void main_menu(void); + /* A valid partition table sector ends in 0x55 0xaa */ static unsigned int part_table_flag(unsigned char *b) { @@ -392,7 +398,10 @@ is_dos_partition(int t) { static void menu(void) { + if (sun_label) { + unset_mainmenu(); + puts(_("Command action")); puts(_(" a toggle a read only flag")); /* sun */ puts(_(" b edit bsd disklabel")); @@ -412,6 +421,8 @@ menu(void) { puts(_(" x extra functionality (experts only)")); } else if (sgi_label) { + unset_mainmenu(); + puts(_("Command action")); puts(_(" a select bootable partition")); /* sgi flavour */ puts(_(" b edit bootfile entry")); /* sgi */ @@ -430,6 +441,8 @@ menu(void) { puts(_(" w write table to disk and exit")); } else if (aix_label || mac_label) { + unset_mainmenu(); + puts(_("Command action")); puts(_(" m print this menu")); puts(_(" o create a new empty DOS partition table")); @@ -1523,6 +1536,8 @@ get_partition_dflt(int warn, int max, int dflt) { struct pte *pe; int i; + unset_mainmenu(); + i = read_int(1, dflt, max, 0, _("Partition number")) - 1; pe = &ptes[i]; @@ -2471,6 +2486,8 @@ static void new_partition(void) { int i, free_primary = 0; + unset_mainmenu(); + if (warn_geometry()) return; @@ -2718,6 +2735,8 @@ static void xselect(void) { char c; + unset_mainmenu(); + while(1) { putchar('\n'); c = tolower(read_char(_("Expert command (m for help): "))); @@ -2920,8 +2939,138 @@ static void unknown_command(int c) { printf(_("%c: unknown command\n"), c); } + +static void +sighand(int sig) { + if (sig == SIGINT) + is_mainmenu() ? _exit(EXIT_SUCCESS) : main_menu(); +} + +static void +sigsetup(void) { + struct sigaction act; + + memset(&act, 0x0, sizeof(act)); + act.sa_handler = &sighand; + /* allow SIGINT to concurrently be handled */ + act.sa_flags = SA_NODEFER; + sigaction(SIGINT, &act, NULL); +} + +static void +main_menu(void) { + int c, j; + set_mainmenu(); + + while (1) { + putchar('\n'); + c = tolower(read_char(_("Command (m for help): "))); + switch (c) { + pause(); + case 'a': + if (dos_label) + toggle_active(get_partition(1, partitions)); + else if (sun_label) + toggle_sunflags(get_partition(1, partitions), + SUN_FLAG_UNMNT); + else if (sgi_label) + sgi_set_bootpartition( + get_partition(1, partitions)); + else + unknown_command(c); + break; + case 'b': + if (sgi_label) { + printf(_("\nThe current boot file is: %s\n"), + sgi_get_bootfile()); + if (read_chars(_("Please enter the name of the " + "new boot file: ")) == '\n') + printf(_("Boot file unchanged\n")); + else + sgi_set_bootfile(line_ptr); + } else + bselect(); + break; + case 'c': + if (dos_label) + toggle_dos_compatibility_flag(); + else if (sun_label) + toggle_sunflags(get_partition(1, partitions), + SUN_FLAG_RONLY); + else if (sgi_label) + sgi_set_swappartition( + get_partition(1, partitions)); + else + unknown_command(c); + break; + case 'd': + /* If sgi_label then don't use get_existing_partition, + let the user select a partition, since + get_existing_partition() only works for Linux-like + partition tables */ + if (!sgi_label) { + j = get_existing_partition(1, partitions); + } else { + j = get_partition(1, partitions); + } + if (j >= 0) + delete_partition(j); + break; + case 'i': + if (sgi_label) + create_sgiinfo(); + else + unknown_command(c); + case 'l': + list_types(get_sys_types()); + break; + case 'm': + menu(); + break; + case 'n': + new_partition(); + break; + case 'o': + create_doslabel(); + break; + case 'p': + list_table(0); + break; + case 'q': + close(fd); + printf("\n"); + exit(0); + case 's': + create_sunlabel(); + break; + case 't': + change_sysid(); + break; + case 'u': + change_units(); + break; + case 'v': + verify(); + break; + case 'w': + write_table(); /* does not return */ + break; + case 'x': + if (sgi_label) { + fprintf(stderr, + _("\n\tSorry, no experts menu for SGI " + "partition tables available.\n\n")); + } else + xselect(); + break; + default: + unknown_command(c); + menu(); + } + } +} int main(int argc, char **argv) { @@ -2931,7 +3080,10 @@ main(int argc, char **argv) { setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - + + unset_mainmenu(); + sigsetup(); + while ((c = getopt(argc, argv, "b:c::C:hH:lsS:u::vV")) != -1) { switch (c) { case 'b': @@ -3065,110 +3217,7 @@ main(int argc, char **argv) { /* If we return we may want to make an empty DOS label? */ } - while (1) { - putchar('\n'); - c = tolower(read_char(_("Command (m for help): "))); - switch (c) { - case 'a': - if (dos_label) - toggle_active(get_partition(1, partitions)); - else if (sun_label) - toggle_sunflags(get_partition(1, partitions), - SUN_FLAG_UNMNT); - else if (sgi_label) - sgi_set_bootpartition( - get_partition(1, partitions)); - else - unknown_command(c); - break; - case 'b': - if (sgi_label) { - printf(_("\nThe current boot file is: %s\n"), - sgi_get_bootfile()); - if (read_chars(_("Please enter the name of the " - "new boot file: ")) == '\n') - printf(_("Boot file unchanged\n")); - else - sgi_set_bootfile(line_ptr); - } else - bselect(); - break; - case 'c': - if (dos_label) - toggle_dos_compatibility_flag(); - else if (sun_label) - toggle_sunflags(get_partition(1, partitions), - SUN_FLAG_RONLY); - else if (sgi_label) - sgi_set_swappartition( - get_partition(1, partitions)); - else - unknown_command(c); - break; - case 'd': - /* If sgi_label then don't use get_existing_partition, - let the user select a partition, since - get_existing_partition() only works for Linux-like - partition tables */ - if (!sgi_label) { - j = get_existing_partition(1, partitions); - } else { - j = get_partition(1, partitions); - } - if (j >= 0) - delete_partition(j); - break; - case 'i': - if (sgi_label) - create_sgiinfo(); - else - unknown_command(c); - case 'l': - list_types(get_sys_types()); - break; - case 'm': - menu(); - break; - case 'n': - new_partition(); - break; - case 'o': - create_doslabel(); - break; - case 'p': - list_table(0); - break; - case 'q': - close(fd); - printf("\n"); - exit(0); - case 's': - create_sunlabel(); - break; - case 't': - change_sysid(); - break; - case 'u': - change_units(); - break; - case 'v': - verify(); - break; - case 'w': - write_table(); /* does not return */ - break; - case 'x': - if (sgi_label) { - fprintf(stderr, - _("\n\tSorry, no experts menu for SGI " - "partition tables available.\n\n")); - } else - xselect(); - break; - default: - unknown_command(c); - menu(); - } - } + main_menu(); + return 0; } diff --git a/fdisk/fdisk.h b/fdisk/fdisk.h index 1a89beb..e837c37 100644 --- a/fdisk/fdisk.h +++ b/fdisk/fdisk.h @@ -100,6 +100,16 @@ extern int sgi_label; extern int aix_label; extern int mac_label; +/* + * let's us know in what part of the program were are in + * to properly handle SIGINTs + */ +volatile int in_mainmenu; +/* wrappers to not directly access in_mainmenu */ +#define set_mainmenu() in_mainmenu = 1 +#define unset_mainmenu() in_mainmenu = 0 +#define is_mainmenu() in_mainmenu == 1 + /* prototypes for fdiskbsdlabel.c */ extern void bselect(void); extern int check_osf_label(void); -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html