Skip to content

Commit

Permalink
Rework miral::launch_app_env() to avoid calling functions that are …
Browse files Browse the repository at this point in the history
…not async-signal-safe after fork() (#3591)

Fixes: #3494
  • Loading branch information
AlanGriffiths authored Sep 11, 2024
2 parents ecc4899 + df25e31 commit 2ef1ecf
Showing 1 changed file with 91 additions and 54 deletions.
145 changes: 91 additions & 54 deletions src/miral/launch_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,69 @@

namespace
{
auto mir_env_variables() -> std::vector<std::string>
class Environment
{
static char const mir_prefix[] = "MIR_";
public:
Environment();

void setenv(std::string const& name, std::string const& value);
void unsetenv(std::string const& name);
auto exec_env() const & -> std::vector<char const*>;
private:
std::vector<std::string> env_strings;
};

std::vector<std::string> vars_to_remove;
Environment::Environment()
{
static char const mir_prefix[] = "MIR_";

for (auto var = environ; *var; ++var)
{
auto const var_begin = *var;
if (strncmp(var_begin, mir_prefix, sizeof(mir_prefix) - 1) == 0)
if (strncmp(var_begin, mir_prefix, sizeof(mir_prefix) - 1) != 0)
{
env_strings.emplace_back(var_begin);
}
}
}

void Environment::setenv(std::string const& name, std::string const& value)
{
auto const entry = name + "=" + value;

for (auto& e : env_strings)
{
if (strncmp(e.c_str(), name.c_str(), name.size()) == 0)
{
if (auto var_end = strchr(var_begin, '='))
{
vars_to_remove.emplace_back(var_begin, var_end);
}
else
{
vars_to_remove.emplace_back(var_begin);
}
e = entry;
return;
}
}

return vars_to_remove;
env_strings.emplace_back(entry);
}

void Environment::unsetenv(std::string const& name)
{
for (auto it = env_strings.begin(); it != env_strings.end(); ++it)
{
if (strncmp(it->c_str(), name.c_str(), name.size()) == 0)
{
env_strings.erase(it);
return;
}
}
}

auto Environment::exec_env() const & -> std::vector<char const*>
{
std::vector<char const*> result;

for (auto const& arg : env_strings)
result.push_back(arg.c_str());

result.push_back(nullptr);
return result;
}
} // namespace

Expand All @@ -61,65 +101,62 @@ auto miral::launch_app_env(
mir::optional_value<std::string> const& x11_display,
miral::AppEnvironment const& app_env) -> pid_t
{
static auto const vars_to_remove = mir_env_variables();
Environment application_environment;

pid_t pid = fork();

if (pid < 0)
for (auto const& [key, value]: app_env)
{
BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to fork process"}));
}

if (pid == 0)
{
for (auto const& var : vars_to_remove)
{
unsetenv(var.c_str());
}

if (x11_display)
if (value)
{
setenv("DISPLAY", x11_display.value().c_str(), true); // configure X11 socket
application_environment.setenv(key, value.value());
}
else
{
unsetenv("DISPLAY");
application_environment.unsetenv(key);
}
}

if (wayland_display)
{
setenv("WAYLAND_DISPLAY", wayland_display.value().c_str(), true); // configure Wayland socket
}
else
{
unsetenv("WAYLAND_DISPLAY");
}
if (wayland_display)
{
application_environment.setenv("WAYLAND_DISPLAY", wayland_display.value()); // configure Wayland socket
}
else
{
application_environment.unsetenv("WAYLAND_DISPLAY");
}

for (auto const& env : app_env)
{
if (env.second)
{
setenv(env.first.c_str(), env.second.value().c_str(), true);
}
else
{
unsetenv(env.first.c_str());
}
}
if (x11_display)
{
application_environment.setenv("DISPLAY", x11_display.value()); // configure X11 socket
}
else
{
application_environment.unsetenv("DISPLAY");
}

auto const exec_env = application_environment.exec_env();

std::vector<char const*> exec_args;
std::vector<char const*> exec_args;

for (auto const& arg : app)
exec_args.push_back(arg.c_str());
for (auto const& arg : app)
exec_args.push_back(arg.c_str());

exec_args.push_back(nullptr);
exec_args.push_back(nullptr);

pid_t pid = fork();

if (pid < 0)
{
BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to fork process"}));
}

if (pid == 0)
{
mir::log_debug("Restoring sigmask");
sigset_t all_signals;
sigfillset(&all_signals);
pthread_sigmask(SIG_UNBLOCK, &all_signals, nullptr);

execvp(exec_args[0], const_cast<char*const*>(exec_args.data()));
execvpe(exec_args[0], const_cast<char*const*>(exec_args.data()), const_cast<char*const*>(exec_env.data()));

mir::log_warning("Failed to execute client (\"%s\") error: %s", exec_args[0], strerror(errno));
exit(EXIT_FAILURE);
Expand Down

0 comments on commit 2ef1ecf

Please sign in to comment.