From 66db6e7063af6f44e5ffabae529655db2bbe6530 Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Tue, 13 Apr 2021 10:50:31 +0300 Subject: [PATCH 1/2] [mapplauncherd] Cleanup compilation warnings Comparing signed vs unsigned integers. Unused static data. Questionable variable declarations. Const correctness issues. Unchecked socket and pipe i/o. Unchecked chdir() call. String sender that silently skips null strings while protocol does not make it possible for receiver to detect such omissions. Signed-off-by: Simo Piiroinen --- src/common/report.h | 6 ++- src/invoker/invokelib.c | 38 +++++++++------ src/invoker/invokelib.h | 6 ++- src/invoker/invoker.c | 15 +++--- src/launcherlib/booster.cpp | 17 +++++-- src/launcherlib/connection.cpp | 7 +-- src/launcherlib/daemon.cpp | 88 +++++++++++++++++----------------- src/launcherlib/daemon.h | 9 ++-- src/launcherlib/logger.cpp | 12 +++-- 9 files changed, 112 insertions(+), 86 deletions(-) diff --git a/src/common/report.h b/src/common/report.h index 08858f4..85b3a2f 100644 --- a/src/common/report.h +++ b/src/common/report.h @@ -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) 2021 Open Mobile Platform LLC. +** Copyright (c) 2021 Jolla Ltd. ** All rights reserved. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -46,7 +48,7 @@ extern void report(enum report_type type, const char *msg, ...); #ifndef DEBUG_LOGGING_DISABLED #define debug(msg, ...) report(report_debug, msg, ##__VA_ARGS__) #else -#define debug(...) +#define debug(...) do {} while (0) #endif #define info(msg, ...) report(report_info, msg, ##__VA_ARGS__) diff --git a/src/invoker/invokelib.c b/src/invoker/invokelib.c index fb516c7..2de5013 100644 --- a/src/invoker/invokelib.c +++ b/src/invoker/invokelib.c @@ -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) 2021 Open Mobile Platform LLC. +** Copyright (c) 2021 Jolla Ltd. ** All rights reserved. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -26,10 +28,21 @@ #include "report.h" #include "invokelib.h" +static void invoke_send_or_die(int fd, const void *data, size_t size) +{ + if (write(fd, data, size) != (ssize_t)size) { + const char m[] = "*** socket write failure, terminating\n"; + if (write(STDERR_FILENO, m, sizeof m - 1) == -1) { + // dontcare + } + _exit(EXIT_FAILURE); + } +} + void invoke_send_msg(int fd, uint32_t msg) { debug("%s: %08x\n", __FUNCTION__, msg); - write(fd, &msg, sizeof(msg)); + invoke_send_or_die(fd, &msg, sizeof(msg)); } bool invoke_recv_msg(int fd, uint32_t *msg) @@ -58,20 +71,17 @@ bool invoke_recv_msg(int fd, uint32_t *msg) } } -void invoke_send_str(int fd, char *str) +void invoke_send_str(int fd, const char *str) { - if (str) - { - uint32_t size; + if (!str) + str = ""; + uint32_t size = strlen(str) + 1; - /* Send size. */ - size = strlen(str) + 1; - invoke_send_msg(fd, size); + /* Send size. */ + invoke_send_msg(fd, size); - debug("%s: '%s'\n", __FUNCTION__, str); + debug("%s: '%s'\n", __FUNCTION__, str); - /* Send the string. */ - write(fd, str, size); - } + /* Send the string. */ + invoke_send_or_die(fd, str, size); } - diff --git a/src/invoker/invokelib.h b/src/invoker/invokelib.h index 615799a..eb1e429 100644 --- a/src/invoker/invokelib.h +++ b/src/invoker/invokelib.h @@ -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. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -25,7 +27,7 @@ void invoke_send_msg(int fd, uint32_t msg); bool invoke_recv_msg(int fd, uint32_t *msg); -void invoke_send_str(int fd, char *str); +void invoke_send_str(int fd, const char *str); // Existence of the test mode control file is checked // to enable test mode. diff --git a/src/invoker/invoker.c b/src/invoker/invoker.c index 873a082..67b9de6 100644 --- a/src/invoker/invoker.c +++ b/src/invoker/invoker.c @@ -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. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -55,7 +57,6 @@ static const unsigned int RESPAWN_DELAY = 1; static const unsigned int MIN_RESPAWN_DELAY = 0; static const unsigned int MAX_RESPAWN_DELAY = 10; -static const unsigned char EXIT_STATUS_APPLICATION_CONNECTION_LOST = 0xfa; static const unsigned char EXIT_STATUS_APPLICATION_NOT_FOUND = 0x7f; // Environment @@ -99,7 +100,8 @@ static void sig_forwarder(int sig) // Write signal number to the self-pipe char signal_id = (char) sig; - write(g_signal_pipe[1], &signal_id, 1); + if (write(g_signal_pipe[1], &signal_id, 1) != 1) + _exit(EXIT_FAILURE); // Send the signal to itself using the default handler raise(sig); @@ -208,7 +210,7 @@ static int invoker_init(const char *app_type) strcat(sun.sun_path, subpath); maxSize -= strlen(sun.sun_path); - if (maxSize < strlen(app_type) || strchr(app_type, '/')) + if (maxSize < (int)strlen(app_type) || strchr(app_type, '/')) die(1, "Invalid type of application: %s\n", app_type); strcat(sun.sun_path, app_type); @@ -274,7 +276,7 @@ static void invoker_send_magic(int fd, uint32_t options) } // Sends the process name to be invoked. -static void invoker_send_name(int fd, char *name) +static void invoker_send_name(int fd, const char *name) { invoke_send_msg(fd, INVOKER_MSG_NAME); invoke_send_str(fd, name); @@ -546,7 +548,8 @@ static int wait_for_launched_process_to_exit(int socket_fd, bool wait_term) { // Clean up the pipe char signal_id; - read(g_signal_pipe[0], &signal_id, sizeof(signal_id)); + if (read(g_signal_pipe[0], &signal_id, 1) != 1) + exit(EXIT_FAILURE); // Set signals forwarding to the invoked process again // (they were reset by the signal forwarder). diff --git a/src/launcherlib/booster.cpp b/src/launcherlib/booster.cpp index aa049fe..c71c330 100644 --- a/src/launcherlib/booster.cpp +++ b/src/launcherlib/booster.cpp @@ -1,8 +1,8 @@ /*************************************************************************** ** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** Copyright (C) 2013 - 2021 Jolla Ltd. -** Copyright (C) 2018 - 2020 Open Mobile Platform LLC. +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (c) 2013 - 2021 Jolla Ltd. +** Copyright (c) 2018 - 2021 Open Mobile Platform LLC. ** All rights reserved. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -478,7 +478,16 @@ void Booster::setEnvironmentBeforeLaunch() // Set PWD const char * pwd = getenv("PWD"); - if (pwd) chdir(pwd); + if (pwd) { + if (chdir(pwd) == -1) { + Logger::logWarning("Booster: chdir(\"%s\") failed: %m", pwd); + pwd = "/"; + if (chdir(pwd) == -1) { + Logger::logWarning("Booster: chdir(\"%s\") failed: %m", pwd); + exit(EXIT_FAILURE); + } + } + } Logger::logDebug("Booster: launching process: '%s' ", m_appData->fileName().c_str()); } diff --git a/src/launcherlib/connection.cpp b/src/launcherlib/connection.cpp index 7f08c03..a883885 100644 --- a/src/launcherlib/connection.cpp +++ b/src/launcherlib/connection.cpp @@ -1,7 +1,8 @@ /*************************************************************************** ** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** Copyright (C) 2021 Jolla Ltd. +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (c) 2021 Jolla Ltd. +** Copyright (c) 2021 Open Mobile Platform LLC. ** All rights reserved. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -620,7 +621,7 @@ std::string Connection::getExecutablePath(pid_t pid) static_assert(sizeof(INVOKER_PATH) < sizeof(exe)); path << "/proc/" << pid << "/exe"; len = readlink(path.str().c_str(), exe, sizeof(exe)); - if (len < 0 || len >= sizeof(exe)) + if (len < 0 || (size_t)len >= sizeof(exe)) { Logger::logError("Connection: %s: readlink failed for pid %d: %s", __FUNCTION__, pid, strerror(errno)); return std::string(); diff --git a/src/launcherlib/daemon.cpp b/src/launcherlib/daemon.cpp index 13fefdf..b6c021f 100644 --- a/src/launcherlib/daemon.cpp +++ b/src/launcherlib/daemon.cpp @@ -1,8 +1,8 @@ /*************************************************************************** ** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** Copyright (C) 2013 - 2021 Jolla Ltd. -** Copyright (C) 2020 Open Mobile Platform LLC. +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (c) 2013 - 2021 Jolla Ltd. +** Copyright (c) 2020 - 2021 Open Mobile Platform LLC. ** All rights reserved. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -53,43 +53,39 @@ extern char ** environ; Daemon * Daemon::m_instance = NULL; const int Daemon::m_boosterSleepTime = 2; -const std::string Daemon::m_stateDir = std::string(getenv("XDG_RUNTIME_DIR"))+"/applauncherd"; -const std::string Daemon::m_stateFile = Daemon::m_stateDir + "/saved-state"; - -static void sigChldHandler(int) -{ - char v = SIGCHLD; - write(Daemon::instance()->sigPipeFd(), &v, 1); -} - -static void sigTermHandler(int) +static void write_dontcare(int fd, const void *data, size_t size) { - char v = SIGTERM; - write(Daemon::instance()->sigPipeFd(), &v, 1); + ssize_t rc = write(fd, data, size); + if (rc == -1) + Logger::logWarning("write to fd=%d failed: %m", fd); + else if ((size_t)rc != size) + Logger::logWarning("write to fd=%d failed", fd); } -static void sigUsr1Handler(int) +static void write_to_signal_pipe(int sig) { - char v = SIGUSR1; - write(Daemon::instance()->sigPipeFd(), &v, 1); -} - -static void sigUsr2Handler(int) -{ - char v = SIGUSR2; - write(Daemon::instance()->sigPipeFd(), &v, 1); -} - -static void sigPipeHandler(int) -{ - char v = SIGPIPE; - write(Daemon::instance()->sigPipeFd(), &v, 1); + char v = (char)sig; + if (write(Daemon::instance()->sigPipeFd(), &v, 1) != 1) { + /* If we can't write to internal signal forwarding + * pipe, we might as well quit */ + const char m[] = "*** signal pipe write failure - terminating\n"; + if (write(STDERR_FILENO, m, sizeof m - 1) == -1) { + // dontcare + } + _exit(EXIT_FAILURE); + } } -static void sigHupHandler(int) +static int read_from_signal_pipe(int fd) { - char v = SIGHUP; - write(Daemon::instance()->sigPipeFd(), &v, 1); + char sig = 0; + if (read(fd, &sig, 1) != 1) { + /* If we can't read from internal signal forwarding + * pipe, we might as well quit */ + Logger::logError("signal pipe read failure - terminating\n"); + exit(EXIT_FAILURE); + } + return sig; } Daemon::Daemon(int & argc, char * argv[]) : @@ -108,12 +104,12 @@ Daemon::Daemon(int & argc, char * argv[]) : // Install signal handlers. The original handlers are saved // in the daemon instance so that they can be restored in boosters. - setUnixSignalHandler(SIGCHLD, sigChldHandler); // reap zombies - setUnixSignalHandler(SIGTERM, sigTermHandler); // exit launcher - setUnixSignalHandler(SIGUSR1, sigUsr1Handler); // enter normal mode from boot mode - setUnixSignalHandler(SIGUSR2, sigUsr2Handler); // enter boot mode (same as --boot-mode) - setUnixSignalHandler(SIGPIPE, sigPipeHandler); // broken invoker's pipe - setUnixSignalHandler(SIGHUP, sigHupHandler); // re-exec + setUnixSignalHandler(SIGCHLD, write_to_signal_pipe); // reap zombies + setUnixSignalHandler(SIGTERM, write_to_signal_pipe); // exit launcher + setUnixSignalHandler(SIGUSR1, write_to_signal_pipe); // enter normal mode from boot mode + setUnixSignalHandler(SIGUSR2, write_to_signal_pipe); // enter boot mode (same as --boot-mode) + setUnixSignalHandler(SIGPIPE, write_to_signal_pipe); // broken invoker's pipe + setUnixSignalHandler(SIGHUP, write_to_signal_pipe); // re-exec if (!Daemon::m_instance) { @@ -210,8 +206,7 @@ void Daemon::run(Booster *booster) if (FD_ISSET(m_sigPipeFd[0], &rfds)) { Logger::logDebug("Daemon: FD_ISSET(m_sigPipeFd[0])"); - char dataReceived; - read(m_sigPipeFd[0], &dataReceived, 1); + int dataReceived = read_from_signal_pipe(m_sigPipeFd[0]); switch (dataReceived) { @@ -380,6 +375,10 @@ void Daemon::forkBooster(int sleepTime) if (newPid == 0) /* Child process */ { + // Will be reopened with new identity when/if + // there is something to report + Logger::closeLog(); + // Restore used signal handlers restoreUnixSignalHandlers(); @@ -483,9 +482,10 @@ void Daemon::reapZombies() FdMap::iterator fd = m_boosterPidToInvokerFd.find(pid); if (fd != m_boosterPidToInvokerFd.end()) { - write((*fd).second, &INVOKER_MSG_EXIT, sizeof(uint32_t)); - int exitStatus = WEXITSTATUS(status); - write((*fd).second, &exitStatus, sizeof(int)); + uint32_t command = INVOKER_MSG_EXIT; + uint32_t argument = WEXITSTATUS(status); + write_dontcare((*fd).second, &command, sizeof command); + write_dontcare((*fd).second, &argument, sizeof argument); close((*fd).second); m_boosterPidToInvokerFd.erase(fd); } diff --git a/src/launcherlib/daemon.h b/src/launcherlib/daemon.h index 0f7c4db..d0838ec 100644 --- a/src/launcherlib/daemon.h +++ b/src/launcherlib/daemon.h @@ -1,7 +1,8 @@ /*************************************************************************** ** -** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). -** Copyright (C) 2013 - 2021 Jolla Ltd. +** 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. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -212,10 +213,6 @@ private: //! Booster instance Booster * m_booster; - //! Name of the state saving directory and file - static const std::string m_stateDir; - static const std::string m_stateFile; - #ifdef UNIT_TEST friend class Ut_Daemon; #endif diff --git a/src/launcherlib/logger.cpp b/src/launcherlib/logger.cpp index 1b8cc67..f2f6aa9 100644 --- a/src/launcherlib/logger.cpp +++ b/src/launcherlib/logger.cpp @@ -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. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -73,7 +75,7 @@ void Logger::logDebug(const char * format, ...) { if (m_debugMode) { - va_list(ap); + va_list ap; va_start(ap, format); writeLog(LOG_DEBUG, format, ap); va_end(ap); @@ -82,7 +84,7 @@ void Logger::logDebug(const char * format, ...) void Logger::logInfo(const char * format, ...) { - va_list(ap); + va_list ap; va_start(ap, format); writeLog(LOG_INFO, format, ap); va_end(ap); @@ -92,7 +94,7 @@ void Logger::logInfo(const char * format, ...) void Logger::logWarning(const char * format, ...) { - va_list(ap); + va_list ap; va_start(ap, format); writeLog(LOG_WARNING, format, ap); va_end(ap); @@ -100,7 +102,7 @@ void Logger::logWarning(const char * format, ...) void Logger::logError(const char * format, ...) { - va_list(ap); + va_list ap; va_start(ap, format); writeLog(LOG_ERR, format, ap); va_end(ap); From 23846d0db409673398d17290c26375880db6b476 Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Tue, 13 Apr 2021 12:11:56 +0300 Subject: [PATCH 2/2] [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 --- src/common/report.h | 2 +- src/invoker/invoker.c | 439 +++++++++++++++++++----------- src/launcherlib/booster.cpp | 57 ++++ src/launcherlib/booster.h | 12 +- src/launcherlib/daemon.cpp | 37 ++- src/launcherlib/daemon.h | 1 + src/launcherlib/socketmanager.cpp | 135 ++++++--- src/launcherlib/socketmanager.h | 6 +- 8 files changed, 474 insertions(+), 215 deletions(-) diff --git a/src/common/report.h b/src/common/report.h index 85b3a2f..4bc3405 100644 --- a/src/common/report.h +++ b/src/common/report.h @@ -43,7 +43,7 @@ enum report_type { }; 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 #define debug(msg, ...) report(report_debug, msg, ##__VA_ARGS__) diff --git a/src/invoker/invoker.c b/src/invoker/invoker.c index 67b9de6..87c7b33 100644 --- a/src/invoker/invoker.c +++ b/src/invoker/invoker.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,71 @@ #include "invokelib.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. static const unsigned int EXIT_DELAY = 0; static const unsigned int MIN_EXIT_DELAY = 1; @@ -71,8 +137,6 @@ static void sigs_init(void); //! Pipe used to safely catch Unix signals static int g_signal_pipe[2]; -static const char *g_desktop_file = NULL; - // Forwards Unix signals from invoker to the invoked process 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 -static int invoker_init(const char *app_type) +static int invoker_init(const char *app_type, const char *app_name) { - int fd; - struct sockaddr_un sun; + bool connected = false; + int fd = -1; - fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (fd < 0) - { - error("Failed to open invoker socket for type %s.\n", app_type); - return -1; - } + /* Sanity check args */ + if (!app_type || strchr(app_type, '/')) + goto EXIT; + if (app_name && strchr(app_name, '/')) + goto EXIT; - sun.sun_family = AF_UNIX; - int maxSize = sizeof(sun.sun_path) - 1; - const char *runtimeDir = getenv("XDG_RUNTIME_DIR"); - const char *subpath = "/mapplauncherd/"; - const int subpathLen = strlen(subpath); + if (!runtimeDir || !*runtimeDir) { + error("XDG_RUNTIME_DIR is not defined.\n"); + goto EXIT; + } - if (runtimeDir && *runtimeDir) - strncpy(sun.sun_path, runtimeDir, maxSize - subpathLen); - else - strncpy(sun.sun_path, "/tmp", maxSize - subpathLen); + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { + error("Failed to create socket: %m\n"); + goto EXIT; + } - sun.sun_path[maxSize - subpathLen] = 0; - strcat(sun.sun_path, subpath); + struct sockaddr_un sun = { + .sun_family = AF_UNIX, + }; + int maxSize = sizeof(sun.sun_path); + int length; - maxSize -= strlen(sun.sun_path); - if (maxSize < (int)strlen(app_type) || strchr(app_type, '/')) - die(1, "Invalid type of application: %s\n", app_type); + if (app_name) + length = snprintf(sun.sun_path, maxSize, "%s/mapplauncherd/_%s/%s/socket", + 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) - { - error("Failed to initiate connect on the socket for type %s.\n", app_type); - return -1; + if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + if (errno != ENOENT) + warning("connect(\"%s\") failed: %m\n", sun.sun_path); + goto EXIT; } + debug("connected to: %s\n", sun.sun_path); + connected = true; + +EXIT: + if (!connected && fd != -1) + close(fd), fd = -1; return fd; } @@ -565,9 +646,36 @@ static int wait_for_launched_process_to_exit(int socket_fd, bool wait_term) 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 -static int invoke_remote(int socket_fd, int prog_argc, char **prog_argv, char *prog_name, - uint32_t magic_options, bool wait_term, unsigned int respawn_delay) +static int invoke_remote(int socket_fd, const InvokeArgs *args) { // Get process priority 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, // send the data. - invoker_send_magic(socket_fd, magic_options); - invoker_send_name(socket_fd, prog_name); - invoker_send_exec(socket_fd, prog_argv[0]); - invoker_send_args(socket_fd, prog_argc, prog_argv); + invoker_send_magic(socket_fd, args->magic_options); + invoker_send_name(socket_fd, args->prog_name); + invoker_send_exec(socket_fd, args->prog_argv[0]); + invoker_send_args(socket_fd, args->prog_argc, args->prog_argv); 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_io(socket_fd); invoker_send_env(socket_fd); invoker_send_end(socket_fd); - if (prog_name) - { - free(prog_name); - } + if (args->desktop_file) + notify_app_lauch(args->desktop_file); - if (g_desktop_file) { - notify_app_lauch(g_desktop_file); - } - int exit_status = wait_for_launched_process_to_exit(socket_fd, wait_term); + int exit_status = wait_for_launched_process_to_exit(socket_fd, args->wait_term); 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, // try to launch application via execve 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 - if(!wait_term) + if (!args->wait_term) { // Fork a new process 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 - execve(prog_name, prog_argv, environ); + execve(args->prog_name, args->prog_argv, environ); perror("execve"); /* execve() only returns on error */ exit(EXIT_FAILURE); } // Invokes the given application -static int invoke(int prog_argc, char **prog_argv, char *prog_name, - const char *app_type, uint32_t magic_options, bool wait_term, unsigned int respawn_delay, - bool test_mode) +static int invoke(InvokeArgs *args) { - int status = 0; - if (prog_name && prog_argv) - { - //If TEST_MODE_CONTROL_FILE doesn't exists switch off test mode - if (test_mode && access(TEST_MODE_CONTROL_FILE, F_OK) != 0) - { - test_mode = false; - info("Invoker test mode is not enabled.\n"); - } - - // The app can be launched with a comma delimited list of boosters to attempt. - - char *app_type_list = strdup(app_type); - char *saveptr = NULL; - char *token; + /* Note: Contents of 'args' are assumed to have been + * checked and sanitized before invoke() call. + */ + + int status = EXIT_FAILURE; + + /* The app can be launched with a comma delimited list of + * booster types to attempt. + */ + char **types = split(args->app_type, ","); + + int fd = -1; + + /* Session booster is a special case: + * - 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; - for (token = strtok_r(app_type_list, ",", &saveptr); fd == -1 && token != NULL; token = strtok_r(NULL, ",", &saveptr)) - { - app_type = token; - if (app_type != app_type_list) { - info("Trying fallback type %s.\n", app_type); - } - fd = invoker_init(app_type); - } - - 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); + /* Application aware boosters + * - have fallback strategy, but it + * - must not cross application vs "default" boundary + */ + if (fd == -1 && !tried_session) { + bool tried_generic = false; + for (size_t i = 0; fd == -1 && types[i]; ++i) { + if (!strcmp(types[i], "generic")) + tried_generic = true; + fd = invoker_init(types[i], args->app_name); } + 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; } int main(int argc, char *argv[]) { - const char *app_type = NULL; - 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; + InvokeArgs args = INVOKE_ARGS_INIT; // Called with a different name (old way of using 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 {"test-mode", no_argument, NULL, 'T'}, {"type", required_argument, NULL, 't'}, + {"application", required_argument, NULL, 'a'}, {"delay", required_argument, NULL, 'd'}, {"respawn", required_argument, NULL, 'r'}, {"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 environment 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) { @@ -758,41 +858,45 @@ int main(int argc, char *argv[]) break; case 'o': - magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE; + args.magic_options |= INVOKER_MSG_MAGIC_OPTION_OOM_ADJ_DISABLE; break; case 'n': - wait_term = false; - magic_options &= (~INVOKER_MSG_MAGIC_OPTION_WAIT); + args.wait_term = false; + args.magic_options &= (~INVOKER_MSG_MAGIC_OPTION_WAIT); break; case 'G': - magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL; + args.magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_GLOBAL; break; case 'D': - magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_DEEP; + args.magic_options |= INVOKER_MSG_MAGIC_OPTION_DLOPEN_DEEP; break; case 'T': - test_mode = true; + args.test_mode = true; break; case 't': - app_type = optarg; + args.app_type = optarg; + break; + + case 'a': + args.app_name = optarg; break; 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; case 'r': - respawn_delay = get_delay(optarg, "respawn delay", + args.respawn_delay = get_delay(optarg, "respawn delay", MIN_RESPAWN_DELAY, MAX_RESPAWN_DELAY); break; case 's': - magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE; + args.magic_options |= INVOKER_MSG_MAGIC_OPTION_SINGLE_INSTANCE; break; case 'S': @@ -801,7 +905,7 @@ int main(int argc, char *argv[]) break; case 'F': - g_desktop_file = optarg; + args.desktop_file = optarg; break; case '?': @@ -810,57 +914,63 @@ int main(int argc, char *argv[]) } // 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 - // application to find out its installation directory from there - prog_argv[0] = prog_name; - } + args.prog_argc = argc - optind; + args.prog_argv = &argv[optind]; - // Check if application name isn't defined - if (!prog_name) - { - report(report_error, "Application's name is not defined.\n"); - usage(1); + if (args.prog_argc < 1) { + report(report_error, "No command line to invoke was given.\n"); + exit(EXIT_FAILURE); } + // 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 - if (stat(prog_name, &file_stat)) - { - report(report_error, "%s: not found\n", prog_name); + struct stat file_stat; + if (stat(args.prog_argv[0], &file_stat) == -1) { + report(report_error, "%s: not found: %m\n", args.prog_argv[0]); return EXIT_STATUS_APPLICATION_NOT_FOUND; } - // Check that - if (!S_ISREG(file_stat.st_mode) && !S_ISLNK(file_stat.st_mode)) - { - report(report_error, "%s: not a file\n", prog_name); + // Check that application is regular file (or symlink to such) + if (!S_ISREG(file_stat.st_mode)) { + report(report_error, "%s: not a file\n", args.prog_argv[0]); return EXIT_STATUS_APPLICATION_NOT_FOUND; } // 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) - if (strcmp(prog_name, "/usr/bin/sailfish-qml") == 0) { - if (prog_argc < 2) { - report(report_error, "%s: requires an argument\n", prog_name); + if (strcmp(args.prog_argv[0], "/usr/bin/sailfish-qml") == 0) { + if (args.prog_argc < 2) { + report(report_error, "%s: requires an argument\n", args.prog_argv[0]); return EXIT_STATUS_APPLICATION_NOT_FOUND; } - // Must not free() the existing prog_name, as it's pointing to prog_argv[0] - prog_name = (char *)malloc(strlen(prog_argv[0]) + strlen(prog_argv[1]) + 2); - sprintf(prog_name, "%s %s", prog_argv[0], prog_argv[1]); + if (asprintf(&args.prog_name, "%s %s", args.prog_argv[0], args.prog_argv[1]) < 0) + exit(EXIT_FAILURE); + } 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"); 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) { 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 - info("Invoking execution: '%s'\n", prog_name); - int ret_val = invoke(prog_argc, prog_argv, prog_name, app_type, magic_options, wait_term, respawn_delay, test_mode); + info("Invoking execution: '%s'\n", args.prog_name); + int ret_val = invoke(&args); // Sleep for delay before exiting - if (delay) - { + if (args.exit_delay) { // DBUS cannot cope some times if the invoker exits too early. - debug("Delaying exit for %d seconds..\n", delay); - sleep(delay); + debug("Delaying exit for %d seconds..\n", args.exit_delay); + sleep(args.exit_delay); } return ret_val; diff --git a/src/launcherlib/booster.cpp b/src/launcherlib/booster.cpp index c71c330..61c4f6c 100644 --- a/src/launcherlib/booster.cpp +++ b/src/launcherlib/booster.cpp @@ -58,6 +58,7 @@ Booster::Booster() : m_oldPriority(0), m_oldPriorityOk(false), m_spaceAvailable(0), + m_boostedApplication("default"), m_bootMode(false) { Connection::setMountNamespace(Connection::getMountNamespace(getpid())); @@ -586,6 +587,62 @@ bool Booster::popPriority() 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() { if (m_connection->isReportAppExitStatusNeeded()) diff --git a/src/launcherlib/booster.h b/src/launcherlib/booster.h index 2ce43fb..d5cb444 100644 --- a/src/launcherlib/booster.h +++ b/src/launcherlib/booster.h @@ -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. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -107,6 +109,11 @@ public: */ virtual const string & boosterType() const = 0; + const string &boostedApplication() const; + void setBoostedApplication(const string &application); + + const string socketId() const; + //! Get invoker's pid pid_t invokersPid(); @@ -213,6 +220,9 @@ private: //! Original space available for arguments int m_spaceAvailable; + //! Application name when applicable, or "default" + string m_boostedApplication; + //! True, if being run in boot mode. bool m_bootMode; diff --git a/src/launcherlib/daemon.cpp b/src/launcherlib/daemon.cpp index b6c021f..763d44c 100644 --- a/src/launcherlib/daemon.cpp +++ b/src/launcherlib/daemon.cpp @@ -26,6 +26,7 @@ #include "singleinstance.h" #include "socketmanager.h" +#include #include #include #include @@ -147,6 +148,9 @@ void Daemon::run(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 // use lazy binding in later dlopen() calls. unsetenv("LD_BIND_NOW"); @@ -156,7 +160,7 @@ void Daemon::run(Booster *booster) // Create socket for the booster Logger::logDebug("Daemon: initing socket: %s", booster->boosterType().c_str()); - m_socketManager->initSocket(booster->boosterType()); + m_socketManager->initSocket(booster->socketId()); // Daemonize if desired if (m_daemon) @@ -417,7 +421,7 @@ void Daemon::forkBooster(int sleepTime) // Initialize and wait for commands from invoker try { 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); } catch (const std::runtime_error &e) { Logger::logError("Booster: Failed to initialize: %s\n", e.what()); @@ -603,33 +607,46 @@ void Daemon::daemonize() void Daemon::parseArgs(const ArgVect & args) { + std::deque queue; for (ArgVect::const_iterator i(args.begin() + 1); i != args.end(); i++) - { - if ((*i) == "--boot-mode" || (*i) == "-b") + queue.push_back(*i); + + while (!queue.empty()) { + string option(queue.front()); + queue.pop_front(); + + if (option == "--boot-mode" || option == "-b") { Logger::logInfo("Daemon: Boot mode set."); m_bootMode = true; } - else if ((*i) == "--daemon" || (*i) == "-d") + else if (option == "--daemon" || option == "-d") { m_daemon = true; } - else if ((*i) == "--debug") + else if (option == "--debug") { Logger::setDebugMode(true); m_debugMode = true; } - else if ((*i) == "--help" || (*i) == "-h") + else if (option == "--help" || option == "-h") { usage(args[0].c_str(), EXIT_SUCCESS); } - else if ((*i) == "--systemd") + else if (option == "--systemd") { m_notifySystemd = true; } + else if (option == "--application" || option == "-a") + { + if (!queue.empty()) { + m_boostedApplication = queue.front(); + queue.pop_front(); + } + } else { - if ((*i).find_first_not_of(' ') != string::npos) + if (option.find_first_not_of(' ') != string::npos) 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" " to the launcher.\n" " -d, --daemon Run as %s a daemon.\n" + " -a, --application \n" + " Run as application specific booster.\n" " --systemd Notify systemd when initialization is done\n" " --debug Enable debug messages and log everything also to stdout.\n" " -h, --help Print this help.\n\n", diff --git a/src/launcherlib/daemon.h b/src/launcherlib/daemon.h index d0838ec..75c0915 100644 --- a/src/launcherlib/daemon.h +++ b/src/launcherlib/daemon.h @@ -206,6 +206,7 @@ private: //! True if systemd needs to be notified bool m_notifySystemd; + string m_boostedApplication; //! Drop capabilities needed for initialization static void dropCapabilities(); diff --git a/src/launcherlib/socketmanager.cpp b/src/launcherlib/socketmanager.cpp index a6124eb..67cbe0e 100644 --- a/src/launcherlib/socketmanager.cpp +++ b/src/launcherlib/socketmanager.cpp @@ -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. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -35,7 +37,7 @@ SocketManager::SocketManager() { const char *runtimeDir = getenv("XDG_RUNTIME_DIR"); if (!runtimeDir || !*runtimeDir) - runtimeDir = "/tmp/"; + runtimeDir = "/tmp"; m_socketRootPath = runtimeDir; m_socketRootPath += "/mapplauncherd"; @@ -46,66 +48,123 @@ SocketManager::SocketManager() 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 // exist for that id / path. 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()); + // 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 int socketFd = socket(PF_UNIX, SOCK_STREAM, 0); if (socketFd < 0) 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 if (bind(socketFd, (struct sockaddr*) &sun, sizeof(sun)) < 0) { - std::string msg("SocketManager: Failed to bind to socket (fd="); - std::stringstream ss; - ss << socketFd; - msg += ss.str() + ")"; + string msg; + msg += "SocketManager: Failed to bind socket to "; + msg += socketPath; throw std::runtime_error(msg); } // Listen to the socket if (listen(socketFd, 10) < 0) { - std::string msg("SocketManager: Failed to listen to socket (fd="); - std::stringstream ss; - ss << socketFd; - msg += ss.str() + ")"; + string msg; + msg += "SocketManager: Failed to listen to socket "; + msg += socketPath; throw std::runtime_error(msg); } @@ -165,6 +224,6 @@ void SocketManager::addMapping(const string & socketId, int fd) string SocketManager::socketRootPath() const { - return m_socketRootPath; + return m_socketRootPath + '/'; } diff --git a/src/launcherlib/socketmanager.h b/src/launcherlib/socketmanager.h index 58e3c5d..f319b1c 100644 --- a/src/launcherlib/socketmanager.h +++ b/src/launcherlib/socketmanager.h @@ -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. ** Contact: Nokia Corporation (directui@nokia.com) ** @@ -38,6 +40,8 @@ class DECL_EXPORT SocketManager public: SocketManager(); + string prepareSocket(const string &socketId) const; + /*! \brief Initialize a file socket. * \param socketId Path to the socket file. */