FOSSology  4.4.0
Open Source License Compliance by Open Source Software
adj2nest.c
Go to the documentation of this file.
1 /*
2  adj2nest: Convert adjacency list to nested sets.
3 
4  SPDX-FileCopyrightText: © 2007-2015 Hewlett-Packard Development Company, L.P.
5  SPDX-FileCopyrightText: © 2015 Siemens AG
6 
7  SPDX-License-Identifier: GPL-2.0-only
8 */
9 
66 #include <stdlib.h>
67 #include <stdio.h>
68 #include <unistd.h>
69 #include <string.h>
70 #include <ctype.h>
71 #include <signal.h>
72 #include <libgen.h>
73 
74 #include "libfossology.h"
75 
76 #define MAXCMD 4096
77 #define myBUFSIZ 2048
78 char SQL[256];
79 
80 #ifdef COMMIT_HASH_S
81 char BuildVersion[]="adj2nest build version: " VERSION_S " r(" COMMIT_HASH_S ").\n";
82 #else
83 char BuildVersion[]="adj2nest build version: NULL.\n";
84 #endif
85 
86 PGconn *pgConn = NULL;
87 
92 struct uploadtree
93 {
94  long UploadtreePk;
95  long Child;
96  long Sibling;
97 };
98 typedef struct uploadtree uploadtree;
99 uploadtree *Tree=NULL;
101 long TreeSize=0;
102 long TreeSet=0;
103 long SetNum=0;
104 int isBigUpload=0;
105 /************************************************************/
106 /************************************************************/
107 /************************************************************/
108 
114 void WalkTree (long Index, long Depth)
115 {
116  long LeftSet;
117  PGresult* pgResult;
118 
119  if (agent_verbose)
120  {
121  int i;
122  for(i=0; i<Depth; i++) printf(" ");
123  LOG_VERBOSE("%ld\n",Tree[Index].UploadtreePk);
124  }
125 
126  LeftSet = SetNum;
127  SetNum++;
128 
129  if (Tree[Index].Child > -1)
130  {
131  WalkTree(Tree[Index].Child,Depth+1);
132  SetNum++;
133  }
134 
135  snprintf(SQL,sizeof(SQL),"UPDATE %s SET lft='%ld', rgt='%ld' WHERE uploadtree_pk='%ld'",
136  uploadtree_tablename,LeftSet,SetNum,Tree[Index].UploadtreePk);
137  pgResult = PQexec(pgConn, SQL);
138  fo_checkPQcommand(pgConn, pgResult, SQL, __FILE__, __LINE__);
139  PQclear(pgResult);
141 
142  if (Tree[Index].Sibling > -1)
143  {
144  SetNum++;
145  WalkTree(Tree[Index].Sibling,Depth+1);
146  }
147 
148 } /* WalkTree() */
149 
156 void SetParent (long Parent, long Child)
157 {
158  long P;
159  static long LastParentId=-1;
160  static long LastParentIndex=-1;
161 
162  /* Insert the child */
163  Tree[TreeSet].UploadtreePk = Child;
164  TreeSet++;
165 
166  if (Parent == 0) /* ignore null parent */
167  {
168  return;
169  }
170 
171  /* Find the index of the parent */
172  if (Parent == LastParentId)
173  {
174  P = LastParentIndex;
175  }
176  else
177  {
178  P=0;
179  while((P<TreeSet) && (Tree[P].UploadtreePk != Parent)) { P++; }
180  if (P < TreeSet)
181  {
182  LastParentId = Parent;
183  LastParentIndex = P;
184  }
185  }
186 
187  if (P >= TreeSet)
188  {
189  /* Parent not found, so create it (right after the child). */
190  Tree[TreeSet].UploadtreePk = Parent;
191  Tree[TreeSet].Child = TreeSet-1;
192  LastParentId = Parent;
193  LastParentIndex = TreeSet;
194  TreeSet++;
195  return;
196  }
197 
198  /* Parent found, so follow the chain and add the child to the
199  end of the chain. */
200  if (Tree[P].Child < 0)
201  {
202  Tree[P].Child = TreeSet-1;
203  }
204  else
205  {
206  /* Already have a child so follow that child's sibling chain */
207  P=Tree[P].Child;
208  while(Tree[P].Sibling > -1) P=Tree[P].Sibling; /* find end of the chain */
209  Tree[P].Sibling = TreeSet-1;
210  }
211 } /* SetParent() */
212 
219 void LoadAdj (long UploadPk)
220 {
221  long i;
222  long Parent,Child;
223  long RootRows, NonRootRows;
224  PGresult* pgNonRootResult;
225  PGresult* pgRootResult;
226  PGresult* pgResult;
227  char LastChar;
228 
230 
231  /* If the last character of the uploadtree_tablename is a digit, run analyze */
232  LastChar = uploadtree_tablename[strlen(uploadtree_tablename)-1];
233  if (LastChar >= '0' && LastChar <= '9')
234  {
235  isBigUpload=1;
236  snprintf(SQL,sizeof(SQL),"ANALYZE %s",uploadtree_tablename);
237  pgResult = PQexec(pgConn, SQL);
238  fo_checkPQcommand(pgConn, pgResult, SQL, __FILE__ ,__LINE__);
239  PQclear(pgResult);
240  }
241 
242  snprintf(SQL,sizeof(SQL),"SELECT uploadtree_pk,parent FROM %s WHERE upload_fk = %ld AND parent IS NOT NULL ORDER BY parent, ufile_mode&(1<<29) DESC, ufile_name",uploadtree_tablename,UploadPk);
243  pgNonRootResult = PQexec(pgConn, SQL);
244  fo_checkPQresult(pgConn, pgNonRootResult, SQL, __FILE__, __LINE__);
245 
246  NonRootRows = PQntuples(pgNonRootResult);
247  TreeSize = NonRootRows;
248  LOG_VERBOSE("# Upload %ld: %ld items\n",UploadPk,TreeSize);
249 
250  snprintf(SQL,sizeof(SQL),"SELECT uploadtree_pk,parent FROM %s WHERE upload_fk = %ld AND parent IS NULL",uploadtree_tablename,UploadPk);
251  pgRootResult = PQexec(pgConn, SQL);
252  fo_checkPQresult(pgConn, pgRootResult, SQL, __FILE__, __LINE__);
253 
254  RootRows = PQntuples(pgRootResult);
255  TreeSize += RootRows;
256 
257  /* Got data! Populate the tree! */
258  if (Tree) { free(Tree); }
259  if (TreeSize <= 0) { Tree=NULL; return; }
260  Tree = (uploadtree *)calloc(TreeSize+1,sizeof(uploadtree));
261  for(i=0; i<TreeSize+1; i++)
262  {
263  Tree[i].UploadtreePk=-1;
264  Tree[i].Child=-1;
265  Tree[i].Sibling=-1;
266  }
267 
268  TreeSet=0;
269  SetNum=1;
270 
271  /* Load the roots */
272  for(i=0; i<RootRows; i++)
273  {
274  Child = atol(PQgetvalue(pgRootResult, i, 0));
275  Tree[TreeSet].UploadtreePk = Child;
276  TreeSet++;
277 
278  /* dummy heart to make sure the scheduler knows we are still alive */
279  if ((i % 100000) == 0) fo_scheduler_heart(0);
280  }
281 
282  /* Load all non-roots */
283  for(i=0; i<NonRootRows; i++)
284  {
285  Child = atol(PQgetvalue(pgNonRootResult,i,0));
286  Parent = atol(PQgetvalue(pgNonRootResult,i,1));
287  SetParent(Parent,Child);
288 
289  /* dummy heart to make sure the scheduler knows we are still alive */
290  if ((i % 100000) == 0) fo_scheduler_heart(0);
291  }
292 
293  /* Free up DB memory */
294  PQclear(pgNonRootResult);
295  PQclear(pgRootResult);
296  return;
297 } /* LoadAdj() */
298 
299 /*********************************************
300  Run on all uploads WHERE the upload
301  has no nested set numbers.
302  This displays each upload as it runs!
303  *********************************************/
304 void RunAllNew ()
305 {
306  int Row,MaxRow;
307  long UploadPk;
308  PGresult *pgResult;
309 
310  snprintf(SQL,sizeof(SQL), "SELECT DISTINCT upload_pk,upload_desc,upload_filename FROM upload WHERE upload_pk IN ( SELECT DISTINCT upload_fk FROM uploadtree WHERE lft IS NULL )");
311  pgResult = PQexec(pgConn, SQL);
312  fo_checkPQresult(pgConn, pgResult, SQL, __FILE__, __LINE__);
313 
314  MaxRow = PQntuples(pgResult);
315  for(Row=0; Row < MaxRow; Row++)
316  {
317  UploadPk = atol(PQgetvalue(pgResult,Row,0));
318  if (UploadPk >= 0)
319  {
320  char *S;
321  printf("Processing %ld :: %s",UploadPk,PQgetvalue(pgResult,Row,2));
322  S = PQgetvalue(pgResult,Row,1);
323  if (S && S[0]) printf(" (%s)",S);
324  printf("\n");
325  LoadAdj(UploadPk);
326  if (Tree) WalkTree(0,0);
327  if (Tree) free(Tree);
328  Tree=NULL;
329  TreeSize=0;
330  }
331  }
332  PQclear(pgResult);
333 } /* RunAllNew() */
334 
335 /*********************************************
336  ListUploads(): List every upload ID.
337  *********************************************/
338 void ListUploads ()
339 {
340  int Row,MaxRow;
341  long NewPid;
342  PGresult *pgResult;
343 
344  printf("# Uploads\n");
345  snprintf(SQL,sizeof(SQL), "SELECT upload_pk,upload_desc,upload_filename FROM upload ORDER BY upload_pk");
346  pgResult = PQexec(pgConn, SQL);
347  fo_checkPQresult(pgConn, pgResult, SQL, __FILE__, __LINE__);
348 
349  /* list each value */
350  MaxRow = PQntuples(pgResult);
351  for(Row=0; Row < MaxRow; Row++)
352  {
353  NewPid = atol(PQgetvalue(pgResult,Row,0));
354  if (NewPid >= 0)
355  {
356  char *S;
357  printf("%ld :: %s",NewPid,PQgetvalue(pgResult,Row,2));
358  S = PQgetvalue(pgResult,Row,1);
359  if (S && S[0]) printf(" (%s)",S);
360  printf("\n");
361  }
362  }
363  PQclear(pgResult);
364 } /* ListUploads() */
365 
366 
367 /************************************************************/
368 /************************************************************/
369 /************************************************************/
370 
371 /**********************************************
372  Given a string that contains
373  field='value' pairs, check if the field name
374  matches.
375  \param Field Haystack
376  \param S Needle
377  \return 1 on match, 0 on miss, -1 on no data.
378  **********************************************/
379 int MatchField (char *Field, char *S)
380 {
381  int Len;
382  if (!S || (S[0]=='\0')) return(-1);
383  while(isspace(S[0])) S++;
384  Len = strlen(Field);
385  if (!strncmp(Field,S,Len))
386  {
387  /* Matched string, now make sure it is a real match */
388  while (isspace(S[Len])) Len++;
389  if (S[Len] == '=') return (1);
390  }
391  return(0);
392 } /* MatchField() */
393 
394 /**********************************************
395  Given a string that contains
396  field='value' pairs, skip the first pair and
397  return the pointer to the next pair (or NULL if
398  end of string).
399  \param S field='value' pairs
400  \return Pointer to next pair
401  **********************************************/
402 char * SkipFieldValue (char *S)
403 {
404  char Quote;
405 
406  if (!S || (S[0]=='\0')) return(NULL);
407 
408  /* Skip the field */
409  while((S[0] != '\0') && (S[0]!='=')) S++; /* skip until the '=' is found */
410  if (S[0]=='\0') return(NULL);
411  S++; /* skip the '=' */
412  while(isspace(S[0])) S++; /* Skip any spaces */
413  if (S[0]=='\0') return(NULL);
414 
415  /* Now for the fun part... Skip the Value. This may be quoted. */
416  switch(S[0])
417  {
418  case '\"': case '\'':
419  Quote=S[0];
420  S++;
421  break;
422  default:
423  Quote=' ';
424  break;
425  }
426  while((S[0]!='\0') && (S[0]!=Quote))
427  {
428  if (S[0] == '\\') {
429  S += 2;
430  }
431  else S++;
432  }
433  if (S[0]==Quote) S++;
434  while(isspace(S[0])) S++; /* Skip any spaces */
435  return(S);
436 } /* SkipFieldValue() */
437 
438 /**********************************************
439  The scheduler taints field=value
440  pairs. Given a pair, return the untainted value.
441  NOTE: In string and out string CAN be the same string!
442  NOTE: strlen(Sout) is ALWAYS < strlen(Sin).
443  \param[in] Sin Tainted string
444  \param[out] Sout Untainted string
445  \return Untainted string or NULL if there is an error.
446  **********************************************/
447 char * UntaintValue (char *Sin, char *Sout)
448 {
449  char Quote;
450 
451  /* Skip the field */
452  while((Sin[0] != '\0') && (Sin[0]!='=')) Sin++; /* skip until the '=' is found */
453  if (Sin[0]=='\0') return(NULL);
454  Sin++; /* skip the '=' */
455  while(isspace(Sin[0])) Sin++; /* Skip any spaces */
456  if (Sin[0]=='\0') { Sout[0]='\0'; return(NULL); }
457 
458  /* The value may be inside quotes */
459  switch(Sin[0])
460  {
461  case '\"': case '\'':
462  Quote=Sin[0];
463  Sin++;
464  break;
465  default:
466  Quote=' ';
467  break;
468  }
469 
470  /* Now we're ready to untaint the value */
471  while((Sin[0]!='\0') && (Sin[0]!=Quote))
472  {
473  if (Sin[0] == '\\') {
474  Sin++; /* skip quote char */
475  if (Sin[0] == 'n') {
476  Sout[0] = '\n';
477  }
478  else if (Sin[0] == 'r') {
479  Sout[0] = '\r';
480  }
481  else if (Sin[0] == 'a') {
482  Sout[0] = '\a';
483  }
484  else {
485  Sout[0] = Sin[0];
486  }
487  Sout++;
488  Sin++; /* skip processed char */
489  }
490  else {
491  Sout[0] = Sin[0];
492  Sin++;
493  Sout++;
494  };
495  }
496  Sout[0]='\0'; /* terminate string */
497  return(Sout);
498 } /* UntaintValue() */
499 
500 /**********************************************
501  Convert field=value pairs into parameter.
502  This overwrites the parameter string!
503  The parameter is untainted from the scheduler.
504  \param ParmName field='value' pairs.
505  \param Parm Parameter to be set.
506  \returns 1 if Parm is set, 0 if not.
507  **********************************************/
508 int SetParm (char *ParmName, char *Parm)
509 {
510  int rc;
511  char *OldParm;
512  OldParm=Parm;
513  if (!ParmName || (ParmName[0]=='\0')) return(1); /* no change */
514  if (!Parm || (Parm[0]=='\0')) return(1); /* no change */
515 
516  /* Find the parameter */
517  while(!(rc=MatchField(ParmName,Parm)))
518  {
519  Parm = SkipFieldValue(Parm);
520  }
521  if (rc != 1) return(0); /* no match */
522 
523  /* Found it! Set the value */
524  UntaintValue(Parm,OldParm);
525  return(1);
526 } /* SetParm() */
527 
528 
536 int UpdateUpload(long UploadPk)
537 {
538  PGresult *pgResult;
539 
540  /* update upload.upload_mode to say that adj2nest was successful */
541  snprintf(SQL, sizeof(SQL), "UPDATE upload SET upload_mode = upload_mode | (1<<6) WHERE upload_pk='%ld'",
542  UploadPk);
543  pgResult = PQexec(pgConn, SQL); /* UPDATE upload */
544  if (fo_checkPQcommand(pgConn, pgResult, SQL, __FILE__ ,__LINE__)) return -1;
545  PQclear(pgResult);
546 
547  if(isBigUpload)
548  {
549  snprintf(SQL,sizeof(SQL),"VACUUM ANALYZE %s",uploadtree_tablename);
550  pgResult = PQexec(pgConn, SQL);
551  if (fo_checkPQcommand(pgConn, pgResult, SQL, __FILE__ ,__LINE__)) return -1;
552  PQclear(pgResult);
553  }
554  return(0);
555 }
556 
557 /*********************************************************
558  Usage of the agent
559  \param Name absolute path of the agent
560  *********************************************************/
561 void Usage (char *Name)
562 {
563  printf("Usage: %s [options] [id [id ...]]\n",Name);
564  printf(" -h :: help (print this message), then exit.\n");
565  printf(" -i :: initialize the database, then exit.\n");
566  printf(" -a :: run on ALL uploads that have no nested set records.\n");
567  printf(" -c SYSCONFDIR :: Specify the directory for the system configuration.\n");
568  printf(" -v :: verbose (-vv = more verbose).\n");
569  printf(" -u :: list all upload ids, then exit.\n");
570  printf(" no file :: process upload ids from the scheduler.\n");
571  printf(" id :: process upload ids from the command-line.\n");
572  printf(" -V :: print the version info, then exit.\n");
573 } /* Usage() */
574 
575 /*********************************************************/
576 int main (int argc, char *argv[])
577 {
578  int c, i, rv;
579  long UploadPk=-1;
580  long *uploads_to_scan;
581  int upload_count = 0;
582  int user_pk;
583  char *agent_desc = "Adj2nest Agent";
584  char *COMMIT_HASH;
585  char *VERSION;
586  char agent_rev[myBUFSIZ];
587 
588  /* connect to scheduler. Noop if not run from scheduler. */
589  fo_scheduler_connect(&argc, argv, &pgConn);
590 
591  COMMIT_HASH = fo_sysconfig("adj2nest", "COMMIT_HASH");
592  VERSION = fo_sysconfig("adj2nest", "VERSION");
593  sprintf(agent_rev, "%s.%s", VERSION, COMMIT_HASH);
594  /* Get the Agent Key from the DB */
595  fo_GetAgentKey(pgConn, basename(argv[0]), 0, agent_rev, agent_desc);
596 
597  /* for list of upload_pk's from the command line */
598  uploads_to_scan = calloc(argc, sizeof(long));
599 
600  /* Process command-line */
601  while((c = getopt(argc,argv,"aciuvVh")) != -1)
602  {
603  switch(c)
604  {
605  case 'a': /* run on ALL */
606  RunAllNew();
607  break;
608  case 'c':
609  break; /* handled by fo_scheduler_connect() */
610  case 'i':
611  PQfinish(pgConn);
612  return(0);
613  case 'v': agent_verbose++; break;
614  case 'u':
615  /* list ids */
616  ListUploads();
617  PQfinish(pgConn);
618  return(0);
619  case 'V':
620  printf("%s", BuildVersion);
621  PQfinish(pgConn);
622  return(0);
623  default:
624  Usage(argv[0]);
625  fflush(stdout);
626  PQfinish(pgConn);
627  exit(-1);
628  }
629  }
630 
631  /* Copy filename args (if any) into array */
632  for (i = optind; i < argc; i++)
633  {
634  uploads_to_scan[upload_count] = atol(argv[i]);
635  upload_count++;
636  }
637 
638  if (upload_count == 0)
639  {
640  user_pk = fo_scheduler_userID(); /* get user_pk for user who queued the agent */
641  while(fo_scheduler_next())
642  {
643  UploadPk = atol(fo_scheduler_current());
644 
645  /* Check Permissions */
646  if (GetUploadPerm(pgConn, UploadPk, user_pk) < PERM_WRITE)
647  {
648  LOG_ERROR("You have no update permissions on upload %ld", UploadPk);
649  continue;
650  }
651 
652  LoadAdj(UploadPk);
653  if (Tree) WalkTree(0,0);
654  if (Tree) free(Tree);
655  Tree=NULL;
656  TreeSize=0;
657  /* Update Upload */
658  rv = UpdateUpload(UploadPk);
659  if (rv == -1) LOG_ERROR("Unable to update mode on upload %ld", UploadPk);
660  } /* while() */
661  }
662  else
663  {
664  for (i = 0; i < upload_count; i++)
665  {
666  UploadPk = uploads_to_scan[i];
667  LoadAdj(UploadPk);
668  if (Tree) WalkTree(0,0);
669  if (Tree) free(Tree);
670  Tree=NULL;
671  TreeSize=0;
672  /* Update Upload */
673  rv = UpdateUpload(UploadPk);
674  if (rv == -1) LOG_ERROR("Unable to update mode on upload %ld", UploadPk);
675  }
676  free(uploads_to_scan);
677  }
678 
679  PQfinish(pgConn);
681  return 0;
682 } /* main() */
683 
void LoadAdj(long UploadPk)
Definition: adj2nest.c:219
void SetParent(long Parent, long Child)
Definition: adj2nest.c:156
long SetNum
Definition: adj2nest.c:103
int UpdateUpload(long UploadPk)
Finish updating the upload record and permissions data.
Definition: adj2nest.c:536
char SQL[256]
SQL query to execute.
Definition: adj2nest.c:78
PGconn * pgConn
Database connection.
Definition: adj2nest.c:86
long TreeSet
Definition: adj2nest.c:102
void WalkTree(long Index, long Depth)
Definition: adj2nest.c:114
char * uploadtree_tablename
upload.uploadtree_tablename
Definition: adj2nest.c:100
char BuildVersion[]
Definition: buckets.c:68
Usage()
Print Usage statement.
Definition: fo_dbcheck.php:63
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
FUNCTION int GetUploadPerm(PGconn *pgConn, long UploadPk, int user_pk)
Get users permission to this upload.
Definition: libfossagent.c:378
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
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_checkPQcommand(PGconn *pgConn, PGresult *result, char *sql, char *FileID, int LineNumb)
Check the result status of a postgres commands (not select) If an error occured, write the error to s...
Definition: libfossdb.c:204
The main FOSSology C library.
#define PERM_WRITE
Read-Write permission.
Definition: libfossology.h:33
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.
int agent_verbose
Common verbose flags for the agents, this is used so that the scheduler can change the verbose level ...
int fo_scheduler_userID()
Gets the id of the user that created the job that the agent is running.
char * fo_scheduler_current()
Get the last read string from the scheduler.
char * fo_scheduler_next()
Get the next data to process from the scheduler.
void fo_scheduler_connect(int *argc, char **argv, PGconn **db_conn)
Establish a connection between an agent and the scheduler.
Contains information required by uploadtree elements.
Definition: adj2nest.c:93
long UploadtreePk
Definition: adj2nest.c:94
long Sibling
Definition: adj2nest.c:96
long Child
Definition: adj2nest.c:95