FOSSology  4.7.1
Open Source License Compliance by Open Source Software
scancode_utils.cc
1 /*
2  SPDX-FileCopyrightText: © 2021 Sarita Singh <saritasingh.0425@gmail.com>
3 
4  SPDX-License-Identifier: GPL-2.0-only
5 */
6 
7 #include "scancode_utils.hpp"
8 
9 namespace po = boost::program_options;
10 
15 void bail(int exitval) {
16  fo_scheduler_disconnect(exitval);
17  exit(exitval);
18 }
19 
26  int agentId = queryAgentId(dbManager);
27  return State(agentId);
28 }
29 
36  char *COMMIT_HASH = fo_sysconfig(AGENT_NAME, "COMMIT_HASH");
37  char *VERSION = fo_sysconfig(AGENT_NAME, "VERSION");
38  char *agentRevision;
39 
40  if (!asprintf(&agentRevision, "%s.%s", VERSION, COMMIT_HASH))
41  bail(-1);
42 
43  int agentId = fo_GetAgentKey(dbManager.getConnection(), AGENT_NAME, 0,
44  agentRevision, AGENT_DESC);
45  free(agentRevision);
46 
47  if (agentId <= 0)
48  bail(1);
49 
50  return agentId;
51 }
52 
62 int writeARS(const State &state, int arsId, int uploadId, int success,
64  PGconn *connection = dbManager.getConnection();
65  int agentId = state.getAgentId();
66 
67  return fo_WriteARS(connection, arsId, uploadId, agentId, AGENT_ARS, NULL,
68  success);
69 }
70 
78 bool processUploadId(const State &state, int uploadId,
79  ScancodeDatabaseHandler &databaseHandler, bool ignoreFilesWithMimeType) {
80  vector<unsigned long> fileIds =
81  databaseHandler.queryFileIdsForUpload(uploadId,ignoreFilesWithMimeType);
82 
83  unordered_map<unsigned long, string> fileIdsMap;
84  unordered_map<string, unsigned long> fileIdsMapReverse;
85 
86  bool errors = false;
87 
88  char fileLocationTpl[] = "/tmp/scancode_input_XXXXXX";
89  int fdInput = mkstemp(fileLocationTpl);
90  if (fdInput == -1) {
91  LOG_ERROR("Failed to create temporary input file.\n");
92  return false;
93  }
94  close(fdInput);
95  string fileLocation(fileLocationTpl);
96 
97  char outputFileTpl[] = "/tmp/scancode_output_XXXXXX";
98  int fdOutput = mkstemp(outputFileTpl);
99  if (fdOutput == -1) {
100  LOG_ERROR("Failed to create temporary output file.\n");
101  unlink(fileLocationTpl);
102  return false;
103  }
104  close(fdOutput);
105  string outputFile(outputFileTpl);
106 
107  size_t pFileCount = fileIds.size();
108  for (size_t it = 0; it < pFileCount; ++it) {
109  unsigned long pFileId = fileIds[it];
110 
111  if (pFileId == 0)
112  continue;
113 
114  mapFileNameWithId(pFileId, fileIdsMap, fileIdsMapReverse, databaseHandler);
115 
117  }
118 
119  writeFileNameToTextFile(fileIdsMap, fileLocation);
120  scanFileWithScancode(state, fileLocation, outputFile);
121 
122  std::ifstream opfile(outputFile);
123  if (!opfile) {
124  LOG_ERROR("Error opening the JSON file.\n");
125  return false;
126  }
127 
128  vector<string> scanResults;
129  string line;
130  while (getline(opfile, line)) {
131  scanResults.push_back(getScanResult(line));
132  }
133 
134 #pragma omp parallel default(none) \
135  shared(databaseHandler, scanResults, fileIdsMapReverse, state, errors)
136  {
137  ScancodeDatabaseHandler threadLocalDatabaseHandler(databaseHandler.spawn());
138 #pragma omp for
139  for (size_t i = 0; i < scanResults.size(); ++i) {
140  // Process each object
141  Json::CharReaderBuilder json_reader_builder;
142  auto scanner = unique_ptr<Json::CharReader>(json_reader_builder.newCharReader());
143  Json::Value scancodeValue;
144  string errs;
145  const bool isSuccessful = scanner->parse(scanResults[i].c_str(),
146  scanResults[i].c_str() + scanResults[i].length(), &scancodeValue,
147  &errs);
148 
149  if (isSuccessful) {
150  string fileName = scancodeValue["file"].asString();
151  unsigned long fileId = 0; // preserve old behavior
152 
153  auto it = fileIdsMapReverse.find(fileName);
154  if (it != fileIdsMapReverse.end()) {
155  fileId = it->second;
156  }
157  if (!matchFileWithLicenses(state, threadLocalDatabaseHandler,
158  scanResults[i], fileName, fileId)) {
159  errors = true;
160  }
161  }
162  }
163  }
164  if (unlink(outputFile.c_str()) != 0) {
165  LOG_FATAL("Unable to delete file %s \n", outputFile.c_str());
166  }
167  if (unlink(fileLocation.c_str()) != 0) {
168  LOG_FATAL("Unable to delete file %s \n", fileLocation.c_str());
169  }
170 
171  return !errors;
172 }
173 
180 void mapFileNameWithId(unsigned long pFileId,
181  unordered_map<unsigned long, string> &fileIdsMap,
182  unordered_map<string, unsigned long> &fileIdsMapReverse,
183  ScancodeDatabaseHandler &databaseHandler) {
184  char *pFile = databaseHandler.getPFileNameForFileId(pFileId);
185  if (!pFile) {
186  LOG_FATAL("File not found %lu \n", pFileId);
187  bail(8);
188  }
189 
190  char *fileName = NULL;
191  {
192  fileName = fo_RepMkPath("files", pFile);
193  }
194  if (fileName) {
195  fo::File file(pFileId, fileName);
196 
197  fileIdsMap[file.getId()] = file.getFileName();
198  fileIdsMapReverse[file.getFileName()] = file.getId();
199 
200  free(fileName);
201  free(pFile);
202  } else {
203  LOG_FATAL("PFile not found in repo %lu \n", pFileId);
204  bail(7);
205  }
206 }
207 
212 void writeFileNameToTextFile(unordered_map<unsigned long, string> &fileIdsMap, string fileLocation) {
213  std::ofstream outputFile(fileLocation, std::ios::app); // Open in append mode
214 
215  if (!outputFile.is_open()) {
216  LOG_FATAL("Unable to open file");
217  }
218 
219  for (auto const& x : fileIdsMap)
220  {
221  outputFile << x.second <<"\n";
222  }
223 
224  outputFile.close();
225 }
226 
232 string getScanResult(const string& line) {
233  string scanResult;
234 
235  size_t startIndex = 0;
236  size_t braceCount = 0;
237 
238  for (size_t i = 0; i < line.length(); ++i) {
239  char c = line[i];
240 
241  if (c == '{') {
242  if (braceCount == 0) {
243  startIndex = i;
244  }
245  braceCount++;
246  } else if (c == '}') {
247  braceCount--;
248  if (braceCount == 0) {
249  scanResult = line.substr(startIndex, i - startIndex + 1);
250  break;
251  }
252  }
253  }
254  return scanResult;
255 }
256 
267 bool matchFileWithLicenses(const State &state,
268  ScancodeDatabaseHandler &databaseHandler,
269  string scancodeResult, string &filename, unsigned long fileId) {
270 map<string, vector<Match>> scancodeData =
271  extractDataFromScancodeResult(scancodeResult, filename);
272 return saveLicenseMatchesToDatabase(
273  state, scancodeData["scancode_license"], fileId,
274  databaseHandler) &&
275  saveOtherMatchesToDatabase(
276  state, scancodeData["scancode_statement"], fileId,
277  databaseHandler) &&
278  saveOtherMatchesToDatabase(
279  state, scancodeData["scancode_author"], fileId,
280  databaseHandler) &&
281  saveOtherMatchesToDatabase(
282  state, scancodeData["scancode_email"], fileId,
283  databaseHandler) &&
284  saveOtherMatchesToDatabase(
285  state, scancodeData["scancode_url"], fileId,
286  databaseHandler);
287 }
288 
302 bool saveLicenseMatchesToDatabase(const State &state,
303  const vector<Match> &matches,
304  unsigned long pFileId,
305  ScancodeDatabaseHandler &databaseHandler)
306  {
307  for (const auto & match : matches) {
308  databaseHandler.insertOrCacheLicenseIdForName(
309  match.getMatchName(), match.getLicenseFullName(), match.getTextUrl());
310  }
311 
312  if (!databaseHandler.begin()) {
313  return false;
314  }
315  for (const auto & match : matches) {
316  int agentId = state.getAgentId();
317  string rfShortname = match.getMatchName();
318  int percent = match.getPercentage();
319  unsigned start = match.getStartPosition();
320  unsigned length = match.getLength();
321  unsigned long licenseId =
322  databaseHandler.getCachedLicenseIdForName(rfShortname);
323 
324  if (licenseId == 0) {
325  databaseHandler.rollback();
326  LOG_ERROR("cannot get licenseId for shortname '%s' \n",
327  rfShortname.c_str());
328  return false;
329  }
330  if (rfShortname == "No_license_found") {
331  if (!databaseHandler.insertNoResultInDatabase(agentId, pFileId, licenseId)) {
332  databaseHandler.rollback();
333  LOG_ERROR("failing save licenseMatch \n");
334  return false;
335  }
336  } else {
337  long licenseFileId = databaseHandler.saveLicenseMatch(agentId, pFileId,
338  licenseId, percent);
339  if (licenseFileId > 0) {
340  bool highlightRes =
341  databaseHandler.saveHighlightInfo(licenseFileId, start, length);
342  if (!highlightRes) {
343  databaseHandler.rollback();
344  LOG_ERROR("failing save licensehighlight \n");
345  }
346  } else {
347  databaseHandler.rollback();
348  LOG_ERROR("failing save licenseMatch \n");
349  return false;
350  }
351  }
352  }
353  return databaseHandler.commit();
354 }
355 
364 bool saveOtherMatchesToDatabase(const State &state,
365  const vector<Match> &matches,
366  unsigned long pFileId,
367  ScancodeDatabaseHandler &databaseHandler) {
368 
369  if (!databaseHandler.begin())
370  return false;
371 
372  for (const auto & match : matches) {
373  DatabaseEntry entry(match,state.getAgentId(),pFileId);
374 
375  if (!databaseHandler.insertInDatabase(entry))
376  {
377  databaseHandler.rollback();
378  LOG_ERROR("failing save otherMatches \n");
379  return false;
380  }
381  }
382  return databaseHandler.commit();
383 }
384 
385 // clueI add in this command line parser
386 
395 bool parseCommandLine(int argc, char **argv, string &cliOption, bool &ignoreFilesWithMimeType)
396 {
397  po::options_description desc(AGENT_NAME ": available options");
398  desc.add_options()
399  ("help,h", "show this help")
400  ("ignoreFilesWithMimeType,I","ignoreFilesWithMimeType")
401  ("license,l", "scancode license")
402  ("copyright,r", "scancode copyright")
403  ("email,e", "scancode email")
404  ("url,u", "scancode url")
405  ("config,c", po::value<string>(), "path to the sysconfigdir")
406  ("scheduler_start", "specifies, that the command was called by the scheduler")
407  ("userID", po::value<int>(), "the id of the user that created the job (only in combination with --scheduler_start)")
408  ("groupID", po::value<int>(), "the id of the group of the user that created the job (only in combination with --scheduler_start)")
409  ("jobId", po::value<int>(), "the id of the job (only in combination with --scheduler_start)");
410  po::variables_map vm;
411  try
412  {
413  po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
414  if (vm.count("help") > 0)
415  {
416  cout << desc << "\n";
417  exit(EXIT_SUCCESS);
418  }
419  cliOption = "";
420  cliOption += vm.count("license") > 0 ? "l" : "";
421  cliOption += vm.count("copyright") > 0 ? "c" : "";
422  cliOption += vm.count("email") > 0 ? "e" : "";
423  cliOption += vm.count("url") > 0 ? "u" : "";
424  ignoreFilesWithMimeType =
425  vm.count("ignoreFilesWithMimeType") > 0 ? true : false;
426  }
427  catch (boost::bad_any_cast &)
428  {
429  LOG_FATAL("wrong parameter type\n ");
430  cout << desc << "\n";
431  return false;
432  }
433  catch (po::error &)
434  {
435  LOG_FATAL("wrong command line arguments\n");
436  cout << desc << "\n";
437  return false;
438  }
439  return true;
440 }
bool processUploadId(const CompatibilityState &state, int uploadId, CompatibilityDatabaseHandler &databaseHandler, int groupId)
CompatibilityState getState(DbManager &dbManager, CompatibilityCliOptions &&cliOptions)
Create a new state for the current agent based on CliOptions.
int writeARS(const CompatibilityState &state, int arsId, int uploadId, int success, DbManager &dbManager)
int queryAgentId(DbManager &dbManager)
Query and register the agent id. Bails on failure.
void bail(int exitval)
Disconnect scheduler and exit.
Maps agent data to database schema.
Definition: database.hpp:25
bool saveHighlightInfo(long licenseFileId, unsigned start, unsigned length)
save highlight information in the highlight table
unsigned long getCachedLicenseIdForName(std::string const &rfShortName) const
for given short name search license
long saveLicenseMatch(int agentId, long pFileId, long licenseId, int percentMatch)
save license match with license_ref table in license_file table Insert license if already not present...
bool insertInDatabase(DatabaseEntry &entry) const
insert copyright/author in scancode_copyright/scancode_author table
void insertOrCacheLicenseIdForName(std::string const &rfShortName, std::string const &rfFullname, std::string const &rfTexturl)
calling function for selectOrInsertLicenseIdForName
bool insertNoResultInDatabase(int agentId, long pFileId, long licenseId)
Insert null value of license for uploads having no licenses.
std::vector< unsigned long > queryFileIdsForUpload(int uploadId, bool ignoreFilesWithMimeType)
Function to get pfile ID for uploads.
ScancodeDatabaseHandler spawn() const
Instantiate a new object spawn for ScanCode Database handler Used to create new objects for threads.
Definition: state.hpp:16
int getAgentId() const
getter function for agent Id
Definition: state.cc:14
bool commit() const
COMMIT a transaction block in DB.
bool begin() const
BEGIN a transaction block in DB.
char * getPFileNameForFileId(unsigned long pfileId) const
Get the file name of a give pfile id.
bool rollback() const
ROLLBACK a transaction block in DB.
DB wrapper for agents.
Class to handle file related operations.
Definition: files.hpp:26
Abstract class to provide interface to scanners.
Definition: scanners.hpp:59
void matchFileWithLicenses(const string &sContent, unsigned long pFileId, CopyrightState const &state, int agentId, CopyrightDatabaseHandler &databaseHandler, int uploadId, const string &uploadTreeTableName)
Scan a given file with all available scanners and save findings to database.
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:214
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:158
char * fo_RepMkPath(const char *Type, char *Filename)
Given a filename, construct the full path to the file.
Definition: libfossrepo.c:352
void fo_scheduler_disconnect(int retcode)
Disconnect the scheduler connection.
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...
char * fo_sysconfig(const char *sectionname, const char *variablename)
gets a system configuration variable from the configuration data.
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
start($application)
start the application Assumes application is restartable via /etc/init.d/<script>....
Definition: pkgConfig.php:1214
Store the results of a regex match.
Definition: scanners.hpp:28