1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <Eina.h>
6 #include <Ecore.h>
7 #include <Ecore_File.h>
8 #include <Eio.h>
9 #include <Eet.h>
10 #include "efreetd.h"
11 #include "efreetd_ipc.h"
12
13 #include "Efreet.h"
14 #define EFREET_MODULE_LOG_DOM efreetd_log_dom
15 #include "efreet_private.h"
16 #include "efreetd_cache.h"
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21
22 extern FILE *efreetd_log_file;
23
24 static Eina_Hash *icon_change_monitors = NULL;
25 static Eina_Hash *icon_change_monitors_mon = NULL;
26 static Eina_Hash *desktop_change_monitors = NULL;
27 static Eina_Hash *desktop_change_monitors_mon = NULL;
28
29 static Ecore_Event_Handler *cache_exe_del_handler = NULL;
30 static Ecore_Event_Handler *cache_exe_data_handler = NULL;
31 static Ecore_Exe *icon_cache_exe = NULL;
32 static Ecore_Exe *desktop_cache_exe = NULL;
33 static Ecore_Timer *icon_cache_timer = NULL;
34 static Ecore_Timer *desktop_cache_timer = NULL;
35 static Eina_Prefix *pfx = NULL;
36
37 static Eina_Bool desktop_exists = EINA_FALSE;
38
39 static Eina_List *desktop_system_dirs = NULL;
40 static Eina_List *desktop_extra_dirs = NULL;
41 static Eina_List *icon_extra_dirs = NULL;
42 static Eina_List *icon_exts = NULL;
43 static Eina_Bool icon_flush = EINA_FALSE;
44
45 static Eina_Bool desktop_queue = EINA_FALSE;
46 static Eina_Bool icon_queue = EINA_FALSE;
47
48 static Eina_List *_handlers = NULL;
49
50 static void icon_changes_listen(void);
51 static void desktop_changes_listen(void);
52
53 /* internal */
54 typedef struct _Subdir_Cache Subdir_Cache;
55 typedef struct _Subdir_Cache_Dir Subdir_Cache_Dir;
56
57 struct _Subdir_Cache
58 {
59 Eina_Hash *dirs;
60 };
61
62 struct _Subdir_Cache_Dir
63 {
64 unsigned long long dev;
65 unsigned long long ino;
66 unsigned long long mode;
67 unsigned long long uid;
68 unsigned long long gid;
69 unsigned long long size;
70 unsigned long long mtim;
71 unsigned long long ctim;
72 const char **dirs;
73 unsigned int dirs_count;
74 };
75
76 static Eet_Data_Descriptor *subdir_edd = NULL;
77 static Eet_Data_Descriptor *subdir_dir_edd = NULL;
78 static Subdir_Cache *subdir_cache = NULL;
79 static Eina_Bool subdir_need_save = EINA_FALSE;
80
81 static Eina_Hash *mime_monitors = NULL;
82 static Eina_Hash *mime_monitors_mon = NULL;
83 static Ecore_Timer *mime_update_timer = NULL;
84 static Ecore_Exe *mime_cache_exe = NULL;
85
86 static void mime_cache_init(void);
87 static void mime_cache_shutdown(void);
88 static Eina_Bool mime_update_cache_cb(void *data EINA_UNUSED);
89
90 static void
subdir_cache_dir_free(Subdir_Cache_Dir * cd)91 subdir_cache_dir_free(Subdir_Cache_Dir *cd)
92 {
93 unsigned int i;
94 if (!cd) return;
95 if (cd->dirs)
96 {
97 for (i = 0; i < cd->dirs_count; i++)
98 eina_stringshare_del(cd->dirs[i]);
99 free(cd->dirs);
100 }
101 free(cd);
102 }
103
104 static void *
subdir_cache_hash_add(void * hash,const char * key,void * data)105 subdir_cache_hash_add(void *hash, const char *key, void *data)
106 {
107 if (!hash) hash = eina_hash_string_superfast_new(EINA_FREE_CB(subdir_cache_dir_free));
108 if (!hash) return NULL;
109 eina_hash_add(hash, key, data);
110 return hash;
111 }
112
113 static void
subdir_cache_init(void)114 subdir_cache_init(void)
115 {
116 Eet_Data_Descriptor_Class eddc;
117 Eet_File *ef;
118 Eina_Strbuf *buf = eina_strbuf_new();
119 if (!buf) return;
120
121 // set up data codecs for subdirs in memory
122 eet_eina_stream_data_descriptor_class_set(&eddc, sizeof(Subdir_Cache_Dir), "D", sizeof(Subdir_Cache_Dir));
123 EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Subdir_Cache_Dir);
124 subdir_dir_edd = eet_data_descriptor_stream_new(&eddc);
125 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "0", dev, EET_T_ULONG_LONG);
126 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "1", ino, EET_T_ULONG_LONG);
127 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "2", mode, EET_T_ULONG_LONG);
128 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "3", uid, EET_T_ULONG_LONG);
129 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "4", gid, EET_T_ULONG_LONG);
130 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "5", size, EET_T_ULONG_LONG);
131 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "6", mtim, EET_T_ULONG_LONG);
132 EET_DATA_DESCRIPTOR_ADD_BASIC(subdir_dir_edd, Subdir_Cache_Dir, "7", ctim, EET_T_ULONG_LONG);
133 EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(subdir_dir_edd, Subdir_Cache_Dir, "d", dirs);
134
135 eet_eina_stream_data_descriptor_class_set(&eddc, sizeof(Subdir_Cache), "C", sizeof(Subdir_Cache));
136 eddc.func.hash_add = subdir_cache_hash_add;
137 subdir_edd = eet_data_descriptor_stream_new(&eddc);
138 EET_DATA_DESCRIPTOR_ADD_HASH(subdir_edd, Subdir_Cache, "dirs", dirs, subdir_dir_edd);
139
140 // load subdirs from the cache file
141 eina_strbuf_append_printf(buf, "%s/efreet/subdirs_%s.eet",
142 efreet_cache_home_get(), efreet_hostname_get());
143 ef = eet_open(eina_strbuf_string_get(buf), EET_FILE_MODE_READ);
144 if (ef)
145 {
146 subdir_cache = eet_data_read(ef, subdir_edd, "subdirs");
147 eet_close(ef);
148 }
149 eina_strbuf_free(buf);
150
151 // if we don't have a decoded subdir cache - allocate one
152 if (!subdir_cache) subdir_cache = calloc(1, sizeof(Subdir_Cache));
153 if (!subdir_cache)
154 {
155 ERR("Cannot allocate subdir cache in memory");
156 return;
157 }
158
159 // if we don't have a hash in the subdir cache - allocate it
160 if (!subdir_cache->dirs)
161 subdir_cache->dirs = eina_hash_string_superfast_new(EINA_FREE_CB(subdir_cache_dir_free));
162 }
163
164 static void
subdir_cache_shutdown(void)165 subdir_cache_shutdown(void)
166 {
167 // free up in-memory subdir scan info - don't need it anymore
168 if (subdir_cache)
169 {
170 if (subdir_cache->dirs) eina_hash_free(subdir_cache->dirs);
171 free(subdir_cache);
172 }
173 eet_data_descriptor_free(subdir_dir_edd);
174 eet_data_descriptor_free(subdir_edd);
175 subdir_cache = NULL;
176 subdir_dir_edd = NULL;
177 subdir_edd = NULL;
178 }
179
180 static void
subdir_cache_save(void)181 subdir_cache_save(void)
182 {
183 Eina_Strbuf *buf;
184 Eet_File *ef;
185 Eina_Tmpstr *tmpstr = NULL;
186 int tmpfd;
187
188 // only if subdirs need saving... and we have subdirs.
189 if (!subdir_need_save) return;
190 if (!subdir_cache) return;
191 if (!subdir_cache->dirs) return;
192
193 buf = eina_strbuf_new();
194 if (!buf) return;
195
196 // save to tmp file first
197 eina_strbuf_append_printf(buf, "%s/efreet/subdirs_%s.eet.XXXXXX.cache",
198 efreet_cache_home_get(), efreet_hostname_get());
199
200 tmpfd = eina_file_mkstemp(eina_strbuf_string_get(buf), &tmpstr);
201 if (tmpfd < 0)
202 {
203 eina_strbuf_free(buf);
204 return;
205 }
206
207 eina_strbuf_reset(buf);
208
209 // write out eet file to tmp file
210 ef = eet_open(tmpstr, EET_FILE_MODE_WRITE);
211 eet_data_write(ef, subdir_edd, "subdirs", subdir_cache, EET_COMPRESSION_SUPERFAST);
212 eet_close(ef);
213
214 /*
215 * On Windows, buf2 has one remaining ref, hence it can not be renamed below.
216 * Stupid NTFS... So we close it first. "Magically", on Windows, this
217 * temporary file is not deleted...
218 */
219 #ifdef _WIN32
220 close(tmpfd);
221 #endif
222
223 // atomically rename subdirs file on top from tmp file
224 eina_strbuf_append_printf(buf, "%s/efreet/subdirs_%s.eet",
225 efreet_cache_home_get(), efreet_hostname_get());
226
227 if (rename(tmpstr, eina_strbuf_string_get(buf)) < 0)
228 {
229 unlink(tmpstr);
230 ERR("Can't save subdir cache %s", eina_strbuf_string_get(buf));
231 }
232 // we dont need saving anymore - we just did
233 subdir_need_save = EINA_FALSE;
234 eina_tmpstr_del(tmpstr);
235 eina_strbuf_free(buf);
236 }
237
238 static const Subdir_Cache_Dir *
subdir_cache_get(const struct stat * st,const char * path)239 subdir_cache_get(const struct stat *st, const char *path)
240 {
241 Eina_Iterator *it;
242 Eina_File_Direct_Info *info;
243 Subdir_Cache_Dir *cd;
244 Eina_List *files = NULL;
245 int i = 0;
246 const char *file;
247
248 // if no subdir cache at all - return null
249 if (!subdir_cache) return NULL;
250 if (!subdir_cache->dirs) return NULL;
251
252 // if found but something invalid in stored stat info...
253 cd = eina_hash_find(subdir_cache->dirs, path);
254 if ((cd) &&
255 ((cd->dev != (unsigned long long)st->st_dev) ||
256 (cd->ino != (unsigned long long)st->st_ino) ||
257 (cd->mode != (unsigned long long)st->st_mode) ||
258 (cd->uid != (unsigned long long)st->st_uid) ||
259 (cd->gid != (unsigned long long)st->st_gid) ||
260 (cd->size != (unsigned long long)st->st_size) ||
261 (cd->mtim != (unsigned long long)st->st_mtime) ||
262 (cd->ctim != (unsigned long long)st->st_ctime)))
263 {
264 // delete old node and prepare to scan a new one
265 eina_hash_del(subdir_cache->dirs, path, cd);
266 cd = NULL;
267 }
268 // if cached dir is ok by now - return it
269 if (cd) return cd;
270
271 // we need a new node (fesh or invalid)
272 cd = calloc(1, sizeof(Subdir_Cache_Dir));
273 if (!cd) return NULL;
274
275 // store stat info
276 cd->dev = (unsigned long long)st->st_dev;
277 cd->ino = (unsigned long long)st->st_ino;
278 cd->mode = (unsigned long long)st->st_mode;
279 cd->uid = (unsigned long long)st->st_uid;
280 cd->gid = (unsigned long long)st->st_gid;
281 cd->size = (unsigned long long)st->st_size;
282 cd->mtim = (unsigned long long)st->st_mtime;
283 cd->ctim = (unsigned long long)st->st_ctime;
284
285 // go through content finding directories
286 it = eina_file_stat_ls(path);
287 if (!it) return cd;
288
289 EINA_ITERATOR_FOREACH(it, info)
290 {
291 // if ., .. or other "hidden" dot files - ignore
292 if (info->path[info->name_start] == '.') continue;
293 // if it's a dir or link to a dir - store it.
294 if (((info->type == EINA_FILE_LNK) && (ecore_file_is_dir(info->path))) ||
295 (info->type == EINA_FILE_DIR))
296 {
297 // store just the name, not the full path
298 files = eina_list_append
299 (files, eina_stringshare_add(info->path + info->name_start));
300 }
301 }
302 eina_iterator_free(it);
303
304 // now convert our temporary list into an array of stringshare strings
305 cd->dirs_count = eina_list_count(files);
306 if (cd->dirs_count > 0)
307 {
308 cd->dirs = malloc(cd->dirs_count * sizeof(char *));
309 EINA_LIST_FREE(files, file)
310 {
311 cd->dirs[i] = file;
312 i++;
313 }
314 }
315 // add cache dir to hash with full path as key
316 eina_hash_add(subdir_cache->dirs, path, cd);
317 // mark subdirs as needing a save - something changed
318 subdir_need_save = EINA_TRUE;
319 return cd;
320 }
321
322 static Eina_Bool
icon_cache_update_cache_cb(void * data EINA_UNUSED)323 icon_cache_update_cache_cb(void *data EINA_UNUSED)
324 {
325 Eina_Strbuf *file = eina_strbuf_new();
326 if (!file) return EINA_FALSE;
327
328 icon_cache_timer = NULL;
329
330 if (icon_cache_exe)
331 {
332 icon_queue = EINA_TRUE;
333 eina_strbuf_free(file);
334 return ECORE_CALLBACK_CANCEL;
335 }
336 icon_queue = EINA_FALSE;
337 if ((!icon_flush) && (!icon_exts))
338 {
339 eina_strbuf_free(file);
340 return ECORE_CALLBACK_CANCEL;
341 }
342
343 if (icon_change_monitors) eina_hash_free(icon_change_monitors);
344 if (icon_change_monitors_mon) eina_hash_free(icon_change_monitors_mon);
345 icon_change_monitors = eina_hash_string_superfast_new
346 (EINA_FREE_CB(eio_monitor_del));
347 icon_change_monitors_mon = eina_hash_pointer_new(NULL);
348 icon_changes_listen();
349 subdir_cache_save();
350
351 /* TODO: Queue if already running */
352 eina_strbuf_append_printf(file, "%s/efreet/" MODULE_ARCH "/efreet_icon_cache_create",
353 eina_prefix_lib_get(pfx));
354 if (icon_extra_dirs)
355 {
356 Eina_List *ll;
357 char *p;
358
359 eina_strbuf_append(file, " -d");
360 EINA_LIST_FOREACH(icon_extra_dirs, ll, p)
361 {
362 eina_strbuf_append(file, " ");
363 eina_strbuf_append(file, p);
364 }
365 }
366 if (icon_exts)
367 {
368 Eina_List *ll;
369 char *p;
370
371 eina_strbuf_append(file, " -e");
372 EINA_LIST_FOREACH(icon_exts, ll, p)
373 {
374 eina_strbuf_append(file, " ");
375 eina_strbuf_append(file, p);
376 }
377 }
378 if (icon_flush)
379 eina_strbuf_append(file, " -f");
380 icon_flush = EINA_FALSE;
381 fprintf(efreetd_log_file, "[%09.3f] Run:\n %s\n", ecore_time_get(),
382 eina_strbuf_string_get(file));
383 fflush(efreetd_log_file);
384 icon_cache_exe = ecore_exe_pipe_run
385 (eina_strbuf_string_get(file),
386 ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_READ_LINE_BUFFERED,
387 NULL);
388
389 eina_strbuf_free(file);
390
391 return ECORE_CALLBACK_CANCEL;
392 }
393
394 static Eina_Bool
desktop_cache_update_cache_cb(void * data EINA_UNUSED)395 desktop_cache_update_cache_cb(void *data EINA_UNUSED)
396 {
397 Eina_Strbuf *file;
398
399 desktop_cache_timer = NULL;
400
401 if (desktop_cache_exe)
402 {
403 desktop_queue = EINA_TRUE;
404 return ECORE_CALLBACK_CANCEL;
405 }
406 desktop_queue = EINA_FALSE;
407 file = eina_strbuf_new();
408
409 if (desktop_change_monitors) eina_hash_free(desktop_change_monitors);
410 if (desktop_change_monitors_mon) eina_hash_free(desktop_change_monitors_mon);
411 desktop_change_monitors = eina_hash_string_superfast_new
412 (EINA_FREE_CB(eio_monitor_del));
413 desktop_change_monitors_mon = eina_hash_pointer_new(NULL);
414 desktop_changes_listen();
415 subdir_cache_save();
416
417 eina_strbuf_append_printf(file, "%s/efreet/" MODULE_ARCH "/efreet_desktop_cache_create",
418 eina_prefix_lib_get(pfx));
419 if (desktop_extra_dirs)
420 {
421 Eina_List *ll;
422 const char *str;
423
424 eina_strbuf_append(file, " -d");
425 EINA_LIST_FOREACH(desktop_extra_dirs, ll, str)
426 {
427 eina_strbuf_append(file, " ");
428 eina_strbuf_append(file, str);
429 }
430 }
431 INF("Run desktop cache creation: %s", eina_strbuf_string_get(file));
432 fprintf(efreetd_log_file, "[%09.3f] Run:\n %s\n", ecore_time_get(),
433 eina_strbuf_string_get(file));
434 fflush(efreetd_log_file);
435 desktop_cache_exe = ecore_exe_pipe_run
436 (eina_strbuf_string_get(file),
437 ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_READ_LINE_BUFFERED,
438 NULL);
439
440 eina_strbuf_free(file);
441
442 return ECORE_CALLBACK_CANCEL;
443 }
444
445 static void
cache_icon_update(Eina_Bool flush)446 cache_icon_update(Eina_Bool flush)
447 {
448 if (icon_cache_timer) ecore_timer_del(icon_cache_timer);
449 if (flush) icon_flush = flush;
450 icon_cache_timer = ecore_timer_add(0.2, icon_cache_update_cache_cb, NULL);
451 }
452
453 void
cache_desktop_update(void)454 cache_desktop_update(void)
455 {
456 if (desktop_cache_timer) ecore_timer_del(desktop_cache_timer);
457 desktop_cache_timer = ecore_timer_add(0.2, desktop_cache_update_cache_cb, NULL);
458 }
459
460 static Eina_Bool
_cb_monitor_event(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)461 _cb_monitor_event(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
462 {
463 Eio_Monitor_Event *ev = event;
464
465 // if it's an icon
466 if (eina_hash_find(icon_change_monitors_mon, &(ev->monitor)))
467 {
468 cache_icon_update(EINA_FALSE);
469 }
470 // if it's a desktop
471 else if (eina_hash_find(desktop_change_monitors_mon, &(ev->monitor)))
472 {
473 cache_desktop_update();
474 }
475 // if it's a mime file
476 else if (eina_hash_find(mime_monitors_mon, &(ev->monitor)))
477 {
478 if ((!strcmp("/etc/mime.types", ev->filename)) ||
479 (!strcmp("globs", ecore_file_file_get(ev->filename))))
480 {
481 mime_cache_shutdown();
482 mime_cache_init();
483 if (mime_update_timer) ecore_timer_del(mime_update_timer);
484 mime_update_timer = ecore_timer_add(0.2, mime_update_cache_cb, NULL);
485 }
486 }
487 return ECORE_CALLBACK_PASS_ON;
488 }
489
490 static void
icon_changes_monitor_add(const struct stat * st,const char * path)491 icon_changes_monitor_add(const struct stat *st, const char *path)
492 {
493 Eio_Monitor *mon;
494 char *realp = NULL;
495 const char *monpath = path;
496
497 if (eina_hash_find(icon_change_monitors, path)) return;
498 #ifndef _WIN32
499 if (S_ISLNK(st->st_mode))
500 {
501 realp = ecore_file_realpath(path);
502 if (!realp) return;
503 monpath = realp;
504 }
505 #endif
506 if (ecore_file_is_dir(monpath))
507 {
508 mon = eio_monitor_add(monpath);
509 if (mon)
510 {
511 eina_hash_add(icon_change_monitors, path, mon);
512 eina_hash_add(icon_change_monitors_mon, &mon, mon);
513 }
514 }
515 free(realp);
516 }
517
518 static void
desktop_changes_monitor_add(const struct stat * st,const char * path)519 desktop_changes_monitor_add(const struct stat *st, const char *path)
520 {
521 Eio_Monitor *mon;
522 char *realp = NULL;
523 const char *monpath = path;
524
525 if (eina_hash_find(desktop_change_monitors, path)) return;
526 #ifndef _WIN32
527 if (S_ISLNK(st->st_mode))
528 {
529 realp = ecore_file_realpath(path);
530 if (!realp) return;
531 monpath = realp;
532 }
533 #endif
534 if (ecore_file_is_dir(monpath))
535 {
536 mon = eio_monitor_add(monpath);
537 if (mon)
538 {
539 eina_hash_add(desktop_change_monitors, path, mon);
540 eina_hash_add(desktop_change_monitors_mon, &mon, mon);
541 }
542 }
543 free(realp);
544 }
545
546 static int
stat_cmp(const void * a,const void * b)547 stat_cmp(const void *a, const void *b)
548 {
549 const struct stat *st1 = a;
550 const struct stat *st2 = b;
551
552 if ((st2->st_dev == st1->st_dev) && (st2->st_ino == st1->st_ino))
553 return 0;
554 return 1;
555 }
556
557 static Eina_Bool
_check_recurse_monitor_sanity(Eina_Inarray * stack,const char * path,unsigned int stack_limit)558 _check_recurse_monitor_sanity(Eina_Inarray *stack, const char *path, unsigned int stack_limit)
559 {
560 const char *home = eina_environment_home_get();
561
562 // protect against too deep recursion even if it's valid.
563 if (eina_inarray_count(stack) >= stack_limit)
564 {
565 ERR("Recursing too far. Level %i. Stopping at %s\n", stack_limit, path);
566 return EINA_FALSE;
567 }
568 // detect if we start recursing at $HOME - a sign of something wrong
569 if ((home) && (!strcmp(home, path)))
570 {
571 ERR("Recursively monitor homedir! Ignore.");
572 return EINA_FALSE;
573 }
574 return EINA_TRUE;
575 }
576
577 static void
icon_changes_listen_recursive(Eina_Inarray * stack,const char * path,Eina_Bool base)578 icon_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool base)
579 {
580 struct stat *st = eina_mempool_malloc(efreetd_mp_stat, sizeof(struct stat));
581 if (!st) return;
582
583 if (stat(path, st) == -1) return;
584 if (eina_inarray_search(stack, st, stat_cmp) >= 0) return;
585 if (!_check_recurse_monitor_sanity(stack, path, 10)) return;
586 eina_inarray_push(stack, st);
587
588 if ((!S_ISDIR(st->st_mode)) && (base))
589 {
590 // XXX: if it doesn't exist... walk the parent dirs back down
591 // to this path until we find one that doesn't exist, then
592 // monitor its parent, and treat it specially as it needs
593 // to look for JUST the creation of this specific child
594 // and when this child is created, replace this monitor with
595 // monitoring the next specific child dir down until we are
596 // monitoring the original path again.
597 }
598 if (S_ISDIR(st->st_mode))
599 {
600 unsigned int i;
601 const Subdir_Cache_Dir *cd = subdir_cache_get(st, path);
602 icon_changes_monitor_add(st, path);
603 if (cd)
604 {
605 Eina_Strbuf *buf = eina_strbuf_new();
606 if (!buf) return;
607 for (i = 0; i < cd->dirs_count; i++)
608 {
609
610 eina_strbuf_append_printf(buf, "%s/%s", path, cd->dirs[i]);
611 icon_changes_listen_recursive(stack, eina_strbuf_string_get(buf), EINA_FALSE);
612 eina_strbuf_reset(buf);
613 }
614 eina_strbuf_free(buf);
615 }
616 }
617 eina_inarray_pop(stack);
618 eina_mempool_free(efreetd_mp_stat, st);
619 }
620
621 static void
desktop_changes_listen_recursive(Eina_Inarray * stack,const char * path,Eina_Bool base)622 desktop_changes_listen_recursive(Eina_Inarray *stack, const char *path, Eina_Bool base)
623 {
624 struct stat *st = eina_mempool_malloc(efreetd_mp_stat, sizeof(struct stat));
625 if (!st) return;
626
627 if (stat(path, st) == -1) return;
628 if (eina_inarray_search(stack, st, stat_cmp) >= 0) return;
629 if (!_check_recurse_monitor_sanity(stack, path, 10)) return;
630 eina_inarray_push(stack, st);
631
632 if ((!S_ISDIR(st->st_mode)) && (base))
633 {
634 // XXX: if it doesn't exist... walk the parent dirs back down
635 // to this path until we find one that doesn't exist, then
636 // monitor its parent, and treat it specially as it needs
637 // to look for JUST the creation of this specific child
638 // and when this child is created, replace this monitor with
639 // monitoring the next specific child dir down until we are
640 // monitoring the original path again.
641 }
642 if (S_ISDIR(st->st_mode))
643 {
644 unsigned int i;
645 const Subdir_Cache_Dir *cd = subdir_cache_get(st, path);
646 desktop_changes_monitor_add(st, path);
647 if (cd)
648 {
649 Eina_Strbuf *buf = eina_strbuf_new();
650 if (!buf) return;
651 for (i = 0; i < cd->dirs_count; i++)
652 {
653 eina_strbuf_append_printf(buf, "%s/%s", path, cd->dirs[i]);
654 desktop_changes_listen_recursive(stack, eina_strbuf_string_get(buf), EINA_FALSE);
655 eina_strbuf_reset(buf);
656 }
657 eina_strbuf_free(buf);
658 }
659 }
660 eina_inarray_pop(stack);
661 eina_mempool_free(efreetd_mp_stat, st);
662 }
663
664 static void
icon_changes_listen(void)665 icon_changes_listen(void)
666 {
667 Eina_List *l;
668 Eina_List *xdg_dirs;
669 const char *dir;
670 Eina_Inarray *stack;
671 Eina_Strbuf *buf = eina_strbuf_new();
672 if (!buf) return;
673
674 stack = eina_inarray_new(sizeof(struct stat), 16);
675 if (!stack)
676 {
677 eina_strbuf_free(buf);
678 return;
679 }
680 icon_changes_listen_recursive(stack, efreet_icon_deprecated_user_dir_get(), EINA_TRUE);
681 eina_inarray_flush(stack);
682 icon_changes_listen_recursive(stack, efreet_icon_user_dir_get(), EINA_TRUE);
683 EINA_LIST_FOREACH(icon_extra_dirs, l, dir)
684 {
685 if (!strcmp(dir, "/")) continue;
686 eina_inarray_flush(stack);
687 icon_changes_listen_recursive(stack, dir, EINA_TRUE);
688 }
689
690 xdg_dirs = efreet_data_dirs_get();
691 EINA_LIST_FOREACH(xdg_dirs, l, dir)
692 {
693 eina_strbuf_append_printf(buf, "%s/icons", dir);
694 eina_inarray_flush(stack);
695 icon_changes_listen_recursive(stack, eina_strbuf_string_get(buf), EINA_TRUE);
696 eina_strbuf_reset(buf);
697 }
698
699 #ifndef STRICT_SPEC
700 EINA_LIST_FOREACH(xdg_dirs, l, dir)
701 {
702 eina_strbuf_append_printf(buf, "%s/pixmaps", dir);
703 eina_inarray_flush(stack);
704 icon_changes_listen_recursive(stack, eina_strbuf_string_get(buf), EINA_TRUE);
705 eina_strbuf_reset(buf);
706 }
707 #endif
708 eina_inarray_flush(stack);
709 icon_changes_listen_recursive(stack, "/usr/local/share/pixmaps", EINA_TRUE);
710 icon_changes_listen_recursive(stack, "/usr/share/pixmaps", EINA_TRUE);
711 eina_inarray_free(stack);
712 eina_strbuf_free(buf);
713 }
714
715 static void
desktop_changes_listen(void)716 desktop_changes_listen(void)
717 {
718 Eina_List *l;
719 const char *path;
720 Eina_Inarray *stack;
721
722 stack = eina_inarray_new(sizeof(struct stat), 16);
723 if (!stack) return;
724 EINA_LIST_FOREACH(desktop_system_dirs, l, path)
725 {
726 eina_inarray_flush(stack);
727 desktop_changes_listen_recursive(stack, path, EINA_TRUE);
728 }
729 EINA_LIST_FOREACH(desktop_extra_dirs, l, path)
730 {
731 eina_inarray_flush(stack);
732 desktop_changes_listen_recursive(stack, path, EINA_TRUE);
733 }
734 eina_inarray_free(stack);
735 }
736
737 static void
fill_list(const char * file,Eina_List ** l)738 fill_list(const char *file, Eina_List **l)
739 {
740 Eina_File *f = NULL;
741 Eina_Iterator *it = NULL;
742 Eina_File_Line *line = NULL;
743 Eina_Strbuf *buf = eina_strbuf_new();
744 if (!buf) return;
745
746 eina_strbuf_append_printf(buf, "%s/efreet/%s", efreet_cache_home_get(), file);
747 f = eina_file_open(eina_strbuf_string_get(buf), EINA_FALSE);
748 if (!f) goto error_buf;
749 it = eina_file_map_lines(f);
750 if (!it) goto error;
751 EINA_ITERATOR_FOREACH(it, line)
752 {
753 if (line->end > line->start)
754 {
755 const char *s = eina_stringshare_add_length(line->start, line->end - line->start);
756 if (s) *l = eina_list_append(*l, s);
757 }
758 }
759 eina_iterator_free(it);
760 error:
761 eina_file_close(f);
762 error_buf:
763 eina_strbuf_free(buf);
764 }
765
766 static void
read_lists(void)767 read_lists(void)
768 {
769 // dont use extra dirs as the only way to get extra dirs is by loading a
770 // specific desktop file at a specific path, and this is wrong
771 // fill_list("extra_desktops.dirs", &desktop_extra_dirs);
772 fill_list("extra_icons.dirs", &icon_extra_dirs);
773 fill_list("icons.exts", &icon_exts);
774 }
775
776 static void
save_list(const char * file,Eina_List * l)777 save_list(const char *file, Eina_List *l)
778 {
779 FILE *f;
780 Eina_List *ll;
781 const char *path;
782 Eina_Strbuf *buf = eina_strbuf_new();
783 if (!buf) return;
784
785 eina_strbuf_append_printf(buf, "%s/efreet/%s", efreet_cache_home_get(), file);
786 f = fopen(eina_strbuf_string_get(buf), "wb");
787 if (!f)
788 {
789 eina_strbuf_free(buf);
790 return;
791 }
792 EINA_LIST_FOREACH(l, ll, path)
793 fprintf(f, "%s\n", path);
794 fclose(f);
795 eina_strbuf_free(buf);
796 }
797
798 static int
strcmplen(const void * data1,const void * data2)799 strcmplen(const void *data1, const void *data2)
800 {
801 return strncmp(data1, data2, eina_stringshare_strlen(data1));
802 }
803
804 static Eina_Bool
cache_exe_data_cb(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)805 cache_exe_data_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
806 {
807 Ecore_Exe_Event_Data *ev = event;
808
809 if (ev->exe == desktop_cache_exe)
810 {
811 Eina_Bool update = EINA_FALSE;
812
813 fprintf(efreetd_log_file, "[%09.3f] Data desktop_cache_create\n", ecore_time_get());
814 fflush(efreetd_log_file);
815 if ((ev->lines) && (*ev->lines->line == 'c')) update = EINA_TRUE;
816 if (!desktop_exists)
817 send_signal_desktop_cache_build();
818 desktop_exists = EINA_TRUE;
819 send_signal_desktop_cache_update(update);
820 }
821 else if (ev->exe == icon_cache_exe)
822 {
823 Eina_Bool update = EINA_FALSE;
824
825 fprintf(efreetd_log_file, "[%09.3f] Data icon_cache_create\n", ecore_time_get());
826 fflush(efreetd_log_file);
827 if ((ev->lines) && (*ev->lines->line == 'c')) update = EINA_TRUE;
828 send_signal_icon_cache_update(update);
829 }
830 else if (ev->exe == mime_cache_exe)
831 {
832 fprintf(efreetd_log_file, "[%09.3f] Data mime_cache_create\n", ecore_time_get());
833 fflush(efreetd_log_file);
834 // XXX: ZZZ: handle stdout here from cache updater... if needed
835 }
836 return ECORE_CALLBACK_RENEW;
837 }
838
839 static Eina_Bool
cache_exe_del_cb(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)840 cache_exe_del_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
841 {
842 Ecore_Exe_Event_Del *ev = event;
843
844 if (ev->exe == desktop_cache_exe)
845 {
846 fprintf(efreetd_log_file, "[%09.3f] Exit desktop_cache_create\n", ecore_time_get());
847 fflush(efreetd_log_file);
848 desktop_cache_exe = NULL;
849 if (desktop_queue) cache_desktop_update();
850 }
851 else if (ev->exe == icon_cache_exe)
852 {
853 fprintf(efreetd_log_file, "[%09.3f] Exit icon_cache_create\n", ecore_time_get());
854 fflush(efreetd_log_file);
855 icon_cache_exe = NULL;
856 if (icon_queue) cache_icon_update(EINA_FALSE);
857 }
858 else if (ev->exe == mime_cache_exe)
859 {
860 fprintf(efreetd_log_file, "[%09.3f] Exit mime_cache_create\n", ecore_time_get());
861 fflush(efreetd_log_file);
862 mime_cache_exe = NULL;
863 send_signal_mime_cache_build();
864 }
865 return ECORE_CALLBACK_RENEW;
866 }
867
868 /* external */
869 void
cache_desktop_dir_add(const char * dir)870 cache_desktop_dir_add(const char *dir)
871 {
872 char *san;
873 Eina_List *l;
874
875 san = eina_file_path_sanitize(dir);
876 if (!san) return;
877 if ((l = eina_list_search_unsorted_list(desktop_system_dirs, strcmplen, san)))
878 {
879 /* Path is registered, but maybe not monitored */
880 const char *path = eina_list_data_get(l);
881 if (!eina_hash_find(desktop_change_monitors, path))
882 cache_desktop_update();
883 }
884 else if (!eina_list_search_unsorted_list(desktop_extra_dirs, EINA_COMPARE_CB(strcmp), san))
885 {
886 /* Not a registered path */
887 desktop_extra_dirs = eina_list_append(desktop_extra_dirs, eina_stringshare_add(san));
888 save_list("extra_desktops.dirs", desktop_extra_dirs);
889 cache_desktop_update();
890 }
891 free(san);
892 }
893
894 void
cache_icon_dir_add(const char * dir)895 cache_icon_dir_add(const char *dir)
896 {
897 char *san;
898
899 san = eina_file_path_sanitize(dir);
900 if (!san) return;
901 if (!eina_list_search_unsorted_list(icon_extra_dirs, EINA_COMPARE_CB(strcmp), san))
902 {
903 if (!strcmp(san, "/")) goto out;
904 icon_extra_dirs = eina_list_append(icon_extra_dirs, eina_stringshare_add(san));
905 save_list("extra_icons.dirs", icon_extra_dirs);
906 cache_icon_update(EINA_TRUE);
907 }
908 out:
909 free(san);
910 }
911
912 void
cache_icon_ext_add(const char * ext)913 cache_icon_ext_add(const char *ext)
914 {
915 if (!eina_list_search_unsorted_list(icon_exts, EINA_COMPARE_CB(strcmp), ext))
916 {
917 icon_exts = eina_list_append(icon_exts, eina_stringshare_add(ext));
918 save_list("icons.exts", icon_exts);
919 cache_icon_update(EINA_TRUE);
920 }
921 }
922
923 Eina_Bool
cache_desktop_exists(void)924 cache_desktop_exists(void)
925 {
926 return desktop_exists;
927 }
928
929 static void
mime_update_launch(void)930 mime_update_launch(void)
931 {
932 Eina_Strbuf *file = eina_strbuf_new();
933 if (!file) return;
934
935 eina_strbuf_append_printf(file,
936 "%s/efreet/" MODULE_ARCH "/efreet_mime_cache_create",
937 eina_prefix_lib_get(pfx));
938 mime_cache_exe = ecore_exe_pipe_run(eina_strbuf_string_get(file),
939 ECORE_EXE_PIPE_READ |
940 ECORE_EXE_PIPE_READ_LINE_BUFFERED,
941 NULL);
942 eina_strbuf_free(file);
943 }
944
945 static Eina_Bool
mime_update_cache_cb(void * data EINA_UNUSED)946 mime_update_cache_cb(void *data EINA_UNUSED)
947 {
948 mime_update_timer = NULL;
949 if (mime_cache_exe)
950 {
951 ecore_exe_kill(mime_cache_exe);
952 ecore_exe_free(mime_cache_exe);
953 }
954 mime_update_launch();
955 return EINA_FALSE;
956 }
957
958 static void
mime_cache_init(void)959 mime_cache_init(void)
960 {
961 Eio_Monitor *mon;
962 Eina_List *datadirs, *l;
963 const char *s;
964 Eina_Strbuf *buf = eina_strbuf_new();
965 if (!buf) return;
966
967 mime_monitors = eina_hash_string_superfast_new
968 (EINA_FREE_CB(eio_monitor_del));
969 mime_monitors_mon = eina_hash_pointer_new(NULL);
970
971 if (ecore_file_is_dir("/etc"))
972 {
973 mon = eio_monitor_add("/etc"); // specifically look at /etc/mime.types
974 if (mon)
975 {
976 eina_hash_add(mime_monitors, "/etc", mon);
977 eina_hash_add(mime_monitors_mon, &mon, mon);
978 }
979 }
980 if (ecore_file_is_dir("/usr/share/mime"))
981 {
982 mon = eio_monitor_add("/usr/share/mime"); // specifically look at /usr/share/mime/globs
983 if (mon)
984 {
985 eina_hash_add(mime_monitors, "/usr/share/mime", mon);
986 eina_hash_add(mime_monitors_mon, &mon, mon);
987 }
988 }
989
990 datadirs = efreet_data_dirs_get();
991 EINA_LIST_FOREACH(datadirs, l, s)
992 {
993 eina_strbuf_append_printf(buf, "%s/mime", s); // specifically lok at XXX/mime/globs
994 if (ecore_file_is_dir(eina_strbuf_string_get(buf)))
995 {
996 if (!eina_hash_find(mime_monitors, eina_strbuf_string_get(buf)))
997 {
998 mon = eio_monitor_add(eina_strbuf_string_get(buf));
999 if (mon)
1000 {
1001 eina_hash_add(mime_monitors, eina_strbuf_string_get(buf), mon);
1002 eina_hash_add(mime_monitors_mon, &mon, mon);
1003 }
1004 }
1005 }
1006 }
1007 eina_strbuf_free(buf);
1008 }
1009
1010 static void
mime_cache_shutdown(void)1011 mime_cache_shutdown(void)
1012 {
1013 if (mime_update_timer)
1014 {
1015 ecore_timer_del(mime_update_timer);
1016 mime_update_timer = NULL;
1017 }
1018 if (mime_monitors)
1019 {
1020 eina_hash_free(mime_monitors);
1021 mime_monitors = NULL;
1022 }
1023 if (mime_monitors_mon)
1024 {
1025 eina_hash_free(mime_monitors_mon);
1026 mime_monitors_mon = NULL;
1027 }
1028 }
1029
1030 Eina_Bool
cache_init(void)1031 cache_init(void)
1032 {
1033 char **argv;
1034
1035 ecore_app_args_get(NULL, &argv);
1036
1037 pfx = eina_prefix_new(argv[0], cache_init,
1038 "EFREET", "efreet", "checkme",
1039 PACKAGE_BIN_DIR,
1040 PACKAGE_LIB_DIR,
1041 PACKAGE_DATA_DIR,
1042 PACKAGE_DATA_DIR);
1043
1044 cache_exe_del_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
1045 cache_exe_del_cb, NULL);
1046 if (!cache_exe_del_handler)
1047 {
1048 ERR("Failed to add exe del handler");
1049 goto error;
1050 }
1051 cache_exe_data_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DATA,
1052 cache_exe_data_cb, NULL);
1053 if (!cache_exe_data_handler)
1054 {
1055 ERR("Failed to add exe data handler");
1056 goto error;
1057 }
1058
1059 icon_change_monitors = eina_hash_string_superfast_new
1060 (EINA_FREE_CB(eio_monitor_del));
1061 icon_change_monitors_mon = eina_hash_pointer_new(NULL);
1062 desktop_change_monitors = eina_hash_string_superfast_new
1063 (EINA_FREE_CB(eio_monitor_del));
1064 desktop_change_monitors_mon = eina_hash_pointer_new(NULL);
1065
1066 efreet_cache_update = 0;
1067 if (!efreet_init()) goto error;
1068 eio_init();
1069
1070 #define MONITOR_EVENT(ev, fn) \
1071 _handlers = eina_list_append(_handlers, ecore_event_handler_add(ev, fn, NULL))
1072 MONITOR_EVENT(EIO_MONITOR_FILE_CREATED, _cb_monitor_event);
1073 MONITOR_EVENT(EIO_MONITOR_FILE_DELETED, _cb_monitor_event);
1074 MONITOR_EVENT(EIO_MONITOR_FILE_MODIFIED, _cb_monitor_event);
1075 MONITOR_EVENT(EIO_MONITOR_DIRECTORY_CREATED, _cb_monitor_event);
1076 MONITOR_EVENT(EIO_MONITOR_DIRECTORY_DELETED, _cb_monitor_event);
1077 MONITOR_EVENT(EIO_MONITOR_DIRECTORY_MODIFIED, _cb_monitor_event);
1078 MONITOR_EVENT(EIO_MONITOR_SELF_RENAME, _cb_monitor_event);
1079 MONITOR_EVENT(EIO_MONITOR_SELF_DELETED, _cb_monitor_event);
1080
1081 subdir_cache_init();
1082 mime_cache_init();
1083 mime_update_launch();
1084 read_lists();
1085 /* TODO: Should check if system dirs has changed and handles extra_dirs */
1086 desktop_system_dirs = efreet_default_dirs_get(efreet_data_home_get(),
1087 efreet_data_dirs_get(), "applications");
1088 desktop_system_dirs =
1089 eina_list_merge(
1090 desktop_system_dirs, efreet_default_dirs_get(efreet_data_home_get(),
1091 efreet_data_dirs_get(), "desktop-directories"));
1092 icon_changes_listen();
1093 desktop_changes_listen();
1094 cache_icon_update(EINA_FALSE);
1095 cache_desktop_update();
1096 subdir_cache_save();
1097
1098 return EINA_TRUE;
1099 error:
1100 if (cache_exe_del_handler) ecore_event_handler_del(cache_exe_del_handler);
1101 cache_exe_del_handler = NULL;
1102 if (cache_exe_data_handler) ecore_event_handler_del(cache_exe_data_handler);
1103 cache_exe_data_handler = NULL;
1104 return EINA_FALSE;
1105 }
1106
1107 Eina_Bool
cache_shutdown(void)1108 cache_shutdown(void)
1109 {
1110 const char *data;
1111 Ecore_Event_Handler *handler;
1112
1113 eina_prefix_free(pfx);
1114 pfx = NULL;
1115
1116 mime_cache_shutdown();
1117 subdir_cache_shutdown();
1118 efreet_shutdown();
1119
1120 if (cache_exe_del_handler) ecore_event_handler_del(cache_exe_del_handler);
1121 cache_exe_del_handler = NULL;
1122 if (cache_exe_data_handler) ecore_event_handler_del(cache_exe_data_handler);
1123 cache_exe_data_handler = NULL;
1124
1125 if (icon_change_monitors) eina_hash_free(icon_change_monitors);
1126 icon_change_monitors = NULL;
1127 if (icon_change_monitors_mon) eina_hash_free(icon_change_monitors_mon);
1128 icon_change_monitors_mon = NULL;
1129 if (desktop_change_monitors) eina_hash_free(desktop_change_monitors);
1130 desktop_change_monitors = NULL;
1131 if (desktop_change_monitors_mon) eina_hash_free(desktop_change_monitors_mon);
1132 desktop_change_monitors_mon = NULL;
1133 EINA_LIST_FREE(desktop_system_dirs, data)
1134 eina_stringshare_del(data);
1135 EINA_LIST_FREE(desktop_extra_dirs, data)
1136 eina_stringshare_del(data);
1137 EINA_LIST_FREE(icon_extra_dirs, data)
1138 eina_stringshare_del(data);
1139 EINA_LIST_FREE(icon_exts, data)
1140 eina_stringshare_del(data);
1141 EINA_LIST_FREE(_handlers, handler)
1142 ecore_event_handler_del(handler);
1143 eio_shutdown();
1144 return EINA_TRUE;
1145 }
1146