Compare commits
5 Commits
140910763e
...
bfb45c7d4a
Author | SHA1 | Date | |
---|---|---|---|
bfb45c7d4a | |||
d3fbddb191 | |||
0ab8efd405 | |||
2aefbdc4ee | |||
15dc71e8e1 |
@ -40,6 +40,12 @@ Additionally, the build process needs some extra dependencies to run: `cmake`, `
|
||||
|
||||
If you have no toolchain set up, `run.sh` will build it automatically, which means that you don't necessarily have to run `setup.sh` manually since `run.sh` does it for you.
|
||||
|
||||
## Login UI
|
||||
|
||||
For development convenience, the system automatically starts a GUI session as the default user, without prompting for a password.
|
||||
|
||||
Despite this, Luna does have a login window built-in. If you'd like to try this feature out or start a GUI session as a different user, you'll need to edit [base/etc/init/99-login](base/etc/init/99-login) and change the line that says `Command=/usr/bin/loginui --autologin=selene` to `Command=/usr/bin/loginui`.
|
||||
|
||||
## Prebuilt images
|
||||
|
||||
Prebuilt ISO images for every release version can be found at [pub.cloudapio.eu](https://pub.cloudapio.eu/luna/releases).
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name=login
|
||||
Description=Start a graphical user session.
|
||||
Command=/usr/bin/startui --user=selene
|
||||
Command=/usr/bin/loginui --autologin=selene
|
||||
StandardOutput=/dev/uart0
|
||||
StandardError=/dev/uart0
|
||||
Restart=true
|
||||
|
@ -13,3 +13,5 @@ add_subdirectory(apps)
|
||||
|
||||
luna_service(launch.cpp launch)
|
||||
luna_service(run.cpp run)
|
||||
luna_service(loginui.cpp loginui)
|
||||
target_link_libraries(loginui PRIVATE ui)
|
||||
|
@ -23,7 +23,7 @@ void sigchld_handler(int)
|
||||
wait(nullptr);
|
||||
}
|
||||
|
||||
void sighup_handler(int)
|
||||
void sigquit_handler(int)
|
||||
{
|
||||
// Reload the taskbar by exec-ing the executable, resetting everything.
|
||||
StringView args[] = { "/usr/bin/taskbar" };
|
||||
@ -151,7 +151,7 @@ Result<int> luna_main(int, char**)
|
||||
TRY(app.init("/tmp/wsys.sock"));
|
||||
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler));
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGHUP, sighup_handler));
|
||||
TRY(os::EventLoop::the().register_signal_handler(SIGQUIT, sigquit_handler));
|
||||
|
||||
launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false));
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <errno.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/LocalServer.h>
|
||||
#include <os/Process.h>
|
||||
#include <os/Security.h>
|
||||
@ -66,6 +67,9 @@ Result<int> luna_main(int argc, char** argv)
|
||||
auto server = TRY(os::LocalServer::create(socket_path, false));
|
||||
TRY(server->listen(20));
|
||||
|
||||
// We're ready now.
|
||||
os::IPC::notify_parent();
|
||||
|
||||
Vector<OwnedPtr<os::IPC::ClientConnection>> clients;
|
||||
Vector<struct pollfd> fds;
|
||||
TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 }));
|
||||
|
@ -23,6 +23,8 @@ namespace ui
|
||||
|
||||
Result<void> draw(ui::Canvas& canvas) override;
|
||||
|
||||
void clear();
|
||||
|
||||
StringView data();
|
||||
|
||||
void on_submit(os::Function<StringView>&& action)
|
||||
|
@ -17,6 +17,8 @@ namespace ui
|
||||
{
|
||||
InputField::InputField(SharedPtr<ui::Font> font) : ui::TextInput(), m_font(font)
|
||||
{
|
||||
u8 zero = 0;
|
||||
m_data.append_data(&zero, 1);
|
||||
}
|
||||
|
||||
Result<ui::EventResult> InputField::handle_key_event(const ui::KeyEventRequest& request)
|
||||
@ -35,7 +37,7 @@ namespace ui
|
||||
|
||||
if (request.code == moon::K_RightArrow)
|
||||
{
|
||||
if (m_cursor < m_data.size()) m_cursor++;
|
||||
if (m_cursor < (m_data.size() - 1)) m_cursor++;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
update_cursor();
|
||||
@ -67,8 +69,6 @@ namespace ui
|
||||
|
||||
if (iscntrl(request.letter)) return ui::EventResult::DidNotHandle;
|
||||
|
||||
if (m_cursor == m_data.size()) TRY(m_data.append_data((const u8*)&request.letter, 1));
|
||||
else
|
||||
TRY(insert_character(request.letter));
|
||||
|
||||
m_cursor++;
|
||||
@ -78,6 +78,14 @@ namespace ui
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
void InputField::clear()
|
||||
{
|
||||
m_data.try_resize(0);
|
||||
u8 zero = 0;
|
||||
m_data.append_data(&zero, 1);
|
||||
m_cursor = 0;
|
||||
}
|
||||
|
||||
Result<void> InputField::draw(ui::Canvas& canvas)
|
||||
{
|
||||
int visible_characters = canvas.width / m_font->width();
|
||||
@ -107,6 +115,7 @@ namespace ui
|
||||
|
||||
StringView InputField::data()
|
||||
{
|
||||
if (!m_data.size()) return StringView {};
|
||||
return StringView { (const char*)m_data.data(), m_data.size() };
|
||||
}
|
||||
}
|
||||
|
171
gui/loginui.cpp
Normal file
171
gui/loginui.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* @file loginui.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Graphical login prompt.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <luna/String.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/Process.h>
|
||||
#include <pwd.h>
|
||||
#include <shadow.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ui/App.h>
|
||||
#include <ui/Button.h>
|
||||
#include <ui/InputField.h>
|
||||
#include <ui/Label.h>
|
||||
#include <ui/Layout.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum Stage
|
||||
{
|
||||
UsernameInput,
|
||||
PasswordInput,
|
||||
};
|
||||
|
||||
static constexpr ui::Color BACKGROUND_COLOR = ui::Color::from_rgb(89, 89, 89);
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
StringView username;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("Login prompt for a graphical UI session.");
|
||||
parser.add_system_program_info("loginui"_sv);
|
||||
// FIXME: Make this a config option instead of a switch.
|
||||
// Also, calling "loginui --autologin=user" is functionally identical to calling "startui --user=user", the only
|
||||
// difference is that it makes the init config easier to change (only adding or removing the autologin flag, instead
|
||||
// of changing the program to use)
|
||||
parser.add_value_argument(username, ' ', "autologin", "login as a specific user without prompting");
|
||||
parser.parse(argc, argv);
|
||||
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
os::eprintln("error: %s can only be started as root.", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
setsid();
|
||||
|
||||
bool success = os::IPC::Notifier::run_and_wait(
|
||||
[&] {
|
||||
StringView wind_command[] = { "/usr/bin/wind" };
|
||||
os::Process::spawn(wind_command[0], Slice<StringView>(wind_command, 1));
|
||||
},
|
||||
1000);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
os::eprintln("loginui: failed to start wind, timed out");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!username.is_empty())
|
||||
{
|
||||
auto flag = String::format("--user=%s"_sv, username.chars()).release_value();
|
||||
|
||||
StringView startui_command[] = { "/usr/bin/startui", flag.view() };
|
||||
os::Process::exec(startui_command[0], Slice<StringView>(startui_command, 2));
|
||||
unreachable();
|
||||
}
|
||||
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 300 }));
|
||||
app.set_main_window(window);
|
||||
|
||||
window->set_title("Log in");
|
||||
window->set_background(BACKGROUND_COLOR);
|
||||
|
||||
ui::VerticalLayout main_layout;
|
||||
window->set_main_widget(main_layout);
|
||||
|
||||
ui::Label label("Username:");
|
||||
main_layout.add_widget(label);
|
||||
|
||||
ui::InputField input(ui::Font::default_font());
|
||||
main_layout.add_widget(input);
|
||||
|
||||
ui::Label error("");
|
||||
error.set_font(ui::Font::default_bold_font());
|
||||
error.set_color(ui::RED);
|
||||
main_layout.add_widget(error);
|
||||
|
||||
Stage stage = Stage::UsernameInput;
|
||||
struct passwd* pw;
|
||||
|
||||
input.on_submit([&](StringView data) {
|
||||
error.set_text("");
|
||||
if (stage == Stage::UsernameInput)
|
||||
{
|
||||
struct passwd* entry = getpwnam(data.chars());
|
||||
if (!entry)
|
||||
{
|
||||
error.set_text("User not found.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
pw = entry;
|
||||
stage = Stage::PasswordInput;
|
||||
label.set_text("Password:");
|
||||
|
||||
String title = String::format("Log in: %s"_sv, data.chars()).release_value();
|
||||
window->set_title(title.view());
|
||||
|
||||
input.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* passwd = pw->pw_passwd;
|
||||
|
||||
// If the user's password entry is 'x', read their password from the shadow file instead.
|
||||
if (!strcmp(pw->pw_passwd, "x"))
|
||||
{
|
||||
struct spwd* sp = getspnam(pw->pw_name);
|
||||
|
||||
if (!sp)
|
||||
{
|
||||
error.set_text("User not found in shadow file.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
endspent();
|
||||
|
||||
passwd = sp->sp_pwdp;
|
||||
}
|
||||
|
||||
if (!strcmp(passwd, "!"))
|
||||
{
|
||||
error.set_text("User's password is disabled.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(data.chars(), passwd))
|
||||
{
|
||||
error.set_text("Incorrect password.");
|
||||
input.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
auto flag = String::format("--user=%s"_sv, pw->pw_name).release_value();
|
||||
|
||||
StringView startui_command[] = { "/usr/bin/startui", flag.view() };
|
||||
os::Process::exec(startui_command[0], Slice<StringView>(startui_command, 2));
|
||||
unreachable();
|
||||
}
|
||||
});
|
||||
|
||||
return app.run();
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
#include <moon/Keyboard.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/LocalServer.h>
|
||||
#include <os/Process.h>
|
||||
#include <os/Security.h>
|
||||
@ -115,6 +116,9 @@ Result<int> luna_main(int argc, char** argv)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// We're ready now.
|
||||
os::IPC::notify_parent();
|
||||
|
||||
ui::Color background = ui::Color::from_rgb(0x10, 0x10, 0x10);
|
||||
|
||||
Vector<OwnedPtr<Client>> clients;
|
||||
|
@ -107,7 +107,7 @@ namespace os
|
||||
|
||||
void call()
|
||||
{
|
||||
return m_function();
|
||||
m_function();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -320,5 +320,78 @@ namespace os
|
||||
bool m_ipc_in_progress { false };
|
||||
u8 m_ipc_saved_id { 0 };
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief API used to notify a parent process when a child process finishes initialization. The Notifier struct
|
||||
* is the parent part of the API.
|
||||
*
|
||||
*/
|
||||
struct Notifier
|
||||
{
|
||||
int pfds[2];
|
||||
|
||||
/**
|
||||
* @brief Create a new Notifier.
|
||||
*
|
||||
* This function will create a pipe for the parent and child to communicate.
|
||||
*
|
||||
* @return Notifier The new Notifier object.
|
||||
*/
|
||||
static Notifier create();
|
||||
|
||||
/**
|
||||
* @brief Hook the Notifier into any child process started afterwards.
|
||||
*
|
||||
* This will set an environment variable, which if detected by a child process, will use it to notify the
|
||||
* parent whenever it's ready.
|
||||
*
|
||||
* The recommended order to call this API is:
|
||||
* hook()
|
||||
* fork+exec
|
||||
* unhook()
|
||||
*/
|
||||
void hook();
|
||||
|
||||
/**
|
||||
* @brief Remove the previously created environment variable, so that any future child processes will not
|
||||
* notify this Notifier.
|
||||
*
|
||||
*/
|
||||
void unhook();
|
||||
|
||||
/**
|
||||
* @brief Wait for a child process to be ready. If several child processes are hooked by the hook() method,
|
||||
* this method will only catch the first one that notifies the parent.
|
||||
*
|
||||
* @param timeout If positive, specifies the timeout after which the function fails if no notification is
|
||||
* received.
|
||||
* @return true The child is ready.
|
||||
* @return false The method timed out.
|
||||
*/
|
||||
bool wait(int timeout = -1);
|
||||
|
||||
/**
|
||||
* @brief Combines hook(), unhook() and wait() into one single method. This method takes a function, and
|
||||
* executes it in a "hooked" context, so that any child process started by this function will automatically
|
||||
* detect the parent when it's ready (if it supports the notification API). Then, the function waits for the
|
||||
* child to be ready.
|
||||
*
|
||||
* @param action The function to run.
|
||||
* @param timeout If positive, specifies the timeout after which the function fails if no notification is
|
||||
* received.
|
||||
* @return true The child is ready.
|
||||
* @return false The method timed out.
|
||||
*/
|
||||
static bool run_and_wait(os::Action&& action, int timeout = -1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Use this function to notify a parent process whenever your program finishes initialization.
|
||||
*
|
||||
* If the parent has not used the Notifier API to request a notification from the child process, this function
|
||||
* does nothing.
|
||||
*
|
||||
*/
|
||||
void notify_parent();
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <luna/String.h>
|
||||
#include <os/IPC.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace os::IPC
|
||||
{
|
||||
@ -84,4 +90,60 @@ namespace os::IPC
|
||||
Client::Client(OwnedPtr<LocalClient>&& connection) : m_connection(move(connection))
|
||||
{
|
||||
}
|
||||
|
||||
Notifier Notifier::create()
|
||||
{
|
||||
Notifier result;
|
||||
pipe(result.pfds);
|
||||
fcntl(result.pfds[0], F_SETFD, FD_CLOEXEC);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Notifier::hook()
|
||||
{
|
||||
auto value = String::format("%d"_sv, pfds[1]).release_value();
|
||||
setenv("LUNA_NOTIFY_FD", value.chars(), 1);
|
||||
}
|
||||
|
||||
void Notifier::unhook()
|
||||
{
|
||||
unsetenv("LUNA_NOTIFY_FD");
|
||||
}
|
||||
|
||||
bool Notifier::wait(int timeout)
|
||||
{
|
||||
close(pfds[1]);
|
||||
struct pollfd fds[] = { { .fd = pfds[0], .events = POLLIN, .revents = 0 } };
|
||||
|
||||
poll:
|
||||
int result = poll(fds, 1, timeout);
|
||||
if (result < 0 && errno == EINTR) goto poll;
|
||||
|
||||
close(pfds[0]);
|
||||
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
bool Notifier::run_and_wait(os::Action&& action, int timeout)
|
||||
{
|
||||
auto notifier = create();
|
||||
notifier.hook();
|
||||
action();
|
||||
notifier.unhook();
|
||||
return notifier.wait(timeout);
|
||||
}
|
||||
|
||||
void notify_parent()
|
||||
{
|
||||
char* fd_string = getenv("LUNA_NOTIFY_FD");
|
||||
if (!fd_string) return;
|
||||
|
||||
int fd = atoi(fd_string);
|
||||
|
||||
u8 data = 0;
|
||||
write(fd, &data, 1);
|
||||
|
||||
close(fd);
|
||||
unsetenv("LUNA_NOTIFY_FD");
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/Directory.h>
|
||||
#include <os/File.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/Process.h>
|
||||
#include <os/Security.h>
|
||||
#include <pwd.h>
|
||||
@ -38,6 +39,7 @@ struct Service
|
||||
Option<uid_t> user {};
|
||||
Option<gid_t> group {};
|
||||
bool wait { false };
|
||||
bool wait_notify { false };
|
||||
Option<pid_t> pid {};
|
||||
};
|
||||
|
||||
@ -132,9 +134,13 @@ static Result<void> try_start_service(Service& service)
|
||||
new_stdin->set_close_on_exec();
|
||||
}
|
||||
|
||||
os::IPC::Notifier notifier;
|
||||
if (service.wait_notify) { notifier = os::IPC::Notifier::create(); }
|
||||
|
||||
pid_t pid = TRY(os::Process::fork());
|
||||
if (pid == 0)
|
||||
{
|
||||
if (service.wait_notify) { notifier.hook(); }
|
||||
auto rc = service_child(service, new_stdout, new_stderr, new_stdin);
|
||||
if (rc.has_error())
|
||||
{
|
||||
@ -157,7 +163,17 @@ static Result<void> try_start_service(Service& service)
|
||||
do_log("[init] child process %d exited with code %d\n", pid, WEXITSTATUS(status));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (service.wait_notify)
|
||||
{
|
||||
bool success = notifier.wait(1000);
|
||||
if (!success)
|
||||
{
|
||||
do_log("[init] service %s with pid %d failed to start successfully\n", service.name.chars(), pid);
|
||||
}
|
||||
}
|
||||
service.pid = pid;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -273,6 +289,17 @@ static Result<void> load_service(const os::Path& path)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts[0].view() == "WaitUntilReady")
|
||||
{
|
||||
if (parts[1].view() == "true" || parts[1].view().to_uint().value_or(0) == 1)
|
||||
{
|
||||
service.wait_notify = true;
|
||||
continue;
|
||||
}
|
||||
service.wait_notify = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
do_log("[init] skipping unknown entry name %s\n", parts[0].chars());
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <grp.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/IPC.h>
|
||||
#include <os/Main.h>
|
||||
#include <os/Process.h>
|
||||
#include <pwd.h>
|
||||
@ -18,19 +20,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Timeout should be provided in milliseconds.
|
||||
Result<void> wait_until_file_created(StringView path, int timeout)
|
||||
{
|
||||
struct stat st;
|
||||
while (stat(path.chars(), &st) < 0)
|
||||
{
|
||||
if (timeout <= 0) return err(ETIMEDOUT);
|
||||
usleep(100000); // FIXME: Implement something like inotify or use signals to avoid polling.
|
||||
timeout -= 100;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<Vector<gid_t>> read_supplementary_groups(const char* name)
|
||||
{
|
||||
Vector<gid_t> extra_groups;
|
||||
@ -70,12 +59,6 @@ Result<void> spawn_process_as_user(Slice<StringView> arguments, uid_t user, gid_
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
os::eprintln("error: %s can only be started as root.", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
StringView username;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
@ -84,6 +67,12 @@ Result<int> luna_main(int argc, char** argv)
|
||||
parser.add_value_argument(username, 'u', "user", "the user to start the UI session as");
|
||||
parser.parse(argc, argv);
|
||||
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
os::eprintln("error: %s can only be started as root.", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (username.is_empty())
|
||||
{
|
||||
os::eprintln("error: startui needs a --user parameter to run.");
|
||||
@ -105,9 +94,22 @@ Result<int> luna_main(int argc, char** argv)
|
||||
|
||||
setsid();
|
||||
|
||||
// First of all, start the display server.
|
||||
// First of all, start the display server, in case we haven't been started by loginui.
|
||||
if (!os::FileSystem::exists("/tmp/wind.sock"))
|
||||
{
|
||||
// We need to wait for this one, since its sockets are required later.
|
||||
bool success = os::IPC::Notifier::run_and_wait(
|
||||
[&] {
|
||||
StringView wind_command[] = { "/usr/bin/wind" };
|
||||
TRY(os::Process::spawn(wind_command[0], Slice<StringView>(wind_command, 1)));
|
||||
os::Process::spawn(wind_command[0], Slice<StringView>(wind_command, 1));
|
||||
},
|
||||
1000);
|
||||
if (!success)
|
||||
{
|
||||
os::eprintln("startui: failed to start wind, timed out");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
clearenv();
|
||||
chdir(pw->pw_dir);
|
||||
@ -116,18 +118,22 @@ Result<int> luna_main(int argc, char** argv)
|
||||
setenv("HOME", pw->pw_dir, 1);
|
||||
setenv("SHELL", pw->pw_shell, 1);
|
||||
|
||||
// Next, start the required UI components.
|
||||
// We also need to wait for this one, since taskbar requires launch.sock.
|
||||
bool success = os::IPC::Notifier::run_and_wait(
|
||||
[&] {
|
||||
StringView launch_command[] = { "/usr/bin/launch" };
|
||||
TRY(spawn_process_as_user(Slice<StringView>(launch_command, 1), pw->pw_uid, pw->pw_gid, groups.slice()));
|
||||
|
||||
TRY(wait_until_file_created("/tmp/wsys.sock", 10000));
|
||||
TRY(wait_until_file_created("/tmp/launch.sock", 10000));
|
||||
spawn_process_as_user(Slice<StringView>(launch_command, 1), pw->pw_uid, pw->pw_gid, groups.slice());
|
||||
},
|
||||
1000);
|
||||
if (!success)
|
||||
{
|
||||
os::eprintln("startui: failed to start launch server, timed out");
|
||||
return 1;
|
||||
}
|
||||
|
||||
StringView taskbar_command[] = { "/usr/bin/taskbar" };
|
||||
TRY(spawn_process_as_user(Slice<StringView>(taskbar_command, 1), pw->pw_uid, pw->pw_gid, system_groups.slice()));
|
||||
|
||||
TRY(wait_until_file_created("/tmp/wind.sock", 10000));
|
||||
|
||||
// Finally, start init in user mode to manage all configured optional services.
|
||||
StringView init_command[] = { "/usr/bin/init", "--user" };
|
||||
TRY(spawn_process_as_user(Slice<StringView>(init_command, 2), pw->pw_uid, pw->pw_gid, groups.slice()));
|
||||
|
Loading…
Reference in New Issue
Block a user