FOSSology  3.2.0rc1
Open Source License Compliance by Open Source Software
OjoUtils.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019, Siemens AG
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * version 2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  */
22 #include <iostream>
23 
24 #include "OjoUtils.hpp"
25 #include "OjoAgent.hpp"
26 
27 using namespace fo;
28 
37 {
38  int agentId = queryAgentId(dbManager);
39  return OjoState(agentId, std::move(cliOptions));
40 }
41 
48 {
49  return OjoState(-1, std::move(cliOptions));
50 }
51 
58 {
59  char* COMMIT_HASH = fo_sysconfig(AGENT_NAME, "COMMIT_HASH");
60  char* VERSION = fo_sysconfig(AGENT_NAME, "VERSION");
61  char *agentRevision;
62 
63  if (!asprintf(&agentRevision, "%s.%s", VERSION, COMMIT_HASH))
64  bail(-1);
65 
66  int agentId = fo_GetAgentKey(dbManager.getConnection(), AGENT_NAME, 0,
67  agentRevision, AGENT_DESC);
68  free(agentRevision);
69 
70  if (agentId <= 0)
71  bail(1);
72 
73  return agentId;
74 }
75 
85 int writeARS(const OjoState &state, int arsId, int uploadId, int success,
87 {
88  PGconn *connection = dbManager.getConnection();
89  int agentId = state.getAgentId();
90 
91  return fo_WriteARS(connection, arsId, uploadId, agentId, AGENT_ARS, NULL,
92  success);
93 }
94 
99 void bail(int exitval)
100 {
101  fo_scheduler_disconnect(exitval);
102  exit(exitval);
103 }
104 
113 bool processUploadId(const OjoState &state, int uploadId,
114  OjosDatabaseHandler &databaseHandler, bool ignoreFilesWithMimeType)
115 {
116  vector<unsigned long> fileIds = databaseHandler.queryFileIdsForUpload(
117  uploadId, ignoreFilesWithMimeType);
118  char const *repoArea = "files";
119 
120  bool errors = false;
121 #pragma omp parallel
122  {
123  OjosDatabaseHandler threadLocalDatabaseHandler(databaseHandler.spawn());
124 
125  size_t pFileCount = fileIds.size();
126  OjoAgent agentObj = state.getOjoAgent();
127 #pragma omp for
128  for (size_t it = 0; it < pFileCount; ++it)
129  {
130  if (errors)
131  continue;
132 
133  unsigned long pFileId = fileIds[it];
134 
135  if (pFileId == 0)
136  continue;
137 
138  char *fileName = threadLocalDatabaseHandler.getPFileNameForFileId(
139  pFileId);
140  char *filePath = NULL;
141 #pragma omp critical (repo_mk_path)
142  filePath = fo_RepMkPath(repoArea, fileName);
143 
144  if (!filePath)
145  {
146  LOG_FATAL(
147  AGENT_NAME" was unable to derive a file path for pfile %ld. Check your HOSTS configuration.",
148  pFileId);
149  errors = true;
150  }
151 
152  vector<ojomatch> identified;
153  try
154  {
155  identified = agentObj.processFile(filePath, threadLocalDatabaseHandler);
156  }
157  catch (std::runtime_error &e)
158  {
159  LOG_FATAL("Unable to read %s.", e.what());
160  continue;
161  }
162 
163  if (!storeResultInDb(identified, threadLocalDatabaseHandler,
164  state.getAgentId(), pFileId))
165  {
166  LOG_FATAL("Unable to store results in database for pfile %ld.",
167  pFileId);
168  bail(-20);
169  }
170 
172  }
173  }
174 
175  return !errors;
176 }
177 
190 bool storeResultInDb(const vector<ojomatch> &matches,
191  OjosDatabaseHandler &databaseHandle, const int agent_fk, const int pfile_fk)
192 {
193  if (!databaseHandle.begin())
194  {
195  return false;
196  }
197 
198  size_t count = 0;
199  if (matches.size() == 0)
200  {
201  OjoDatabaseEntry entry(-1, agent_fk, pfile_fk);
202  databaseHandle.insertNoResultInDatabase(entry);
203  return databaseHandle.commit();
204  }
205  for (auto m : matches)
206  {
207  OjoDatabaseEntry entry(m.license_fk, agent_fk, pfile_fk);
208 
209  if (entry.license_fk > 0)
210  {
211  ++count;
212  unsigned long int fl_pk = databaseHandle.saveLicenseToDatabase(entry);
213  if (!(fl_pk > 0) || !databaseHandle.saveHighlightToDatabase(m, fl_pk))
214  {
215  databaseHandle.rollback();
216  return false;
217  }
218  }
219  else
220  {
221  databaseHandle.insertNoResultInDatabase(entry);
222  }
223  }
224 
225  return databaseHandle.commit();
226 }
227 
237 bool parseCliOptions(int argc, char **argv, OjoCliOptions &dest,
238  std::vector<std::string> &fileNames, string &directoryToScan)
239 {
240  boost::program_options::options_description desc(
241  AGENT_NAME ": recognized options");
242  desc.add_options()
243  (
244  "help,h", "shows help"
245  )
246  (
247  "verbose,v", "increase verbosity"
248  )
249  (
250  "files",
251  boost::program_options::value<vector<string> >(),
252  "files to scan"
253  )
254  (
255  "json,J", "output JSON"
256  )
257  (
258  "ignoreFilesWithMimeType,I", "ignoreFilesWithMimeType"
259  )
260  (
261  "config,c",
262  boost::program_options::value<string>(),
263  "path to the sysconfigdir"
264  )
265  (
266  "scheduler_start",
267  "specifies, that the command was called by the scheduler"
268  )
269  (
270  "userID",
271  boost::program_options::value<int>(),
272  "the id of the user that created the job (only in combination with --scheduler_start)"
273  )
274  (
275  "groupID",
276  boost::program_options::value<int>(),
277  "the id of the group of the user that created the job (only in combination with --scheduler_start)"
278  )
279  (
280  "jobId",
281  boost::program_options::value<int>(),
282  "the id of the job (only in combination with --scheduler_start)"
283  )
284  (
285  "directory,d",
286  boost::program_options::value<string>(),
287  "directory to scan (recursive)"
288  )
289  ;
290 
291  boost::program_options::positional_options_description p;
292  p.add("files", -1);
293 
294  boost::program_options::variables_map vm;
295 
296  try
297  {
298  boost::program_options::store(
299  boost::program_options::command_line_parser(argc, argv).options(desc).positional(
300  p).run(), vm);
301 
302  if (vm.count("help") > 0)
303  {
304  cout << desc << endl;
305  exit(0);
306  }
307 
308  if (vm.count("files"))
309  {
310  fileNames = vm["files"].as<std::vector<string> >();
311  }
312 
313  unsigned long verbosity = vm.count("verbose");
314  bool json = vm.count("json") > 0 ? true : false;
315  bool ignoreFilesWithMimeType = vm.count("ignoreFilesWithMimeType") > 0 ? true : false;
316 
317  dest = OjoCliOptions(verbosity, json, ignoreFilesWithMimeType);
318 
319  if (vm.count("directory"))
320  {
321  if (vm.count("files"))
322  {
323  cout << "cannot pass files and directory at the same time" << endl;
324  cout << desc << endl;
325  fileNames.clear();
326  return false;
327  }
328  directoryToScan = vm["directory"].as<std::string>();
329  }
330 
331  return true;
332  }
333  catch (boost::bad_any_cast&)
334  {
335  cout << "wrong parameter type" << endl;
336  cout << desc << endl;
337  return false;
338  }
339  catch (boost::program_options::error&)
340  {
341  cout << "wrong command line arguments" << endl;
342  cout << desc << endl;
343  return false;
344  }
345 }
346 
354 void appendToJson(const std::string fileName,
355  const std::pair<string, vector<ojomatch>> resultPair,
356  bool &printComma)
357 {
358  Json::Value result;
359 #if JSONCPP_VERSION_HEXA < ((1 << 24) | (4 << 16))
360  // Use FastWriter for versions below 1.4.0
361  Json::FastWriter jsonWriter;
362 #else
363  // Since version 1.4.0, FastWriter is deprecated and replaced with
364  // StreamWriterBuilder
365  Json::StreamWriterBuilder jsonWriter;
366  jsonWriter["commentStyle"] = "None";
367  jsonWriter["indentation"] = "";
368 #endif
369  if (resultPair.first.empty())
370  {
371  result["file"] = fileName;
372  result["results"] = "Unable to read file";
373  }
374  else
375  {
376  vector<ojomatch> resultList = resultPair.second;
377  Json::Value results;
378  for (auto m : resultList)
379  {
380  Json::Value j;
381  j["start"] = Json::Value::UInt(m.start);
382  j["end"] = Json::Value::UInt(m.end);
383  j["len"] = Json::Value::UInt(m.len);
384  j["license"] = m.content;
385  results.append(j);
386  }
387  result["file"] = fileName;
388  result["results"] = results;
389  }
390  // Thread-Safety: output all matches JSON at once to STDOUT
391 #pragma omp critical (jsonPrinter)
392  {
393  if (printComma)
394  {
395  cout << "," << endl;
396  }
397  else
398  {
399  printComma = true;
400  }
401  string jsonString;
402 #if JSONCPP_VERSION_HEXA < ((1 << 24) | (4 << 16))
403  // For version below 1.4.0, every writer append `\n` at end.
404  // Find and replace it.
405  jsonString = jsonWriter.write(result);
406  jsonString.replace(jsonString.find("\n"), string("\n").length(), "");
407 #else
408  // For version >= 1.4.0, \n is not appended.
409  jsonString = Json::writeString(jsonWriter, result);
410 #endif
411  cout << " " << jsonString << flush;
412  }
413 }
414 
420 void printResultToStdout(const std::string fileName,
421  const std::pair<string, vector<ojomatch>> resultPair)
422 {
423  if (resultPair.first.empty())
424  {
425  cout << fileName << " :: Unable to read file" << endl;
426  return;
427  }
428  stringstream ss;
429  ss << fileName << " ::" << endl;
430  // Output matches
431  vector<ojomatch> resultList = resultPair.second;
432  for (auto m : resultList)
433  {
434  ss << "\t[" << m.start << ':' << m.end << "]: '" << m.content << "'" << endl;
435  }
436  // Thread-Safety: output all matches (collected in ss) at once to cout
437  cout << ss.str();
438 }
int queryAgentId(DbManager &dbManager)
Definition: OjoUtils.cc:57
unsigned long saveLicenseToDatabase(OjoDatabaseEntry &entry) const
Save findings to the database if agent was called by scheduler.
bool storeResultInDb(const vector< ojomatch > &matches, OjosDatabaseHandler &databaseHandle, const int agent_fk, const int pfile_fk)
Store the results from scan to DB.
Definition: OjoUtils.cc:190
DB wrapper for agents.
bool commit() const
COMMIT a transaction block in DB.
bool rollback() const
ROLLBACK a transaction block in DB.
const OjoAgent & getOjoAgent() const
Definition: OjoState.cc:52
void fo_scheduler_disconnect(int retcode)
Disconnect the scheduler connection.
OjoState getState(DbManager &dbManager, OjoCliOptions &&cliOptions)
Create a new state for the current agent based on CliOptions.
Definition: OjoUtils.cc:36
int getAgentId() const
Definition: OjoState.cc:43
PGconn * getConnection() const
void printResultToStdout(const std::string fileName, const std::pair< string, vector< ojomatch >> resultPair)
Definition: OjoUtils.cc:420
void bail(int exitval)
Disconnect with scheduler returning an error code and exit.
Definition: OjoUtils.cc:99
Store the state of the agent.
Definition: OjoState.hpp:54
bool saveHighlightToDatabase(const ojomatch &match, const unsigned long fl_fk) const
int writeARS(const OjoState &state, int arsId, int uploadId, int success, DbManager &dbManager)
Definition: OjoUtils.cc:85
OjosDatabaseHandler spawn() const
bool processUploadId(const OjoState &state, int uploadId, OjosDatabaseHandler &databaseHandler, bool ignoreFilesWithMimeType)
Definition: OjoUtils.cc:113
void appendToJson(const std::string fileName, const std::pair< string, vector< ojomatch >> resultPair, bool &printComma)
Definition: OjoUtils.cc:354
bool begin() const
BEGIN a transaction block in DB.
char * fo_RepMkPath(const char *Type, char *Filename)
Given a filename, construct the full path to the file.
Definition: libfossrepo.c:364
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:28
FUNCTION int fo_GetAgentKey(PGconn *pgConn, const char *agent_name, long Upload_pk, const char *rev, const char *agent_desc)
Get the latest enabled agent key (agent_pk) from the database.
Definition: libfossagent.c:172
fo namespace holds the FOSSology library functions.
FUNCTION int fo_WriteARS(PGconn *pgConn, int ars_pk, int upload_pk, int agent_pk, const char *tableName, const char *ars_status, int ars_success)
Write ars record.
Definition: libfossagent.c:228
bool insertNoResultInDatabase(OjoDatabaseEntry &entry) const
Save no result to the database.
void fo_scheduler_heart(int i)
This function must be called by agents to let the scheduler know they are alive and how many items th...
const unsigned long int license_fk
bool parseCliOptions(int argc, char **argv, OjoCliOptions &dest, std::vector< std::string > &fileNames, string &directoryToScan)
Parse the options sent by CLI to CliOptions object.
Definition: OjoUtils.cc:237
char * fo_sysconfig(const char *sectionname, const char *variablename)
gets a system configuration variable from the configuration data.
std::vector< unsigned long > queryFileIdsForUpload(int uploadId, bool ignoreFilesWithMimeType)
Store the options sent through the CLI.
Definition: OjoState.hpp:34