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 {
344 die(1, "Received wrong ack (%08x)\n", action);
345 }
346
347 return true;
348}
349
350// Inits a socket connection for the given application type
351static int invoker_init(const char *app_type, const char *app_name)
352{
353 info("try type=%s app=%s ...", app_type, app_name);
354
355 bool connected = false;
356 int fd = -1;
357
358 /* Sanity check args */
359 if (!app_type || strchr(app_type, '/'))
360 goto EXIT;
361 if (app_name && strchr(app_name, '/'))
362 goto EXIT;
363
364 const char *runtimeDir = getenv("XDG_RUNTIME_DIR");
365 if (!runtimeDir || !*runtimeDir) {
366 error("XDG_RUNTIME_DIR is not defined.\n");
367 goto EXIT;
368 }
369
370 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
371 error("Failed to create socket: %m\n");
372 goto EXIT;
373 }
374
375 struct sockaddr_un sun = {
376 .sun_family = AF_UNIX,
377 };
378 int maxSize = sizeof(sun.sun_path);
379 int length;
380
381 if (app_name)
382 length = snprintf(sun.sun_path, maxSize, "%s/mapplauncherd/_%s/%s/socket",
383 runtimeDir, app_name, app_type);
384 else
385 length = snprintf(sun.sun_path, maxSize, "%s/mapplauncherd/%s",
386 runtimeDir, app_type);
387
388 if (length <= 0 || length >= maxSize) {
389 if (app_name)
390 error("Invalid booster type: %s / application: %s\n",
391 app_type, app_name);
392 else
393 error("Invalid booster type: %s\n", app_type);
394 goto EXIT;
395 }
396
397 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
398 if (errno != ENOENT)
399 warning("connect(\"%s\") failed: %m\n", sun.sun_path);
400 goto EXIT;
401 }
402
403 info("connected to: %s\n", sun.sun_path);
404 connected = true;
405
406EXIT:
407 if (!connected && fd != -1)
408 close(fd), fd = -1;
409 return fd;
410}
411
412// Receives pid of the invoked process.
413// Invoker doesn't know it, because the launcher daemon
414// is the one who forks.
415static uint32_t invoker_recv_pid(int fd)
416{
417 // Receive action.
418 uint32_t action;
419 invoke_recv_msg(fd, &action);
420 if (action != INVOKER_MSG_PID)
421 die(1, "Received a bad message id (%08x)\n", action);
422
423 // Receive pid.
424 uint32_t pid = 0;
425 invoke_recv_msg(fd, &pid);
426 if (pid == 0)
427 die(1, "Received a zero pid \n");
428
429 return pid;
430}
431
432// Receives exit status of the invoked process
433static bool invoker_recv_exit(int fd, int* status)
434{
435 uint32_t action;
436
437 // Receive action.
438 bool res = invoke_recv_msg(fd, &action);
439
440 if (!res || (action != INVOKER_MSG_EXIT))
441 {
442 // Boosted application process was killed somehow.
443 // Let's give applauncherd process some time to cope
444 // with this situation.
445 sleep(2);
446
447 // If nothing happend, return
448 return false;
449 }
450
451 // Receive exit status.
452 res = invoke_recv_msg(fd, (uint32_t*) status);
453 return res;
454}
455
456// Sends magic number / protocol version
457static void invoker_send_magic(int fd, uint32_t options)
458{
459 // Send magic.
460 invoke_send_msg(fd, INVOKER_MSG_MAGIC | INVOKER_MSG_MAGIC_VERSION | options);
461}
462
463// Sends the process name to be invoked.
464static void invoker_send_name(int fd, const char *name)
465{
466 invoke_send_msg(fd, INVOKER_MSG_NAME);
467 invoke_send_str(fd, name);
468}
469
470static void invoker_send_exec(int fd, char *exec)
471{
472 invoke_send_msg(fd, INVOKER_MSG_EXEC);
473 invoke_send_str(fd, exec);
474}
475
476static void invoker_send_args(int fd, int argc, char **argv)
477{
478 int i;
479
480 invoke_send_msg(fd, INVOKER_MSG_ARGS);
481 invoke_send_msg(fd, argc);
482 for (i = 0; i < argc; i++)
483 {
484 info("param %d %s \n", i, argv[i]);
485 invoke_send_str(fd, argv[i]);
486 }
487}
488
489static void invoker_send_prio(int fd, int prio)
490{
491 invoke_send_msg(fd, INVOKER_MSG_PRIO);
492 invoke_send_msg(fd, prio);
493}
494
495// Sends booster respawn delay
496static void invoker_send_delay(int fd, int delay)
497{
498 invoke_send_msg(fd, INVOKER_MSG_DELAY);
499 invoke_send_msg(fd, delay);
500}
501
502// Sends UID and GID
503static void invoker_send_ids(int fd, int uid, int gid)
504{
505 invoke_send_msg(fd, INVOKER_MSG_IDS);
506 invoke_send_msg(fd, uid);
507 invoke_send_msg(fd, gid);
508}
509
510// Sends the environment variables
511static void invoker_send_env(int fd)
512{
513 int i, n_vars;
514
515 // Count environment variables.
516 for (n_vars = 0; environ[n_vars] != NULL; n_vars++) ;
517
518 invoke_send_msg(fd, INVOKER_MSG_ENV);
519 invoke_send_msg(fd, n_vars);
520
521 for (i = 0; i < n_vars; i++)
522 {
523 invoke_send_str(fd, environ[i]);
524 }
525
526 return;
527}
528
529// Sends I/O descriptors
530static void invoker_send_io(int fd)
531{
532 struct msghdr msg;
533 struct cmsghdr *cmsg = NULL;
534 int io[3] = { 0, 1, 2 };
535 char buf[CMSG_SPACE(sizeof(io))];
536 struct iovec iov;
537 int dummy;
538
539 memset(&msg, 0, sizeof(struct msghdr));
540
541 iov.iov_base = &dummy;
542 iov.iov_len = 1;
543
544 msg.msg_iov = &iov;
545 msg.msg_iovlen = 1;
546 msg.msg_control = buf;
547 msg.msg_controllen = sizeof(buf);
548
549 cmsg = CMSG_FIRSTHDR(&msg);
550 cmsg->cmsg_len = CMSG_LEN(sizeof(io));
551 cmsg->cmsg_level = SOL_SOCKET;
552 cmsg->cmsg_type = SCM_RIGHTS;
553
554 memcpy(CMSG_DATA(cmsg), io, sizeof(io));
555
556 msg.msg_controllen = cmsg->cmsg_len;
557
558 invoke_send_msg(fd, INVOKER_MSG_IO);
559 if (sendmsg(fd, &msg, 0) < 0)
560 {
561 warning("sendmsg failed in invoker_send_io: %s \n", strerror(errno));
562 }
563
564 return;
565}
566
567// Sends the END message
568static void invoker_send_end(int fd)
569{
570 invoke_send_msg(fd, INVOKER_MSG_END);
571 invoke_recv_ack(fd);
572
573}
574
575// Prints the usage and exits with given status
576static void usage(int status)
577{
578 printf("\n"
579 "Usage: %s [options] [--type=TYPE] [file] [args]\n"
580 "\n"
581 "Launch applications compiled as a shared library (-shared) or\n"
582 "a position independent executable (-pie) through mapplauncherd.\n"
583 "\n"
584 "TYPE chooses the type of booster used. Qt-booster may be used to\n"
585 "launch anything. Possible values for TYPE:\n"
586 " qt5 Launch a Qt 5 application.\n"
587 " qtquick2 Launch a Qt Quick 2 (QML) application.\n"
588 " silica-qt5 Launch a Sailfish Silica application.\n"
589 " generic Launch any application, even if it's not a library.\n"
590 "\n"
591 "The TYPE may also be a comma delimited list of boosters to try. The first available\n"
592 "booster will be used.\n"
593 "\n"
594 "Options:\n"
595 " -t, --type TYPE Define booster type\n"
596 " -a, --application APP Define application booster name\n"
597 " -A, --auto-application Get application booster name from binary\n"
598 " -d, --delay SECS After invoking sleep for SECS seconds\n"
599 " (default %d).\n"
600 " -r, --respawn SECS After invoking respawn new booster after SECS seconds\n"
601 " (default %d, max %d).\n"
602 " -w, --wait-term Wait for launched process to terminate (default).\n"
603 " -n, --no-wait Do not wait for launched process to terminate.\n"
604 " -G, --global-syms Places symbols in the application binary and its\n"
605 " libraries to the global scope.\n"
606 " See RTLD_GLOBAL in the dlopen manual page.\n"
607 " -D, --deep-syms (TBD)"
608 " -s, --single-instance Launch the application as a single instance.\n"
609 " The existing application window will be activated\n"
610 " if already launched.\n"
611 " -o, --keep-oom-score Notify invoker that the launched process should inherit oom_score_adj\n"
612 " from the booster. The score is reset to 0 normally.\n"
613 " -T, --test-mode Invoker test mode. Also control file in root home should be in place.\n"
614 " -F, --desktop-file Desktop file of the application to notify lipstick of launching app.\n"
615 " -I, --id Sandboxing id to check if sandboxing should be forced.\n"
616 " If this is not defined, it's guessed from binary name.\n"
617 " -h, --help Print this help.\n"
618 " -v, --verbose Make invoker more verbose. Can be given several times.\n"
619 "\n"
620 "Example: %s --type=qt5 /usr/bin/helloworld\n"
621 "\n",
622 PROG_NAME_INVOKER, EXIT_DELAY, RESPAWN_DELAY, MAX_RESPAWN_DELAY, PROG_NAME_INVOKER);
623
624 exit(status);
625}
626
627// Return delay as integer
628static unsigned int get_delay(char *delay_arg, char *param_name,
629 unsigned int min_value, unsigned int max_value)
630{
631 unsigned int delay = EXIT_DELAY;
632
633 if (delay_arg)
634 {
635 errno = 0; // To distinguish success/failure after call
636 delay = strtoul(delay_arg, NULL, 10);
637
638 // Check for various possible errors
639 if (errno == ERANGE
640 || delay < min_value
641 || delay > max_value)
642 {
643 report(report_error, "Wrong value of %s parameter: %s\n", param_name, delay_arg);
644 usage(1);
645 }
646 }
647
648 return delay;
649}
650
651static void notify_app_launch(const char *desktop_file)
652{
653 DBusConnection *connection;
654 DBusMessage *message;
655 DBusError error;
656
657 dbus_error_init (&error);
658 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
659
660 if (connection) {
661 message = dbus_message_new_method_call("org.nemomobile.lipstick", "/LauncherModel",
662 "org.nemomobile.lipstick.LauncherModel", "notifyLaunching");
663 dbus_message_append_args(message, DBUS_TYPE_STRING, &desktop_file, DBUS_TYPE_INVALID);
664
665 dbus_connection_send(connection, message, NULL);
666 dbus_message_unref(message);
667 dbus_connection_flush(connection);
668 } else {
669 info("Failed to connect to the DBus session bus: %s", error.message);
670 dbus_error_free(&error);
671 return;
672 }
673}
674
675static bool ask_for_sandboxing(const char *app)
676{
677 char *path = strdup(app);
678 bool ret_val = sailjail_sandbox(basename(path));
679 free(path);
680 return ret_val;
681}
682
683static int wait_for_launched_process_to_exit(int socket_fd)
684{
685 int exit_status = EXIT_FAILURE;
686 int exit_signal = 0;
687
688 // coverity[tainted_string_return_content]
689 g_invoked_pid = invoker_recv_pid(socket_fd);
690 info("Booster's pid is %d \n ", g_invoked_pid);
691
692 // Setup signal handlers
693 sigs_init();
694
695 for (;;) {
696 // Setup things for select()
697 fd_set readfds;
698 int ndfs = 0;
699
700 FD_ZERO(&readfds);
701
702 FD_SET(socket_fd, &readfds);
703 ndfs = (socket_fd > ndfs) ? socket_fd : ndfs;
704
705 // sig_forwarder() handles signals.
706 // We only have to receive those here.
707 FD_SET(g_signal_pipe[0], &readfds);
708 ndfs = (g_signal_pipe[0] > ndfs) ? g_signal_pipe[0] : ndfs;
709
710 // Wait for something appearing in the pipes.
711 if (select(ndfs + 1, &readfds, NULL, NULL, NULL) == -1) {
712 if (errno == EINTR || errno == EAGAIN)
713 continue;
714 warning("socket select failed: %m\n");
715 break;
716 }
717
718 // Check if we got exit status from the invoked application
719 if (FD_ISSET(socket_fd, &readfds)) {
720 if (!invoker_recv_exit(socket_fd, &exit_status)) {
721 // connection to application was lost
722 exit_status = EXIT_FAILURE;
723 } else {
724 // there is no need to kill the application
725 g_invoked_pid = -1;
726 }
727 break;
728 }
729
730 // Check if we got a UNIX signal.
731 if (FD_ISSET(g_signal_pipe[0], &readfds)) {
732 // Clean up the pipe
733 char signal_id = 0;
734 if (read(g_signal_pipe[0], &signal_id, 1) != 1) {
735 error("signal pipe read failure, terminating\n");
736 exit(EXIT_FAILURE);
737 }
738 exit_signal = signal_id;
739 if (exit_signal == SIGTERM)
740 exit_status = EXIT_SUCCESS;
741 break;
742 }
743 }
744
745 // Restore default signal handlers
746 sigs_restore();
747
748 if (exit_status != EXIT_SUCCESS)
749 warning("application (pid=%d) exit(%d) signal(%d)\n",
750 (int)g_invoked_pid, exit_status, exit_signal);
751 else
752 info("application (pid=%d) exit(%d) signal(%d)\n",
753 (int)g_invoked_pid, exit_status, exit_signal);
754
755 if (socket_fd != -1) {
756 if (shutdown_socket(socket_fd))
757 g_invoked_pid = -1;
758 close(socket_fd),
759 socket_fd = -1;
760 if (g_invoked_pid != -1)
762 }
763
764 return exit_status;
765}
766
767typedef struct InvokeArgs {
769 char **prog_argv;
771 const char *app_type;
772 const char *app_name;
775 unsigned int respawn_delay;
777 const char *desktop_file;
779 unsigned int exit_delay;
781
782#define INVOKE_ARGS_INIT {\
783 .prog_argc = 0,\
784 .prog_argv = NULL,\
785 .prog_name = NULL,\
786 .app_type = NULL,\
787 .app_name = UNDEFINED_APPLICATION,\
788 .magic_options = INVOKER_MSG_MAGIC_OPTION_WAIT,\
789 .wait_term = true,\
790 .respawn_delay = RESPAWN_DELAY,\
791 .test_mode = false,\
792 .desktop_file = NULL,\
793 .sandboxing_id = NULL,\
794 .exit_delay = EXIT_DELAY,\
795}
796
797// "normal" invoke through a socket connection
798static int invoke_remote(int socket_fd, const InvokeArgs *args)
799{
800 int exit_status = EXIT_FAILURE;
801
802 // Get process priority
803 errno = 0;
804 int prog_prio = getpriority(PRIO_PROCESS, 0);
805 if (errno && prog_prio < 0)
806 {
807 prog_prio = 0;
808 }
809
810 // Connection with launcher process is established,
811 // send the data.
812 invoker_send_magic(socket_fd, args->magic_options);
813 invoker_send_name(socket_fd, args->prog_name);
814 invoker_send_exec(socket_fd, args->prog_argv[0]);
815 invoker_send_args(socket_fd, args->prog_argc, args->prog_argv);
816 invoker_send_prio(socket_fd, prog_prio);
817 invoker_send_delay(socket_fd, args->respawn_delay);
818 invoker_send_ids(socket_fd, getuid(), getgid());
819 invoker_send_io(socket_fd);
820 invoker_send_env(socket_fd);
821 invoker_send_end(socket_fd);
822
823 if (args->desktop_file)
825
826 if (args->wait_term) {
827 exit_status = wait_for_launched_process_to_exit(socket_fd),
828 socket_fd = -1;
829 } else {
830 exit_status = EXIT_SUCCESS;
831 }
832
833 if (socket_fd != -1)
834 close(socket_fd);
835
836 return exit_status;
837}
838
839static void invoke_fallback(const InvokeArgs *args)
840{
841 // Connection with launcher is broken,
842 // try to launch application via execve
843 warning("Connection with launcher process is broken. \n");
844 error("Start application %s as a binary executable without launcher...\n", args->prog_name);
845
846 // Fork if wait_term not set
847 if (!args->wait_term)
848 {
849 // Fork a new process
850 pid_t newPid = fork();
851
852 if (newPid == -1)
853 {
854 error("Invoker failed to fork. \n");
855 exit(EXIT_FAILURE);
856 }
857 else if (newPid != 0) /* parent process */
858 {
859 return;
860 }
861 }
862
863 // Exec the process image
864 execve(args->prog_name, args->prog_argv, environ);
865 perror("execve"); /* execve() only returns on error */
866 exit(EXIT_FAILURE);
867}
868
869// Invokes the given application
870static int invoke(InvokeArgs *args)
871{
872 /* Note: Contents of 'args' are assumed to have been
873 * checked and sanitized before invoke() call.
874 */
875
876 int status = EXIT_FAILURE;
877
878 /* The app can be launched with a comma delimited list of
879 * booster types to attempt.
880 */
881 char **types = split(args->app_type, ",");
882
883 int fd = -1;
884
885 /* Session booster is a special case:
886 * - is never going to be application specific
887 * - can use and still uses legacy socket path
888 * - mutually exclusive with all other choises
889 * - no fallbacks should be utilized
890 */
891
892 bool tried_session = false;
893 for (size_t i = 0; !tried_session && types[i]; ++i) {
894 if (strcmp(types[i], BOOSTER_SESSION))
895 continue;
896 tried_session = true;
897 fd = invoker_init(types[i], NULL);
898 }
899
900 /* Application aware boosters
901 * - have fallback strategy, but it
902 * - must not cross application vs UNDEFINED_APPLICATION boundary
903 */
904 if (fd == -1 && !tried_session) {
905 bool tried_generic = false;
906 for (size_t i = 0; fd == -1 && types[i]; ++i) {
907 if (!strcmp(types[i], BOOSTER_GENERIC))
908 tried_generic = true;
909 fd = invoker_init(types[i], args->app_name);
910 }
911 if (fd == -1 && !tried_generic)
913 }
914
915 if (fd != -1) {
916 /* "normal" invoke through a socket connetion */
917 status = invoke_remote(fd, args),
918 fd = -1;
919 } else if (tried_session) {
920 warning("Launch failed, session booster is not available.\n");
921 } else if (strcmp(args->app_name, UNDEFINED_APPLICATION)) {
922 /* Boosters that deal explicitly with one application only
923 * must be assumed to run within sandbox -> skipping boosting
924 * would also skip sandboxing -> no direct launch fallback
925 */
926 warning("Launch failed, application specific booster is not available.\n");
927 } else {
928 /* Give up and start unboosted */
929 warning("Also fallback boosters failed, launch without boosting.\n");
930 invoke_fallback(args);
931 /* Returns only in case of: no-wait was specified and fork succeeded */
932 status = EXIT_SUCCESS;
933 }
934
935 for (int i = 0; types[i]; ++i)
936 free(types[i]);
937 free(types);
938
939 return status;
940}
941
942int main(int argc, char *argv[])
943{
945 bool auto_application = false;
946 // Called with a different name (old way of using invoker) ?
947 if (!strstr(argv[0], PROG_NAME_INVOKER) )
948 {
949 die(1,
950 "Incorrect use of invoker, don't use symlinks. "
951 "Run invoker explicitly from e.g. a D-Bus service file instead.\n");
952 }
953
954 // Options recognized
955 struct option longopts[] = {
956 {"help", no_argument, NULL, 'h'},
957 {"wait-term", no_argument, NULL, 'w'},
958 {"no-wait", no_argument, NULL, 'n'},
959 {"global-syms", no_argument, NULL, 'G'},
960 {"deep-syms", no_argument, NULL, 'D'},
961 {"single-instance", no_argument, NULL, 's'},
962 {"keep-oom-score", no_argument, NULL, 'o'},
963 {"daemon-mode", no_argument, NULL, 'o'}, // Legacy alias
964 {"test-mode", no_argument, NULL, 'T'},
965 {"type", required_argument, NULL, 't'},
966 {"application", required_argument, NULL, 'a'},
967 {"auto-application", no_argument, NULL, 'A'},
968 {"delay", required_argument, NULL, 'd'},
969 {"respawn", required_argument, NULL, 'r'},
970 {"splash", required_argument, NULL, 'S'}, // Legacy, ignored
971 {"splash-landscape", required_argument, NULL, 'L'}, // Legacy, ignored
972 {"desktop-file", required_argument, NULL, 'F'},
973 {"id", required_argument, NULL, 'I'},
974 {"verbose", no_argument, NULL, 'v'},
975 {0, 0, 0, 0}
976 };
977
978 // Parse options
979 // The use of + for POSIXLY_CORRECT behavior is a GNU extension, but avoids polluting
980 // the environment
981 int opt;
982 while ((opt = getopt_long(argc, argv, "+hvcwnGDsoTd:t:a:Ar:S:L:F:I:", longopts, NULL)) != -1)
983 {
984 switch(opt)
985 {
986 case 'h':
987 usage(0);
988 break;
989
990 case 'v':
991 report_set_type(report_get_type() + 1);
992 break;
993
994 case 'w':
995 // nothing to do, it's by default now
996 break;
997
998 case 'o':
999 args.magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE;
1000 break;
1001
1002 case 'n':
1003 args.wait_term = false;
1004 args.magic_options &= (~INVOKER_MSG_MAGIC_OPTION_WAIT);
1005 break;
1006
1007 case 'G':
1008 args.magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL;
1009 break;
1010
1011 case 'D':
1012 args.magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_DEEP;
1013 break;
1014
1015 case 'T':
1016 args.test_mode = true;
1017 break;
1018
1019 case 't':
1020 args.app_type = optarg;
1021 break;
1022
1023 case 'a':
1024 args.app_name = optarg;
1025 auto_application = false;
1026 break;
1027
1028 case 'A':
1029 auto_application = true;
1030 break;
1031
1032 case 'd':
1033 args.exit_delay = get_delay(optarg, "delay", MIN_EXIT_DELAY, MAX_EXIT_DELAY);
1034 break;
1035
1036 case 'r':
1037 args.respawn_delay = get_delay(optarg, "respawn delay",
1039 break;
1040
1041 case 's':
1042 args.magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE;
1043 break;
1044
1045 case 'S':
1046 case 'L':
1047 // Removed splash support. Ignore.
1048 break;
1049
1050 case 'F':
1051 args.desktop_file = optarg;
1052 break;
1053
1054 case 'I':
1055 args.sandboxing_id = strdup(optarg);
1056 break;
1057
1058 case '?':
1059 usage(1);
1060 }
1061 }
1062
1063 // Option processing stops as soon as application name is encountered
1064
1065 args.prog_argc = argc - optind;
1066 args.prog_argv = &argv[optind];
1067
1068 if (args.prog_argc < 1) {
1069 report(report_error, "No command line to invoke was given.\n");
1070 exit(EXIT_FAILURE);
1071 }
1072
1073 // Force argv[0] of application to be the absolute path to allow the
1074 // application to find out its installation directory from there
1075 args.prog_argv[0] = search_program(args.prog_argv[0]);
1076
1077 // Check if application exists
1078 struct stat file_stat;
1079 if (stat(args.prog_argv[0], &file_stat) == -1) {
1080 report(report_error, "%s: not found: %m\n", args.prog_argv[0]);
1082 }
1083
1084 // Check that application is regular file (or symlink to such)
1085 if (!S_ISREG(file_stat.st_mode)) {
1086 report(report_error, "%s: not a file\n", args.prog_argv[0]);
1088 }
1089
1090 // If it's a launcher, append its first argument to the name
1091 // (at this point, we have already checked if it exists and is a file)
1092 if (strcmp(args.prog_argv[0], "/usr/bin/sailfish-qml") == 0) {
1093 if (args.prog_argc < 2) {
1094 report(report_error, "%s: requires an argument\n", args.prog_argv[0]);
1096 }
1097
1098 if (asprintf(&args.prog_name, "%s %s", args.prog_argv[0], args.prog_argv[1]) < 0)
1099 exit(EXIT_FAILURE);
1100 } else {
1101 if (!(args.prog_name = strdup(args.prog_argv[0])))
1102 exit(EXIT_FAILURE);
1103 }
1104
1105 if (auto_application)
1106 args.app_name = basename(args.prog_argv[0]);
1107
1108 if (!args.app_type) {
1109 report(report_error, "Application type must be specified with --type.\n");
1110 usage(1);
1111 }
1112
1113 if (!args.app_name) {
1114 report(report_error, "Application name must be specified with --application.\n");
1115 usage(1);
1116 }
1117
1118 // If TEST_MODE_CONTROL_FILE doesn't exists switch off test mode
1119 if (args.test_mode && access(TEST_MODE_CONTROL_FILE, F_OK) != 0) {
1120 args.test_mode = false;
1121 info("Invoker test mode is not enabled.\n");
1122 }
1123
1124 if (pipe(g_signal_pipe) == -1)
1125 {
1126 report(report_error, "Creating a pipe for Unix signals failed!\n");
1127 exit(EXIT_FAILURE);
1128 }
1129
1130 // If sailjail is already used or app specific booster is used, skip checking for sandboxing
1131 if (!strcmp(args.prog_name, SAILJAIL_PATH) || strcmp(args.app_name, UNDEFINED_APPLICATION)) {
1132 args.sandboxing_id = NULL;
1133 } else if (!args.sandboxing_id) {
1134 // When id is not defined, assume it can be derived from binary path
1135 char *path = strdup(args.prog_name);
1136 args.sandboxing_id = strdup(basename(path));
1137 free(path);
1138 }
1139
1140 // Application specific boosters are running in sandbox and can
1141 // thus launch only sandboxed processes, otherwise
1142 // If arguments don't define sailjail and sailjaild says the app must be sandboxed,
1143 // we force sandboxing here
1145 warning("enforcing sandboxing for '%s'", args.prog_name);
1146 // We must use generic booster here as nothing else would work
1147 // to run sailjail which is not compiled for launching via booster
1149 // Prepend sailjail
1150 char **old_argv = args.prog_argv;
1151 args.prog_argc += 4;
1152 args.prog_argv = (char **)calloc(args.prog_argc + 1, sizeof *args.prog_argv);
1153 args.prog_argv[0] = SAILJAIL_PATH;
1154 args.prog_argv[1] = "-p";
1155 args.prog_argv[2] = args.sandboxing_id,
1156 args.sandboxing_id = NULL;
1157 args.prog_argv[3] = "--";
1158 for (int i = 4; i < args.prog_argc + 1; ++i)
1159 args.prog_argv[i] = old_argv[i-4];
1160 // Don't free old_argv because it's probably not dynamically allocated
1161 free(args.prog_name);
1162 args.prog_name = strdup(SAILJAIL_PATH);
1163 }
1164
1165 // Send commands to the launcher daemon
1166 info("Invoking execution: '%s'\n", args.prog_name);
1167 int ret_val = invoke(&args);
1168
1169 // Sleep for delay before exiting
1170 if (args.exit_delay) {
1171 // DBUS cannot cope some times if the invoker exits too early.
1172 info("Delaying exit for %d seconds..\n", args.exit_delay);
1173 sleep(args.exit_delay);
1174 }
1175
1176 info("invoker exit(%d)\n", ret_val);
1177 return ret_val;
1178}
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:74
#define TEST_MODE_CONTROL_FILE
Definition invokelib.h:34
int main(int argc, char *argv[])
Definition invoker.c:942
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:470
#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:839
static void usage(int status)
Definition invoker.c:576
static void invoker_send_magic(int fd, uint32_t options)
Definition invoker.c:457
static void invoker_send_prio(int fd, int prio)
Definition invoker.c:489
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:675
static int invoke_remote(int socket_fd, const InvokeArgs *args)
Definition invoker.c:798
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:651
#define INVOKE_ARGS_INIT
Definition invoker.c:782
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:351
static void invoker_send_env(int fd)
Definition invoker.c:511
static void invoker_send_name(int fd, const char *name)
Definition invoker.c:464
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:530
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:496
static void sig_forwarder(int sig)
Definition invoker.c:159
static void invoker_send_end(int fd)
Definition invoker.c:568
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:683
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:628
#define BOOSTER_SESSION
Definition invoker.c:54
static bool invoker_recv_exit(int fd, int *status)
Definition invoker.c:433
static uint32_t invoker_recv_pid(int fd)
Definition invoker.c:415
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:476
static int invoke(InvokeArgs *args)
Definition invoker.c:870
static void invoker_send_ids(int fd, int uid, int gid)
Definition invoker.c:503
static const unsigned char EXIT_STATUS_APPLICATION_NOT_FOUND
Definition invoker.c:144
char * search_program(const char *progname)
Definition search.c:43
char * prog_name
Definition invoker.c:770
const char * app_name
Definition invoker.c:772
char * sandboxing_id
Definition invoker.c:778
bool wait_term
Definition invoker.c:774
int prog_argc
Definition invoker.c:768
uint32_t magic_options
Definition invoker.c:773
unsigned int respawn_delay
Definition invoker.c:775
bool test_mode
Definition invoker.c:776
const char * desktop_file
Definition invoker.c:777
unsigned int exit_delay
Definition invoker.c:779
const char * app_type
Definition invoker.c:771
char ** prog_argv
Definition invoker.c:769