bes Updated for version 3.20.13
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/stat.h>
28#include <unistd.h>
29#include <signal.h>
30#include <sys/wait.h> // for wait
31#include <sstream> // std::stringstream
32
33#include <cstdlib>
34#include <cstring>
35#include <cerrno>
36#include <vector>
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::once_flag TempFile::d_init_once;
58std::unique_ptr< std::map<std::string, int> > TempFile::open_files;
59struct sigaction TempFile::cached_sigpipe_handler;
60
61
68{
69 try {
70 if (sig == SIGPIPE) {
71 for (const auto &mpair: *open_files) {
72 if (unlink((mpair.first).c_str()) == -1) {
73 stringstream msg;
74 msg << "Error unlinking temporary file: '" << mpair.first << "'";
75 msg << " errno: " << errno << " message: " << strerror(errno) << endl;
76 ERROR_LOG(msg.str());
77 }
78 }
79 // Files cleaned up? Sweet! Time to bail...
80 // Remove this SIGPIPE handler
81 sigaction(SIGPIPE, &cached_sigpipe_handler, nullptr);
82 // Re-raise SIGPIPE
83 raise(SIGPIPE);
84 }
85 }
86 catch (BESError &e) {
87 cerr << "Encountered BESError. Message: " << e.get_verbose_message();
88 cerr << " (location: " << __FILE__ << " at line: " << __LINE__ << ")" << endl;
89 }
90 catch (...) {
91 cerr << "Encountered unknown error in " << __FILE__ << " at line: " << __LINE__ << endl;
92 }
93}
94
95
100void TempFile::mk_temp_dir(const std::string &dir_name) {
101
102 mode_t mode = S_IRWXU | S_IRWXG;
103 stringstream ss;
104 ss << prolog << "mode: " << std::oct << mode << endl;
105 BESDEBUG(MODULE, ss.str());
106
107 if(mkdir(dir_name.c_str(), mode)){
108 if(errno != EEXIST){
109 stringstream msg;
110 msg << prolog << "ERROR - Failed to create temp directory: " << dir_name;
111 msg << " errno: " << errno << " reason: " << strerror(errno);
112 throw BESInternalFatalError(msg.str(),__FILE__,__LINE__);
113 }
114 else {
115 BESDEBUG(MODULE,prolog << "The temp directory: " << dir_name << " exists." << endl);
116#if STRICT
117 uid_t uid = getuid();
118 gid_t gid = getgid();
119 BESDEBUG(MODULE,prolog << "Assuming ownership of " << dir_name << " (uid: " << uid << " gid: "<< gid << ")" << endl);
120 if(chown(dir_name.c_str(),uid,gid)){
121 stringstream msg;
122 msg << prolog << "ERROR - Failed to assume ownership (uid: "<< uid;
123 msg << " gid: " << gid << ") of: " << dir_name;
124 msg << " errno: " << errno << " reason: " << strerror(errno);
125 throw BESInternalFatalError(msg.str(),__FILE__,__LINE__);
126 }
127 BESDEBUG(MODULE,prolog << "Changing permissions to mode: " << std::oct << mode << endl);
128 if(chmod(dir_name.c_str(),mode)){
129 stringstream msg;
130 msg << prolog << "ERROR - Failed to change permissions (mode: " << std::oct << mode;
131 msg << ") for: " << dir_name;
132 msg << " errno: " << errno << " reason: " << strerror(errno);
133 throw BESInternalFatalError(msg.str(),__FILE__,__LINE__);
134 }
135#endif
136 }
137 }
138 else {
139 BESDEBUG(MODULE,prolog << "The temp directory: " << dir_name << " was created." << endl);
140 }
141}
142
146void TempFile::init() {
147 open_files.reset(new std::map<string, int>());
148}
149
154TempFile::TempFile(bool keep_temps): d_keep_temps(keep_temps) {
155 std::call_once(d_init_once,TempFile::init);
156}
157
158
170string TempFile::create(const std::string &dir_name, const std::string &temp_file_prefix)
171{
172 std::lock_guard<std::recursive_mutex> lock_me(d_tf_lock_mutex);
173
174 BESDEBUG(MODULE, prolog << "dir_name: " << dir_name << endl);
175 mk_temp_dir(dir_name);
176
177 BESDEBUG(MODULE, prolog << "temp_file_prefix: " << temp_file_prefix << endl);
178 string tmplt("XXXXXX");
179 if(!BESUtil::endsWith(temp_file_prefix,"_")){
180 tmplt = "_" + tmplt;
181 }
182 string file_template = temp_file_prefix + tmplt;
183 BESDEBUG(MODULE, prolog << "file_template: " << file_template << endl);
184
185 string target_file = BESUtil::pathConcat(dir_name,file_template);
186 BESDEBUG(MODULE, prolog << "target_file: " << target_file << endl);
187
188 char tmp_name[target_file.length() + 1];
189 std::string::size_type len = target_file.copy(tmp_name, target_file.length());
190 tmp_name[len] = '\0';
191
192 // cover the case where older versions of mkstemp() create the file using
193 // a mode of 666.
194 mode_t original_mode = umask(077);
195 d_fd = mkstemp(tmp_name);
196 umask(original_mode);
197
198 if (d_fd == -1) {
199 stringstream msg;
200 msg << "Failed to open the temporary file using mkstemp()";
201 msg << " errno: " << errno << " message: " << strerror(errno);
202 msg << " FileTemplate: " + target_file;
203 throw BESInternalError(msg.str(), __FILE__, __LINE__);
204 }
205 d_fname.assign(tmp_name);
206
207 // Check to see if there are already active TempFile things,
208 // we can tell because if open_files->size() is zero then this
209 // is the first, and we need to register SIGPIPE handler.
210 if (open_files->empty()) {
211 struct sigaction act;
212 sigemptyset(&act.sa_mask);
213 sigaddset(&act.sa_mask, SIGPIPE);
214 act.sa_flags = 0;
215
216 act.sa_handler = bes::TempFile::sigpipe_handler;
217
218 if (sigaction(SIGPIPE, &act, &cached_sigpipe_handler)) {
219 stringstream msg;
220 msg << "Could not register a handler to catch SIGPIPE.";
221 msg << " errno: " << errno << " message: " << strerror(errno);
222 throw BESInternalFatalError(msg.str(), __FILE__, __LINE__);
223 }
224 }
225 open_files->insert(std::pair<string, int>(d_fname, d_fd));
226
227 return d_fname;
228}
229
236{
237 try {
238 if(d_fd != -1 && close(d_fd) == -1) {
239 stringstream msg;
240 msg << "Error closing temporary file: '" << d_fname ;
241 msg << " errno: " << errno << " message: " << strerror(errno) << endl;
242 ERROR_LOG(msg.str());
243 }
244 if(!d_fname.empty()) {
245 std::lock_guard<std::recursive_mutex> lock_me(d_tf_lock_mutex);
246 if (!d_keep_temps) {
247 if (unlink(d_fname.c_str()) == -1) {
248 stringstream msg;
249 msg << "Error unlinking temporary file: '" << d_fname ;
250 msg << " errno: " << errno << " message: " << strerror(errno) << endl;
251 ERROR_LOG(msg.str());
252 }
253 }
254 open_files->erase(d_fname);
255 if (open_files->empty()) {
256 // No more files means we can unload the SIGPIPE handler
257 // If more files are created at a later time then it will get reloaded.
258 if (sigaction(SIGPIPE, &cached_sigpipe_handler, nullptr)) {
259 stringstream msg;
260 msg << "Could not de-register the SIGPIPE handler function cached_sigpipe_handler(). ";
261 msg << " errno: " << errno << " message: " << strerror(errno);
262 ERROR_LOG(msg.str());
263 }
264 }
265 }
266 }
267 catch (BESError const &e) {
268 cerr << "Encountered BESError will closing " << d_fname << " Message: " << e.get_verbose_message();
269 cerr << " (location: " << __FILE__ << " at line: " << __LINE__ << ")" << endl;
270 }
271 catch (...) {
272 cerr << "Encountered unknown error while closing " << d_fname;
273 cerr << " " << __FILE__ << " at line: " << __LINE__ << endl;
274 }
275}
276
277} // namespace bes
278
Base exception class for the BES with basic string message.
Definition: BESError.h:59
exception thrown if internal error encountered
exception thrown if an internal error is found and is fatal to the BES
static bool endsWith(std::string const &fullString, std::string const &ending)
Definition: BESUtil.cc:834
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:751
~TempFile()
Free the temporary file.
Definition: TempFile.cc:235
TempFile(bool keep_temps=false)
Definition: TempFile.cc:154
static void sigpipe_handler(int signal)
Definition: TempFile.cc:67
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:170