1
#pragma once
2

            
3
#include <fcntl.h>
4
#include <sys/un.h>
5

            
6
#include <array>
7
#include <atomic>
8
#include <cstdint>
9
#include <string>
10

            
11
#include "envoy/common/platform.h"
12
#include "envoy/server/hot_restart.h"
13

            
14
#include "source/common/common/assert.h"
15
#include "source/common/stats/allocator.h"
16
#include "source/server/hot_restarting_child.h"
17
#include "source/server/hot_restarting_parent.h"
18

            
19
namespace Envoy {
20
namespace Server {
21

            
22
// Increment this whenever there is a shared memory / RPC change that will prevent a hot restart
23
// from working. Operations code can then cope with this and do a full restart.
24
const uint64_t HOT_RESTART_VERSION = 11;
25

            
26
/**
27
 * Shared memory segment. This structure is laid directly into shared memory and is used amongst
28
 * all running envoy processes.
29
 */
30
struct SharedMemory {
31
  uint64_t size_;
32
  uint64_t version_;
33
  pthread_mutex_t log_lock_;
34
  pthread_mutex_t access_log_lock_;
35
  std::atomic<uint64_t> flags_;
36
};
37
static const uint64_t SHMEM_FLAGS_INITIALIZING = 0x1;
38

            
39
/**
40
 * Initialize the shared memory segment, depending on whether we are the first running
41
 * envoy, or a host restarted envoy process.
42
 *
43
 * @param base_id uint32_t that is the base id flag used to start this Envoy.
44
 * @param restart_epoch uint32_t the restart epoch flag used to start this Envoy.
45
 */
46
SharedMemory* attachSharedMemory(uint32_t base_id, uint32_t restart_epoch);
47

            
48
/**
49
 * Initialize a pthread mutex for process shared locking.
50
 */
51
void initializeMutex(pthread_mutex_t& mutex);
52

            
53
/**
54
 * Implementation of Thread::BasicLockable that operates on a process shared pthread mutex.
55
 */
56
class ProcessSharedMutex : public Thread::BasicLockable {
57
public:
58
38
  ProcessSharedMutex(pthread_mutex_t& mutex) : mutex_(mutex) {}
59

            
60
111
  void lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() override {
61
    // Deal with robust handling here. If the other process dies without unlocking, we are going
62
    // to die shortly but try to make sure that we can handle any signals, etc. that happen without
63
    // getting into a further messed up state.
64
111
    int rc = pthread_mutex_lock(&mutex_);
65
111
    ASSERT(rc == 0 || rc == EOWNERDEAD);
66
111
    if (rc == EOWNERDEAD) {
67
      pthread_mutex_consistent(&mutex_);
68
    }
69
111
  }
70

            
71
  bool tryLock() ABSL_EXCLUSIVE_TRYLOCK_FUNCTION(true) override {
72
    int rc = pthread_mutex_trylock(&mutex_);
73
    if (rc == EBUSY) {
74
      return false;
75
    }
76

            
77
    ASSERT(rc == 0 || rc == EOWNERDEAD);
78
    if (rc == EOWNERDEAD) {
79
      pthread_mutex_consistent(&mutex_);
80
    }
81

            
82
    return true;
83
  }
84

            
85
111
  void unlock() ABSL_UNLOCK_FUNCTION() override {
86
111
    int rc = pthread_mutex_unlock(&mutex_);
87
111
    ASSERT(rc == 0);
88
111
  }
89

            
90
private:
91
  pthread_mutex_t& mutex_;
92
};
93

            
94
/**
95
 * Implementation of HotRestart built for Linux. Most of the "protocol" type logic is split out into
96
 * HotRestarting{Base,Parent,Child}. This class ties all that to shared memory and version logic.
97
 */
98
class HotRestartImpl : public HotRestart {
99
public:
100
  HotRestartImpl(uint32_t base_id, uint32_t restart_epoch, const std::string& socket_path,
101
                 mode_t socket_mode, bool skip_hot_restart_on_no_parent, bool skip_parent_stats);
102

            
103
  // Server::HotRestart
104
  void drainParentListeners() override;
105
  int duplicateParentListenSocket(const std::string& address, uint32_t worker_index,
106
                                  absl::string_view network_namespace) override;
107
  void registerUdpForwardingListener(
108
      Network::Address::InstanceConstSharedPtr address,
109
      std::shared_ptr<Network::UdpListenerConfig> listener_config) override;
110
  OptRef<Network::ParentDrainedCallbackRegistrar> parentDrainedCallbackRegistrar() override;
111
  void initialize(Event::Dispatcher& dispatcher, Server::Instance& server) override;
112
  absl::optional<AdminShutdownResponse> sendParentAdminShutdownRequest() override;
113
  void sendParentTerminateRequest() override;
114
  ServerStatsFromParent mergeParentStatsIfAny(Stats::StoreRoot& stats_store) override;
115
  void shutdown() override;
116
  uint32_t baseId() override;
117
  std::string version() override;
118
10
  Thread::BasicLockable& logLock() override { return log_lock_; }
119
10
  Thread::BasicLockable& accessLogLock() override { return access_log_lock_; }
120
  bool isInitializing() const override;
121

            
122
  /**
123
   * envoy --hot_restart_version doesn't initialize Envoy, but computes the version string
124
   * based on the configured options.
125
   */
126
  static std::string hotRestartVersion();
127

            
128
private:
129
  friend class HotRestartUdpForwardingTestHelper;
130
  uint32_t base_id_;
131
  uint32_t scaled_base_id_;
132
  HotRestartingChild as_child_;
133
  HotRestartingParent as_parent_;
134
  // This pointer is shared memory, and is expected to exist until process end.
135
  // It will automatically be unmapped when the process terminates.
136
  SharedMemory* shmem_;
137
  ProcessSharedMutex log_lock_;
138
  ProcessSharedMutex access_log_lock_;
139
};
140

            
141
} // namespace Server
142
} // namespace Envoy