FOSSology  4.4.0
Open Source License Compliance by Open Source Software
CompatibilityUtils.cc
Go to the documentation of this file.
1 /*
2  SPDX-FileCopyrightText: © 2024 Siemens AG
3 
4  SPDX-License-Identifier: GPL-2.0-only
5 */
11 #include "CompatibilityUtils.hpp"
12 
13 #include "CompatibilityAgent.hpp"
14 
15 #include <iostream>
16 
17 using namespace fo;
18 
27  CompatibilityCliOptions&& cliOptions)
28 {
29  int agentId = queryAgentId(dbManager);
30  return CompatibilityState(agentId, std::move(cliOptions));
31 }
32 
39 {
40  return CompatibilityState(-1, std::move(cliOptions));
41 }
42 
49 {
50  char* COMMIT_HASH = fo_sysconfig(AGENT_NAME, "COMMIT_HASH");
51  char* VERSION = fo_sysconfig(AGENT_NAME, "VERSION");
52  char* agentRevision;
53 
54  if (!asprintf(&agentRevision, "%s.%s", VERSION, COMMIT_HASH))
55  bail(-1);
56 
57  int agentId = fo_GetAgentKey(dbManager.getConnection(), AGENT_NAME, 0,
58  agentRevision, AGENT_DESC);
59  free(agentRevision);
60 
61  if (agentId <= 0)
62  bail(1);
63 
64  return agentId;
65 }
66 
76 int writeARS(const CompatibilityState& state, int arsId, int uploadId,
77  int success, DbManager& dbManager)
78 {
79  PGconn* connection = dbManager.getConnection();
80  int agentId = state.getAgentId();
81 
82  return fo_WriteARS(connection, arsId, uploadId, agentId, AGENT_ARS, NULL,
83  success);
84 }
85 
90 void bail(int exitval)
91 {
92  fo_scheduler_disconnect(exitval);
93  exit(exitval);
94 }
95 
104 bool processUploadId(const CompatibilityState& state, int uploadId,
105  CompatibilityDatabaseHandler& databaseHandler, int groupId)
106 {
107  vector<unsigned long> fileIds =
108  databaseHandler.queryFileIdsForScan(uploadId, state.getAgentId());
109  vector<unsigned long> agentIds = databaseHandler.queryScannerIdsForUpload
110  (uploadId);
111  auto mainLicenses = databaseHandler.queryMainLicenseForUpload(uploadId,
112  groupId);
113  bool errors = false;
114 
115 #pragma omp parallel default(none) \
116  shared(databaseHandler, fileIds, agentIds, state, errors, stdout, \
117  mainLicenses)
118  {
119  CompatibilityDatabaseHandler threadLocalDatabaseHandler(
120  databaseHandler.spawn());
121 
122  size_t pFileCount = fileIds.size();
123  CompatibilityAgent agentObj = state.getCompatibilityAgent();
124 #pragma omp for
125  for (size_t it = 0; it < pFileCount; ++it)
126  {
127  if (errors)
128  continue;
129 
130  unsigned long pFileId = fileIds[it];
131 
132  if (pFileId == 0)
133  continue;
134 
135  vector<unsigned long> licId =
136  threadLocalDatabaseHandler.queryLicIdsFromPfile(pFileId, agentIds);
137  if (!mainLicenses.empty())
138  {
139  set<unsigned long> licSet;
140  licSet.insert(licId.begin(), licId.end());
141  licSet.insert(mainLicenses.begin(), mainLicenses.end());
142 
143  licId.clear();
144  licId.insert(licId.end(), licSet.begin(), licSet.end());
145  }
146 
147  if (licId.size() < 2)
148  {
149  continue;
150  }
151 
152  bool identified;
153  try
154  {
155  identified = agentObj.checkCompatibilityForPfile(
156  licId, pFileId,
157  threadLocalDatabaseHandler); // pass licID vector, return the
158  // result(true or false)
159  }
160  catch (std::runtime_error& e)
161  {
162  LOG_FATAL("Unable to read %s.", e.what());
163  errors = true;
164  continue;
165  }
166 
167  if (!identified)
168  {
169  LOG_FATAL("Unable to store results in database for pfile %ld.",
170  pFileId);
171  bail(-20);
172  }
174  }
175  }
176 
177  return !errors;
178 }
179 
191 bool parseCliOptions(int argc, char** argv, CompatibilityCliOptions& dest,
192  std::string& types, std::string& rules, string& jFile,
193  string& mainLicense)
194 {
195  boost::program_options::options_description desc(AGENT_NAME
196  ": recognized options");
197  desc.add_options()
198  ("help,h", "shows help")
199  ("verbose,v", "increase verbosity")
200  ("file,f", boost::program_options::value<string>(),
201  "json file, containing fileNames and licenses within that fileNames")
202  ("json,J", "output as JSON")
203  ("main_license", boost::program_options::value<string>(),
204  "name of main license to check licenses in files against")
205  ("config,c", boost::program_options::value<string>(),
206  "path to the sysconfigdir")
207  ("scheduler_start",
208  "specifies, that the agent was called by the scheduler")
209  ("userID", boost::program_options::value<int>(),
210  "the id of the user that created the job (only in combination with "
211  "--scheduler_start)")
212  ("groupID", boost::program_options::value<int>(),
213  "the id of the group of the user that created the job (only in "
214  "combination with --scheduler_start)")
215  ("jobId", boost::program_options::value<int>(),
216  "the id of the job (only in combination with --scheduler_start)")
217  ("types,t", boost::program_options::value<string>(),
218  "license types for compatibility rules")
219  ("rules,r", boost::program_options::value<string>(),
220  "license compatibility rules");
221 
222  boost::program_options::positional_options_description p;
223  boost::program_options::variables_map vm;
224 
225  try
226  {
227  boost::program_options::store(
228  boost::program_options::command_line_parser(argc, argv)
229  .options(desc)
230  .positional(p)
231  .run(),
232  vm);
233 
234  if (vm.count("help") > 0)
235  {
236  cout << desc << '\n';
237  exit(0);
238  }
239 
240  if (vm.count("rules"))
241  {
242  rules = vm["rules"].as<std::string>();
243  }
244 
245  if (vm.count("types"))
246  {
247  types = vm["types"].as<std::string>();
248  }
249 
250  if (vm.count("file"))
251  {
252  jFile = vm["file"].as<std::string>();
253  }
254 
255  if (vm.count("main_license"))
256  {
257  mainLicense = vm["main_license"].as<std::string>();
258  }
259 
260  int verbosity = (int) vm.count("verbose");
261  bool json = vm.count("json") > 0;
262 
263  dest = CompatibilityCliOptions(verbosity, json);
264 
265  return true;
266  }
267  catch (boost::bad_any_cast&)
268  {
269  cout << "wrong parameter type\n";
270  cout << desc << '\n';
271  return false;
272  }
273  catch (boost::program_options::error&)
274  {
275  cout << "wrong command line arguments\n";
276  cout << desc << '\n';
277  return false;
278  }
279 }
280 
289 void appendToJson(const std::vector<tuple<string, string, bool>>& resultPair,
290  const std::string& fileName, bool& printComma)
291 {
292  Json::Value result;
293 #if JSONCPP_VERSION_HEXA < ((1 << 24) | (4 << 16))
294  // Use FastWriter for versions below 1.4.0
295  Json::FastWriter jsonBuilder;
296 #else
297  // Since version 1.4.0, FastWriter is deprecated and replaced with
298  // StreamWriterBuilder
299  Json::StreamWriterBuilder jsonBuilder;
300  jsonBuilder["commentStyle"] = "None";
301  jsonBuilder["indentation"] = "";
302 #endif
303  // jsonBuilder.omitEndingLineFeed();
304  Json::Value licenses;
305  Json::Value res(Json::arrayValue);
306  for (const auto& i : resultPair)
307  {
308  Json::Value license(Json::arrayValue);
309  Json::Value comp;
310  Json::Value final;
311  license.append(get<0>(i));
312  license.append(get<1>(i));
313  comp = get<2>(i);
314  final["license"] = license;
315  final["compatibility"] = comp;
316  res.append(final);
317  }
318 
319  if (fileName != "null")
320  {
321  result["file"] = fileName;
322  result["results"] = res;
323  }
324  else
325  {
326  result["package-level-result"] = res;
327  }
328 
329  // Thread-Safety: output all matches JSON at once to STDOUT
330 #pragma omp critical(jsonPrinter)
331  {
332  if (printComma)
333  {
334  cout << ",\n";
335  }
336  else
337  {
338  printComma = true;
339  }
340  string jsonString;
341 #if JSONCPP_VERSION_HEXA < ((1 << 24) | (4 << 16))
342  // For version below 1.4.0, every writer append `\n` at end.
343  // Find and replace it.
344  jsonString = jsonBuilder.write(result);
345  jsonString.replace(jsonString.find("\n"), string("\n").length(), "");
346 #else
347  // For version >= 1.4.0, \n is not appended.
348  jsonString = Json::writeString(jsonBuilder, result);
349 #endif
350  cout << " " << jsonString;
351  }
352 }
353 
361  const std::vector<tuple<string, string, bool>>& resultPair,
362  const std::string& fileName)
363 {
364  stringstream ss;
365  if (fileName != "null")
366  {
367  cout << "----" << fileName << "----\n";
368  for (const auto& i : resultPair)
369  {
370  string result = get<2>(i) ? "true" : "false";
371  cout << get<0>(i) << "," << get<1>(i) << "::" << result << '\n';
372  }
373  }
374  else
375  {
376  cout << "----all licenses with their compatibility----\n";
377  for (const auto& i : resultPair)
378  {
379  string result = get<2>(i) ? "true" : "false";
380  cout << get<0>(i) << "," << get<1>(i) << "::" << result << '\n';
381  }
382  }
383 }
384 
390 std::set<std::string> mainLicenseToSet(const string& mainLicense)
391 {
392  std::set<std::string> licenses;
393  std::string delimiter = " AND ";
394  std::string s = mainLicense;
395  size_t pos;
396 
397  while ((pos = s.find(delimiter)) != std::string::npos) {
398  licenses.insert(s.substr(0, pos));
399  s.erase(0, pos + delimiter.length());
400  }
401  if (licenses.empty() && !s.empty()) {
402  licenses.insert(mainLicense);
403  }
404  else if (! s.empty())
405  {
406  // Insert the last element from list
407  licenses.insert(s.substr(0, s.length()));
408  }
409  return licenses;
410 }
411 
421 bool are_licenses_compatible(const string& first_name,
422  const string& first_type, const string& second_name,
423  const string& second_type,
424  const map<tuple<string, string, string, string>, bool>& rule_list)
425 {
426  auto result_check =
427  rule_list.find(make_tuple(first_name, "", second_name, ""));
428  if (result_check == rule_list.end())
429  {
430  result_check = rule_list.find(make_tuple(second_name, "", first_name, ""));
431  }
432  if (result_check != rule_list.end())
433  {
434  return result_check->second;
435  }
436  result_check = rule_list.find(make_tuple("", first_type, "", second_type));
437  if (result_check == rule_list.end())
438  {
439  result_check = rule_list.find(make_tuple("", second_type, "", first_type));
440  }
441  if (result_check != rule_list.end())
442  {
443  return result_check->second;
444  }
445  result_check = rule_list.find(make_tuple(first_name, "", "", second_type));
446  if (result_check == rule_list.end())
447  {
448  result_check = rule_list.find(make_tuple(second_name, "", "", first_type));
449  }
450  if (result_check == rule_list.end())
451  {
452  result_check = rule_list.find(make_tuple("", first_type, second_name, ""));
453  }
454  if (result_check == rule_list.end())
455  {
456  result_check = rule_list.find(make_tuple("", second_type, first_name, ""));
457  }
458  if (result_check != rule_list.end())
459  {
460  return result_check->second;
461  }
462  auto default_value = rule_list.find(make_tuple("~", "~", "~", "~"));
463  if (default_value == rule_list.end())
464  {
465  return false;
466  }
467  else
468  {
469  return default_value->second;
470  }
471 }
472 
482 int get_column_ids(const string& header, int& name_col, int& type_col)
483 {
484  stringstream lineStream(header);
485  string cell;
486  int i = 0;
487  while(getline(lineStream, cell, ','))
488  {
489  string::size_type start = cell.find_first_not_of(' ');
490  string::size_type end = cell.find_last_not_of(' ');
491 
492  cell = cell.substr(start, end - start + 1);
493 
494  if (cell == "shortname")
495  {
496  name_col = i;
497  }
498  else if (cell == "licensetype")
499  {
500  type_col = i;
501  }
502  i++;
503  }
504  if (name_col == -1)
505  {
506  return -1;
507  }
508  else if (type_col == -1)
509  {
510  return -2;
511  }
512  return 1;
513 }
514 
521 unordered_map<string, string> initialize_license_map(
522  const string& file_location)
523 {
524  std::ifstream ip(file_location);
525  string line, name, type;
526  unordered_map<string, string> license_type_map;
527  int name_column = -1, type_column = -1;
528  getline(ip, line);
529  int retval = get_column_ids(line, name_column, type_column);
530  if (retval != 1)
531  {
532  throw invalid_argument("missing header `shortname` and/or `licensetype` "
533  "from CSV file.");
534  }
535  while (getline(ip, line)) // parsing the csv file
536  {
537  stringstream ss(line);
538  string cell;
539  int i = 0;
540  while (getline(ss, cell, ','))
541  {
542  if (i == name_column)
543  {
544  name = cell;
545  }
546  if (i == type_column)
547  {
548  type = cell;
549  }
550  i++;
551  }
552  license_type_map[name] = type;
553  }
554  return license_type_map;
555 }
556 
567 map<tuple<string, string, string, string>, bool> initialize_rule_list(
568  const string& file_location)
569 {
570  YAML::Node root = YAML::LoadFile(file_location);
571  map<tuple<string, string, string, string>, bool> rule_map;
572 
573  for (const auto& yml_rule : root["rules"]) // iterating the yml.rules
574  {
575  string first_type, second_type, first_name, second_name;
576  bool ans = false;
577  for (const auto& tag : yml_rule)
578  {
579  string first = tag.first.as<string>();
580  if (first == "comment")
581  {
582  continue;
583  }
584  string second;
585  if (tag.second.IsNull())
586  {
587  second = "~";
588  }
589  else
590  {
591  second = tag.second.as<string>();
592  }
593  if (first == "compatibility")
594  {
595  ans = second == "true";
596  }
597  else if (first == "firsttype" && (second != "~"))
598  {
599  first_type = second;
600  }
601  else if (first == "secondtype" && (second != "~"))
602  {
603  second_type = second;
604  }
605  else if (first == "firstname" && (second != "~"))
606  {
607  first_name = second;
608  }
609  else if (first == "secondname" && (second != "~"))
610  {
611  second_name = second;
612  }
613  }
614  rule_map[make_tuple(first_name, first_type, second_name, second_type)] = ans;
615  }
616  rule_map[make_tuple("~", "~", "~", "~")] =
617  root["default"].as<string>() == "true";
618  return rule_map;
619 }
map< tuple< string, string, string, string >, bool > initialize_rule_list(const string &file_location)
Read YAML file of rules and parse it as map.
int get_column_ids(const string &header, int &name_col, int &type_col)
Get the column index for shortname and licensetype columns from CSV header.
unordered_map< string, string > initialize_license_map(const string &file_location)
Parse license type CSV and create a map.
bool processUploadId(const CompatibilityState &state, int uploadId, CompatibilityDatabaseHandler &databaseHandler, int groupId)
bool parseCliOptions(int argc, char **argv, CompatibilityCliOptions &dest, std::string &types, std::string &rules, string &jFile, string &mainLicense)
Parse the options sent by CLI to CliOptions object.
void printResultToStdout(const std::vector< tuple< string, string, bool >> &resultPair, const std::string &fileName)
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)
std::set< std::string > mainLicenseToSet(const string &mainLicense)
void appendToJson(const std::vector< tuple< string, string, bool >> &resultPair, const std::string &fileName, bool &printComma)
void bail(int exitval)
bool are_licenses_compatible(const string &first_name, const string &first_type, const string &second_name, const string &second_type, const map< tuple< string, string, string, string >, bool > &rule_list)
Check the licenses against rules and get the compatibility result.
bool checkCompatibilityForPfile(vector< unsigned long > &licId, unsigned long &pFileId, CompatibilityDatabaseHandler &databaseHandler) const
find the compatibility between the licenses using scheduler mode
Store the options sent through the CLI.
std::vector< unsigned long > queryScannerIdsForUpload(int uploadId)
Get the agent id of latest scanners run on the upload.
std::vector< unsigned long > queryMainLicenseForUpload(int uploadId, int groupId)
Get the main licenses for the upload.
std::vector< unsigned long > queryLicIdsFromPfile(unsigned long pFileId, vector< unsigned long > agentIds)
to get the license id from the file id
std::vector< unsigned long > queryFileIdsForScan(int uploadId, int agentId)
CompatibilityDatabaseHandler spawn() const
Store the state of the agent.
const CompatibilityAgent & getCompatibilityAgent() const
DB wrapper for agents.
int s
The socket that the CLI will use to communicate.
Definition: fo_cli.c:37
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
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
fo namespace holds the FOSSology library functions.
start($application)
start the application Assumes application is restartable via /etc/init.d/<script>....
Definition: pkgConfig.php:1214
int tag[15]
Definition: pkgagent.c:25
Definition: nomos.h:426