tizen 2.3.1 release
[framework/web/mobile/wrt.git] / src / wrt-launchpad-daemon / src / launchpad.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 /*
18  * simple AUL daemon - launchpad
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <dlfcn.h>
24 #include <X11/Xlib.h>
25 #include <sys/types.h>
26 #include <signal.h>
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <poll.h>
30 #include <sys/prctl.h>
31 #include <sys/time.h>
32 #include <sys/resource.h>
33 #include <malloc.h>
34
35 #include "app_sock.h"
36 #include <aul.h>
37
38 #include "config.h"
39
40 #include "menu_db_util.h"
41 #include "simple_util.h"
42 #include "access_control.h"
43 #include "preload.h"
44 #include "preexec.h"
45 #include "sigchild.h"
46
47 #include <app-checker.h>
48 #include <sqlite3.h>
49
50 #include "process_pool.h"
51 #include "launchpad_util.h"
52
53 #if ENABLE(PRE_LAUNCH)
54 #include "pre_launching.h"
55 #endif
56
57 #define _static_ static inline
58 #define SQLITE_FLUSH_MAX    (1048576)       /* (1024*1024) */
59 #define AUL_POLL_CNT        15
60 #define AUL_PR_NAME         16
61 #define PKG_ID_LENGTH       11
62
63 #define EXEC_DUMMY_EXPIRED 5
64 #define DIFF(a,b) (((a)>(b))?(a)-(b):(b)-(a))
65 #define WRT_CLIENT_PATH "/usr/bin/wrt-client"
66 #define LOWEST_PRIO 20
67 #define DUMMY_NONE 0
68
69 static char *launchpad_cmdline;
70 static int initialized = 0;
71 static int dummy_process_pid    = DUMMY_NONE;
72 static int dummy_process_fd     = -1;
73 static int last_dummy_exec_time = 0;
74 static int process_pool_disable = 0;
75
76 _static_ int __prepare_exec(const char *pkg_name,
77                             const char *app_path, app_info_from_db * menu_info,
78                             bundle * kb);
79 _static_ int __fake_launch_app(int cmd, int pid, bundle * kb);
80 _static_ char **__create_argc_argv(bundle * kb, int *margc);
81 _static_ void __real_launch(const char *app_path, bundle * kb);
82 _static_ int __dummy_launch(int dummy_client_fd, app_pkt_t* pkt);
83 _static_ int __send_to_sigkill(int pid);
84 _static_ int __term_app(int pid);
85 _static_ int __real_send(int clifd, int ret);
86 _static_ void __send_result_to_caller(int clifd, int ret);
87 _static_ void __launchpad_exec_dummy(int main_fd, int pool_fd, int client_fd);
88 _static_ void __launchpad_main_loop(int main_fd, int pool_fd);
89 _static_ int __launchpad_pre_init(int argc, char **argv);
90 _static_ int __launchpad_post_init();
91
92 extern ail_error_e ail_db_close(void);
93
94 _static_ int __prepare_exec(const char *pkg_name,
95                             const char *app_path, app_info_from_db * menu_info,
96                             bundle * kb)
97 {
98     const char* file_name;
99     char process_name[AUL_PR_NAME];
100
101     /* Set new session ID & new process group ID*/
102     /* In linux, child can set new session ID without check permission */
103     /* TODO : should be add to check permission in the kernel*/
104     setsid();
105
106     __preexec_run(menu_info->pkg_type, pkg_name, app_path);
107
108     /* SET PRIVILEGES*/
109     char pkg_id[PKG_ID_LENGTH];
110     memset(pkg_id, '\0', PKG_ID_LENGTH);
111     snprintf(pkg_id, PKG_ID_LENGTH, "%s", pkg_name);
112
113     /* SET PR_SET_KEEPCAPS */
114     if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
115         WrtLogE("prctl(PR_SET_KEEPCAPS) failed.");
116     }
117
118     if (AccessControl::setPriviledge(pkg_id, menu_info->pkg_type, app_path) < 0) {
119         WrtLogD("fail to set privileges - check your package's credential\n");
120         return -1;
121     }
122
123     __set_inherit_bit_for_CAP_MAC_ADMIN();
124
125     /* SET DUMPABLE - for coredump*/
126     prctl(PR_SET_DUMPABLE, 1);
127
128     /* SET PROCESS NAME*/
129     if (app_path == NULL) {
130         WrtLogD("app_path should not be NULL - check menu db");
131         return -1;
132     }
133     file_name = strrchr(app_path, '/');
134     if (file_name == NULL) {
135         WrtLogD("can't locate file name to execute");
136         return -1;
137     }
138     memset(process_name, '\0', AUL_PR_NAME);
139     snprintf(process_name, AUL_PR_NAME, "%s", file_name + 1);
140     prctl(PR_SET_NAME, process_name);
141
142     /* SET ENVIROMENT*/
143     __set_env(menu_info, kb);
144
145     return 0;
146 }
147
148 _static_ int __fake_launch_app(int cmd, int pid, bundle * kb)
149 {
150     int datalen;
151     int ret;
152     bundle_raw *kb_data;
153
154     bundle_encode(kb, &kb_data, &datalen);
155     if ((ret = __app_send_raw(pid, cmd, kb_data, datalen)) < 0) {
156         WrtLogE("error request fake launch - error code = %d", ret);
157     }
158     free(kb_data);
159     return ret;
160 }
161
162 _static_ void __real_launch(const char *app_path, bundle * kb)
163 {
164     int app_argc;
165     char **app_argv;
166     int i;
167
168     app_argv = __create_argc_argv(kb, &app_argc);
169
170 #ifndef NATIVE_LAUNCHPAD
171     if (__change_cmdline((char *)app_path) < 0) {
172         WrtLogE("change cmdline fail");
173         return;
174     }
175
176     app_argv[0] = g_argv[0];
177 #else
178     app_argv[0] = strdup(app_path);
179 #endif
180
181     for (i = 0; i < app_argc; i++) {
182         WrtLogD("input argument %d : %s##", i, app_argv[i]);
183     }
184
185     WrtLogE("lock up test log(no error) : setup argument done");
186
187     /* Temporary log: launch time checking */
188     LOG(LOG_DEBUG, "LAUNCH", "[%s:Platform:launchpad:done]", app_path);
189
190     __preload_exec(app_argc, app_argv);
191 }
192
193 _static_ int __dummy_launch(int dummy_client_fd, app_pkt_t* pkt)
194 {
195     return __send_pkt_raw_data(dummy_client_fd, pkt);
196 }
197
198 _static_ int __send_to_sigkill(int pid)
199 {
200     int pgid;
201
202     pgid = getpgid(pid);
203     if (pgid <= 1) {
204         return -1;
205     }
206
207     if (killpg(pgid, SIGKILL) < 0) {
208         return -1;
209     }
210
211     return 0;
212 }
213
214 _static_ int __term_app(int pid)
215 {
216     int dummy;
217     if (__app_send_raw
218             (pid, APP_TERM_BY_PID, (unsigned char *)&dummy, sizeof(int)) < 0)
219     {
220         WrtLogD("terminate packet send error - use SIGKILL");
221         if (__send_to_sigkill(pid) < 0) {
222             WrtLogE("fail to killing - %d\n", pid);
223             return -1;
224         }
225     }
226     WrtLogD("term done\n");
227     return 0;
228 }
229
230 static int __get_caller_pid(bundle *kb)
231 {
232     const char *pid_str;
233     int pid;
234
235     pid_str = bundle_get_val(kb, AUL_K_ORG_CALLER_PID);
236     if (pid_str) {
237         goto end;
238     }
239
240     pid_str = bundle_get_val(kb, AUL_K_CALLER_PID);
241     if (pid_str == NULL) {
242         return -1;
243     }
244
245 end:
246     pid = atoi(pid_str);
247     if (pid <= 1) {
248         return -1;
249     }
250
251     return pid;
252 }
253
254 _static_ int __foward_cmd(int cmd, bundle *kb, int cr_pid)
255 {
256     int pid;
257     char tmp_pid[MAX_PID_STR_BUFSZ];
258     int datalen;
259     bundle_raw *kb_data;
260     int res;
261
262     if ((pid = __get_caller_pid(kb)) < 0) {
263         return AUL_R_ERROR;
264     }
265
266     snprintf(tmp_pid, MAX_PID_STR_BUFSZ, "%d", cr_pid);
267
268     bundle_add(kb, AUL_K_CALLEE_PID, tmp_pid);
269
270     bundle_encode(kb, &kb_data, &datalen);
271     if ((res = __app_send_raw(pid, cmd, kb_data, datalen)) < 0) {
272         res = AUL_R_ERROR;
273     }
274
275     free(kb_data);
276
277     return res;
278 }
279
280 _static_ int __real_send(int clifd, int ret)
281 {
282     if (send(clifd, &ret, sizeof(int), MSG_NOSIGNAL) < 0) {
283         if (errno == EPIPE) {
284             WrtLogE("send failed due to EPIPE.\n");
285             close(clifd);
286             return -1;
287         }
288         WrtLogE("send fail to client");
289     }
290
291     close(clifd);
292     return 0;
293 }
294
295 _static_ void __send_result_to_caller(int clifd, int ret)
296 {
297     char *cmdline;
298     int wait_count;
299     int cmdline_changed = 0;
300     int cmdline_exist = 0;
301     int r;
302
303     if (clifd == -1) {
304         return;
305     }
306
307     if (ret <= 1) {
308         __real_send(clifd, ret);
309         return;
310     }
311     /* check normally was launched?*/
312     wait_count = 1;
313     do {
314         cmdline = __proc_get_cmdline_bypid(ret);
315         if (cmdline == NULL) {
316             WrtLogE("error founded when being launched with %d", ret);
317         } else if (strcmp(cmdline, launchpad_cmdline)) {
318             free(cmdline);
319             cmdline_changed = 1;
320             break;
321         } else {
322             cmdline_exist = 1;
323             free(cmdline);
324         }
325
326         WrtLogD("-- now wait to change cmdline --");
327         struct timespec duration = { 0, 50 * 1000 * 1000 };
328         nanosleep(&duration, NULL);     /* 50ms sleep*/
329         wait_count++;
330     } while (wait_count <= 20);         /* max 50*20ms will be sleep*/
331
332     if ((!cmdline_exist) && (!cmdline_changed)) {
333         __real_send(clifd, -1);         /* abnormally launched*/
334         return;
335     }
336
337     if (!cmdline_changed) {
338         WrtLogE("process launched, but cmdline not changed");
339     }
340
341     if (__real_send(clifd, ret) < 0) {
342         r = kill(ret, SIGKILL);
343         if (r == -1) {
344             WrtLogE("send SIGKILL: %s", strerror(errno));
345         }
346     }
347
348     return;
349 }
350
351 _static_ void __launchpad_exec_dummy(int main_fd, int pool_fd, int client_fd)
352 {
353     int pid;
354
355     last_dummy_exec_time = time(NULL);
356
357     pid = fork();
358
359     if (pid == 0) // child
360     {
361         setpriority(PRIO_PROCESS, 0, LOWEST_PRIO);
362         WrtLogD("Launch dummy process...");
363
364         //temp - this requires some optimization.
365         sleep(1);
366         WrtLogD("sleeping 1 sec...");
367
368         /* Set new session ID & new process group ID*/
369         /* In linux, child can set new session ID without check permission */
370         /* TODO : should be add to check permission in the kernel*/
371         setsid();
372
373         if (main_fd != -1)
374         {
375             close(main_fd);
376         }
377
378         if (pool_fd != -1)
379         {
380             close(pool_fd);
381         }
382
383         if (client_fd != -1)
384         {
385             close(client_fd);
386         }
387
388         __signal_unset_sigchld();
389         __signal_fini();
390
391         /* SET PR_SET_KEEPCAPS */
392         if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
393             WrtLogE("prctl(PR_SET_KEEPCAPS) failed.");
394         }
395
396         /* SET DUMPABLE - for coredump*/
397         prctl(PR_SET_DUMPABLE, 1);
398
399         {
400             void *handle = NULL;
401             int (*dl_main) (int, char **);
402
403             handle = dlopen(WRT_CLIENT_PATH, RTLD_NOW | RTLD_GLOBAL);
404
405             if (handle == NULL)
406             {
407                 WrtLogE("dlopen failed.");
408                 exit(-1);
409             }
410
411             dl_main = reinterpret_cast<int (*)(int, char **)>(dlsym(handle, "main"));
412
413             sprintf(g_argv[1], "%s", "-d");
414
415             if (dl_main != NULL)
416             {
417                 dl_main(g_argc, g_argv);
418             }
419             else
420             {
421                 WrtLogE("dlsym not founded. bad preloaded app - check fpie pie");
422             }
423
424             exit(0);
425         }
426     }
427 }
428
429 _static_ void __launchpad_main_loop(int main_fd, int pool_fd)
430 {
431     bundle *kb = NULL;
432     app_pkt_t *pkt = NULL;
433     app_info_from_db *menu_info = NULL;
434
435     const char *pkg_name = NULL;
436     const char *app_path = NULL;
437     int pid = -1;
438     int clifd = -1;
439     struct ucred cr;
440     int is_real_launch = 0;
441
442     char sock_path[UNIX_PATH_MAX] = { 0, };
443
444     pkt = __app_recv_raw(main_fd, &clifd, &cr);
445     if (!pkt) {
446         WrtLogD("packet is NULL");
447         goto end;
448     }
449
450     kb = bundle_decode(pkt->data, pkt->len);
451     if (!kb) {
452         WrtLogD("bundle decode error");
453         goto end;
454     }
455
456     pkg_name = bundle_get_val(kb, AUL_K_PKG_NAME);
457     SECURE_LOGD("pkg name : %s\n", pkg_name);
458
459     menu_info = _get_app_info_from_bundle_by_pkgname(pkg_name, kb);
460     if (menu_info == NULL) {
461         WrtLogD("such pkg no found");
462         goto end;
463     }
464
465     app_path = _get_app_path(menu_info);
466     if (app_path == NULL) {
467         WrtLogE("app_path is NULL");
468         goto end;
469     }
470     if (app_path[0] != '/') {
471         WrtLogD("app_path is not absolute path");
472         goto end;
473     }
474
475     __modify_bundle(kb, cr.pid, menu_info, pkt->cmd);
476     pkg_name = _get_pkgname(menu_info);
477
478     if (dummy_process_pid != DUMMY_NONE)
479     {
480         snprintf(sock_path, UNIX_PATH_MAX, "%s/%d", AUL_SOCK_PREFIX, dummy_process_pid);
481         unlink(sock_path);
482
483         __dummy_launch(dummy_process_fd, pkt);
484
485         pid = dummy_process_pid;
486         is_real_launch = 1;
487         close(dummy_process_fd);
488
489         dummy_process_pid = DUMMY_NONE;
490         dummy_process_fd  = -1;
491
492         /* Temporary log: launch time checking */
493         LOG(LOG_DEBUG, "LAUNCH", "[%s:Platform:launchpad:done]", app_path);
494
495         __launchpad_exec_dummy(main_fd, pool_fd, clifd);
496
497         WrtLogD("==> dummy launch pid : %d %s\n", pid, app_path);
498     }
499     else
500     {
501         pid = fork();
502
503         if (pid == 0)
504         {
505             WrtLogE("lock up test log(no error) : fork done");
506
507             close(clifd);
508             close(main_fd);
509             __signal_unset_sigchld();
510             __signal_fini();
511
512             snprintf(sock_path, UNIX_PATH_MAX, "%s/%d", AUL_SOCK_PREFIX, getpid());
513             unlink(sock_path);
514
515             WrtLogE("lock up test log(no error) : prepare exec - first done");
516
517             if (__prepare_exec(pkg_name, app_path,
518                                menu_info, kb) < 0)
519             {
520                 SECURE_LOGE("preparing work fail to launch - "
521                    "can not launch %s\n", pkg_name);
522                 exit(-1);
523             }
524
525             WrtLogE("lock up test log(no error) : prepare exec - second done");
526
527             __real_launch(app_path, kb);
528
529             exit(-1);
530         }
531         WrtLogD("==> real launch pid : %d %s\n", pid, app_path);
532         is_real_launch = 1;
533     }
534
535 end:
536     __send_result_to_caller(clifd, pid);
537
538     if (pid > 0) {
539         if (is_real_launch) {
540             /*TODO: retry*/
541             __signal_block_sigchld();
542             __send_app_launch_signal(pid);
543
544 #if ENABLE(PRE_LAUNCH)
545             if (is_pre_launching_appid(pkg_name)) {
546                 add_to_pre_launching_info_list(pkg_name, pid);
547             }
548 #endif
549             __signal_unblock_sigchld();
550         }
551     }
552
553     if (menu_info != NULL) {
554         _free_app_info_from_db(menu_info);
555     }
556
557     if (kb != NULL) {
558         bundle_free(kb);
559     }
560     if (pkt != NULL) {
561         free(pkt);
562     }
563
564     /* Active Flusing for Daemon */
565     if (initialized > AUL_POLL_CNT) {
566         sqlite3_release_memory(SQLITE_FLUSH_MAX);
567         malloc_trim(0);
568         initialized = 1;
569     }
570 }
571
572 _static_ int __launchpad_pre_init(int argc, char **argv)
573 {
574     int fd;
575
576     /* signal init*/
577     __signal_init();
578
579     /* get my(launchpad) command line*/
580     launchpad_cmdline = __proc_get_cmdline_bypid(getpid());
581     if (launchpad_cmdline == NULL) {
582         WrtLogE("launchpad cmdline fail to get");
583         return -1;
584     }
585     WrtLogD("launchpad cmdline = %s", launchpad_cmdline);
586
587     /* create launchpad sock        */
588     fd = __create_server_sock(WRT_LAUNCHPAD_PID);
589     if (fd < 0) {
590         WrtLogE("server sock error");
591         return -1;
592     }
593
594     __preload_init(argc, argv);
595
596     __preload_init_for_wrt();
597
598     __preexec_init(argc, argv);
599
600     return fd;
601 }
602
603 _static_ int __launchpad_post_init()
604 {
605     /* Setting this as a global variable to keep track
606      * of launchpad poll cnt */
607     /* static int initialized = 0;*/
608
609     if (initialized) {
610         initialized++;
611         return 0;
612     }
613
614     if (__signal_set_sigchld() < 0) {
615         return -1;
616     }
617
618     initialized++;
619
620     return 0;
621 }
622
623 int main(int argc, char **argv)
624 {
625     enum {
626         LAUNCH_PAD = 0,
627         POOL_SERVER,
628         DUMMY_PROCESS,
629         POLLFD_MAX
630     };
631
632     int main_fd = -1, pool_fd = -1;
633     struct pollfd pfds[POLLFD_MAX];
634
635     memset(pfds, 0x00, sizeof(pfds));
636
637 #if ENABLE(PRE_LAUNCH)
638     pre_launch_webapps();
639 #endif
640
641     // process pool feature disable
642     if (getenv("WRT_PROCESS_POOL_DISABLE"))
643     {
644         process_pool_disable = 1;
645     }
646
647     /* init without concerning X & EFL*/
648     main_fd = __launchpad_pre_init(argc, argv);
649
650     if (main_fd < 0)
651     {
652         WrtLogE("launchpad pre init failed");
653         goto exit_main;
654     }
655
656     pfds[LAUNCH_PAD].fd      = main_fd;
657     pfds[LAUNCH_PAD].events  = POLLIN;
658     pfds[LAUNCH_PAD].revents = 0;
659
660     pool_fd = __create_process_pool_server();
661
662     if (pool_fd == -1)
663     {
664         WrtLogE("Error creationg pool server!");
665         goto exit_main;
666     }
667
668     pfds[POOL_SERVER].fd      = pool_fd;
669     pfds[POOL_SERVER].events  = POLLIN;
670     pfds[POOL_SERVER].revents = 0;
671
672
673     // This is temporary code to delay creating dummy process.
674     // It will make that a dummy_process is created on first webapp launching.
675     last_dummy_exec_time = time(NULL);
676
677     while (1)
678     {
679         if (dummy_process_pid == DUMMY_NONE)
680         {
681             pfds[DUMMY_PROCESS].fd      = -1;
682             pfds[DUMMY_PROCESS].events  = 0;
683             pfds[DUMMY_PROCESS].revents = 0;
684
685             if ( !process_pool_disable &&
686                  DIFF(last_dummy_exec_time, time(NULL)) > EXEC_DUMMY_EXPIRED)
687             {
688                 __launchpad_exec_dummy(main_fd, pool_fd, -1);
689             }
690         }
691
692         if (poll(pfds, POLLFD_MAX, -1) < 0)
693         {
694             continue;
695         }
696
697         WrtLogD("pfds[LAUNCH_PAD].revents    : 0x%x", pfds[LAUNCH_PAD].revents) ;
698         WrtLogD("pfds[POOL_SERVER].revents   : 0x%x", pfds[POOL_SERVER].revents) ;
699         WrtLogD("pfds[DUMMY_PROCESS].revents : 0x%x", pfds[DUMMY_PROCESS].revents) ;
700
701         /* init with concerning X & EFL (because of booting
702         * sequence problem)*/
703         if (__launchpad_post_init() < 0)
704         {
705             WrtLogE("launcpad post init failed");
706             goto exit_main;
707         }
708
709         if ((pfds[LAUNCH_PAD].revents & POLLIN) != 0)
710         {
711             WrtLogD("pfds[LAUNCH_PAD].revents & POLLIN");
712             __launchpad_main_loop(pfds[LAUNCH_PAD].fd, pfds[POOL_SERVER].fd);
713         }
714
715         if ((pfds[POOL_SERVER].revents & POLLIN) != 0)
716         {
717             int server_fd, client_fd, client_pid;
718
719             server_fd = pfds[POOL_SERVER].fd;
720
721             WrtLogD("pfds[POOL_SERVER].revents & POLLIN");
722
723             if (dummy_process_pid == DUMMY_NONE)
724             {
725                 __accept_dummy_process(server_fd, &client_fd, &client_pid);
726
727                 dummy_process_pid = client_pid;
728                 dummy_process_fd  = client_fd;
729
730                 pfds[DUMMY_PROCESS].fd     = dummy_process_fd;
731                 pfds[DUMMY_PROCESS].events = POLLIN|POLLHUP;
732                 pfds[DUMMY_PROCESS].revents = 0;
733
734                 WrtLogD("Dummy process was connected! (pid:%d)", dummy_process_pid);
735             }
736             else
737             {
738                 __refuse_dummy_process(server_fd);
739
740                 WrtLogE("Refused dummy process connection!");
741             }
742         }
743
744         if ((pfds[DUMMY_PROCESS].revents & (POLLHUP|POLLNVAL)) != 0)
745         {
746             WrtLogD("pfds[DUMMY_PROCESS].revents & (POLLHUP|POLLNVAL) (pid:%d)", dummy_process_pid);
747
748             if (pfds[DUMMY_PROCESS].fd > -1)
749             {
750                 close(pfds[DUMMY_PROCESS].fd);
751             }
752
753             dummy_process_pid = DUMMY_NONE;
754             dummy_process_fd = -1;
755
756             pfds[DUMMY_PROCESS].fd      = -1;
757             pfds[DUMMY_PROCESS].events  = 0;
758             pfds[DUMMY_PROCESS].revents = 0;
759         }
760     }
761
762     return 0;
763
764     exit_main:
765     if (main_fd != -1)
766     {
767         close(main_fd);
768     }
769
770     if (pool_fd != -1)
771     {
772         close(pool_fd);
773     }
774
775     return -1;
776 }
777