/src/mysql-server/mysys/my_file.cc
Line | Count | Source |
1 | | /* Copyright (c) 2000, 2025, Oracle and/or its affiliates. |
2 | | |
3 | | This program is free software; you can redistribute it and/or modify |
4 | | it under the terms of the GNU General Public License, version 2.0, |
5 | | as published by the Free Software Foundation. |
6 | | |
7 | | This program is designed to work with certain software (including |
8 | | but not limited to OpenSSL) that is licensed under separate terms, |
9 | | as designated in a particular file or component or in included license |
10 | | documentation. The authors of MySQL hereby grant you an additional |
11 | | permission to link the program and your derivative works with the |
12 | | separately licensed software that they have either included with |
13 | | the program or referenced in the documentation. |
14 | | |
15 | | Without limiting anything contained in the foregoing, this file, |
16 | | which is part of C Driver for MySQL (Connector/C), is also subject to the |
17 | | Universal FOSS Exception, version 1.0, a copy of which can be found at |
18 | | http://oss.oracle.com/licenses/universal-foss-exception. |
19 | | |
20 | | This program is distributed in the hope that it will be useful, |
21 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 | | GNU General Public License, version 2.0, for more details. |
24 | | |
25 | | You should have received a copy of the GNU General Public License |
26 | | along with this program; if not, write to the Free Software |
27 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
28 | | |
29 | | /** |
30 | | @file mysys/my_file.cc |
31 | | */ |
32 | | |
33 | | #include "my_config.h" |
34 | | |
35 | | #include <errno.h> // IWYU pragma: keep errno |
36 | | #include <sys/types.h> |
37 | | |
38 | | #include <algorithm> |
39 | | #include <cassert> |
40 | | #include <cstring> |
41 | | #include <iostream> |
42 | | #include <limits> |
43 | | #include <utility> |
44 | | #include <vector> |
45 | | |
46 | | #include "sql/malloc_allocator.h" |
47 | | |
48 | | #include "mutex_lock.h" // MUTEX_LOCK |
49 | | #include "my_dbug.h" |
50 | | #include "my_inttypes.h" |
51 | | #include "my_io.h" |
52 | | #include "my_sys.h" |
53 | | #include "mysql/service_mysql_alloc.h" |
54 | | #include "mysys/mysys_priv.h" |
55 | | #ifdef HAVE_SYS_RESOURCE_H |
56 | | #include <sys/resource.h> /* RLIMIT_NOFILE */ |
57 | | #endif |
58 | | |
59 | | namespace { |
60 | | /** |
61 | | Set the OS limit on the number of open files. On POSIX systems this |
62 | | calls setrlimit(RLIMIT_NOFILE, ...). On Windows there is no |
63 | | corresponding api so the requested value is returned. The assumption |
64 | | being that the request will never be larger than OS_FILE_LIMIT, @see |
65 | | my_set_max_open_files. |
66 | | |
67 | | @param max_file_limit Files to open |
68 | | |
69 | | @note The request may not fulfilled because of system limitations |
70 | | |
71 | | @return Files available to open. May be more or less than max_file_limit! |
72 | | */ |
73 | | |
74 | 0 | uint SetOsLimitMaxOpenFiles(uint max_file_limit) { |
75 | 0 | DBUG_TRACE; |
76 | |
|
77 | 0 | #ifndef _WIN32 |
78 | 0 | rlimit existing; |
79 | 0 | if (getrlimit(RLIMIT_NOFILE, &existing) == -1) { |
80 | 0 | DBUG_PRINT("warning", ("getrlimit(RLIMIT_NOFILE) failed: %s (%d)", |
81 | 0 | strerror(errno), errno)); |
82 | 0 | return max_file_limit; |
83 | 0 | } |
84 | | |
85 | | // If rlim_cur is larger than what is requested, we use that |
86 | | // instead, but capped to the largest value an uint can hold, |
87 | | // (rlim_t can be 64 bit). |
88 | 0 | if (existing.rlim_cur >= max_file_limit) { |
89 | 0 | constexpr const rlim_t uim = std::numeric_limits<uint>::max(); |
90 | 0 | return std::min(existing.rlim_cur, uim); |
91 | 0 | } |
92 | | |
93 | | // Attempt to modify OS setting |
94 | 0 | rlimit request; |
95 | 0 | request.rlim_cur = max_file_limit; |
96 | 0 | request.rlim_max = max_file_limit; |
97 | 0 | if (setrlimit(RLIMIT_NOFILE, &request) == -1) { |
98 | 0 | DBUG_PRINT("warning", ("setrlimit(RLIMIT_NOFILE)=%u failed: %s (%d)", |
99 | 0 | max_file_limit, strerror(errno), errno)); |
100 | 0 | return existing.rlim_cur; /* Use original value */ |
101 | 0 | } |
102 | | |
103 | | #ifndef NDEBUG |
104 | | // Read back new value to check "what we got". Seems overly |
105 | | // pessimistic to assume that a successful setrlimit did not |
106 | | // actually set the requested values. |
107 | | rlimit readback; |
108 | | if (getrlimit(RLIMIT_NOFILE, &readback) == -1) { |
109 | | DBUG_PRINT("warning", |
110 | | ("getrlimit(RLIMIT_NOFILE) (after set) failed: %s (%d)", |
111 | | strerror(errno), errno)); |
112 | | return max_file_limit; |
113 | | } |
114 | | assert(readback.rlim_cur == request.rlim_cur && |
115 | | readback.rlim_max == request.rlim_max); |
116 | | #endif /* NDEBUG */ |
117 | 0 | return request.rlim_cur; |
118 | | #else /* not defined(_WIN32) */ |
119 | | // We don't know the limit. |
120 | | assert(max_file_limit <= OS_FILE_LIMIT); |
121 | | return max_file_limit; |
122 | | #endif /* not defined _WIN32 */ |
123 | 0 | } |
124 | | |
125 | | /** |
126 | | Rule of 5 class. |
127 | | @see https://en.cppreference.com/w/cpp/language/rule_of_three |
128 | | @see https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-five |
129 | | */ |
130 | | class FileInfo { |
131 | | const char *m_name = nullptr; |
132 | | file_info::OpenType m_type = file_info::OpenType::UNOPEN; |
133 | | |
134 | | public: |
135 | 0 | FileInfo() = default; |
136 | | |
137 | | FileInfo(const char *n, file_info::OpenType t) |
138 | 0 | : m_name{my_strdup(key_memory_my_file_info, n, |
139 | 0 | MYF(MY_WME | ME_FATALERROR))}, |
140 | 0 | m_type{t} {} |
141 | | |
142 | | // Rule of 5 (2) |
143 | | FileInfo(const FileInfo &) = delete; |
144 | | |
145 | | // Rule of 5 (4) |
146 | | FileInfo(FileInfo &&src) noexcept |
147 | 0 | : m_name{std::exchange(src.m_name, nullptr)}, |
148 | 0 | m_type{std::exchange(src.m_type, file_info::OpenType::UNOPEN)} {} |
149 | | |
150 | | // Rule of 5 (1) |
151 | 0 | ~FileInfo() { my_free(const_cast<char *>(m_name)); } |
152 | | |
153 | | // Rule of 5 (3) |
154 | | FileInfo &operator=(const FileInfo &) = delete; |
155 | | |
156 | | // Rule of 5 (5) |
157 | 0 | FileInfo &operator=(FileInfo &&src) { |
158 | 0 | FileInfo tmp{std::move(src)}; |
159 | 0 | Swap(&tmp); |
160 | 0 | return *this; |
161 | 0 | } |
162 | | |
163 | | // Member swap for move assignment. |
164 | 0 | void Swap(FileInfo *src) noexcept { |
165 | 0 | std::swap(m_type, src->m_type); |
166 | 0 | std::swap(m_name, src->m_name); |
167 | 0 | } |
168 | | |
169 | 0 | const char *name() const { return m_name; } |
170 | 0 | file_info::OpenType type() const { return m_type; } |
171 | | }; |
172 | | |
173 | | using FileInfoAllocator = Malloc_allocator<FileInfo>; |
174 | | using FileInfoVector = std::vector<FileInfo, FileInfoAllocator>; |
175 | | FileInfoVector *fivp = nullptr; |
176 | | } // namespace |
177 | | |
178 | | namespace file_info { |
179 | | |
180 | | /** |
181 | | Add FileInfo entry for file descriptor. Increments status variable |
182 | | for open files/streams. |
183 | | @relates file_info::RegisterFilename |
184 | | |
185 | | @param fd file descriptor |
186 | | @param file_name name of file |
187 | | @param type_of_file tag indicating how the fd was created |
188 | | */ |
189 | 0 | void RegisterFilename(File fd, const char *file_name, OpenType type_of_file) { |
190 | 0 | assert(fd > -1); |
191 | 0 | FileInfoVector &fiv = *fivp; |
192 | 0 | MUTEX_LOCK(g, &THR_LOCK_open); |
193 | 0 | if (static_cast<size_t>(fd) >= fiv.size()) { |
194 | 0 | fiv.resize(fd + 1); |
195 | 0 | } |
196 | 0 | CountFileOpen(fiv[fd].type(), type_of_file); |
197 | 0 | fiv[fd] = {file_name, type_of_file}; |
198 | |
|
199 | 0 | dbug("fileinfo", [&]() { |
200 | 0 | std::cerr << "Registering (" << fd << ", '" << file_name << ")" |
201 | 0 | << std::endl; |
202 | 0 | }); |
203 | 0 | } |
204 | | |
205 | | /** |
206 | | Remove FileInfo entry for file descriptor. Decrements status |
207 | | variables for open files/streams. |
208 | | @relates file_info::UnregisterFilename |
209 | | |
210 | | @param fd file descriptor |
211 | | */ |
212 | 0 | void UnregisterFilename(File fd) { |
213 | 0 | FileInfoVector &fiv = *fivp; |
214 | 0 | MUTEX_LOCK(g, &THR_LOCK_open); |
215 | |
|
216 | 0 | if (static_cast<size_t>(fd) >= fiv.size()) { |
217 | 0 | dbug("fileinfo", [&]() { |
218 | 0 | std::cerr << "Un-registering unknown fd:" << fd << "!" << std::endl; |
219 | 0 | }); |
220 | 0 | return; |
221 | 0 | } |
222 | 0 | if (fiv[fd].type() == OpenType::UNOPEN) { |
223 | 0 | dbug("fileinfo", [&]() { |
224 | 0 | std::cerr << "Un-registering already UNOPEN fd:" << fd << std::endl; |
225 | 0 | }); |
226 | 0 | return; |
227 | 0 | } |
228 | 0 | CountFileClose(fiv[fd].type()); |
229 | |
|
230 | 0 | dbug("fileinfo", [&]() { |
231 | 0 | std::cerr << "Un-registering (" << fd << ", '" << fiv[fd].name() << "')" |
232 | 0 | << std::endl; |
233 | 0 | }); |
234 | 0 | fiv[fd] = {}; |
235 | 0 | } |
236 | | } // namespace file_info |
237 | | |
238 | | /** |
239 | | Get filename of file. |
240 | | |
241 | | @param fd file descriptor |
242 | | @return file name in file_info object |
243 | | */ |
244 | 0 | const char *my_filename(File fd) { |
245 | 0 | DBUG_TRACE; |
246 | 0 | const FileInfoVector &fiv = *fivp; |
247 | 0 | MUTEX_LOCK(g, &THR_LOCK_open); |
248 | 0 | if (fd < 0 || fd >= static_cast<int>(fiv.size())) { |
249 | 0 | return "<fd out of range>"; |
250 | 0 | } |
251 | 0 | const FileInfo &fi = fiv[fd]; |
252 | 0 | if (fi.type() == file_info::OpenType::UNOPEN) { |
253 | 0 | return "<unopen fd>"; |
254 | 0 | } |
255 | 0 | return fi.name(); |
256 | 0 | } |
257 | | |
258 | | /** |
259 | | Sets the OS limit on the number of open files (if supported). |
260 | | |
261 | | @param files Number of requested files |
262 | | |
263 | | @return The actual new OS limit which may be both more or less than |
264 | | what was requested. |
265 | | */ |
266 | 0 | uint my_set_max_open_files(uint files) { |
267 | 0 | DBUG_TRACE; |
268 | 0 | return SetOsLimitMaxOpenFiles(std::min(files + MY_FILE_MIN, OS_FILE_LIMIT)); |
269 | 0 | } |
270 | | |
271 | | /** |
272 | | Constructs static objects. |
273 | | */ |
274 | 2 | void MyFileInit() { |
275 | 2 | fivp = |
276 | 2 | new FileInfoVector(Malloc_allocator<FileInfo>{key_memory_my_file_info}); |
277 | 2 | } |
278 | | |
279 | | /** |
280 | | Destroys static objects. |
281 | | */ |
282 | 0 | void MyFileEnd() { delete fivp; } |