bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
TempFile.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of the BES, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2018 OPeNDAP, Inc.
7// Author: Nathan Potter <ndp@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25#include "config.h"
26
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#include <csignal>
31#include <sys/wait.h>
32#include <sstream>
33
34#include <cstdlib>
35#include <cstring>
36#include <cerrno>
37#include <string>
38#include <memory>
39
40#include <BESInternalError.h>
41#include <BESInternalFatalError.h>
42
43#include "TempFile.h"
44#include "BESLog.h"
45#include "BESUtil.h"
46#include "BESDebug.h"
47
48using namespace std;
49
50#define MODULE "dap"
51#define prolog string("TempFile::").append(__func__).append("() - ")
52
53#define STRICT 0
54
55namespace bes {
56
57std::map<std::string, int, std::less<>> TempFile::open_files;
58struct sigaction TempFile::cached_sigpipe_handler;
59
60static string build_error_msg(const string &msg, const string &file = "", int line = -1) {
61 if (file.empty())
62 return {msg + ": " + strerror(errno) + " (errno: " + to_string(errno) + ")"};
63 else
64 return {msg + ": " + strerror(errno) + " (errno: " + to_string(errno) + "at" + file + ":"
65 + to_string(line) + ")\n"};
66}
67
75 try {
76 int saved_errno = errno;
77 if (sig == SIGPIPE) {
78 for (const auto &mpair: open_files) {
79 if (unlink((mpair.first).c_str()) == -1) {
80 ERROR_LOG(build_error_msg("Error unlinking temporary file: " + mpair.first, __FILE__, __LINE__));
81 }
82 }
83
84 // Files cleaned up? Sweet! Time to bail...
85 // replace this SIGPIPE handler with the cached one.
86 sigaction(SIGPIPE, &cached_sigpipe_handler, nullptr);
87
88 // Re-raise SIGPIPE
89 errno = saved_errno;
90 raise(SIGPIPE);
91 }
92 }
93 catch (const std::exception &e) {
94 ERROR_LOG(build_error_msg("std::exception: " + string(e.what()), __FILE__, __LINE__));
95 }
96}
97
112bool TempFile::mk_temp_dir(const std::string &dir_name) {
113
114 mode_t mode = S_IRWXU | S_IRWXG;
115 BESDEBUG(MODULE, prolog << "mode: " << std::oct << mode << endl);
116
117 if (mkdir(dir_name.c_str(), mode)) {
118 if (errno != EEXIST) {
119 throw BESInternalFatalError(build_error_msg("Failed to create temp directory: " + dir_name), __FILE__,
120 __LINE__);
121 }
122 else {
123 BESDEBUG(MODULE, prolog << "The temp directory: " << dir_name << " exists." << endl);
124 return false;
125#if STRICT
126 uid_t uid = getuid();
127 gid_t gid = getgid();
128 BESDEBUG(MODULE,prolog << "Assuming ownership of " << dir_name << " (uid: " << uid << " gid: "<< gid << ")" << endl);
129 if(chown(dir_name.c_str(),uid,gid)){
130 stringstream msg;
131 msg << prolog << "ERROR - Failed to assume ownership (uid: "<< uid;
132 msg << " gid: " << gid << ") of: " << dir_name;
133 msg << " errno: " << errno << " reason: " << strerror(errno);
134 throw BESInternalFatalError(msg.str(),__FILE__,__LINE__);
135 }
136 BESDEBUG(MODULE,prolog << "Changing permissions to mode: " << std::oct << mode << endl);
137 if(chmod(dir_name.c_str(),mode)){
138 stringstream msg;
139 msg << prolog << "ERROR - Failed to change permissions (mode: " << std::oct << mode;
140 msg << ") for: " << dir_name;
141 msg << " errno: " << errno << " reason: " << strerror(errno);
142 throw BESInternalFatalError(msg.str(),__FILE__,__LINE__);
143 }
144#endif
145 }
146 }
147 else {
148 BESDEBUG(MODULE, prolog << "The temp directory: " << dir_name << " was created." << endl);
149 return true;
150 }
151}
152
164string TempFile::create(const std::string &dir_name, const std::string &temp_file_prefix) {
165 // TODO The lock can be moved down to the section with the shared resource. jhrg 8/13/24
166 std::lock_guard<std::recursive_mutex> lock_me(d_tf_lock_mutex);
167
168 BESDEBUG(MODULE, prolog << "dir_name: " << dir_name << endl);
169
170 if (access(dir_name.c_str(), F_OK) == -1) {
171 if (errno == ENOENT) {
172 mk_temp_dir(dir_name);
173 }
174 else {
175 throw BESInternalFatalError(build_error_msg("Failed to access temp directory: " + dir_name), __FILE__,
176 __LINE__);
177 }
178 }
179
180 // Added process id (ala getpid()) to tempfile name to see if that affects
181 // our troubles with permission denied errors. This may need to be revisited if this
182 // is going to be utilized in a place where multiple threads could end up in this spot.
183 // - ndp 04/03/24
184 string target_file = BESUtil::pathConcat(dir_name, temp_file_prefix + "_" + to_string(getpid()) + "_XXXXXX");
185
186 BESDEBUG(MODULE, prolog << "target_file: " << target_file << endl);
187
188 // cover the case where older versions of mkstemp() create the file using
189 // a mode of 666.
190 mode_t original_mode = umask(077);
191 // The 'hack' &temp_file_name[0] is explicitly supported by the C++ 11 standard.
192 d_fd = mkstemp(&target_file[0]);
193 umask(original_mode);
194
195 if (d_fd == -1) {
196 throw BESInternalError(
197 build_error_msg("Failed to open the temporary file using mkstemp(), FileTemplate: " + target_file),
198 __FILE__, __LINE__);
199 }
200
201 d_fname.assign(target_file);
202
203 // TODO Lock only this section. jhrg 8/13/24
204 // Check to see if there are already active TempFile things,
205 // we can tell because if open_files->size() is zero then this
206 // is the first, and we need to register SIGPIPE handler.
207 if (open_files.empty()) {
208 struct sigaction act{};
209 sigemptyset(&act.sa_mask);
210 sigaddset(&act.sa_mask, SIGPIPE);
211 act.sa_flags = 0;
212
213 // FIXME Save the existing signal handler. jhrg 8/13/24
214 act.sa_handler = bes::TempFile::sigpipe_handler;
215
216 if (sigaction(SIGPIPE, &act, &cached_sigpipe_handler)) {
217 throw BESInternalFatalError(build_error_msg("Could not register a handler to catch SIGPIPE"), __FILE__,
218 __LINE__);
219 }
220 }
221
222 open_files.insert(std::pair<string, int>(d_fname, d_fd));
223
224 return d_fname;
225}
226
233 try {
234 if (d_fd != -1 && close(d_fd) == -1) {
235 ERROR_LOG(build_error_msg("Error closing temporary file: " + d_fname, __FILE__, __LINE__));
236 }
237 if (!d_fname.empty()) {
238 std::lock_guard<std::recursive_mutex> lock_me(d_tf_lock_mutex);
239 if (!d_keep_temps) {
240 if (unlink(d_fname.c_str()) == -1) {
241 ERROR_LOG(build_error_msg("Error unlinking temporary file: " + d_fname, __FILE__, __LINE__));
242 }
243 }
244
245 open_files.erase(d_fname);
246
247 if (open_files.empty()) {
248 // No more files means we can unload the SIGPIPE handler
249 // If more files are created at a later time then it will get reloaded.
250 // FIXME Restore saved signal handler. jhrg 8/13/24
251 if (sigaction(SIGPIPE, &cached_sigpipe_handler, nullptr)) {
252 ERROR_LOG(build_error_msg("Could not remove SIGPIPE handler" , __FILE__, __LINE__));
253 }
254 }
255 }
256 }
257 catch (BESError const &e) {
258 ERROR_LOG(build_error_msg("BESError while closing " + d_fname + ": " + e.get_verbose_message(),
259 __FILE__, __LINE__));
260 }
261 catch (std::exception const &e) {
262 ERROR_LOG(build_error_msg("C++ exception while closing " + d_fname + ": " + e.what(), __FILE__, __LINE__));
263 }
264}
265
266} // namespace bes
267
Base exception class for the BES with basic string message.
Definition BESError.h:66
const char * what() const noexcept override
Return a brief message about the exception.
Definition BESError.h:184
exception thrown if internal error encountered
exception thrown if an internal error is found and is fatal to the BES
static std::string pathConcat(const std::string &firstPart, const std::string &secondPart, char separator='/')
Concatenate path fragments making sure that they are separated by a single '/' character.
Definition BESUtil.cc:754
Get a new temporary file.
Definition TempFile.h:43
~TempFile()
Free the temporary file.
Definition TempFile.cc:232
static void sigpipe_handler(int signal)
Definition TempFile.cc:74
std::string create(const std::string &dir_name="/tmp/hyrax_tmp", const std::string &path_template="opendap")
Create a new temporary file.
Definition TempFile.cc:164