EFL 1.7 svn doobies
[profile/ivi/efreet.git] / src / bin / efreet_desktop_cache_create.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <errno.h>
8
9 #include <Eina.h>
10 #include <Eet.h>
11 #include <Ecore.h>
12 #include <Ecore_File.h>
13
14 #define EFREET_MODULE_LOG_DOM _efreet_desktop_cache_log_dom
15 static int _efreet_desktop_cache_log_dom = -1;
16
17 #include "Efreet.h"
18 #include "efreet_private.h"
19 #include "efreet_cache_private.h"
20
21 static Eet_Data_Descriptor *edd = NULL;
22 static Eet_File *ef = NULL;
23 static Eet_File *util_ef = NULL;
24
25 static Eina_Hash *desktops = NULL;
26
27 static Eina_Hash         *file_ids = NULL;
28 static Efreet_Cache_Hash *old_file_ids = NULL;
29 static Eina_Hash         *paths = NULL;
30
31 static Eina_Hash *mime_types = NULL;
32 static Eina_Hash *categories = NULL;
33 static Eina_Hash *startup_wm_class = NULL;
34 static Eina_Hash *name = NULL;
35 static Eina_Hash *generic_name = NULL;
36 static Eina_Hash *comment = NULL;
37 static Eina_Hash *exec = NULL;
38
39 static int
40 strcmplen(const void *data1, const void *data2)
41 {
42     return strncmp(data1, data2, eina_stringshare_strlen(data1));
43 }
44
45 static int
46 cache_add(const char *path, const char *file_id, int priority __UNUSED__, int *changed)
47 {
48     Efreet_Desktop *desk;
49     char *ext;
50
51     INF("FOUND: %s", path);
52     if (file_id) INF(" (id): %s", file_id);
53     ext = strrchr(path, '.');
54     if (!ext || (strcmp(ext, ".desktop") && strcmp(ext, ".directory"))) return 1;
55     desk = efreet_desktop_new(path);
56     if (desk) INF("  OK");
57     else      INF("  FAIL");
58     if (!desk) return 1;
59     if (!desk->eet)
60     {
61         /* This file isn't in cache */
62         *changed = 1;
63         INF("  NEW");
64     }
65     else if (ecore_file_mod_time(desk->orig_path) != desk->load_time)
66     {
67         efreet_desktop_free(desk);
68         *changed = 1;
69         desk = efreet_desktop_uncached_new(path);
70         if (desk) INF("  CHANGED");
71         else      INF("  NO UNCACHED");
72     }
73     if (!desk) return 1;
74     if (file_id && old_file_ids && !eina_hash_find(old_file_ids->hash, file_id))
75     {
76         *changed = 1;
77         INF("  NOT IN UTILS");
78     }
79     if (!eina_hash_find(paths, desk->orig_path))
80     {
81         if (!eet_data_write(ef, edd, desk->orig_path, desk, 0))
82             return 0;
83         eina_hash_add(paths, desk->orig_path, (void *)1);
84     }
85     /* TODO: We should check priority, and not just hope we search in right order */
86     /* TODO: We need to find out if prioritized file id has changed because of
87      * changed search order. */
88     if (!desk->hidden && desk->type == EFREET_DESKTOP_TYPE_APPLICATION &&
89         file_id && !eina_hash_find(file_ids, file_id))
90     {
91         Eina_List *l;
92         char *data;
93         Efreet_Cache_Array_String *array;
94
95 #define ADD_LIST(list, hash) \
96         EINA_LIST_FOREACH((list), l, data) \
97         { \
98             array = eina_hash_find((hash), data); \
99             if (!array) \
100                 array = NEW(Efreet_Cache_Array_String, 1); \
101             array->array = realloc(array->array, sizeof (char *) * (array->array_count + 1)); \
102             array->array[array->array_count++] = desk->orig_path; \
103             eina_hash_set((hash), data, array); \
104         }
105 #define ADD_ELEM(elem, hash) \
106         if ((elem)) \
107         { \
108             data = (elem); \
109             array = eina_hash_find((hash), data); \
110             if (!array) \
111                 array = NEW(Efreet_Cache_Array_String, 1); \
112             array->array = realloc(array->array, sizeof (char *) * (array->array_count + 1)); \
113             array->array[array->array_count++] = desk->orig_path; \
114             eina_hash_set((hash), data, array); \
115         }
116         ADD_LIST(desk->mime_types, mime_types);
117         ADD_LIST(desk->categories, categories);
118         ADD_ELEM(desk->startup_wm_class, startup_wm_class);
119         ADD_ELEM(desk->name, name);
120         ADD_ELEM(desk->generic_name, generic_name);
121         ADD_ELEM(desk->comment, comment);
122         ADD_ELEM(desk->exec, exec);
123         eina_hash_add(file_ids, file_id, desk->orig_path);
124         eina_hash_add(desktops, desk->orig_path, desk);
125     }
126     else
127         efreet_desktop_free(desk);
128     return 1;
129 }
130
131
132 static int
133 cache_scan(const char *path, const char *base_id, int priority, int recurse, int *changed)
134 {
135     char *file_id = NULL;
136     char id[PATH_MAX];
137     char buf[PATH_MAX];
138     Eina_Iterator *it;
139     Eina_File_Direct_Info *info;
140
141     if (!ecore_file_is_dir(path)) return 1;
142
143     it = eina_file_direct_ls(path);
144     if (!it) return 1;
145
146     id[0] = '\0';
147     EINA_ITERATOR_FOREACH(it, info)
148     {
149         const char *fname;
150        
151         fname = info->path + info->name_start;
152         if (base_id)
153         {
154             if (*base_id)
155                 snprintf(id, sizeof(id), "%s-%s", base_id, fname);
156             else
157                 strcpy(id, fname);
158             file_id = id;
159         }
160
161         snprintf(buf, sizeof(buf), "%s/%s", path, fname);
162         if (ecore_file_is_dir(buf))
163         {
164             if (recurse)
165                 cache_scan(buf, file_id, priority, recurse, changed);
166         }
167         else
168         {
169             if (!cache_add(buf, file_id, priority, changed))
170             {
171                 eina_iterator_free(it);
172                 return 0;
173             }
174         }
175     }
176     eina_iterator_free(it);
177     return 1;
178 }
179
180 static int
181 cache_lock_file(void)
182 {
183     char file[PATH_MAX];
184     struct flock fl;
185     int lockfd;
186
187     snprintf(file, sizeof(file), "%s/efreet/desktop_data.lock", efreet_cache_home_get());
188     lockfd = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
189     if (lockfd < 0) return -1;
190     efreet_fsetowner(lockfd);
191
192     memset(&fl, 0, sizeof(struct flock));
193     fl.l_type = F_WRLCK;
194     fl.l_whence = SEEK_SET;
195     if (fcntl(lockfd, F_SETLK, &fl) < 0)
196     {
197         INF("LOCKED! You may want to delete %s if this persists", file);
198         close(lockfd);
199         return -1;
200     }
201
202     return lockfd;
203 }
204
205 int
206 main(int argc, char **argv)
207 {
208     /* TODO:
209      * - Add file monitor on files, so that we catch changes on files
210      *   during whilst this program runs.
211      * - Maybe linger for a while to reduce number of cache re-creates.
212      */
213     Efreet_Cache_Hash hash;
214     Efreet_Cache_Version version;
215     Eina_List *dirs = NULL;
216     Eina_List *systemdirs = NULL;
217     Efreet_Cache_Array_String *user_dirs = NULL;
218     Eina_List *extra_dirs = NULL;
219     Eina_List *store_dirs = NULL;
220     int priority = 0;
221     char *dir = NULL;
222     char *path;
223     int lockfd = -1, tmpfd;
224     int changed = 0;
225     int i;
226     char file[PATH_MAX] = { '\0' };
227     char util_file[PATH_MAX] = { '\0' };
228
229     if (!eina_init()) goto eina_error;
230     _efreet_desktop_cache_log_dom =
231         eina_log_domain_register("efreet_desktop_cache", EFREET_DEFAULT_LOG_COLOR);
232     if (_efreet_desktop_cache_log_dom < 0)
233     {
234         EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_desktop_cache.");
235         return -1;
236     }
237
238     for (i = 1; i < argc; i++)
239     {
240         if (!strcmp(argv[i], "-v"))
241             eina_log_domain_level_set("efreet_desktop_cache", EINA_LOG_LEVEL_DBG);
242         else if ((!strcmp(argv[i], "-h")) ||
243                  (!strcmp(argv[i], "-help")) ||
244                  (!strcmp(argv[i], "--h")) ||
245                  (!strcmp(argv[i], "--help")))
246         {
247             printf("Options:\n");
248             printf("  -v              Verbose mode\n");
249             printf("  -d dir1 dir2    Extra dirs\n");
250             exit(0);
251         }
252         else if (!strcmp(argv[i], "-d"))
253         {
254             while ((i < (argc - 1)) && (argv[(i + 1)][0] != '-'))
255                 extra_dirs = eina_list_append(extra_dirs, argv[++i]);
256         }
257     }
258     extra_dirs = eina_list_sort(extra_dirs, -1, EINA_COMPARE_CB(strcmp));
259
260     /* init external subsystems */
261     if (!eet_init()) goto eet_error;
262     if (!ecore_init()) goto ecore_error;
263
264     efreet_cache_update = 0;
265     /* finish efreet init */
266     if (!efreet_init()) goto efreet_error;
267
268     /* create homedir */
269     snprintf(file, sizeof(file), "%s/efreet", efreet_cache_home_get());
270     if (!ecore_file_exists(file))
271     {
272         if (!ecore_file_mkpath(file)) goto efreet_error;
273         efreet_setowner(file);
274     }
275
276     /* lock process, so that we only run one copy of this program */
277     lockfd = cache_lock_file();
278     if (lockfd == -1) goto efreet_error;
279
280     edd = efreet_desktop_edd();
281     if (!edd) goto edd_error;
282
283     /* read user dirs from old cache */
284     ef = eet_open(efreet_desktop_cache_file(), EET_FILE_MODE_READ);
285     if (ef)
286     {
287         user_dirs = eet_data_read(ef, efreet_array_string_edd(), EFREET_CACHE_DESKTOP_DIRS);
288         eet_close(ef);
289     }
290
291     ef = eet_open(efreet_desktop_util_cache_file(), EET_FILE_MODE_READ);
292     if (ef)
293     {
294         old_file_ids = eet_data_read(ef, efreet_hash_string_edd(), "file_id");
295         eet_close(ef);
296     }
297
298     /* create cache */
299     snprintf(file, sizeof(file), "%s.XXXXXX", efreet_desktop_cache_file());
300     tmpfd = mkstemp(file);
301     if (tmpfd < 0) goto error;
302     close(tmpfd);
303     ef = eet_open(file, EET_FILE_MODE_READ_WRITE);
304     if (!ef) goto error;
305
306     snprintf(util_file, sizeof(util_file), "%s.XXXXXX", efreet_desktop_util_cache_file());
307     tmpfd = mkstemp(util_file);
308     if (tmpfd < 0) goto error;
309     close(tmpfd);
310     util_ef = eet_open(util_file, EET_FILE_MODE_READ_WRITE);
311     if (!util_ef) goto error;
312
313     /* write cache version */
314     version.major = EFREET_DESKTOP_UTILS_CACHE_MAJOR;
315     version.minor = EFREET_DESKTOP_UTILS_CACHE_MINOR;
316     eet_data_write(util_ef, efreet_version_edd(), EFREET_CACHE_VERSION, &version, 1);
317     version.major = EFREET_DESKTOP_CACHE_MAJOR;
318     version.minor = EFREET_DESKTOP_CACHE_MINOR;
319     eet_data_write(ef, efreet_version_edd(), EFREET_CACHE_VERSION, &version, 1);
320
321     desktops = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
322
323     file_ids = eina_hash_string_superfast_new(NULL);
324     paths = eina_hash_string_superfast_new(NULL);
325
326     mime_types = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
327     categories = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
328     startup_wm_class = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
329     name = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
330     generic_name = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
331     comment = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
332     exec = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
333
334     dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
335                                                                     "applications");
336     if (!dirs) goto error;
337
338     EINA_LIST_FREE(dirs, path)
339     {
340         char file_id[PATH_MAX] = { '\0' };
341
342         if (!cache_scan(path, file_id, priority++, 1, &changed)) goto error;
343         systemdirs = eina_list_append(systemdirs, path);
344     }
345
346     if (user_dirs)
347     {
348         unsigned int j;
349
350         for (j = 0; j < user_dirs->array_count; j++)
351         {
352             if (eina_list_search_unsorted_list(systemdirs, strcmplen, user_dirs->array[j]))
353                 continue;
354             if (!ecore_file_is_dir(user_dirs->array[j])) continue;
355             if (!cache_scan(user_dirs->array[j], NULL, priority, 0, &changed)) goto error;
356
357             store_dirs = eina_list_append(store_dirs, user_dirs->array[j]);
358         }
359         store_dirs = eina_list_sort(store_dirs, -1, EINA_COMPARE_CB(strcmp));
360     }
361
362     if (extra_dirs)
363     {
364         Eina_List *l;
365
366         EINA_LIST_FOREACH(extra_dirs, l, path)
367         {
368             if (eina_list_search_unsorted_list(systemdirs, strcmplen, path))
369                 continue;
370             if (eina_list_search_unsorted_list(store_dirs, EINA_COMPARE_CB(strcmp), path))
371                 continue;
372             if (!ecore_file_is_dir(path)) continue;
373
374             /* If we scan a passed dir, we must have changed */
375             changed = 1;
376             if (!cache_scan(path, NULL, priority, 0, &changed)) goto error;
377
378             store_dirs = eina_list_append(store_dirs, path);
379         }
380         store_dirs = eina_list_sort(store_dirs, -1, EINA_COMPARE_CB(strcmp));
381     }
382
383     if (user_dirs)
384         efreet_cache_array_string_free(user_dirs);
385
386     /* store user dirs */
387     if (store_dirs)
388     {
389         Eina_List *l;
390
391         user_dirs = NEW(Efreet_Cache_Array_String, 1);
392         user_dirs->array = NEW(char *, eina_list_count(store_dirs));
393         user_dirs->array_count = 0;
394         EINA_LIST_FOREACH(store_dirs, l, path)
395             user_dirs->array[user_dirs->array_count++] = path;
396
397         eet_data_write(ef, efreet_array_string_edd(), EFREET_CACHE_DESKTOP_DIRS, user_dirs, 1);
398         IF_FREE(user_dirs->array);
399         free(user_dirs);
400     }
401
402     /* store util */
403 #define STORE_HASH_ARRAY(_hash) \
404     if (eina_hash_population((_hash)) > 0) \
405     { \
406         Eina_Iterator *it;   \
407         Efreet_Cache_Array_String array; \
408         const char *str;     \
409                              \
410         hash.hash = (_hash); \
411         eet_data_write(util_ef, efreet_hash_array_string_edd(), #_hash "_hash", &hash, 1); \
412         array.array_count = 0; \
413         array.array = malloc(eina_hash_population(hash.hash) * sizeof(char *)); \
414         it = eina_hash_iterator_key_new(hash.hash); \
415         EINA_ITERATOR_FOREACH(it, str) \
416             array.array[array.array_count++] = str; \
417         eina_iterator_free(it); \
418         eet_data_write(util_ef, efreet_array_string_edd(), #_hash "_list", &array, 1); \
419         free(array.array); \
420     }
421     STORE_HASH_ARRAY(mime_types);
422     STORE_HASH_ARRAY(categories);
423     STORE_HASH_ARRAY(startup_wm_class);
424     STORE_HASH_ARRAY(name);
425     STORE_HASH_ARRAY(generic_name);
426     STORE_HASH_ARRAY(comment);
427     STORE_HASH_ARRAY(exec);
428     if (eina_hash_population(file_ids) > 0)
429     {
430         hash.hash = file_ids;
431         eet_data_write(util_ef, efreet_hash_string_edd(), "file_id", &hash, 1);
432     }
433
434     eina_hash_free(mime_types);
435     eina_hash_free(categories);
436     eina_hash_free(startup_wm_class);
437     eina_hash_free(name);
438     eina_hash_free(generic_name);
439     eina_hash_free(comment);
440     eina_hash_free(exec);
441
442     if (old_file_ids)
443     {
444         eina_hash_free(old_file_ids->hash);
445         free(old_file_ids);
446     }
447
448     eina_hash_free(file_ids);
449     eina_hash_free(paths);
450
451     eina_hash_free(desktops);
452
453     /* check if old and new caches contain the same number of entries */
454     if (!changed)
455     {
456         Eet_File *old;
457
458         old = eet_open(efreet_desktop_cache_file(), EET_FILE_MODE_READ);
459         if (!old || eet_num_entries(old) != eet_num_entries(ef)) changed = 1;
460         if (old) eet_close(old);
461         old = eet_open(efreet_desktop_util_cache_file(), EET_FILE_MODE_READ);
462         if (!old || eet_num_entries(old) != eet_num_entries(util_ef)) changed = 1;
463         if (old) eet_close(old);
464     }
465
466     /* cleanup */
467     eet_close(util_ef);
468     eet_close(ef);
469
470     /* unlink old cache files */
471     if (changed)
472     {
473         if (unlink(efreet_desktop_cache_file()) < 0)
474         {
475             if (errno != ENOENT) goto error;
476         }
477         if (unlink(efreet_desktop_util_cache_file()) < 0)
478         {
479             if (errno != ENOENT) goto error;
480         }
481         /* rename tmp files to real files */
482         if (rename(util_file, efreet_desktop_util_cache_file()) < 0) goto error;
483         efreet_setowner(efreet_desktop_util_cache_file());
484         if (rename(file, efreet_desktop_cache_file()) < 0) goto error;
485         efreet_setowner(efreet_desktop_cache_file());
486     }
487     else
488     {
489         unlink(util_file);
490         unlink(file);
491     }
492
493     /* touch update file */
494     snprintf(file, sizeof(file), "%s/efreet/desktop_data.update", efreet_cache_home_get());
495     tmpfd = open(file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
496     if (tmpfd >= 0)
497     {
498         char c = 'n';
499
500         efreet_fsetowner(tmpfd);
501         if (changed) c = 'c';
502         if (write(tmpfd, &c, 1) != 1) perror("write");
503         close(tmpfd);
504     }
505
506     EINA_LIST_FREE(systemdirs, dir)
507         eina_stringshare_del(dir);
508     eina_list_free(extra_dirs);
509     eina_list_free(store_dirs);
510     efreet_shutdown();
511     ecore_shutdown();
512     eet_shutdown();
513     eina_log_domain_unregister(_efreet_desktop_cache_log_dom);
514     eina_shutdown();
515     close(lockfd);
516     return 0;
517 error:
518     IF_FREE(dir);
519 edd_error:
520     if (user_dirs) efreet_cache_array_string_free(user_dirs);
521     if (old_file_ids)
522     {
523         eina_hash_free(old_file_ids->hash);
524         free(old_file_ids);
525     }
526     efreet_shutdown();
527 efreet_error:
528     ecore_shutdown();
529 ecore_error:
530     eet_shutdown();
531 eet_error:
532     EINA_LIST_FREE(systemdirs, dir)
533         eina_stringshare_del(dir);
534     eina_list_free(extra_dirs);
535     eina_list_free(store_dirs);
536     eina_log_domain_unregister(_efreet_desktop_cache_log_dom);
537     eina_shutdown();
538 eina_error:
539     if (lockfd >= 0) close(lockfd);
540     return 1;
541 }