31#include <sys/socket.h>
35#include <sys/resource.h>
54#define BOOSTER_SESSION "silica-session"
55#define BOOSTER_GENERIC "generic"
60#define UNDEFINED_APPLICATION "default"
66#define VERBOSE_SIGNALS 0
74 while (*src && isspace(*src))
77 while (*src && !isspace(*src))
79 while (*src && isspace(*src))
90static char *
slice(
const char *pos,
const char **ppos,
const char *delim)
94 const char *beg = pos;
95 while (*pos && !strchr(delim, *pos))
97 tok = strndup(beg, pos - beg);
106static char **
split(
const char *str,
const char *delim)
112 for (
const char *pos = str; *pos; ++pos)
113 if (strchr(delim, *pos))
117 arr = calloc(n + 1,
sizeof *arr);
122 char *tok =
slice(str, &str, delim);
162 static const char m[] =
"*** signal\n";
163 if (write(STDERR_FILENO, m,
sizeof m - 1) == -1) {
169 char signal_id = (char) sig;
171 const char m[] =
"*** signal pipe write failure, terminating\n";
172 if (write(STDERR_FILENO, m,
sizeof m - 1) == -1) {
182 sigaction(SIGINT, sig, NULL);
183 sigaction(SIGTERM, sig, NULL);
189 struct sigaction sig;
191 memset(&sig, 0,
sizeof(sig));
192 sig.sa_flags = SA_RESTART;
201 struct sigaction sig;
203 memset(&sig, 0,
sizeof(sig));
204 sig.sa_flags = SA_RESTART;
205 sig.sa_handler = SIG_DFL;
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");
223 return ((
unsigned)(ts.tv_sec * 1000u) +
224 (
unsigned)(ts.tv_nsec / (1000 * 1000u)));
229 bool disconnected =
false;
236 info(
"trying to disconnect booster socket...\n");
238 if (shutdown(socket_fd, SHUT_WR) == -1) {
239 warning(
"socket shutdown failed: %m\n");
244 unsigned timeout = 5000;
246 unsigned elapsed =
timestamp() - started;
247 if (elapsed >= timeout)
250 struct pollfd pfd = {
256 info(
"waiting for booster socket input...\n");
257 int rc = poll(&pfd, 1, (
int)(timeout - elapsed));
263 if (errno == EINTR || errno == EAGAIN)
265 warning(
"socket poll failed: %m\n");
270 rc = recv(socket_fd, buf,
sizeof buf, MSG_DONTWAIT);
278 warning(
"socket read failed: %m\n");
282 warning(
"socket poll timeout\n");
286 info(
"booster socket was succesfully disconnected\n");
288 warning(
"could not disconnect booster socket\n");
296 warning(
"application pid is not known, can't kill it");
300 warning(
"sending SIGTERM to application (pid=%d)", (
int)pid);
302 if (kill(pid, SIGTERM) == -1)
305 for (
int i = 0; i < 10; ++i) {
307 if (kill(pid, 0) == -1)
311 warning(
"sending SIGKILL to application (pid=%d)", (
int)pid);
313 if (kill(pid, SIGKILL) == -1)
316 for (
int i = 0; i < 10; ++i) {
318 if (kill(pid, 0) == -1)
322 warning(
"application (pid=%d) did not exit", (
int)pid);
327 info(
"application (pid=%d) has exited", (
int)pid);
329 warning(
"application (pid=%d) kill failed: %m", (
int)pid);
342 if (action != INVOKER_MSG_ACK) {
343 die(1,
"Received wrong ack (%08x)\n", action);
352 info(
"try type=%s app=%s ...", app_type, app_name);
354 bool connected =
false;
358 if (!app_type || strchr(app_type,
'/'))
360 if (app_name && strchr(app_name,
'/'))
363 const char *runtimeDir = getenv(
"XDG_RUNTIME_DIR");
364 if (!runtimeDir || !*runtimeDir) {
365 error(
"XDG_RUNTIME_DIR is not defined.\n");
369 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
370 error(
"Failed to create socket: %m\n");
374 struct sockaddr_un sun = {
375 .sun_family = AF_UNIX,
377 int maxSize =
sizeof(sun.sun_path);
381 length = snprintf(sun.sun_path, maxSize,
"%s/mapplauncherd/_%s/%s/socket",
382 runtimeDir, app_name, app_type);
384 length = snprintf(sun.sun_path, maxSize,
"%s/mapplauncherd/%s",
385 runtimeDir, app_type);
387 if (length <= 0 || length >= maxSize) {
389 error(
"Invalid booster type: %s / application: %s\n",
392 error(
"Invalid booster type: %s\n", app_type);
396 if (connect(fd, (
struct sockaddr *)&sun,
sizeof(sun)) == -1) {
398 warning(
"connect(\"%s\") failed: %m\n", sun.sun_path);
402 info(
"connected to: %s\n", sun.sun_path);
406 if (!connected && fd != -1)
419 if (action != INVOKER_MSG_PID)
420 die(1,
"Received a bad message id (%08x)\n", action);
426 die(1,
"Received a zero pid \n");
439 if (!res || (action != INVOKER_MSG_EXIT)) {
458 invoke_send_msg(fd, INVOKER_MSG_MAGIC | INVOKER_MSG_MAGIC_VERSION | options);
480 for (i = 0; i < argc; i++) {
481 info(
"param %d %s \n", i, argv[i]);
513 for (n_vars = 0;
environ[n_vars] != NULL; n_vars++) ;
518 for (i = 0; i < n_vars; i++) {
529 struct cmsghdr *cmsg = NULL;
530 int io[3] = { 0, 1, 2 };
531 char buf[CMSG_SPACE(
sizeof(io))];
535 memset(&msg, 0,
sizeof(
struct msghdr));
537 iov.iov_base = &dummy;
542 msg.msg_control = buf;
543 msg.msg_controllen =
sizeof(buf);
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;
550 memcpy(CMSG_DATA(cmsg), io,
sizeof(io));
552 msg.msg_controllen = cmsg->cmsg_len;
555 if (sendmsg(fd, &msg, 0) < 0) {
556 warning(
"sendmsg failed in invoker_send_io: %s \n", strerror(errno));
574 "Usage: %s [options] [--type=TYPE] [file] [args]\n"
576 "Launch applications compiled as a shared library (-shared) or\n"
577 "a position independent executable (-pie) through mapplauncherd.\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"
586 "The TYPE may also be a comma delimited list of boosters to try. The first available\n"
587 "booster will be used.\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"
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"
615 "Example: %s --type=qt5 /usr/bin/helloworld\n"
623static unsigned int get_delay(
char *delay_arg,
char *param_name,
624 unsigned int min_value,
unsigned int max_value)
630 delay = strtoul(delay_arg, NULL, 10);
635 || delay > max_value) {
636 report(report_error,
"Wrong value of %s parameter: %s\n", param_name, delay_arg);
646 DBusConnection *connection;
647 DBusMessage *message;
650 dbus_error_init (&error);
651 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
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);
658 dbus_connection_send(connection, message, NULL);
659 dbus_message_unref(message);
660 dbus_connection_flush(connection);
662 info(
"Failed to connect to the DBus session bus: %s", error.message);
663 dbus_error_free(&error);
670 char *path = strdup(app);
671 bool ret_val = sailjail_sandbox(basename(path));
678 int exit_status = EXIT_FAILURE;
695 FD_SET(socket_fd, &readfds);
696 ndfs = (socket_fd > ndfs) ? socket_fd : ndfs;
704 if (select(ndfs + 1, &readfds, NULL, NULL, NULL) == -1) {
705 if (errno == EINTR || errno == EAGAIN)
707 warning(
"socket select failed: %m\n");
712 if (FD_ISSET(socket_fd, &readfds)) {
715 exit_status = EXIT_FAILURE;
728 error(
"signal pipe read failure, terminating\n");
731 exit_signal = signal_id;
732 if (exit_signal == SIGTERM)
733 exit_status = EXIT_SUCCESS;
741 if (exit_status != EXIT_SUCCESS)
742 warning(
"application (pid=%d) exit(%d) signal(%d)\n",
745 info(
"application (pid=%d) exit(%d) signal(%d)\n",
748 if (socket_fd != -1) {
775#define INVOKE_ARGS_INIT {\
780 .app_name = UNDEFINED_APPLICATION,\
781 .magic_options = INVOKER_MSG_MAGIC_OPTION_WAIT,\
783 .respawn_delay = RESPAWN_DELAY,\
785 .desktop_file = NULL,\
786 .sandboxing_id = NULL,\
787 .exit_delay = EXIT_DELAY,\
793 int exit_status = EXIT_FAILURE;
797 int prog_prio = getpriority(PRIO_PROCESS, 0);
798 if (errno && prog_prio < 0) {
822 exit_status = EXIT_SUCCESS;
835 warning(
"Connection with launcher process is broken. \n");
836 error(
"Start application %s as a binary executable without launcher...\n", args->
prog_name);
841 pid_t newPid = fork();
844 error(
"Invoker failed to fork. \n");
846 }
else if (newPid != 0) {
864 int status = EXIT_FAILURE;
880 bool tried_session =
false;
881 for (
size_t i = 0; !tried_session && types[i]; ++i) {
884 tried_session =
true;
892 if (fd == -1 && !tried_session) {
893 bool tried_generic =
false;
894 for (
size_t i = 0; fd == -1 && types[i]; ++i) {
896 tried_generic =
true;
899 if (fd == -1 && !tried_generic)
907 }
else if (tried_session) {
908 warning(
"Launch failed, session booster is not available.\n");
914 warning(
"Launch failed, application specific booster is not available.\n");
917 warning(
"Also fallback boosters failed, launch without boosting.\n");
920 status = EXIT_SUCCESS;
923 for (
int i = 0; types[i]; ++i)
930int main(
int argc,
char *argv[])
933 bool auto_application =
false;
935 if (!strstr(argv[0], PROG_NAME_INVOKER)) {
937 "Incorrect use of invoker, don't use symlinks. "
938 "Run invoker explicitly from e.g. a D-Bus service file instead.\n");
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'},
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'},
958 {
"splash-landscape", required_argument, NULL,
'L'},
959 {
"desktop-file", required_argument, NULL,
'F'},
960 {
"id", required_argument, NULL,
'I'},
961 {
"verbose", no_argument, NULL,
'v'},
969 while ((opt = getopt_long(argc, argv,
"+hvcwnGDsoTd:t:a:Ar:S:L:F:I:", longopts, NULL)) != -1) {
976 report_set_type(report_get_type() + 1);
984 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE;
993 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL;
1010 auto_application =
false;
1014 auto_application =
true;
1027 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE;
1054 report(report_error,
"No command line to invoke was given.\n");
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]);
1070 if (!S_ISREG(file_stat.st_mode)) {
1071 report(report_error,
"%s: not a file\n", args.
prog_argv[0]);
1077 if (strcmp(args.
prog_argv[0],
"/usr/bin/sailfish-qml") == 0) {
1079 report(report_error,
"%s: requires an argument\n", args.
prog_argv[0]);
1090 if (auto_application)
1094 report(report_error,
"Application type must be specified with --type.\n");
1099 report(report_error,
"Application name must be specified with --application.\n");
1106 info(
"Invoker test mode is not enabled.\n");
1110 report(report_error,
"Creating a pipe for Unix signals failed!\n");
1129 warning(
"enforcing sandboxing for '%s'", args.
prog_name);
1142 for (
int i = 4; i < args.
prog_argc + 1; ++i)
1150 info(
"Invoking execution: '%s'\n", args.
prog_name);
1151 int ret_val =
invoke(&args);
1156 info(
"Delaying exit for %d seconds..\n", args.
exit_delay);
1160 info(
"invoker exit(%d)\n", ret_val);
void invoke_send_msg(int fd, uint32_t msg)
bool invoke_recv_msg(int fd, uint32_t *msg)
void invoke_send_str(int fd, const char *str)
#define TEST_MODE_CONTROL_FILE
int main(int argc, char *argv[])
static void kill_application(pid_t pid)
static char * slice(const char *pos, const char **ppos, const char *delim)
static void invoker_send_exec(int fd, char *exec)
#define UNDEFINED_APPLICATION
static char ** split(const char *str, const char *delim)
static void invoke_fallback(const InvokeArgs *args)
static void usage(int status)
static void invoker_send_magic(int fd, uint32_t options)
static void invoker_send_prio(int fd, int prio)
static const unsigned int RESPAWN_DELAY
static void sigs_set(struct sigaction *sig)
static bool ask_for_sandboxing(const char *app)
static int invoke_remote(int socket_fd, const InvokeArgs *args)
static const unsigned int MIN_EXIT_DELAY
static void sigs_init(void)
static void notify_app_launch(const char *desktop_file)
static int g_signal_pipe[2]
Pipe used to safely catch Unix signals.
static int invoker_init(const char *app_type, const char *app_name)
static void invoker_send_env(int fd)
static void invoker_send_name(int fd, const char *name)
static const unsigned int MAX_RESPAWN_DELAY
static pid_t g_invoked_pid
static void invoker_send_io(int fd)
static const unsigned int MAX_EXIT_DELAY
static const unsigned int EXIT_DELAY
static void invoker_send_delay(int fd, int delay)
static void sig_forwarder(int sig)
static void invoker_send_end(int fd)
static bool shutdown_socket(int socket_fd)
static int wait_for_launched_process_to_exit(int socket_fd)
static unsigned timestamp(void)
static unsigned int get_delay(char *delay_arg, char *param_name, unsigned int min_value, unsigned int max_value)
static bool invoker_recv_exit(int fd, int *status)
static uint32_t invoker_recv_pid(int fd)
static const unsigned int MIN_RESPAWN_DELAY
static char * strip(char *str)
static void sigs_restore(void)
static bool invoke_recv_ack(int fd)
static void invoker_send_args(int fd, int argc, char **argv)
static int invoke(InvokeArgs *args)
static void invoker_send_ids(int fd, int uid, int gid)
static const unsigned char EXIT_STATUS_APPLICATION_NOT_FOUND
char * search_program(const char *progname)
unsigned int respawn_delay
const char * desktop_file