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