31 #include <sys/socket.h>
35 #include <sys/resource.h>
45 #include <dbus/dbus.h>
52 #define BOOSTER_SESSION "silica-session"
53 #define BOOSTER_GENERIC "generic"
59 #define VERBOSE_SIGNALS 01
67 while (*src && isspace(*src))
70 while (*src && !isspace(*src))
72 while (*src && isspace(*src))
83 static char *
slice(
const char *pos,
const char **ppos,
const char *delim)
87 const char *beg = pos;
88 while (*pos && !strchr(delim, *pos))
90 tok = strndup(beg, pos - beg);
99 static char **
split(
const char *str,
const char *delim)
105 for (
const char *pos = str; *pos; ++pos)
106 if (strchr(delim, *pos))
110 arr = calloc(n + 1,
sizeof *arr);
115 char *tok =
slice(str, &str, delim);
155 static const char m[] =
"*** signal\n";
156 if (write(STDERR_FILENO, m,
sizeof m - 1) == -1) {
162 char signal_id = (char) sig;
164 const char m[] =
"*** signal pipe write failure, terminating\n";
165 if (write(STDERR_FILENO, m,
sizeof m - 1) == -1) {
175 sigaction(SIGINT, sig, NULL);
176 sigaction(SIGTERM, sig, NULL);
182 struct sigaction sig;
184 memset(&sig, 0,
sizeof(sig));
185 sig.sa_flags = SA_RESTART;
194 struct sigaction sig;
196 memset(&sig, 0,
sizeof(sig));
197 sig.sa_flags = SA_RESTART;
198 sig.sa_handler = SIG_DFL;
205 struct timespec ts = { 0, 0 };
206 if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1 &&
207 clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
208 error(
"can't obtain a monotonic timestamp\n");
216 return ((
unsigned)(ts.tv_sec * 1000u) +
217 (
unsigned)(ts.tv_nsec / (1000 * 1000u)));
222 bool disconnected =
false;
229 info(
"trying to disconnect booster socket...\n");
231 if (shutdown(socket_fd, SHUT_WR) == -1) {
232 warning(
"socket shutdown failed: %m\n");
237 unsigned timeout = 5000;
239 unsigned elapsed =
timestamp() - started;
240 if (elapsed >= timeout)
243 struct pollfd pfd = {
249 info(
"waiting for booster socket input...\n");
250 int rc = poll(&pfd, 1, (
int)(timeout - elapsed));
256 if (errno == EINTR || errno == EAGAIN)
258 warning(
"socket poll failed: %m\n");
263 rc = recv(socket_fd, buf,
sizeof buf, MSG_DONTWAIT);
271 warning(
"socket read failed: %m\n");
275 warning(
"socket poll timeout\n");
279 info(
"booster socket was succesfully disconnected\n");
281 warning(
"could not disconnect booster socket\n");
289 warning(
"application pid is not known, can't kill it");
293 warning(
"sending SIGTERM to application (pid=%d)", (
int)pid);
295 if (kill(pid, SIGTERM) == -1)
298 for (
int i = 0; i < 10; ++i) {
300 if (kill(pid, 0) == -1)
304 warning(
"sending SIGKILL to application (pid=%d)", (
int)pid);
306 if (kill(pid, SIGKILL) == -1)
309 for (
int i = 0; i < 10; ++i) {
311 if (kill(pid, 0) == -1)
315 warning(
"application (pid=%d) did not exit", (
int)pid);
320 info(
"application (pid=%d) has exited", (
int)pid);
322 warning(
"application (pid=%d) kill failed: %m", (
int)pid);
335 if (action != INVOKER_MSG_ACK)
337 die(1,
"Received wrong ack (%08x)\n", action);
346 info(
"try type=%s app=%s ...", app_type, app_name);
348 bool connected =
false;
352 if (!app_type || strchr(app_type,
'/'))
354 if (app_name && strchr(app_name,
'/'))
357 const char *runtimeDir = getenv(
"XDG_RUNTIME_DIR");
358 if (!runtimeDir || !*runtimeDir) {
359 error(
"XDG_RUNTIME_DIR is not defined.\n");
363 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
364 error(
"Failed to create socket: %m\n");
368 struct sockaddr_un sun = {
369 .sun_family = AF_UNIX,
371 int maxSize =
sizeof(sun.sun_path);
375 length = snprintf(sun.sun_path, maxSize,
"%s/mapplauncherd/_%s/%s/socket",
376 runtimeDir, app_name, app_type);
378 length = snprintf(sun.sun_path, maxSize,
"%s/mapplauncherd/%s",
379 runtimeDir, app_type);
381 if (length <= 0 || length >= maxSize) {
383 error(
"Invalid booster type: %s / application: %s\n",
386 error(
"Invalid booster type: %s\n", app_type);
390 if (connect(fd, (
struct sockaddr *)&sun,
sizeof(sun)) == -1) {
392 warning(
"connect(\"%s\") failed: %m\n", sun.sun_path);
396 info(
"connected to: %s\n", sun.sun_path);
400 if (!connected && fd != -1)
413 if (action != INVOKER_MSG_PID)
414 die(1,
"Received a bad message id (%08x)\n", action);
420 die(1,
"Received a zero pid \n");
433 if (!res || (action != INVOKER_MSG_EXIT))
453 invoke_send_msg(fd, INVOKER_MSG_MAGIC | INVOKER_MSG_MAGIC_VERSION | options);
475 for (i = 0; i < argc; i++)
477 info(
"param %d %s \n", i, argv[i]);
509 for (n_vars = 0;
environ[n_vars] != NULL; n_vars++) ;
514 for (i = 0; i < n_vars; i++)
526 struct cmsghdr *cmsg = NULL;
527 int io[3] = { 0, 1, 2 };
528 char buf[CMSG_SPACE(
sizeof(io))];
532 memset(&msg, 0,
sizeof(
struct msghdr));
534 iov.iov_base = &dummy;
539 msg.msg_control = buf;
540 msg.msg_controllen =
sizeof(buf);
542 cmsg = CMSG_FIRSTHDR(&msg);
543 cmsg->cmsg_len = CMSG_LEN(
sizeof(io));
544 cmsg->cmsg_level = SOL_SOCKET;
545 cmsg->cmsg_type = SCM_RIGHTS;
547 memcpy(CMSG_DATA(cmsg), io,
sizeof(io));
549 msg.msg_controllen = cmsg->cmsg_len;
552 if (sendmsg(fd, &msg, 0) < 0)
554 warning(
"sendmsg failed in invoker_send_io: %s \n", strerror(errno));
571 printf(
"\nUsage: %s [options] [--type=TYPE] [file] [args]\n\n"
572 "Launch applications compiled as a shared library (-shared) or\n"
573 "a position independent executable (-pie) through mapplauncherd.\n\n"
574 "TYPE chooses the type of booster used. Qt-booster may be used to\n"
575 "launch anything. Possible values for TYPE:\n"
576 " qt5 Launch a Qt 5 application.\n"
577 " qtquick2 Launch a Qt Quick 2 (QML) application.\n"
578 " silica-qt5 Launch a Sailfish Silica application.\n"
579 " generic Launch any application, even if it's not a library.\n\n"
580 "The TYPE may also be a comma delimited list of boosters to try. The first available\n"
581 "booster will be used.\n\n"
583 " -d, --delay SECS After invoking sleep for SECS seconds\n"
585 " -r, --respawn SECS After invoking respawn new booster after SECS seconds\n"
586 " (default %d, max %d).\n"
587 " -w, --wait-term Wait for launched process to terminate (default).\n"
588 " -n, --no-wait Do not wait for launched process to terminate.\n"
589 " -G, --global-syms Places symbols in the application binary and its\n"
590 " libraries to the global scope.\n"
591 " See RTLD_GLOBAL in the dlopen manual page.\n"
592 " -s, --single-instance Launch the application as a single instance.\n"
593 " The existing application window will be activated\n"
594 " if already launched.\n"
595 " -o, --keep-oom-score Notify invoker that the launched process should inherit oom_score_adj\n"
596 " from the booster. The score is reset to 0 normally.\n"
597 " -T, --test-mode Invoker test mode. Also control file in root home should be in place.\n"
598 " -F, --desktop-file Desktop file of the application to notify lipstick of launching app.\n"
599 " -h, --help Print this help.\n\n"
600 "Example: %s --type=qt5 /usr/bin/helloworld\n\n",
607 static unsigned int get_delay(
char *delay_arg,
char *param_name,
608 unsigned int min_value,
unsigned int max_value)
615 delay = strtoul(delay_arg, NULL, 10);
618 if ((errno == ERANGE && delay == ULONG_MAX)
620 || delay > max_value)
622 report(report_error,
"Wrong value of %s parameter: %s\n", param_name, delay_arg);
632 DBusConnection *connection;
633 DBusMessage *message;
636 dbus_error_init (&error);
637 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
640 message = dbus_message_new_method_call(
"org.nemomobile.lipstick",
"/LauncherModel",
641 "org.nemomobile.lipstick.LauncherModel",
"notifyLaunching");
642 dbus_message_append_args(message, DBUS_TYPE_STRING, &desktop_file, DBUS_TYPE_INVALID);
644 dbus_connection_send(connection, message, NULL);
645 dbus_message_unref(message);
646 dbus_connection_flush(connection);
648 info(
"Failed to connect to the DBus session bus: %s", error.message);
649 dbus_error_free(&error);
656 int exit_status = EXIT_FAILURE;
673 FD_SET(socket_fd, &readfds);
674 ndfs = (socket_fd > ndfs) ? socket_fd : ndfs;
682 if (select(ndfs + 1, &readfds, NULL, NULL, NULL) == -1) {
683 if (errno == EINTR || errno == EAGAIN)
685 warning(
"socket select failed: %m\n");
690 if (FD_ISSET(socket_fd, &readfds)) {
693 exit_status = EXIT_FAILURE;
706 error(
"signal pipe read failure, terminating\n");
709 exit_signal = signal_id;
710 if (exit_signal == SIGTERM)
711 exit_status = EXIT_SUCCESS;
719 if (exit_status != EXIT_SUCCESS)
720 warning(
"application (pid=%d) exit(%d) signal(%d)\n",
723 info(
"application (pid=%d) exit(%d) signal(%d)\n",
726 if (socket_fd != -1) {
752 #define INVOKE_ARGS_INIT {\
757 .app_name = "default",\
758 .magic_options = INVOKER_MSG_MAGIC_OPTION_WAIT,\
760 .respawn_delay = RESPAWN_DELAY,\
762 .desktop_file = NULL,\
763 .exit_delay = EXIT_DELAY,\
769 int exit_status = EXIT_FAILURE;
773 int prog_prio = getpriority(PRIO_PROCESS, 0);
774 if (errno && prog_prio < 0)
810 warning(
"Connection with launcher process is broken. \n");
811 error(
"Start application %s as a binary executable without launcher...\n", args->
prog_name);
817 pid_t newPid = fork();
821 error(
"Invoker failed to fork. \n");
824 else if (newPid != 0)
843 int status = EXIT_FAILURE;
859 bool tried_session =
false;
860 for (
size_t i = 0; !tried_session && types[i]; ++i) {
863 tried_session =
true;
871 if (fd == -1 && !tried_session) {
872 bool tried_generic =
false;
873 for (
size_t i = 0; fd == -1 && types[i]; ++i) {
875 tried_generic =
true;
878 if (fd == -1 && !tried_generic)
886 }
else if (tried_session) {
887 warning(
"Launch failed, session booster is not available.\n");
888 }
else if (strcmp(args->
app_name,
"default")) {
893 warning(
"Launch failed, application specific booster is not available.\n");
896 warning(
"Also fallback boosters failed, launch without boosting.\n");
899 status = EXIT_SUCCESS;
902 for (
int i = 0; types[i]; ++i)
909 int main(
int argc,
char *argv[])
914 if (!strstr(argv[0], PROG_NAME_INVOKER) )
917 "Incorrect use of invoker, don't use symlinks. "
918 "Run invoker explicitly from e.g. a D-Bus service file instead.\n");
922 struct option longopts[] = {
923 {
"help", no_argument, NULL,
'h'},
924 {
"wait-term", no_argument, NULL,
'w'},
925 {
"no-wait", no_argument, NULL,
'n'},
926 {
"global-syms", no_argument, NULL,
'G'},
927 {
"deep-syms", no_argument, NULL,
'D'},
928 {
"single-instance", no_argument, NULL,
's'},
929 {
"keep-oom-score", no_argument, NULL,
'o'},
930 {
"daemon-mode", no_argument, NULL,
'o'},
931 {
"test-mode", no_argument, NULL,
'T'},
932 {
"type", required_argument, NULL,
't'},
933 {
"application", required_argument, NULL,
'a'},
934 {
"delay", required_argument, NULL,
'd'},
935 {
"respawn", required_argument, NULL,
'r'},
936 {
"splash", required_argument, NULL,
'S'},
937 {
"splash-landscape", required_argument, NULL,
'L'},
938 {
"desktop-file", required_argument, NULL,
'F'},
939 {
"verbose", no_argument, NULL,
'v'},
947 while ((opt = getopt_long(argc, argv,
"+hvcwnGDsoTd:t:a:r:S:L:F:", longopts, NULL)) != -1)
956 report_set_type(report_get_type() + 1);
964 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE;
973 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL;
1002 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE;
1025 report(report_error,
"No command line to invoke was given.\n");
1034 struct stat file_stat;
1035 if (stat(args.
prog_argv[0], &file_stat) == -1) {
1036 report(report_error,
"%s: not found: %m\n", args.
prog_argv[0]);
1041 if (!S_ISREG(file_stat.st_mode)) {
1042 report(report_error,
"%s: not a file\n", args.
prog_argv[0]);
1048 if (strcmp(args.
prog_argv[0],
"/usr/bin/sailfish-qml") == 0) {
1050 report(report_error,
"%s: requires an argument\n", args.
prog_argv[0]);
1062 report(report_error,
"Application type must be specified with --type.\n");
1067 report(report_error,
"Application name must be specified with --application.\n");
1074 info(
"Invoker test mode is not enabled.\n");
1079 report(report_error,
"Creating a pipe for Unix signals failed!\n");
1084 info(
"Invoking execution: '%s'\n", args.
prog_name);
1085 int ret_val =
invoke(&args);
1090 info(
"Delaying exit for %d seconds..\n", args.
exit_delay);
1094 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
static char * strip(char *str)
int main(int argc, char *argv[])
static void kill_application(pid_t pid)
static void invoker_send_exec(int fd, char *exec)
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 int invoke_remote(int socket_fd, const InvokeArgs *args)
static const unsigned int MIN_EXIT_DELAY
static void sigs_init(void)
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 char * slice(const char *pos, const char **ppos, const char *delim)
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 ** split(const char *str, const char *delim)
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 notify_app_lauch(const char *desktop_file)
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