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