1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/commands/cvd/server.h"
18
19 #include <mutex>
20
21 #include "common/libs/fs/shared_buf.h"
22 #include "host/commands/cvd/command_sequence.h"
23 #include "host/commands/cvd/server_command/utils.h"
24 #include "host/commands/cvd/types.h"
25 #include "host/libs/config/inject.h"
26
27 namespace cuttlefish {
28
29 static constexpr char kHelpMessage[] = R"(Cuttlefish Virtual Device (CVD) CLI.
30
31 usage: cvd <selector/driver options> <command> <args>
32
33 Selector Options:
34 -group_name <name> Specify the name of the instance group created
35 or selected.
36 -instance_name <name> Selects the device of the given name to perform the
37 commands for.
38 -instance_name <names> Takes the names of the devices to create within an
39 instance group. The 'names' is comma-separated.
40
41 Driver Options:
42 --help Print this message
43 -disable_default_group If the flag is true, the group's runtime files are
44 not populated under the user's HOME. Instead the
45 files are created under an automatically-generated
46 directory. (default: false)
47 -acquire_file_lock If the flag is given, the cvd server attempts to
48 acquire the instance lock file lock. (default: true)
49
50 Commands:
51 help Print this message.
52 help <command> Print help for a command.
53 start Start a device.
54 stop Stop a running device.
55 clear Stop all running devices and delete all instance and
56 assembly directories.
57 fleet View the current fleet status.
58 kill-server Kill the cvd_server background process.
59 server-kill Same as kill-server
60 powerwash Delivers powerwash command to the selected device
61 restart Restart the device without reinitializing the disks
62 restart-server Restart the cvd_server background process.
63 status Check and print the state of a running instance.
64 host_bugreport Capture a host bugreport, including configs, logs, and
65 tombstones.
66
67 Args:
68 <command args> Each command has its own set of args.
69 See cvd help <command>.
70
71 Experimental:
72 reset See cvd reset --help. Requires cvd >= v1.2
73 )";
74
75 class CvdHelpHandler : public CvdServerHandler {
76 public:
INJECT(CvdHelpHandler (CommandSequenceExecutor & executor))77 INJECT(CvdHelpHandler(CommandSequenceExecutor& executor))
78 : executor_(executor) {}
79
CanHandle(const RequestWithStdio & request) const80 Result<bool> CanHandle(const RequestWithStdio& request) const override {
81 auto invocation = ParseInvocation(request.Message());
82 return (invocation.command == "help");
83 }
84
Handle(const RequestWithStdio & request)85 Result<cvd::Response> Handle(const RequestWithStdio& request) override {
86 std::unique_lock interrupt_lock(interruptible_);
87 if (interrupted_) {
88 return CF_ERR("Interrupted");
89 }
90
91 cvd::Response response;
92 response.mutable_command_response(); // Sets oneof member
93 response.mutable_status()->set_code(cvd::Status::OK);
94
95 CF_EXPECT(CanHandle(request));
96
97 auto [subcmd, subcmd_args] = ParseInvocation(request.Message());
98 const auto supported_subcmd_list = executor_.CmdList();
99
100 /*
101 * cvd help, cvd help invalid_token, cvd help help
102 */
103 if (subcmd_args.empty() ||
104 !Contains(supported_subcmd_list, subcmd_args.front()) ||
105 subcmd_args.front() == "help") {
106 WriteAll(request.Out(), kHelpMessage);
107 return response;
108 }
109
110 cvd::Request modified_proto = HelpSubcommandToFlag(request);
111
112 RequestWithStdio inner_cmd(request.Client(), modified_proto,
113 request.FileDescriptors(),
114 request.Credentials());
115
116 interrupt_lock.unlock();
117 executor_.Execute({inner_cmd}, SharedFD::Open("/dev/null", O_RDWR));
118
119 return response;
120 }
121
Interrupt()122 Result<void> Interrupt() override {
123 std::scoped_lock interrupt_lock(interruptible_);
124 interrupted_ = true;
125 CF_EXPECT(executor_.Interrupt());
126 return {};
127 }
128
CmdList() const129 cvd_common::Args CmdList() const override { return {"help"}; }
130
131 private:
132 cvd::Request HelpSubcommandToFlag(const RequestWithStdio& request);
133
134 std::mutex interruptible_;
135 bool interrupted_ = false;
136 CommandSequenceExecutor& executor_;
137 };
138
HelpSubcommandToFlag(const RequestWithStdio & request)139 cvd::Request CvdHelpHandler::HelpSubcommandToFlag(
140 const RequestWithStdio& request) {
141 cvd::Request modified_proto = request.Message();
142 auto all_args =
143 cvd_common::ConvertToArgs(modified_proto.command_request().args());
144 auto& args = *modified_proto.mutable_command_request()->mutable_args();
145 args.Clear();
146 // there must be one or more "help" in all_args
147 // delete the first "help"
148 bool found_help = false;
149 for (const auto& cmd_arg : all_args) {
150 if (cmd_arg != "help" || found_help) {
151 args.Add(cmd_arg.c_str());
152 continue;
153 }
154 // skip first help
155 found_help = true;
156 }
157 args.Add("--help");
158 return modified_proto;
159 }
160
CvdHelpComponent()161 fruit::Component<fruit::Required<CommandSequenceExecutor>> CvdHelpComponent() {
162 return fruit::createComponent()
163 .addMultibinding<CvdServerHandler, CvdHelpHandler>();
164 }
165
166 } // namespace cuttlefish
167