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-2021 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 
44  = "FOAM_CODE_TEMPLATES";
45 
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 
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  // Read the next line without continuation
130  is.getLine(line, false);
131 
132  // Expand according to mapping.
133  // Expanding according to env variables might cause too many
134  // surprises
135  stringOps::inplaceExpand(line, mapping);
136  os.writeQuoted(line, false) << nl;
137  }
138  while (is.good());
139 }
140 
141 
143 (
144  const fileName& templateName
145 )
146 {
147  // Try to get template from FOAM_CODESTREAM_TEMPLATES
148  const fileName templateDir(Foam::getEnv(codeTemplateEnvName));
149 
150  fileName file;
151  if (!templateDir.empty() && isDir(templateDir))
152  {
153  file = templateDir/templateName;
154  if (!isFile(file, false, false))
155  {
156  file.clear();
157  }
158  }
159 
160  // Not found - fallback to ~OpenFOAM expansion
161  if (file.empty())
162  {
163  file = findEtcFile(codeTemplateDirName/templateName);
164  }
165 
166  return file;
167 }
168 
169 
171 (
172  const UList<fileName>& templateNames,
173  DynamicList<fileName>& resolvedFiles,
174  DynamicList<fileName>& badFiles
175 )
176 {
177  // Try to get template from FOAM_CODESTREAM_TEMPLATES
178  const fileName templateDir(Foam::getEnv(codeTemplateEnvName));
179 
180  bool allOkay = true;
181  forAll(templateNames, fileI)
182  {
183  const fileName& templateName = templateNames[fileI];
184 
185  fileName file;
186  if (!templateDir.empty() && isDir(templateDir))
187  {
188  file = templateDir/templateName;
189  if (!isFile(file, false, false))
190  {
191  file.clear();
192  }
193  }
194 
195  // Not found - fallback to ~OpenFOAM expansion
196  if (file.empty())
197  {
198  file = findEtcFile(codeTemplateDirName/templateName);
199  }
200 
201  if (file.empty())
202  {
203  badFiles.append(templateName);
204  allOkay = false;
205  }
206  else
207  {
208  resolvedFiles.append(file);
209  }
210  }
211 
212  return allOkay;
213 }
214 
215 
217 {
218  const bool hasSHA1 = filterVars_.found("SHA1sum");
219 
220  if (hasSHA1)
221  {
222  os << "# dynamicCode:\n# SHA1 = ";
223  os.writeQuoted(filterVars_["SHA1sum"], false) << "\n\n";
224  }
225 
226  return hasSHA1;
227 }
228 
229 
231 {
232  // Create Make/files
233  if (compileFiles_.empty())
234  {
235  return false;
236  }
237 
238  const fileName dstFile(this->codePath()/"Make/files");
239 
240  // Create dir
241  mkDir(dstFile.path());
242 
243  OFstream os(dstFile);
244  // Info<< "Writing to " << dstFile << endl;
245  if (!os.good())
246  {
248  << "Failed writing " << dstFile
249  << exit(FatalError);
250  }
251 
252  writeCommentSHA1(os);
253 
254  // Write compile files
255  forAll(compileFiles_, fileI)
256  {
257  os.writeQuoted(compileFiles_[fileI].name(), false) << nl;
258  }
259 
260  os << nl
261  << libTargetRoot << codeName_.c_str() << nl;
262 
263  return true;
264 }
265 
266 
268 {
269  // Create Make/options
270  if (compileFiles_.empty() || makeOptions_.empty())
271  {
272  return false;
273  }
274 
275  const fileName dstFile(this->codePath()/"Make/options");
276 
277  // Create dir
278  mkDir(dstFile.path());
279 
280  OFstream os(dstFile);
281  // Info<< "Writing to " << dstFile << endl;
282  if (!os.good())
283  {
285  << "Failed writing " << dstFile
286  << exit(FatalError);
287  }
288 
289  writeCommentSHA1(os);
290  os.writeQuoted(makeOptions_, false) << nl;
291 
292  return true;
293 }
294 
295 
297 {
298  const fileName file = digestFile();
299  mkDir(file.path());
300 
301  OFstream os(file);
302  sha1.write(os, true) << nl;
303 
304  return os.good();
305 }
306 
307 
308 bool Foam::dynamicCode::writeDigest(const std::string& sha1) const
309 {
310  const fileName file = digestFile();
311  mkDir(file.path());
312 
313  OFstream os(file);
314  os << '_';
315  os.writeQuoted(sha1, false) << nl;
316 
317  return os.good();
318 }
319 
320 
321 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
322 
323 Foam::dynamicCode::dynamicCode(const word& codeName, const word& codeDirName)
324 :
325  codeRoot_(stringOps::expand("$FOAM_CASE")/topDirName),
326  libSubDir_(stringOps::expand("platforms/$WM_OPTIONS/lib")),
327  codeName_(codeName),
328  codeDirName_(codeDirName)
329 {
330  if (codeDirName_.empty())
331  {
332  codeDirName_ = codeName_;
333  }
334 
335  clear();
336 }
337 
338 
339 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
340 
342 {
343  return topDirName/codeDirName_;
344 }
345 
346 
348 {
349  return codeRelPath()/libSubDir_/"lib" + codeName_ + ".so";
350 }
351 
352 
354 {
355  compileFiles_.clear();
356  copyFiles_.clear();
357  createFiles_.clear();
358  filterVars_.clear();
359  filterVars_.set("typeName", codeName_);
360  filterVars_.set("SHA1sum", SHA1Digest().str());
361 
362  // Provide default Make/options
363  makeOptions_ =
364  "EXE_INC = -g\n"
365  "\n\nLIB_LIBS = ";
366 }
367 
368 
370 (
371  const dynamicCodeContext& context
372 )
373 {
374  clear();
375 
376  forAllConstIter(HashTable<string>, context.code(), iter)
377  {
378  setFilterVariable(iter.key(), iter());
379  }
380 
381  setFilterVariable("SHA1sum", context.sha1().str());
382 }
383 
384 
386 {
387  compileFiles_.append(name);
388 }
389 
390 
392 {
393  copyFiles_.append(name);
394 }
395 
396 
398 (
399  const fileName& name,
400  const string& contents
401 )
402 {
403  createFiles_.append(fileAndContent(name, contents));
404 }
405 
406 
408 (
409  const word& key,
410  const std::string& value
411 )
412 {
413  filterVars_.set(key, value);
414 }
415 
416 
417 void Foam::dynamicCode::setMakeOptions(const std::string& content)
418 {
419  makeOptions_ = content;
420 }
421 
422 
423 bool Foam::dynamicCode::copyOrCreateFiles(const bool verbose) const
424 {
425  if (verbose)
426  {
427  Info<< "Creating new library in " << this->libRelPath() << endl;
428  }
429 
430  const label nFiles = compileFiles_.size() + copyFiles_.size();
431 
432  DynamicList<fileName> resolvedFiles(nFiles);
433  DynamicList<fileName> badFiles(nFiles);
434 
435  // Resolve template, or add to bad-files
436  resolveTemplates(compileFiles_, resolvedFiles, badFiles);
437  resolveTemplates(copyFiles_, resolvedFiles, badFiles);
438 
439  if (!badFiles.empty())
440  {
442  << "Could not find the code template(s): "
443  << badFiles << nl
444  << "Under the $" << codeTemplateEnvName
445  << " directory or via via the ~OpenFOAM/"
446  << codeTemplateDirName << " expansion"
447  << exit(FatalError);
448  }
449 
450 
451 
452  // Create dir
453  const fileName outputDir = this->codePath();
454 
455  // Create dir
456  mkDir(outputDir);
457 
458  // Copy/filter files
459  forAll(resolvedFiles, fileI)
460  {
461  const fileName& srcFile = resolvedFiles[fileI];
462  const fileName dstFile(outputDir/srcFile.name());
463 
464  Info << srcFile << " " << dstFile << endl;
465 
466  IFstream is(srcFile);
467  // Info<< "Reading from " << is.name() << endl;
468  if (!is.good())
469  {
471  << "Failed opening " << srcFile
472  << exit(FatalError);
473  }
474 
475  OFstream os(dstFile);
476  // Info<< "Writing to " << dstFile.name() << endl;
477  if (!os.good())
478  {
480  << "Failed writing " << dstFile
481  << exit(FatalError);
482  }
483 
484  // Copy lines while expanding variables
485  copyAndFilter(is, os, filterVars_);
486  }
487 
488 
489  // Create files:
490  forAll(createFiles_, fileI)
491  {
492  const fileName dstFile
493  (
494  outputDir/stringOps::expand(createFiles_[fileI].first())
495  );
496 
497  mkDir(dstFile.path());
498  OFstream os(dstFile);
499  // Info<< "Writing to " << createFiles_[fileI].first() << endl;
500  if (!os.good())
501  {
503  << "Failed writing " << dstFile
504  << exit(FatalError);
505  }
506  os.writeQuoted(createFiles_[fileI].second(), false) << nl;
507  }
508 
509 
510  // Create Make/files + Make/options
511  createMakeFiles();
512  createMakeOptions();
513 
514  writeDigest(filterVars_["SHA1sum"]);
515 
516  return true;
517 }
518 
519 
521 {
522  const Foam::string wmakeCmd("wmake -s libso " + this->codePath());
523  Info<< "Invoking " << wmakeCmd << endl;
524 
525  if (Foam::system(wmakeCmd))
526  {
527  return false;
528  }
529  else
530  {
531  return true;
532  }
533 }
534 
535 
537 {
538  const fileName file = digestFile();
539 
540  if (!exists(file, false, false) || SHA1Digest(IFstream(file)()) != sha1)
541  {
542  return false;
543  }
544 
545  return true;
546 }
547 
548 
550 {
551  return upToDate(context.sha1());
552 }
553 
554 
555 // ************************************************************************* //
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition: DynamicList.H:78
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:296
An STL-conforming hash table.
Definition: HashTable.H:127
Input from file stream.
Definition: IFstream.H:85
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:330
Generic input stream.
Definition: ISstream.H:55
virtual const fileName & name() const
Return the name of the stream.
Definition: ISstream.H:121
ISstream & getLine(string &, const bool continuation=true)
Read line into a string.
Definition: ISstream.C:692
Output to file stream.
Definition: OFstream.H:86
Generic output stream.
Definition: OSstream.H:54
virtual Ostream & writeQuoted(const std::string &, const bool quoted=true)
Write std::string with optional double quotes.
Definition: OSstream.C:78
virtual const fileName & name() const
Return the name of the stream.
Definition: OSstream.H:85
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
virtual Ostream & writeQuoted(const std::string &, const bool quoted=true)=0
Write std::string surrounded by quotes.
The SHA1 message digest.
Definition: SHA1Digest.H:63
std::string str(const bool prefixed=false) const
Return (40-byte) text representation, optionally with '_' prefix.
Definition: SHA1Digest.C:112
Ostream & write(Ostream &, const bool prefixed=false) const
Write (40-byte) text representation, optionally with '_' prefix.
Definition: SHA1Digest.C:137
A 2-tuple for storing two objects of different types.
Definition: Tuple2.H:63
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: UList.H:74
bool empty() const
Return true if the UList is empty (ie, size() is zero)
Definition: UListI.H:325
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:160
Encapsulation of dynamic code dictionaries.
const HashTable< string > & code() const
Return the code table.
const SHA1Digest & sha1() const
Return SHA1 digest calculated from include, options, code.
static int allowSystemOperations
Flag if system operations are allowed.
Definition: dynamicCode.H:156
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:323
bool copyOrCreateFiles(const bool verbose=false) const
Copy/create files prior to compilation.
Definition: dynamicCode.C:423
void addCopyFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:391
void reset(const dynamicCodeContext &)
Clear files and reset variables to specified context.
Definition: dynamicCode.C:370
fileName libRelPath() const
Library path for specified code name relative to $FOAM_CASE.
Definition: dynamicCode.C:347
void addCreateFile(const fileName &name, const string &contents)
Add a file to create with its contents. Will not be filtered.
Definition: dynamicCode.C:398
static bool resolveTemplates(const UList< fileName > &templateNames, DynamicList< fileName > &resolvedFiles, DynamicList< fileName > &badFiles)
Resolve code-templates via the codeTemplateEnvName.
Definition: dynamicCode.C:171
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 setFilterVariable(const word &key, const std::string &value)
Define a filter variable.
Definition: dynamicCode.C:408
static const char *const libTargetRoot
Root of the LIB target for Make/files.
Definition: dynamicCode.H:101
static word libraryBaseName(const fileName &libPath)
Return the library basename without leading 'lib' or trailing '.so'.
Definition: dynamicCode.C:93
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:58
bool createMakeFiles() const
Copy/create Make/files prior to compilation.
Definition: dynamicCode.C:230
bool writeDigest(const SHA1Digest &) const
Write digest to Make/SHA1Digest.
Definition: dynamicCode.C:296
fileName codeRelPath() const
Path for specified code name relative to $FOAM_CASE.
Definition: dynamicCode.C:341
void addCompileFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:385
void clear()
Clear files and variables.
Definition: dynamicCode.C:353
static fileName resolveTemplate(const fileName &templateName)
Resolve code-template via the codeTemplateEnvName.
Definition: dynamicCode.C:143
bool upToDate(const dynamicCodeContext &context) const
Verify if the copied code is up-to-date, based on Make/SHA1Digest.
Definition: dynamicCode.C:549
static const fileName codeTemplateDirName
Name of the code template sub-directory.
Definition: dynamicCode.H:153
bool createMakeOptions() const
Copy/create Make/options prior to compilation.
Definition: dynamicCode.C:267
bool wmakeLibso() const
Compile a libso.
Definition: dynamicCode.C:520
bool writeCommentSHA1(Ostream &) const
Write SHA1 value as C-comment.
Definition: dynamicCode.C:216
void setMakeOptions(const std::string &content)
Define contents for Make/options.
Definition: dynamicCode.C:417
A class for handling file names.
Definition: fileName.H:82
word name() const
Return file name (part beyond last /)
Definition: fileName.C:195
fileName path() const
Return directory path name (part before last /)
Definition: fileName.C:265
A line primitive.
Definition: line.H:71
A class for handling character strings derived from std::string.
Definition: string.H:79
A class for handling words, derived from string.
Definition: word.H:62
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:318
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
Functions to search 'etc' directories for configuration files etc.
tUEqn clear()
int infoSwitch(const char *name, const int defaultValue=0)
Lookup info switch or add default value.
Definition: debug.C:233
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
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
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
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
int system(const std::string &command)
Execute the specified command.
Definition: POSIX.C:1230
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
bool mkDir(const fileName &, mode_t=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:290
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
bool isAdministrator()
Is user administrator.
Definition: POSIX.C:180
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
messageStream Info
labelList second(const UList< labelPair > &p)
Definition: patchToPatch.C:49
fileName findEtcFile(const fileName &, bool mandatory=false)
Search for a file using findEtcFiles.
Definition: etcFiles.C:252
labelList first(const UList< labelPair > &p)
Definition: patchToPatch.C:39
bool isDir(const fileName &, const bool followLink=true)
Does the name exist as a directory in the file system?
Definition: POSIX.C:539
string getEnv(const word &)
Return environment variable of given name.
Definition: POSIX.C:97
IOerror FatalIOError
error FatalError
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
static const char nl
Definition: Ostream.H:260
dictionary dict