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 <sys/types.h>
20
21 #include <fruit/fruit.h>
22
23 #include "cvd_server.pb.h"
24
25 #include "common/libs/fs/shared_buf.h"
26 #include "common/libs/fs/shared_fd.h"
27 #include "common/libs/utils/result.h"
28 #include "host/commands/cvd/instance_manager.h"
29 #include "host/commands/cvd/server_command/components.h"
30 #include "host/commands/cvd/types.h"
31
32 namespace cuttlefish {
33 namespace {
34
35 class CvdShutdownHandler : public CvdServerHandler {
36 public:
INJECT(CvdShutdownHandler (CvdServer & server,InstanceManager & instance_manager))37 INJECT(CvdShutdownHandler(CvdServer& server,
38 InstanceManager& instance_manager))
39 : server_(server), instance_manager_(instance_manager) {}
40
CanHandle(const RequestWithStdio & request) const41 Result<bool> CanHandle(const RequestWithStdio& request) const override {
42 return request.Message().contents_case() ==
43 cvd::Request::ContentsCase::kShutdownRequest;
44 }
45
Handle(const RequestWithStdio & request)46 Result<cvd::Response> Handle(const RequestWithStdio& request) override {
47 CF_EXPECT(CanHandle(request));
48 CF_EXPECT(request.Credentials() != std::nullopt);
49 const uid_t uid = request.Credentials()->uid;
50
51 cvd::Response response;
52 response.mutable_shutdown_response();
53
54 if (!request.Extra()) {
55 response.mutable_status()->set_code(cvd::Status::FAILED_PRECONDITION);
56 response.mutable_status()->set_message(
57 "Missing extra SharedFD for shutdown");
58 return response;
59 }
60
61 if (request.Message().shutdown_request().clear()) {
62 *response.mutable_status() =
63 instance_manager_.CvdClear(request.Out(), request.Err());
64 if (response.status().code() != cvd::Status::OK) {
65 return response;
66 }
67 }
68
69 if (instance_manager_.HasInstanceGroups(uid)) {
70 response.mutable_status()->set_code(cvd::Status::FAILED_PRECONDITION);
71 response.mutable_status()->set_message(
72 "Cannot shut down cvd_server while devices are being tracked. "
73 "Try `cvd kill-server`.");
74 return response;
75 }
76
77 // Intentionally leak the write_pipe fd so that it only closes
78 // when this process fully exits.
79 (*request.Extra())->UNMANAGED_Dup();
80
81 WriteAll(request.Out(), "Stopping the cvd_server.\n");
82 server_.Stop();
83
84 response.mutable_status()->set_code(cvd::Status::OK);
85 return response;
86 }
87
Interrupt()88 Result<void> Interrupt() override { return CF_ERR("Can't interrupt"); }
89
90 // For now, shutdown isn't done by cvd shutdown.
CmdList() const91 cvd_common::Args CmdList() const override { return {}; }
92
93 private:
94 CvdServer& server_;
95 InstanceManager& instance_manager_;
96 };
97
98 } // namespace
99
100 fruit::Component<fruit::Required<CvdServer, InstanceManager>>
cvdShutdownComponent()101 cvdShutdownComponent() {
102 return fruit::createComponent()
103 .addMultibinding<CvdServerHandler, CvdShutdownHandler>();
104 }
105
106 } // namespace cuttlefish
107