dynamicCode.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2019 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "dynamicCode.H"
27 #include "dynamicCodeContext.H"
28 #include "stringOps.H"
29 #include "IFstream.H"
30 #include "OFstream.H"
31 #include "OSspecific.H"
32 #include "etcFiles.H"
33 #include "dictionary.H"
34 
35 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
36 
38 (
39  Foam::debug::infoSwitch("allowSystemOperations", 0)
40 );
41 
42 
43 const Foam::word Foam::dynamicCode::codeTemplateEnvName
44  = "FOAM_CODE_TEMPLATES";
45 
46 const Foam::fileName Foam::dynamicCode::codeTemplateDirName
47  = "codeTemplates/dynamicCode";
48 
49 const char* const Foam::dynamicCode::libTargetRoot =
50  "LIB = $(PWD)/../platforms/$(WM_OPTIONS)/lib/lib";
51 
52 const char* const Foam::dynamicCode::topDirName = "dynamicCode";
53 
54 
55 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
56 
58 (
59  const char* title,
60  const dictionary& dict
61 )
62 {
63  if (isAdministrator())
64  {
66  << "This code should not be executed by someone with administrator"
67  << " rights due to security reasons." << nl
68  << "(it writes a shared library which then gets loaded "
69  << "using dlopen)"
70  << exit(FatalIOError);
71  }
72 
73  if (!allowSystemOperations)
74  {
76  << "Loading a shared library using case-supplied code is not"
77  << " enabled by default" << nl
78  << "because of security issues. If you trust the code you can"
79  << " enable this" << nl
80  << "facility be adding to the InfoSwitches setting in the system"
81  << " controlDict:" << nl << nl
82  << " allowSystemOperations 1" << nl << nl
83  << "The system controlDict is either" << nl << nl
84  << " ~/.OpenFOAM/$WM_PROJECT_VERSION/controlDict" << nl << nl
85  << "or" << nl << nl
86  << " $WM_PROJECT_DIR/etc/controlDict" << nl
87  << endl
88  << exit(FatalIOError);
89  }
90 }
91 
92 
94 {
95  word libName(libPath.name(true));
96  libName.erase(0, 3); // Remove leading 'lib' from name
97  return libName;
98 }
99 
100 
101 
102 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
103 
105 (
106  ISstream& is,
107  OSstream& os,
108  const HashTable<string>& mapping
109 )
110 {
111  if (!is.good())
112  {
114  << "Failed opening for reading " << is.name()
115  << exit(FatalError);
116  }
117 
118  if (!os.good())
119  {
121  << "Failed writing " << os.name()
122  << exit(FatalError);
123  }
124 
125  // Copy file while rewriting $VARS and ${VARS}
126  string line;
127  do
128  {
129  is.getLine(line);
130 
131  // Expand according to mapping.
132  // Expanding according to env variables might cause too many
133  // surprises
134  stringOps::inplaceExpand(line, mapping);
135  os.writeQuoted(line, false) << nl;
136  }
137  while (is.good());
138 }
139 
140 
142 (
143  const UList<fileName>& templateNames,
144  DynamicList<fileName>& resolvedFiles,
145  DynamicList<fileName>& badFiles
146 )
147 {
148  // Try to get template from FOAM_CODESTREAM_TEMPLATES
149  const fileName templateDir(Foam::getEnv(codeTemplateEnvName));
150 
151  bool allOkay = true;
152  forAll(templateNames, fileI)
153  {
154  const fileName& templateName = templateNames[fileI];
155 
156  fileName file;
157  if (!templateDir.empty() && isDir(templateDir))
158  {
159  file = templateDir/templateName;
160  if (!isFile(file, false, false))
161  {
162  file.clear();
163  }
164  }
165 
166  // Not found - fallback to ~OpenFOAM expansion
167  if (file.empty())
168  {
169  file = findEtcFile(codeTemplateDirName/templateName);
170  }
171 
172  if (file.empty())
173  {
174  badFiles.append(templateName);
175  allOkay = false;
176  }
177  else
178  {
179  resolvedFiles.append(file);
180  }
181  }
182 
183  return allOkay;
184 }
185 
186 
188 {
189  const bool hasSHA1 = filterVars_.found("SHA1sum");
190 
191  if (hasSHA1)
192  {
193  os << "/* dynamicCode:\n * SHA1 = ";
194  os.writeQuoted(filterVars_["SHA1sum"], false) << "\n */\n";
195  }
196 
197  return hasSHA1;
198 }
199 
200 
202 {
203  // Create Make/files
204  if (compileFiles_.empty())
205  {
206  return false;
207  }
208 
209  const fileName dstFile(this->codePath()/"Make/files");
210 
211  // Create dir
212  mkDir(dstFile.path());
213 
214  OFstream os(dstFile);
215  // Info<< "Writing to " << dstFile << endl;
216  if (!os.good())
217  {
219  << "Failed writing " << dstFile
220  << exit(FatalError);
221  }
222 
223  writeCommentSHA1(os);
224 
225  // Write compile files
226  forAll(compileFiles_, fileI)
227  {
228  os.writeQuoted(compileFiles_[fileI], false) << nl;
229  }
230 
231  os << nl
232  << libTargetRoot << codeName_.c_str() << nl;
233 
234  return true;
235 }
236 
237 
239 {
240  // Create Make/options
241  if (compileFiles_.empty() || makeOptions_.empty())
242  {
243  return false;
244  }
245 
246  const fileName dstFile(this->codePath()/"Make/options");
247 
248  // Create dir
249  mkDir(dstFile.path());
250 
251  OFstream os(dstFile);
252  // Info<< "Writing to " << dstFile << endl;
253  if (!os.good())
254  {
256  << "Failed writing " << dstFile
257  << exit(FatalError);
258  }
259 
260  writeCommentSHA1(os);
261  os.writeQuoted(makeOptions_, false) << nl;
262 
263  return true;
264 }
265 
266 
268 {
269  const fileName file = digestFile();
270  mkDir(file.path());
271 
272  OFstream os(file);
273  sha1.write(os, true) << nl;
274 
275  return os.good();
276 }
277 
278 
279 bool Foam::dynamicCode::writeDigest(const std::string& sha1) const
280 {
281  const fileName file = digestFile();
282  mkDir(file.path());
283 
284  OFstream os(file);
285  os << '_';
286  os.writeQuoted(sha1, false) << nl;
287 
288  return os.good();
289 }
290 
291 
292 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
293 
295 :
296  codeRoot_(stringOps::expand("$FOAM_CASE")/topDirName),
297  libSubDir_(stringOps::expand("platforms/$WM_OPTIONS/lib")),
298  codeName_(codeName),
299  codeDirName_(codeDirName)
300 {
301  if (codeDirName_.empty())
302  {
303  codeDirName_ = codeName_;
304  }
305 
306  clear();
307 }
308 
309 
310 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
311 
313 {
314  return topDirName/codeDirName_;
315 }
316 
317 
319 {
320  return codeRelPath()/libSubDir_/"lib" + codeName_ + ".so";
321 }
322 
323 
325 {
326  compileFiles_.clear();
327  copyFiles_.clear();
328  createFiles_.clear();
329  filterVars_.clear();
330  filterVars_.set("typeName", codeName_);
331  filterVars_.set("SHA1sum", SHA1Digest().str());
332 
333  // Provide default Make/options
334  makeOptions_ =
335  "EXE_INC = -g\n"
336  "\n\nLIB_LIBS = ";
337 }
338 
339 
341 (
342  const dynamicCodeContext& context
343 )
344 {
345  clear();
346 
347  forAllConstIter(HashTable<string>, context.code(), iter)
348  {
349  setFilterVariable(iter.key(), iter());
350  }
351 
352  setFilterVariable("SHA1sum", context.sha1().str());
353 }
354 
355 
357 {
358  compileFiles_.append(name);
359 }
360 
361 
363 {
364  copyFiles_.append(name);
365 }
366 
367 
369 (
370  const fileName& name,
371  const string& contents
372 )
373 {
374  createFiles_.append(fileAndContent(name, contents));
375 }
376 
377 
379 (
380  const word& key,
381  const std::string& value
382 )
383 {
384  filterVars_.set(key, value);
385 }
386 
387 
388 void Foam::dynamicCode::setMakeOptions(const std::string& content)
389 {
390  makeOptions_ = content;
391 }
392 
393 
394 bool Foam::dynamicCode::copyOrCreateFiles(const bool verbose) const
395 {
396  if (verbose)
397  {
398  Info<< "Creating new library in " << this->libRelPath() << endl;
399  }
400 
401  const label nFiles = compileFiles_.size() + copyFiles_.size();
402 
403  DynamicList<fileName> resolvedFiles(nFiles);
404  DynamicList<fileName> badFiles(nFiles);
405 
406  // Resolve template, or add to bad-files
407  resolveTemplates(compileFiles_, resolvedFiles, badFiles);
408  resolveTemplates(copyFiles_, resolvedFiles, badFiles);
409 
410  if (!badFiles.empty())
411  {
413  << "Could not find the code template(s): "
414  << badFiles << nl
415  << "Under the $" << codeTemplateEnvName
416  << " directory or via via the ~OpenFOAM/"
417  << codeTemplateDirName << " expansion"
418  << exit(FatalError);
419  }
420 
421 
422 
423  // Create dir
424  const fileName outputDir = this->codePath();
425 
426  // Create dir
427  mkDir(outputDir);
428 
429  // Copy/filter files
430  forAll(resolvedFiles, fileI)
431  {
432  const fileName& srcFile = resolvedFiles[fileI];
433  const fileName dstFile(outputDir/srcFile.name());
434 
435  IFstream is(srcFile);
436  // Info<< "Reading from " << is.name() << endl;
437  if (!is.good())
438  {
440  << "Failed opening " << srcFile
441  << exit(FatalError);
442  }
443 
444  OFstream os(dstFile);
445  // Info<< "Writing to " << dstFile.name() << endl;
446  if (!os.good())
447  {
449  << "Failed writing " << dstFile
450  << exit(FatalError);
451  }
452 
453  // Copy lines while expanding variables
454  copyAndFilter(is, os, filterVars_);
455  }
456 
457 
458  // Create files:
459  forAll(createFiles_, fileI)
460  {
461  const fileName dstFile
462  (
463  outputDir/stringOps::expand(createFiles_[fileI].first())
464  );
465 
466  mkDir(dstFile.path());
467  OFstream os(dstFile);
468  // Info<< "Writing to " << createFiles_[fileI].first() << endl;
469  if (!os.good())
470  {
472  << "Failed writing " << dstFile
473  << exit(FatalError);
474  }
475  os.writeQuoted(createFiles_[fileI].second(), false) << nl;
476  }
477 
478 
479  // Create Make/files + Make/options
480  createMakeFiles();
482 
483  writeDigest(filterVars_["SHA1sum"]);
484 
485  return true;
486 }
487 
488 
490 {
491  const Foam::string wmakeCmd("wmake -s libso " + this->codePath());
492  Info<< "Invoking " << wmakeCmd << endl;
493 
494  if (Foam::system(wmakeCmd))
495  {
496  return false;
497  }
498  else
499  {
500  return true;
501  }
502 }
503 
504 
506 {
507  const fileName file = digestFile();
508 
509  if (!exists(file, false, false) || SHA1Digest(IFstream(file)()) != sha1)
510  {
511  return false;
512  }
513 
514  return true;
515 }
516 
517 
519 {
520  return upToDate(context.sha1());
521 }
522 
523 
524 // ************************************************************************* //
string getEnv(const word &)
Return environment variable of given name.
Definition: POSIX.C:97
virtual const fileName & name() const
Return the name of the stream.
Definition: OSstream.H:82
Generic output stream.
Definition: OSstream.H:51
bool exists(const fileName &, const bool checkVariants=true, const bool followLink=true)
Does the name exist (as directory or file) in the file system?
Definition: POSIX.C:520
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
bool empty() const
Return true if the UList is empty (ie, size() is zero)
Definition: UListI.H:313
string expand(const string &, const HashTable< string, word, string::hash > &mapping, const char sigil='$')
Expand occurrences of variables according to the mapping.
Definition: stringOps.C:69
bool writeDigest(const SHA1Digest &) const
Write digest to Make/SHA1Digest.
Definition: dynamicCode.C:267
const word & codeDirName() const
Return the code-dirname.
Definition: dynamicCode.H:191
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
A line primitive.
Definition: line.H:56
A class for handling file names.
Definition: fileName.H:79
const SHA1Digest & sha1() const
Return SHA1 digest calculated from include, options, code.
void reset(const dynamicCodeContext &)
Clear files and reset variables to specified context.
Definition: dynamicCode.C:341
virtual const fileName & name() const
Return the name of the stream.
Definition: ISstream.H:104
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
error FatalError
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:158
void setMakeOptions(const std::string &content)
Define contents for Make/options.
Definition: dynamicCode.C:388
const word & codeName() const
Return the code-name.
Definition: dynamicCode.H:185
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
bool isFile(const fileName &, const bool checkVariants=true, const bool followLink=true)
Does the name exist as a file in the file system?
Definition: POSIX.C:555
Output to file stream.
Definition: OFstream.H:82
static const char *const libTargetRoot
Root of the LIB target for Make/files.
Definition: dynamicCode.H:101
static bool resolveTemplates(const UList< fileName > &templateNames, DynamicList< fileName > &resolvedFiles, DynamicList< fileName > &badFiles)
Resolve code-templates via the codeTemplateEnvName.
Definition: dynamicCode.C:142
virtual Ostream & writeQuoted(const std::string &, const bool quoted=true)
Write std::string with optional double quotes.
Definition: OSstream.C:78
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
The SHA1 message digest.
Definition: SHA1Digest.H:62
static const word codeTemplateEnvName
Name of the code template environment variable.
Definition: dynamicCode.H:149
static const char *const topDirName
Top-level directory name for copy/compiling.
Definition: dynamicCode.H:104
void addCompileFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:356
bool writeCommentSHA1(Ostream &) const
Write SHA1 value as C-comment.
Definition: dynamicCode.C:187
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:333
int infoSwitch(const char *name, const int defaultValue=0)
Lookup info switch or add default value.
Definition: debug.C:187
virtual Ostream & writeQuoted(const std::string &, const bool quoted=true)=0
Write std::string surrounded by quotes.
bool createMakeOptions() const
Copy/create Make/options prior to compilation.
Definition: dynamicCode.C:238
fileName codeRelPath() const
Path for specified code name relative to $FOAM_CASE.
Definition: dynamicCode.C:312
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
Ostream & write(Ostream &, const bool prefixed=false) const
Write (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1Digest.C:137
fileName digestFile() const
Path for SHA1Digest.
Definition: dynamicCode.H:237
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:56
fileName libRelPath() const
Library path for specified code name relative to $FOAM_CASE.
Definition: dynamicCode.C:318
bool isDir(const fileName &, const bool followLink=true)
Does the name exist as a directory in the file system?
Definition: POSIX.C:539
A class for handling words, derived from string.
Definition: word.H:59
Functions to search &#39;etc&#39; directories for configuration files etc.
static const fileName codeTemplateDirName
Name of the code template sub-directory.
Definition: dynamicCode.H:153
static int allowSystemOperations
Flag if system operations are allowed.
Definition: dynamicCode.H:156
word name() const
Return file name (part beyond last /)
Definition: fileName.C:183
static void copyAndFilter(ISstream &, OSstream &, const HashTable< string > &mapping)
Copy lines while expanding variables.
Definition: dynamicCode.C:105
dynamicCode(const word &codeName, const word &codeDirName="")
Construct for a specified code name and code directory name.
Definition: dynamicCode.C:294
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
void addCreateFile(const fileName &name, const string &contents)
Add a file to create with its contents. Will not be filtered.
Definition: dynamicCode.C:369
bool wmakeLibso() const
Compile a libso.
Definition: dynamicCode.C:489
An STL-conforming hash table.
Definition: HashTable.H:61
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
fileName findEtcFile(const fileName &, bool mandatory=false)
Search for a file using findEtcFiles.
Definition: etcFiles.C:252
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: HashTable.H:60
fileName codePath() const
Path for specified code name.
Definition: dynamicCode.H:212
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:54
static const char nl
Definition: Ostream.H:260
bool upToDate(const dynamicCodeContext &context) const
Verify if the copied code is up-to-date, based on Make/SHA1Digest.
Definition: dynamicCode.C:518
bool copyOrCreateFiles(const bool verbose=false) const
Copy/create files prior to compilation.
Definition: dynamicCode.C:394
Input from file stream.
Definition: IFstream.H:81
static word libraryBaseName(const fileName &libPath)
Return the library basename without leading &#39;lib&#39; or trailing &#39;.so&#39;.
Definition: dynamicCode.C:93
bool mkDir(const fileName &, mode_t=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:290
const HashTable< string > & code() const
Return the code table.
bool isAdministrator()
Is user administrator.
Definition: POSIX.C:180
Encapsulation of dynamic code dictionaries.
Generic input stream.
Definition: ISstream.H:51
void setFilterVariable(const word &key, const std::string &value)
Define a filter variable.
Definition: dynamicCode.C:379
string & inplaceExpand(string &, const HashTable< string, word, string::hash > &mapping, const char sigil='$')
Inplace expand occurrences of variables according to the mapping.
Definition: stringOps.C:81
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:331
void addCopyFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:362
messageStream Info
fileName path() const
Return directory path name (part before last /)
Definition: fileName.C:253
std::string str(const bool prefixed=false) const
Return (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1Digest.C:112
void clear()
Clear files and variables.
Definition: dynamicCode.C:324
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:58
A class for handling character strings derived from std::string.
Definition: string.H:76
bool createMakeFiles() const
Copy/create Make/files prior to compilation.
Definition: dynamicCode.C:201
int system(const std::string &command)
Execute the specified command.
Definition: POSIX.C:1234
ISstream & getLine(string &)
Read line into a string.
Definition: ISstream.C:772
IOerror FatalIOError
Tuple2< fileName, string > fileAndContent
Definition: dynamicCode.H:61