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 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 "dictionary.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
37 (
38  Foam::debug::infoSwitch("allowSystemOperations", 0)
39 );
40 
41 
42 const Foam::word Foam::dynamicCode::codeTemplateEnvName
43  = "FOAM_CODE_TEMPLATES";
44 
45 const Foam::fileName Foam::dynamicCode::codeTemplateDirName
46  = "codeTemplates/dynamicCode";
47 
48 const char* const Foam::dynamicCode::libTargetRoot =
49  "LIB = $(PWD)/../platforms/$(WM_OPTIONS)/lib/lib";
50 
51 const char* const Foam::dynamicCode::topDirName = "dynamicCode";
52 
53 
54 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
55 
57 (
58  const char* title,
59  const dictionary& dict
60 )
61 {
62  if (isAdministrator())
63  {
65  (
66  title,
67  dict
68  ) << "This code should not be executed by someone with administrator"
69  << " rights due to security reasons." << nl
70  << "(it writes a shared library which then gets loaded "
71  << "using dlopen)"
72  << exit(FatalIOError);
73  }
74 
75  if (!allowSystemOperations)
76  {
78  (
79  title,
80  dict
81  ) << "Loading a shared library using case-supplied code is not"
82  << " enabled by default" << nl
83  << "because of security issues. If you trust the code you can"
84  << " enable this" << nl
85  << "facility be adding to the InfoSwitches setting in the system"
86  << " controlDict:" << nl << nl
87  << " allowSystemOperations 1" << nl << nl
88  << "The system controlDict is either" << nl << nl
89  << " ~/.OpenFOAM/$WM_PROJECT_VERSION/controlDict" << nl << nl
90  << "or" << nl << nl
91  << " $WM_PROJECT_DIR/etc/controlDict" << nl
92  << endl
93  << exit(FatalIOError);
94  }
95 }
96 
97 
99 {
100  word libName(libPath.name(true));
101  libName.erase(0, 3); // remove leading 'lib' from name
102  return libName;
103 }
104 
105 
106 
107 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
108 
110 (
111  ISstream& is,
112  OSstream& os,
113  const HashTable<string>& mapping
114 )
115 {
116  if (!is.good())
117  {
119  (
120  "dynamicCode::copyAndFilter()"
121  " const"
122  ) << "Failed opening for reading " << is.name()
123  << exit(FatalError);
124  }
125 
126  if (!os.good())
127  {
129  (
130  "dynamicCode::copyAndFilter()"
131  " const"
132  ) << "Failed writing " << os.name()
133  << exit(FatalError);
134  }
135 
136  // Copy file while rewriting $VARS and ${VARS}
137  string line;
138  do
139  {
140  is.getLine(line);
141 
142  // expand according to mapping
143  // expanding according to env variables might cause too many
144  // surprises
145  stringOps::inplaceExpand(line, mapping);
146  os.writeQuoted(line, false) << nl;
147  }
148  while (is.good());
149 }
150 
151 
153 (
154  const UList<fileName>& templateNames,
155  DynamicList<fileName>& resolvedFiles,
156  DynamicList<fileName>& badFiles
157 )
158 {
159  // try to get template from FOAM_CODESTREAM_TEMPLATES
160  const fileName templateDir(Foam::getEnv(codeTemplateEnvName));
161 
162  bool allOkay = true;
163  forAll(templateNames, fileI)
164  {
165  const fileName& templateName = templateNames[fileI];
166 
167  fileName file;
168  if (!templateDir.empty() && isDir(templateDir))
169  {
170  file = templateDir/templateName;
171  if (!isFile(file, false))
172  {
173  file.clear();
174  }
175  }
176 
177  // not found - fallback to ~OpenFOAM expansion
178  if (file.empty())
179  {
180  file = findEtcFile(codeTemplateDirName/templateName);
181  }
182 
183  if (file.empty())
184  {
185  badFiles.append(templateName);
186  allOkay = false;
187  }
188  else
189  {
190  resolvedFiles.append(file);
191  }
192  }
193 
194  return allOkay;
195 }
196 
197 
199 {
200  const bool hasSHA1 = filterVars_.found("SHA1sum");
201 
202  if (hasSHA1)
203  {
204  os << "/* dynamicCode:\n * SHA1 = ";
205  os.writeQuoted(filterVars_["SHA1sum"], false) << "\n */\n";
206  }
207 
208  return hasSHA1;
209 }
210 
211 
213 {
214  // Create Make/files
215  if (compileFiles_.empty())
216  {
217  return false;
218  }
219 
220  const fileName dstFile(this->codePath()/"Make/files");
221 
222  // Create dir
223  mkDir(dstFile.path());
224 
225  OFstream os(dstFile);
226  //Info<< "Writing to " << dstFile << endl;
227  if (!os.good())
228  {
230  (
231  "dynamicCode::createMakeFiles()"
232  " const"
233  ) << "Failed writing " << dstFile
234  << exit(FatalError);
235  }
236 
237  writeCommentSHA1(os);
238 
239  // Write compile files
240  forAll(compileFiles_, fileI)
241  {
242  os.writeQuoted(compileFiles_[fileI], false) << nl;
243  }
244 
245  os << nl
246  << libTargetRoot << codeName_.c_str() << nl;
247 
248  return true;
249 }
250 
251 
253 {
254  // Create Make/options
255  if (compileFiles_.empty() || makeOptions_.empty())
256  {
257  return false;
258  }
259 
260  const fileName dstFile(this->codePath()/"Make/options");
261 
262  // Create dir
263  mkDir(dstFile.path());
264 
265  OFstream os(dstFile);
266  //Info<< "Writing to " << dstFile << endl;
267  if (!os.good())
268  {
270  (
271  "dynamicCode::createMakeOptions()"
272  " const"
273  ) << "Failed writing " << dstFile
274  << exit(FatalError);
275  }
276 
277  writeCommentSHA1(os);
278  os.writeQuoted(makeOptions_, false) << nl;
279 
280  return true;
281 }
282 
283 
285 {
286  const fileName file = digestFile();
287  mkDir(file.path());
288 
289  OFstream os(file);
290  sha1.write(os, true) << nl;
291 
292  return os.good();
293 }
294 
295 
296 bool Foam::dynamicCode::writeDigest(const std::string& sha1) const
297 {
298  const fileName file = digestFile();
299  mkDir(file.path());
300 
301  OFstream os(file);
302  os << '_';
303  os.writeQuoted(sha1, false) << nl;
304 
305  return os.good();
306 }
307 
308 
309 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
310 
311 Foam::dynamicCode::dynamicCode(const word& codeName, const word& codeDirName)
312 :
313  codeRoot_(stringOps::expand("$FOAM_CASE")/topDirName),
314  libSubDir_(stringOps::expand("platforms/$WM_OPTIONS/lib")),
315  codeName_(codeName),
316  codeDirName_(codeDirName)
317 {
318  if (codeDirName_.empty())
319  {
320  codeDirName_ = codeName_;
321  }
322 
323  clear();
324 }
325 
326 
327 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
328 
330 {
331  return topDirName/codeDirName_;
332 }
333 
334 
336 {
337  return codeRelPath()/libSubDir_/"lib" + codeName_ + ".so";
338 }
339 
340 
342 {
343  compileFiles_.clear();
344  copyFiles_.clear();
345  createFiles_.clear();
346  filterVars_.clear();
347  filterVars_.set("typeName", codeName_);
348  filterVars_.set("SHA1sum", SHA1Digest().str());
349 
350  // provide default Make/options
351  makeOptions_ =
352  "EXE_INC = -g\n"
353  "\n\nLIB_LIBS = ";
354 }
355 
356 
358 (
359  const dynamicCodeContext& context
360 )
361 {
362  clear();
363  setFilterContext(context);
364 }
365 
366 
368 {
369  compileFiles_.append(name);
370 }
371 
372 
374 {
375  copyFiles_.append(name);
376 }
377 
378 
380 (
381  const fileName& name,
382  const string& contents
383 )
384 {
385  createFiles_.append(fileAndContent(name, contents));
386 }
387 
388 
390 (
391  const dynamicCodeContext& context
392 )
393 {
394  filterVars_.set("localCode", context.localCode());
395  filterVars_.set("code", context.code());
396  filterVars_.set("codeInclude", context.include());
397  filterVars_.set("SHA1sum", context.sha1().str());
398 }
399 
400 
402 (
403  const word& key,
404  const std::string& value
405 )
406 {
407  filterVars_.set(key, value);
408 }
409 
410 
411 void Foam::dynamicCode::setMakeOptions(const std::string& content)
412 {
413  makeOptions_ = content;
414 }
415 
416 
417 bool Foam::dynamicCode::copyOrCreateFiles(const bool verbose) const
418 {
419  if (verbose)
420  {
421  Info<< "Creating new library in " << this->libRelPath() << endl;
422  }
423 
424  const label nFiles = compileFiles_.size() + copyFiles_.size();
425 
426  DynamicList<fileName> resolvedFiles(nFiles);
427  DynamicList<fileName> badFiles(nFiles);
428 
429  // resolve template, or add to bad-files
430  resolveTemplates(compileFiles_, resolvedFiles, badFiles);
431  resolveTemplates(copyFiles_, resolvedFiles, badFiles);
432 
433  if (!badFiles.empty())
434  {
436  (
437  "dynamicCode::copyFilesContents(..)"
438  ) << "Could not find the code template(s): "
439  << badFiles << nl
440  << "Under the $" << codeTemplateEnvName
441  << " directory or via via the ~OpenFOAM/"
442  << codeTemplateDirName << " expansion"
443  << exit(FatalError);
444  }
445 
446 
447 
448  // Create dir
449  const fileName outputDir = this->codePath();
450 
451  // Create dir
452  mkDir(outputDir);
453 
454  // Copy/filter files
455  forAll(resolvedFiles, fileI)
456  {
457  const fileName& srcFile = resolvedFiles[fileI];
458  const fileName dstFile(outputDir/srcFile.name());
459 
460  IFstream is(srcFile);
461  //Info<< "Reading from " << is.name() << endl;
462  if (!is.good())
463  {
465  (
466  "dynamicCode::copyFilesContents(const fileName&)"
467  " const"
468  ) << "Failed opening " << srcFile
469  << exit(FatalError);
470  }
471 
472  OFstream os(dstFile);
473  //Info<< "Writing to " << dstFile.name() << endl;
474  if (!os.good())
475  {
477  (
478  "dynamicCode::copyFilesContents(const fileName&)"
479  " const"
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  (
504  "dynamicCode::copyOrCreateFiles()"
505  " const"
506  ) << "Failed writing " << dstFile
507  << exit(FatalError);
508  }
509  os.writeQuoted(createFiles_[fileI].second(), false) << nl;
510  }
511 
512 
513  // Create Make/files + Make/options
514  createMakeFiles();
516 
517  writeDigest(filterVars_["SHA1sum"]);
518 
519  return true;
520 }
521 
522 
524 {
525  const Foam::string wmakeCmd("wmake -s libso " + this->codePath());
526  Info<< "Invoking " << wmakeCmd << endl;
527 
528  if (Foam::system(wmakeCmd))
529  {
530  return false;
531  }
532  else
533  {
534  return true;
535  }
536 }
537 
538 
540 {
541  const fileName file = digestFile();
542 
543  if (!exists(file, false) || SHA1Digest(IFstream(file)()) != sha1)
544  {
545  return false;
546  }
547 
548  return true;
549 }
550 
551 
553 {
554  return upToDate(context.sha1());
555 }
556 
557 
558 // ************************************************************************* //
Output to file stream.
Definition: OFstream.H:81
static void copyAndFilter(ISstream &, OSstream &, const HashTable< string > &mapping)
Copy lines while expanding variables.
Definition: dynamicCode.C:110
int infoSwitch(const char *name, const int defaultValue=0)
Lookup info switch or add default value.
Definition: debug.C:174
word name() const
Return file name (part beyond last /)
Definition: fileName.C:206
const word & codeName() const
Return the code-name.
Definition: dynamicCode.H:187
Generic output stream.
Definition: OSstream.H:51
static bool resolveTemplates(const UList< fileName > &templateNames, DynamicList< fileName > &resolvedFiles, DynamicList< fileName > &badFiles)
Resolve code-templates via the codeTemplateEnvName.
Definition: dynamicCode.C:153
bool empty() const
Return true if the UList is empty (ie, size() is zero).
Definition: UListI.H:313
bool upToDate(const dynamicCodeContext &context) const
Verify if the copied code is up-to-date, based on Make/SHA1Digest.
Definition: dynamicCode.C:552
Ostream & write(Ostream &, const bool prefixed=false) const
Write (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1Digest.C:137
An STL-conforming hash table.
Definition: HashTable.H:61
string getEnv(const word &)
Return environment variable of given name.
Definition: POSIX.C:102
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
A class for handling words, derived from string.
Definition: word.H:59
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 class for handling character strings derived from std::string.
Definition: string.H:74
DynamicList< T, SizeInc, SizeMult, SizeDiv > & append(const T &)
Append an element at the end of the list.
Definition: DynamicListI.H:310
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
bool exists(const fileName &, const bool checkGzip=true)
Does the name exist (as DIRECTORY or FILE) in the file system?
Definition: POSIX.C:609
const word & codeDirName() const
Return the code-dirname.
Definition: dynamicCode.H:193
messageStream Info
static const word codeTemplateEnvName
Name of the code template environment variable.
Definition: dynamicCode.H:154
Tuple2< fileName, string > fileAndContent
Definition: dynamicCode.H:59
fileName path() const
Return directory path name (part before last /)
Definition: fileName.C:287
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:137
Input from file stream.
Definition: IFstream.H:81
const SHA1Digest & sha1() const
Return SHA1 digest calculated from include, options, code.
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:86
void setMakeOptions(const std::string &content)
Define contents for Make/options.
Definition: dynamicCode.C:411
A line primitive.
Definition: line.H:56
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:74
static int allowSystemOperations
Flag if system operations are allowed.
Definition: dynamicCode.H:161
static void checkSecurity(const char *title, const dictionary &)
Check security for creating dynamic code.
Definition: dynamicCode.C:57
const string & include() const
Return the code-includes.
bool writeCommentSHA1(Ostream &) const
Write SHA1 value as C-comment.
Definition: dynamicCode.C:198
Encapsulation of dynamic code dictionaries.
static const char nl
Definition: Ostream.H:260
bool createMakeOptions() const
Copy/create Make/options prior to compilation.
Definition: dynamicCode.C:252
fileName libRelPath() const
Library path for specified code name relative to $FOAM_CASE.
Definition: dynamicCode.C:335
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
void setFilterContext(const dynamicCodeContext &)
Define filter variables for code, codeInclude, SHA1sum.
Definition: dynamicCode.C:390
IOerror FatalIOError
static const char *const topDirName
Top-level directory name for copy/compiling.
Definition: dynamicCode.H:109
#define forAll(list, i)
Definition: UList.H:421
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:333
Generic input stream.
Definition: ISstream.H:51
virtual Ostream & writeQuoted(const std::string &, const bool quoted=true)=0
Write std::string surrounded by quotes.
fileName findEtcFile(const fileName &, bool mandatory=false)
Search for a file using findEtcFiles.
Definition: POSIX.C:405
bool wmakeLibso() const
Compile a libso.
Definition: dynamicCode.C:523
ISstream & getLine(string &)
Raw, low-level getline into a string function.
Definition: ISstreamI.H:77
const string & localCode() const
Return the local (file-scope) code.
virtual const fileName & name() const
Return the name of the stream.
Definition: OSstream.H:88
#define FatalErrorIn(functionName)
Report an error message using Foam::FatalError.
Definition: error.H:314
fileName digestFile() const
Path for SHA1Digest.
Definition: dynamicCode.H:239
bool isDir(const fileName &)
Does the name exist as a DIRECTORY in the file system?
Definition: POSIX.C:616
static word libraryBaseName(const fileName &libPath)
Return the library basename without leading &#39;lib&#39; or trailing &#39;.so&#39;.
Definition: dynamicCode.C:98
void reset(const dynamicCodeContext &)
Clear files and reset variables to specified context.
Definition: dynamicCode.C:358
void addCopyFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:373
int system(const std::string &command)
Execute the specified command.
Definition: POSIX.C:1162
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects...
Definition: DynamicList.H:56
error FatalError
static const char *const libTargetRoot
Root of the LIB target for Make/files.
Definition: dynamicCode.H:106
virtual const fileName & name() const
Return the name of the stream.
Definition: ISstream.H:106
A class for handling file names.
Definition: fileName.H:69
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
bool copyOrCreateFiles(const bool verbose=false) const
Copy/create files prior to compilation.
Definition: dynamicCode.C:417
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:53
void addCreateFile(const fileName &name, const string &contents)
Add a file to create with its contents. Will not be filtered.
Definition: dynamicCode.C:380
void addCompileFile(const fileName &name)
Add a file template name, which will be found and filtered.
Definition: dynamicCode.C:367
bool createMakeFiles() const
Copy/create Make/files prior to compilation.
Definition: dynamicCode.C:212
void clear()
Clear files and variables.
Definition: dynamicCode.C:341
fileName codeRelPath() const
Path for specified code name relative to $FOAM_CASE.
Definition: dynamicCode.C:329
bool isFile(const fileName &, const bool checkGzip=true)
Does the name exist as a FILE in the file system?
Definition: POSIX.C:623
virtual Ostream & writeQuoted(const std::string &, const bool quoted=true)
Write std::string surrounded by quotes.
Definition: OSstream.C:125
bool mkDir(const fileName &, mode_t=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:420
void setFilterVariable(const word &key, const std::string &value)
Define a filter variable.
Definition: dynamicCode.C:402
const string & code() const
Return the code.
bool writeDigest(const SHA1Digest &) const
Write digest to Make/SHA1Digest.
Definition: dynamicCode.C:284
The SHA1 message digest.
Definition: SHA1Digest.H:62
fileName codePath() const
Path for specified code name.
Definition: dynamicCode.H:214
static const fileName codeTemplateDirName
Name of the code template sub-directory.
Definition: dynamicCode.H:158
#define FatalIOErrorIn(functionName, ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:325
bool isAdministrator()
Is user administrator.
Definition: POSIX.C:185
std::string str(const bool prefixed=false) const
Return (40-byte) text representation, optionally with &#39;_&#39; prefix.
Definition: SHA1Digest.C:112