7a99a6b1cd49bec305f208c33544f9d7f7db6edf
[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
17 static void env_set(const char *var, const char *val);
18 static int prefix_determine(char *argv0);
19
20 static void
21 env_set(const char *var, const char *val)
22 {
23    if (val)
24      {
25 #ifdef HAVE_SETENV
26         setenv(var, val, 1);
27 #else
28         char *buf;
29
30         buf = alloca(strlen(var) + 1 + strlen(val) + 1);
31         snprintf(buf, sizeof(buf), "%s=%s", var, val);
32         if (getenv(var))
33           putenv(buf);
34         else
35           putenv(strdup(buf));
36 #endif
37      }
38    else
39      {
40 #ifdef HAVE_UNSETENV
41         unsetenv(var);
42 #else
43         if (getenv(var)) putenv(var);
44 #endif
45      }
46 }
47
48 /* local subsystem functions */
49 static int _prefix_fallbacks(void);
50 static int _prefix_try_proc(void);
51 static int _prefix_try_argv(char *argv0);
52
53 /* local subsystem globals */
54 static char *_exe_path = NULL;
55 static char *_prefix_path = NULL;
56
57 /* externally accessible functions */
58 static int
59 prefix_determine(char *argv0)
60 {
61    char *p;
62
63    if (!_prefix_try_proc())
64      {
65         if (!_prefix_try_argv(argv0))
66           {
67              _prefix_fallbacks();
68              return 0;
69           }
70      }
71    /* _exe_path is now a full absolute path TO this exe - figure out rest */
72    /*   if
73     * exe        = /blah/whatever/bin/exe
74     *   then
75     * prefix     = /blah/whatever
76     */
77    p = strrchr(_exe_path, '/');
78    if (p)
79      {
80         p--;
81         while (p >= _exe_path)
82           {
83              if (*p == '/')
84                {
85                   _prefix_path = malloc(p - _exe_path + 1);
86                   if (_prefix_path)
87                     {
88                        strncpy(_prefix_path, _exe_path, p - _exe_path);
89                        _prefix_path[p - _exe_path] = 0;
90                        return 1;
91                     }
92                   else
93                     {
94                        free(_exe_path);
95                        _exe_path = NULL;
96                        _prefix_fallbacks();
97                        return 0;
98                     }
99                }
100              p--;
101           }
102      }
103    free(_exe_path);
104    _exe_path = NULL;
105    _prefix_fallbacks();
106    return 0;
107 }
108
109 static int
110 _prefix_fallbacks(void)
111 {
112    char *p;
113
114    _prefix_path = strdup(PACKAGE_BIN_DIR);
115    p = strrchr(_prefix_path, '/');
116    if (p) *p = 0;
117    printf("WARNING: Enlightenment could not determine its installed prefix\n"
118           "         and is falling back on the compiled in default:\n"
119           "         %s\n", _prefix_path);
120    return 1;
121 }
122
123 static int
124 _prefix_try_proc(void)
125 {
126    FILE *f;
127    char buf[4096];
128    void *func = NULL;
129
130    func = (void *)_prefix_try_proc;
131    f = fopen("/proc/self/maps", "r");
132    if (!f) return 0;
133    while (fgets(buf, sizeof(buf), f))
134      {
135         int len;
136         char *p, mode[5] = "";
137         unsigned long ptr1 = 0, ptr2 = 0;
138
139         len = strlen(buf);
140         if (buf[len - 1] == '\n')
141           {
142              buf[len - 1] = 0;
143              len--;
144           }
145         if (sscanf(buf, "%lx-%lx %4s", &ptr1, &ptr2, mode) == 3)
146           {
147              if (!strcmp(mode, "r-xp"))
148                {
149                   if (((void *)ptr1 <= func) && (func < (void *)ptr2))
150                     {
151                        p = strchr(buf, '/');
152                        if (p)
153                          {
154                             if (len > 10)
155                               {
156                                  if (!strcmp(buf + len - 10, " (deleted)"))
157                                    buf[len - 10] = 0;
158                               }
159                             _exe_path = strdup(p);
160                             fclose(f);
161                             return 1;
162                          }
163                        else
164                          break;
165                     }
166                }
167           }
168      }
169    fclose(f);
170    return 0;
171 }
172
173 static int
174 _prefix_try_argv(char *argv0)
175 {
176    char *path, *p, *cp, *s;
177    int len, lenexe;
178 #ifdef PATH_MAX
179    char buf[PATH_MAX], buf2[PATH_MAX], buf3[PATH_MAX];
180 #else
181    char buf[4096], buf2[4096], buf3[4096];
182 #endif  
183
184    /* 1. is argv0 abs path? */
185    if (argv0[0] == '/')
186      {
187         _exe_path = strdup(argv0);
188         if (access(_exe_path, X_OK) == 0) return 1;
189         free(_exe_path);
190         _exe_path = NULL;
191         return 0;
192      }
193    /* 2. relative path */
194    if (strchr(argv0, '/'))
195      {
196         if (getcwd(buf3, sizeof(buf3)))
197           {
198              snprintf(buf2, sizeof(buf2), "%s/%s", buf3, argv0);
199              if (realpath(buf2, buf))
200                {
201                   _exe_path = strdup(buf);
202                   if (access(_exe_path, X_OK) == 0) return 1;
203                   free(_exe_path);
204                   _exe_path = NULL;
205                }
206           }
207      }
208    /* 3. argv0 no path - look in PATH */
209    path = getenv("PATH");
210    if (!path) return 0;
211    p = path;
212    cp = p;
213    lenexe = strlen(argv0);
214    while ((p = strchr(cp, ':')))
215      {
216         len = p - cp;
217         s = malloc(len + 1 + lenexe + 1);
218         if (s)
219           {
220              strncpy(s, cp, len);
221              s[len] = '/';
222              strcpy(s + len + 1, argv0);
223              if (realpath(s, buf))
224                {
225                   if (access(buf, X_OK) == 0)
226                     {
227                        _exe_path = strdup(buf);
228                        free(s);
229                        return 1;
230                     }
231                }
232              free(s);
233           }
234         cp = p + 1;
235      }
236    len = strlen(cp);
237    s = malloc(len + 1 + lenexe + 1);
238    if (s)
239      {
240         strncpy(s, cp, len);
241         s[len] = '/';
242         strcpy(s + len + 1, argv0);
243         if (realpath(s, buf))
244           {
245              if (access(buf, X_OK) == 0)
246                {
247                   _exe_path = strdup(buf);
248                   free(s);
249                   return 1;
250                }
251           }
252         free(s);
253      }
254    /* 4. big problems. arg[0] != executable - weird execution */
255    return 0;
256 }
257
258 static void
259 precache(void)
260 {
261    FILE *f;
262    char *home;
263    char buf[4096], tbuf[256 * 1024];
264    struct stat st;
265    int l, fd, children = 0, cret;
266
267    home = getenv("HOME");
268    if (home) snprintf(buf, sizeof(buf), "%s/.e-precache", home);
269    else snprintf(buf, sizeof(buf), "/tmp/.e-precache");
270    f = fopen(buf, "r");
271    if (!f) return;
272    unlink(buf);
273    if (fork()) return;
274 //   while (fgets(buf, sizeof(buf), f));
275 //   rewind(f);
276    while (fgets(buf, sizeof(buf), f))
277      {
278         l = strlen(buf);
279         if (l > 0) buf[l - 1] = 0;
280         if (!fork())
281           {
282              if (buf[0] == 's')
283                stat(buf + 2, &st);
284              else if (buf[0] == 'o')
285                {
286                   fd = open(buf + 2, O_RDONLY);
287                   if (fd >= 0)
288                     {
289                        while (read(fd, tbuf, 256 * 1024) > 0);
290                        close(fd);
291                     }
292                }
293              else if (buf[0] == 'd')
294                {
295                   fd = open(buf + 2, O_RDONLY);
296                   if (fd >= 0)
297                     {
298                        while (read(fd, tbuf, 256 * 1024) > 0);
299                        close(fd);
300                     }
301                }
302              exit(0);
303           }
304         children++;
305         if (children > 400)
306           {
307              wait(&cret);
308              children--;
309           }
310      }
311    fclose(f);
312    while (children > 0)
313      {
314         wait(&cret);
315         children--;
316      }
317    exit(0);
318 }
319
320 static int
321 find_valgrind(char *path, size_t path_len)
322 {
323    const char *env = getenv("PATH");
324
325    while (env)
326      {
327         const char *p = strchr(env, ':');
328         ssize_t p_len;
329
330         if (p)
331           p_len = p - env;
332         else
333           p_len = strlen(env);
334
335         if (p_len <= 0)
336           goto next;
337         else if (p_len + sizeof("/valgrind") >= path_len)
338           goto next;
339
340         memcpy(path, env, p_len);
341         memcpy(path + p_len, "/valgrind", sizeof("/valgrind"));
342         if (access(path, X_OK | R_OK) == 0)
343           return 1;
344
345      next:
346         if (p)
347           env = p + 1;
348         else
349           break;
350      }
351    path[0] = '\0';
352    return 0;
353 }
354
355
356 /* maximum number of arguments added above */
357 #define VALGRIND_MAX_ARGS 9
358 /* bitmask with all supported bits set */
359 #define VALGRIND_MODE_ALL 15
360
361 static int
362 valgrind_append(char **dst, int valgrind_mode, int valgrind_tool, char *valgrind_path, const char *valgrind_log)
363 {
364    int i = 0;
365
366    if (valgrind_tool)
367      {
368         dst[i++] = valgrind_path;
369
370         switch (valgrind_tool)
371           {
372            case 1: dst[i++] = "--tool=massif"; break;
373            case 2: dst[i++] = "--tool=callgrind"; break;
374           }
375
376         return i;
377      }
378
379    if (!valgrind_mode)
380      return 0;
381    dst[i++] = valgrind_path;
382    dst[i++] = "--track-origins=yes";
383    dst[i++] = "--malloc-fill=13"; /* invalid pointer, make it crash */
384
385    if (valgrind_log)
386      {
387         static char logparam[PATH_MAX + sizeof("--log-file=")];
388
389         snprintf(logparam, sizeof(logparam), "--log-file=%s", valgrind_log);
390         dst[i++] = logparam;
391      }
392
393    if (valgrind_mode & 2)
394      dst[i++] = "--trace-children=yes";
395
396    if (valgrind_mode & 4)
397      {
398         dst[i++] = "--leak-check=full";
399         dst[i++] = "--leak-resolution=high";
400         dst[i++] = "--track-fds=yes";
401      }
402
403    if (valgrind_mode & 8)
404      dst[i++] = "--show-reachable=yes";
405
406    return i;
407 }
408
409 static void
410 copy_args(char **dst, char **src, size_t count)
411 {
412    for (; count > 0; count--, dst++, src++)
413      *dst = *src;
414 }
415
416 int
417 main(int argc, char **argv)
418 {
419    int i, do_precache = 0, valgrind_mode = 0;
420    int valgrind_tool = 0;
421    char buf[16384], **args, *p;
422    char valgrind_path[PATH_MAX] = "";
423    const char *valgrind_log = NULL;
424
425    prefix_determine(argv[0]);
426
427    env_set("E_START", argv[0]);
428
429    p = getenv("PATH");
430    if (p) snprintf(buf, sizeof(buf), "%s/bin:%s", _prefix_path, p);
431    else snprintf(buf, sizeof(buf), "%s/bin", _prefix_path);
432    env_set("PATH", buf);
433
434    p = getenv("LD_LIBRARY_PATH");
435    if (p) snprintf(buf, sizeof(buf), "%s/lib:%s", _prefix_path, p);
436    else snprintf(buf, sizeof(buf), "%s/lib", _prefix_path);
437    env_set("LD_LIBRARY_PATH", buf);
438
439    for (i = 1; i < argc; i++)
440      {
441         if (!strcmp(argv[i], "-no-precache")) do_precache = 0;
442         else if (!strncmp(argv[i], "-valgrind", sizeof("-valgrind") - 1))
443           {
444              const char *val = argv[i] + sizeof("-valgrind") - 1;
445
446              if (*val == '\0')
447                valgrind_mode = 1;
448              else if (*val == '-')
449                {
450                   val++;
451                   if (!strncmp(val, "log-file=", sizeof("log-file=") - 1))
452                     {
453                        valgrind_log = val + sizeof("log-file=") - 1;
454                        if (*valgrind_log == '\0')
455                          valgrind_log = NULL;
456                     }
457                }
458              else if (*val == '=')
459                {
460                   val++;
461                   if (!strcmp(val, "all"))
462                     valgrind_mode = VALGRIND_MODE_ALL;
463                   else
464                     valgrind_mode = atoi(val);
465                }
466              else
467                printf("Unknown valgrind option: %s\n", argv[i]);
468           }
469         else if (!strcmp(argv[i], "-massif"))
470           {
471              valgrind_tool = 1;
472           }
473         else if (!strcmp(argv[i], "-callgrind"))
474           {
475              valgrind_tool = 2;
476           }
477         else if ((!strcmp(argv[i], "-h")) ||
478                  (!strcmp(argv[i], "-help")) ||
479                  (!strcmp(argv[i], "--help")))
480           {
481              printf
482                ("Options:\n"
483                 "\t-no-precache\n"
484                 "\t\tDisable pre-caching of files\n"
485                 "\t-valgrind[=MODE]\n"
486                 "\t\tRun enlightenment from inside valgrind, mode is OR of:\n"
487                 "\t\t   1 = plain valgrind to catch crashes (default)\n"
488                 "\t\t   2 = trace children (thumbnailer, efm slaves, ...)\n"
489                 "\t\t   4 = check leak\n"
490                 "\t\t   8 = show reachable after processes finish.\n"
491                 "\t\t all = all of above\n"
492                 "\t-massif\n"
493                 "\t\tRun enlightenment from inside massif valgrind tool.\n"
494                 "\t-callgrind\n"
495                 "\t\tRun enlightenment from inside callgrind valgrind tool.\n"
496                 "\t-valgrind-log-file=<FILENAME>\n"
497                 "\t\tSave valgrind log to file, see valgrind's --log-file for details.\n"
498                 "\n"
499                 "Please run:\n"
500                 "\tenlightenment %s\n"
501                 "for more options.\n",
502                 argv[i]);
503              exit(0);
504           }
505      }
506
507    if (valgrind_mode || valgrind_tool)
508      {
509         if (!find_valgrind(valgrind_path, sizeof(valgrind_path)))
510           {
511              printf("E - valgrind required but no binary found! Ignoring request.\n");
512              valgrind_mode = 0;
513           }
514      }
515
516    printf("E - PID=%i, do_precache=%i, valgrind=%d", getpid(), do_precache, valgrind_mode);
517    if (valgrind_mode)
518      {
519         printf(" valgrind-command='%s'", valgrind_path);
520         if (valgrind_log)
521           printf(" valgrind-log-file='%s'", valgrind_log);
522      }
523    putchar('\n');
524
525    if (do_precache)
526      {
527         void *lib, *func;
528
529         /* sanity checks - if precache might fail - check here first */
530         lib = dlopen("libevas.so", RTLD_GLOBAL | RTLD_LAZY);
531         if (!lib) dlopen("libevas.so.1", RTLD_GLOBAL | RTLD_LAZY);
532         if (!lib) goto done;
533         func = dlsym(lib, "evas_init");
534         if (!func) goto done;
535         lib = dlopen("libecore_file.so", RTLD_GLOBAL | RTLD_LAZY);
536         if (!lib) dlopen("libecore_file.so.1", RTLD_GLOBAL | RTLD_LAZY);
537         if (!lib) goto done;
538         func = dlsym(lib, "ecore_file_init");
539         if (!func) goto done;
540         lib = dlopen("libeet.so", RTLD_GLOBAL | RTLD_LAZY);
541         if (!lib) dlopen("libeet.so.0", RTLD_GLOBAL | RTLD_LAZY);
542         if (!lib) goto done;
543         func = dlsym(lib, "eet_init");
544         if (!func) goto done;
545         /* precache SHOULD work */
546         snprintf(buf, sizeof(buf), "%s/lib/enlightenment/preload/e_precache.so", _prefix_path);
547         env_set("LD_PRELOAD", buf);
548         printf("E - PRECACHE GOING NOW...\n");
549         fflush(stdout);
550         precache();
551      }
552    done:
553
554    /* try dbus-launch */
555    snprintf(buf, sizeof(buf), "%s/bin/enlightenment", _prefix_path);
556
557    args = alloca((argc + 2 + VALGRIND_MAX_ARGS) * sizeof(char *));
558    if ((!getenv("DBUS_SESSION_BUS_ADDRESS")) &&
559        (!getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")))
560      {
561         args[0] = "dbus-launch";
562         args[1] = "--exit-with-session";
563
564         i = 2 + valgrind_append(args + 2, valgrind_mode, valgrind_tool, valgrind_path, valgrind_log);
565         args[i++] = buf;
566         copy_args(args + i, argv + 1, argc - 1);
567         args[i + argc - 1] = NULL;
568         execvp("dbus-launch", args);
569      }
570
571    /* dbus-launch failed - run e direct */
572    i = valgrind_append(args, valgrind_mode, valgrind_tool, valgrind_path, valgrind_log);
573    args[i++] = buf;
574    copy_args(args + i, argv + 1, argc - 1);
575    args[i + argc - 1] = NULL;
576    execv(args[0], args);
577
578    printf("FAILED TO RUN:\n");
579    printf("  %s\n", buf);
580    perror("execv");
581    return -1;
582 }