31 #include <sys/socket.h>
35 #include <sys/resource.h>
45 #include <dbus/dbus.h>
56 #define VERBOSE_SIGNALS 0
64 while (*src && isspace(*src))
67 while (*src && !isspace(*src))
69 while (*src && isspace(*src))
80 static char *
slice(
const char *pos,
const char **ppos,
const char *delim)
84 const char *beg = pos;
85 while (*pos && !strchr(delim, *pos))
87 tok = strndup(beg, pos - beg);
96 static char **
split(
const char *str,
const char *delim)
102 for (
const char *pos = str; *pos; ++pos)
103 if (strchr(delim, *pos))
107 arr = calloc(n + 1,
sizeof *arr);
112 char *tok =
slice(str, &str, delim);
152 static const char m[] =
"*** signal\n";
153 if (write(STDERR_FILENO, m,
sizeof m - 1) == -1) {
159 char signal_id = (char) sig;
161 const char m[] =
"*** signal pipe write failure, terminating\n";
162 if (write(STDERR_FILENO, m,
sizeof m - 1) == -1) {
172 sigaction(SIGINT, sig, NULL);
173 sigaction(SIGTERM, sig, NULL);
179 struct sigaction sig;
181 memset(&sig, 0,
sizeof(sig));
182 sig.sa_flags = SA_RESTART;
191 struct sigaction sig;
193 memset(&sig, 0,
sizeof(sig));
194 sig.sa_flags = SA_RESTART;
195 sig.sa_handler = SIG_DFL;
202 struct timespec ts = { 0, 0 };
203 if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1 &&
204 clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
205 error(
"can't obtain a monotonic timestamp\n");
213 return ((
unsigned)(ts.tv_sec * 1000u) +
214 (
unsigned)(ts.tv_nsec / (1000 * 1000u)));
219 bool disconnected =
false;
226 debug(
"trying to disconnect booster socket...\n");
228 if (shutdown(socket_fd, SHUT_WR) == -1) {
229 warning(
"socket shutdown failed: %m\n");
234 unsigned timeout = 5000;
236 unsigned elapsed =
timestamp() - started;
237 if (elapsed >= timeout)
240 struct pollfd pfd = {
246 debug(
"waiting for booster socket input...\n");
247 int rc = poll(&pfd, 1, (
int)(timeout - elapsed));
253 if (errno == EINTR || errno == EAGAIN)
255 warning(
"socket poll failed: %m\n");
260 rc = recv(socket_fd, buf,
sizeof buf, MSG_DONTWAIT);
268 warning(
"socket read failed: %m\n");
272 warning(
"socket poll timeout\n");
276 debug(
"booster socket was succesfully disconnected\n");
278 warning(
"could not disconnect booster socket\n");
286 warning(
"application pid is not known, can't kill it");
290 warning(
"sending SIGTERM to application (pid=%d)", (
int)pid);
292 if (kill(pid, SIGTERM) == -1)
295 for (
int i = 0; i < 10; ++i) {
297 if (kill(pid, 0) == -1)
301 warning(
"sending SIGKILL to application (pid=%d)", (
int)pid);
303 if (kill(pid, SIGKILL) == -1)
306 for (
int i = 0; i < 10; ++i) {
308 if (kill(pid, 0) == -1)
312 warning(
"application (pid=%d) did not exit", (
int)pid);
317 debug(
"application (pid=%d) has exited", (
int)pid);
319 warning(
"application (pid=%d) kill failed: %m", (
int)pid);
332 if (action != INVOKER_MSG_ACK)
334 die(1,
"Received wrong ack (%08x)\n", action);
343 bool connected =
false;
347 if (!app_type || strchr(app_type,
'/'))
349 if (app_name && strchr(app_name,
'/'))
352 const char *runtimeDir = getenv(
"XDG_RUNTIME_DIR");
353 if (!runtimeDir || !*runtimeDir) {
354 error(
"XDG_RUNTIME_DIR is not defined.\n");
358 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
359 error(
"Failed to create socket: %m\n");
363 struct sockaddr_un sun = {
364 .sun_family = AF_UNIX,
366 int maxSize =
sizeof(sun.sun_path);
370 length = snprintf(sun.sun_path, maxSize,
"%s/mapplauncherd/_%s/%s/socket",
371 runtimeDir, app_name, app_type);
373 length = snprintf(sun.sun_path, maxSize,
"%s/mapplauncherd/%s",
374 runtimeDir, app_type);
376 if (length <= 0 || length >= maxSize) {
378 error(
"Invalid booster type: %s / application: %s\n",
381 error(
"Invalid booster type: %s\n", app_type);
385 if (connect(fd, (
struct sockaddr *)&sun,
sizeof(sun)) == -1) {
387 warning(
"connect(\"%s\") failed: %m\n", sun.sun_path);
391 debug(
"connected to: %s\n", sun.sun_path);
395 if (!connected && fd != -1)
408 if (action != INVOKER_MSG_PID)
409 die(1,
"Received a bad message id (%08x)\n", action);
415 die(1,
"Received a zero pid \n");
428 if (!res || (action != INVOKER_MSG_EXIT))
448 invoke_send_msg(fd, INVOKER_MSG_MAGIC | INVOKER_MSG_MAGIC_VERSION | options);
470 for (i = 0; i < argc; i++)
472 debug(
"param %d %s \n", i, argv[i]);
504 for (n_vars = 0;
environ[n_vars] != NULL; n_vars++) ;
509 for (i = 0; i < n_vars; i++)
521 struct cmsghdr *cmsg = NULL;
522 int io[3] = { 0, 1, 2 };
523 char buf[CMSG_SPACE(
sizeof(io))];
527 memset(&msg, 0,
sizeof(
struct msghdr));
529 iov.iov_base = &dummy;
534 msg.msg_control = buf;
535 msg.msg_controllen =
sizeof(buf);
537 cmsg = CMSG_FIRSTHDR(&msg);
538 cmsg->cmsg_len = CMSG_LEN(
sizeof(io));
539 cmsg->cmsg_level = SOL_SOCKET;
540 cmsg->cmsg_type = SCM_RIGHTS;
542 memcpy(CMSG_DATA(cmsg), io,
sizeof(io));
544 msg.msg_controllen = cmsg->cmsg_len;
547 if (sendmsg(fd, &msg, 0) < 0)
549 warning(
"sendmsg failed in invoker_send_io: %s \n", strerror(errno));
566 printf(
"\nUsage: %s [options] [--type=TYPE] [file] [args]\n\n"
567 "Launch applications compiled as a shared library (-shared) or\n"
568 "a position independent executable (-pie) through mapplauncherd.\n\n"
569 "TYPE chooses the type of booster used. Qt-booster may be used to\n"
570 "launch anything. Possible values for TYPE:\n"
571 " qt5 Launch a Qt 5 application.\n"
572 " qtquick2 Launch a Qt Quick 2 (QML) application.\n"
573 " silica-qt5 Launch a Sailfish Silica application.\n"
574 " generic Launch any application, even if it's not a library.\n\n"
575 "The TYPE may also be a comma delimited list of boosters to try. The first available\n"
576 "booster will be used.\n\n"
578 " -d, --delay SECS After invoking sleep for SECS seconds\n"
580 " -r, --respawn SECS After invoking respawn new booster after SECS seconds\n"
581 " (default %d, max %d).\n"
582 " -w, --wait-term Wait for launched process to terminate (default).\n"
583 " -n, --no-wait Do not wait for launched process to terminate.\n"
584 " -G, --global-syms Places symbols in the application binary and its\n"
585 " libraries to the global scope.\n"
586 " See RTLD_GLOBAL in the dlopen manual page.\n"
587 " -s, --single-instance Launch the application as a single instance.\n"
588 " The existing application window will be activated\n"
589 " if already launched.\n"
590 " -o, --keep-oom-score Notify invoker that the launched process should inherit oom_score_adj\n"
591 " from the booster. The score is reset to 0 normally.\n"
592 " -T, --test-mode Invoker test mode. Also control file in root home should be in place.\n"
593 " -F, --desktop-file Desktop file of the application to notify lipstick of launching app.\n"
594 " -h, --help Print this help.\n\n"
595 "Example: %s --type=qt5 /usr/bin/helloworld\n\n",
602 static unsigned int get_delay(
char *delay_arg,
char *param_name,
603 unsigned int min_value,
unsigned int max_value)
610 delay = strtoul(delay_arg, NULL, 10);
613 if ((errno == ERANGE && delay == ULONG_MAX)
615 || delay > max_value)
617 report(report_error,
"Wrong value of %s parameter: %s\n", param_name, delay_arg);
627 DBusConnection *connection;
628 DBusMessage *message;
631 dbus_error_init (&error);
632 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
635 message = dbus_message_new_method_call(
"org.nemomobile.lipstick",
"/LauncherModel",
636 "org.nemomobile.lipstick.LauncherModel",
"notifyLaunching");
637 dbus_message_append_args(message, DBUS_TYPE_STRING, &desktop_file, DBUS_TYPE_INVALID);
639 dbus_connection_send(connection, message, NULL);
640 dbus_message_unref(message);
641 dbus_connection_flush(connection);
643 info(
"Failed to connect to the DBus session bus: %s", error.message);
644 dbus_error_free(&error);
651 int exit_status = EXIT_FAILURE;
668 FD_SET(socket_fd, &readfds);
669 ndfs = (socket_fd > ndfs) ? socket_fd : ndfs;
677 if (select(ndfs + 1, &readfds, NULL, NULL, NULL) == -1) {
678 if (errno == EINTR || errno == EAGAIN)
680 warning(
"socket select failed: %m\n");
685 if (FD_ISSET(socket_fd, &readfds)) {
688 exit_status = EXIT_FAILURE;
701 error(
"signal pipe read failure, terminating\n");
704 exit_signal = signal_id;
705 if (exit_signal == SIGTERM)
706 exit_status = EXIT_SUCCESS;
714 if (exit_status != EXIT_SUCCESS)
715 warning(
"application (pid=%d) exit(%d) signal(%d)\n",
718 debug(
"application (pid=%d) exit(%d) signal(%d)\n",
721 if (socket_fd != -1) {
747 #define INVOKE_ARGS_INIT {\
752 .app_name = "default",\
753 .magic_options = INVOKER_MSG_MAGIC_OPTION_WAIT,\
755 .respawn_delay = RESPAWN_DELAY,\
757 .desktop_file = NULL,\
758 .exit_delay = EXIT_DELAY,\
764 int exit_status = EXIT_FAILURE;
768 int prog_prio = getpriority(PRIO_PROCESS, 0);
769 if (errno && prog_prio < 0)
805 warning(
"Connection with launcher process is broken. \n");
806 error(
"Start application %s as a binary executable without launcher...\n", args->
prog_name);
812 pid_t newPid = fork();
816 error(
"Invoker failed to fork. \n");
819 else if (newPid != 0)
838 int status = EXIT_FAILURE;
854 bool tried_session =
false;
855 for (
size_t i = 0; !tried_session && types[i]; ++i) {
856 if (strcmp(types[i],
"session"))
858 tried_session =
true;
866 if (fd == -1 && !tried_session) {
867 bool tried_generic =
false;
868 for (
size_t i = 0; fd == -1 && types[i]; ++i) {
869 if (!strcmp(types[i],
"generic"))
870 tried_generic =
true;
873 if (fd == -1 && !tried_generic)
881 }
else if (tried_session) {
882 warning(
"Launch failed, session booster is not available.\n");
883 }
else if (strcmp(args->
app_name,
"default")) {
888 warning(
"Launch failed, application specific booster is not available.\n");
891 warning(
"Also fallback boosters failed, launch without boosting.\n");
894 status = EXIT_SUCCESS;
897 for (
int i = 0; types[i]; ++i)
904 int main(
int argc,
char *argv[])
909 if (!strstr(argv[0], PROG_NAME_INVOKER) )
912 "Incorrect use of invoker, don't use symlinks. "
913 "Run invoker explicitly from e.g. a D-Bus service file instead.\n");
917 struct option longopts[] = {
918 {
"help", no_argument, NULL,
'h'},
919 {
"wait-term", no_argument, NULL,
'w'},
920 {
"no-wait", no_argument, NULL,
'n'},
921 {
"global-syms", no_argument, NULL,
'G'},
922 {
"deep-syms", no_argument, NULL,
'D'},
923 {
"single-instance", no_argument, NULL,
's'},
924 {
"keep-oom-score", no_argument, NULL,
'o'},
925 {
"daemon-mode", no_argument, NULL,
'o'},
926 {
"test-mode", no_argument, NULL,
'T'},
927 {
"type", required_argument, NULL,
't'},
928 {
"application", required_argument, NULL,
'a'},
929 {
"delay", required_argument, NULL,
'd'},
930 {
"respawn", required_argument, NULL,
'r'},
931 {
"splash", required_argument, NULL,
'S'},
932 {
"splash-landscape", required_argument, NULL,
'L'},
933 {
"desktop-file", required_argument, NULL,
'F'},
941 while ((opt = getopt_long(argc, argv,
"+hcwnGDsoTd:t:a:r:S:L:F:", longopts, NULL)) != -1)
954 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE;
963 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL;
992 args.
magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE;
1015 report(report_error,
"No command line to invoke was given.\n");
1024 struct stat file_stat;
1025 if (stat(args.
prog_argv[0], &file_stat) == -1) {
1026 report(report_error,
"%s: not found: %m\n", args.
prog_argv[0]);
1031 if (!S_ISREG(file_stat.st_mode)) {
1032 report(report_error,
"%s: not a file\n", args.
prog_argv[0]);
1038 if (strcmp(args.
prog_argv[0],
"/usr/bin/sailfish-qml") == 0) {
1040 report(report_error,
"%s: requires an argument\n", args.
prog_argv[0]);
1052 report(report_error,
"Application type must be specified with --type.\n");
1057 report(report_error,
"Application name must be specified with --application.\n");
1064 info(
"Invoker test mode is not enabled.\n");
1069 report(report_error,
"Creating a pipe for Unix signals failed!\n");
1074 info(
"Invoking execution: '%s'\n", args.
prog_name);
1075 int ret_val =
invoke(&args);
1080 debug(
"Delaying exit for %d seconds..\n", args.
exit_delay);
1084 debug(
"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