[PATCH] fdisk: properly handle SIGINTs for better user control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux