mirror of https://github.com/grpc/grpc.git
Add support for systemd socket activation (#31667)
* Revert "Revert "Add support for systemd socket activation (#30485)" (#31617)"
This reverts commit 867dc6cae2
.
* Add checks to unix tests
* Ran generate_projects.sh and fixed styling in test
* Fix variable in unit test
* Use reinterpret_cast in test
* Rebase and fix sanity failures
pull/32037/head
parent
c952d37814
commit
2904ee8fd3
35 changed files with 909 additions and 21 deletions
@ -0,0 +1,34 @@ |
|||||||
|
# Copyright 2022 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
|
||||||
|
find_package(systemd QUIET CONFIG) |
||||||
|
if(systemd_FOUND) |
||||||
|
message(STATUS "Found systemd via CMake.") |
||||||
|
return() |
||||||
|
endif() |
||||||
|
|
||||||
|
if(TARGET systemd) |
||||||
|
message(STATUS "Found systemd via pkg-config already?") |
||||||
|
return() |
||||||
|
endif() |
||||||
|
|
||||||
|
find_package(PkgConfig) |
||||||
|
pkg_check_modules(SYSTEMD libsystemd) |
||||||
|
|
||||||
|
if(SYSTEMD_FOUND) |
||||||
|
set(systemd_FOUND "${SYSTEMD_FOUND}") |
||||||
|
add_library(systemd INTERFACE IMPORTED) |
||||||
|
message(STATUS "Found systemd via pkg-config.") |
||||||
|
endif() |
@ -0,0 +1,20 @@ |
|||||||
|
# Copyright 2022 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
find_package(systemd) |
||||||
|
if(TARGET systemd) |
||||||
|
set(_gRPC_SYSTEMD_LIBRARIES systemd ${SYSTEMD_LINK_LIBRARIES}) |
||||||
|
add_definitions(-DHAVE_LIBSYSTEMD) |
||||||
|
endif() |
||||||
|
set(_gRPC_FIND_SYSTEMD "if(NOT systemd_FOUND)\n find_package(systemd)\nendif()") |
@ -0,0 +1,34 @@ |
|||||||
|
# Copyright 2022 the gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
licenses(["notice"]) |
||||||
|
|
||||||
|
cc_binary( |
||||||
|
name = "client", |
||||||
|
srcs = ["client.cc"], |
||||||
|
deps = [ |
||||||
|
"//:grpc++", |
||||||
|
"//examples/protos:helloworld_cc_grpc", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
cc_binary( |
||||||
|
name = "server", |
||||||
|
srcs = ["server.cc"], |
||||||
|
deps = [ |
||||||
|
"//:grpc++", |
||||||
|
"//:grpc++_reflection", |
||||||
|
"//examples/protos:helloworld_cc_grpc", |
||||||
|
], |
||||||
|
) |
@ -0,0 +1,100 @@ |
|||||||
|
// Copyright 2022 the gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "examples/protos/helloworld.grpc.pb.h" |
||||||
|
|
||||||
|
#include <grpcpp/grpcpp.h> |
||||||
|
|
||||||
|
using grpc::Channel; |
||||||
|
using grpc::ClientContext; |
||||||
|
using grpc::Status; |
||||||
|
using helloworld::Greeter; |
||||||
|
using helloworld::HelloReply; |
||||||
|
using helloworld::HelloRequest; |
||||||
|
|
||||||
|
class GreeterClient { |
||||||
|
public: |
||||||
|
GreeterClient(std::shared_ptr<Channel> channel) |
||||||
|
: stub_(Greeter::NewStub(channel)) {} |
||||||
|
|
||||||
|
// Assembles the client's payload, sends it and presents the response back
|
||||||
|
// from the server.
|
||||||
|
std::string SayHello(const std::string& user) { |
||||||
|
// Data we are sending to the server.
|
||||||
|
HelloRequest request; |
||||||
|
request.set_name(user); |
||||||
|
|
||||||
|
// Container for the data we expect from the server.
|
||||||
|
HelloReply reply; |
||||||
|
|
||||||
|
// Context for the client. It could be used to convey extra information to
|
||||||
|
// the server and/or tweak certain RPC behaviors.
|
||||||
|
ClientContext context; |
||||||
|
|
||||||
|
// The actual RPC.
|
||||||
|
Status status = stub_->SayHello(&context, request, &reply); |
||||||
|
|
||||||
|
// Act upon its status.
|
||||||
|
if (status.ok()) { |
||||||
|
return reply.message(); |
||||||
|
} else { |
||||||
|
std::cout << status.error_code() << ": " << status.error_message() |
||||||
|
<< std::endl; |
||||||
|
return "RPC failed"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
std::unique_ptr<Greeter::Stub> stub_; |
||||||
|
}; |
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
// Instantiate the client. It requires a channel, out of which the actual RPCs
|
||||||
|
// are created. This channel models a connection to an endpoint specified by
|
||||||
|
// the argument "--target=" which is the only expected argument.
|
||||||
|
// We indicate that the channel isn't authenticated (use of
|
||||||
|
// InsecureChannelCredentials()).
|
||||||
|
std::string target_str; |
||||||
|
std::string arg_str("--target"); |
||||||
|
if (argc > 1) { |
||||||
|
std::string arg_val = argv[1]; |
||||||
|
size_t start_pos = arg_val.find(arg_str); |
||||||
|
if (start_pos != std::string::npos) { |
||||||
|
start_pos += arg_str.size(); |
||||||
|
if (arg_val[start_pos] == '=') { |
||||||
|
target_str = arg_val.substr(start_pos + 1); |
||||||
|
} else { |
||||||
|
std::cout << "The only correct argument syntax is --target=" |
||||||
|
<< std::endl; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} else { |
||||||
|
std::cout << "The only acceptable argument is --target=" << std::endl; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} else { |
||||||
|
target_str = "unix:/tmp/server"; |
||||||
|
} |
||||||
|
GreeterClient greeter( |
||||||
|
grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials())); |
||||||
|
std::string user("world"); |
||||||
|
std::string reply(greeter.SayHello(user)); |
||||||
|
std::cout << "Greeter received: " << reply << std::endl; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
// Copyright 2022 the gRPC authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "examples/protos/helloworld.grpc.pb.h" |
||||||
|
|
||||||
|
#include <grpcpp/ext/proto_server_reflection_plugin.h> |
||||||
|
#include <grpcpp/grpcpp.h> |
||||||
|
#include <grpcpp/health_check_service_interface.h> |
||||||
|
|
||||||
|
using grpc::Server; |
||||||
|
using grpc::ServerBuilder; |
||||||
|
using grpc::ServerContext; |
||||||
|
using grpc::Status; |
||||||
|
using helloworld::Greeter; |
||||||
|
using helloworld::HelloReply; |
||||||
|
using helloworld::HelloRequest; |
||||||
|
|
||||||
|
// Logic and data behind the server's behavior.
|
||||||
|
class GreeterServiceImpl final : public Greeter::Service { |
||||||
|
Status SayHello(ServerContext* context, const HelloRequest* request, |
||||||
|
HelloReply* reply) override { |
||||||
|
std::string prefix("Hello "); |
||||||
|
reply->set_message(prefix + request->name()); |
||||||
|
return Status::OK; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
void RunServer() { |
||||||
|
std::string server_address("unix:/tmp/server"); |
||||||
|
GreeterServiceImpl service; |
||||||
|
|
||||||
|
grpc::EnableDefaultHealthCheckService(true); |
||||||
|
grpc::reflection::InitProtoReflectionServerBuilderPlugin(); |
||||||
|
ServerBuilder builder; |
||||||
|
// Listen on the given address without any authentication mechanism.
|
||||||
|
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); |
||||||
|
// Register "service" as the instance through which we'll communicate with
|
||||||
|
// clients. In this case it corresponds to an *synchronous* service.
|
||||||
|
builder.RegisterService(&service); |
||||||
|
// Finally assemble the server.
|
||||||
|
std::unique_ptr<Server> server(builder.BuildAndStart()); |
||||||
|
std::cout << "Server listening on " << server_address << std::endl; |
||||||
|
|
||||||
|
// Wait for the server to shutdown. Note that some other thread must be
|
||||||
|
// responsible for shutting down the server for this call to ever return.
|
||||||
|
server->Wait(); |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
RunServer(); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
# Copyright 2022 gRPC authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
# Test structure borrowed with gratitude from |
||||||
|
# https://github.com/grpc/grpc-go/tree/master/examples/features |
||||||
|
|
||||||
|
# Run this script as root |
||||||
|
|
||||||
|
clean() { |
||||||
|
echo "Cleaning..." |
||||||
|
systemctl stop sdsockact.socket |
||||||
|
systemctl stop sdsockact.service |
||||||
|
systemctl daemon-reload |
||||||
|
rm /tmp/greeter_server |
||||||
|
rm /tmp/greeter_client |
||||||
|
rm /etc/systemd/system/sdsockact.service |
||||||
|
rm /etc/systemd/system/sdsockact.socket |
||||||
|
} |
||||||
|
|
||||||
|
fail() { |
||||||
|
clean |
||||||
|
echo "FAIL: $@" >&2 |
||||||
|
exit 1 |
||||||
|
} |
||||||
|
|
||||||
|
pass() { |
||||||
|
echo "SUCCESS: $1" |
||||||
|
} |
||||||
|
|
||||||
|
bazel build --define=use_systemd=true //examples/cpp/features/sd_sock_act:all || fail "Failed to build sd_sock_act" |
||||||
|
cp ../../../../bazel-bin/examples/cpp/features/sd_sock_act/server /tmp/greeter_server |
||||||
|
cp ../../../../bazel-bin/examples/cpp/features/sd_sock_act/client /tmp/greeter_client |
||||||
|
|
||||||
|
cat << EOF > /etc/systemd/system/sdsockact.service |
||||||
|
[Service] |
||||||
|
ExecStart=/tmp/greeter_server |
||||||
|
EOF |
||||||
|
|
||||||
|
cat << EOF > /etc/systemd/system/sdsockact.socket |
||||||
|
[Socket] |
||||||
|
ListenStream=/tmp/server |
||||||
|
ReusePort=true |
||||||
|
|
||||||
|
[Install] |
||||||
|
WantedBy=sockets.target |
||||||
|
EOF |
||||||
|
|
||||||
|
systemctl daemon-reload |
||||||
|
systemctl enable sdsockact.socket |
||||||
|
systemctl start sdsockact.socket |
||||||
|
|
||||||
|
pushd /tmp |
||||||
|
./greeter_client | grep "Hello" |
||||||
|
if [ $? -ne 0 ]; then |
||||||
|
popd |
||||||
|
fail "Response not received" |
||||||
|
fi |
||||||
|
|
||||||
|
popd |
||||||
|
pass "Response received" |
||||||
|
clean |
@ -0,0 +1,116 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2022 gRPC authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/iomgr/port.h" |
||||||
|
|
||||||
|
#ifdef GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON |
||||||
|
|
||||||
|
#ifdef HAVE_LIBSYSTEMD |
||||||
|
#include <systemd/sd-daemon.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "src/core/lib/address_utils/sockaddr_utils.h" |
||||||
|
#include "src/core/lib/iomgr/resolve_address.h" |
||||||
|
#include "src/core/lib/iomgr/sockaddr.h" |
||||||
|
#include "src/core/lib/iomgr/systemd_utils.h" |
||||||
|
|
||||||
|
#ifdef HAVE_LIBSYSTEMD |
||||||
|
bool set_matching_sd_unix_fd(grpc_tcp_server* s, |
||||||
|
const grpc_resolved_address* addr, |
||||||
|
const int fd_start, const int n) { |
||||||
|
absl::StatusOr<std::string> addr_name = grpc_sockaddr_to_string(addr, true); |
||||||
|
for (int i = fd_start; i < fd_start + n; i++) { |
||||||
|
if (sd_is_socket_unix(i, SOCK_STREAM, 1, addr_name.value().c_str(), 0)) { |
||||||
|
grpc_tcp_server_set_pre_allocated_fd(s, i); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool set_matching_sd_inet_fd(grpc_tcp_server* s, |
||||||
|
const grpc_resolved_address* addr, |
||||||
|
const int family, const int port, |
||||||
|
const int fd_start, const int n) { |
||||||
|
for (int i = fd_start; i < fd_start + n; i++) { |
||||||
|
int r_inet = sd_is_socket_inet(i, family, SOCK_STREAM, 1, (uint16_t)port); |
||||||
|
int r_addr = sd_is_socket_sockaddr( |
||||||
|
i, SOCK_STREAM, |
||||||
|
reinterpret_cast<grpc_sockaddr*>(const_cast<char*>(addr->addr)), |
||||||
|
addr->len, 1); |
||||||
|
|
||||||
|
if (r_inet > 0 && r_addr > 0) { |
||||||
|
grpc_tcp_server_set_pre_allocated_fd(s, i); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
void set_matching_sd_fds(grpc_tcp_server* s, const grpc_resolved_address* addr, |
||||||
|
int requested_port) { |
||||||
|
int n = sd_listen_fds(0); |
||||||
|
if (n <= 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
int fd_start = SD_LISTEN_FDS_START; |
||||||
|
grpc_resolved_address addr6_v4mapped; |
||||||
|
|
||||||
|
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { |
||||||
|
addr = &addr6_v4mapped; |
||||||
|
} |
||||||
|
|
||||||
|
int family = grpc_sockaddr_get_family(addr); |
||||||
|
int port = grpc_sockaddr_get_port(addr); |
||||||
|
|
||||||
|
if (family == AF_UNIX) { |
||||||
|
set_matching_sd_unix_fd(s, addr, fd_start, n); |
||||||
|
} else { |
||||||
|
if (grpc_sockaddr_is_wildcard(addr, &requested_port)) { |
||||||
|
grpc_resolved_address wild4; |
||||||
|
grpc_resolved_address wild6; |
||||||
|
grpc_resolved_address wildcard_addrs[2]; |
||||||
|
|
||||||
|
grpc_sockaddr_make_wildcards(requested_port, &wild4, &wild6); |
||||||
|
wildcard_addrs[0] = wild4; |
||||||
|
wildcard_addrs[1] = wild6; |
||||||
|
|
||||||
|
for (grpc_resolved_address addr_w : wildcard_addrs) { |
||||||
|
int family_w = grpc_sockaddr_get_family(&addr_w); |
||||||
|
int port_w = grpc_sockaddr_get_port(&addr_w); |
||||||
|
if (set_matching_sd_inet_fd(s, &addr_w, family_w, port_w, fd_start, |
||||||
|
n)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
set_matching_sd_inet_fd(s, addr, family, port, fd_start, n); |
||||||
|
} |
||||||
|
} |
||||||
|
#else |
||||||
|
void set_matching_sd_fds(GRPC_UNUSED grpc_tcp_server* s, |
||||||
|
GRPC_UNUSED const grpc_resolved_address* addr, |
||||||
|
GRPC_UNUSED int requested_port) {} |
||||||
|
#endif /* HAVE_LIBSYSTEMD */ |
||||||
|
|
||||||
|
#endif /* GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON */ |
@ -0,0 +1,33 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2022 gRPC authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef GRPC_CORE_LIB_IOMGR_SYSTEMD_UTILS_H |
||||||
|
#define GRPC_CORE_LIB_IOMGR_SYSTEMD_UTILS_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/iomgr/tcp_server_utils_posix.h" |
||||||
|
|
||||||
|
/* Check whether systemd has pre-allocated FDs. If so, check whether any
|
||||||
|
* pre-allocated FD is valid, i.e. matches addr and its family. If there is |
||||||
|
* any valid FD, set its value to s->pre_allocated_fd |
||||||
|
*/ |
||||||
|
void set_matching_sd_fds(grpc_tcp_server* s, const grpc_resolved_address* addr, |
||||||
|
int requested_port); |
||||||
|
|
||||||
|
#endif /* GRPC_CORE_LIB_IOMGR_SYSTEMD_UTILS_H */ |
Loading…
Reference in new issue