|
@@ -6,9 +6,11 @@
|
|
|
|
|
|
#include <AK/FixedArray.h>
|
|
|
#include <AK/QuickSort.h>
|
|
|
+#include <AK/TypedTransfer.h>
|
|
|
#include <AK/URL.h>
|
|
|
#include <LibConfig/Client.h>
|
|
|
#include <LibConfig/Listener.h>
|
|
|
+#include <LibCore/Account.h>
|
|
|
#include <LibCore/ArgsParser.h>
|
|
|
#include <LibCore/Directory.h>
|
|
|
#include <LibCore/System.h>
|
|
@@ -35,17 +37,7 @@
|
|
|
#include <LibGfx/Palette.h>
|
|
|
#include <LibMain/Main.h>
|
|
|
#include <LibVT/TerminalWidget.h>
|
|
|
-#include <assert.h>
|
|
|
-#include <errno.h>
|
|
|
#include <pty.h>
|
|
|
-#include <pwd.h>
|
|
|
-#include <signal.h>
|
|
|
-#include <stdio.h>
|
|
|
-#include <stdlib.h>
|
|
|
-#include <string.h>
|
|
|
-#include <sys/ioctl.h>
|
|
|
-#include <sys/wait.h>
|
|
|
-#include <unistd.h>
|
|
|
|
|
|
class TerminalChangeListener : public Config::Listener {
|
|
|
public:
|
|
@@ -111,45 +103,45 @@ private:
|
|
|
VT::TerminalWidget& m_parent_terminal;
|
|
|
};
|
|
|
|
|
|
-static void utmp_update(DeprecatedString const& tty, pid_t pid, bool create)
|
|
|
+static ErrorOr<void> utmp_update(StringView tty, pid_t pid, bool create)
|
|
|
{
|
|
|
- int utmpupdate_pid = fork();
|
|
|
- if (utmpupdate_pid < 0) {
|
|
|
- perror("fork");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (utmpupdate_pid == 0) {
|
|
|
- // Be careful here! Because fork() only clones one thread it's
|
|
|
- // possible that we deadlock on anything involving a mutex,
|
|
|
- // including the heap! So resort to low-level APIs
|
|
|
- char pid_str[32];
|
|
|
- snprintf(pid_str, sizeof(pid_str), "%d", pid);
|
|
|
- execl("/bin/utmpupdate", "/bin/utmpupdate", "-f", "Terminal", "-p", pid_str, (create ? "-c" : "-d"), tty.characters(), nullptr);
|
|
|
- } else {
|
|
|
- wait_again:
|
|
|
- int status = 0;
|
|
|
- if (waitpid(utmpupdate_pid, &status, 0) < 0) {
|
|
|
- int err = errno;
|
|
|
- if (err == EINTR)
|
|
|
- goto wait_again;
|
|
|
- perror("waitpid");
|
|
|
- return;
|
|
|
+ auto pid_string = TRY(String::number(pid));
|
|
|
+ Array utmp_update_command {
|
|
|
+ "-f"sv,
|
|
|
+ "Terminal"sv,
|
|
|
+ "-p"sv,
|
|
|
+ pid_string.bytes_as_string_view(),
|
|
|
+ (create ? "-c"sv : "-d"sv),
|
|
|
+ tty,
|
|
|
+ };
|
|
|
+
|
|
|
+ auto utmpupdate_pid = TRY(Core::Process::spawn("/bin/utmpupdate"sv, utmp_update_command, {}, Core::Process::KeepAsChild::Yes));
|
|
|
+
|
|
|
+ Core::System::WaitPidResult status;
|
|
|
+ auto wait_successful = false;
|
|
|
+ while (!wait_successful) {
|
|
|
+ auto result = Core::System::waitpid(utmpupdate_pid, 0);
|
|
|
+ if (result.is_error() && result.error().code() != EINTR) {
|
|
|
+ return result.release_error();
|
|
|
+ } else if (!result.is_error()) {
|
|
|
+ status = result.release_value();
|
|
|
+ wait_successful = true;
|
|
|
}
|
|
|
- if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
|
|
|
- dbgln("Terminal: utmpupdate exited with status {}", WEXITSTATUS(status));
|
|
|
- else if (WIFSIGNALED(status))
|
|
|
- dbgln("Terminal: utmpupdate exited due to unhandled signal {}", WTERMSIG(status));
|
|
|
}
|
|
|
+
|
|
|
+ if (WIFEXITED(status.status) && WEXITSTATUS(status.status) != 0)
|
|
|
+ dbgln("Terminal: utmpupdate exited with status {}", WEXITSTATUS(status.status));
|
|
|
+ else if (WIFSIGNALED(status.status))
|
|
|
+ dbgln("Terminal: utmpupdate exited due to unhandled signal {}", WTERMSIG(status.status));
|
|
|
+
|
|
|
+ return {};
|
|
|
}
|
|
|
|
|
|
-static ErrorOr<void> run_command(DeprecatedString command, bool keep_open)
|
|
|
+static ErrorOr<void> run_command(StringView command, bool keep_open)
|
|
|
{
|
|
|
- DeprecatedString shell = "/bin/Shell";
|
|
|
- auto* pw = getpwuid(getuid());
|
|
|
- if (pw && pw->pw_shell) {
|
|
|
- shell = pw->pw_shell;
|
|
|
- }
|
|
|
- endpwent();
|
|
|
+ auto shell = TRY(String::from_deprecated_string(TRY(Core::Account::self(Core::Account::Read::PasswdOnly)).shell()));
|
|
|
+ if (shell.is_empty())
|
|
|
+ shell = TRY("/bin/Shell"_string);
|
|
|
|
|
|
Vector<StringView> arguments;
|
|
|
arguments.append(shell);
|
|
@@ -239,7 +231,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
|
TRY(Core::System::pledge("stdio tty rpath cpath wpath recvfd sendfd proc exec unix sigaction"));
|
|
|
|
|
|
struct sigaction act;
|
|
|
- memset(&act, 0, sizeof(act));
|
|
|
+ act.sa_mask = 0;
|
|
|
+ // Do not trust that both function pointers overlap.
|
|
|
+ act.sa_sigaction = nullptr;
|
|
|
+
|
|
|
act.sa_flags = SA_NOCLDWAIT;
|
|
|
act.sa_handler = SIG_IGN;
|
|
|
|
|
@@ -267,12 +262,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
|
|
|
|
int ptm_fd;
|
|
|
pid_t shell_pid = forkpty(&ptm_fd, nullptr, nullptr, nullptr);
|
|
|
- if (shell_pid < 0) {
|
|
|
- perror("forkpty");
|
|
|
- return 1;
|
|
|
- }
|
|
|
+ if (shell_pid < 0)
|
|
|
+ return Error::from_errno(errno);
|
|
|
+
|
|
|
+ // We're the child process; run the startup command.
|
|
|
if (shell_pid == 0) {
|
|
|
- close(ptm_fd);
|
|
|
if (!command_to_execute.is_empty())
|
|
|
TRY(run_command(command_to_execute, keep_open));
|
|
|
else
|
|
@@ -281,7 +275,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
|
}
|
|
|
|
|
|
auto ptsname = TRY(Core::System::ptsname(ptm_fd));
|
|
|
- utmp_update(ptsname, shell_pid, true);
|
|
|
+ TRY(utmp_update(ptsname, shell_pid, true));
|
|
|
|
|
|
auto app_icon = GUI::Icon::default_icon("app-terminal"sv);
|
|
|
|
|
@@ -356,7 +350,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
|
|
|
|
auto shell_child_process_count = [&] {
|
|
|
int background_process_count = 0;
|
|
|
- Core::Directory::for_each_entry(DeprecatedString::formatted("/proc/{}/children", shell_pid), Core::DirIterator::Flags::SkipParentAndBaseDir, [&](auto&, auto&) {
|
|
|
+ Core::Directory::for_each_entry(String::formatted("/proc/{}/children", shell_pid).release_value_but_fixme_should_propagate_errors(), Core::DirIterator::Flags::SkipParentAndBaseDir, [&](auto&, auto&) {
|
|
|
++background_process_count;
|
|
|
return IterationDecision::Continue;
|
|
|
}).release_value_but_fixme_should_propagate_errors();
|
|
@@ -366,17 +360,17 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
|
auto check_terminal_quit = [&]() -> GUI::Dialog::ExecResult {
|
|
|
if (!should_confirm_close)
|
|
|
return GUI::MessageBox::ExecResult::OK;
|
|
|
- Optional<DeprecatedString> close_message;
|
|
|
+ Optional<String> close_message;
|
|
|
auto title = "Running Process"sv;
|
|
|
if (tty_has_foreground_process()) {
|
|
|
- close_message = "Close Terminal and kill its foreground process?";
|
|
|
+ close_message = "Close Terminal and kill its foreground process?"_string.release_value_but_fixme_should_propagate_errors();
|
|
|
} else {
|
|
|
auto child_process_count = shell_child_process_count();
|
|
|
if (child_process_count > 1) {
|
|
|
title = "Running Processes"sv;
|
|
|
- close_message = DeprecatedString::formatted("Close Terminal and kill its {} background processes?", child_process_count);
|
|
|
+ close_message = String::formatted("Close Terminal and kill its {} background processes?", child_process_count).release_value_but_fixme_should_propagate_errors();
|
|
|
} else if (child_process_count == 1) {
|
|
|
- close_message = "Close Terminal and kill its background process?";
|
|
|
+ close_message = "Close Terminal and kill its background process?"_string.release_value_but_fixme_should_propagate_errors();
|
|
|
}
|
|
|
}
|
|
|
if (close_message.has_value())
|
|
@@ -471,6 +465,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
|
modified_state_check_timer->start();
|
|
|
int result = app->exec();
|
|
|
dbgln("Exiting terminal, updating utmp");
|
|
|
- utmp_update(ptsname, 0, false);
|
|
|
+ TRY(utmp_update(ptsname, 0, false));
|
|
|
return result;
|
|
|
}
|