1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include <Eet.h>
6 #include <Eeze.h>
7 #include <Ecore_Con.h>
8 #include <Eeze_Disk.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14
15 #include "eeze_scanner.h"
16
17 #define DBG(...) EINA_LOG_DOM_DBG(es_log_dom, __VA_ARGS__)
18 #define INF(...) EINA_LOG_DOM_INFO(es_log_dom, __VA_ARGS__)
19 #define WRN(...) EINA_LOG_DOM_WARN(es_log_dom, __VA_ARGS__)
20 #define ERR(...) EINA_LOG_DOM_ERR(es_log_dom, __VA_ARGS__)
21 #define CRI(...) EINA_LOG_DOM_CRIT(es_log_dom, __VA_ARGS__)
22
23 #ifndef O_BINARY
24 # define O_BINARY 0
25 #endif
26
27 static int es_log_dom = -1;
28 static int retval = EXIT_SUCCESS;
29 static Eet_Data_Descriptor *es_edd = NULL;
30 static Eina_List *clients = NULL;
31
32 static Eina_List *storage_devices = NULL;
33 static Eina_List *storage_cdrom = NULL;
34
35 static Eina_List *volume_cdrom = NULL;
36 static Eina_List *volume_devices = NULL;
37
38 static void
event_send(const char * device,Eeze_Scanner_Event_Type type,Eina_Bool volume)39 event_send(const char *device, Eeze_Scanner_Event_Type type, Eina_Bool volume)
40 {
41 Eeze_Scanner_Event ev;
42 Eet_Connection *ec;
43 const Eina_List *n;
44
45
46 ev.device = device;
47 ev.type = type;
48 ev.volume = volume;
49 EINA_LIST_FOREACH(clients, n, ec)
50 {
51 const char *ts;
52 const char *vol = volume ? " (volume)" : "";
53 switch (type)
54 {
55 case EEZE_SCANNER_EVENT_TYPE_ADD: ts = "ADD"; break;
56 case EEZE_SCANNER_EVENT_TYPE_REMOVE: ts = "REMOVE"; break;
57 case EEZE_SCANNER_EVENT_TYPE_CHANGE: ts = "CHANGE"; break;
58 default: ts = "?";
59 }
60 INF("Serializing event %s '%s'%s for client %p...", ts, device, vol, ec);
61 eet_connection_send(ec, es_edd, &ev, NULL);
62 }
63 }
64
65 static Eina_Bool
disk_mount(void * data EINA_UNUSED,int type EINA_UNUSED,Eeze_Disk * disk)66 disk_mount(void *data EINA_UNUSED, int type EINA_UNUSED, Eeze_Disk *disk)
67 {
68 Eina_List *l;
69 Eeze_Scanner_Device *d;
70 if (eeze_disk_type_get(disk) != EEZE_DISK_TYPE_CDROM) return ECORE_CALLBACK_RENEW;
71
72 EINA_LIST_FOREACH(storage_cdrom, l, d)
73 {
74 if (d->device == eeze_disk_syspath_get(disk))
75 {
76 d->mounted = !d->mounted;
77 break;
78 }
79 }
80 return ECORE_CALLBACK_RENEW;
81 }
82
83 static void
cl_setup(Eo * cl,Eet_Connection * ec)84 cl_setup(Eo *cl, Eet_Connection *ec)
85 {
86 Eina_List *l;
87 Eeze_Scanner_Device *dev;
88 Eeze_Scanner_Event ev;
89 const char *sys;
90
91 INF("Sending initial events to new client %p (ec=%p)", cl, ec);
92 EINA_LIST_FOREACH(storage_devices, l, sys)
93 {
94 ev.device = sys;
95 ev.type = EEZE_SCANNER_EVENT_TYPE_ADD;
96 ev.volume = EINA_FALSE;
97 eet_connection_send(ec, es_edd, &ev, NULL);
98 }
99 EINA_LIST_FOREACH(storage_cdrom, l, dev)
100 {
101 ev.device = dev->device;
102 ev.type = EEZE_SCANNER_EVENT_TYPE_ADD;
103 ev.volume = EINA_FALSE;
104 eet_connection_send(ec, es_edd, &ev, NULL);
105 }
106 EINA_LIST_FOREACH(volume_devices, l, sys)
107 {
108 ev.device = sys;
109 ev.type = EEZE_SCANNER_EVENT_TYPE_ADD;
110 ev.volume = EINA_TRUE;
111 eet_connection_send(ec, es_edd, &ev, NULL);
112 }
113 EINA_LIST_FOREACH(volume_cdrom, l, dev)
114 {
115 ev.device = dev->device;
116 ev.type = EEZE_SCANNER_EVENT_TYPE_ADD;
117 ev.volume = EINA_TRUE;
118 eet_connection_send(ec, es_edd, &ev, NULL);
119 }
120 }
121
122 static Eina_Bool
ec_write(const void * data,size_t size,void * user_data)123 ec_write(const void *data, size_t size, void *user_data)
124 {
125 Eo *client = user_data;
126 Eina_Slice slice = { .mem = data, .len = size };
127 Eina_Error err;
128 DBG("Write " EINA_SLICE_FMT " to %p", EINA_SLICE_PRINT(slice), client);
129 err = efl_io_writer_write(client, &slice, NULL);
130 if (err)
131 {
132 DBG("Failed write " EINA_SLICE_FMT " to %p: %s", EINA_SLICE_PRINT(slice), client, eina_error_msg_get(err));
133 if (!efl_io_closer_closed_get(client))
134 efl_io_closer_close(client);
135 return EINA_FALSE;
136 }
137 return EINA_TRUE;
138 }
139
140 static Eina_Bool
ec_read(const void * eet_data EINA_UNUSED,size_t size EINA_UNUSED,void * user_data EINA_UNUSED)141 ec_read(const void *eet_data EINA_UNUSED, size_t size EINA_UNUSED, void *user_data EINA_UNUSED)
142 {
143 return EINA_TRUE;
144 }
145
146 static void
cl_finished(void * data,const Efl_Event * event)147 cl_finished(void *data, const Efl_Event *event)
148 {
149 Eo *client = event->object;
150 Eet_Connection *ec = data;
151
152 DBG("Finished %p (ec=%p)", client, ec);
153
154 if (!efl_io_closer_closed_get(client))
155 efl_io_closer_close(client);
156 }
157
158 static void
cl_error(void * data,const Efl_Event * event)159 cl_error(void *data, const Efl_Event *event)
160 {
161 Eo *client = event->object;
162 Eina_Error *perr = event->info;
163 Eet_Connection *ec = data;
164
165 WRN("client %p (ec=%p) error: %s", client, ec, eina_error_msg_get(*perr));
166
167 if (!efl_io_closer_closed_get(client))
168 efl_io_closer_close(client);
169 }
170
171 static Efl_Callback_Array_Item *cl_cbs(void);
172
173 static void
cl_closed(void * data,const Efl_Event * event)174 cl_closed(void *data, const Efl_Event *event)
175 {
176 Eo *client = event->object;
177 Eet_Connection *ec = data;
178
179 INF("Removed client %p (ec=%p)", client, ec);
180 clients = eina_list_remove(clients, ec);
181 efl_event_callback_array_del(client, cl_cbs(), ec);
182 efl_unref(client);
183 eet_connection_close(ec, NULL);
184 }
185
186 EFL_CALLBACKS_ARRAY_DEFINE(cl_cbs,
187 { EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED, cl_finished },
188 { EFL_IO_BUFFERED_STREAM_EVENT_ERROR, cl_error },
189 { EFL_IO_CLOSER_EVENT_CLOSED, cl_closed });
190
191 static void
cl_add(void * data EINA_UNUSED,const Efl_Event * event)192 cl_add(void *data EINA_UNUSED, const Efl_Event *event)
193 {
194 Eo *client = event->info;
195 Eet_Connection *ec;
196
197 INF("Added client %p", client);
198 ec = eet_connection_new(ec_read, ec_write, client);
199 if (!ec)
200 {
201 ERR("Could not create eet serializer! Lost client %p!", client);
202 return;
203 }
204
205 efl_ref(client);
206 efl_event_callback_array_add(client, cl_cbs(), ec);
207 clients = eina_list_append(clients, ec);
208 cl_setup(client, ec);
209 }
210
211 static Eina_Bool
eet_setup(void)212 eet_setup(void)
213 {
214 Eet_Data_Descriptor_Class eddc;
215
216 if (!eet_eina_stream_data_descriptor_class_set(&eddc, sizeof(eddc), "eeze_scanner_event", sizeof(Eeze_Scanner_Event)))
217 {
218 CRI("Could not create eet data descriptor!");
219 return EINA_FALSE;
220 }
221
222 es_edd = eet_data_descriptor_stream_new(&eddc);
223 #define DAT(MEMBER, TYPE) EET_DATA_DESCRIPTOR_ADD_BASIC(es_edd, Eeze_Scanner_Event, #MEMBER, MEMBER, EET_T_##TYPE)
224 DAT(device, INLINED_STRING);
225 DAT(type, UINT);
226 DAT(volume, UCHAR);
227 #undef DAT
228 return EINA_TRUE;
229 }
230
231 static Eina_Bool
cdrom_timer(Eeze_Scanner_Device * dev)232 cdrom_timer(Eeze_Scanner_Device *dev)
233 {
234 const char *devpath;
235 int fd;
236
237 /* cdrom already mounted, no need to poll */
238 if (dev->mounted) return EINA_TRUE;
239 devpath = eeze_udev_syspath_get_devpath(dev->device);
240 fd = open(devpath, O_RDONLY | O_BINARY);
241 if (fd < 0)
242 {
243 Eina_List *l;
244
245 l = eina_list_data_find_list(volume_cdrom, dev);
246 if (l)
247 {
248 /* disc removed, delete volume */
249 INF("Removed cdrom '%s'", dev->device);
250 volume_cdrom = eina_list_remove_list(volume_cdrom, l);
251 event_send(dev->device, EEZE_SCANNER_EVENT_TYPE_CHANGE, EINA_TRUE);
252 }
253 /* just in case */
254 dev->mounted = EINA_FALSE;
255 }
256 else
257 {
258 if (!eina_list_data_find(volume_cdrom, dev))
259 {
260 INF("Added cdrom '%s'", dev->device);
261 volume_cdrom = eina_list_append(volume_cdrom, dev);
262 event_send(dev->device, EEZE_SCANNER_EVENT_TYPE_CHANGE, EINA_TRUE);
263 }
264 close(fd);
265 }
266 eina_stringshare_del(devpath);
267 return EINA_TRUE;
268 }
269
270 static Eina_Bool
storage_setup(void)271 storage_setup(void)
272 {
273 Eina_List *l, *ll;
274 const char *sys;
275
276 storage_devices = eeze_udev_find_by_type(EEZE_UDEV_TYPE_DRIVE_INTERNAL, NULL);
277 if (!storage_devices)
278 {
279 ERR("No storage devices found! This is not supposed to happen!");
280 return EINA_FALSE;
281 }
282
283 ll = eeze_udev_find_by_type(EEZE_UDEV_TYPE_DRIVE_REMOVABLE, NULL);
284 EINA_LIST_FREE(ll, sys)
285 {
286 storage_devices = eina_list_append(storage_devices, sys);
287 }
288
289 l = eeze_udev_find_by_type(EEZE_UDEV_TYPE_DRIVE_CDROM, NULL);
290 EINA_LIST_FREE(l, sys)
291 {
292 Eeze_Scanner_Device *dev;
293 Eeze_Disk *disk;
294
295 dev = calloc(1, sizeof(Eeze_Scanner_Device));
296 if (!dev)
297 {
298 ERR("Lost cdrom device '%s'!", sys);
299 eina_stringshare_del(sys);
300 continue;
301 }
302 disk = eeze_disk_new(sys);
303 if (!disk)
304 {
305 ERR("Lost cdrom device '%s'!", sys);
306 eina_stringshare_del(sys);
307 free(dev);
308 continue;
309 }
310 dev->device = sys;
311 dev->mounted = eeze_disk_mounted_get(disk);
312 eeze_disk_free(disk);
313 dev->poller = ecore_poller_add(ECORE_POLLER_CORE, 32, (Ecore_Task_Cb)cdrom_timer, dev);
314 storage_cdrom = eina_list_append(storage_cdrom, dev);
315 }
316 volume_devices = eeze_udev_find_by_type(EEZE_UDEV_TYPE_DRIVE_MOUNTABLE, NULL);
317 EINA_LIST_FOREACH_SAFE(volume_devices, l, ll, sys)
318 {
319 Eina_List *c;
320 Eeze_Scanner_Device *dev;
321
322 EINA_LIST_FOREACH(storage_cdrom, c, dev)
323 if (sys == dev->device)
324 {
325 eina_stringshare_del(sys);
326 volume_devices = eina_list_remove_list(volume_devices, l);
327 volume_cdrom = eina_list_append(volume_cdrom, dev);
328 l = NULL;
329 break;
330 }
331 }
332
333 return EINA_TRUE;
334 }
335
336 static void
cb_vol_chg(const char * device,Eeze_Udev_Event ev,void * data EINA_UNUSED,Eeze_Udev_Watch * watch EINA_UNUSED)337 cb_vol_chg(const char *device, Eeze_Udev_Event ev, void *data EINA_UNUSED, Eeze_Udev_Watch *watch EINA_UNUSED)
338 {
339 Eina_List *l;
340 Eeze_Scanner_Device *dev;
341
342 DBG("device='%s'", device);
343
344 if (ev == EEZE_UDEV_EVENT_ONLINE) ev = EEZE_UDEV_EVENT_ADD;
345 else if (ev == EEZE_UDEV_EVENT_OFFLINE) ev = EEZE_UDEV_EVENT_REMOVE;
346
347 event_send(device, (Eeze_Scanner_Event_Type)ev, EINA_TRUE);
348 switch (ev)
349 {
350 case EEZE_UDEV_EVENT_ADD:
351 case EEZE_UDEV_EVENT_ONLINE:
352 INF("Added volume '%s'", device);
353 EINA_LIST_FOREACH(storage_cdrom, l, dev)
354 if (device == dev->device)
355 {
356 volume_cdrom = eina_list_append(volume_cdrom, dev);
357 return;
358 }
359 volume_devices = eina_list_append(volume_devices, eina_stringshare_add(device));
360 break;
361 case EEZE_UDEV_EVENT_REMOVE:
362 case EEZE_UDEV_EVENT_OFFLINE:
363 INF("Removed volume '%s'", device);
364 EINA_LIST_FOREACH(volume_cdrom, l, dev)
365 if (device == dev->device)
366 {
367 volume_cdrom = eina_list_remove_list(volume_cdrom, l);
368 return;
369 }
370 volume_devices = eina_list_remove(volume_devices, device);
371 eina_stringshare_del(device);
372 break;
373 default:
374 INF("Changed volume '%s'", device);
375 break;
376 }
377 }
378
379 static void
cb_stor_chg(const char * device,Eeze_Udev_Event ev,void * data EINA_UNUSED,Eeze_Udev_Watch * watch EINA_UNUSED)380 cb_stor_chg(const char *device, Eeze_Udev_Event ev, void *data EINA_UNUSED, Eeze_Udev_Watch *watch EINA_UNUSED)
381 {
382 Eina_List *l;
383 Eeze_Scanner_Device *dev = NULL;
384 const char *str;
385
386
387 DBG("device='%s'", device);
388 switch (ev)
389 {
390 case EEZE_UDEV_EVENT_ADD:
391 case EEZE_UDEV_EVENT_ONLINE:
392 INF("Added device '%s'", device);
393 event_send(device, (Eeze_Scanner_Event_Type)ev, EINA_FALSE);
394 str = eeze_udev_syspath_get_property(device, "ID_CDROM");
395 if (!str)
396 {
397 storage_devices = eina_list_append(storage_devices, eina_stringshare_add(device));
398 return;
399 }
400 eina_stringshare_del(str);
401 dev = calloc(1, sizeof(Eeze_Scanner_Device));
402 if (!dev) return;
403 dev->device = eina_stringshare_add(device);
404 dev->poller = ecore_poller_add(ECORE_POLLER_CORE, 32,
405 (Ecore_Task_Cb)cdrom_timer, dev);
406 storage_cdrom = eina_list_append(storage_cdrom, dev);
407 break;
408 case EEZE_UDEV_EVENT_REMOVE:
409 case EEZE_UDEV_EVENT_OFFLINE:
410 if (!eina_list_data_find(storage_devices, device))
411 {
412 EINA_LIST_FOREACH(storage_cdrom, l, dev)
413 if (dev->device == device) break;
414 if ((!dev) || (dev->device != device)) return;
415 }
416 INF("Removed device '%s'", device);
417 event_send(device, (Eeze_Scanner_Event_Type)ev, EINA_FALSE);
418 EINA_LIST_FOREACH(storage_cdrom, l, dev)
419 if (device == dev->device)
420 {
421 if (dev->poller) ecore_poller_del(dev->poller);
422 storage_cdrom = eina_list_remove_list(storage_cdrom, l);
423 eina_stringshare_del(dev->device);
424 free(dev);
425 return;
426 }
427 storage_devices = eina_list_remove(storage_devices, device);
428 eina_stringshare_del(device);
429 break;
430 default:
431 INF("Changed device '%s'", device);
432 break;
433 }
434 }
435
436 static void
server_error(void * data EINA_UNUSED,const Efl_Event * event)437 server_error(void *data EINA_UNUSED, const Efl_Event *event)
438 {
439 Eina_Error *perr = event->info;
440
441 ERR("server error: %s", eina_error_msg_get(*perr));
442 retval = EXIT_FAILURE;
443 ecore_main_loop_quit();
444 }
445
446 int
main(void)447 main(void)
448 {
449 Eo *server = NULL, *loop;
450 char *path = NULL;
451 Ecore_Event_Handler *eh;
452 Eina_List *ehl = NULL;
453 Eeze_Udev_Watch *uw;
454 Eina_List *uwl = NULL;
455 Eina_Error err;
456
457 ecore_app_no_system_modules();
458
459 eina_init();
460 ecore_init();
461 eet_init();
462 ecore_con_init();
463 eeze_init();
464 eeze_disk_function();
465 eeze_mount_tabs_watch();
466
467 es_log_dom = eina_log_domain_register("eeze_scanner", EINA_COLOR_CYAN);
468
469 if (!eet_setup())
470 goto error_setup;
471
472 if (!storage_setup())
473 goto error_setup;
474
475 #define ADD_HANDLER(type, cb) \
476 ehl = eina_list_append(ehl, ecore_event_handler_add(type, (Ecore_Event_Handler_Cb)cb, NULL))
477 ADD_HANDLER(EEZE_EVENT_DISK_UNMOUNT, disk_mount);
478 ADD_HANDLER(EEZE_EVENT_DISK_MOUNT, disk_mount);
479 #undef ADD_HANDLER
480
481 #define ADD_WATCH(type, cb) \
482 uwl = eina_list_append(uwl, eeze_udev_watch_add(type, EEZE_UDEV_EVENT_NONE, cb, NULL))
483 ADD_WATCH(EEZE_UDEV_TYPE_DRIVE_INTERNAL, cb_stor_chg);
484 ADD_WATCH(EEZE_UDEV_TYPE_DRIVE_REMOVABLE, cb_stor_chg);
485 ADD_WATCH(EEZE_UDEV_TYPE_DRIVE_CDROM, cb_stor_chg);
486 ADD_WATCH(EEZE_UDEV_TYPE_DRIVE_MOUNTABLE, cb_vol_chg);
487 #undef ADD_WATCH
488
489 path = ecore_con_local_path_new(EINA_TRUE, "eeze_scanner", 0);
490 if (!path)
491 {
492 fprintf(stderr, "ERROR: could not get local communication path\n");
493 retval = EXIT_FAILURE;
494 goto end;
495 }
496
497 loop = efl_main_loop_get();
498
499 #ifdef EFL_NET_SERVER_UNIX_CLASS
500 server = efl_add(EFL_NET_SERVER_SIMPLE_CLASS, loop,
501 efl_net_server_simple_inner_class_set(efl_added, EFL_NET_SERVER_UNIX_CLASS));
502 #else
503 /* TODO: maybe start a TCP using locahost:12345?
504 * Right now eina_debug_monitor is only for AF_UNIX, so not an issue.
505 */
506 fprintf(stderr, "ERROR: your platform doesn't support Efl.Net.Server.Unix\n");
507 #endif
508 if (!server)
509 {
510 fprintf(stderr, "ERROR: could not create communication server\n");
511 retval = EXIT_FAILURE;
512 goto end;
513 }
514
515 efl_event_callback_add(server, EFL_NET_SERVER_EVENT_CLIENT_ADD, cl_add, NULL);
516 efl_event_callback_add(server, EFL_NET_SERVER_EVENT_SERVER_ERROR, server_error, NULL);
517
518 #ifdef EFL_NET_SERVER_UNIX_CLASS
519 {
520 Eo *inner_server = efl_net_server_simple_inner_server_get(server);
521 efl_net_server_unix_unlink_before_bind_set(inner_server, EINA_TRUE);
522 efl_net_server_unix_leading_directories_create_set(inner_server, EINA_TRUE, 0700);
523 }
524 #endif
525
526 err = efl_net_server_serve(server, path);
527 if (err)
528 {
529 fprintf(stderr, "ERROR: could not serve '%s': %s\n", path, eina_error_msg_get(err));
530 retval = EXIT_FAILURE;
531 goto end;
532 }
533
534 ecore_main_loop_begin();
535
536 end:
537 efl_del(server);
538 server = NULL;
539
540 free(path);
541 path = NULL;
542
543 EINA_LIST_FREE(ehl, eh) ecore_event_handler_del(eh);
544 EINA_LIST_FREE(uwl, uw) eeze_udev_watch_del(uw);
545
546 error_setup:
547 eeze_shutdown();
548 ecore_con_shutdown();
549 eet_shutdown();
550 ecore_shutdown();
551 eina_shutdown();
552
553 return retval;
554 }
555