[mapplauncherd] Adjust booster socket paths. Fixes JB#53864 OMP#JOLLA-39

Having all booster sockets reside at the same level in /run/user
directory structure makes it difficult to limit what boosters
sandboxed applications have access to.

Move socket files to booster specific sub-directories. And as an enabler
for sandboxed boosters, add another sub-directory level that can be used
for identifying application specific boosters.

As an example, silica-qt booster socket file path changes from

  /run/user/UID/mapplauncherd/silica-qt5

to

  /run/user/UID/mapplauncherd/_default/silica-qt5/socket

and sandboxed silica-qt5 booster for application APP would use

  /run/user/UID/mapplauncherd/_APP/silica-qt5/socket

Signed-off-by: Simo Piiroinen <simo.piiroinen@jolla.com>
pull/1/head
Simo Piiroinen 5 years ago
parent 66db6e7063
commit 23846d0db4

@ -43,7 +43,7 @@ enum report_type {
}; };
extern void report_set_output(enum report_output new_output); extern void report_set_output(enum report_output new_output);
extern void report(enum report_type type, const char *msg, ...); extern void report(enum report_type type, const char *msg, ...) __attribute__((format(printf, 2, 3)));
#ifndef DEBUG_LOGGING_DISABLED #ifndef DEBUG_LOGGING_DISABLED
#define debug(msg, ...) report(report_debug, msg, ##__VA_ARGS__) #define debug(msg, ...) report(report_debug, msg, ##__VA_ARGS__)

