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