dynamicCode.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | Copyright (C) 2011-2016 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))
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 
294 Foam::dynamicCode::dynamicCode(const word& codeName, const word& codeDirName)
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  setFilterContext(context);
347 }
348 
349 
351 {
352  compileFiles_.append(name);
353 }
354 
355 
357 {
358  copyFiles_.append(name);
359 }
360 
361 
363 (
364  const fileName& name,
365  const string& contents
366 )
367 {
368  createFiles_.append(fileAndContent(name, contents));
369 }
370 
371 
373 (
374  const dynamicCodeContext& context
375 )
376 {
377  filterVars_.set("localCode", context.localCode());
378  filterVars_.set("code", context.code());
379  filterVars_.set("codeInclude", context.include());
380  filterVars_.set("SHA1sum", context.sha1().str());
381 }
382 
383 
385 (
386  const word& key,
387  const std::string& value
388 )
389 {
390  filterVars_.set(key, value);
391 }
392 
393 
394 void Foam::dynamicCode::setMakeOptions(const std::string& content)
395 {
396  makeOptions_ = content;
397 }
398 
399 
400 bool Foam::dynamicCode::copyOrCreateFiles(const bool verbose) const
401 {
402  if (verbose)
403  {
404  Info<< "Creating new library in " << this->libRelPath() << endl;
405  }
406 
407  const label nFiles = compileFiles_.size() + copyFiles_.size();
408 
409  DynamicList<fileName> resolvedFiles(nFiles);
410  DynamicList<fileName> badFiles(nFiles);
411 
412  // Resolve template, or add to bad-files
413  resolveTemplates(compileFiles_, resolvedFiles, badFiles);
414  resolveTemplates(copyFiles_, resolvedFiles, badFiles);
415 
416  if (!badFiles.empty())
417  {
419  << "Could not find the code template(s): "
420  << badFiles << nl
421  << "Under the $" << codeTemplateEnvName
422  << " directory or via via the ~OpenFOAM/"
423  << codeTemplateDirName << " expansion"
424  << exit(FatalError);
425  }
426 
427 
428 
429  // Create dir
430  const fileName outputDir = this->codePath();
431 
432  // Create dir
433  mkDir(outputDir);
434 
435  // Copy/filter files
436  forAll(resolvedFiles, fileI)
437  {
438  const fileName& srcFile = resolvedFiles[fileI];
439  const fileName dstFile(outputDir/srcFile.name());
440 
441  IFstream is(srcFile);
442  //Info<< "Reading from " << is.name() << endl;
443  if (!is.good())
444  {
446  << "Failed opening " << srcFile
447  << exit(FatalError);
448  }
449 
450  OFstream os(dstFile);
451  //Info<< "Writing to " << dstFile.name() << endl;
452  if (!os.good())
453  {
455  << "Failed writing " << dstFile
456  << exit(FatalError);
457  }
458 
459  // Copy lines while expanding variables
460  copyAndFilter(is, os, filterVars_);
461  }
462 
463 
464  // Create files:
465  forAll(createFiles_, fileI)
466  {
467  const fileName dstFile
468  (
469  outputDir/stringOps::expand(createFiles_[fileI].first())
470  );
471 
472  mkDir(dstFile.path());
473  OFstream os(dstFile);
474  //Info<< "Writing to " << createFiles_[fileI].first() << endl;
475  if (!os.good())
476  {
478  << "Failed writing " << dstFile
479  << exit(FatalError);
480  }
481  os.writeQuoted(createFiles_[fileI].second(), false) << nl;
482  }
483 
484 
485  // Create Make/files + Make/options
486  createMakeFiles();
488 
489  writeDigest(filterVars_["SHA1sum"]);
490 
491  return true;
492 }
493 
494 
496 {
497  const Foam::string wmakeCmd("wmake -s libso " + this->codePath());
498  Info<< "Invoking " << wmakeCmd << endl;
499 
500  if (Foam::system(wmakeCmd))
501  {
502  return false;
503  }
504  else
505  {
506  return true;
507  }
508 }
509 
510 
512 {
513  const fileName file = digestFile();
514 
515  if (!exists(file, false) || SHA1Digest(IFstream(file)()) != sha1)
516  {
517  return false;
518  }
519 
520  return true;
521 }
522 
523 
525 {
526  return upToDate(context.sha1());
527 }
528 
529 
530 // ************************************************************************* //
string getEnv(const word &)
Return environment variable of given name.
Definition: POSIX.C:104
virtual const fileName & name() const
Return the name of the stream.
Definition: OSstream.H:88
const string & localCode() const
Return the local (file-scope) code.
const string & include() const
Return the code-includes.
Generic output stream.
Definition: OSstream.H:51
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:428
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 occurences of variables according to the mapping.
Definition: stringOps.C:75
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:193
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:69
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:106
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:137
void setMakeOptions(const std::string &content)
Define contents for Make/options.
Definition: dynamicCode.C:394
const word & codeName() const
Return the code-name.
Definition: dynamicCode.H:187
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
Output to file stream.
Definition: OFstream.H:82
static const char *const libTargetRoot
Root of the LIB target for Make/files.
Definition: dynamicCode.H:106
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 surrounded by quotes.
Definition: OSstream.C:125
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:253
The SHA1 message digest.
Definition: SHA1Digest.H:62
void setFilterContext(const dynamicCodeContext &)
Define filter variables for code, codeInclude, SHA1sum.
Definition: dynamicCode.C:373
static const word codeTemplateEnvName
Name of the code template environment variable.
Definition: dynamicCode.H:154
static const char *const topDirName
Top-level directory name for copy/compiling.
Definition: dynamicCode.H:109
void addCompileFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:350
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:177
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:239
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:536
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:158
static int allowSystemOperations
Flag if system operations are allowed.
Definition: dynamicCode.H:161
word name() const
Return file name (part beyond last /)
Definition: fileName.C:180
static void copyAndFilter(ISstream &, OSstream &, const HashTable< string > &mapping)
Copy lines while expanding variables.
Definition: dynamicCode.C:105
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:292
void addCreateFile(const fileName &name, const string &contents)
Add a file to create with its contents. Will not be filtered.
Definition: dynamicCode.C:363
bool wmakeLibso() const
Compile a libso.
Definition: dynamicCode.C:495
An STL-conforming hash table.
Definition: HashTable.H:62
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:61
fileName codePath() const
Path for specified code name.
Definition: dynamicCode.H:214
bool isFile(const fileName &, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: POSIX.C:551
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:53
static const char nl
Definition: Ostream.H:262
bool upToDate(const dynamicCodeContext &context) const
Verify if the copied code is up-to-date, based on Make/SHA1Digest.
Definition: dynamicCode.C:524
bool copyOrCreateFiles(const bool verbose=false) const
Copy/create files prior to compilation.
Definition: dynamicCode.C:400
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:297
bool isAdministrator()
Is user administrator.
Definition: POSIX.C:187
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:385
string & inplaceExpand(string &, const HashTable< string, word, string::hash > &mapping, const char sigil='$')
Inplace expand occurences of variables according to the mapping.
Definition: stringOps.C:87
#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:356
messageStream Info
fileName path() const
Return directory path name (part before last /)
Definition: fileName.C:250
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:74
bool createMakeFiles() const
Copy/create Make/files prior to compilation.
Definition: dynamicCode.C:201
const string & code() const
Return the code.
bool exists(const fileName &, const bool checkGzip=true, const bool followLink=true)
Does the name exist (as DIRECTORY or FILE) in the file system?
Definition: POSIX.C:517
int system(const std::string &command)
Execute the specified command.
Definition: POSIX.C:1193
ISstream & getLine(string &)
Raw, low-level getline into a string function.
Definition: ISstreamI.H:77
IOerror FatalIOError
Tuple2< fileName, string > fileAndContent
Definition: dynamicCode.H:59