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)
344 die(1,
"Received wrong ack (%08x)\n", action);
353 info(
"try type=%s app=%s ...", app_type, app_name);
355 bool connected =
false;
359 if (!app_type || strchr(app_type,
'/'))
361 if (app_name && strchr(app_name,
'/'))
364 const char *runtimeDir = getenv(
"XDG_RUNTIME_DIR");
365 if (!runtimeDir || !*runtimeDir) {
366 error(
"XDG_RUNTIME_DIR is not defined.\n");
370 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
371 error(
"Failed to create socket: %m\n");
375 struct sockaddr_un sun = {
376 .sun_family = AF_UNIX,
378 int maxSize =
sizeof(sun.sun_path);
382 length = snprintf(sun.sun_path, maxSize,
"%s/mapplauncherd/_%s/%s/socket",
383 runtimeDir, app_name, app_type);
385 length = snprintf(sun.sun_path, maxSize,
"%s/mapplauncherd/%s",
386 runtimeDir, app_type);
388 if (length <= 0 || length >= maxSize) {
390 error(
"Invalid booster type: %s / application: %s\n",
393 error(
"Invalid booster type: %s\n", app_type);
397 if (connect(fd, (
struct sockaddr *)&sun,
sizeof(sun)) == -1) {
399 warning(
"connect(\"%s\") failed: %m\n", sun.sun_path);
403 info(
"connected to: %s\n", sun.sun_path);
407 if (!connected && fd != -1)
420 if (action != INVOKER_MSG_PID)
421 die(1,
"Received a bad message id (%08x)\n", action);
427 die(1,
"Received a zero pid \n");
440 if (!res || (action != INVOKER_MSG_EXIT))
460 invoke_send_msg(fd, INVOKER_MSG_MAGIC | INVOKER_MSG_MAGIC_VERSION | options);
482 for (i = 0; i < argc; i++)
484 info(
"param %d %s \n", i, argv[i]);
516 for (n_vars = 0;
environ[n_vars] != NULL; n_vars++) ;
521 for (i = 0; i < n_vars; i++)
533 struct cmsghdr *cmsg = NULL;
534 int io[3] = { 0, 1, 2 };
535 char buf[CMSG_SPACE(
sizeof(io))];
539 memset(&msg, 0,
sizeof(
struct msghdr));
541 iov.iov_base = &dummy;
546 msg.msg_control = buf;
547 msg.msg_controllen =
sizeof(buf);
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;
554 memcpy(CMSG_DATA(cmsg), io,
sizeof(io));
556 msg.msg_controllen = cmsg->cmsg_len;
559 if (sendmsg(fd, &msg, 0) < 0)
561 warning(
"sendmsg failed in invoker_send_io: %s \n", strerror(errno));
579 "Usage: %s [options] [--type=TYPE] [file] [args]\n"
581 "Launch applications compiled as a shared library (-shared) or\n"
582 "a position independent executable (-pie) through mapplauncherd.\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"
591 "The TYPE may also be a comma delimited list of boosters to try. The first available\n"
592 "booster will be used.\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"
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"
620 "Example: %s --type=qt5 /usr/bin/helloworld\n"
628static unsigned int get_delay(
char *delay_arg,
char *param_name,
629 unsigned int min_value,
unsigned int max_value)
636 delay = strtoul(delay_arg, NULL, 10);
641 || delay > max_value)
643 report(report_error,
"Wrong value of %s parameter: %s\n", param_name, delay_arg);
653 DBusConnection *connection;
654 DBusMessage *message;
657 dbus_error_init (&error);
658 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
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);
665 dbus_connection_send(connection, message, NULL);
666 dbus_message_unref(message);
667 dbus_connection_flush(connection);
669 info(
"Failed to connect to the DBus session bus: %s", error.message);
670 dbus_error_free(&error);
677 char *path = strdup(app);
678 bool ret_val = sailjail_sandbox(basename(path));
685 int exit_status = EXIT_FAILURE;
702 FD_SET(socket_fd, &readfds);
703 ndfs = (socket_fd > ndfs) ? socket_fd : ndfs;
711 if (select(ndfs + 1, &readfds, NULL, NULL, NULL) == -1) {
712 if (errno == EINTR || errno == EAGAIN)
714 warning(
"socket select failed: %m\n");
719 if (FD_ISSET(socket_fd, &readfds)) {
722 exit_status = EXIT_FAILURE;
735 error(
"signal pipe read failure, terminating\n");
738 exit_signal = signal_id;
739 if (exit_signal == SIGTERM)
740 exit_status = EXIT_SUCCESS;
748 if (exit_status != EXIT_SUCCESS)
749 warning(
"application (pid=%d) exit(%d) signal(%d)\n",
752 info(
"application (pid=%d) exit(%d) signal(%d)\n",
755 if (socket_fd != -1) {
782#define INVOKE_ARGS_INIT {\
787 .app_name = UNDEFINED_APPLICATION,\
788 .magic_options = INVOKER_MSG_MAGIC_OPTION_WAIT,\
790 .respawn_delay = RESPAWN_DELAY,\
792 .desktop_file = NULL,\
793 .sandboxing_id = NULL,\
794 .exit_delay = EXIT_DELAY,\
800 int exit_status = EXIT_FAILURE;
804 int prog_prio = getpriority(PRIO_PROCESS, 0);
805 if (errno && prog_prio < 0)
830 exit_status = EXIT_SUCCESS;
843 warning(
"Connection with launcher process is broken. \n");
844 error(
"Start application %s as a binary executable without launcher...\n", args->
prog_name);
850 pid_t newPid = fork();
854 error(
"Invoker failed to fork. \n");
857 else if (newPid != 0)
876 int status = EXIT_FAILURE;
892 bool tried_session =
false;
893 for (
size_t i = 0; !tried_session && types[i]; ++i) {
896 tried_session =
true;
904 if (fd == -1 && !tried_session) {
905 bool tried_generic =
false;
906 for (
size_t i = 0; fd == -1 && types[i]; ++i) {
908 tried_generic =
true;
911 if (fd == -1 && !tried_generic)
919 }
else if (tried_session) {
920 warning(
"Launch failed, session booster is not available.\n");
926 warning(
"Launch failed, application specific booster is not available.\n");
929 warning(
"Also fallback boosters failed, launch without boosting.\n");
932 status = EXIT_SUCCESS;
935 for (
int i = 0; types[i]; ++i)
942int main(
int argc,
char *argv[])
945 bool auto_application =
false;
947 if (!strstr(argv[0], PROG_NAME_INVOKER) )
950 "Incorrect use of invoker, don't use symlinks. "
951 "Run invoker explicitly from e.g. a D-Bus service file instead.\n");
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'},
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'},
971 {
"splash-landscape", required_argument, NULL,
'L'},
972 {
"desktop-file", required_argument, NULL,
'F'},
973 {
"id", required_argument, NULL,
'I'},
974 {
"verbose", no_argument, NULL,
'v'},
982 while ((opt = getopt_long(argc, argv,
"+hvcwnGDsoTd:t:a:Ar:S:L:F:I:", longopts, NULL)) != -1)
991 report_set_type(report_get_type() + 1);
999 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE;
1008 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL;
1025 auto_application =
false;
1029 auto_application =
true;
1042 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE;
1069 report(report_error,
"No command line to invoke was given.\n");
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]);
1085 if (!S_ISREG(file_stat.st_mode)) {
1086 report(report_error,
"%s: not a file\n", args.
prog_argv[0]);
1092 if (strcmp(args.
prog_argv[0],
"/usr/bin/sailfish-qml") == 0) {
1094 report(report_error,
"%s: requires an argument\n", args.
prog_argv[0]);
1105 if (auto_application)
1109 report(report_error,
"Application type must be specified with --type.\n");
1114 report(report_error,
"Application name must be specified with --application.\n");
1121 info(
"Invoker test mode is not enabled.\n");
1126 report(report_error,
"Creating a pipe for Unix signals failed!\n");
1145 warning(
"enforcing sandboxing for '%s'", args.
prog_name);
1158 for (
int i = 4; i < args.
prog_argc + 1; ++i)
1166 info(
"Invoking execution: '%s'\n", args.
prog_name);
1167 int ret_val =
invoke(&args);
1172 info(
"Delaying exit for %d seconds..\n", args.
exit_delay);
1176 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