Serenity Operating System
at master 200 lines 6.5 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/QuickSort.h> 8#include <LibCore/ArgsParser.h> 9#include <LibCore/ProcessStatisticsReader.h> 10#include <LibCore/System.h> 11#include <LibMain/Main.h> 12#include <sys/sysmacros.h> 13#include <unistd.h> 14 15static ErrorOr<DeprecatedString> determine_tty_pseudo_name() 16{ 17 struct stat tty_stat; 18 if (fstat(STDIN_FILENO, &tty_stat) < 0) { 19 int saved_errno = errno; 20 perror("fstat"); 21 return Error::from_errno(saved_errno); 22 } 23 24 int tty_device_major = major(tty_stat.st_rdev); 25 int tty_device_minor = minor(tty_stat.st_rdev); 26 27 if (tty_device_major == 201) { 28 return DeprecatedString::formatted("pts:{}", tty_device_minor); 29 } 30 31 if (tty_device_major == 4) { 32 return DeprecatedString::formatted("tty:{}", tty_device_minor); 33 } 34 return "n/a"; 35} 36 37ErrorOr<int> serenity_main(Main::Arguments arguments) 38{ 39 TRY(Core::System::pledge("stdio rpath tty")); 40 41 auto this_pseudo_tty_name = TRY(determine_tty_pseudo_name()); 42 43 TRY(Core::System::pledge("stdio rpath")); 44 TRY(Core::System::unveil("/sys/kernel/processes", "r")); 45 TRY(Core::System::unveil("/etc/passwd", "r")); 46 TRY(Core::System::unveil(nullptr, nullptr)); 47 48 enum class Alignment { 49 Left, 50 Right, 51 }; 52 53 struct Column { 54 DeprecatedString title; 55 Alignment alignment { Alignment::Left }; 56 int width { 0 }; 57 DeprecatedString buffer; 58 }; 59 60 bool every_process_flag = false; 61 bool full_format_flag = false; 62 DeprecatedString pid_list; 63 64 Core::ArgsParser args_parser; 65 args_parser.add_option(every_process_flag, "Show every process", nullptr, 'e'); 66 args_parser.add_option(full_format_flag, "Full format", nullptr, 'f'); 67 args_parser.add_option(pid_list, "A comma-separated list of PIDs. Only processes matching those PIDs will be selected", nullptr, 'q', "pid-list"); 68 args_parser.parse(arguments); 69 70 Vector<Column> columns; 71 72 int uid_column = -1; 73 int pid_column = -1; 74 int ppid_column = -1; 75 int pgid_column = -1; 76 int sid_column = -1; 77 int state_column = -1; 78 int tty_column = -1; 79 int cmd_column = -1; 80 81 auto add_column = [&](auto title, auto alignment) { 82 columns.append({ title, alignment, 0, {} }); 83 return columns.size() - 1; 84 }; 85 86 if (full_format_flag) { 87 uid_column = add_column("UID", Alignment::Left); 88 pid_column = add_column("PID", Alignment::Right); 89 ppid_column = add_column("PPID", Alignment::Right); 90 pgid_column = add_column("PGID", Alignment::Right); 91 sid_column = add_column("SID", Alignment::Right); 92 state_column = add_column("STATE", Alignment::Left); 93 tty_column = add_column("TTY", Alignment::Left); 94 cmd_column = add_column("CMD", Alignment::Left); 95 } else { 96 pid_column = add_column("PID", Alignment::Right); 97 tty_column = add_column("TTY", Alignment::Left); 98 cmd_column = add_column("CMD", Alignment::Left); 99 } 100 101 auto all_processes = TRY(Core::ProcessStatisticsReader::get_all()); 102 103 auto& processes = all_processes.processes; 104 105 if (!pid_list.is_empty()) { 106 every_process_flag = true; 107 auto string_parts = pid_list.split_view(','); 108 Vector<pid_t> selected_pids; 109 selected_pids.ensure_capacity(string_parts.size()); 110 111 for (size_t i = 0; i < string_parts.size(); i++) { 112 auto pid = string_parts[i].to_int(); 113 114 if (!pid.has_value()) { 115 warnln("Invalid value for -q: {}", pid_list); 116 warnln("Could not parse '{}' as a PID.", string_parts[i]); 117 return 1; 118 } 119 120 selected_pids.append(pid.value()); 121 } 122 123 processes.remove_all_matching([&](auto& a) { return selected_pids.find(a.pid) == selected_pids.end(); }); 124 125 auto processes_sort_predicate = [&selected_pids](auto& a, auto& b) { 126 return selected_pids.find_first_index(a.pid).value() < selected_pids.find_first_index(b.pid).value(); 127 }; 128 quick_sort(processes, processes_sort_predicate); 129 } else { 130 quick_sort(processes, [](auto& a, auto& b) { return a.pid < b.pid; }); 131 } 132 133 Vector<Vector<DeprecatedString>> rows; 134 TRY(rows.try_ensure_capacity(1 + processes.size())); 135 136 Vector<DeprecatedString> header; 137 TRY(header.try_ensure_capacity(columns.size())); 138 for (auto& column : columns) 139 header.unchecked_append(column.title); 140 rows.append(move(header)); 141 142 for (auto const& process : processes) { 143 auto tty = process.tty; 144 if (!every_process_flag && tty != this_pseudo_tty_name) 145 continue; 146 147 auto* state = process.threads.is_empty() ? "Zombie" : process.threads.first().state.characters(); 148 149 Vector<DeprecatedString> row; 150 TRY(row.try_resize(columns.size())); 151 152 if (tty == "") 153 tty = "n/a"; 154 155 if (uid_column != -1) 156 row[uid_column] = process.username; 157 if (pid_column != -1) 158 row[pid_column] = DeprecatedString::number(process.pid); 159 if (ppid_column != -1) 160 row[ppid_column] = DeprecatedString::number(process.ppid); 161 if (pgid_column != -1) 162 row[pgid_column] = DeprecatedString::number(process.pgid); 163 if (sid_column != -1) 164 row[sid_column] = DeprecatedString::number(process.sid); 165 if (tty_column != -1) 166 row[tty_column] = tty; 167 if (state_column != -1) 168 row[state_column] = state; 169 if (cmd_column != -1) 170 row[cmd_column] = process.name; 171 172 TRY(rows.try_append(move(row))); 173 } 174 175 for (size_t i = 0; i < columns.size(); i++) { 176 auto& column = columns[i]; 177 for (auto& row : rows) 178 column.width = max(column.width, static_cast<int>(row[i].length())); 179 } 180 181 for (auto& row : rows) { 182 for (size_t i = 0; i < columns.size(); i++) { 183 auto& column = columns[i]; 184 auto& cell_text = row[i]; 185 if (!column.width) { 186 out("{}", cell_text); 187 continue; 188 } 189 if (column.alignment == Alignment::Right) 190 out("{1:>{0}} ", column.width, cell_text); 191 else 192 out("{1:{0}} ", column.width, cell_text); 193 if (i != columns.size() - 1) 194 out(" "); 195 } 196 outln(); 197 } 198 199 return 0; 200}