X-Git-Url: http://git.tdb.fi/?p=pmount-gui.git;a=blobdiff_plain;f=main.c;h=9172ab6d7b9d049c6c432b063e24c1f82caa2d93;hp=24b87fc5994ee8e5c70a4e4a58822eff14b38117;hb=b70580815b02ba5ccf83d8d96c5138afbc7124ae;hpb=2853a5c24eb6daa351687f6685f5fe71560412b7 diff --git a/main.c b/main.c index 24b87fc..9172ab6 100644 --- a/main.c +++ b/main.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -33,12 +34,20 @@ typedef struct sDevice typedef struct sGuiContext { int manager; + int autohide; + Device *devices; + GtkWidget *window; GtkWidget *list; GtkWidget *button; char *post_mount_command; + int inotify_fd; + int dev_wd; + GIOChannel *inotify_channel; + int refresh_pending; } GuiContext; int verbosity = 0; +char **pmount_argv = NULL; /** Parses a string of the form name=value and places the components in a Property @@ -74,6 +83,7 @@ the array. */ Property *get_device_properties(char *node) { + static const char *udevadm_path = NULL; int pid; int pipe_fd[2]; char *buf; @@ -83,6 +93,22 @@ Property *get_device_properties(char *node) Property *props = NULL; int n_props = 0; + if(!udevadm_path) + { + struct stat st; + udevadm_path = "/bin/udevadm"; + if(stat(udevadm_path, &st)<0 || !(st.st_mode&0111)) + { + udevadm_path = "/sbin/udevadm"; + if(stat(udevadm_path, &st)<0 || !(st.st_mode&0111)) + { + udevadm_path = NULL; + perror("Unable to find udevadm"); + return NULL; + } + } + } + pipe(pipe_fd); pid = fork(); @@ -95,7 +121,9 @@ Property *get_device_properties(char *node) close(pipe_fd[0]); dup2(pipe_fd[1], 1); - execl("/sbin/udevadm", "udevadm", "info", "-q", "property", "-n", node, NULL); + if(execl(udevadm_path, "udevadm", "info", "-q", "property", "-n", node, NULL)<0) + fprintf(stderr, "Unable to execute %s: %s\n", udevadm_path, strerror(errno)); + _exit(1); } else if(pid<0) @@ -515,7 +543,6 @@ Device *get_devices(void) char **nodes = NULL; Device *devices = NULL; int n_devices = 0; - char **mounted = NULL; char **fstab = NULL; int i; @@ -534,6 +561,7 @@ Device *get_devices(void) { if(verbosity>=2) printf(" No properties\n"); + free(nodes[i]); continue; } @@ -599,7 +627,7 @@ Device *get_devices(void) } free(nodes); - free_string_array(mounted); + free_string_array(fstab); if(devices) { @@ -682,12 +710,31 @@ int toggle_device(Device *device, char *out_buf, int out_size) if(pid==0) { /* Child process */ + + /* Complete construction of pmount call */ + int last; + + pmount_argv[0] = "pmount"; + + last = 0; + while(pmount_argv[++last]); + + pmount_argv[last] = device->node; + pmount_argv[last+1] = mount_point+7; + pmount_argv[last+2] = NULL; + if(verbosity>=1) { if(umount) printf("Running pumount %s\n", device->node); else - printf("Running pmount %s %s\n", device->node, mount_point+7); + { + int i = 0; + printf("Running pmount"); + while(pmount_argv[++i]) + printf(" %s", pmount_argv[i]); + printf("\n"); + } } close(pipe_fd[0]); @@ -697,7 +744,7 @@ int toggle_device(Device *device, char *out_buf, int out_size) if(umount) execl("/usr/bin/pumount", "pumount", device->node, NULL); else - execl("/usr/bin/pmount", "pmount", device->node, mount_point+7, NULL); + execvp("/usr/bin/pmount", pmount_argv); _exit(1); } else if(pid<0) @@ -766,6 +813,82 @@ int toggle_device(Device *device, char *out_buf, int out_size) return 0; } +/** +Refreshes both the internal devices array and the GUI list. +*/ +int refresh_devices(GuiContext *context, int umount) +{ + GtkListStore *store; + GtkTreeSelection *selection; + int n_listed; + time_t latest; + int i; + GtkTreeIter iter; + + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(context->list))); + gtk_list_store_clear(store); + + free_devices(context->devices); + + context->devices = get_devices(); + if(!context->devices) + return 0; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context->list)); + + /* Populate the list with devices in appropriate state. */ + n_listed = 0; + latest = 0; + for(i=0; context->devices[i].node; ++i) + { + Device *dev; + + dev = &context->devices[i]; + if(umount<0 || !dev->mount_point==!umount) + { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + 0, dev->description, + 1, dev, + 2, !!dev->mount_point, + 3, dev->mount_point, + -1); + if(dev->time>latest) + { + /* Pre-select the device that appeared on the system most recently. */ + latest = dev->time; + gtk_tree_selection_select_iter(selection, &iter); + } + + ++n_listed; + } + } + + return n_listed; +} + +/** +Handles an automatic refresh of the device list in response to inotify events. +*/ +gboolean refresh_devices_idle(gpointer data) +{ + GuiContext *context = (GuiContext *)data; + int n_listed; + + n_listed = refresh_devices(context, -1); + context->refresh_pending = 0; + + if(context->autohide) + { + if(n_listed) + gtk_widget_show_all(context->window); + else + gtk_widget_hide(context->window); + } + + return FALSE; +} + /** Callback for selection in the device list changing. Updates the action button label according to device status. @@ -777,7 +900,9 @@ void selection_changed(GtkTreeSelection *selection, gpointer user_data) GtkTreeModel *model; Device *device; - gtk_tree_selection_get_selected(selection, &model, &iter); + if(!gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + gtk_tree_model_get(model, &iter, 1, &device, -1); gtk_button_set_label(GTK_BUTTON(context->button), (device->mount_point ? "Unmount" : "Mount")); } @@ -888,42 +1013,71 @@ gboolean key_press(GtkWidget *widget, GdkEvent *event, gpointer user_data) return FALSE; } +/** +Callback for inotify events. +*/ +gboolean inotify_event_available(GIOChannel *source, GIOCondition condition, gpointer user_data) +{ + GuiContext *context = (GuiContext *)user_data; + int fd; + char eventbuf[sizeof(struct inotify_event)+NAME_MAX+1]; + int len; + + fd = g_io_channel_unix_get_fd(source); + len = read(fd, eventbuf, sizeof(eventbuf)); + if(len>=(int)sizeof(struct inotify_event)) + { + if(!context->refresh_pending) + { + g_timeout_add(500, &refresh_devices_idle, context); + context->refresh_pending = 1; + } + } + + (void)condition; + + return TRUE; +} + void show_help(void) { printf("pmount-gui\n" - "Copyright (c) 2011-2015 Mikko Rasa, Mikkosoft Productions\n\n" - "Usage: pmount-gui [-v] [-u] [-r ] [-h]\n\n" + "Copyright (c) 2011-2016 Mikko Rasa, Mikkosoft Productions\n\n" + "Usage: pmount-gui [-v] [-u] [-r ] [-m|-M] [-h] [-- ]\n\n" "Options:\n" " -v Increase verbosity\n" " -u Unmount a device (default is mount)\n" " -r Run a command after mounting\n" " -m Start a persistent mount manager\n" + " -M Like -m, but hide the window if there are no devices\n" " -h Display this help\n"); } int main(int argc, char **argv) { GuiContext context; - GtkWidget *window; GtkWidget *box; GtkWidget *viewport; GtkListStore *store; GtkTreeSelection *selection; GtkCellRenderer *mounted_toggle; - GtkTreeIter iter; - Device *devices; - int i; - time_t latest; int opt; int umount = 0; int n_listed; + int i; context.manager = 0; + context.autohide = 0; + context.devices = NULL; context.post_mount_command = NULL; + context.inotify_fd = -1; + context.dev_wd = -1; + context.inotify_channel = NULL; + context.refresh_pending = 0; gtk_init(&argc, &argv); - while((opt = getopt(argc, argv, "vur:mh"))!=-1) switch(opt) + while((opt = getopt(argc, argv, "vur:mMh"))!=-1) switch(opt) { case 'v': ++verbosity; @@ -936,19 +1090,33 @@ int main(int argc, char **argv) break; case 'm': context.manager = 1; + context.autohide = 0; + break; + case 'M': + context.manager = 1; + context.autohide = 1; break; case 'h': show_help(); return 0; } - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_container_set_border_width(GTK_CONTAINER(window), 5); - g_signal_connect(window, "destroy", G_CALLBACK(>k_main_quit), NULL); - g_signal_connect(window, "key-press-event", G_CALLBACK(&key_press), NULL); + /* argc - optind equals to number of options after "--" */ + /* One empty element at the beginning is for program name for execvp */ + /* Three empty elements at the end are required options for pmount + NULL */ + + pmount_argv = malloc(sizeof(char *)*(argc-optind+4)); + + for(i=0; i<(argc-optind); ++i) + pmount_argv[i+1] = argv[optind+i]; + pmount_argv[argc-optind+1] = NULL; + + context.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(context.window), 5); + g_signal_connect(context.window, "destroy", G_CALLBACK(>k_main_quit), NULL); box = gtk_vbox_new(FALSE, 5); - gtk_container_add(GTK_CONTAINER(window), box); + gtk_container_add(GTK_CONTAINER(context.window), box); viewport = gtk_viewport_new(NULL, NULL); gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_IN); @@ -980,7 +1148,22 @@ int main(int argc, char **argv) gtk_tree_view_column_add_attribute(mounted_column, mount_point_renderer, "text", 3); gtk_tree_view_insert_column(GTK_TREE_VIEW(context.list), mounted_column, -1); + + context.inotify_fd = inotify_init(); + if(context.inotify_fd>=0) + { + context.dev_wd = inotify_add_watch(context.inotify_fd, "/dev/disk/by-id", IN_CREATE|IN_DELETE); + + context.inotify_channel = g_io_channel_unix_new(context.inotify_fd); + g_io_add_watch(context.inotify_channel, G_IO_IN, &inotify_event_available, &context); + } + else + printf("Warning: Unable to initialize inotify\n"); + + umount = -1; } + else + g_signal_connect(context.window, "key-press-event", G_CALLBACK(&key_press), NULL); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context.list)); g_signal_connect(selection, "changed", G_CALLBACK(&selection_changed), &context); @@ -989,37 +1172,11 @@ int main(int argc, char **argv) g_signal_connect(context.button, "clicked", G_CALLBACK(&button_clicked), &context); gtk_box_pack_start(GTK_BOX(box), context.button, FALSE, TRUE, 0); - devices = get_devices(); - n_listed = 0; - if(devices) - { - /* Populate the list with devices in appropriate state. */ - latest = 0; - for(i=0; devices[i].node; ++i) - if(!devices[i].mount_point==!umount || context.manager) - { - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - 0, devices[i].description, - 1, &devices[i], - 2, !!devices[i].mount_point, - 3, devices[i].mount_point, - -1); - if(devices[i].time>latest) - { - /* Pre-select the device that appeared on the system most recently. */ - latest = devices[i].time; - gtk_tree_selection_select_iter(selection, &iter); - } + n_listed = refresh_devices(&context, umount); - ++n_listed; - } - - } - - if(n_listed || context.manager) - gtk_widget_show_all(window); - else + if(n_listed || (context.manager && !context.autohide)) + gtk_widget_show_all(context.window); + else if(!context.manager) { GtkWidget *dialog; @@ -1032,7 +1189,11 @@ int main(int argc, char **argv) gtk_main(); - free_devices(devices); + free_devices(context.devices); + if(context.inotify_fd>=0) + close(context.inotify_fd); + + free(pmount_argv); return 0; }