ATLAS Offline Software
Loading...
Searching...
No Matches
SubmitDirManager.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7
8
9//
10// includes
11//
12
14
16#include <EventLoop/Job.h>
22#include <TSystem.h>
23#include <format>
24#include <boost/functional/hash.hpp>
25#include <fcntl.h>
26#include <regex>
27#include <system_error>
28#include <sys/stat.h>
29#include <sys/time.h>
30#include <sys/types.h>
31#include <time.h>
32#include <unistd.h>
33
34
35using namespace EL::msgEventLoop;
36
37//
38// method implementations
39//
40
41namespace EL
42{
43 namespace Detail
44 {
45 namespace
46 {
47 void reportErrno (int myerrno)
48 {
49 ANA_MSG_ERROR ("encountered system error: "
50 << std::system_category().message (myerrno));
51 }
52
53 void reportErrno ()
54 {
55 reportErrno (errno);
56 }
57 }
58
59
60
61 std::pair<Detail::ManagerOrder,std::string> SubmitDirManager ::
62 getManagerOrder () const noexcept
63 {
64 return std::make_pair (ManagerOrder::SUBMIT_DIR, "");
65 }
66
67
68
69 ::StatusCode SubmitDirManager ::
70 doManagerStep (Detail::ManagerData& data) const
71 {
72 switch (data.step)
73 {
75 {
76 std::smatch match;
77
78 // make sure directory is absolute
79 if (data.submitDir[0] != '/')
80 data.submitDir = gSystem->WorkingDirectory () + ("/" + data.submitDir);
81
82 // make sure we don't end in "/", "/." or "/.."
83 const std::regex identityEndExpr {"(/$)|(/\\.$)"};
84 while (std::regex_search (data.submitDir, match, identityEndExpr))
85 data.submitDir.replace (match.position(0), match.length(0), "");
86 const std::regex relativeEndExpr {"/\\.\\.$"};
87 if (std::regex_search (data.submitDir, match, relativeEndExpr))
88 {
89 ANA_MSG_ERROR ("submit directory can not end in \"..\": " + data.submitDir);
90 return ::StatusCode::FAILURE;
91 }
92
93 // make sure we don't include any '..' in our path. this is
94 // mainly relevant for Ganga Tasks which may crash
95 // otherwise. this specifically doesn't use any of the
96 // "normal" regularization functions because those resolve
97 // symlinks as well, and depending on the driver we may not
98 // want that to happen.
99 const std::regex identityExpr {"(/\\./)|(//)|(^/\\.\\./)"};
100 while (std::regex_search (data.submitDir, match, identityExpr))
101 data.submitDir.replace (match.position(0), match.length(0), "/");
102 const std::regex relativeExpr {"/[^/]+/\\.\\./"};
103 while (std::regex_search (data.submitDir, match, relativeExpr))
104 data.submitDir.replace (match.position(0), match.length(0), "/");
105
106 if (data.submitDir.find ("/pnfs/") == 0)
107 {
108 ANA_MSG_ERROR ("can not place submit directory on pnfs: " + data.submitDir);
109 return ::StatusCode::FAILURE;
110 }
111
112 ANA_MSG_DEBUG ("changed submit-dir to " << data.submitDir);
113 }
114 break;
115
117 {
118 std::string mode = data.options.castString (Job::optSubmitDirMode);
119 if (!mode.empty())
120 {
121 if (mode == "no-clobber")
122 data.submitDirMode = SubmitDirMode::NO_CLOBBER;
123 else if (mode == "overwrite")
124 data.submitDirMode = SubmitDirMode::OVERWRITE;
125 else if (mode == "unique")
126 data.submitDirMode = SubmitDirMode::UNIQUE;
127 else if (mode == "unique-link")
128 data.submitDirMode = SubmitDirMode::UNIQUE_LINK;
129 else
130 {
131 ANA_MSG_ERROR ("unknown submit-dir mode: " << mode);
132 ANA_MSG_ERROR ("known modes: no-clobber, overwrite, unique, unique-link");
133 return ::StatusCode::FAILURE;
134 }
135 }
136
137 if (data.options.castBool (Job::optRemoveSubmitDir, false))
138 {
139 if (!mode.empty())
140 {
141 ANA_MSG_ERROR ("can't specify both an explicit submit-dir mode and optRemoveSubmitDir");
142 return ::StatusCode::FAILURE;
143 }
144 data.submitDirMode = SubmitDirMode::OVERWRITE;
145 }
146 }
147 break;
148
150 {
151 ANA_MSG_DEBUG ("using submit-dir mode: " << unsigned (data.submitDirMode));
152
153 bool success {false};
154 unsigned tries {0};
155 std::string submitDir;
156 std::size_t hash {0};
157 while (success == false && tries < 10)
158 {
159 tries += 1;
160
161 switch (data.submitDirMode)
162 {
165 submitDir = data.submitDir;
166 break;
167
170 {
171 timeval tv;
172 tm tvSplit;
173 if (gettimeofday (&tv, nullptr) == -1 ||
174 localtime_r (&tv.tv_sec, &tvSplit) == nullptr)
175 {
176 reportErrno ();
177 ANA_MSG_ERROR ("failed to get time of day???");
178 return ::StatusCode::FAILURE;
179 }
180 const std::string uniqueDateFormat {
181 data.options.castString (Job::optUniqueDateFormat,
182 "-%Y-%m-%d-%H%M-")};
183 char timeString [160];
184 if (strftime (timeString, sizeof (timeString),
185 uniqueDateFormat.c_str(), &tvSplit) == 0)
186 {
187 ANA_MSG_ERROR ("failed to format the unique date, please check optUniqueDateFormat: " << uniqueDateFormat);
188 return ::StatusCode::FAILURE;
189 }
190
191 // make a hash value and reduce it to 16 bits
192 boost::hash_combine (hash, std::hash<pid_t>() (getpid()));
193 boost::hash_combine (hash, std::hash<suseconds_t>() (tv.tv_usec));
194 std::size_t hash16 {hash};
195 while (hash16 > 0xffff)
196 hash16 = (hash16&0xffff) ^ (hash16 >> 16);
197
198 // we are appending both a date and a unique hash
199 // here, in most cases the date should be sufficient
200 // to make it unique, but for unit tests a lot of jobs
201 // may be submitted in rapid succcession, so having
202 // some random suffix should help avoid clashes. the
203 // date is purposely put first because in some cases
204 // that can provide a useful ordering of output
205 // directories, i.e. the latest results will be listed
206 // last.
207 submitDir = data.submitDir + timeString +
208 std::format("{:04x}", hash16);
209 ANA_MSG_DEBUG ("unique submit-dir: " << submitDir);
210 }
211 break;
212 }
213
214 if (::mkdir (submitDir.c_str(), 0777) == 0)
215 {
216 success = true;
217 } else
218 {
219 const int myerrno {errno};
220 if (myerrno == EEXIST)
221 {
222 switch (data.submitDirMode)
223 {
225 ANA_MSG_ERROR ("cowardly refusing to overwrite " << submitDir);
226 ANA_MSG_ERROR ("change the name or remove file/directory already there");
227 return ::StatusCode::FAILURE;
229 if (tries > 1)
230 {
231 ANA_MSG_ERROR ("failed to remove directory " << submitDir);
232 ANA_MSG_ERROR ("please try to remove it manually");
233 return ::StatusCode::FAILURE;
234 }
235 ANA_MSG_DEBUG ("removing directory " << submitDir);
236 if (gSystem->Exec (("rm -rf " + RCU::Shell::quote (submitDir)).c_str()) != 0)
237 ANA_MSG_WARNING ("failed to remove directory " << submitDir);
238 break;
241 // just pass through, try again with a new directory
242 // name next time
243 break;
244 }
245 } else
246 {
247 reportErrno (myerrno);
248 ANA_MSG_ERROR ("failed to create directory: " << submitDir);
249 return ::StatusCode::FAILURE;
250 }
251 }
252 }
253
254 if (success)
255 {
256 ANA_MSG_INFO ("created submission directory " + submitDir);
257 switch (data.submitDirMode)
258 {
262 // no-op
263 break;
264
266 {
267 if (unlink (data.submitDir.c_str()) == -1 && errno != ENOENT)
268 {
269 reportErrno ();
270 ANA_MSG_ERROR ("failed to remove: " << data.submitDir);
271 return ::StatusCode::FAILURE;
272 }
273
274 std::string file = submitDir.substr (submitDir.rfind ('/')+1);
275 if (symlink (file.c_str(), data.submitDir.c_str()) == -1)
276 {
277 reportErrno ();
278 ANA_MSG_ERROR ("failed to create symlink at: " << data.submitDir);
279 return ::StatusCode::FAILURE;
280 }
281 ANA_MSG_INFO ("created sym-link at: " << data.submitDir);
282 }
283 break;
284 }
285 data.submitDir = submitDir;
286 } else
287 {
288 ANA_MSG_ERROR ("tried " << tries << " times to create directory and failed: " << data.submitDir);
289 ANA_MSG_ERROR ("try removing existing directory manually");
290 return ::StatusCode::FAILURE;
291 }
292 }
293 break;
294
295 default:
296 // no-op
297 break;
298 }
299 return ::StatusCode::SUCCESS;
300 }
301 }
302}
#define ANA_MSG_ERROR(xmsg,...)
Macro printing error messages.
#define ANA_MSG_DEBUG(xmsg,...)
Macro printing debug messages.
#define ANA_MSG_INFO(xmsg,...)
Macro printing info messages.
#define ANA_MSG_WARNING(xmsg,...)
Macro printing warning messages.
static const std::string optSubmitDirMode
the submit-dir mode (allowed values: "no-clobber", "overwrite", "unique", "unique-link")
Definition Job.h:191
static const std::string optUniqueDateFormat
the date-format to use when generating unique submission directory names
Definition Job.h:196
static const std::string optRemoveSubmitDir
description: the name of the option for overwriting the submission directory.
Definition Job.h:185
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition hcg.cxx:359
@ OVERWRITE
create the directory as is, removing existing directories if needed
@ UNIQUE_LINK
append a unique suffix to the directory name and place a symbolic link in place of the originally req...
@ NO_CLOBBER
create the directory as is, raise an error if it already exists
@ UNIQUE
append a unique suffix to the directory name
@ updateSubmitDir
update the submitDir variable to be an absolute path
Definition ManagerStep.h:51
@ extractOptions
extract any options into ManagerData for which it is appropriate
Definition ManagerStep.h:60
@ createSubmitDir
create the submission directory
Definition ManagerStep.h:63
@ SUBMIT_DIR
the basic operations for the submission directory
This module defines the arguments passed from the BATCH driver to the BATCH worker.
::StatusCode StatusCode
StatusCode definition for legacy code.
std::string quote(const std::string &name)
effects: quote the given name to protect it from the shell returns: the quoted name guarantee: strong...
Definition ShellExec.cxx:65
an internal data structure for passing data between different manager objects anbd step
Definition ManagerData.h:46
TFile * file