@ -26,6 +26,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <signal.h> #include <signal.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
@ -46,6 +47,71 @@
#include "invokelib.h" #include "invokelib.h"
#include "search.h" #include "search.h"
// Utility functions
static char *strip(char *str)
{
if (str) {
char *dst = str;
char *src = str;
while (*src && isspace(*src))
++src;
for (;;) {
while (*src && !isspace(*src))
*dst++ = *src++;
while (*src && isspace(*src))
++src;
if (!*src)
break;
*dst++ = ' ';
}
*dst = 0;
}
return str;
}
static char *slice(const char *pos, const char **ppos, const char *delim)
{
char *tok = NULL;
if (pos) {
const char *beg = pos;
while (*pos && !strchr(delim, *pos))
++pos;
tok = strndup(beg, pos - beg);
if (*pos)
++pos;
}
if (ppos)
*ppos = pos;
return tok;
}
static char **split(const char *str, const char *delim)
{
char **arr = NULL;
if (str) {
/* Upper limit for token count is number of delimeters + one */
int n = 1;
for (const char *pos = str; *pos; ++pos)
if (strchr(delim, *pos))
++n;
/* Upper limit for required array size is token count + one */
arr = calloc(n + 1, sizeof *arr);
/* Fill in the array */
int i = 0;
while (*str) {
char *tok = slice(str, &str, delim);
if (*strip(tok))
arr[i++] = tok;
else
free(tok);
}
arr[i] = NULL;
}
return arr;
}
// Delay before exit. // Delay before exit.
static const unsigned int EXIT_DELAY = 0; static const unsigned int EXIT_DELAY = 0;
static const unsigned int MIN_EXIT_DELAY = 1; static const unsigned int MIN_EXIT_DELAY = 1;
@ -71,8 +137,6 @@ static void sigs_init(void);
//! Pipe used to safely catch Unix signals //! Pipe used to safely catch Unix signals
static int g_signal_pipe[2]; static int g_signal_pipe[2];
static const char *g_desktop_file = NULL;
// Forwards Unix signals from invoker to the invoked process // Forwards Unix signals from invoker to the invoked process
static void sig_forwarder(int sig) static void sig_forwarder(int sig)
{ {
@ -182,45 +246,62 @@ static bool invoke_recv_ack(int fd)
} }
// Inits a socket connection for the given application type // Inits a socket connection for the given application type
static int invoker_init(const char *app_type) static int invoker_init(const char *app_type, const char *app_name)
{ {
int fd; bool connected = false;
struct sockaddr_un sun; int fd = -1;
fd = socket(PF_UNIX, SOCK_STREAM, 0); /* Sanity check args */
if (fd < 0) if (!app_type || strchr(app_type, '/'))
{ goto EXIT;
error("Failed to open invoker socket for type %s.\n", app_type); if (app_name && strchr(app_name, '/'))
return -1; goto EXIT;
}
sun.sun_family = AF_UNIX;
int maxSize = sizeof(sun.sun_path) - 1;
const char *runtimeDir = getenv("XDG_RUNTIME_DIR"); const char *runtimeDir = getenv("XDG_RUNTIME_DIR");
const char *subpath = "/mapplauncherd/"; if (!runtimeDir || !*runtimeDir) {
const int subpathLen = strlen(subpath); error("XDG_RUNTIME_DIR is not defined.\n");
goto EXIT;
}
if (runtimeDir && *runtimeDir) if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
strncpy(sun.sun_path, runtimeDir, maxSize - subpathLen); error("Failed to create socket: %m\n");
else goto EXIT;
strncpy(sun.sun_path, "/tmp", maxSize - subpathLen); }
sun.sun_path[maxSize - subpathLen] = 0; struct sockaddr_un sun = {
strcat(sun.sun_path, subpath); .sun_family = AF_UNIX,
};
int maxSize = sizeof(sun.sun_path);
int length;
maxSize -= strlen(sun.sun_path); if (app_name)
if (maxSize < (int)strlen(app_type) || strchr(app_type, '/')) length = snprintf(sun.sun_path, maxSize, "%s/mapplauncherd/_%s/%s/socket",
die(1, "Invalid type of application: %s\n", app_type); runtimeDir, app_name, app_type);
else
length = snprintf(sun.sun_path, maxSize, "%s/mapplauncherd/%s",
runtimeDir, app_type);
strcat(sun.sun_path, app_type); if (length <= 0 || length >= maxSize) {
if (app_name)
error("Invalid booster type: %s / application: %s\n",
app_type, app_name);
else
error("Invalid booster type: %s\n", app_type);
goto EXIT;
}
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
{ if (errno != ENOENT)
error("Failed to initiate connect on the socket for type %s.\n", app_type); warning("connect(\"%s\") failed: %m\n", sun.sun_path);
return -1; goto EXIT;
} }
debug("connected to: %s\n", sun.sun_path);
connected = true;
EXIT:
if (!connected && fd != -1)
close(fd), fd = -1;
return fd; return fd;
} }
@ -565,9 +646,36 @@ static int wait_for_launched_process_to_exit(int socket_fd, bool wait_term)
return status; return status;
} }
typedef struct InvokeArgs {
int prog_argc;
char **prog_argv;
char *prog_name;
const char *app_type;
const char *app_name;
uint32_t magic_options;
bool wait_term;
unsigned int respawn_delay;
bool test_mode;
const char *desktop_file;
unsigned int exit_delay;
} InvokeArgs;
#define INVOKE_ARGS_INIT {\
.prog_argc = 0,\
.prog_argv = NULL,\
.prog_name = NULL,\
.app_type = NULL,\
.app_name = "default",\
.magic_options = INVOKER_MSG_MAGIC_OPTION_WAIT,\
.wait_term = true,\
.respawn_delay = RESPAWN_DELAY,\
.test_mode = false,\
.desktop_file = NULL,\
.exit_delay = EXIT_DELAY,\
}
// "normal" invoke through a socket connection // "normal" invoke through a socket connection
static int invoke_remote(int socket_fd, int prog_argc, char **prog_argv, char *prog_name, static int invoke_remote(int socket_fd, const InvokeArgs *args)
uint32_t magic_options, bool wait_term, unsigned int respawn_delay)
{ {
// Get process priority // Get process priority
errno = 0; errno = 0;
@ -579,38 +687,33 @@ static int invoke_remote(int socket_fd, int prog_argc, char **prog_argv, char *p
// Connection with launcher process is established, // Connection with launcher process is established,
// send the data. // send the data.
invoker_send_magic(socket_fd, magic_options); invoker_send_magic(socket_fd, args->magic_options);
invoker_send_name(socket_fd, prog_name); invoker_send_name(socket_fd, args->prog_name);
invoker_send_exec(socket_fd, prog_argv[0]); invoker_send_exec(socket_fd, args->prog_argv[0]);
invoker_send_args(socket_fd, prog_argc, prog_argv); invoker_send_args(socket_fd, args->prog_argc, args->prog_argv);
invoker_send_prio(socket_fd, prog_prio); invoker_send_prio(socket_fd, prog_prio);
invoker_send_delay(socket_fd, respawn_delay); invoker_send_delay(socket_fd, args->respawn_delay);
invoker_send_ids(socket_fd, getuid(), getgid()); invoker_send_ids(socket_fd, getuid(), getgid());
invoker_send_io(socket_fd); invoker_send_io(socket_fd);
invoker_send_env(socket_fd); invoker_send_env(socket_fd);
invoker_send_end(socket_fd); invoker_send_end(socket_fd);
if (prog_name) if (args->desktop_file)
{ notify_app_lauch(args->desktop_file);
free(prog_name);
}
if (g_desktop_file) { int exit_status = wait_for_launched_process_to_exit(socket_fd, args->wait_term);
notify_app_lauch(g_desktop_file);
}
int exit_status = wait_for_launched_process_to_exit(socket_fd, wait_term);
return exit_status; return exit_status;
} }
static void invoke_fallback(char **prog_argv, char *prog_name, bool wait_term) static void invoke_fallback(const InvokeArgs *args)
{ {
// Connection with launcher is broken, // Connection with launcher is broken,
// try to launch application via execve // try to launch application via execve
warning("Connection with launcher process is broken. \n"); warning("Connection with launcher process is broken. \n");
error("Start application %s as a binary executable without launcher...\n", prog_name); error("Start application %s as a binary executable without launcher...\n", args->prog_name);
// Fork if wait_term not set // Fork if wait_term not set
if(!wait_term) if (!args->wait_term)
{ {
// Fork a new process // Fork a new process
pid_t newPid = fork(); pid_t newPid = fork();
@ -627,91 +730,87 @@ static void invoke_fallback(char **prog_argv, char *prog_name, bool wait_term)
} }
// Exec the process image // Exec the process image
execve(prog_name, prog_argv, environ); execve(args->prog_name, args->prog_argv, environ);
perror("execve"); /* execve() only returns on error */ perror("execve"); /* execve() only returns on error */
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Invokes the given application // Invokes the given application
static int invoke(int prog_argc, char **prog_argv, char *prog_name, static int invoke(InvokeArgs *args)
const char *app_type, uint32_t magic_options, bool wait_term, unsigned int respawn_delay,
bool test_mode)
{ {
int status = 0; /* Note: Contents of 'args' are assumed to have been
if (prog_name && prog_argv) * checked and sanitized before invoke() call.
{ */
//If TEST_MODE_CONTROL_FILE doesn't exists switch off test mode
if (test_mode && access(TEST_MODE_CONTROL_FILE, F_OK) != 0) int status = EXIT_FAILURE;
{
test_mode = false; /* The app can be launched with a comma delimited list of
info("Invoker test mode is not enabled.\n"); * booster types to attempt.
} */
char **types = split(args->app_type, ",");
// The app can be launched with a comma delimited list of boosters to attempt.
int fd = -1;
char *app_type_list = strdup(app_type);
char *saveptr = NULL; /* Session booster is a special case:
char *token; * - is never going to be application specific
* - can use and still uses legacy socket path
* - mutually exclusive with all other choises
* - no fallbacks should be utilized
*/
bool tried_session = false;
for (size_t i = 0; !tried_session && types[i]; ++i) {
if (strcmp(types[i], "session"))
continue;
tried_session = true;
fd = invoker_init(types[i], NULL);
}
int fd = -1; /* Application aware boosters
for (token = strtok_r(app_type_list, ",", &saveptr); fd == -1 && token != NULL; token = strtok_r(NULL, ",", &saveptr)) * - have fallback strategy, but it
{ * - must not cross application vs "default" boundary
app_type = token; */
if (app_type != app_type_list) { if (fd == -1 && !tried_session) {
info("Trying fallback type %s.\n", app_type); bool tried_generic = false;
} for (size_t i = 0; fd == -1 && types[i]; ++i) {
fd = invoker_init(app_type); if (!strcmp(types[i], "generic"))
} tried_generic = true;
fd = invoker_init(types[i], args->app_name);
if (fd == -1)
{
// This is a fallback if connection with the launcher
// process is broken
// if the attempt was to use the generic booster, and that failed,
// then give up and start unboosted. otherwise, make an attempt to
// use the generic booster before not boosting. this means single
// instance support (for instance) will still work.
if (strcmp(app_type, "generic") == 0)
{
warning("Can't try fall back to generic, already using it\n");
invoke_fallback(prog_argv, prog_argv[0], wait_term);
}
else
{
warning("Booster %s is not available. Falling back to generic.\n", app_type);
invoke(prog_argc, prog_argv, prog_argv[0], "generic", magic_options, wait_term, respawn_delay, test_mode);
}
}
// "normal" invoke through a socket connetion
else
{
status = invoke_remote(fd, prog_argc, prog_argv, prog_name,
magic_options, wait_term, respawn_delay);
close(fd);
} }
if (fd == -1 && !tried_generic)
fd = invoker_init("generic", args->app_name);
}
free(app_type_list); if (fd != -1) {
/* "normal" invoke through a socket connetion */
status = invoke_remote(fd, args);
close(fd);
} else if (tried_session) {
warning("Launch failed, session booster is not available.\n");
} else if (strcmp(args->app_name, "default")) {
/* Boosters that deal explicitly with one application only
* must be assumed to run within sandbox -> skipping boosting
* would also skip sandboxing -> no direct launch fallback
*/
warning("Launch failed, application specific booster is not available.\n");
} else {
/* Give up and start unboosted */
warning("Also fallback boosters failed, launch without boosting.\n");
invoke_fallback(args);
/* Returns only in case of: no-wait was specified and fork succeeded */
status = EXIT_SUCCESS;
} }
for (int i = 0; types[i]; ++i)
free(types[i]);
free(types);
return status; return status;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
const char *app_type = NULL; InvokeArgs args = INVOKE_ARGS_INIT;
int prog_argc = 0;
uint32_t magic_options = 0;
bool wait_term = true;
unsigned int delay = EXIT_DELAY;
unsigned int respawn_delay = RESPAWN_DELAY;
char **prog_argv = NULL;
char *prog_name = NULL;
struct stat file_stat;
bool test_mode = false;
// wait-term parameter by default
magic_options |= INVOKER_MSG_MAGIC_OPTION_WAIT;
// Called with a different name (old way of using invoker) ? // Called with a different name (old way of using invoker) ?
if (!strstr(argv[0], PROG_NAME_INVOKER) ) if (!strstr(argv[0], PROG_NAME_INVOKER) )
@ -733,6 +832,7 @@ int main(int argc, char *argv[])
{"daemon-mode", no_argument, NULL, 'o'}, // Legacy alias {"daemon-mode", no_argument, NULL, 'o'}, // Legacy alias
{"test-mode", no_argument, NULL, 'T'}, {"test-mode", no_argument, NULL, 'T'},
{"type", required_argument, NULL, 't'}, {"type", required_argument, NULL, 't'},
{"application", required_argument, NULL, 'a'},
{"delay", required_argument, NULL, 'd'}, {"delay", required_argument, NULL, 'd'},
{"respawn", required_argument, NULL, 'r'}, {"respawn", required_argument, NULL, 'r'},
{"splash", required_argument, NULL, 'S'}, {"splash", required_argument, NULL, 'S'},
@ -745,7 +845,7 @@ int main(int argc, char *argv[])
// The use of + for POSIXLY_CORRECT behavior is a GNU extension, but avoids polluting // The use of + for POSIXLY_CORRECT behavior is a GNU extension, but avoids polluting
// the environment // the environment
int opt; int opt;
while ((opt = getopt_long(argc, argv, "+hcwnGDsoTd:t:r:S:L:F:", longopts, NULL)) != -1) while ((opt = getopt_long(argc, argv, "+hcwnGDsoTd:t:a:r:S:L:F:", longopts, NULL)) != -1)
{ {
switch(opt) switch(opt)
{ {
@ -758,41 +858,45 @@ int main(int argc, char *argv[])
break; break;
case 'o': case 'o':
magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE; args.magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE;
break; break;
case 'n': case 'n':
wait_term = false; args.wait_term = false;
magic_options &= (~INVOKER_MSG_MAGIC_OPTION_WAIT); args.magic_options &= (~INVOKER_MSG_MAGIC_OPTION_WAIT);
break; break;
case 'G': case 'G':
magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL; args.magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL;
break; break;
case 'D': case 'D':
magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_DEEP; args.magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_DEEP;
break; break;
case 'T': case 'T':
test_mode = true; args.test_mode = true;
break; break;
case 't': case 't':
app_type = optarg; args.app_type = optarg;
break;
case 'a':
args.app_name = optarg;
break; break;
case 'd': case 'd':
delay = get_delay(optarg, "delay", MIN_EXIT_DELAY, MAX_EXIT_DELAY); args.exit_delay = get_delay(optarg, "delay", MIN_EXIT_DELAY, MAX_EXIT_DELAY);
break; break;
case 'r': case 'r':
respawn_delay = get_delay(optarg, "respawn delay", args.respawn_delay = get_delay(optarg, "respawn delay",
MIN_RESPAWN_DELAY, MAX_RESPAWN_DELAY); MIN_RESPAWN_DELAY, MAX_RESPAWN_DELAY);
break; break;
case 's': case 's':
magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE; args.magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE;
break; break;
case 'S': case 'S':
@ -801,7 +905,7 @@ int main(int argc, char *argv[])
break; break;
case 'F': case 'F':
g_desktop_file = optarg; args.desktop_file = optarg;
break; break;
case '?': case '?':
@ -810,57 +914,63 @@ int main(int argc, char *argv[])
} }
// Option processing stops as soon as application name is encountered // Option processing stops as soon as application name is encountered
if (optind < argc)
{
prog_name = search_program(argv[optind]);
prog_argc = argc - optind;
prog_argv = &argv[optind];
// Force argv[0] of application to be the absolute path to allow the args.prog_argc = argc - optind;
// application to find out its installation directory from there args.prog_argv = &argv[optind];
prog_argv[0] = prog_name;
}
// Check if application name isn't defined if (args.prog_argc < 1) {
if (!prog_name) report(report_error, "No command line to invoke was given.\n");
{ exit(EXIT_FAILURE);
report(report_error, "Application's name is not defined.\n");
usage(1);
} }
// Force argv[0] of application to be the absolute path to allow the
// application to find out its installation directory from there
args.prog_argv[0] = search_program(args.prog_argv[0]);
// Check if application exists // Check if application exists
if (stat(prog_name, &file_stat)) struct stat file_stat;
{ if (stat(args.prog_argv[0], &file_stat) == -1) {
report(report_error, "%s: not found\n", prog_name); report(report_error, "%s: not found: %m\n", args.prog_argv[0]);
return EXIT_STATUS_APPLICATION_NOT_FOUND; return EXIT_STATUS_APPLICATION_NOT_FOUND;
} }
// Check that // Check that application is regular file (or symlink to such)
if (!S_ISREG(file_stat.st_mode) && !S_ISLNK(file_stat.st_mode)) if (!S_ISREG(file_stat.st_mode)) {
{ report(report_error, "%s: not a file\n", args.prog_argv[0]);
report(report_error, "%s: not a file\n", prog_name);
return EXIT_STATUS_APPLICATION_NOT_FOUND; return EXIT_STATUS_APPLICATION_NOT_FOUND;
} }
// If it's a launcher, append its first argument to the name // If it's a launcher, append its first argument to the name
// (at this point, we have already checked if it exists and is a file) // (at this point, we have already checked if it exists and is a file)
if (strcmp(prog_name, "/usr/bin/sailfish-qml") == 0) { if (strcmp(args.prog_argv[0], "/usr/bin/sailfish-qml") == 0) {
if (prog_argc < 2) { if (args.prog_argc < 2) {
report(report_error, "%s: requires an argument\n", prog_name); report(report_error, "%s: requires an argument\n", args.prog_argv[0]);
return EXIT_STATUS_APPLICATION_NOT_FOUND; return EXIT_STATUS_APPLICATION_NOT_FOUND;
} }
// Must not free() the existing prog_name, as it's pointing to prog_argv[0] if (asprintf(&args.prog_name, "%s %s", args.prog_argv[0], args.prog_argv[1]) < 0)
prog_name = (char *)malloc(strlen(prog_argv[0]) + strlen(prog_argv[1]) + 2); exit(EXIT_FAILURE);
sprintf(prog_name, "%s %s", prog_argv[0], prog_argv[1]); } else {
if (!(args.prog_name = strdup(args.prog_argv[0])))
exit(EXIT_FAILURE);
} }
if (!app_type) if (!args.app_type) {
{
report(report_error, "Application type must be specified with --type.\n"); report(report_error, "Application type must be specified with --type.\n");
usage(1); usage(1);
} }
if (!args.app_name) {
report(report_error, "Application name must be specified with --application.\n");
usage(1);
}
// If TEST_MODE_CONTROL_FILE doesn't exists switch off test mode
if (args.test_mode && access(TEST_MODE_CONTROL_FILE, F_OK) != 0) {
args.test_mode = false;
info("Invoker test mode is not enabled.\n");
}
if (pipe(g_signal_pipe) == -1) if (pipe(g_signal_pipe) == -1)
{ {
report(report_error, "Creating a pipe for Unix signals failed!\n"); report(report_error, "Creating a pipe for Unix signals failed!\n");
@ -868,15 +978,14 @@ int main(int argc, char *argv[])
} }
// Send commands to the launcher daemon // Send commands to the launcher daemon
info("Invoking execution: '%s'\n", prog_name); info("Invoking execution: '%s'\n", args.prog_name);
int ret_val = invoke(prog_argc, prog_argv, prog_name, app_type, magic_options, wait_term, respawn_delay, test_mode); int ret_val = invoke(&args);
// Sleep for delay before exiting // Sleep for delay before exiting
if (delay) if (args.exit_delay) {
{
// DBUS cannot cope some times if the invoker exits too early. // DBUS cannot cope some times if the invoker exits too early.
debug("Delaying exit for %d seconds..\n", delay); debug("Delaying exit for %d seconds..\n", args.exit_delay);
sleep(delay); sleep(args.exit_delay);
} }
return ret_val; return ret_val;

@ -58,6 +58,7 @@ Booster::Booster() :
m_oldPriority(0), m_oldPriority(0),
m_oldPriorityOk(false), m_oldPriorityOk(false),
m_spaceAvailable(0), m_spaceAvailable(0),
m_boostedApplication("default"),
m_bootMode(false) m_bootMode(false)
{ {
Connection::setMountNamespace(Connection::getMountNamespace(getpid())); Connection::setMountNamespace(Connection::getMountNamespace(getpid()));
@ -586,6 +587,62 @@ bool Booster::popPriority()
return false; return false;
} }
const string &Booster::boostedApplication() const
{
return m_boostedApplication;
}
void Booster::setBoostedApplication(const string &application)
{
string filtered;
filtered.reserve(application.size());
bool alnum = false;
bool error = false;
for (auto chr : application) {
switch (chr) {
case 'A' ... 'Z':
chr += 'a' - 'A';
// FALLTHRU
case 'a' ... 'z':
case '0' ... '9':
alnum = true;
// FALLTHRU
case '-':
case '_':
if (alnum) {
filtered.push_back(chr);
break;
}
// FALLTHRU
default:
error = true;
break;
}
}
if (error || filtered.empty())
Logger::logError("Rejected invalid application name '%s'", application.c_str());
else
m_boostedApplication = filtered;
}
const string Booster::socketId() const
{
string id;
if (boosterType() == "silica-session") {
// Session is never going to be application specific
// -> retain legacy socket path
id += boosterType();
} else {
id += '_';
id += boostedApplication();
id += '/';
id += boosterType();
id += "/socket";
}
return id;
}
pid_t Booster::invokersPid() pid_t Booster::invokersPid()
{ {
if (m_connection->isReportAppExitStatusNeeded()) if (m_connection->isReportAppExitStatusNeeded())

@ -1,6 +1,8 @@
/*************************************************************************** /***************************************************************************
** **
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
** Copyright (c) 2012 - 2021 Jolla Ltd.
** Copyright (c) 2021 Open Mobile Platform LLC.
** All rights reserved. ** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com) ** Contact: Nokia Corporation (directui@nokia.com)
** **
@ -107,6 +109,11 @@ public:
*/ */
virtual const string & boosterType() const = 0; virtual const string & boosterType() const = 0;
const string &boostedApplication() const;
void setBoostedApplication(const string &application);
const string socketId() const;
//! Get invoker's pid //! Get invoker's pid
pid_t invokersPid(); pid_t invokersPid();
@ -213,6 +220,9 @@ private:
//! Original space available for arguments //! Original space available for arguments
int m_spaceAvailable; int m_spaceAvailable;
//! Application name when applicable, or "default"
string m_boostedApplication;
//! True, if being run in boot mode. //! True, if being run in boot mode.
bool m_bootMode; bool m_bootMode;

@ -26,6 +26,7 @@
#include "singleinstance.h" #include "singleinstance.h"
#include "socketmanager.h" #include "socketmanager.h"
#include <deque>
#include <cstdlib> #include <cstdlib>
#include <cerrno> #include <cerrno>
#include <sys/capability.h> #include <sys/capability.h>
@ -147,6 +148,9 @@ void Daemon::run(Booster *booster)
{ {
m_booster = booster; m_booster = booster;
if (!m_boostedApplication.empty())
m_booster->setBoostedApplication(m_boostedApplication);
// Make sure that LD_BIND_NOW does not prevent dynamic linker to // Make sure that LD_BIND_NOW does not prevent dynamic linker to
// use lazy binding in later dlopen() calls. // use lazy binding in later dlopen() calls.
unsetenv("LD_BIND_NOW"); unsetenv("LD_BIND_NOW");
@ -156,7 +160,7 @@ void Daemon::run(Booster *booster)
// Create socket for the booster // Create socket for the booster
Logger::logDebug("Daemon: initing socket: %s", booster->boosterType().c_str()); Logger::logDebug("Daemon: initing socket: %s", booster->boosterType().c_str());
m_socketManager->initSocket(booster->boosterType()); m_socketManager->initSocket(booster->socketId());
// Daemonize if desired // Daemonize if desired
if (m_daemon) if (m_daemon)
@ -417,7 +421,7 @@ void Daemon::forkBooster(int sleepTime)
// Initialize and wait for commands from invoker // Initialize and wait for commands from invoker
try { try {
m_booster->initialize(m_initialArgc, m_initialArgv, m_boosterLauncherSocket[1], m_booster->initialize(m_initialArgc, m_initialArgv, m_boosterLauncherSocket[1],
m_socketManager->findSocket(m_booster->boosterType().c_str()), m_socketManager->findSocket(m_booster->socketId()),
m_singleInstance, m_bootMode); m_singleInstance, m_bootMode);
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
Logger::logError("Booster: Failed to initialize: %s\n", e.what()); Logger::logError("Booster: Failed to initialize: %s\n", e.what());
@ -603,33 +607,46 @@ void Daemon::daemonize()
void Daemon::parseArgs(const ArgVect & args) void Daemon::parseArgs(const ArgVect & args)
{ {
std::deque<string> queue;
for (ArgVect::const_iterator i(args.begin() + 1); i != args.end(); i++) for (ArgVect::const_iterator i(args.begin() + 1); i != args.end(); i++)
{ queue.push_back(*i);
if ((*i) == "--boot-mode" || (*i) == "-b")
while (!queue.empty()) {
string option(queue.front());
queue.pop_front();
if (option == "--boot-mode" || option == "-b")
{ {
Logger::logInfo("Daemon: Boot mode set."); Logger::logInfo("Daemon: Boot mode set.");
m_bootMode = true; m_bootMode = true;
} }
else if ((*i) == "--daemon" || (*i) == "-d") else if (option == "--daemon" || option == "-d")
{ {
m_daemon = true; m_daemon = true;
} }
else if ((*i) == "--debug") else if (option == "--debug")
{ {
Logger::setDebugMode(true); Logger::setDebugMode(true);
m_debugMode = true; m_debugMode = true;
} }
else if ((*i) == "--help" || (*i) == "-h") else if (option == "--help" || option == "-h")
{ {
usage(args[0].c_str(), EXIT_SUCCESS); usage(args[0].c_str(), EXIT_SUCCESS);
} }
else if ((*i) == "--systemd") else if (option == "--systemd")
{ {
m_notifySystemd = true; m_notifySystemd = true;
} }
else if (option == "--application" || option == "-a")
{
if (!queue.empty()) {
m_boostedApplication = queue.front();
queue.pop_front();
}
}
else else
{ {
if ((*i).find_first_not_of(' ') != string::npos) if (option.find_first_not_of(' ') != string::npos)
usage(args[0].c_str(), EXIT_FAILURE); usage(args[0].c_str(), EXIT_FAILURE);
} }
} }
@ -649,6 +666,8 @@ void Daemon::usage(const char *name, int status)
" Boot mode can be activated also by sending SIGUSR2\n" " Boot mode can be activated also by sending SIGUSR2\n"
" to the launcher.\n" " to the launcher.\n"
" -d, --daemon Run as %s a daemon.\n" " -d, --daemon Run as %s a daemon.\n"
" -a, --application <application>\n"
" Run as application specific booster.\n"
" --systemd Notify systemd when initialization is done\n" " --systemd Notify systemd when initialization is done\n"
" --debug Enable debug messages and log everything also to stdout.\n" " --debug Enable debug messages and log everything also to stdout.\n"
" -h, --help Print this help.\n\n", " -h, --help Print this help.\n\n",

@ -206,6 +206,7 @@ private:
//! True if systemd needs to be notified //! True if systemd needs to be notified
bool m_notifySystemd; bool m_notifySystemd;
string m_boostedApplication;
//! Drop capabilities needed for initialization //! Drop capabilities needed for initialization
static void dropCapabilities(); static void dropCapabilities();

@ -1,6 +1,8 @@
/*************************************************************************** /***************************************************************************
** **
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
** Copyright (c) 2013 - 2021 Jolla Ltd.
** Copyright (c) 2021 Open Mobile Platform LLC.
** All rights reserved. ** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com) ** Contact: Nokia Corporation (directui@nokia.com)
** **
@ -35,7 +37,7 @@ SocketManager::SocketManager()
{ {
const char *runtimeDir = getenv("XDG_RUNTIME_DIR"); const char *runtimeDir = getenv("XDG_RUNTIME_DIR");
if (!runtimeDir || !*runtimeDir) if (!runtimeDir || !*runtimeDir)
runtimeDir = "/tmp/"; runtimeDir = "/tmp";
m_socketRootPath = runtimeDir; m_socketRootPath = runtimeDir;
m_socketRootPath += "/mapplauncherd"; m_socketRootPath += "/mapplauncherd";
@ -46,66 +48,123 @@ SocketManager::SocketManager()
m_socketRootPath.c_str(), strerror(errno)); m_socketRootPath.c_str(), strerror(errno));
} }
} }
}
m_socketRootPath += '/'; static string extractTail(string &work)
{
string tok;
string::size_type n;
if ((n = work.rfind('/')) == string::npos) {
tok = work;
work.clear();
} else {
tok = work.substr(n + 1);
work.erase(n);
}
return tok;
} }
void SocketManager::initSocket(const string & socketId) string SocketManager::prepareSocket(const string &socketId) const
{ {
string socketPath = m_socketRootPath + socketId; string socketPath;
/* Extract APP/TYPE/SOCKET */
string work(socketId);
string socketFile(extractTail(work));
string typeId(extractTail(work));
string appId(extractTail(work));
if (!work.empty() || socketFile.empty()) {
Logger::logError("Daemon: Invalid socketId: %s\n", socketId.c_str());
return socketPath;
}
/* Construct socket path and subdirectories */
work = m_socketRootPath;
if (!appId.empty()) {
work += '/';
work += appId;
if (mkdir(work.c_str(), 0750) == -1 && errno != EEXIST) {
Logger::logError("Daemon: Cannot create socket app directory %s: %s\n",
work.c_str(), strerror(errno));
return socketPath;
}
}
if (!typeId.empty()) {
work += '/';
work += typeId;
if (mkdir(work.c_str(), 0750) == -1 && errno != EEXIST) {
Logger::logError("Daemon: Cannot create socket type directory %s: %s\n",
work.c_str(), strerror(errno));
return socketPath;
}
}
work += '/';
work += socketFile;
if (unlink(work.c_str()) == -1 && errno != ENOENT) {
Logger::logError("Daemon: Cannot remove stale socket %s: %s\n",
work.c_str(), strerror(errno));
return socketPath;
}
/* Success */
socketPath = work;
return socketPath;
}
void SocketManager::initSocket(const string & socketId)
{
// Initialize a socket at socketId if one already doesn't // Initialize a socket at socketId if one already doesn't
// exist for that id / path. // exist for that id / path.
if (m_socketHash.find(socketId) == m_socketHash.end()) if (m_socketHash.find(socketId) == m_socketHash.end())
{ {
string socketPath = prepareSocket(socketId);
if (socketPath.empty()) {
string msg;
msg += "SocketManager: Failed to prepare socketId ";
msg += socketId;
throw std::runtime_error(msg);
}
Logger::logDebug("SocketManager: Initing socket at '%s'..", socketPath.c_str()); Logger::logDebug("SocketManager: Initing socket at '%s'..", socketPath.c_str());
// Initialize the socket struct
struct sockaddr_un sun;
memset(&sun, 0, sizeof sun);
sun.sun_family = AF_UNIX;
size_t maxSize = sizeof(sun.sun_path);
int length = snprintf(sun.sun_path, maxSize, "%s", socketPath.c_str());
if (length <= 0 || (size_t)length >= maxSize) {
string msg;
msg += "SocketManager: Invalid socket path ";
msg += socketPath;
throw std::runtime_error(msg);
}
// Create a new local socket // Create a new local socket
int socketFd = socket(PF_UNIX, SOCK_STREAM, 0); int socketFd = socket(PF_UNIX, SOCK_STREAM, 0);
if (socketFd < 0) if (socketFd < 0)
throw std::runtime_error("SocketManager: Failed to open socket\n"); throw std::runtime_error("SocketManager: Failed to open socket\n");
// TODO: Error if socketPath >= maxLen. Also unlink() here may
// try to remove a different file than is passed to sun.sa_data.
// Remove the previous socket file
struct stat sb;
stat(socketPath.c_str(), &sb);
if (S_ISSOCK(sb.st_mode))
{
// coverity[toctou]
if (unlink(socketPath.c_str()) == -1)
{
std::string msg("SocketManager: Failed to unlink existing socket file '");
msg += socketPath + "': " + strerror(errno);
Logger::logWarning(msg.c_str());
}
}
// Initialize the socket struct
struct sockaddr_un sun;
sun.sun_family = AF_UNIX;
int maxLen = sizeof(sun.sun_path) - 1;
strncpy(sun.sun_path, socketPath.c_str(), maxLen);
sun.sun_path[maxLen] = '\0';
// Bind the socket // Bind the socket
if (bind(socketFd, (struct sockaddr*) &sun, sizeof(sun)) < 0) if (bind(socketFd, (struct sockaddr*) &sun, sizeof(sun)) < 0)
{ {
std::string msg("SocketManager: Failed to bind to socket (fd="); string msg;
std::stringstream ss; msg += "SocketManager: Failed to bind socket to ";
ss << socketFd; msg += socketPath;
msg += ss.str() + ")";
throw std::runtime_error(msg); throw std::runtime_error(msg);
} }
// Listen to the socket // Listen to the socket
if (listen(socketFd, 10) < 0) if (listen(socketFd, 10) < 0)
{ {
std::string msg("SocketManager: Failed to listen to socket (fd="); string msg;
std::stringstream ss; msg += "SocketManager: Failed to listen to socket ";
ss << socketFd; msg += socketPath;
msg += ss.str() + ")";
throw std::runtime_error(msg); throw std::runtime_error(msg);
} }
@ -165,6 +224,6 @@ void SocketManager::addMapping(const string & socketId, int fd)
string SocketManager::socketRootPath() const string SocketManager::socketRootPath() const
{ {
return m_socketRootPath; return m_socketRootPath + '/';
} }

@ -1,6 +1,8 @@
/*************************************************************************** /***************************************************************************
** **
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
** Copyright (c) 2012 - 2021 Jolla Ltd.
** Copyright (c) 2021 Open Mobile Platform LLC.
** All rights reserved. ** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com) ** Contact: Nokia Corporation (directui@nokia.com)
** **
@ -38,6 +40,8 @@ class DECL_EXPORT SocketManager
public: public:
SocketManager(); SocketManager();
string prepareSocket(const string &socketId) const;
/*! \brief Initialize a file socket. /*! \brief Initialize a file socket.
* \param socketId Path to the socket file. * \param socketId Path to the socket file.
*/ */

Loading…
Cancel
Save