Serenity Operating System
at master 574 lines 20 kB view raw
1/* 2 * Copyright (c) 2020-2022, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#pragma once 8 9#include "Job.h" 10#include "Parser.h" 11#include <AK/Array.h> 12#include <AK/CircularQueue.h> 13#include <AK/DeprecatedString.h> 14#include <AK/HashMap.h> 15#include <AK/StackInfo.h> 16#include <AK/StringBuilder.h> 17#include <AK/StringView.h> 18#include <AK/Types.h> 19#include <AK/Vector.h> 20#include <LibCore/Notifier.h> 21#include <LibCore/Object.h> 22#include <LibLine/Editor.h> 23#include <LibMain/Main.h> 24#include <termios.h> 25 26#define ENUMERATE_SHELL_BUILTINS() \ 27 __ENUMERATE_SHELL_BUILTIN(alias) \ 28 __ENUMERATE_SHELL_BUILTIN(where) \ 29 __ENUMERATE_SHELL_BUILTIN(cd) \ 30 __ENUMERATE_SHELL_BUILTIN(cdh) \ 31 __ENUMERATE_SHELL_BUILTIN(pwd) \ 32 __ENUMERATE_SHELL_BUILTIN(type) \ 33 __ENUMERATE_SHELL_BUILTIN(exec) \ 34 __ENUMERATE_SHELL_BUILTIN(exit) \ 35 __ENUMERATE_SHELL_BUILTIN(export) \ 36 __ENUMERATE_SHELL_BUILTIN(glob) \ 37 __ENUMERATE_SHELL_BUILTIN(unalias) \ 38 __ENUMERATE_SHELL_BUILTIN(unset) \ 39 __ENUMERATE_SHELL_BUILTIN(history) \ 40 __ENUMERATE_SHELL_BUILTIN(umask) \ 41 __ENUMERATE_SHELL_BUILTIN(not ) \ 42 __ENUMERATE_SHELL_BUILTIN(dirs) \ 43 __ENUMERATE_SHELL_BUILTIN(pushd) \ 44 __ENUMERATE_SHELL_BUILTIN(popd) \ 45 __ENUMERATE_SHELL_BUILTIN(setopt) \ 46 __ENUMERATE_SHELL_BUILTIN(shift) \ 47 __ENUMERATE_SHELL_BUILTIN(source) \ 48 __ENUMERATE_SHELL_BUILTIN(time) \ 49 __ENUMERATE_SHELL_BUILTIN(jobs) \ 50 __ENUMERATE_SHELL_BUILTIN(disown) \ 51 __ENUMERATE_SHELL_BUILTIN(fg) \ 52 __ENUMERATE_SHELL_BUILTIN(bg) \ 53 __ENUMERATE_SHELL_BUILTIN(wait) \ 54 __ENUMERATE_SHELL_BUILTIN(dump) \ 55 __ENUMERATE_SHELL_BUILTIN(kill) \ 56 __ENUMERATE_SHELL_BUILTIN(noop) \ 57 __ENUMERATE_SHELL_BUILTIN(argsparser_parse) 58 59#define ENUMERATE_SHELL_OPTIONS() \ 60 __ENUMERATE_SHELL_OPTION(inline_exec_keep_empty_segments, false, "Keep empty segments in inline execute $(...)") \ 61 __ENUMERATE_SHELL_OPTION(verbose, false, "Announce every command that is about to be executed") \ 62 __ENUMERATE_SHELL_OPTION(invoke_program_for_autocomplete, false, "Attempt to use the program being completed itself for autocompletion via --complete") 63 64#define ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS() \ 65 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(concat_lists) \ 66 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length) \ 67 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length_across) \ 68 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(remove_suffix) \ 69 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(remove_prefix) \ 70 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(regex_replace) \ 71 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(filter_glob) \ 72 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(split) \ 73 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(join) \ 74 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(value_or_default) \ 75 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(assign_default) \ 76 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(error_if_empty) \ 77 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(null_or_alternative) \ 78 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(defined_value_or_default) \ 79 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(assign_defined_default) \ 80 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(error_if_unset) \ 81 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(null_if_unset_or_alternative) \ 82 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(length_of_variable) \ 83 __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(reexpand) 84 85namespace Shell { 86 87class Shell; 88 89class Shell : public Core::Object { 90 C_OBJECT(Shell); 91 92public: 93 constexpr static auto local_init_file_path = "~/.shellrc"; 94 constexpr static auto global_init_file_path = "/etc/shellrc"; 95 96 bool should_format_live() const { return m_should_format_live; } 97 void set_live_formatting(bool value) { m_should_format_live = value; } 98 99 void setup_signals(); 100 101 struct SourcePosition { 102 DeprecatedString source_file; 103 DeprecatedString literal_source_text; 104 Optional<AST::Position> position; 105 }; 106 107 struct RunnablePath { 108 enum class Kind { 109 Builtin, 110 Function, 111 Alias, 112 Executable, 113 }; 114 115 Kind kind; 116 DeprecatedString path; 117 118 bool operator<(RunnablePath const& other) const 119 { 120 return path < other.path; 121 } 122 123 bool operator==(RunnablePath const&) const = default; 124 }; 125 126 struct RunnablePathComparator { 127 int operator()(RunnablePath const& lhs, RunnablePath const& rhs) 128 { 129 if (lhs.path > rhs.path) 130 return 1; 131 132 if (lhs.path < rhs.path) 133 return -1; 134 135 return 0; 136 } 137 138 int operator()(StringView lhs, RunnablePath const& rhs) 139 { 140 if (lhs > rhs.path) 141 return 1; 142 143 if (lhs < rhs.path) 144 return -1; 145 146 return 0; 147 } 148 }; 149 150 int run_command(StringView, Optional<SourcePosition> = {}); 151 Optional<RunnablePath> runnable_path_for(StringView); 152 Optional<DeprecatedString> help_path_for(Vector<RunnablePath> visited, RunnablePath const& runnable_path); 153 ErrorOr<RefPtr<Job>> run_command(const AST::Command&); 154 Vector<NonnullRefPtr<Job>> run_commands(Vector<AST::Command>&); 155 bool run_file(DeprecatedString const&, bool explicitly_invoked = true); 156 ErrorOr<bool> run_builtin(const AST::Command&, Vector<NonnullRefPtr<AST::Rewiring>> const&, int& retval); 157 bool has_builtin(StringView) const; 158 ErrorOr<RefPtr<AST::Node>> run_immediate_function(StringView name, AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&); 159 static bool has_immediate_function(StringView); 160 void block_on_job(RefPtr<Job>); 161 void block_on_pipeline(RefPtr<AST::Pipeline>); 162 DeprecatedString prompt() const; 163 164 static DeprecatedString expand_tilde(StringView expression); 165 static Vector<DeprecatedString> expand_globs(StringView path, StringView base); 166 static Vector<DeprecatedString> expand_globs(Vector<StringView> path_segments, StringView base); 167 ErrorOr<Vector<AST::Command>> expand_aliases(Vector<AST::Command>); 168 DeprecatedString resolve_path(DeprecatedString) const; 169 DeprecatedString resolve_alias(StringView) const; 170 171 static bool has_history_event(StringView); 172 173 ErrorOr<RefPtr<AST::Value const>> get_argument(size_t) const; 174 ErrorOr<RefPtr<AST::Value const>> lookup_local_variable(StringView) const; 175 ErrorOr<DeprecatedString> local_variable_or(StringView, DeprecatedString const&) const; 176 void set_local_variable(DeprecatedString const&, RefPtr<AST::Value>, bool only_in_current_frame = false); 177 void unset_local_variable(StringView, bool only_in_current_frame = false); 178 179 void define_function(DeprecatedString name, Vector<DeprecatedString> argnames, RefPtr<AST::Node> body); 180 bool has_function(StringView); 181 bool invoke_function(const AST::Command&, int& retval); 182 183 DeprecatedString format(StringView, ssize_t& cursor) const; 184 185 RefPtr<Line::Editor> editor() const { return m_editor; } 186 187 struct LocalFrame { 188 LocalFrame(DeprecatedString name, HashMap<DeprecatedString, RefPtr<AST::Value>> variables) 189 : name(move(name)) 190 , local_variables(move(variables)) 191 { 192 } 193 194 DeprecatedString name; 195 HashMap<DeprecatedString, RefPtr<AST::Value>> local_variables; 196 }; 197 198 struct Frame { 199 Frame(Vector<NonnullOwnPtr<LocalFrame>>& frames, LocalFrame const& frame) 200 : frames(frames) 201 , frame(frame) 202 { 203 } 204 ~Frame(); 205 206 void leak_frame() { should_destroy_frame = false; } 207 208 private: 209 Vector<NonnullOwnPtr<LocalFrame>>& frames; 210 LocalFrame const& frame; 211 bool should_destroy_frame { true }; 212 }; 213 214 [[nodiscard]] Frame push_frame(DeprecatedString name); 215 void pop_frame(); 216 217 struct Promise { 218 struct Data { 219 struct Unveil { 220 DeprecatedString path; 221 DeprecatedString access; 222 }; 223 DeprecatedString exec_promises; 224 Vector<Unveil> unveils; 225 } data; 226 227 IntrusiveListNode<Promise> node; 228 using List = IntrusiveList<&Promise::node>; 229 }; 230 231 struct ScopedPromise { 232 ScopedPromise(Promise::List& promises, Promise&& promise) 233 : promises(promises) 234 , promise(move(promise)) 235 { 236 promises.append(this->promise); 237 } 238 239 ~ScopedPromise() 240 { 241 promises.remove(promise); 242 } 243 244 Promise::List& promises; 245 Promise promise; 246 }; 247 [[nodiscard]] ScopedPromise promise(Promise::Data data) 248 { 249 return { m_active_promises, { move(data), {} } }; 250 } 251 252 enum class EscapeMode { 253 Bareword, 254 SingleQuotedString, 255 DoubleQuotedString, 256 }; 257 static DeprecatedString escape_token_for_double_quotes(StringView token); 258 static DeprecatedString escape_token_for_single_quotes(StringView token); 259 static DeprecatedString escape_token(StringView token, EscapeMode = EscapeMode::Bareword); 260 static DeprecatedString escape_token(Utf32View token, EscapeMode = EscapeMode::Bareword); 261 static DeprecatedString unescape_token(StringView token); 262 enum class SpecialCharacterEscapeMode { 263 Untouched, 264 Escaped, 265 QuotedAsEscape, 266 QuotedAsHex, 267 }; 268 static SpecialCharacterEscapeMode special_character_escape_mode(u32 c, EscapeMode); 269 270 static bool is_glob(StringView); 271 static Vector<StringView> split_path(StringView); 272 273 enum class ExecutableOnly { 274 Yes, 275 No 276 }; 277 278 ErrorOr<void> highlight(Line::Editor&) const; 279 Vector<Line::CompletionSuggestion> complete(); 280 Vector<Line::CompletionSuggestion> complete(StringView); 281 Vector<Line::CompletionSuggestion> complete_program_name(StringView, size_t offset, EscapeMode = EscapeMode::Bareword); 282 Vector<Line::CompletionSuggestion> complete_variable(StringView, size_t offset); 283 Vector<Line::CompletionSuggestion> complete_user(StringView, size_t offset); 284 Vector<Line::CompletionSuggestion> complete_immediate_function_name(StringView, size_t offset); 285 286 Vector<Line::CompletionSuggestion> complete_path(StringView base, StringView, size_t offset, ExecutableOnly executable_only, AST::Node const* command_node, AST::Node const*, EscapeMode = EscapeMode::Bareword); 287 Vector<Line::CompletionSuggestion> complete_option(StringView, StringView, size_t offset, AST::Node const* command_node, AST::Node const*); 288 ErrorOr<Vector<Line::CompletionSuggestion>> complete_via_program_itself(size_t offset, AST::Node const* command_node, AST::Node const*, EscapeMode escape_mode, StringView known_program_name); 289 290 void restore_ios(); 291 292 u64 find_last_job_id() const; 293 Job* find_job(u64 id, bool is_pid = false); 294 Job* current_job() const { return m_current_job; } 295 void kill_job(Job const*, int sig); 296 297 DeprecatedString get_history_path(); 298 void print_path(StringView path); 299 void cache_path(); 300 301 bool read_single_line(); 302 303 void notify_child_event(); 304 305 struct termios termios; 306 struct termios default_termios; 307 bool was_interrupted { false }; 308 bool was_resized { false }; 309 310 DeprecatedString cwd; 311 DeprecatedString username; 312 DeprecatedString home; 313 314 constexpr static auto TTYNameSize = 32; 315 constexpr static auto HostNameSize = 64; 316 317 char ttyname[TTYNameSize]; 318 char hostname[HostNameSize]; 319 320 uid_t uid; 321 Optional<int> last_return_code; 322 Vector<DeprecatedString> directory_stack; 323 CircularQueue<DeprecatedString, 8> cd_history; // FIXME: have a configurable cd history length 324 HashMap<u64, NonnullRefPtr<Job>> jobs; 325 Vector<RunnablePath, 256> cached_path; 326 327 DeprecatedString current_script; 328 329 enum ShellEventType { 330 ReadLine, 331 }; 332 333 enum class ShellError { 334 None, 335 InternalControlFlowBreak, 336 InternalControlFlowContinue, 337 InternalControlFlowInterrupted, 338 InternalControlFlowKilled, 339 EvaluatedSyntaxError, 340 NonExhaustiveMatchRules, 341 InvalidGlobError, 342 InvalidSliceContentsError, 343 OpenFailure, 344 OutOfMemory, 345 LaunchError, 346 PipeFailure, 347 WriteFailure, 348 }; 349 350 void raise_error(ShellError kind, DeprecatedString description, Optional<AST::Position> position = {}) 351 { 352 m_error = kind; 353 m_error_description = move(description); 354 if (m_source_position.has_value() && position.has_value()) 355 m_source_position.value().position = position.release_value(); 356 } 357 bool has_error(ShellError err) const { return m_error == err; } 358 bool has_any_error() const { return !has_error(ShellError::None); } 359 DeprecatedString const& error_description() const { return m_error_description; } 360 ShellError take_error() 361 { 362 auto err = m_error; 363 m_error = ShellError::None; 364 m_error_description = {}; 365 return err; 366 } 367 void possibly_print_error() const; 368 static bool is_control_flow(ShellError error) 369 { 370 switch (error) { 371 case ShellError::InternalControlFlowBreak: 372 case ShellError::InternalControlFlowContinue: 373 case ShellError::InternalControlFlowInterrupted: 374 case ShellError::InternalControlFlowKilled: 375 return true; 376 default: 377 return false; 378 } 379 } 380 381#define __ENUMERATE_SHELL_OPTION(name, default_, description) \ 382 bool name { default_ }; 383 384 struct Options { 385 ENUMERATE_SHELL_OPTIONS(); 386 } options; 387 388#undef __ENUMERATE_SHELL_OPTION 389 390private: 391 Shell(Line::Editor&, bool attempt_interactive, bool posix_mode = false); 392 Shell(); 393 virtual ~Shell() override; 394 395 RefPtr<AST::Node> parse(StringView, bool interactive = false, bool as_command = true) const; 396 397 void timer_event(Core::TimerEvent&) override; 398 399 bool is_allowed_to_modify_termios(const AST::Command&) const; 400 401 // FIXME: Port to Core::Property 402 void save_to(JsonObject&); 403 void bring_cursor_to_beginning_of_a_line() const; 404 405 Optional<int> resolve_job_spec(StringView); 406 void add_entry_to_cache(RunnablePath const&); 407 void remove_entry_from_cache(StringView); 408 void stop_all_jobs(); 409 Job* m_current_job { nullptr }; 410 LocalFrame* find_frame_containing_local_variable(StringView name); 411 LocalFrame const* find_frame_containing_local_variable(StringView name) const 412 { 413 return const_cast<Shell*>(this)->find_frame_containing_local_variable(name); 414 } 415 416 void run_tail(RefPtr<Job>); 417 void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code); 418 419 [[noreturn]] void execute_process(Vector<char const*>&& argv); 420 ErrorOr<void> execute_process(Span<StringView> argv); 421 422 virtual void custom_event(Core::CustomEvent&) override; 423 424#define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(name) \ 425 ErrorOr<RefPtr<AST::Node>> immediate_##name(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&); 426 427 ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS(); 428 429#undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION 430 431 ErrorOr<RefPtr<AST::Node>> immediate_length_impl(AST::ImmediateExpression& invoking_node, Vector<NonnullRefPtr<AST::Node>> const&, bool across); 432 433#define __ENUMERATE_SHELL_BUILTIN(builtin) \ 434 ErrorOr<int> builtin_##builtin(Main::Arguments); 435 436 ENUMERATE_SHELL_BUILTINS(); 437 438#undef __ENUMERATE_SHELL_BUILTIN 439 440 static constexpr Array builtin_names = { 441#define __ENUMERATE_SHELL_BUILTIN(builtin) #builtin##sv, 442 443 ENUMERATE_SHELL_BUILTINS() 444 445#undef __ENUMERATE_SHELL_BUILTIN 446 447 ":"sv, // POSIX-y name for "noop". 448 }; 449 450 bool m_should_ignore_jobs_on_next_exit { false }; 451 pid_t m_pid { 0 }; 452 453 struct ShellFunction { 454 DeprecatedString name; 455 Vector<DeprecatedString> arguments; 456 RefPtr<AST::Node> body; 457 }; 458 459 HashMap<DeprecatedString, ShellFunction> m_functions; 460 Vector<NonnullOwnPtr<LocalFrame>> m_local_frames; 461 Promise::List m_active_promises; 462 Vector<NonnullRefPtr<AST::Redirection>> m_global_redirections; 463 464 HashMap<DeprecatedString, DeprecatedString> m_aliases; 465 bool m_is_interactive { true }; 466 bool m_is_subshell { false }; 467 bool m_should_reinstall_signal_handlers { true }; 468 bool m_in_posix_mode { false }; 469 470 ShellError m_error { ShellError::None }; 471 DeprecatedString m_error_description; 472 Optional<SourcePosition> m_source_position; 473 474 bool m_should_format_live { false }; 475 476 RefPtr<Line::Editor> m_editor; 477 478 bool m_default_constructed { false }; 479 480 mutable bool m_last_continuation_state { false }; // false == not needed. 481 482 Optional<size_t> m_history_autosave_time; 483 484 StackInfo m_completion_stack_info; 485}; 486 487[[maybe_unused]] static constexpr bool is_word_character(char c) 488{ 489 return c == '_' || (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || (c <= '9' && c >= '0'); 490} 491 492inline size_t find_offset_into_node(StringView unescaped_text, size_t escaped_offset, Shell::EscapeMode escape_mode) 493{ 494 size_t unescaped_offset = 0; 495 size_t offset = 0; 496 auto do_find_offset = [&](auto& unescaped_text) { 497 for (auto c : unescaped_text) { 498 if (offset == escaped_offset) 499 return unescaped_offset; 500 501 switch (Shell::special_character_escape_mode(c, escape_mode)) { 502 case Shell::SpecialCharacterEscapeMode::Untouched: 503 break; 504 case Shell::SpecialCharacterEscapeMode::Escaped: 505 ++offset; // X -> \X 506 break; 507 case Shell::SpecialCharacterEscapeMode::QuotedAsEscape: 508 switch (escape_mode) { 509 case Shell::EscapeMode::Bareword: 510 offset += 3; // X -> "\Y" 511 break; 512 case Shell::EscapeMode::SingleQuotedString: 513 offset += 5; // X -> '"\Y"' 514 break; 515 case Shell::EscapeMode::DoubleQuotedString: 516 offset += 1; // X -> \Y 517 break; 518 } 519 break; 520 case Shell::SpecialCharacterEscapeMode::QuotedAsHex: 521 switch (escape_mode) { 522 case Shell::EscapeMode::Bareword: 523 offset += 2; // X -> "\..." 524 break; 525 case Shell::EscapeMode::SingleQuotedString: 526 offset += 4; // X -> '"\..."' 527 break; 528 case Shell::EscapeMode::DoubleQuotedString: 529 // X -> \... 530 break; 531 } 532 if (c > NumericLimits<u8>::max()) 533 offset += 8; // X -> "\uhhhhhhhh" 534 else 535 offset += 3; // X -> "\xhh" 536 break; 537 } 538 ++offset; 539 ++unescaped_offset; 540 } 541 return unescaped_offset; 542 }; 543 544 Utf8View view { unescaped_text }; 545 if (view.validate()) 546 return do_find_offset(view); 547 return do_find_offset(unescaped_text); 548} 549 550} 551 552namespace AK { 553 554template<> 555struct Traits<Shell::Shell::RunnablePath> : public GenericTraits<Shell::Shell::RunnablePath> { 556 static constexpr bool is_trivial() { return false; } 557 558 static bool equals(Shell::Shell::RunnablePath const& self, Shell::Shell::RunnablePath const& other) 559 { 560 return self == other; 561 } 562 563 static bool equals(Shell::Shell::RunnablePath const& self, StringView other) 564 { 565 return self.path == other; 566 } 567 568 static bool equals(Shell::Shell::RunnablePath const& self, DeprecatedString const& other) 569 { 570 return self.path == other; 571 } 572}; 573 574}