FOSSology  4.7.1
Open Source License Compliance by Open Source Software
snippet_scan.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0-only
26 #define _GNU_SOURCE
27 #include "snippet_scan.h"
28 #include <stdio.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33 
34 extern void logme(char *msg);
35 
46 static int run_scanoss_command(const char *pythonPath, const char *scanossPath,
47  const char *folder, const char *outputFile,
48  const char *apiurl, const char *key)
49 {
50  pid_t pid;
51  int status;
52 
53  pid = fork();
54 
55  if (pid < 0)
56  {
57  LOG_ERROR("Snippet scan: fork() failed: %s", strerror(errno));
58  return -1;
59  }
60 
61  if (pid == 0)
62  {
63 
64  /* Set PYTHONPATH environment variable */
65  if (setenv("PYTHONPATH", pythonPath, 1) != 0)
66  {
67  _exit(127);
68  }
69 
70  /* Build argument list for execv */
71  /* Maximum args: scanoss-py scan <folder> --format=csv -o <output> [--apiurl X] [--key Y] NULL */
72  char *args[12];
73  int argc = 0;
74 
75  args[argc++] = (char *)scanossPath;
76  args[argc++] = "scan";
77  args[argc++] = (char *)folder;
78  args[argc++] = "--format=csv";
79  args[argc++] = "-o";
80  args[argc++] = (char *)outputFile;
81 
82  if (apiurl != NULL && apiurl[0] != '\0')
83  {
84  args[argc++] = "--apiurl";
85  args[argc++] = (char *)apiurl;
86  }
87 
88  if (key != NULL && key[0] != '\0')
89  {
90  args[argc++] = "--key";
91  args[argc++] = (char *)key;
92  }
93 
94  args[argc] = NULL;
95 
96  execv(scanossPath, args);
97 
98  /* If execv returns, it failed */
99  _exit(127);
100  }
101 
102  /* Parent process - wait for child to complete */
103  if (waitpid(pid, &status, 0) < 0)
104  {
105  LOG_ERROR("Snippet scan: waitpid() failed: %s", strerror(errno));
106  return -1;
107  }
108 
109  if (WIFEXITED(status))
110  {
111  int exit_code = WEXITSTATUS(status);
112  if (exit_code != 0)
113  {
114  LOG_ERROR("Snippet scan: scanoss-py exited with code %d", exit_code);
115  return -1;
116  }
117  }
118  else if (WIFSIGNALED(status))
119  {
120  LOG_ERROR("Snippet scan: scanoss-py killed by signal %d", WTERMSIG(status));
121  return -1;
122  }
123 
124  return 0;
125 }
126 
127 extern char *baseTMP;
128 int Verbose = 0;
129 PGconn *db_conn = NULL;
130 extern int Agent_pk;
131 extern char ApiUrl[200];
132 extern char accToken[100];
133 
134 /***************************************************************************/
135 
136 int splitLine(char *lineToSplit, char *separator, char **fields)
137 {
138  int i = 0;
139 
140  char *token;
141 
142  char *strSplit = lineToSplit;
143  while ((token = strtok_r(strSplit, separator, &strSplit)))
144  sprintf(fields[i++], "%s", token);
145  return i;
146 }
147 
148 void extract_csv(char *out, char *in, int n, long limit, char sep)
149 {
150  char seps[3];
151  char line[2048];
152  sprintf(line, "%s", in);
153  sprintf(seps, "%c", sep);
154  char *token = strtok(line, seps);
155  // loop through the string to extract all other tokens
156  int count = 0;
157  while (token != NULL)
158  {
159  count++; // printf( " %s\n", token ); //printing each token
160  if (count == n)
161  {
162  sprintf(out, "%s", token);
163  break;
164  }
165  token = strtok(NULL, seps);
166  }
167 }
168 
174 FILE *openFileByKey(long pFileKey)
175 {
176  char sqlbuf[200];
177  PGresult *result;
178  sprintf(sqlbuf, "SELECT pfile_sha1 || '.' || pfile_md5 || '.' || pfile_size AS pfile_path FROM pfile WHERE pfile_pk = %ld;", pFileKey);
179  result = PQexec(db_conn, sqlbuf);
180  if (fo_checkPQresult(db_conn, result, sqlbuf, __FILE__, __LINE__))
181  {
182 
183  exit(-1);
184  }
185  char path[500];
186  sprintf(path, "%s", PQgetvalue(result, 0, 0));
187  return fo_RepFread("files", path);
188 }
189 
193 int getLicenseId(unsigned char *name)
194 {
195  PGresult *result;
196  char sqlbuf[500];
197  sprintf(sqlbuf, "select lr.rf_pk from license_ref lr where lr.rf_shortname like '%s';", name);
198  result = PQexec(db_conn, sqlbuf);
199 
200  if (fo_checkPQresult(db_conn, result, sqlbuf, __FILE__, __LINE__))
201  {
202  return -1;
203  }
204 
205  int num_rows = PQntuples(result);
206  int return_value = -1;
207  if (num_rows > 0)
208  {
209  const char *value = PQgetvalue(result, 0, 0);
210  if (value && *value)
211  {
212  return_value = atoi(value);
213  }
214  else
215  {
216  /* Found empty response */
217  return_value = -2;
218  }
219  }
220  else
221  {
222  /* No results found */
223  return_value = -3;
224  }
225 
226  PQclear(result);
227  return return_value;
228 }
229 
236 void dumpToFile(const char *path, unsigned char *content, long size)
237 {
238  FILE *fptr;
239  fptr = fopen(path, "w");
240 
241  if (fptr == NULL)
242  {
243  LOG_ERROR("Snippet scan: Could not create temp file")
244  }
245  else
246  {
247  fwrite(content, size, 1, fptr);
248  fclose(fptr);
249  }
250 }
251 
252 void RestoreTempFile(char *uploadFolder, long key, long realParent, char *realName)
253 {
254  char dstName[256];
255  FILE *f = openFileByKey(key); /* Open the file from the repository by its key */
256  sprintf(dstName, "%s/%ld_%ld_%s", uploadFolder, realParent, key, realName); /* Create a name for the temp file, including the temp folder */
257  if (f != NULL)
258  {
259  fseek(f, 0, SEEK_END); /* Read the file and dump the content into the temp file*/
260  int size = ftell(f);
261  unsigned char *contents = calloc(1, size);
262  memset(contents, '\0', size);
263  rewind(f);
264  size_t readSize = fread(contents, size, 1, f);
265  fo_RepFclose(f);
266  if (readSize == 0)
267  return;
268  dumpToFile(dstName, contents, size);
269  }
270 }
271 
272 /*1 inventory_id,
273  2 path,
274  3 detected_usage,
275  4 detected_component,
276  5 detected_license,
277  6 detected_version,
278  7 detected_latest,
279  8 detected_purls,
280  9 detected_url,
281  10 detected_match,
282  11 detected_lines,
283  12 detected_oss_lines,
284  13 detected_path
285 */
291 void ParseResults(char *folder)
292 {
293  PGresult *result;
294  char Cmd[100];
295  char auxSql[MAXCMD * 4];
296 
297  char detectedUsage[MAXCMD];
298  char detPurls[MAXCMD];
299  char detLicenses[MAXCMD];
300  char path[MAXCMD];
301  char detMatch[MAXCMD];
302  char detLines[MAXCMD];
303  char detPath[MAXCMD];
304  char detUrl[MAXCMD];
305 
306  sprintf(Cmd, "%s/results.csv", folder);
307  FILE *file = fopen(Cmd, "r");
308 
309  char line[MAXCMD * 10];
310  int resCount = 0;
311  if (file == NULL)
312  {
313  LOG_ERROR("Error while opening the file");
314  return;
315  }
316  while (fgets(line, sizeof(line), file))
317  {
318  if (resCount == 0)
319  {
320  resCount++;
321  continue;
322  } // Skip header
323  if (line[0] != 0 && line[0] != ' ')
324  {
325  long parent;
326  long key;
327  char srcName[MAXCMD];
328  extract_csv(path, line, 2, MAXCMD, ',');
329 
330  if (strlen(path)>0)
331  {
332  sscanf(path, "%ld_%ld_%s", &parent, &key, srcName);
333  }
334  else
335  {
336  return;
337  }
338  int lenLine = strlen(line);
339  extract_csv(detectedUsage, line, 3, lenLine, ',');
340  extract_csv(detPurls, line, 8, lenLine, ',');
341  extract_csv(detLicenses, line, 5, lenLine, ',');
342  extract_csv(detUrl, line, 9, lenLine, ',');
343 
344  extract_csv(detMatch, line, 10, lenLine, ',');
345  extract_csv(detLines, line, 12, lenLine, ',');
346  extract_csv(detPath, line, 13, lenLine, ',');
347 
348  // License store
349 
350  for (int i = 1; i < 5; i++)
351  {
352  char aux[100000];
353  if (strlen(detLicenses) > 1)
354  {
355  extract_csv(aux, detLicenses, i, strlen(detLicenses), ';');
356  if (strlen(aux)<=0)
357  break;
358  else
359  {
360 
361  int detLic = getLicenseId((unsigned char *)aux);
362  /* ... from name, get the key of license that matches short_name at license_ref*/
363  if (detLic > 0)
364  { /* If the key is valid, insert the result on DB Table */
365  sprintf(auxSql, "INSERT INTO license_file(rf_fk, agent_fk, rf_timestamp, pfile_fk) VALUES(%d,%d, now(), %ld);", detLic, Agent_pk, key);
366  result = PQexec(db_conn, auxSql);
367  }
368  else
369  {
370  // Unknown license
371  }
372  }
373  }
374  }
375 
376  // File info store
377  if (strcmp((char *)detectedUsage, "none") && (!(strcmp((char *)detectedUsage, "file")) || !(strcmp((char *)detectedUsage, "snippet"))))
378  {
379  char *auxSQL;
380  asprintf(&auxSQL, "INSERT INTO scanoss_fileinfo (pfile_fk, matchtype, lineranges, purl,filepath,url) VALUES(%ld, '%s', '%s', '%s','%s','%s');", key, detectedUsage, detLines, detPurls, detPath, detUrl); //,url,filePath);
381  result = PQexec(db_conn, auxSQL);
382  free(auxSQL);
383  if (PQntuples(result) == 0)
384  {
385  PQclear(result);
386  }
387  }
388  resCount++;
389  }
390  else
391  {
392  break;
393  }
394  }
395 }
396 
403 int ScanFolder(char *folder)
404 {
405  char pythonPath[512];
406  char scanossPath[512];
407  char outputFile[512];
408  char *apiurlPtr = NULL;
409  char *keyPtr = NULL;
410 
411  /* Get project user from config */
412  char *user = fo_config_get(sysconfig, "DIRECTORIES", "PROJECTUSER", NULL);
413  if (user == NULL)
414  {
415  LOG_ERROR("Snippet scan: PROJECTUSER not configured");
416  return -1;
417  }
418 
419  /* Build paths */
420  snprintf(pythonPath, sizeof(pythonPath), "/home/%s/pythondeps/", user);
421  snprintf(scanossPath, sizeof(scanossPath), "/home/%s/pythondeps/bin/scanoss-py", user);
422  snprintf(outputFile, sizeof(outputFile), "%s/results.csv", folder);
423 
424  /* Set API URL if configured */
425  if (ApiUrl[0] != '\0')
426  {
427  apiurlPtr = ApiUrl;
428  }
429 
430  /* Set API key if configured */
431  if (accToken[0] != '\0' && accToken[0] != ' ')
432  {
433  keyPtr = accToken;
434  }
435 
436  /* Log the command for debugging */
437  char logMsg[MAXCMD];
438  snprintf(logMsg, sizeof(logMsg), "Running scanoss-py scan on %s", folder);
439  logme(logMsg);
440 
441  /* Use the safe fork+exec function to avoid libcrypto crash (Issue #3109) */
442  int result = run_scanoss_command(pythonPath, scanossPath, folder, outputFile,
443  apiurlPtr, keyPtr);
444 
445  return result;
446 }
447 
448 int RebuildUpload(long upload_pk, char *tempFolder)
449 {
450 
451  char sqlbuf[1024];
452  PGresult *result;
453  int numrows;
454  int i;
455  char *uploadtree_tablename;
456 
457  if (!upload_pk) /* when upload_pk is empty */
458  {
459  LOG_ERROR("Snippet scan: Missing upload key");
460  return -1;
461  }
462 
464  if (NULL == uploadtree_tablename)
465  uploadtree_tablename = strdup("uploadtree_a");
466  /* retrieve the records to process */
467  snprintf(sqlbuf, sizeof(sqlbuf),
468  "SELECT * from uploadtree_a, upload where upload_fk = upload_pk and upload_pk = '%ld' ", upload_pk);
469  result = PQexec(db_conn, sqlbuf);
470  if (fo_checkPQresult(db_conn, result, sqlbuf, __FILE__, __LINE__))
471  {
472  LOG_ERROR("Snippet scan: Error retrieving jobs");
473  exit(-1);
474  }
475 
476  numrows = PQntuples(result);
477  long parent = 0;
478  long realParent = 0;
479  long fileMode = 0;
480  long pFileFK = 0;
481  char *realName;
482  /* for each record, get it name and real parent */
483  for (i = 0; i < numrows; i++)
484  {
485 
487  parent = atoi(PQgetvalue(result, i, 1));
488  realParent = atoi(PQgetvalue(result, i, 2));
489  fileMode = atol(PQgetvalue(result, i, 5));
490  asprintf(&realName, "%s", PQgetvalue(result, i, 8)); // 8 fileName
491  for (int j = 0; j < strlen(realName); j++)
492  {
493  if ((realName[j] >= 'A' && realName[j] <= 'Z') ||
494  (realName[j] >= 'a' && realName[j] <= 'z') ||
495  (realName[j] >= '0' && realName[j] <= '9') || realName[j] == '.')
496  ;
497  else
498  realName[j] = '_';
499  }
500  // Nothing to be done on folders entries
501  if (parent != realParent && (fileMode == ((1 << 28) | (1 << 13) | (1 << 9))))
502  {
503  }
504  else
505  {
506  // Ensure that it is a real file
507  // fileMode & ((1<<28)|(1<<13)|(1<<9)) == 0
508  if (fileMode != ((1 << 28) | (1 << 13) | (1 << 9)))
509  {
510  pFileFK = atoi(PQgetvalue(result, i, 4));
511  if (pFileFK != 0)
512  {
513  RestoreTempFile(tempFolder, pFileFK, parent, realName);
514  }
515  }
516  }
517  free(realName);
518  }
519  PQclear(result);
520 
521  return (0);
522 }
523 
524 /***********************************************
525  Usage():
526  Command line options allow you to write the agent so it works
527  stand alone, in addition to working with the scheduler.
528  This simplifies code development and testing.
529  So if you have options, have a Usage().
530  Here are some suggested options (in addition to the program
531  specific options you may already have).
532  ***********************************************/
533 void Usage(char *Name)
534 {
535  printf("Usage: %s [file|folder]\n", Name);
536  printf(" -i :: initialize the database, then exit.\n");
537  printf(" -v :: verbose (-vv = more verbose)\n");
538  printf(" -c :: Specify the directory for the system configuration.\n");
539  printf(" -C <file path/folder path> :: run from command line.\n");
540  printf(" -V :: print the version info, then exit.\n");
541 } /* Usage() */
char * uploadtree_tablename
upload.uploadtree_tablename
Definition: adj2nest.c:100
char * fo_config_get(fo_conf *conf, const char *group, const char *key, GError **error)
Gets an element based on its group name and key name. If the group or key is not found,...
Definition: fossconfig.c:336
FUNCTION char * GetUploadtreeTableName(PGconn *pgConn, int upload_pk)
Get the uploadtree table name for this upload_pk If upload_pk does not exist, return "uploadtree".
Definition: libfossagent.c:414
int fo_checkPQresult(PGconn *pgConn, PGresult *result, char *sql, char *FileID, int LineNumb)
Check the result status of a postgres SELECT.
Definition: libfossdb.c:170
int fo_RepFclose(FILE *F)
Perform an fclose.
Definition: libfossrepo.c:601
FILE * fo_RepFread(char *Type, char *Filename)
Perform an fopen for reading only.
Definition: libfossrepo.c:613
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...
fo_conf * sysconfig
void Usage(char *Name)
Say how to run this program.
Definition: snippet_scan.c:533
void dumpToFile(const char *path, unsigned char *content, long size)
Dumps the content of a file in the repository to a temporary file.
Definition: snippet_scan.c:236
PGconn * db_conn
The connection to Database.
Definition: snippet_scan.c:129
void ParseResults(char *folder)
Parse results from a temporary file and store results on database.
Definition: snippet_scan.c:291
int ScanFolder(char *folder)
Scans a Temporary folder.
Definition: snippet_scan.c:403
int Verbose
Verbose level.
Definition: snippet_scan.c:128
static int run_scanoss_command(const char *pythonPath, const char *scanossPath, const char *folder, const char *outputFile, const char *apiurl, const char *key)
Safely run scanoss-py command using fork+exec instead of popen.
Definition: snippet_scan.c:46
FILE * openFileByKey(long pFileKey)
Open a file of the repository given its primary key.
Definition: snippet_scan.c:174
int Agent_pk
agent identifier
Definition: finder.c:19
int getLicenseId(unsigned char *name)
Retrieves the license id (license_ref.rf_pk) given its short name.
Definition: snippet_scan.c:193
scanoss header
const char * upload_pk
Definition: sqlstatements.h:82