Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, Alex Major
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <LibCore/ArgsParser.h>
9#include <LibCore/DirIterator.h>
10#include <LibCore/File.h>
11#include <LibMain/Main.h>
12
13static bool s_set_variable = false;
14
15static DeprecatedString get_variable(StringView name)
16{
17 auto path = DeprecatedString::formatted("/sys/kernel/variables/{}", name);
18 auto file = Core::File::open(path, Core::File::OpenMode::Read);
19 if (file.is_error()) {
20 warnln("Failed to open {}: {}", path, file.error());
21 return {};
22 }
23 auto buffer = file.value()->read_until_eof();
24 if (buffer.is_error()) {
25 warnln("Failed to read {}: {}", path, buffer.error());
26 return {};
27 }
28 return { (char const*)buffer.value().data(), buffer.value().size(), Chomp };
29}
30
31static bool read_variable(StringView name)
32{
33 auto value = get_variable(name);
34 if (value.is_null())
35 return false;
36 outln("{} = {}", name, value);
37 return true;
38}
39
40static bool write_variable(StringView name, StringView value)
41{
42 auto old_value = get_variable(name);
43 if (old_value.is_null())
44 return false;
45 auto path = DeprecatedString::formatted("/sys/kernel/variables/{}", name);
46 auto file = Core::File::open(path, Core::File::OpenMode::Write);
47 if (file.is_error()) {
48 warnln("Failed to open {}: {}", path, file.error());
49 return false;
50 }
51 if (auto result = file.value()->write_until_depleted(value.bytes()); result.is_error()) {
52 warnln("Failed to write {}: {}", path, result.error());
53 return false;
54 }
55 outln("{}: {} -> {}", name, old_value, value);
56 return true;
57}
58
59static int handle_variables(Vector<StringView> const& variables)
60{
61 bool success = false;
62 for (auto const& variable : variables) {
63 auto maybe_index = variable.find('=');
64 if (!maybe_index.has_value()) {
65 success = read_variable(variable);
66 continue;
67 }
68 auto equal_index = maybe_index.release_value();
69 auto name = variable.substring_view(0, equal_index);
70 auto value = variable.substring_view(equal_index + 1, variable.length() - equal_index - 1);
71 if (name.is_empty())
72 warnln("Malformed setting '{}'", variable);
73 else if (!s_set_variable)
74 warnln("Must specify '-w' to set variables");
75 else
76 success = write_variable(name, value);
77 }
78 return success ? 0 : 1;
79}
80
81static int handle_show_all()
82{
83 Core::DirIterator di("/sys/kernel/variables", Core::DirIterator::SkipDots);
84 if (di.has_error()) {
85 outln("DirIterator: {}", di.error());
86 return 1;
87 }
88
89 bool success = false;
90 while (di.has_next()) {
91 auto name = di.next_path();
92 success = read_variable(name);
93 }
94 return success ? 0 : 1;
95}
96
97ErrorOr<int> serenity_main(Main::Arguments arguments)
98{
99 bool show_all = false;
100 Vector<StringView> variables;
101
102 Core::ArgsParser args_parser;
103 args_parser.set_general_help("Show or modify system-internal values. This requires root, and can crash your system.");
104 args_parser.add_option(show_all, "Show all variables", "all", 'a');
105 args_parser.add_option(s_set_variable, "Set variables", "write", 'w');
106 args_parser.add_positional_argument(variables, "variable[=value]", "variables", Core::ArgsParser::Required::No);
107 args_parser.parse(arguments);
108
109 if (!show_all && variables.is_empty()) {
110 args_parser.print_usage(stdout, arguments.strings[0]);
111 return 1;
112 }
113
114 if (show_all) {
115 // Ignore `variables`, even if they are supplied. Just like the real procps does.
116 return handle_show_all();
117 }
118
119 return handle_variables(variables);
120}