1 /* Required for strdup, snprintf, getopt */
2 #define _XOPEN_SOURCE 500
12 #include <sys/select.h>
14 #include <gdk/gdkkeysyms.h>
16 typedef struct sProperty
22 typedef struct sDevice
33 int parse_property(char *str, int size, Property *prop)
38 for(i=0; (equals<0 && i<size); ++i)
45 prop->name = malloc(equals+1);
46 strncpy(prop->name, str, equals);
47 prop->name[equals] = 0;
49 prop->value = malloc(size-equals);
50 strncpy(prop->value, str+equals+1, size-equals-1);
51 prop->value[size-equals-1] = 0;
56 Property *get_device_properties(char *node)
67 printf("Running udevadm info -q property -n \"%s\"\n", node);
72 execl("/sbin/udevadm", "udevadm", "info", "-q", "property", "-n", node, NULL);
81 Property *props = NULL;
87 buf = (char *)malloc(bufsize);
99 len = read(pipe_fd[0], buf+pos, bufsize-pos);
108 for(i=0; (newline<0 && i<pos); ++i)
117 buf = (char *)realloc(buf, bufsize);
121 if(parse_property(buf, newline, &prop)==0)
123 props = (Property *)realloc(props, (n_props+2)*sizeof(Property));
124 props[n_props] = prop;
127 memmove(buf, buf+newline+1, pos-newline-1);
138 props[n_props].name = NULL;
139 props[n_props].value = NULL;
142 waitpid(pid, NULL, 0);
156 char *get_property_value(Property *props, char *name)
159 for(i=0; props[i].name; ++i)
160 if(strcmp(props[i].name, name)==0)
161 return props[i].value;
165 int match_property_value(Property *props, char *name, char *value)
167 char *v = get_property_value(props, name);
170 return strcmp(v, value)==0;
173 void free_properties(Property *props)
178 for(i=0; props[i].name; ++i)
181 free(props[i].value);
186 char **get_mount_entries(char *filename, int (*predicate)(struct mntent *))
190 char **devices = NULL;
193 file = setmntent(filename, "r");
197 while((me = getmntent(file)))
198 if(!predicate || predicate(me))
200 devices = (char **)realloc(devices, (n_devices+2)*sizeof(char *));
201 devices[n_devices] = strdup(me->mnt_fsname);
207 devices[n_devices] = NULL;
212 char **get_mounted_devices(void)
214 return get_mount_entries("/etc/mtab", NULL);
217 int is_user_mountable(struct mntent *me)
219 return hasmntopt(me, "user")!=NULL;
222 char **get_fstab_devices(void)
224 return get_mount_entries("/etc/fstab", &is_user_mountable);
227 int is_in_array(char **names, char *devname)
230 if(!names || !devname)
232 for(i=0; names[i]; ++i)
233 if(!strcmp(devname, names[i]))
238 void free_device_names(char **names)
243 for(i=0; names[i]; ++i)
248 int is_removable(char *devpath)
255 len = snprintf(fnbuf, sizeof(fnbuf), "/sys%s", devpath);
256 if(len+10>=(int)sizeof(fnbuf))
259 for(ptr=fnbuf+len; (ptr>fnbuf && *ptr!='/'); --ptr) ;
260 strcpy(ptr, "/removable");
261 fd = open(fnbuf, O_RDONLY);
270 printf(" Removable\n");
274 printf(" Not removable\n");
280 int check_buses(char *devpath, char **buses)
286 len = snprintf(fnbuf, sizeof(fnbuf), "/sys%s", devpath);
287 if(len+10>=(int)sizeof(fnbuf))
290 for(ptr=fnbuf+len; ptr>fnbuf+12; --ptr)
294 strcpy(ptr, "/subsystem");
295 len = readlink(fnbuf, linkbuf, sizeof(linkbuf)-1);
302 for(; (len>0 && linkbuf[len-1]!='/'); --len) ;
304 printf(" Subsystem of %s is %s\n", fnbuf, linkbuf+len);
305 for(i=0; buses[i]; ++i)
306 if(strcmp(linkbuf+len, buses[i])==0)
314 int can_mount(Property *props, char **allowed)
316 static char *removable_buses[] = { "usb", "firewire", 0 };
321 devname = get_property_value(props, "DEVNAME");
322 if(is_in_array(allowed, devname))
325 if(match_property_value(props, "ID_TYPE", "cd") && match_property_value(props, "ID_CDROM_MEDIA", "1"))
328 if(!match_property_value(props, "DEVTYPE", "partition"))
331 devpath = get_property_value(props, "DEVPATH");
332 if(is_removable(devpath))
335 bus = get_property_value(props, "ID_BUS");
336 if(is_in_array(removable_buses, bus))
339 return check_buses(devpath, removable_buses);
342 char **get_device_nodes(char *dirname)
351 char **checked = NULL;
355 dir = opendir(dirname);
359 while((de = readdir(dir)))
364 if(de->d_name[0]=='.' && (de->d_name[1]==0 || (de->d_name[1]=='.' && de->d_name[2]==0)))
367 snprintf(fnbuf, sizeof(fnbuf), "%s/%s", dirname, de->d_name);
371 if(S_ISLNK(st.st_mode))
374 len = readlink(fnbuf, linkbuf, sizeof(linkbuf)-1);
384 for(i=0; (!duplicate && i<n_checked); ++i)
385 if(strcmp(node, checked[i])==0)
391 printf("Device %s is a duplicate\n", fnbuf);
395 checked = (char **)realloc(checked, (n_checked+1)*sizeof(char *));
396 checked[n_checked] = strdup(node);
399 nodes = (char **)realloc(nodes, (n_nodes+2)*sizeof(char *));
400 nodes[n_nodes] = strdup(fnbuf);
407 for(i=0; i<n_checked; ++i)
413 nodes[n_nodes] = NULL;
418 Device *get_devices(void)
421 Device *devices = NULL;
423 char **mounted = NULL;
427 nodes = get_device_nodes("/dev/disk/by-id");
428 mounted = get_mounted_devices();
429 fstab = get_fstab_devices();
431 for(i=0; nodes[i]; ++i)
436 printf("Examining device %s\n", nodes[i]);
438 props = get_device_properties(nodes[i]);
442 printf(" No properties\n");
449 for(j=0; props[j].name; ++j)
450 printf(" %s = %s\n", props[j].name, props[j].value);
453 if(can_mount(props, fstab))
464 printf(" Using device\n");
466 devname = get_property_value(props, "DEVNAME");
468 label = get_property_value(props, "ID_FS_LABEL");
470 label = get_property_value(props, "ID_FS_UUID");
476 for(ptr=label; *ptr; ++ptr)
481 vendor = get_property_value(props, "ID_VENDOR");
482 model = get_property_value(props, "ID_MODEL");
484 pos = snprintf(buf, sizeof(buf), "%s", label);
486 pos += snprintf(buf+pos, sizeof(buf)-pos, " (%s %s)", vendor, model);
490 devices = (Device *)realloc(devices, (n_devices+2)*sizeof(Device));
491 devices[n_devices].node = nodes[i];
492 devices[n_devices].label = strdup(label);
493 devices[n_devices].description = strdup(buf);
494 devices[n_devices].mounted = is_in_array(mounted, devname);
495 devices[n_devices].time = st.st_mtime;
500 free_properties(props);
504 free_device_names(mounted);
508 devices[n_devices].node = NULL;
509 devices[n_devices].label = NULL;
510 devices[n_devices].description = NULL;
516 void free_devices(Device *devices)
521 for(i=0; devices[i].node; ++i)
523 free(devices[i].node);
524 free(devices[i].label);
525 free(devices[i].description);
530 void row_activated(GtkTreeView *list, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
534 int umount = *(int *)user_data;
536 model = gtk_tree_view_get_model(list);
538 if(gtk_tree_model_get_iter(model, &iter, path))
544 gtk_tree_model_get(model, &iter, 1, &device, -1);
554 printf("Running pumount %s\n", device->node);
556 printf("Running pmount %s %s\n", device->node, device->label);
564 execl("/usr/bin/pumount", "pumount", device->node, NULL);
566 execl("/usr/bin/pmount", "pmount", device->node, device->label, NULL);
575 struct timeval timeout;
579 FD_SET(pipe_fd[0], &fds);
581 timeout.tv_usec = 200000;
585 if(select(pipe_fd[0]+1, &fds, NULL, NULL, &timeout))
589 len = read(pipe_fd[0], buf+pos, sizeof(buf)-pos-1);
594 else if(waitpid(pid, &status, 0))
602 waitpid(pid, &status, 0);
608 if(WIFEXITED(status))
610 if(WEXITSTATUS(status))
611 printf("Command exited successfully\n");
613 printf("Command exited with status %d\n", WEXITSTATUS(status));
615 else if(WIFSIGNALED(status))
616 printf("Command terminated with signal %d\n", WTERMSIG(status));
618 printf("Command exited with unknown result %04X\n", status);
621 if(!WIFEXITED(status) || WEXITSTATUS(status))
625 dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", buf);
626 g_signal_connect(dialog, "response", >k_main_quit, NULL);
627 gtk_widget_show_all(dialog);
637 gboolean key_press(GtkWidget *widget, GdkEvent *event, gpointer user_data)
639 if(event->key.keyval==GDK_KEY_Escape)
651 int main(int argc, char **argv)
657 GtkTreeSelection *selection;
666 gtk_init(&argc, &argv);
668 while((opt = getopt(argc, argv, "vu"))!=-1) switch(opt)
678 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
679 gtk_container_set_border_width(GTK_CONTAINER(window), 5);
680 g_signal_connect(window, "destroy", G_CALLBACK(>k_main_quit), NULL);
681 g_signal_connect(window, "key-press-event", G_CALLBACK(&key_press), NULL);
683 viewport = gtk_viewport_new(NULL, NULL);
684 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_IN);
685 gtk_container_add(GTK_CONTAINER(window), viewport);
687 list = gtk_tree_view_new();
688 gtk_container_add(GTK_CONTAINER(viewport), list);
689 g_signal_connect(list, "row-activated", G_CALLBACK(&row_activated), &umount);
691 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
692 gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
693 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list),
694 -1, "Device", gtk_cell_renderer_text_new(), "text", 0, NULL);
696 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
698 devices = get_devices();
703 for(i=0; devices[i].node; ++i)
704 if(!devices[i].mounted==!umount)
706 gtk_list_store_append(store, &iter);
707 gtk_list_store_set(store, &iter, 0, devices[i].description, 1, &devices[i], -1);
708 if(devices[i].time>latest)
710 latest = devices[i].time;
711 gtk_tree_selection_select_iter(selection, &iter);
720 gtk_widget_show_all(window);
725 dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
726 "No devices to %s", (umount ? "unmount" : "mount"));
727 g_signal_connect(dialog, "response", G_CALLBACK(>k_main_quit), NULL);
728 gtk_widget_show_all(dialog);
733 free_devices(devices);