Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* Copyright 2015 Florian Philipp
- *
- * 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 <signal.h>
- // using sigaction, pthread_sigmask
- #include <arpa/inet.h>
- // using htons
- #include <netinet/ip.h>
- // using accept, bind, listen, socket
- #include <unistd.h>
- // using close
- #include <atomic>
- // using std::atomic_signal_fence
- #include <cstring>
- // using std::memset
- #include <system_error>
- // using std::system_error, std::system_category
- #include <cerrno>
- // using errno
- #include <iostream>
- // using std::cout
- #include <array>
- // using std::array
- #include <cassert>
- // using assert
- namespace {
- /**
- * Throws an std::system_error using errno
- */
- void make_system_error(const char* operation)
- {
- throw std::system_error(errno, std::system_category(), operation);
- }
- /**
- * RAII for an installed signal handler. Dtor restores old behavior
- */
- class AlteredSignal
- {
- int signal;
- struct sigaction orig;
- public:
- typedef void (*signal_handler_t)(int);
- AlteredSignal() noexcept : signal(-1) {}
- AlteredSignal(int signal, signal_handler_t handler)
- : signal(signal)
- {
- struct sigaction action;
- std::memset(&action, 0, sizeof(action));
- action.sa_handler = handler;
- if(sigaction(signal, &action, &orig))
- make_system_error("sigaction");
- }
- AlteredSignal(const AlteredSignal&) = delete;
- AlteredSignal(AlteredSignal&& o) noexcept
- {
- signal = o.signal;
- o.signal = -1;
- orig = o.orig;
- }
- ~AlteredSignal()
- {
- if(signal >= 0)
- sigaction(signal, &orig, nullptr);
- }
- AlteredSignal& operator=(const AlteredSignal&) = delete;
- AlteredSignal& operator=(AlteredSignal&& o)
- {
- if(signal >= 0 && sigaction(signal, &orig, nullptr))
- make_system_error("sigaction");
- signal = o.signal;
- if(o.signal >= 0) {
- orig = o.orig;
- o.signal = -1;
- }
- return *this;
- }
- };
- /**
- * RAII for blocked signals. Dtor unblocks signals again
- */
- class SignalBlock
- {
- sigset_t orig;
- bool active;
- public:
- SignalBlock() noexcept : orig(), active(false) {}
- explicit SignalBlock(const sigset_t& mask)
- : orig(), active(true)
- {
- if(pthread_sigmask(SIG_BLOCK, &mask, &orig))
- make_system_error("pthread_sigmask");
- }
- SignalBlock(const SignalBlock&) = delete;
- SignalBlock(SignalBlock&& o) noexcept
- : orig(o.orig), active(o.active)
- {
- o.active = false;
- }
- ~SignalBlock()
- {
- if(active)
- pthread_sigmask(SIG_SETMASK, &orig, nullptr);
- }
- SignalBlock& operator=(const SignalBlock&) = delete;
- SignalBlock& operator=(SignalBlock&& o)
- {
- if(active && pthread_sigmask(SIG_SETMASK, &orig, nullptr))
- make_system_error("pthread_sigmask");
- active = o.active;
- if(o.active) {
- orig = o.orig;
- o.active = false;
- }
- return *this;
- }
- /**
- * Signal mask that can be used to unblock signals in pselect or ppoll
- */
- const sigset_t& unmask_set() const noexcept
- { return orig; }
- };
- /**
- * Installs a signal handler that sets a flag on SIGINT and SIGTERM
- */
- class TerminationFlag
- {
- static bool termflag;
- static const std::array<int, 2> caught_signals;
- std::array<AlteredSignal, 2> altered;
- /**
- * Signal handler
- */
- static void term_action(int) noexcept
- {
- termflag = true;
- std::atomic_signal_fence(std::memory_order_release);
- }
- public:
- TerminationFlag()
- {
- for(std::size_t i = 0; i < caught_signals.size(); ++i)
- altered[i] = AlteredSignal(caught_signals[i], term_action);
- }
- /**
- * Blocks SIGTERM and SIGINT until the returned object is destroyed
- */
- SignalBlock block()
- {
- sigset_t mask;
- if(sigemptyset(&mask))
- make_system_error("sigemptyset");
- for(int sig: caught_signals)
- if(sigaddset(&mask, sig))
- make_system_error("sigaddset");
- return SignalBlock(mask);
- }
- /**
- * True if SIGTERM or SIGINT have been received
- */
- bool triggered() const noexcept
- {
- bool val = termflag;
- std::atomic_signal_fence(std::memory_order_acquire);
- return val;
- }
- };
- bool TerminationFlag::termflag;
- const std::array<int, 2> TerminationFlag::caught_signals = {
- SIGTERM, SIGINT
- };
- /**
- * RAII for a socket or any kind of file descriptor
- */
- struct Socket
- {
- int fd;
- explicit Socket(int fd = -1) noexcept
- : fd(fd) {}
- ~Socket()
- {
- if(fd >= 0)
- close(fd);
- }
- Socket(const Socket&) = delete;
- Socket(Socket&& o) noexcept
- : fd(o.fd)
- {
- o.fd = -1;
- }
- explicit operator bool() const noexcept
- { return fd >= 0; }
- Socket& operator=(const Socket&) = delete;
- Socket& operator=(Socket&& o)
- {
- using std::swap;
- swap(fd, o.fd);
- return *this;
- }
- };
- /**
- * Listens on 127.0.0.1:1337 for connections
- */
- class Listener
- {
- Socket sock;
- public:
- Listener()
- {
- if((sock.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- make_system_error("socket");
- struct sockaddr_in addr;
- std::memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(1337);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- if(bind(sock.fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))
- make_system_error("bind");
- if(listen(sock.fd, 128))
- make_system_error("listen");
- }
- /**
- * Blocking call to accept a new connection
- */
- Socket accept()
- {
- int fd;
- if((fd = ::accept(sock.fd, nullptr, 0)) < 0)
- make_system_error("accept");
- return Socket(fd);
- }
- /**
- * Blocking, interruptible call to accept a new connection
- *
- * \param sigmask signal mask for pselect
- */
- Socket poll(const sigset_t& sigmask)
- {
- fd_set read_fds;
- FD_ZERO(&read_fds);
- FD_SET(sock.fd, &read_fds);
- int n_fds;
- if((n_fds = pselect(sock.fd + 1, &read_fds, nullptr /*write*/,
- nullptr /*except*/, nullptr /*timeout*/, &sigmask))
- < 0)
- make_system_error("pselect");
- assert(n_fds == 1);
- assert(FD_ISSET(sock.fd, &read_fds));
- return accept();
- }
- };
- /**
- * TODO: Actually process the client.
- */
- void handle_client(Socket)
- {}
- }
- int main()
- {
- TerminationFlag termination;
- Listener listener;
- while(1) {
- Socket sock;
- {
- SignalBlock block = termination.block();
- /* important: Block signals before checking them
- * to avoid race condition
- */
- if(termination.triggered())
- break;
- try {
- /* Now use pselect or ppoll to wait for a client with unmasked signals.
- * A signal will cause an EINTR. For simplicity, we check this in the
- * next iteration
- */
- sock = listener.poll(block.unmask_set());
- } catch(std::system_error& err) {
- if(err.code().value() == EINTR)
- continue;
- throw;
- }
- }
- handle_client(std::move(sock));
- }
- std::cout << "Clean termination\n";
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement