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 
48 using namespace std;
49 
50 #define MODULE "dap"
51 #define prolog string("TempFile::").append(__func__).append("() - ")
52 
53 #define STRICT 0
54 
55 namespace bes {
56 
57 std::once_flag TempFile::d_init_once;
58 std::unique_ptr< std::map<std::string, int> > TempFile::open_files;
59 struct sigaction TempFile::cached_sigpipe_handler;
60 
61 
67 void TempFile::sigpipe_handler(int sig)
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 
100 void 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 
146 void TempFile::init() {
147  open_files.reset(new std::map<string, int>());
148 }
149 
154 TempFile::TempFile(bool keep_temps): d_keep_temps(keep_temps) {
155  std::call_once(d_init_once,TempFile::init);
156 }
157 
158 
170 string 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
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