FOSSology  4.5.1
Open Source License Compliance by Open Source Software
ununpack.c
Go to the documentation of this file.
1 /*
2  Ununpack: The universal unpacker.
3 
4  SPDX-FileCopyrightText: © 2007-2013 Hewlett-Packard Development Company, L.P.
5  SPDX-FileCopyrightText: © 2015,2023 Siemens AG
6  SPDX-FileContributor: Kaushlendra Pratap <kaushlendra-pratap.singh@siemens.com>
7 
8 
9  SPDX-License-Identifier: GPL-2.0-only
10 */
102 #define _GNU_SOURCE
103 #include "ununpack.h"
104 #include "ununpack_globals.h"
105 #include <gcrypt.h>
106 
107 #ifdef COMMIT_HASH_S
108 char BuildVersion[]="ununpack build version: " VERSION_S " r(" COMMIT_HASH_S ").\n";
109 #else
110 char BuildVersion[]="ununpack build version: NULL.\n";
111 #endif
112 
113 /***********************************************************************/
114 int main(int argc, char *argv[])
115 {
116  int Pid;
117  int c;
118  int rvExist1=0, rvExist2=0;
119  PGresult *result;
120  char *NewDir=".";
121  char *AgentName = "ununpack";
122  char *AgentARSName = "ununpack_ars";
123  char *agent_desc = "Unpacks archives (iso, tar, etc)";
124  int Recurse=0;
125  int ars_pk = 0;
126  int user_pk = 0;
127  long Pfile_size = 0;
128  char *ListOutName=NULL;
129  char *Fname = NULL;
130  char *FnameCheck = NULL;
131  char *COMMIT_HASH;
132  char *VERSION;
133  char agent_rev[PATH_MAX];
134  struct stat Stat;
135  char *ExcludePatterns = NULL;
136 
137  /* connect to the scheduler */
138  fo_scheduler_connect(&argc, argv, &pgConn);
139 
140  while((c = getopt(argc,argv,"ACc:d:FfHhL:m:PQiIqRr:T:t:U:VvXxE:")) != -1)
141  {
142  switch(c)
143  {
144  case 'A': SetContainerArtifact=0; break;
145  case 'C': ForceContinue=1; break;
146  case 'c': break; /* handled by fo_scheduler_connect() */
147  case 'd':
148  /* if there is a %U in the path, substitute a unique ID */
149  NewDir=PathCheck(optarg);
150  break;
151  case 'F': UseRepository=1; break;
152  case 'f': ForceDuplicate=1; break;
153  case 'L': ListOutName=optarg; break;
154  case 'm':
155  MaxThread = atoi(optarg);
156  if (MaxThread < 1) MaxThread=1;
157  break;
158  case 'P': PruneFiles=1; break;
159  case 'R': Recurse=-1; break;
160  case 'r': Recurse=atoi(optarg); break;
161  case 'i':
162  if (!IsExe("dpkg-source",Quiet))
163  LOG_WARNING("dpkg-source is not available on this system. This means that debian source packages will NOT be unpacked.");
164  SafeExit(0);
165  break; /* never reached */
166  case 'I': IgnoreSCMData=1; break;
167  case 'Q':
168  UseRepository=1;
169 
170  user_pk = fo_scheduler_userID(); /* get user_pk for user who queued the agent */
171 
172  /* Get the upload_pk from the scheduler */
173  if((Upload_Pk = fo_scheduler_next()) == NULL) SafeExit(0);
174  break;
175  case 'q': Quiet=1; break;
176  case 'T':
177  memset(REP_GOLD,0,sizeof(REP_GOLD));
178  strncpy(REP_GOLD,optarg,sizeof(REP_GOLD)-1);
179  break;
180  case 't':
181  memset(REP_FILES,0,sizeof(REP_FILES));
182  strncpy(REP_FILES,optarg,sizeof(REP_FILES)-1);
183  break;
184  case 'U':
185  UseRepository = 1;
186  Recurse = -1;
187  Upload_Pk = optarg;
188  break;
189  case 'V': printf("%s", BuildVersion);SafeExit(0);
190  case 'v': Verbose++; break;
191  case 'X': UnlinkSource=1; break;
192  case 'x': UnlinkAll=1; break;
193  case 'E': ExcludePatterns = optarg; break;
194  default:
195  Usage(argv[0], BuildVersion);
196  SafeExit(25);
197  }
198  }
199 
200  // Only print exclude patterns if actually set and not empty
201  if (ExcludePatterns && strlen(ExcludePatterns) > 0) {
202  if (Verbose) LOG_DEBUG("Excluding patterns: %s", ExcludePatterns);
203  } else {
204  ExcludePatterns = NULL;
205  }
206 
207  /* Initialize gcrypt and disable security memory */
208  gcry_check_version(NULL);
209  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
210  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
211 
212  /* Open DB and Initialize CMD table */
213  if (UseRepository)
214  {
215  /* Check Permissions */
216  if (GetUploadPerm(pgConn, atoi(Upload_Pk), user_pk) < PERM_WRITE)
217  {
218  LOG_ERROR("You have no update permissions on upload %s", Upload_Pk);
219  SafeExit(100);
220  }
221 
222  COMMIT_HASH = fo_sysconfig(AgentName, "COMMIT_HASH");
223  VERSION = fo_sysconfig(AgentName, "VERSION");
224  sprintf(agent_rev, "%s.%s", VERSION, COMMIT_HASH);
225  /* Get the unpack agent key */
226  agent_pk = fo_GetAgentKey(pgConn, AgentName, atoi(Upload_Pk), agent_rev,agent_desc);
227 
228  InitCmd();
229 
230  /* Make sure ars table exists */
231  if (!fo_CreateARSTable(pgConn, AgentARSName)) SafeExit(0);
232 
233  /* Has this user previously unpacked this upload_pk successfully?
234  * In this case we are done. No new ars record is needed since no
235  * processing is initiated.
236  * The unpack version is ignored.
237  */
238  snprintf(SQL,MAXSQL,
239  "SELECT ars_pk from %s where upload_fk='%s' and ars_success=TRUE",
240  AgentARSName, Upload_Pk);
241  result = PQexec(pgConn, SQL);
242  if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__)) SafeExit(101);
243 
244  if (PQntuples(result) > 0) /* if there is a value */
245  {
246  PQclear(result);
247  LOG_WARNING("Upload_pk %s, has already been unpacked. No further action required",
248  Upload_Pk)
249  SafeExit(0);
250  }
251  PQclear(result);
252 
253  /* write the unpack_ars start record */
254  ars_pk = fo_WriteARS(pgConn, ars_pk, atoi(Upload_Pk), agent_pk, AgentARSName, 0, 0);
255 
256  /* Get Pfile path and Pfile_Pk, from Upload_Pk */
257  snprintf(SQL,MAXSQL,
258  "SELECT pfile.pfile_sha1 || '.' || pfile.pfile_md5 || '.' || pfile.pfile_size AS pfile, pfile_fk, pfile_size FROM upload INNER JOIN pfile ON upload.pfile_fk = pfile.pfile_pk WHERE upload.upload_pk = '%s'",
259  Upload_Pk);
260  result = PQexec(pgConn, SQL);
261  if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__)) SafeExit(102);
262 
263  if (PQntuples(result) > 0) /* if there is a value */
264  {
265  Pfile = strdup(PQgetvalue(result,0,0));
266  Pfile_Pk = strdup(PQgetvalue(result,0,1));
267  Pfile_size = atol(PQgetvalue(result, 0, 2));
268  if (Pfile_size == 0)
269  {
270  PQclear(result);
271  LOG_WARNING("Uploaded file (Upload_pk %s), is zero length. There is nothing to unpack.",
272  Upload_Pk)
273  SafeExit(0);
274  }
275 
276  PQclear(result);
277  }
278 
279  // Determine if uploadtree records should go into a separate table.
280  // If the input file size is > 500MB, then create a separate uploadtree_{upload_pk} table
281  // that inherits from the master uploadtree table.
282  // Save uploadtree_tablename, it will get written to upload.uploadtree_tablename later.
283  if (Pfile_size > 500000000)
284  {
285  sprintf(uploadtree_tablename, "uploadtree_%s", Upload_Pk);
287  {
288  snprintf(SQL,MAXSQL,"CREATE TABLE %s (LIKE uploadtree INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES); ALTER TABLE %s ADD CONSTRAINT %s CHECK (upload_fk=%s); ALTER TABLE %s INHERIT uploadtree",
290  PQsetNoticeProcessor(pgConn, SQLNoticeProcessor, SQL); // ignore notice about implicit primary key index creation
291  result = PQexec(pgConn, SQL);
292  // Ignore postgres notice about creating an implicit index
293  if (PQresultStatus(result) != PGRES_NONFATAL_ERROR)
294  if (fo_checkPQcommand(pgConn, result, SQL, __FILE__, __LINE__)) SafeExit(103);
295  PQclear(result);
296  }
297  }
298  else
299  strcpy(uploadtree_tablename, "uploadtree_a");
300  }
301 
303  if (NewDir) MkDir(NewDir);
304  if (Verbose) { fclose(stderr) ; stderr=stdout; } /* don't interlace! */
305  if (ListOutName != NULL)
306  {
307  if ((ListOutName[0]=='-') && (ListOutName[1]=='\0'))
308  ListOutFile=stdout;
309  else ListOutFile = fopen(ListOutName,"w");
310  if (!ListOutFile)
311  {
312  LOG_ERROR("pfile %s Unable to write to %s\n",Pfile_Pk,ListOutName)
313  SafeExit(104);
314  }
315  else
316  {
317  /* Start the file */
318  fputs("<xml tool=\"ununpack\" ",ListOutFile);
319  fputs("version=\"",ListOutFile);
320  fputs(Version,ListOutFile);
321  fputs("\" ",ListOutFile);
322  fputs("compiled_date=\"",ListOutFile);
323  fputs(__DATE__,ListOutFile);
324  fputs(" ",ListOutFile);
325  fputs(__TIME__,ListOutFile);
326  fputs("\"",ListOutFile);
327  fputs(">\n",ListOutFile);
328  }
329  /* Problem: When parallel processing, the XML may be generated out
330  of order. Solution? When using XML, only use 1 thread. */
331  MaxThread=1;
332  }
333 
334  // Set ReunpackSwitch if the uploadtree records are missing from the database.
336  {
337  snprintf(SQL,MAXSQL,"SELECT uploadtree_pk FROM uploadtree WHERE upload_fk=%s limit 1;",Upload_Pk);
338  result = PQexec(pgConn, SQL);
339  if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__)) SafeExit(105);
340  if (PQntuples(result) == 0) ReunpackSwitch=1;
341  PQclear(result);
342  }
343 
344  /*** process files from command line ***/
345  for( ; optind<argc; optind++)
346  {
347  CksumFile *CF=NULL;
348  Cksum *Sum;
349  int i;
350  if (Fname) { free(Fname); Fname=NULL; }
351  if (ListOutName != NULL)
352  {
353  fprintf(ListOutFile,"<source source=\"%s\" ",argv[optind]);
354  if (UseRepository && !fo_RepExist(REP_FILES,argv[optind]))
355  {
356  /* make sure the source exists in the src repository */
357  if (fo_RepImport(argv[optind],REP_FILES,argv[optind],1) != 0)
358  {
359  LOG_ERROR("Failed to import '%s' as '%s' into the repository",argv[optind],argv[optind])
360  SafeExit(106);
361  }
362  }
363  }
364 
365  if (UseRepository)
366  {
367  if (fo_RepExist(REP_FILES,argv[optind]))
368  {
369  Fname=fo_RepMkPath(REP_FILES,argv[optind]);
370  }
371  else if (fo_RepExist(REP_GOLD,argv[optind]))
372  {
373  Fname=fo_RepMkPath(REP_GOLD,argv[optind]);
374  if (fo_RepImport(Fname,REP_FILES,argv[optind],1) != 0)
375  {
376  LOG_ERROR("Failed to import '%s' as '%s' into the repository",Fname,argv[optind])
377  SafeExit(107);
378  }
379  }
380 
381  if (Fname)
382  {
383  FnameCheck = Fname;
384  CF = SumOpenFile(Fname);
385  }
386  else
387  {
388  LOG_ERROR("NO file unpacked. File %s does not exist either in GOLD or FILES", Pfile);
389  SafeExit(108);
390  }
391  /* else: Fname is NULL and CF is NULL */
392  }
393  else
394  {
395  FnameCheck = argv[optind];
396  CF = SumOpenFile(argv[optind]);
397  }
398 
399  /* Check file to unpack. Does it exist? Is it zero length? */
400  if (stat(FnameCheck,&Stat))
401  {
402  LOG_ERROR("File to unpack is unavailable: %s, error: %s", Fname, strerror(errno));
403  SafeExit(109);
404  }
405  else
406  if (Stat.st_size < 1)
407  {
408  LOG_WARNING("File to unpack is empty: %s", Fname);
409  SafeExit(110);
410  }
411 
412  if (ListOutFile)
413  {
414  if (CF)
415  {
416  Sum = SumComputeBuff(CF);
417  SumCloseFile(CF);
418  if (Sum)
419  {
420  fputs("fuid=\"",ListOutFile);
421  for(i=0; i<20; i++)
422  { fprintf(ListOutFile,"%02X",Sum->SHA1digest[i]); }
423  fputs(".",ListOutFile);
424  for(i=0; i<16; i++)
425  { fprintf(ListOutFile,"%02X",Sum->MD5digest[i]); }
426  fputs(".",ListOutFile);
427  fprintf(ListOutFile,"%Lu",(long long unsigned int)Sum->DataLen);
428  fputs("\" ",ListOutFile);
429  free(Sum);
430  } /* if Sum */
431  } /* if CF */
432  else /* file too large to mmap (probably) */
433  {
434  FILE *Fin;
435  Fin = fopen(argv[optind],"rb");
436  if (Fin)
437  {
438  Sum = SumComputeFile(Fin);
439  if (Sum)
440  {
441  fputs("fuid=\"",ListOutFile);
442  for(i=0; i<20; i++)
443  { fprintf(ListOutFile,"%02X",Sum->SHA1digest[i]); }
444  fputs(".",ListOutFile);
445  for(i=0; i<16; i++)
446  { fprintf(ListOutFile,"%02X",Sum->MD5digest[i]); }
447  fputs(".",ListOutFile);
448  fprintf(ListOutFile,"%Lu",(long long unsigned int)Sum->DataLen);
449  fputs("\" ",ListOutFile);
450  free(Sum);
451  }
452  fclose(Fin);
453  }
454  } /* else no CF */
455  fprintf(ListOutFile,">\n"); /* end source XML */
456  }
457  if (Fname) TraverseStart(Fname,"called by main via args",NewDir,Recurse,ExcludePatterns);
458  else TraverseStart(argv[optind],"called by main",NewDir,Recurse,ExcludePatterns);
459  if (ListOutName != NULL) fprintf(ListOutFile,"</source>\n");
460  } /* end for */
461 
462  /* free memory */
463  if (Fname) { free(Fname); Fname=NULL; }
464 
465  /* process pfile from scheduler */
466  if (Pfile)
467  {
468  if (0 == (rvExist1 = fo_RepExist2(REP_FILES,Pfile)))
469  {
471  }
472  else if (0 == (rvExist2 = fo_RepExist2(REP_GOLD,Pfile)))
473  {
474  Fname=fo_RepMkPath(REP_GOLD,Pfile);
475  if (fo_RepImport(Fname,REP_FILES,Pfile,1) != 0)
476  {
477  LOG_ERROR("Failed to import '%s' as '%s' into the repository",Fname,Pfile)
478  SafeExit(111);
479  }
480  }
481  if (Fname)
482  {
483  TraverseStart(Fname,"called by main via env",NewDir,Recurse,ExcludePatterns);
484  free(Fname);
485  Fname=NULL;
486  }
487  else
488  {
489  LOG_ERROR("NO file unpacked!");
490  if (rvExist1 > 0)
491  {
492  Fname=fo_RepMkPath(REP_FILES, Pfile);
493  LOG_ERROR("Error is %s for %s", strerror(rvExist1), Fname);
494  }
495  if (rvExist2 > 0)
496  {
497  Fname=fo_RepMkPath(REP_GOLD, Pfile);
498  LOG_ERROR("Error is %s for %s", strerror(rvExist2), Fname);
499  }
500  SafeExit(112);
501  }
502  }
503 
504  /* recurse on all the children */
505  if (Thread > 0) do
506  {
507  Pid = ParentWait();
508  Thread--;
509  if (Pid >= 0)
510  {
511  if (!Queue[Pid].ChildEnd)
512  {
513  /* copy over data */
514  if (Recurse > 0)
515  Traverse(Queue[Pid].ChildRecurse,NULL,"called by wait",NULL,Recurse-1,&Queue[Pid].PI,ExcludePatterns);
516  else if (Recurse < 0)
517  Traverse(Queue[Pid].ChildRecurse,NULL,"called by wait",NULL,Recurse,&Queue[Pid].PI,ExcludePatterns);
518  }
519  }
520  } while(Pid >= 0);
521 
522  if (MagicCookie) magic_close(MagicCookie);
523  if (ListOutFile)
524  {
525  fprintf(ListOutFile,"<summary files_regular=\"%d\" files_compressed=\"%d\" artifacts=\"%d\" directories=\"%d\" containers=\"%d\" />\n",
528  fputs("</xml>\n",ListOutFile);
529  }
530  if (pgConn)
531  {
532  /* If it completes, mark it! */
533  if (Upload_Pk)
534  {
535  snprintf(SQL,MAXSQL,"UPDATE upload SET upload_mode = (upload_mode | (1<<5)), uploadtree_tablename='%s' WHERE upload_pk = '%s';",uploadtree_tablename, Upload_Pk);
536  result = PQexec(pgConn, SQL); /* UPDATE upload */
537  if (fo_checkPQcommand(pgConn, result, SQL, __FILE__ ,__LINE__)) SafeExit(113);
538  PQclear(result);
539 
540  snprintf(SQL,MAXSQL,"UPDATE %s SET realparent = getItemParent(uploadtree_pk) WHERE upload_fk = '%s'",uploadtree_tablename, Upload_Pk);
541  result = PQexec(pgConn, SQL); /* UPDATE uploadtree */
542  if (fo_checkPQcommand(pgConn, result, SQL, __FILE__ ,__LINE__)) SafeExit(114);
543  PQclear(result);
544  }
545 
546  if (ars_pk) fo_WriteARS(pgConn, ars_pk, atoi(Upload_Pk), agent_pk, AgentARSName, 0, 1);
547  }
548  if (ListOutFile && (ListOutFile != stdout))
549  {
550  fclose(ListOutFile);
551  }
552 
553  if (UnlinkAll && MaxThread > 1)
554  {
555  /* Delete temporary files */
556  if (strcmp(NewDir, ".")) RemoveDir(NewDir);
557  }
558 
559  SafeExit(0);
560  return(0); // never executed but makes the compiler happy
561 }
char SQL[256]
SQL query to execute.
Definition: adj2nest.c:78
PGconn * pgConn
Database connection.
Definition: adj2nest.c:86
char * uploadtree_tablename
upload.uploadtree_tablename
Definition: adj2nest.c:100
int agent_pk
Definition: agent.h:74
char BuildVersion[]
Definition: buckets.c:68
Cksum * SumComputeBuff(CksumFile *CF)
Compute the checksum, allocate and return a Cksum containing the sum value.
Definition: checksum.c:182
Cksum * SumComputeFile(FILE *Fin)
Compute the checksum, allocate and return a string containing the sum value.
Definition: checksum.c:115
CksumFile * SumOpenFile(char *Fname)
Open and mmap a file.
Definition: checksum.c:26
void SumCloseFile(CksumFile *CF)
Close a file that was opened with SumOpenFile()
Definition: checksum.c:83
int Verbose
Verbose level.
Definition: util.c:19
int SetContainerArtifact
Should initial container be an artifact?
char * Pfile_Pk
Pfile pk in DB.
int ReunpackSwitch
Set if the uploadtree records are missing from db.
int UseRepository
Using files from the repository?
int MaxThread
Value between 1 and MAXCHILD.
int UnlinkSource
Remove recursive sources after unpacking?
char * Pfile
Pfile name (SHA1.MD5.Size)
int TotalDirectories
Number of directories.
unpackqueue Queue[MAXCHILD+1]
Manage children.
int Quiet
Run in quiet mode?
int Thread
Number of threads in execution.
int TotalFiles
Number of regular files.
int TotalArtifacts
Number of artifacts.
int PruneFiles
Remove links? >1 hard links, zero files, etc.
int TotalContainers
Number of containers.
char * Upload_Pk
Upload pk in DB.
int UnlinkAll
Remove ALL unpacked files when done (clean up)?
char REP_FILES[16]
Files repository name.
FILE * ListOutFile
File to store unpack list.
char REP_GOLD[16]
Gold repository name.
int IgnoreSCMData
1: Ignore SCM data, 0: dont ignore it.
int TotalCompressedFiles
Number of compressed files.
int ForceDuplicate
When using db, should it process duplicates?
int ForceContinue
Force continue when unpack tool fails?
magic_t MagicCookie
for Magic
Definition: finder.c:23
Usage()
Print Usage statement.
Definition: fo_dbcheck.php:63
FUNCTION int GetUploadPerm(PGconn *pgConn, long UploadPk, int user_pk)
Get users permission to this upload.
Definition: libfossagent.c:378
FUNCTION int fo_CreateARSTable(PGconn *pgConn, const char *tableName)
Create ars table if it doesn't already exist.
Definition: libfossagent.c:270
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
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_tableExists(PGconn *pgConn, const char *tableName)
Check if table exists. Note, this assumes the database name is 'fossology'.
Definition: libfossdb.c:232
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
#define PERM_WRITE
Read-Write permission.
Definition: libfossology.h:33
char * fo_RepMkPath(const char *Type, char *Filename)
Given a filename, construct the full path to the file.
Definition: libfossrepo.c:352
int fo_RepExist2(char *Type, char *Filename)
Determine if a file exists.
Definition: libfossrepo.c:531
int fo_RepImport(char *Source, char *Type, char *Filename, int Link)
Import a file into the repository.
Definition: libfossrepo.c:812
int fo_RepExist(char *Type, char *Filename)
Determine if a file exists.
Definition: libfossrepo.c:486
char * fo_sysconfig(const char *sectionname, const char *variablename)
gets a system configuration variable from the configuration data.
int fo_scheduler_userID()
Gets the id of the user that created the job that the agent is running.
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.
Store file handler and mmap of a file.
Definition: checksum.h:44
Store check sum of a file.
Definition: checksum.h:33
uint8_t SHA1digest[20]
SHA1 digest of the file.
Definition: checksum.h:35
uint64_t DataLen
Size of the file.
Definition: checksum.h:36
uint8_t MD5digest[16]
MD5 digest of the file.
Definition: checksum.h:34
void TraverseStart(char *Filename, char *Label, char *NewDir, int Recurse, char *ExcludePatterns)
Find all files (assuming a directory) and process (unpack) all of them.
Definition: traverse.c:24
int Traverse(char *Filename, char *Basename, char *Label, char *NewDir, int Recurse, ParentInfo *PI, char *ExcludePatterns)
Find all files, traverse all directories. This is a depth-first search, in inode order!
Definition: traverse.c:275
int RemoveDir(char *dirpath)
Remove all files under dirpath (rm -rf)
Definition: utils.c:1642
void CheckCommands(int Show)
Make sure all commands are usable.
Definition: utils.c:568
int MkDir(char *Fname)
Smart mkdir.
Definition: utils.c:304
int ParentWait()
Wait for a child. Sets child status.
Definition: utils.c:510
void InitCmd()
Initialize the metahandler CMD table.
Definition: utils.c:109
int IsExe(char *Exe, int Quiet)
Check if the executable exists.
Definition: utils.c:394
void SafeExit(int rc)
Close scheduler and database connections, then exit.
Definition: utils.c:78
char * PathCheck(char *DirPath)
Check if path contains a "%U" or "%H". If so, substitute a unique ID for U.
Definition: utils.c:1663
void SQLNoticeProcessor(void *arg, const char *message)
Dummy postgresql notice processor. This prevents Notices from being written to stderr.
Definition: utils.c:1773
int Recurse
Level of unpack recursion. Default to infinite.
Definition: run_tests.c:19
char * NewDir
Test result directory.
Definition: run_tests.c:18
Contains global declaration of variables.