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