applauncherd
invoker.c
Go to the documentation of this file.
1/***************************************************************************
2**
3** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** Copyright (c) 2012 - 2021 Jolla Ltd.
5** Copyright (c) 2021 Open Mobile Platform LLC.
6** All rights reserved.
7** Contact: Nokia Corporation (directui@nokia.com)
8**
9** This file is part of applauncherd
10**
11** If you have questions regarding the use of this file, please contact
12** Nokia at directui@nokia.com.
13**
14** This library is free software; you can redistribute it and/or
15** modify it under the terms of the GNU Lesser General Public
16** License version 2.1 as published by the Free Software Foundation
17** and appearing in the file LICENSE.LGPL included in the packaging
18** of this file.
19**
20****************************************************************************/
21
22#define _GNU_SOURCE
23
24#include <stdio.h>
25#include <stdint.h>
26#include <stdbool.h>
27#include <stdlib.h>
28#include <string.h>
29#include <ctype.h>
30#include <signal.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <sys/uio.h>
34#include <sys/time.h>
35#include <sys/resource.h>
36#include <sys/stat.h>
37#include <unistd.h>
38#include <errno.h>
39#include <sys/wait.h>
40#include <limits.h>
41#include <getopt.h>
42#include <fcntl.h>
43#include <time.h>
44#include <poll.h>
45#include <dbus/dbus.h>
46#include <libgen.h>
47
48#include "report.h"
49#include "protocol.h"
50#include "invokelib.h"
51#include "search.h"
52#include "sailjail.h"
53
54#define BOOSTER_SESSION "silica-session"
55#define BOOSTER_GENERIC "generic"
56
57/* Placeholder value used for regular boosters (that are not
58 * sandboxed application boosters).
59 */
60#define UNDEFINED_APPLICATION "default"
61
62/* Setting VERBOSE_SIGNALS to non-zero value logs receiving of
63 * async-signals - which is useful only when actively debugging
64 * booster / invoker interoperation.
65 */
66#define VERBOSE_SIGNALS 0
67
68// Utility functions
69static char *strip(char *str)
70{
71 if (str) {
72 char *dst = str;
73 char *src = str;
74 while (*src && isspace(*src))
75 ++src;
76 for (;;) {
77 while (*src && !isspace(*src))
78 *dst++ = *src++;
79 while (*src && isspace(*src))
80 ++src;
81 if (!*src)
82 break;
83 *dst++ = ' ';
84 }
85 *dst = 0;
86 }
87 return str;
88}
89
90static char *slice(const char *pos, const char **ppos, const char *delim)
91{
92 char *tok = NULL;
93 if (pos) {
94 const char *beg = pos;
95 while (*pos && !strchr(delim, *pos))
96 ++pos;
97 tok = strndup(beg, pos - beg);
98 if (*pos)
99 ++pos;
100 }
101 if (ppos)
102 *ppos = pos;
103 return tok;
104}
105
106static char **split(const char *str, const char *delim)
107{
108 char **arr = NULL;
109 if (str) {
110 /* Upper limit for token count is number of delimeters + one */
111 int n = 1;
112 for (const char *pos = str; *pos; ++pos)
113 if (strchr(delim, *pos))
114 ++n;
115
116 /* Upper limit for required array size is token count + one */
117 arr = calloc(n + 1, sizeof *arr);
118
119 /* Fill in the array */
120 int i = 0;
121 while (*str) {
122 char *tok = slice(str, &str, delim);
123 if (*strip(tok))
124 arr[i++] = tok;
125 else
126 free(tok);
127 }
128 arr[i] = NULL;
129 }
130 return arr;
131}
132
133// Delay before exit.
134static const unsigned int EXIT_DELAY = 0;
135static const unsigned int MIN_EXIT_DELAY = 1;
136static const unsigned int MAX_EXIT_DELAY = 86400;
137
138// Delay before a new booster is started. This will
139// be sent to the launcher daemon.
140static const unsigned int RESPAWN_DELAY = 1;
141static const unsigned int MIN_RESPAWN_DELAY = 0;
142static const unsigned int MAX_RESPAWN_DELAY = 10;
143
144static const unsigned char EXIT_STATUS_APPLICATION_NOT_FOUND = 0x7f;
145
146// Environment
147extern char ** environ;
148
149// pid of the invoked process
150static pid_t g_invoked_pid = -1;
151
152static void sigs_restore(void);
153static void sigs_init(void);
154
156static int g_signal_pipe[2] = { -1, -1 };
157
158// Forwards Unix signals from invoker to the invoked process
159static void sig_forwarder(int sig)
160{
161#if VERBOSE_SIGNALS
162 static const char m[] = "*** signal\n";
163 if (write(STDERR_FILENO, m, sizeof m - 1) == -1) {
164 // dontcare
165 }
166#endif
167
168 // Write signal number to the self-pipe
169 char signal_id = (char) sig;
170 if (g_signal_pipe[1] == -1 || write(g_signal_pipe[1], &signal_id, 1) != 1) {
171 const char m[] = "*** signal pipe write failure, terminating\n";
172 if (write(STDERR_FILENO, m, sizeof m - 1) == -1) {
173 // dontcare
174 }
175 _exit(EXIT_FAILURE);
176 }
177}
178
179// Sets signal actions for Unix signals
180static void sigs_set(struct sigaction *sig)
181{
182 sigaction(SIGINT, sig, NULL);
183 sigaction(SIGTERM, sig, NULL);
184}
185
186// Sets up the signal forwarder
187static void sigs_init(void)
188{
189 struct sigaction sig;
190
191 memset(&sig, 0, sizeof(sig));
192 sig.sa_flags = SA_RESTART;
193 sig.sa_handler = sig_forwarder;
194
195 sigs_set(&sig);
196}
197
198// Sets up the default signal handler
199static void sigs_restore(void)
200{
201 struct sigaction sig;
202
203 memset(&sig, 0, sizeof(sig));
204 sig.sa_flags = SA_RESTART;
205 sig.sa_handler = SIG_DFL;
206
207 sigs_set(&sig);
208}
209
210static unsigned timestamp(void)
211{
212 struct timespec ts = { 0, 0 };
213 if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1 &&
214 clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
215 error("can't obtain a monotonic timestamp\n");
216 exit(EXIT_FAILURE);
217 }
218
219 /* NOTE: caller must assume overflow to happen i.e.
220 * the return values themselves mean nothing, only
221 * differences between return values should be used.
222 */
223 return ((unsigned)(ts.tv_sec * 1000u) +
224 (unsigned)(ts.tv_nsec / (1000 * 1000u)));
225}
226
227static bool shutdown_socket(int socket_fd)
228{
229 bool disconnected = false;
230
231 /* Close transmit end from our side, then wait
232 * for peer to receive EOF and close the receive
233 * end too.
234 */
235
236 info("trying to disconnect booster socket...\n");
237
238 if (shutdown(socket_fd, SHUT_WR) == -1) {
239 warning("socket shutdown failed: %m\n");
240 goto EXIT;
241 }
242
243 unsigned started = timestamp();
244 unsigned timeout = 5000;
245 for (;;) {
246 unsigned elapsed = timestamp() - started;
247 if (elapsed >= timeout)
248 break;
249
250 struct pollfd pfd = {
251 .fd = socket_fd,
252 .events = POLLIN,
253 .revents = 0,
254 };
255
256 info("waiting for booster socket input...\n");
257 int rc = poll(&pfd, 1, (int)(timeout - elapsed));
258
259 if (rc == 0)
260 break;
261
262 if (rc == -1) {
263 if (errno == EINTR || errno == EAGAIN)
264 continue;
265 warning("socket poll failed: %m\n");
266 goto EXIT;
267 }
268
269 char buf[256];
270 rc = recv(socket_fd, buf, sizeof buf, MSG_DONTWAIT);
271 if (rc == 0) {
272 /* EOF -> peer closed the socket */
273 disconnected = true;
274 goto EXIT;
275 }
276
277 if (rc == -1) {
278 warning("socket read failed: %m\n");
279 goto EXIT;
280 }
281 }
282 warning("socket poll timeout\n");
283
284EXIT:
285 if (disconnected)
286 info("booster socket was succesfully disconnected\n");
287 else
288 warning("could not disconnect booster socket\n");
289
290 return disconnected;
291}
292
293static void kill_application(pid_t pid)
294{
295 if (pid == -1) {
296 warning("application pid is not known, can't kill it");
297 goto EXIT;
298 }
299
300 warning("sending SIGTERM to application (pid=%d)", (int)pid);
301
302 if (kill(pid, SIGTERM) == -1)
303 goto FAIL;
304
305 for (int i = 0; i < 10; ++i) {
306 sleep(1);
307 if (kill(pid, 0) == -1)
308 goto FAIL;
309 }
310
311 warning("sending SIGKILL to application (pid=%d)", (int)pid);
312
313 if (kill(pid, SIGKILL) == -1)
314 goto FAIL;
315
316 for (int i = 0; i < 10; ++i) {
317 sleep(1);
318 if (kill(pid, 0) == -1)
319 goto FAIL;
320 }
321
322 warning("application (pid=%d) did not exit", (int)pid);
323 goto EXIT;
324
325FAIL:
326 if (errno == ESRCH)
327 info("application (pid=%d) has exited", (int)pid);
328 else
329 warning("application (pid=%d) kill failed: %m", (int)pid);
330
331EXIT:
332 return;
333}
334
335// Receive ACK
336static bool invoke_recv_ack(int fd)
337{
338 uint32_t action;
339
340 invoke_recv_msg(fd, &action);
341
342 if (action != INVOKER_MSG_ACK) {
343 die(1, "Received wrong ack (%08x)\n", action);
344 }
345
346 return true;
347}
348
349// Inits a socket connection for the given application type
350static int invoker_init(const char *app_type, const char *app_name)
351{
352 info("try type=%s app=%s ...", app_type, app_name);
353
354 bool connected = false;
355 int fd = -1;
356
357 /* Sanity check args */
358 if (!app_type || strchr(app_type, '/'))
359 goto EXIT;
360 if (app_name && strchr(app_name, '/'))
361 goto EXIT;
362
363 const char *runtimeDir = getenv("XDG_RUNTIME_DIR");
364 if (!runtimeDir || !*runtimeDir) {
365 error("XDG_RUNTIME_DIR is not defined.\n");
366 goto EXIT;
367 }
368
369 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
370 error("Failed to create socket: %m\n");
371 goto EXIT;
372 }
373
374 struct sockaddr_un sun = {
375 .sun_family = AF_UNIX,
376 };
377 int maxSize = sizeof(sun.sun_path);
378 int length;
379
380 if (app_name)
381 length = snprintf(sun.sun_path, maxSize, "%s/mapplauncherd/_%s/%s/socket",
382 runtimeDir, app_name, app_type);
383 else
384 length = snprintf(sun.sun_path, maxSize, "%s/mapplauncherd/%s",
385 runtimeDir, app_type);
386
387 if (length <= 0 || length >= maxSize) {
388 if (app_name)
389 error("Invalid booster type: %s / application: %s\n",
390 app_type, app_name);
391 else
392 error("Invalid booster type: %s\n", app_type);
393 goto EXIT;
394 }
395
396 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
397 if (errno != ENOENT)
398 warning("connect(\"%s\") failed: %m\n", sun.sun_path);
399 goto EXIT;
400 }
401
402 info("connected to: %s\n", sun.sun_path);
403 connected = true;
404
405EXIT:
406 if (!connected && fd != -1)
407 close(fd), fd = -1;
408 return fd;
409}
410
411// Receives pid of the invoked process.
412// Invoker doesn't know it, because the launcher daemon
413// is the one who forks.
414static uint32_t invoker_recv_pid(int fd)
415{
416 // Receive action.
417 uint32_t action;
418 invoke_recv_msg(fd, &action);
419 if (action != INVOKER_MSG_PID)
420 die(1, "Received a bad message id (%08x)\n", action);
421
422 // Receive pid.
423 uint32_t pid = 0;
424 invoke_recv_msg(fd, &pid);
425 if (pid == 0)
426 die(1, "Received a zero pid \n");
427
428 return pid;
429}
430
431// Receives exit status of the invoked process
432static bool invoker_recv_exit(int fd, int* status)
433{
434 uint32_t action;
435
436 // Receive action.
437 bool res = invoke_recv_msg(fd, &action);
438
439 if (!res || (action != INVOKER_MSG_EXIT)) {
440 // Boosted application process was killed somehow.
441 // Let's give applauncherd process some time to cope
442 // with this situation.
443 sleep(2);
444
445 // If nothing happend, return
446 return false;
447 }
448
449 // Receive exit status.
450 res = invoke_recv_msg(fd, (uint32_t*) status);
451 return res;
452}
453
454// Sends magic number / protocol version
455static void invoker_send_magic(int fd, uint32_t options)
456{
457 // Send magic.
458 invoke_send_msg(fd, INVOKER_MSG_MAGIC | INVOKER_MSG_MAGIC_VERSION | options);
459}
460
461// Sends the process name to be invoked.
462static void invoker_send_name(int fd, const char *name)
463{
464 invoke_send_msg(fd, INVOKER_MSG_NAME);
465 invoke_send_str(fd, name);
466}
467
468static void invoker_send_exec(int fd, char *exec)
469{
470 invoke_send_msg(fd, INVOKER_MSG_EXEC);
471 invoke_send_str(fd, exec);
472}
473
474static void invoker_send_args(int fd, int argc, char **argv)
475{
476 int i;
477
478 invoke_send_msg(fd, INVOKER_MSG_ARGS);
479 invoke_send_msg(fd, argc);
480 for (i = 0; i < argc; i++) {
481 info("param %d %s \n", i, argv[i]);
482 invoke_send_str(fd, argv[i]);
483 }
484}
485
486static void invoker_send_prio(int fd, int prio)
487{
488 invoke_send_msg(fd, INVOKER_MSG_PRIO);
489 invoke_send_msg(fd, prio);
490}
491
492// Sends booster respawn delay
493static void invoker_send_delay(int fd, int delay)
494{
495 invoke_send_msg(fd, INVOKER_MSG_DELAY);
496 invoke_send_msg(fd, delay);
497}
498
499// Sends UID and GID
500static void invoker_send_ids(int fd, int uid, int gid)
501{
502 invoke_send_msg(fd, INVOKER_MSG_IDS);
503 invoke_send_msg(fd, uid);
504 invoke_send_msg(fd, gid);
505}
506
507// Sends the environment variables
508static void invoker_send_env(int fd)
509{
510 int i, n_vars;
511
512 // Count environment variables.
513 for (n_vars = 0; environ[n_vars] != NULL; n_vars++) ;
514
515 invoke_send_msg(fd, INVOKER_MSG_ENV);
516 invoke_send_msg(fd, n_vars);
517
518 for (i = 0; i < n_vars; i++) {
519 invoke_send_str(fd, environ[i]);
520 }
521
522 return;
523}
524
525// Sends I/O descriptors
526static void invoker_send_io(int fd)
527{
528 struct msghdr msg;
529 struct cmsghdr *cmsg = NULL;
530 int io[3] = { 0, 1, 2 };
531 char buf[CMSG_SPACE(sizeof(io))];
532 struct iovec iov;
533 int dummy;
534
535 memset(&msg, 0, sizeof(struct msghdr));
536
537 iov.iov_base = &dummy;
538 iov.iov_len = 1;
539
540 msg.msg_iov = &iov;
541 msg.msg_iovlen = 1;
542 msg.msg_control = buf;
543 msg.msg_controllen = sizeof(buf);
544
545 cmsg = CMSG_FIRSTHDR(&msg);
546 cmsg->cmsg_len = CMSG_LEN(sizeof(io));
547 cmsg->cmsg_level = SOL_SOCKET;
548 cmsg->cmsg_type = SCM_RIGHTS;
549
550 memcpy(CMSG_DATA(cmsg), io, sizeof(io));
551
552 msg.msg_controllen = cmsg->cmsg_len;
553
554 invoke_send_msg(fd, INVOKER_MSG_IO);
555 if (sendmsg(fd, &msg, 0) < 0) {
556 warning("sendmsg failed in invoker_send_io: %s \n", strerror(errno));
557 }
558
559 return;
560}
561
562// Sends the END message
563static void invoker_send_end(int fd)
564{
565 invoke_send_msg(fd, INVOKER_MSG_END);
566 invoke_recv_ack(fd);
567
568}
569
570// Prints the usage and exits with given status
571static void usage(int status)
572{
573 printf("\n"
574 "Usage: %s [options] [--type=TYPE] [file] [args]\n"
575 "\n"
576 "Launch applications compiled as a shared library (-shared) or\n"
577 "a position independent executable (-pie) through mapplauncherd.\n"
578 "\n"
579 "TYPE chooses the type of booster used. Qt-booster may be used to\n"
580 "launch anything. Possible values for TYPE:\n"
581 " qt5 Launch a Qt 5 application.\n"
582 " qtquick2 Launch a Qt Quick 2 (QML) application.\n"
583 " silica-qt5 Launch a Sailfish Silica application.\n"
584 " generic Launch any application, even if it's not a library.\n"
585 "\n"
586 "The TYPE may also be a comma delimited list of boosters to try. The first available\n"
587 "booster will be used.\n"
588 "\n"
589 "Options:\n"
590 " -t, --type TYPE Define booster type\n"
591 " -a, --application APP Define application booster name\n"
592 " -A, --auto-application Get application booster name from binary\n"
593 " -d, --delay SECS After invoking sleep for SECS seconds\n"
594 " (default %d).\n"
595 " -r, --respawn SECS After invoking respawn new booster after SECS seconds\n"
596 " (default %d, max %d).\n"
597 " -w, --wait-term Wait for launched process to terminate (default).\n"
598 " -n, --no-wait Do not wait for launched process to terminate.\n"
599 " -G, --global-syms Places symbols in the application binary and its\n"
600 " libraries to the global scope.\n"
601 " See RTLD_GLOBAL in the dlopen manual page.\n"
602 " -D, --deep-syms (TBD)"
603 " -s, --single-instance Launch the application as a single instance.\n"
604 " The existing application window will be activated\n"
605 " if already launched.\n"
606 " -o, --keep-oom-score Notify invoker that the launched process should inherit oom_score_adj\n"
607 " from the booster. The score is reset to 0 normally.\n"
608 " -T, --test-mode Invoker test mode. Also control file in root home should be in place.\n"
609 " -F, --desktop-file Desktop file of the application to notify lipstick of launching app.\n"
610 " -I, --id Sandboxing id to check if sandboxing should be forced.\n"
611 " If this is not defined, it's guessed from binary name.\n"
612 " -h, --help Print this help.\n"
613 " -v, --verbose Make invoker more verbose. Can be given several times.\n"
614 "\n"
615 "Example: %s --type=qt5 /usr/bin/helloworld\n"
616 "\n",
617 PROG_NAME_INVOKER, EXIT_DELAY, RESPAWN_DELAY, MAX_RESPAWN_DELAY, PROG_NAME_INVOKER);
618
619 exit(status);
620}
621
622// Return delay as integer
623static unsigned int get_delay(char *delay_arg, char *param_name,
624 unsigned int min_value, unsigned int max_value)
625{
626 unsigned int delay = EXIT_DELAY;
627
628 if (delay_arg) {
629 errno = 0; // To distinguish success/failure after call
630 delay = strtoul(delay_arg, NULL, 10);
631
632 // Check for various possible errors
633 if (errno == ERANGE
634 || delay < min_value
635 || delay > max_value) {
636 report(report_error, "Wrong value of %s parameter: %s\n", param_name, delay_arg);
637 usage(1);
638 }
639 }
640
641 return delay;
642}
643
644static void notify_app_launch(const char *desktop_file)
645{
646 DBusConnection *connection;
647 DBusMessage *message;
648 DBusError error;
649
650 dbus_error_init (&error);
651 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
652
653 if (connection) {
654 message = dbus_message_new_method_call("org.nemomobile.lipstick", "/LauncherModel",
655 "org.nemomobile.lipstick.LauncherModel", "notifyLaunching");
656 dbus_message_append_args(message, DBUS_TYPE_STRING, &desktop_file, DBUS_TYPE_INVALID);
657
658 dbus_connection_send(connection, message, NULL);
659 dbus_message_unref(message);
660 dbus_connection_flush(connection);
661 } else {
662 info("Failed to connect to the DBus session bus: %s", error.message);
663 dbus_error_free(&error);
664 return;
665 }
666}
667
668static bool ask_for_sandboxing(const char *app)
669{
670 char *path = strdup(app);
671 bool ret_val = sailjail_sandbox(basename(path));
672 free(path);
673 return ret_val;
674}
675
676static int wait_for_launched_process_to_exit(int socket_fd)
677{
678 int exit_status = EXIT_FAILURE;
679 int exit_signal = 0;
680
681 // coverity[tainted_string_return_content]
682 g_invoked_pid = invoker_recv_pid(socket_fd);
683 info("Booster's pid is %d \n ", g_invoked_pid);
684
685 // Setup signal handlers
686 sigs_init();
687
688 for (;;) {
689 // Setup things for select()
690 fd_set readfds;
691 int ndfs = 0;
692
693 FD_ZERO(&readfds);
694
695 FD_SET(socket_fd, &readfds);
696 ndfs = (socket_fd > ndfs) ? socket_fd : ndfs;
697
698 // sig_forwarder() handles signals.
699 // We only have to receive those here.
700 FD_SET(g_signal_pipe[0], &readfds);
701 ndfs = (g_signal_pipe[0] > ndfs) ? g_signal_pipe[0] : ndfs;
702
703 // Wait for something appearing in the pipes.
704 if (select(ndfs + 1, &readfds, NULL, NULL, NULL) == -1) {
705 if (errno == EINTR || errno == EAGAIN)
706 continue;
707 warning("socket select failed: %m\n");
708 break;
709 }
710
711 // Check if we got exit status from the invoked application
712 if (FD_ISSET(socket_fd, &readfds)) {
713 if (!invoker_recv_exit(socket_fd, &exit_status)) {
714 // connection to application was lost
715 exit_status = EXIT_FAILURE;
716 } else {
717 // there is no need to kill the application
718 g_invoked_pid = -1;
719 }
720 break;
721 }
722
723 // Check if we got a UNIX signal.
724 if (FD_ISSET(g_signal_pipe[0], &readfds)) {
725 // Clean up the pipe
726 char signal_id = 0;
727 if (read(g_signal_pipe[0], &signal_id, 1) != 1) {
728 error("signal pipe read failure, terminating\n");
729 exit(EXIT_FAILURE);
730 }
731 exit_signal = signal_id;
732 if (exit_signal == SIGTERM)
733 exit_status = EXIT_SUCCESS;
734 break;
735 }
736 }
737
738 // Restore default signal handlers
739 sigs_restore();
740
741 if (exit_status != EXIT_SUCCESS)
742 warning("application (pid=%d) exit(%d) signal(%d)\n",
743 (int)g_invoked_pid, exit_status, exit_signal);
744 else
745 info("application (pid=%d) exit(%d) signal(%d)\n",
746 (int)g_invoked_pid, exit_status, exit_signal);
747
748 if (socket_fd != -1) {
749 if (shutdown_socket(socket_fd))
750 g_invoked_pid = -1;
751 close(socket_fd),
752 socket_fd = -1;
753 if (g_invoked_pid != -1)
755 }
756
757 return exit_status;
758}
759
760typedef struct InvokeArgs {
762 char **prog_argv;
764 const char *app_type;
765 const char *app_name;
768 unsigned int respawn_delay;
770 const char *desktop_file;
772 unsigned int exit_delay;
774
775#define INVOKE_ARGS_INIT {\
776 .prog_argc = 0,\
777 .prog_argv = NULL,\
778 .prog_name = NULL,\
779 .app_type = NULL,\
780 .app_name = UNDEFINED_APPLICATION,\
781 .magic_options = INVOKER_MSG_MAGIC_OPTION_WAIT,\
782 .wait_term = true,\
783 .respawn_delay = RESPAWN_DELAY,\
784 .test_mode = false,\
785 .desktop_file = NULL,\
786 .sandboxing_id = NULL,\
787 .exit_delay = EXIT_DELAY,\
788}
789
790// "normal" invoke through a socket connection
791static int invoke_remote(int socket_fd, const InvokeArgs *args)
792{
793 int exit_status = EXIT_FAILURE;
794
795 // Get process priority
796 errno = 0;
797 int prog_prio = getpriority(PRIO_PROCESS, 0);
798 if (errno && prog_prio < 0) {
799 prog_prio = 0;
800 }
801
802 // Connection with launcher process is established,
803 // send the data.
804 invoker_send_magic(socket_fd, args->magic_options);
805 invoker_send_name(socket_fd, args->prog_name);
806 invoker_send_exec(socket_fd, args->prog_argv[0]);
807 invoker_send_args(socket_fd, args->prog_argc, args->prog_argv);
808 invoker_send_prio(socket_fd, prog_prio);
809 invoker_send_delay(socket_fd, args->respawn_delay);
810 invoker_send_ids(socket_fd, getuid(), getgid());
811 invoker_send_io(socket_fd);
812 invoker_send_env(socket_fd);
813 invoker_send_end(socket_fd);
814
815 if (args->desktop_file)
817
818 if (args->wait_term) {
819 exit_status = wait_for_launched_process_to_exit(socket_fd),
820 socket_fd = -1;
821 } else {
822 exit_status = EXIT_SUCCESS;
823 }
824
825 if (socket_fd != -1)
826 close(socket_fd);
827
828 return exit_status;
829}
830
831static void invoke_fallback(const InvokeArgs *args)
832{
833 // Connection with launcher is broken,
834 // try to launch application via execve
835 warning("Connection with launcher process is broken. \n");
836 error("Start application %s as a binary executable without launcher...\n", args->prog_name);
837
838 // Fork if wait_term not set
839 if (!args->wait_term) {
840 // Fork a new process
841 pid_t newPid = fork();
842
843 if (newPid == -1) {
844 error("Invoker failed to fork. \n");
845 exit(EXIT_FAILURE);
846 } else if (newPid != 0) { /* parent process */
847 return;
848 }
849 }
850
851 // Exec the process image
852 execve(args->prog_name, args->prog_argv, environ);
853 perror("execve"); /* execve() only returns on error */
854 exit(EXIT_FAILURE);
855}
856
857// Invokes the given application
858static int invoke(InvokeArgs *args)
859{
860 /* Note: Contents of 'args' are assumed to have been
861 * checked and sanitized before invoke() call.
862 */
863
864 int status = EXIT_FAILURE;
865
866 /* The app can be launched with a comma delimited list of
867 * booster types to attempt.
868 */
869 char **types = split(args->app_type, ",");
870
871 int fd = -1;
872
873 /* Session booster is a special case:
874 * - is never going to be application specific
875 * - can use and still uses legacy socket path
876 * - mutually exclusive with all other choises
877 * - no fallbacks should be utilized
878 */
879
880 bool tried_session = false;
881 for (size_t i = 0; !tried_session && types[i]; ++i) {
882 if (strcmp(types[i], BOOSTER_SESSION))
883 continue;
884 tried_session = true;
885 fd = invoker_init(types[i], NULL);
886 }
887
888 /* Application aware boosters
889 * - have fallback strategy, but it
890 * - must not cross application vs UNDEFINED_APPLICATION boundary
891 */
892 if (fd == -1 && !tried_session) {
893 bool tried_generic = false;
894 for (size_t i = 0; fd == -1 && types[i]; ++i) {
895 if (!strcmp(types[i], BOOSTER_GENERIC))
896 tried_generic = true;
897 fd = invoker_init(types[i], args->app_name);
898 }
899 if (fd == -1 && !tried_generic)
901 }
902
903 if (fd != -1) {
904 /* "normal" invoke through a socket connetion */
905 status = invoke_remote(fd, args),
906 fd = -1;
907 } else if (tried_session) {
908 warning("Launch failed, session booster is not available.\n");
909 } else if (strcmp(args->app_name, UNDEFINED_APPLICATION)) {
910 /* Boosters that deal explicitly with one application only
911 * must be assumed to run within sandbox -> skipping boosting
912 * would also skip sandboxing -> no direct launch fallback
913 */
914 warning("Launch failed, application specific booster is not available.\n");
915 } else {
916 /* Give up and start unboosted */
917 warning("Also fallback boosters failed, launch without boosting.\n");
918 invoke_fallback(args);
919 /* Returns only in case of: no-wait was specified and fork succeeded */
920 status = EXIT_SUCCESS;
921 }
922
923 for (int i = 0; types[i]; ++i)
924 free(types[i]);
925 free(types);
926
927 return status;
928}
929
930int main(int argc, char *argv[])
931{
933 bool auto_application = false;
934 // Called with a different name (old way of using invoker) ?
935 if (!strstr(argv[0], PROG_NAME_INVOKER)) {
936 die(1,
937 "Incorrect use of invoker, don't use symlinks. "
938 "Run invoker explicitly from e.g. a D-Bus service file instead.\n");
939 }
940
941 // Options recognized
942 struct option longopts[] = {
943 {"help", no_argument, NULL, 'h'},
944 {"wait-term", no_argument, NULL, 'w'},
945 {"no-wait", no_argument, NULL, 'n'},
946 {"global-syms", no_argument, NULL, 'G'},
947 {"deep-syms", no_argument, NULL, 'D'},
948 {"single-instance", no_argument, NULL, 's'},
949 {"keep-oom-score", no_argument, NULL, 'o'},
950 {"daemon-mode", no_argument, NULL, 'o'}, // Legacy alias
951 {"test-mode", no_argument, NULL, 'T'},
952 {"type", required_argument, NULL, 't'},
953 {"application", required_argument, NULL, 'a'},
954 {"auto-application", no_argument, NULL, 'A'},
955 {"delay", required_argument, NULL, 'd'},
956 {"respawn", required_argument, NULL, 'r'},
957 {"splash", required_argument, NULL, 'S'}, // Legacy, ignored
958 {"splash-landscape", required_argument, NULL, 'L'}, // Legacy, ignored
959 {"desktop-file", required_argument, NULL, 'F'},
960 {"id", required_argument, NULL, 'I'},
961 {"verbose", no_argument, NULL, 'v'},
962 {0, 0, 0, 0}
963 };
964
965 // Parse options
966 // The use of + for POSIXLY_CORRECT behavior is a GNU extension, but avoids polluting
967 // the environment
968 int opt;
969 while ((opt = getopt_long(argc, argv, "+hvcwnGDsoTd:t:a:Ar:S:L:F:I:", longopts, NULL)) != -1) {
970 switch(opt) {
971 case 'h':
972 usage(0);
973 break;
974
975 case 'v':
976 report_set_type(report_get_type() + 1);
977 break;
978
979 case 'w':
980 // nothing to do, it's by default now
981 break;
982
983 case 'o':
984 args.magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE;
985 break;
986
987 case 'n':
988 args.wait_term = false;
989 args.magic_options &= (~INVOKER_MSG_MAGIC_OPTION_WAIT);
990 break;
991
992 case 'G':
993 args.magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL;
994 break;
995
996 case 'D':
997 args.magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_DEEP;
998 break;
999
1000 case 'T':
1001 args.test_mode = true;
1002 break;
1003
1004 case 't':
1005 args.app_type = optarg;
1006 break;
1007
1008 case 'a':
1009 args.app_name = optarg;
1010 auto_application = false;
1011 break;
1012
1013 case 'A':
1014 auto_application = true;
1015 break;
1016
1017 case 'd':
1018 args.exit_delay = get_delay(optarg, "delay", MIN_EXIT_DELAY, MAX_EXIT_DELAY);
1019 break;
1020
1021 case 'r':
1022 args.respawn_delay = get_delay(optarg, "respawn delay",
1024 break;
1025
1026 case 's':
1027 args.magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE;
1028 break;
1029
1030 case 'S':
1031 case 'L':
1032 // Removed splash support. Ignore.
1033 break;
1034
1035 case 'F':
1036 args.desktop_file = optarg;
1037 break;
1038
1039 case 'I':
1040 args.sandboxing_id = strdup(optarg);
1041 break;
1042
1043 case '?':
1044 usage(1);
1045 }
1046 }
1047
1048 // Option processing stops as soon as application name is encountered
1049
1050 args.prog_argc = argc - optind;
1051 args.prog_argv = &argv[optind];
1052
1053 if (args.prog_argc < 1) {
1054 report(report_error, "No command line to invoke was given.\n");
1055 exit(EXIT_FAILURE);
1056 }
1057
1058 // Force argv[0] of application to be the absolute path to allow the
1059 // application to find out its installation directory from there
1060 args.prog_argv[0] = search_program(args.prog_argv[0]);
1061
1062 // Check if application exists
1063 struct stat file_stat;
1064 if (stat(args.prog_argv[0], &file_stat) == -1) {
1065 report(report_error, "%s: not found: %m\n", args.prog_argv[0]);
1067 }
1068
1069 // Check that application is regular file (or symlink to such)
1070 if (!S_ISREG(file_stat.st_mode)) {
1071 report(report_error, "%s: not a file\n", args.prog_argv[0]);
1073 }
1074
1075 // If it's a launcher, append its first argument to the name
1076 // (at this point, we have already checked if it exists and is a file)
1077 if (strcmp(args.prog_argv[0], "/usr/bin/sailfish-qml") == 0) {
1078 if (args.prog_argc < 2) {
1079 report(report_error, "%s: requires an argument\n", args.prog_argv[0]);
1081 }
1082
1083 if (asprintf(&args.prog_name, "%s %s", args.prog_argv[0], args.prog_argv[1]) < 0)
1084 exit(EXIT_FAILURE);
1085 } else {
1086 if (!(args.prog_name = strdup(args.prog_argv[0])))
1087 exit(EXIT_FAILURE);
1088 }
1089
1090 if (auto_application)
1091 args.app_name = basename(args.prog_argv[0]);
1092
1093 if (!args.app_type) {
1094 report(report_error, "Application type must be specified with --type.\n");
1095 usage(1);
1096 }
1097
1098 if (!args.app_name) {
1099 report(report_error, "Application name must be specified with --application.\n");
1100 usage(1);
1101 }
1102
1103 // If TEST_MODE_CONTROL_FILE doesn't exists switch off test mode
1104 if (args.test_mode && access(TEST_MODE_CONTROL_FILE, F_OK) != 0) {
1105 args.test_mode = false;
1106 info("Invoker test mode is not enabled.\n");
1107 }
1108
1109 if (pipe(g_signal_pipe) == -1) {
1110 report(report_error, "Creating a pipe for Unix signals failed!\n");
1111 exit(EXIT_FAILURE);
1112 }
1113
1114 // If sailjail is already used or app specific booster is used, skip checking for sandboxing
1115 if (!strcmp(args.prog_name, SAILJAIL_PATH) || strcmp(args.app_name, UNDEFINED_APPLICATION)) {
1116 args.sandboxing_id = NULL;
1117 } else if (!args.sandboxing_id) {
1118 // When id is not defined, assume it can be derived from binary path
1119 char *path = strdup(args.prog_name);
1120 args.sandboxing_id = strdup(basename(path));
1121 free(path);
1122 }
1123
1124 // Application specific boosters are running in sandbox and can
1125 // thus launch only sandboxed processes, otherwise
1126 // If arguments don't define sailjail and sailjaild says the app must be sandboxed,
1127 // we force sandboxing here
1129 warning("enforcing sandboxing for '%s'", args.prog_name);
1130 // We must use generic booster here as nothing else would work
1131 // to run sailjail which is not compiled for launching via booster
1133 // Prepend sailjail
1134 char **old_argv = args.prog_argv;
1135 args.prog_argc += 4;
1136 args.prog_argv = (char **)calloc(args.prog_argc + 1, sizeof *args.prog_argv);
1137 args.prog_argv[0] = SAILJAIL_PATH;
1138 args.prog_argv[1] = "-p";
1139 args.prog_argv[2] = args.sandboxing_id,
1140 args.sandboxing_id = NULL;
1141 args.prog_argv[3] = "--";
1142 for (int i = 4; i < args.prog_argc + 1; ++i)
1143 args.prog_argv[i] = old_argv[i-4];
1144 // Don't free old_argv because it's probably not dynamically allocated
1145 free(args.prog_name);
1146 args.prog_name = strdup(SAILJAIL_PATH);
1147 }
1148
1149 // Send commands to the launcher daemon
1150 info("Invoking execution: '%s'\n", args.prog_name);
1151 int ret_val = invoke(&args);
1152
1153 // Sleep for delay before exiting
1154 if (args.exit_delay) {
1155 // DBUS cannot cope some times if the invoker exits too early.
1156 info("Delaying exit for %d seconds..\n", args.exit_delay);
1157 sleep(args.exit_delay);
1158 }
1159
1160 info("invoker exit(%d)\n", ret_val);
1161 return ret_val;
1162}
void invoke_send_msg(int fd, uint32_t msg)
Definition invokelib.c:42
bool invoke_recv_msg(int fd, uint32_t *msg)
Definition invokelib.c:48
void invoke_send_str(int fd, const char *str)
Definition invokelib.c:69
#define TEST_MODE_CONTROL_FILE
Definition invokelib.h:34
int main(int argc, char *argv[])
Definition invoker.c:930
static void kill_application(pid_t pid)
Definition invoker.c:293
static char * slice(const char *pos, const char **ppos, const char *delim)
Definition invoker.c:90
static void invoker_send_exec(int fd, char *exec)
Definition invoker.c:468
#define UNDEFINED_APPLICATION
Definition invoker.c:60
static char ** split(const char *str, const char *delim)
Definition invoker.c:106
static void invoke_fallback(const InvokeArgs *args)
Definition invoker.c:831
static void usage(int status)
Definition invoker.c:571
static void invoker_send_magic(int fd, uint32_t options)
Definition invoker.c:455
static void invoker_send_prio(int fd, int prio)
Definition invoker.c:486
static const unsigned int RESPAWN_DELAY
Definition invoker.c:140
static void sigs_set(struct sigaction *sig)
Definition invoker.c:180
static bool ask_for_sandboxing(const char *app)
Definition invoker.c:668
static int invoke_remote(int socket_fd, const InvokeArgs *args)
Definition invoker.c:791
static const unsigned int MIN_EXIT_DELAY
Definition invoker.c:135
static void sigs_init(void)
Definition invoker.c:187
static void notify_app_launch(const char *desktop_file)
Definition invoker.c:644
#define INVOKE_ARGS_INIT
Definition invoker.c:775
static int g_signal_pipe[2]
Pipe used to safely catch Unix signals.
Definition invoker.c:156
static int invoker_init(const char *app_type, const char *app_name)
Definition invoker.c:350
static void invoker_send_env(int fd)
Definition invoker.c:508
static void invoker_send_name(int fd, const char *name)
Definition invoker.c:462
static const unsigned int MAX_RESPAWN_DELAY
Definition invoker.c:142
static pid_t g_invoked_pid
Definition invoker.c:150
static void invoker_send_io(int fd)
Definition invoker.c:526
static const unsigned int MAX_EXIT_DELAY
Definition invoker.c:136
static const unsigned int EXIT_DELAY
Definition invoker.c:134
char ** environ
static void invoker_send_delay(int fd, int delay)
Definition invoker.c:493
static void sig_forwarder(int sig)
Definition invoker.c:159
static void invoker_send_end(int fd)
Definition invoker.c:563
static bool shutdown_socket(int socket_fd)
Definition invoker.c:227
static int wait_for_launched_process_to_exit(int socket_fd)
Definition invoker.c:676
static unsigned timestamp(void)
Definition invoker.c:210
static unsigned int get_delay(char *delay_arg, char *param_name, unsigned int min_value, unsigned int max_value)
Definition invoker.c:623
#define BOOSTER_SESSION
Definition invoker.c:54
static bool invoker_recv_exit(int fd, int *status)
Definition invoker.c:432
static uint32_t invoker_recv_pid(int fd)
Definition invoker.c:414
static const unsigned int MIN_RESPAWN_DELAY
Definition invoker.c:141
#define BOOSTER_GENERIC
Definition invoker.c:55
static char * strip(char *str)
Definition invoker.c:69
static void sigs_restore(void)
Definition invoker.c:199
static bool invoke_recv_ack(int fd)
Definition invoker.c:336
static void invoker_send_args(int fd, int argc, char **argv)
Definition invoker.c:474
static int invoke(InvokeArgs *args)
Definition invoker.c:858
static void invoker_send_ids(int fd, int uid, int gid)
Definition invoker.c:500
static const unsigned char EXIT_STATUS_APPLICATION_NOT_FOUND
Definition invoker.c:144
char * search_program(const char *progname)
Definition search.c:42
char * prog_name
Definition invoker.c:763
const char * app_name
Definition invoker.c:765
char * sandboxing_id
Definition invoker.c:771
bool wait_term
Definition invoker.c:767
int prog_argc
Definition invoker.c:761
uint32_t magic_options
Definition invoker.c:766
unsigned int respawn_delay
Definition invoker.c:768
bool test_mode
Definition invoker.c:769
const char * desktop_file
Definition invoker.c:770
unsigned int exit_delay
Definition invoker.c:772
const char * app_type
Definition invoker.c:764
char ** prog_argv
Definition invoker.c:762