update for beta release
[framework/uifw/e17.git] / src / bin / e_start_main.c
1 #include "config.h"
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <dlfcn.h>
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <sys/stat.h>
10 #include <sys/utsname.h>
11 #include <limits.h>
12 #include <fcntl.h>
13 #ifdef HAVE_ALLOCA_H
14 # include <alloca.h>
15 #endif
16 #include <Eina.h>
17
18 static void env_set(const char *var, const char *val);
19 EAPI int prefix_determine(char *argv0);
20
21 static void
22 env_set(const char *var, const char *val)
23 {
24    if (val)
25      {
26 #ifdef HAVE_SETENV
27         setenv(var, val, 1);
28 #else
29         char *buf;
30
31         buf = alloca(strlen(var) + 1 + strlen(val) + 1);
32         snprintf(buf, sizeof(buf), "%s=%s", var, val);
33         if (getenv(var)) putenv(buf);
34         else putenv(strdup(buf));
35 #endif
36      }
37    else
38      {
39 #ifdef HAVE_UNSETENV
40         unsetenv(var);
41 #else
42         if (getenv(var)) putenv(var);
43 #endif
44      }
45 }
46
47 /* local subsystem globals */
48 static Eina_Prefix *pfx = NULL;
49
50 /* externally accessible functions */
51 EAPI int
52 prefix_determine(char *argv0)
53 {
54    pfx = eina_prefix_new(argv0, prefix_determine,
55                          "E", "enlightenment", "AUTHORS",
56                          PACKAGE_BIN_DIR, PACKAGE_LIB_DIR,
57                          PACKAGE_DATA_DIR, LOCALE_DIR);
58    if (!pfx) return 0;
59    return 1;
60 }
61
62 static void
63 precache(void)
64 {
65    FILE *f;
66    char *home;
67    char buf[4096], tbuf[256 * 1024];
68    struct stat st;
69    int l, fd, children = 0, cret;
70
71    home = getenv("HOME");
72    if (home) snprintf(buf, sizeof(buf), "%s/.e-precache", home);
73    else snprintf(buf, sizeof(buf), "/tmp/.e-precache");
74    f = fopen(buf, "r");
75    if (!f) return;
76    unlink(buf);
77    if (fork()) return;
78 //   while (fgets(buf, sizeof(buf), f));
79 //   rewind(f);
80    while (fgets(buf, sizeof(buf), f))
81      {
82         l = strlen(buf);
83         if (l > 0) buf[l - 1] = 0;
84         if (!fork())
85           {
86              if (buf[0] == 's') stat(buf + 2, &st);
87              else if (buf[0] == 'o')
88                {
89                   fd = open(buf + 2, O_RDONLY);
90                   if (fd >= 0)
91                     {
92                        while (read(fd, tbuf, 256 * 1024) > 0);
93                        close(fd);
94                     }
95                }
96              else if (buf[0] == 'd')
97                {
98                   fd = open(buf + 2, O_RDONLY);
99                   if (fd >= 0)
100                     {
101                        while (read(fd, tbuf, 256 * 1024) > 0);
102                        close(fd);
103                     }
104                }
105              exit(0);
106           }
107         children++;
108         if (children > 400)
109           {
110              wait(&cret);
111              children--;
112           }
113      }
114    fclose(f);
115    while (children > 0)
116      {
117         wait(&cret);
118         children--;
119      }
120    exit(0);
121 }
122
123 static int
124 find_valgrind(char *path, size_t path_len)
125 {
126    const char *env = getenv("PATH");
127    
128    while (env)
129      {
130         const char *p = strchr(env, ':');
131         ssize_t p_len;
132         
133         if (p) p_len = p - env;
134         else p_len = strlen(env);
135         if (p_len <= 0) goto next;
136         else if (p_len + sizeof("/valgrind") >= path_len) goto next;
137         memcpy(path, env, p_len);
138         memcpy(path + p_len, "/valgrind", sizeof("/valgrind"));
139         if (access(path, X_OK | R_OK) == 0) return 1;
140 next:
141         if (p) env = p + 1;
142         else break;
143      }
144    path[0] = '\0';
145    return 0;
146 }
147
148
149 /* maximum number of arguments added above */
150 #define VALGRIND_MAX_ARGS 9
151 /* bitmask with all supported bits set */
152 #define VALGRIND_MODE_ALL 15
153
154 static int
155 valgrind_append(char **dst, int valgrind_mode, int valgrind_tool, char *valgrind_path, const char *valgrind_log)
156 {
157    int i = 0;
158
159    if (valgrind_tool)
160      {
161         dst[i++] = valgrind_path;
162         switch (valgrind_tool)
163           {
164            case 1: dst[i++] = "--tool=massif"; break;
165            case 2: dst[i++] = "--tool=callgrind"; break;
166           }
167         return i;
168      }
169    if (!valgrind_mode) return 0;
170    dst[i++] = valgrind_path;
171    dst[i++] = "--track-origins=yes";
172    dst[i++] = "--malloc-fill=13"; /* invalid pointer, make it crash */
173    if (valgrind_log)
174      {
175         static char logparam[PATH_MAX + sizeof("--log-file=")];
176         
177         snprintf(logparam, sizeof(logparam), "--log-file=%s", valgrind_log);
178         dst[i++] = logparam;
179      }
180    if (valgrind_mode & 2) dst[i++] = "--trace-children=yes";
181    if (valgrind_mode & 4)
182      {
183         dst[i++] = "--leak-check=full";
184         dst[i++] = "--leak-resolution=high";
185         dst[i++] = "--track-fds=yes";
186      }
187    if (valgrind_mode & 8) dst[i++] = "--show-reachable=yes";
188    return i;
189 }
190
191 static void
192 copy_args(char **dst, char **src, size_t count)
193 {
194    for (; count > 0; count--, dst++, src++) *dst = *src;
195 }
196
197 int
198 main(int argc, char **argv)
199 {
200    int i, do_precache = 0, valgrind_mode = 0;
201    int valgrind_tool = 0;
202    char buf[16384], **args, *p;
203    char valgrind_path[PATH_MAX] = "";
204    const char *valgrind_log = NULL;
205
206    eina_init();
207    prefix_determine(argv[0]);
208
209    env_set("E_START", argv[0]);
210
211    p = getenv("PATH");
212    if (p) snprintf(buf, sizeof(buf), "%s:%s", eina_prefix_bin_get(pfx), p);
213    else snprintf(buf, sizeof(buf), "%s", eina_prefix_bin_get(pfx));
214    env_set("PATH", buf);
215
216    p = getenv("LD_LIBRARY_PATH");
217    if (p) snprintf(buf, sizeof(buf), "%s:%s", eina_prefix_lib_get(pfx), p);
218    else snprintf(buf, sizeof(buf), "%s", eina_prefix_lib_get(pfx));
219    env_set("LD_LIBRARY_PATH", buf);
220
221    for (i = 1; i < argc; i++)
222      {
223         if (!strcmp(argv[i], "-no-precache")) do_precache = 0;
224         else if (!strncmp(argv[i], "-valgrind", sizeof("-valgrind") - 1))
225           {
226              const char *val = argv[i] + sizeof("-valgrind") - 1;
227
228              if (*val == '\0') valgrind_mode = 1;
229              else if (*val == '-')
230                {
231                   val++;
232                   if (!strncmp(val, "log-file=", sizeof("log-file=") - 1))
233                     {
234                        valgrind_log = val + sizeof("log-file=") - 1;
235                        if (*valgrind_log == '\0') valgrind_log = NULL;
236                     }
237                }
238              else if (*val == '=')
239                {
240                   val++;
241                   if (!strcmp(val, "all")) valgrind_mode = VALGRIND_MODE_ALL;
242                   else valgrind_mode = atoi(val);
243                }
244              else
245                 printf("Unknown valgrind option: %s\n", argv[i]);
246           }
247         else if (!strcmp(argv[i], "-massif")) valgrind_tool = 1;
248         else if (!strcmp(argv[i], "-callgrind")) valgrind_tool = 2;
249         else if ((!strcmp(argv[i], "-h")) ||
250                  (!strcmp(argv[i], "-help")) ||
251                  (!strcmp(argv[i], "--help")))
252           {
253              printf
254                 (
255                     "Options:\n"
256                     "\t-no-precache\n"
257                     "\t\tDisable pre-caching of files\n"
258                     "\t-valgrind[=MODE]\n"
259                     "\t\tRun enlightenment from inside valgrind, mode is OR of:\n"
260                     "\t\t   1 = plain valgrind to catch crashes (default)\n"
261                     "\t\t   2 = trace children (thumbnailer, efm slaves, ...)\n"
262                     "\t\t   4 = check leak\n"
263                     "\t\t   8 = show reachable after processes finish.\n"
264                     "\t\t all = all of above\n"
265                     "\t-massif\n"
266                     "\t\tRun enlightenment from inside massif valgrind tool.\n"
267                     "\t-callgrind\n"
268                     "\t\tRun enlightenment from inside callgrind valgrind tool.\n"
269                     "\t-valgrind-log-file=<FILENAME>\n"
270                     "\t\tSave valgrind log to file, see valgrind's --log-file for details.\n"
271                     "\n"
272                     "Please run:\n"
273                     "\tenlightenment %s\n"
274                     "for more options.\n",
275                     argv[i]);
276              exit(0);
277           }
278      }
279
280    if (valgrind_mode || valgrind_tool)
281      {
282         if (!find_valgrind(valgrind_path, sizeof(valgrind_path)))
283           {
284              printf("E - valgrind required but no binary found! Ignoring request.\n");
285              valgrind_mode = 0;
286           }
287      }
288    
289    printf("E - PID=%i, do_precache=%i, valgrind=%d", getpid(), do_precache, valgrind_mode);
290    if (valgrind_mode)
291      {
292         printf(" valgrind-command='%s'", valgrind_path);
293         if (valgrind_log) printf(" valgrind-log-file='%s'", valgrind_log);
294      }
295    putchar('\n');
296    
297    if (do_precache)
298      {
299         void *lib, *func;
300         
301         /* sanity checks - if precache might fail - check here first */
302         lib = dlopen("libeina.so", RTLD_GLOBAL | RTLD_LAZY);
303         if (!lib) dlopen("libeina.so.1", RTLD_GLOBAL | RTLD_LAZY);
304         if (!lib) goto done;
305         func = dlsym(lib, "eina_init");
306         if (!func) goto done;
307
308         lib = dlopen("libecore.so", RTLD_GLOBAL | RTLD_LAZY);
309         if (!lib) dlopen("libecore.so.1", RTLD_GLOBAL | RTLD_LAZY);
310         if (!lib) goto done;
311         func = dlsym(lib, "ecore_init");
312         if (!func) goto done;
313
314         lib = dlopen("libecore_file.so", RTLD_GLOBAL | RTLD_LAZY);
315         if (!lib) dlopen("libecore_file.so.1", RTLD_GLOBAL | RTLD_LAZY);
316         if (!lib) goto done;
317         func = dlsym(lib, "ecore_file_init");
318         if (!func) goto done;
319
320         lib = dlopen("libecore_x.so", RTLD_GLOBAL | RTLD_LAZY);
321         if (!lib) dlopen("libecore_x.so.1", RTLD_GLOBAL | RTLD_LAZY);
322         if (!lib) goto done;
323         func = dlsym(lib, "ecore_x_init");
324         if (!func) goto done;
325
326         lib = dlopen("libevas.so", RTLD_GLOBAL | RTLD_LAZY);
327         if (!lib) dlopen("libevas.so.1", RTLD_GLOBAL | RTLD_LAZY);
328         if (!lib) goto done;
329         func = dlsym(lib, "evas_init");
330         if (!func) goto done;
331
332         lib = dlopen("libedje.so", RTLD_GLOBAL | RTLD_LAZY);
333         if (!lib) dlopen("libedje.so.1", RTLD_GLOBAL | RTLD_LAZY);
334         if (!lib) goto done;
335         func = dlsym(lib, "edje_init");
336         if (!func) goto done;
337
338         lib = dlopen("libeet.so", RTLD_GLOBAL | RTLD_LAZY);
339         if (!lib) dlopen("libeet.so.0", RTLD_GLOBAL | RTLD_LAZY);
340         if (!lib) goto done;
341         func = dlsym(lib, "eet_init");
342         if (!func) goto done;
343
344         /* precache SHOULD work */
345         snprintf(buf, sizeof(buf), "%s/enlightenment/preload/e_precache.so",
346                  eina_prefix_lib_get(pfx));
347         env_set("LD_PRELOAD", buf);
348         printf("E - PRECACHE GOING NOW...\n");
349         fflush(stdout);
350         precache();
351      }
352 done:
353    
354    /* mtrack memory tracker support */
355    p = getenv("HOME");
356    if (p)
357      {
358         FILE *f;
359
360         /* if you have ~/.e-mtrack, then the tracker will be enabled
361          * using the content of this file as the path to the mtrack.so
362          * shared object that is the mtrack preload */
363         snprintf(buf, sizeof(buf), "%s/.e-mtrack", p);
364         f = fopen(buf, "r");
365         if (f)
366           {
367              if (fgets(buf, sizeof(buf), f))
368                {
369                   int len = strlen(buf);
370                   if ((len > 1) && (buf[len - 1] == '\n'))
371                     {
372                        buf[len - 1] = 0;
373                        len--;
374                     }
375                   env_set("LD_PRELOAD", buf);
376                   env_set("MTRACK", "track");
377                   env_set("E_START_MTRACK", "track");
378                   snprintf(buf, sizeof(buf), "%s/.e-mtrack.log", p);
379                   env_set("MTRACK_TRACE_FILE", buf);
380                }
381              fclose(f);
382           }
383      }
384    
385    /* try dbus-launch */
386    snprintf(buf, sizeof(buf), "%s/enlightenment", eina_prefix_bin_get(pfx));
387
388    args = alloca((argc + 2 + VALGRIND_MAX_ARGS) * sizeof(char *));
389    if ((!getenv("DBUS_SESSION_BUS_ADDRESS")) &&
390        (!getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")))
391      {
392         args[0] = "dbus-launch";
393         args[1] = "--exit-with-session";
394         
395         i = 2 + valgrind_append(args + 2, valgrind_mode, valgrind_tool, valgrind_path, valgrind_log);
396         args[i++] = buf;
397         copy_args(args + i, argv + 1, argc - 1);
398         args[i + argc - 1] = NULL;
399         execvp("dbus-launch", args);
400      }
401    
402    /* dbus-launch failed - run e direct */
403    i = valgrind_append(args, valgrind_mode, valgrind_tool, valgrind_path, valgrind_log);
404    args[i++] = buf;
405    copy_args(args + i, argv + 1, argc - 1);
406    args[i + argc - 1] = NULL;
407    execv(args[0], args);
408
409    printf("FAILED TO RUN:\n");
410    printf("  %s\n", buf);
411    perror("execv");
412    return -1;
413 }