Omit mounted devices from the list
[pmount-gui.git] / main.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <dirent.h>
5 #include <mntent.h>
6 #include <sys/stat.h>
7 #include <sys/wait.h>
8 #include <gtk/gtk.h>
9
10 typedef struct sProperty
11 {
12         char *name;
13         char *value;
14 } Property;
15
16 typedef struct sDevice
17 {
18         char *node;
19         char *label;
20         char *description;
21         int mounted;
22         time_t time;
23 } Device;
24
25 int parse_property(char *str, int size, Property *prop)
26 {
27         int equals = -1;
28         int i;
29
30         for(i=0; (equals<0 && i<size); ++i)
31                 if(str[i]=='=')
32                         equals = i;
33
34         if(equals<0)
35                 return -1;
36
37         prop->name = malloc(equals+1);
38         strncpy(prop->name, str, equals);
39         prop->name[equals] = 0;
40
41         prop->value = malloc(size-equals);
42         strncpy(prop->value, str+equals+1, size-equals-1);
43         prop->value[size-equals-1] = 0;
44
45         return 0;
46 }
47
48 Property *get_device_properties(char *node)
49 {
50         int pid;
51         int pipe_fd[2];
52
53         /*printf("Examining device %s\n", node);*/
54
55         pipe(pipe_fd);
56
57         pid = fork();
58         if(pid==0)
59         {
60                 close(pipe_fd[0]);
61                 dup2(pipe_fd[1], 1);
62
63                 execl("/sbin/udevadm", "udevadm", "info", "-q", "property", "-n", node, NULL);
64                 _exit(1);
65         }
66         else if(pid>0)
67         {
68                 char buf[256];
69                 int pos = 0;
70                 int eof = 0;
71                 Property *props = NULL;
72                 int n_props = 0;
73
74                 close(pipe_fd[1]);
75
76                 while(1)
77                 {
78                         int newline;
79                         int i;
80                         Property prop;
81
82                         if(!eof)
83                         {
84                                 int len;
85
86                                 len = read(pipe_fd[0], buf+pos, sizeof(buf)-pos);
87                                 if(len==0)
88                                         eof = 1;
89                                 else if(len==-1)
90                                         break;
91                                 pos += len;
92                         }
93
94                         newline = -1;
95                         for(i=0; (newline<0 && i<pos); ++i)
96                                 if(buf[i]=='\n')
97                                         newline = i;
98
99                         if(newline<0)
100                                 break;
101
102                         if(parse_property(buf, newline, &prop)==0)
103                         {
104                                 /*printf("Got property '%s' = '%s'\n", prop.name, prop.value);*/
105
106                                 props = (Property *)realloc(props, (n_props+2)*sizeof(Property));
107                                 props[n_props] = prop;
108                                 ++n_props;
109
110                                 memmove(buf, buf+newline+1, pos-newline-1);
111                                 pos -= newline+1;
112                         }
113                         else
114                                 break;
115                 }
116
117                 props[n_props].name = NULL;
118                 props[n_props].value = NULL;
119
120                 waitpid(pid, NULL, 0);
121                 close(pipe_fd[0]);
122
123                 return props;
124         }
125         else
126         {
127                 close(pipe_fd[0]);
128                 close(pipe_fd[1]);
129
130                 return NULL;
131         }
132 }
133
134 char *get_property_value(Property *props, char *name)
135 {
136         int i;
137         for(i=0; props[i].name; ++i)
138                 if(strcmp(props[i].name, name)==0)
139                         return props[i].value;
140         return NULL;
141 }
142
143 int match_property_value(Property *props, char *name, char *value)
144 {
145         char *v = get_property_value(props, name);
146         if(!v)
147                 return value==NULL;
148         return strcmp(v, value)==0;
149 }
150
151 void free_properties(Property *props)
152 {
153         int i;
154         if(!props)
155                 return;
156         for(i=0; props[i].name; ++i)
157         {
158                 free(props[i].name);
159                 free(props[i].value);
160         }
161         free(props);
162 }
163
164 char **get_mounted_devices(void)
165 {
166         FILE *mtab;
167         struct mntent *me;
168         char **mounted = NULL;
169         int n_mounted = 0;
170
171         mtab = setmntent("/etc/mtab", "r");
172         if(!mtab)
173                 return NULL;
174
175         while((me = getmntent(mtab)))
176         {
177                 mounted = (char **)realloc(mounted, (n_mounted+2)*sizeof(char *));
178                 mounted[n_mounted] = strdup(me->mnt_fsname);
179                 ++n_mounted;
180         }
181
182         endmntent(mtab);
183         mounted[n_mounted] = NULL;
184
185         return mounted;
186 }
187
188 int is_mounted(char **mounted, char *devname)
189 {
190         int i;
191         for(i=0; mounted[i]; ++i)
192                 if(!strcmp(devname, mounted[i]))
193                         return 1;
194         return 0;
195 }
196
197 void free_mounted_devices(char **mounted)
198 {
199         int i;
200         if(!mounted)
201                 return;
202         for(i=0; mounted[i]; ++i)
203                 free(mounted[i]);
204         free(mounted);
205 }
206
207 Device *get_devices(void)
208 {
209         DIR *dir;
210         struct dirent *de;
211         char fnbuf[256];
212         Device *devices = NULL;
213         int n_devices = 0;
214         char **mounted = NULL;
215         
216         dir = opendir("/dev/disk/by-id");
217         if(!dir)
218                 return NULL;
219
220         mounted = get_mounted_devices();
221
222         while((de = readdir(dir)))
223         {
224                 if(de->d_name[0]=='.' && (de->d_name[1]==0 || (de->d_name[1]=='.' && de->d_name[2]==0)))
225                         continue;
226
227                 snprintf(fnbuf, sizeof(fnbuf), "/dev/disk/by-id/%s", de->d_name);
228                 Property *props = get_device_properties(fnbuf);
229                 if(match_property_value(props, "ID_BUS", "usb") && match_property_value(props, "DEVTYPE", "partition"))
230                 {
231                         char *devname;
232                         char *label;
233                         char *vendor;
234                         char *model;
235                         char buf[256];
236                         char pos;
237                         struct stat st;
238
239                         /*printf("Using device %s\n", fnbuf);*/
240
241                         devname = get_property_value(props, "DEVNAME");
242
243                         label = get_property_value(props, "ID_FS_LABEL");
244                         if(!label)
245                                 label = get_property_value(props, "ID_FS_UUID");
246                         if(!label)
247                         {
248                                 char *ptr;
249
250                                 label = devname;
251                                 for(ptr=label; *ptr; ++ptr)
252                                         if(*ptr=='/')
253                                                 label = ptr+1;
254                         }
255
256                         vendor = get_property_value(props, "ID_VENDOR");
257                         model = get_property_value(props, "ID_MODEL");
258
259                         pos = snprintf(buf, sizeof(buf), "%s", label);
260                         if(vendor && model)
261                                 pos += snprintf(buf+pos, sizeof(buf)-pos, " (%s %s)", vendor, model);
262
263                         stat(fnbuf, &st);
264
265                         devices = (Device *)realloc(devices, (n_devices+2)*sizeof(Device));
266                         devices[n_devices].node = strdup(fnbuf);
267                         devices[n_devices].label = strdup(label);
268                         devices[n_devices].description = strdup(buf);
269                         devices[n_devices].mounted = is_mounted(mounted, devname);
270                         devices[n_devices].time = st.st_mtime;
271                         ++n_devices;
272                 }
273                 free_properties(props);
274         }
275
276         closedir(dir);
277         free_mounted_devices(mounted);
278
279         if(devices)
280         {
281                 devices[n_devices].node = NULL;
282                 devices[n_devices].label = NULL;
283                 devices[n_devices].description = NULL;
284         }
285
286         return devices;
287 }
288
289 void free_devices(Device *devices)
290 {
291         int i;
292         if(!devices)
293                 return;
294         for(i=0; devices[i].node; ++i)
295         {
296                 free(devices[i].node);
297                 free(devices[i].label);
298         }
299         free(devices);
300 }
301
302 void row_activated(GtkTreeView *list, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
303 {
304         GtkTreeModel *model;
305         GtkTreeIter iter;
306
307         model = gtk_tree_view_get_model(list);
308
309         if(gtk_tree_model_get_iter(model, &iter, path))
310         {
311                 Device *device;
312                 int pid;
313                 int pipe_fd[2];
314
315                 gtk_tree_model_get(model, &iter, 1, &device, -1);
316
317                 pipe(pipe_fd);
318
319                 pid = fork();
320                 if(pid==0)
321                 {
322                         close(pipe_fd[0]);
323                         dup2(pipe_fd[1], 1);
324                         dup2(pipe_fd[1], 2);
325                         execl("/usr/bin/pmount", "pmount", device->node, device->label, NULL);
326                         _exit(1);
327                 }
328                 else if(pid>0)
329                 {
330                         char buf[1024];
331                         int pos = 0;
332                         int status;
333
334                         close(pipe_fd[1]);
335
336                         while(1)
337                         {
338                                 int len;
339
340                                 len = read(pipe_fd[0], buf+pos, sizeof(buf)-pos-1);
341                                 if(len<=0)
342                                         break;
343                                 pos += len;
344                         }
345
346                         buf[pos] = 0;
347
348                         waitpid(pid, &status, 0);
349                         if(!WIFEXITED(status) || WEXITSTATUS(status))
350                         {
351                                 GtkWidget *dialog;
352
353                                 dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", buf);
354                                 g_signal_connect(dialog, "response", &gtk_main_quit, NULL);
355                                 gtk_widget_show_all(dialog);
356                         }
357                         else
358                                 gtk_main_quit();
359                 }
360                 else
361                 {
362                 }
363         }
364
365         (void)column;
366         (void)user_data;
367 }
368
369 int main(int argc, char **argv)
370 {
371         GtkWidget *window;
372         GtkWidget *viewport;
373         GtkWidget *list;
374         GtkListStore *store;
375         GtkTreeSelection *selection;
376         GtkTreeIter iter;
377         Device *devices;
378         int i;
379         time_t latest;
380
381         gtk_init(&argc, &argv);
382
383         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
384         gtk_container_set_border_width(GTK_CONTAINER(window), 5);
385         g_signal_connect(window, "destroy", &gtk_main_quit, NULL);
386
387         viewport = gtk_viewport_new(NULL, NULL);
388         gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_IN);
389         gtk_container_add(GTK_CONTAINER(window), viewport);
390
391         list = gtk_tree_view_new();
392         gtk_container_add(GTK_CONTAINER(viewport), list);
393         g_signal_connect(list, "row-activated", (GCallback)&row_activated, NULL);
394
395         store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
396         gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
397         gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list),
398                 -1, "Device", gtk_cell_renderer_text_new(), "text", 0, NULL);
399
400         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
401
402         devices = get_devices();
403         if(devices)
404         {
405                 latest = 0;
406                 for(i=0; devices[i].node; ++i)
407                         if(!devices[i].mounted)
408                         {
409                                 gtk_list_store_append(store, &iter);
410                                 gtk_list_store_set(store, &iter, 0, devices[i].description, 1, &devices[i], -1);
411                                 if(devices[i].time>latest)
412                                 {
413                                         latest = devices[i].time;
414                                         gtk_tree_selection_select_iter(selection, &iter);
415                                 }
416                         }
417
418                 gtk_widget_show_all(window);
419         }
420         else
421         {
422                 GtkWidget *dialog;
423
424                 dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "No devices found");
425                 g_signal_connect(dialog, "response", &gtk_main_quit, NULL);
426                 gtk_widget_show_all(dialog);
427         }
428
429         gtk_main();
430
431         free_devices(devices);
432
433         return 0;
434 }