Coverage Report

Created: 2026-10-23 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/trafficserver/include/shared/cache_shm/Layout.h
Line
Count
Source
1
/** @file
2
3
  Layout of the cache shared-memory control segment, shared between the cache
4
  subsystem and tools (traffic_ctl) that inspect or clear the segment without
5
  going through the running traffic_server.
6
7
  @section license License
8
9
  Licensed to the Apache Software Foundation (ASF) under one
10
  or more contributor license agreements.  See the NOTICE file
11
  distributed with this work for additional information
12
  regarding copyright ownership.  The ASF licenses this file
13
  to you under the Apache License, Version 2.0 (the
14
  "License"); you may not use this file except in compliance
15
  with the License.  You may obtain a copy of the License at
16
17
      http://www.apache.org/licenses/LICENSE-2.0
18
19
  Unless required by applicable law or agreed to in writing, software
20
  distributed under the License is distributed on an "AS IS" BASIS,
21
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
  See the License for the specific language governing permissions and
23
  limitations under the License.
24
 */
25
26
#pragma once
27
28
#include "tscore/ink_align.h"
29
#include "tscore/ink_memory.h"
30
31
#include <cstddef>
32
#include <cstdint>
33
#include <string>
34
#include <string_view>
35
#include <type_traits>
36
37
namespace cache_shm
38
{
39
40
constexpr char             CACHE_SHM_MAGIC[8]       = {'A', 'T', 'S', '-', 'S', 'H', 'M', '\0'};
41
constexpr uint32_t         CACHE_SHM_SCHEMA_VERSION = 1;
42
constexpr std::string_view CACHE_SHM_CONTROL        = "control";
43
44
// macOS PSHMNAMLEN is 31 chars including the leading '/'. Keep names under that
45
// limit on Linux too, so the same naming works everywhere.
46
constexpr std::size_t MAX_SHM_NAME_LEN = 31;
47
48
// Maximum number of stripes in the control segment. Bumping it changes both the ABI
49
// hash and sizeof(CacheShmControl); a prior segment is dropped on attach either way
50
// (see CONTROL_HEADER_SIZE for how the size change is detected).
51
constexpr std::size_t MAX_STRIPES = 256;
52
53
// Per-stripe entry in the control segment. A stripe is matched to its prior
54
// segment on attach by stripe_key_hash, not by name (order-independent).
55
// dir_untrusted lives here and not in the stripe's own header because that header aliases raw_dir, which is also the source
56
// buffer for the on-disk directory write -- a mark written there can reach disk and cost the stripe on the next start.
57
struct StripeEntry {
58
  char     shm_name[MAX_SHM_NAME_LEN + 1]; ///< full shm name, NUL-terminated.
59
  uint64_t raw_dir_size;                   ///< size of the stripe's raw_dir segment, bytes.
60
  uint64_t stripe_key_hash;                ///< full 64-bit FNV-1a of the stripe hash_text.
61
  uint8_t  dir_untrusted;                  ///< 1 = shutdown could not vouch for this directory; never attach it again.
62
  uint8_t  pad0[7];
63
};
64
65
struct CacheShmControl {
66
  char     magic[8];       ///< CACHE_SHM_MAGIC
67
  uint32_t schema_version; ///< CACHE_SHM_SCHEMA_VERSION
68
  uint32_t pad0;
69
  uint64_t abi_hash;          ///< compile-time ABI fingerprint
70
  uint64_t storage_signature; ///< storage.yaml fingerprint
71
  uint8_t  clean_shutdown;    ///< 0 = dirty, 1 = clean
72
  uint8_t  pad1[3];
73
  int32_t  owner_pid; ///< PID of the process that took the segment; 0 when none. Backs the
74
                      ///< concurrent-attach guard, so it is held until the owner exits, not
75
                      ///< cleared at clean shutdown; the next start tests it for liveness.
76
  uint32_t    stripe_count;
77
  uint32_t    pad2;
78
  StripeEntry stripes[MAX_STRIPES];
79
};
80
81
constexpr std::size_t CONTROL_SIZE = sizeof(CacheShmControl);
82
83
// FROZEN: append to StripeEntry or grow stripes[] freely, but never reorder or extend the bytes ahead of stripes[]. A
84
// build with a different sizeof(CacheShmControl) must still be able to read this far to drop the segment rather than
85
// wedge on EEXIST forever. See "The frozen control header" in the shm-fast-restart developer guide.
86
constexpr std::size_t CONTROL_HEADER_SIZE = offsetof(CacheShmControl, stripes);
87
static_assert(CONTROL_HEADER_SIZE == 48, "the control segment header is a frozen layout; see the comment above");
88
static_assert(std::is_standard_layout_v<CacheShmControl>, "the control segment is shared across processes and builds");
89
90
/// Whether @a actual is the object size this platform reports after truncating
91
/// a POSIX shared-memory object to @a requested bytes.
92
inline bool
93
is_expected_shm_size(std::size_t actual, std::size_t requested)
94
0
{
95
#if defined(__APPLE__)
96
  // macOS rounds the reported object size up to the VM page size.
97
  return actual >= requested && actual <= INK_ALIGN(requested, ats_pagesize());
98
#else
99
0
  return actual == requested;
100
0
#endif
101
0
}
102
103
// Whether a control segment of `actual` bytes was written by *this* build. Anything else has a stripes[] of unknown
104
// stride and must never be walked with our layout. Shared by the attach gate, the purge primitive and
105
// `traffic_ctl cache shm status` so the three cannot drift apart.
106
inline bool
107
is_own_control_size(std::size_t actual)
108
0
{
109
0
  return is_expected_shm_size(actual, CONTROL_SIZE);
110
0
}
111
112
// Frame the operator's middle word (e.g. "ats") as "/<word>-". The framing is supplied here so it cannot be mis-typed:
113
// stray framing from an older config is trimmed, and embedded '/' stripped since POSIX permits only the leading one.
114
inline std::string
115
normalize_name_prefix(std::string_view configured)
116
0
{
117
0
  std::size_t begin = configured.find_first_not_of('/');
118
0
  if (begin == std::string_view::npos) {
119
0
    begin = configured.size(); // all '/' (or empty): no middle.
120
0
  }
121
0
  std::size_t      last_kept = configured.find_last_not_of('-');
122
0
  std::string_view middle    = (last_kept == std::string_view::npos || last_kept < begin) ?
123
0
                                 std::string_view{} :
124
0
                                 configured.substr(begin, last_kept - begin + 1);
125
0
  std::string      word{"/"};
126
0
  for (char c : middle) {
127
0
    if (c != '/') { // POSIX shm names allow only the leading '/'.
128
0
      word += c;
129
0
    }
130
0
  }
131
0
  word += "-";
132
0
  return word;
133
0
}
134
135
// Name of the "<prefix>control" segment. Derived here so the cache subsystem and
136
// traffic_ctl agree; `prefix` is the normalized prefix (e.g. "/ats-").
137
inline std::string
138
control_segment_name(std::string_view prefix)
139
0
{
140
0
  return std::string(prefix) + CACHE_SHM_CONTROL.data();
141
0
}
142
143
} // namespace cache_shm