1 /***************************************************************
2  Copyright (C) 2010 Hewlett-Packard Development Company, L.P.
3 
4  This program is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public License
6  version 2 as published by the Free Software Foundation.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License along
14  with this program; if not, write to the Free Software Foundation, Inc.,
15  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16 
17  ***************************************************************/
18 /**
19  * \file inits.c
20  * \brief Bucket agent initialization and lookup functions
21  */
22 
23 #include "buckets.h"
24 extern int debug;
25 
26 /**
27  * \brief Get a bucketpool_pk based on the bucketpool_name
28  *
29  * \param pgConn          Database connection object
30  * \param bucketpool_name Bucket pool name
31  *
32  * \return active bucketpool_pk or 0 if error
33 ****************************************************/
getBucketpool_pk(PGconn * pgConn,char * bucketpool_name)34 FUNCTION int getBucketpool_pk(PGconn *pgConn, char *bucketpool_name)
35 {
36   char *fcnName = "getBucketpool";
37   int bucketpool_pk=0;
38   char sqlbuf[128];
39   PGresult *result;
40 
41   /* Skip file if it has already been processed for buckets. */
42   sprintf(sqlbuf, "select bucketpool_pk from bucketpool where (bucketpool_name='%s') and (active='Y') order by version desc",
43           bucketpool_name);
44   result = PQexec(pgConn, sqlbuf);
45   if (fo_checkPQresult(pgConn, result, sqlbuf, fcnName, __LINE__)) return 0;
46   if (PQntuples(result) > 0) bucketpool_pk = atoi(PQgetvalue(result, 0, 0));
47   PQclear(result);
48   return bucketpool_pk;
49 }
50 
51 
52 /**
53  * \brief Initialize the bucket definition list.
54  * If an error occured, write the error to stdout
55  *
56  * \param pgConn        Database connection object
57  * \param bucketpool_pk Bucket pool id
58  * \param pcroot        License cache root
59  *
60  * \return an array of bucket definitions (in eval order)
61  * or 0 if error.
62  */
initBuckets(PGconn * pgConn,int bucketpool_pk,cacheroot_t * pcroot)63 FUNCTION pbucketdef_t initBuckets(PGconn *pgConn, int bucketpool_pk, cacheroot_t *pcroot)
64 {
65   char *fcnName = "initBuckets";
66   char sqlbuf[256];
67   char filepath[256];
68   char hostname[256];
69   PGresult *result;
70   pbucketdef_t bucketDefList = 0;
71   int  numRows, rowNum;
72   int  rv, numErrors=0;
73   struct stat statbuf;
74 
75   /* reasonable input validation  */
76   if ((!pgConn) || (!bucketpool_pk))
77   {
78     printf("ERROR: %s.%s.%d Invalid input pgConn: %lx, bucketpool_pk: %d.\n",
79             __FILE__, fcnName, __LINE__, (unsigned long)pgConn, bucketpool_pk);
80     return 0;
81   }
82 
83   /* get bucket defs from db */
84   sprintf(sqlbuf, "select bucket_pk, bucket_type, bucket_regex, bucket_filename, stopon, bucket_name, applies_to from bucket_def where bucketpool_fk=%d order by bucket_evalorder asc", bucketpool_pk);
85   result = PQexec(pgConn, sqlbuf);
86   if (fo_checkPQresult(pgConn, result, sqlbuf, fcnName, __LINE__)) return 0;
87   numRows = PQntuples(result);
88   if (numRows == 0) /* no bucket recs for pool?  return error */
89   {
90     printf("ERROR: %s.%s.%d No bucket defs for pool %d.\n",
91             __FILE__, fcnName, __LINE__, bucketpool_pk);
92     PQclear(result);
93     return 0;
94   }
95 
96   bucketDefList = calloc(numRows+1, sizeof(bucketdef_t));
97   if (bucketDefList == 0)
98   {
99     printf("ERROR: %s.%s.%d No memory to allocate %d bucket defs.\n",
100             __FILE__, fcnName, __LINE__, numRows);
101     return 0;
102   }
103 
104   /* put each db bucket def into bucketDefList in eval order */
105   for (rowNum=0; rowNum<numRows; rowNum++)
106   {
107     bucketDefList[rowNum].bucket_pk = atoi(PQgetvalue(result, rowNum, 0));
108     bucketDefList[rowNum].bucket_type = atoi(PQgetvalue(result, rowNum, 1));
109     bucketDefList[rowNum].bucketpool_pk = bucketpool_pk;
110 
111     /* compile regex if type 3 (REGEX) */
112     if (bucketDefList[rowNum].bucket_type == 3)
113     {
114       rv = regcomp(&bucketDefList[rowNum].compRegex, PQgetvalue(result, rowNum, 2),
115                    REG_NOSUB | REG_ICASE | REG_EXTENDED);
116       if (rv != 0)
117       {
118         printf("ERROR: %s.%s.%d Invalid regular expression for bucketpool_pk: %d, bucket: %s\n",
119                __FILE__, fcnName, __LINE__, bucketpool_pk, PQgetvalue(result, rowNum, 5));
120         numErrors++;
121       }
122       bucketDefList[rowNum].regex = strdup(PQgetvalue(result, rowNum, 2));
123     }
124 
125     bucketDefList[rowNum].dataFilename = strdup(PQgetvalue(result, rowNum, 3));
126 
127     /* verify that external file dataFilename exists */
128     if (strlen(bucketDefList[rowNum].dataFilename) > 0)
129     {
130       snprintf(filepath, sizeof(filepath), "%s/bucketpools/%d/%s",
131         PROJECTSTATEDIR, bucketpool_pk, bucketDefList[rowNum].dataFilename);
132       if (stat(filepath, &statbuf) == -1)
133       {
134         hostname[0] = 0;
135         gethostname(hostname, sizeof(hostname));
136         printf("ERROR: %s.%s.%d File: %s is missing on host: %s.  bucketpool_pk: %d, bucket: %s\n",
137                __FILE__, fcnName, __LINE__, filepath, hostname, bucketpool_pk, PQgetvalue(result, rowNum, 5));
138         numErrors++;
139       }
140     }
141 
142     /* MATCH_EVERY */
143     if (bucketDefList[rowNum].bucket_type == 1)
144       bucketDefList[rowNum].match_every = getMatchEvery(pgConn, bucketpool_pk, bucketDefList[rowNum].dataFilename, pcroot);
145 
146     /* MATCH_ONLY */
147     if (bucketDefList[rowNum].bucket_type == 2)
148     {
149       bucketDefList[rowNum].match_only = getMatchOnly(pgConn, bucketpool_pk, bucketDefList[rowNum].dataFilename, pcroot);
150     }
151 
152     /* REGEX-FILE */
153     if (bucketDefList[rowNum].bucket_type == 5)
154     {
155       bucketDefList[rowNum].regex_row = getRegexFile(pgConn, bucketpool_pk, bucketDefList[rowNum].dataFilename, pcroot);
156     }
157 
158     bucketDefList[rowNum].stopon = *PQgetvalue(result, rowNum, 4);
159     bucketDefList[rowNum].bucket_name = strdup(PQgetvalue(result, rowNum, 5));
160     bucketDefList[rowNum].applies_to = *PQgetvalue(result, rowNum, 6);
161   }
162   PQclear(result);
163   if (numErrors) return 0;
164 
165   if (debug)
166   {
167     for (rowNum=0; rowNum<numRows; rowNum++)
168     {
169       printf("\nbucket_pk[%d] = %d\n", rowNum, bucketDefList[rowNum].bucket_pk);
170       printf("bucket_name[%d] = %s\n", rowNum, bucketDefList[rowNum].bucket_name);
171       printf("bucket_type[%d] = %d\n", rowNum, bucketDefList[rowNum].bucket_type);
172       printf("dataFilename[%d] = %s\n", rowNum, bucketDefList[rowNum].dataFilename);
173       printf("stopon[%d] = %c\n", rowNum, bucketDefList[rowNum].stopon);
174       printf("applies_to[%d] = %c\n", rowNum, bucketDefList[rowNum].applies_to);
175       printf("nomos_agent_pk[%d] = %d\n", rowNum, bucketDefList[rowNum].nomos_agent_pk);
176       printf("bucket_agent_pk[%d] = %d\n", rowNum, bucketDefList[rowNum].bucket_agent_pk);
177       printf("regex[%d] = %s\n", rowNum, bucketDefList[rowNum].regex);
178     }
179   }
180 
181   return bucketDefList;
182 }
183 
184 
185 /**
186  * \brief Read the match only file (bucket type 2)
187  *
188  * \param pgConn        Database connection object
189  * \param bucketpool_pk Bucket pool id
190  * \param filename      File name of match_only file
191  * \param pcroot        Look up cache root
192  *
193  * \return an array of rf_pk's that match the licenses
194  * in filename or 0 if error.
195  */
getMatchOnly(PGconn * pgConn,int bucketpool_pk,char * filename,cacheroot_t * pcroot)196 FUNCTION int *getMatchOnly(PGconn *pgConn, int bucketpool_pk,
197                              char *filename, cacheroot_t *pcroot)
198 {
199   char *fcnName = "getMatchOnly";
200   char *delims = ",\t\n\r";
201   char *sp;
202   char filepath[256];
203   char inbuf[256];
204   int *match_only = 0;
205   int  line_count = 0;
206   int  lr_pk;
207   int  matchNumb = 0;
208   FILE *fin;
209 
210   /* put together complete file path to match_only file */
211   snprintf(filepath, sizeof(filepath), "%s/bucketpools/%d/%s",
212            PROJECTSTATEDIR, bucketpool_pk, filename);
213 
214   /* open filepath */
215   fin = fopen(filepath, "r");
216   if (!fin)
217   {
218     printf("FATAL: %s.%s.%d Failure to open bucket file %s (pool=%d).\nError: %s\n",
219            __FILE__, fcnName, __LINE__, filepath, bucketpool_pk, strerror(errno));
220     return 0;
221   }
222 
223   /* count lines in file */
224   while (fgets(inbuf, sizeof(inbuf), fin)) line_count++;
225 
226   /* calloc match_only array as lines+1.  This set the array to
227      the max possible size +1 for null termination */
228   match_only = calloc(line_count+1, sizeof(int));
229   if (!match_only)
230   {
231     printf("FATAL: %s.%s.%d Unable to allocate %d int array.\n",
232            __FILE__, fcnName, __LINE__, line_count+1);
233     fclose(fin);
234     return 0;
235   }
236 
237   /* read each line fgets
238      A match_only file has one license per line, no leading whitespace.
239      Comments start with leading #
240    */
241   rewind(fin);
242   while (fgets(inbuf, sizeof(inbuf), fin))
243   {
244     /* input string should only contain 1 token (license name) */
245     sp = strtok(inbuf, delims);
246 
247     /* comment? */
248     if ((sp == 0) || (*sp == '#')) continue;
249 
250     /* look up license rf_pk */
251     lr_pk = lrcache_lookup(pcroot, sp);
252     if (lr_pk)
253     {
254       /* save rf_pk in match_only array */
255       match_only[matchNumb++] = lr_pk;
256 //printf("MATCH_ONLY license: %s, FOUND\n", sp);
257     }
258     else
259     {
260 //printf("MATCH_ONLY license: %s, NOT FOUND in DB - ignored\n", sp);
261     }
262   }
263   fclose(fin);
264 
265   return match_only;
266 }
267 
268 
269 /**
270  * \brief Read the match every file filename, for bucket type 1
271  *
272  * \param pgConn        Database connection object
273  * \param bucketpool_pk Bucket pool id
274  * \param filename      File name to match against
275  * \param pcroot        License cache
276  *
277  * \return an array of arrays of rf_pk's that define a
278  * match_every combination or 0 if error.
279  */
getMatchEvery(PGconn * pgConn,int bucketpool_pk,char * filename,cacheroot_t * pcroot)280 FUNCTION int **getMatchEvery(PGconn *pgConn, int bucketpool_pk,
281                              char *filename, cacheroot_t *pcroot)
282 {
283   char *fcnName = "getMatchEvery";
284   char filepath[256];
285   char inbuf[256];
286   int **match_every = 0;
287   int **match_every_head = 0;
288   int  line_count = 0;
289   int  *lr_pkArray;
290   int  matchNumb = 0;
291   FILE *fin;
292 
293   /* put together complete file path to match_every file */
294   snprintf(filepath, sizeof(filepath), "%s/bucketpools/%d/%s",
295            PROJECTSTATEDIR, bucketpool_pk, filename);
296 
297   /* open filepath */
298   fin = fopen(filepath, "r");
299   if (!fin)
300   {
301     printf("FATAL: %s.%s.%d Failure to initialize bucket %s (pool=%d).\nError: %s\n",
302            __FILE__, fcnName, __LINE__, filepath, bucketpool_pk, strerror(errno));
303     return 0;
304   }
305 
306   /* count lines in file */
307   while (fgets(inbuf, sizeof(inbuf), fin)) line_count++;
308 
309   /* calloc match_every array as lines+1.  This sets the array to
310      the max possible size +1 for null termination */
311   match_every = calloc(line_count+1, sizeof(int *));
312   if (!match_every)
313   {
314     printf("FATAL: %s.%s.%d Unable to allocate %d int array.\n",
315            __FILE__, fcnName, __LINE__, line_count+1);
316     fclose(fin);
317     return 0;
318   }
319   match_every_head = match_every;
320 
321   /* read each line fgets
322      A match_every file has 1-n licenses per line
323      Comments start with leading #
324    */
325   rewind(fin);
326   while (fgets(inbuf, sizeof(inbuf), fin))
327   {
328     /* comment? */
329     if (inbuf[0] == '#') continue;
330     lr_pkArray = getLicsInStr(pgConn, inbuf, pcroot);
331     if (lr_pkArray)
332     {
333       /* save rf_pk in match_every array */
334       match_every[matchNumb++] = lr_pkArray;
335     }
336   }
337 
338   if (!matchNumb)
339   {
340     free(match_every_head);
341     match_every_head = 0;
342   }
343   fclose(fin);
344   return match_every_head;
345 }
346 
347 
348 /**
349  * \brief Parse filename, for bucket type 5 REGEX-FILE
350  * Lines are in format:
351  * \code {ftype1} {regex1} {op} {ftype2} {regex2} \endcode
352  *
353  * ftype is either "license" or "filename" \n
354  * op is either "and" (1) or "or" (2) or "not" (3) \n
355  * The op clause is optional. \n
356  * For example:
357  * \code
358  *   license bsd.*clause \n
359  *   license (GPL_?v3|Affero_v3) and filename .*mypkg
360  * \endcode
361  *
362  * \param pgConn        Database connection object
363  * \param bucketpool_pk Bucket pool id
364  * \param filename      Filename to be parsed
365  * \param pcroot        License cache
366  *
367  * \return an array of arrays of regex_file_t's that
368  *        represent the rows in filename. \n
369  * or 0 if error.
370  */
getRegexFile(PGconn * pgConn,int bucketpool_pk,char * filename,cacheroot_t * pcroot)371 FUNCTION regex_file_t *getRegexFile(PGconn *pgConn, int bucketpool_pk,
372                              char *filename, cacheroot_t *pcroot)
373 {
374   char *fcnName = "getRegexFile";
375   char filepath[256];
376   char inbuf[256];
377   regex_file_t *regex_row_head = 0;
378   int  line_count = 0;
379   int  rv;
380   int  rowNumb = 0;
381   int  errorCount = 0;
382   char *Delims = " \t\n\r";
383   char *token;
384   char *saveptr;
385   FILE *fin;
386 
387   /* put together complete file path to match_every file */
388   snprintf(filepath, sizeof(filepath), "%s/bucketpools/%d/%s",
389            PROJECTSTATEDIR, bucketpool_pk, filename);
390 
391   /* open filepath */
392   fin = fopen(filepath, "r");
393   if (!fin)
394   {
395     printf("FATAL: %s.%s.%d Failure to initialize bucket %s (pool=%d).\nError: %s\n",
396            __FILE__, fcnName, __LINE__, filepath, bucketpool_pk, strerror(errno));
397     printf("In v1.3, files were in %s.  To be LSB compliate, v1.4 now requires them to be in %s\n",
398            DATADIR, PROJECTSTATEDIR);
399     return 0;
400   }
401 
402   /* count lines in file */
403   while (fgets(inbuf, sizeof(inbuf), fin)) line_count++;
404 
405   /* calloc array as lines+1.  This sets the array to
406      the max possible size +1 for null termination */
407   regex_row_head = calloc(line_count+1, sizeof(regex_file_t));
408   if (!regex_row_head)
409   {
410     printf("FATAL: %s.%s.%d Unable to allocate %d regex_file_t array.\n",
411            __FILE__, fcnName, __LINE__, line_count+1);
412     fclose(fin);
413     return 0;
414   }
415 
416   /* read each line fgets
417      File has 1-n expressions per line
418      Comments start with leading #
419    */
420   rewind(fin);
421   while (fgets(inbuf, sizeof(inbuf), fin))
422   {
423     /* comment? */
424     if (inbuf[0] == '#') continue;
425 
426     /* get first token ftype1 */
427     token = strtok_r(inbuf, Delims, &saveptr);
428 
429     /* empty line? */
430     if (token[0] == 0) continue;
431 
432     regex_row_head[rowNumb].ftype1 = getRegexFiletype(token, filepath);
433     if (regex_row_head[rowNumb].ftype1 == 0) break;
434 
435     /* get regex1 */
436     token = strtok_r(NULL, Delims, &saveptr);
437     regex_row_head[rowNumb].regex1 = strdup(token);
438     rv = regcomp(&regex_row_head[rowNumb].compRegex1, token, REG_NOSUB | REG_ICASE);
439     if (rv != 0)
440     {
441       printf("ERROR: %s.%s.%d Invalid regular expression for file: %s, [%s], row: %d\n",
442               __FILE__, fcnName, __LINE__, filepath, token, rowNumb+1);
443       errorCount++;
444       break;
445     }
446 
447     /* get optional operator 'and'=1 or 'or'=2 'not'=3 */
448     token = strtok_r(NULL, Delims, &saveptr);
449     if (!token)
450     {
451       rowNumb++;
452       continue;
453     }
454     else
455     {
456       if (strcasecmp(token, "and") == 0) regex_row_head[rowNumb].op = 1;
457       else
458       if (strcasecmp(token, "or") == 0) regex_row_head[rowNumb].op = 2;
459       else
460       if (strcasecmp(token, "not") == 0) regex_row_head[rowNumb].op = 3;
461       else
462       {
463         printf("ERROR: %s.%s.%d Invalid operator in file: %s, [%s], row: %d\n",
464                __FILE__, fcnName, __LINE__, filepath, token, rowNumb+1);
465         errorCount++;
466         break;
467       }
468     }
469 
470     /* get token ftype2 */
471     token = strtok_r(NULL, Delims, &saveptr);
472     regex_row_head[rowNumb].ftype2 = getRegexFiletype(token, filepath);
473     if (regex_row_head[rowNumb].ftype2 == 0) break;
474 
475     /* get regex2 */
476     token = strtok_r(NULL, Delims, &saveptr);
477     regex_row_head[rowNumb].regex2 = strdup(token);
478     rv = regcomp(&regex_row_head[rowNumb].compRegex2, token, REG_NOSUB | REG_ICASE);
479     if (rv != 0)
480     {
481       printf("ERROR: %s.%s.%d Invalid regular expression for file: %s, [%s], row: %d\n",
482               __FILE__, fcnName, __LINE__, filepath, token, rowNumb+1);
483       errorCount++;
484       break;
485     }
486 
487     rowNumb++;
488   }
489 
490   /* bad file data.  Die unceremoniously.
491    * \todo: die more gracefully.
492    */
493   if (errorCount) exit(-1);
494 
495   if (!rowNumb)
496   {
497     free(regex_row_head);
498     regex_row_head = 0;
499   }
500   fclose(fin);
501   return regex_row_head;
502 }
503 
504 
505 /**
506  * \brief Given a filetype token from REGEX-FILE
507  * return the token int representation.
508  *
509  * \param token    Token to convert
510  * \param filepath Path of REGEX-FILE data file.
511  *                 used for error reporting only.
512  *
513  * \return 1=filename, 2=license
514  */
getRegexFiletype(char * token,char * filepath)515 FUNCTION int getRegexFiletype(char *token, char *filepath)
516 {
517   if (strcasecmp(token, "filename") == 0) return(1);
518   else
519   if (strcasecmp(token, "license") == 0) return(2);
520   printf("FATAL: Invalid bucket file (%s), unknown filetype (%s)\n",
521        filepath, token);
522   return(0);
523 }
524 
525 
526 /**
527  * \brief Given a string with | separated license names
528  * return an integer array of rf_pk's
529  *
530  * \param pgConn  Database connection object
531  * \param nameStr String of lic names eg "bsd | gpl"
532  * \param pcroot  License cache
533  *
534  * \return an array of rf_pk's that match the names in nameStr
535  *
536  * \note If nameStr contains a license name that is not in
537  * the license_ref file, then 0 is returned since there
538  * is no way to match all the listed licenses.
539  */
getLicsInStr(PGconn * pgConn,char * nameStr,cacheroot_t * pcroot)540 FUNCTION int *getLicsInStr(PGconn *pgConn, char *nameStr,
541                              cacheroot_t *pcroot)
542 {
543   char *fcnName = "getLicsInStr";
544   char *delims = "|\n\r ";
545   char *sp;
546   int *pkArray;
547   int *pkArrayHead = 0;
548   int  lic_count = 1;
549   int  lr_pk;
550   int  matchNumb = 0;
551 
552   if (!nameStr) return 0;
553 
554   /* count how many seperators are in nameStr
555      number of licenses is the count +1 */
556   sp = nameStr;
557   while (*sp) if (*sp++ == *delims) lic_count++;
558 
559   /* we need lic_count+1 int array.  This sets the array to
560      the max possible size +1 for null termination */
561   pkArray = calloc(lic_count+1, sizeof(int));
562   if (!pkArray)
563   {
564     printf("FATAL: %s.%s.%d Unable to allocate %d int array.\n",
565            __FILE__, fcnName, __LINE__, lic_count+1);
566     return 0;
567   }
568   pkArrayHead = pkArray;  /* save head of array */
569 
570   /* read each line then read each license in the line
571      Comments start with leading #
572    */
573   while ((sp = strtok(nameStr, delims)) != 0)
574   {
575     /* look up license rf_pk */
576     lr_pk = lrcache_lookup(pcroot, sp);
577     if (lr_pk)
578     {
579       /* save rf_pk in match_every array */
580       pkArray[matchNumb++] = lr_pk;
581     }
582     else
583     {
584       /* license not found in license_ref table, so this can never match */
585       matchNumb = 0;
586       break;
587     }
588     nameStr = 0;  // for strtok
589   }
590 
591   if (matchNumb == 0)
592   {
593     free(pkArrayHead);
594     pkArrayHead = 0;
595   }
596 
597   return pkArrayHead;
598 }
599 
600 
601 /**
602  * \brief Get the latest nomos agent_pk that has data for this
603  * this uploadtree.
604  *
605  * \param pgConn    Database connection object
606  * \param upload_pk Upload id
607  *
608  * \return nomos_agent_pk of the latest version of the nomos agent
609  *        that has data for this upload. \n
610  *        Or 0 if there is no license data available
611  *
612  * \note This function writes error to stdout
613  */
LatestNomosAgent(PGconn * pgConn,int upload_pk)614 FUNCTION int LatestNomosAgent(PGconn *pgConn, int upload_pk)
615 {
616   char *fcnName = "LatestNomosAgent";
617   char sql[512];
618   PGresult *result;
619   int  nomos_agent_pk = 0;
620 
621   /*** Find the latest enabled nomos agent_pk ***/
622 
623   snprintf(sql, sizeof(sql),
624           "select agent_fk from nomos_ars, agent \
625               WHERE agent_pk=agent_fk and ars_success=true and upload_fk='%d' \
626                     and agent_enabled=true order by agent_ts desc limit 1",
627           upload_pk);
628   result = PQexec(pgConn, sql);
629   if (fo_checkPQresult(pgConn, result, sql, fcnName, __LINE__)) return 0;
630   if (PQntuples(result) == 0) return 0;
631   nomos_agent_pk = atoi(PQgetvalue(result,0,0));
632   PQclear(result);
633   return nomos_agent_pk;
634 }
635 
636 
637 /**
638  * \brief Given an uploadtree_pk of a container, find the
639  * uploadtree_pk of it's children (i.e. scan down through
640  * artifacts to get the children's parent
641  *
642  * \param pgConn        Database connection object
643  * \param uploadtree_pk Upload tree id for container
644  *
645  * \return uploadtree_pk of children's parent. \n
646  *         Or 0 if there are no children (empty container or non-container)
647  *
648  * \note This function writes error to stdout
649  */
childParent(PGconn * pgConn,int uploadtree_pk)650 FUNCTION int childParent(PGconn *pgConn, int uploadtree_pk)
651 {
652   char *fcnName = "childParent";
653   char sql[256];
654   PGresult *result;
655   int  childParent_pk = 0;   /* uploadtree_pk */
656 
657   do
658   {
659     snprintf(sql, sizeof(sql),
660            "select uploadtree_pk,ufile_mode from uploadtree where parent=%d limit 1",
661            uploadtree_pk);
662     result = PQexec(pgConn, sql);
663     if (fo_checkPQresult(pgConn, result, sql, fcnName, __LINE__)) break;
664     if (PQntuples(result) == 0) break;  /* empty container */
665 
666     /* not an artifact? */
667     if ((atoi(PQgetvalue(result, 0, 1)) & 1<<28) == 0)
668     {
669       childParent_pk = uploadtree_pk;
670       break;
671     }
672     uploadtree_pk = atoi(PQgetvalue(result, 0, 0));
673     PQclear(result);
674   } while (childParent_pk == 0);
675 
676   PQclear(result);
677   return childParent_pk;
678 }
679