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(®ex_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(®ex_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