31#include <sys/socket.h>
35#include <sys/resource.h>
53#define BOOSTER_SESSION "silica-session"
54#define BOOSTER_GENERIC "generic"
59#define UNDEFINED_APPLICATION "default"
65#define VERBOSE_SIGNALS 0
73 while (*src && isspace(*src))
76 while (*src && !isspace(*src))
78 while (*src && isspace(*src))
89static char *
slice(
const char *pos,
const char **ppos,
const char *delim)
93 const char *beg = pos;
94 while (*pos && !strchr(delim, *pos))
96 tok = strndup(beg, pos - beg);
105static char **
split(
const char *str,
const char *delim)
111 for (
const char *pos = str; *pos; ++pos)
112 if (strchr(delim, *pos))
116 arr = calloc(n + 1,
sizeof *arr);
121 char *tok =
slice(str, &str, delim);
161 static const char m[] =
"*** signal\n";
162 if (write(STDERR_FILENO, m,
sizeof m - 1) == -1) {
168 char signal_id = (char) sig;
170 const char m[] =
"*** signal pipe write failure, terminating\n";
171 if (write(STDERR_FILENO, m,
sizeof m - 1) == -1) {
181 sigaction(SIGINT, sig, NULL);
182 sigaction(SIGTERM, sig, NULL);
188 struct sigaction sig;
190 memset(&sig, 0,
sizeof(sig));
191 sig.sa_flags = SA_RESTART;
200 struct sigaction sig;
202 memset(&sig, 0,
sizeof(sig));
203 sig.sa_flags = SA_RESTART;
204 sig.sa_handler = SIG_DFL;
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");
222 return ((
unsigned)(ts.tv_sec * 1000u) +
223 (
unsigned)(ts.tv_nsec / (1000 * 1000u)));
228 bool disconnected =
false;
235 info(
"trying to disconnect booster socket...\n");
237 if (shutdown(socket_fd, SHUT_WR) == -1) {
238 warning(
"socket shutdown failed: %m\n");
243 unsigned timeout = 5000;
245 unsigned elapsed =
timestamp() - started;
246 if (elapsed >= timeout)
249 struct pollfd pfd = {
255 info(
"waiting for booster socket input...\n");
256 int rc = poll(&pfd, 1, (
int)(timeout - elapsed));
262 if (errno == EINTR || errno == EAGAIN)
264 warning(
"socket poll failed: %m\n");
269 rc = recv(socket_fd, buf,
sizeof buf, MSG_DONTWAIT);
277 warning(
"socket read failed: %m\n");
281 warning(
"socket poll timeout\n");
285 info(
"booster socket was succesfully disconnected\n");
287 warning(
"could not disconnect booster socket\n");
295 warning(
"application pid is not known, can't kill it");
299 warning(
"sending SIGTERM to application (pid=%d)", (
int)pid);
301 if (kill(pid, SIGTERM) == -1)
304 for (
int i = 0; i < 10; ++i) {
306 if (kill(pid, 0) == -1)
310 warning(
"sending SIGKILL to application (pid=%d)", (
int)pid);
312 if (kill(pid, SIGKILL) == -1)
315 for (
int i = 0; i < 10; ++i) {
317 if (kill(pid, 0) == -1)
321 warning(
"application (pid=%d) did not exit", (
int)pid);
326 info(
"application (pid=%d) has exited", (
int)pid);
328 warning(
"application (pid=%d) kill failed: %m", (
int)pid);
341 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))
459 invoke_send_msg(fd, INVOKER_MSG_MAGIC | INVOKER_MSG_MAGIC_VERSION | options);
481 for (i = 0; i < argc; i++)
483 info(
"param %d %s \n", i, argv[i]);
515 for (n_vars = 0;
environ[n_vars] != NULL; n_vars++) ;
520 for (i = 0; i < n_vars; i++)
532 struct cmsghdr *cmsg = NULL;
533 int io[3] = { 0, 1, 2 };
534 char buf[CMSG_SPACE(
sizeof(io))];
538 memset(&msg, 0,
sizeof(
struct msghdr));
540 iov.iov_base = &dummy;
545 msg.msg_control = buf;
546 msg.msg_controllen =
sizeof(buf);
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;
553 memcpy(CMSG_DATA(cmsg), io,
sizeof(io));
555 msg.msg_controllen = cmsg->cmsg_len;
558 if (sendmsg(fd, &msg, 0) < 0)
560 warning(
"sendmsg failed in invoker_send_io: %s \n", strerror(errno));
578 "Usage: %s [options] [--type=TYPE] [file] [args]\n"
580 "Launch applications compiled as a shared library (-shared) or\n"
581 "a position independent executable (-pie) through mapplauncherd.\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"
590 "The TYPE may also be a comma delimited list of boosters to try. The first available\n"
591 "booster will be used.\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"
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"
619 "Example: %s --type=qt5 /usr/bin/helloworld\n"
627static unsigned int get_delay(
char *delay_arg,
char *param_name,
628 unsigned int min_value,
unsigned int max_value)
635 delay = strtoul(delay_arg, NULL, 10);
638 if ((errno == ERANGE && delay == ULONG_MAX)
640 || delay > max_value)
642 report(report_error,
"Wrong value of %s parameter: %s\n", param_name, delay_arg);
652 DBusConnection *connection;
653 DBusMessage *message;
656 dbus_error_init (&error);
657 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
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);
664 dbus_connection_send(connection, message, NULL);
665 dbus_message_unref(message);
666 dbus_connection_flush(connection);
668 info(
"Failed to connect to the DBus session bus: %s", error.message);
669 dbus_error_free(&error);
676 char *path = strdup(app);
677 bool ret_val = sailjail_sandbox(basename(path));
684 int exit_status = EXIT_FAILURE;
701 FD_SET(socket_fd, &readfds);
702 ndfs = (socket_fd > ndfs) ? socket_fd : ndfs;
710 if (select(ndfs + 1, &readfds, NULL, NULL, NULL) == -1) {
711 if (errno == EINTR || errno == EAGAIN)
713 warning(
"socket select failed: %m\n");
718 if (FD_ISSET(socket_fd, &readfds)) {
721 exit_status = EXIT_FAILURE;
734 error(
"signal pipe read failure, terminating\n");
737 exit_signal = signal_id;
738 if (exit_signal == SIGTERM)
739 exit_status = EXIT_SUCCESS;
747 if (exit_status != EXIT_SUCCESS)
748 warning(
"application (pid=%d) exit(%d) signal(%d)\n",
751 info(
"application (pid=%d) exit(%d) signal(%d)\n",
754 if (socket_fd != -1) {
781#define INVOKE_ARGS_INIT {\
786 .app_name = UNDEFINED_APPLICATION,\
787 .magic_options = INVOKER_MSG_MAGIC_OPTION_WAIT,\
789 .respawn_delay = RESPAWN_DELAY,\
791 .desktop_file = NULL,\
792 .sandboxing_id = NULL,\
793 .exit_delay = EXIT_DELAY,\
799 int exit_status = EXIT_FAILURE;
803 int prog_prio = getpriority(PRIO_PROCESS, 0);
804 if (errno && prog_prio < 0)
840 warning(
"Connection with launcher process is broken. \n");
841 error(
"Start application %s as a binary executable without launcher...\n", args->
prog_name);
847 pid_t newPid = fork();
851 error(
"Invoker failed to fork. \n");
854 else if (newPid != 0)
873 int status = EXIT_FAILURE;
889 bool tried_session =
false;
890 for (
size_t i = 0; !tried_session && types[i]; ++i) {
893 tried_session =
true;
901 if (fd == -1 && !tried_session) {
902 bool tried_generic =
false;
903 for (
size_t i = 0; fd == -1 && types[i]; ++i) {
905 tried_generic =
true;
908 if (fd == -1 && !tried_generic)
916 }
else if (tried_session) {
917 warning(
"Launch failed, session booster is not available.\n");
923 warning(
"Launch failed, application specific booster is not available.\n");
926 warning(
"Also fallback boosters failed, launch without boosting.\n");
929 status = EXIT_SUCCESS;
932 for (
int i = 0; types[i]; ++i)
939int main(
int argc,
char *argv[])
942 bool auto_application =
false;
944 if (!strstr(argv[0], PROG_NAME_INVOKER) )
947 "Incorrect use of invoker, don't use symlinks. "
948 "Run invoker explicitly from e.g. a D-Bus service file instead.\n");
952 struct option longopts[] = {
953 {
"help", no_argument, NULL,
'h'},
954 {
"wait-term", no_argument, NULL,
'w'},
955 {
"no-wait", no_argument, NULL,
'n'},
956 {
"global-syms", no_argument, NULL,
'G'},
957 {
"deep-syms", no_argument, NULL,
'D'},
958 {
"single-instance", no_argument, NULL,
's'},
959 {
"keep-oom-score", no_argument, NULL,
'o'},
960 {
"daemon-mode", no_argument, NULL,
'o'},
961 {
"test-mode", no_argument, NULL,
'T'},
962 {
"type", required_argument, NULL,
't'},
963 {
"application", required_argument, NULL,
'a'},
964 {
"auto-application", no_argument, NULL,
'A'},
965 {
"delay", required_argument, NULL,
'd'},
966 {
"respawn", required_argument, NULL,
'r'},
967 {
"splash", required_argument, NULL,
'S'},
968 {
"splash-landscape", required_argument, NULL,
'L'},
969 {
"desktop-file", required_argument, NULL,
'F'},
970 {
"id", required_argument, NULL,
'I'},
971 {
"verbose", no_argument, NULL,
'v'},
979 while ((opt = getopt_long(argc, argv,
"+hvcwnGDsoTd:t:a:Ar:S:L:F:I:", longopts, NULL)) != -1)
988 report_set_type(report_get_type() + 1);
996 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE;
1005 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL;
1022 auto_application =
false;
1026 auto_application =
true;
1039 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE;
1066 report(report_error,
"No command line to invoke was given.\n");
1075 struct stat file_stat;
1076 if (stat(args.
prog_argv[0], &file_stat) == -1) {
1077 report(report_error,
"%s: not found: %m\n", args.
prog_argv[0]);
1082 if (!S_ISREG(file_stat.st_mode)) {
1083 report(report_error,
"%s: not a file\n", args.
prog_argv[0]);
1089 if (strcmp(args.
prog_argv[0],
"/usr/bin/sailfish-qml") == 0) {
1091 report(report_error,
"%s: requires an argument\n", args.
prog_argv[0]);
1102 if (auto_application)
1106 report(report_error,
"Application type must be specified with --type.\n");
1111 report(report_error,
"Application name must be specified with --application.\n");
1118 info(
"Invoker test mode is not enabled.\n");
1123 report(report_error,
"Creating a pipe for Unix signals failed!\n");
1142 warning(
"enforcing sandboxing for '%s'", args.
prog_name);
1155 for (
int i = 4; i < args.
prog_argc + 1; ++i)
1163 info(
"Invoking execution: '%s'\n", args.
prog_name);
1164 int ret_val =
invoke(&args);
1169 info(
"Delaying exit for %d seconds..\n", args.
exit_delay);
1173 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)
struct InvokeArgs InvokeArgs
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