1 /***************************************************************
2  Copyright (C) 2010-2013 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 /**
20  * \file leaf.c
21  * Handle leaf buckets
22  */
23 #include "buckets.h"
24 
25 extern int debug;
26 extern int DEB_SOURCE;
27 extern int DEB_BINARY;
28 
29 
30 /**
31  * \brief Determine which bucket(s) a leaf node is in and write results
32  *
33  * \param pgConn         postgresql connection
34  * \param bucketDefArray Bucket Definitions
35  * \param puploadtree    uploadtree record
36  * \param ppackage       package record
37  * \param agent_pk       Id of agent handling the job
38  * \param hasPrules      Not used
39  *
40  * \return 0=success, else error
41  */
processLeaf(PGconn * pgConn,pbucketdef_t bucketDefArray,puploadtree_t puploadtree,ppackage_t ppackage,int agent_pk,int hasPrules)42 FUNCTION int processLeaf(PGconn *pgConn, pbucketdef_t bucketDefArray,
43                          puploadtree_t puploadtree, ppackage_t ppackage,
44                          int agent_pk, int hasPrules)
45 {
46   int rv = 0;
47   int *bucketList;
48 
49   bucketList = getLeafBuckets(pgConn, bucketDefArray, puploadtree, ppackage, hasPrules);
50   if (bucketList)
51   {
52     if (debug)
53     {
54       printf("  buckets for pfile %d:",puploadtree->pfile_fk);
55       for (rv=0;bucketList[rv];rv++) printf("%d ",bucketList[rv]);
56       printf("\n");
57     }
58     rv = writeBuckets(pgConn, puploadtree->pfile_fk, puploadtree->uploadtree_pk,
59                       bucketList, agent_pk,
60                       bucketDefArray->nomos_agent_pk, bucketDefArray->bucketpool_pk);
61   }
62   else
63     rv = -1;
64 
65   free(bucketList);
66   return rv;
67 }
68 
69 
70 /**
71  * \brief Determine what buckets the pfile is in
72  *
73  * \param pgConn         postgresql connection
74  * \param bucketDefArray Bucket definition to work on
75  * \param puploadtree    Upload tree to work on
76  * \param hasPrules      Not used
77  *
78  * \return array of bucket_pk's, or 0 if error
79  */
getLeafBuckets(PGconn * pgConn,pbucketdef_t in_bucketDefArray,puploadtree_t puploadtree,ppackage_t ppackage,int hasPrules)80 FUNCTION int *getLeafBuckets(PGconn *pgConn, pbucketdef_t in_bucketDefArray,
81                              puploadtree_t puploadtree, ppackage_t ppackage,
82                              int hasPrules)
83 {
84   char *fcnName = "getLeafBuckets";
85   int  *bucket_pk_list = 0;
86   int  *bucket_pk_list_start;
87   char  filepath[512];
88   char  sql[1024];
89   PGresult *result;
90   PGresult *resultmime;
91   int   mimetype;
92   int   numLics, licNumb;
93   int   numBucketDefs = 0;
94   int   match = 0;   // bucket match
95   int   foundmatch, foundmatch2;
96   int   *pmatch_array;
97   int  **ppmatch_array;
98   int  *pfile_rfpks;
99   int   rv;
100   int   isPkg = 0;
101   int   envnum;
102   pbucketdef_t bucketDefArray;
103   regex_file_t *regex_row;
104   char *argv[2];
105   char *envp[11];
106   char  envbuf[4096];
107   char  pkgtype=0;
108   pid_t pid;
109 
110   if (debug) printf("debug: %s  pfile: %d\n", fcnName, puploadtree->pfile_fk);
111   /*** count how many elements are in in_bucketDefArray   ***/
112   for (bucketDefArray = in_bucketDefArray; bucketDefArray->bucket_pk; bucketDefArray++)
113     numBucketDefs++;
114 
115   /* allocate return array to hold max number of bucket_pk's + 1 for null terminator */
116   bucket_pk_list_start = calloc(numBucketDefs+1, sizeof(int));
117   if (bucket_pk_list_start == 0)
118   {
119     printf("FATAL: out of memory allocating int array of %d elements\n", numBucketDefs+1);
120     return 0;
121   }
122   bucket_pk_list = bucket_pk_list_start;
123 
124   /*** select all the licenses for uploadtree_pk and children and agent_pk ***/
125   bucketDefArray = in_bucketDefArray;
126 //  snprintf(sql, sizeof(sql),
127 //           "select rf_shortname, rf_pk from license_file, license_ref where agent_fk=%d and pfile_fk=%d and rf_fk=rf_pk",
128 //           bucketDefArray->nomos_agent_pk, puploadtree->pfile_fk);
129   snprintf(sql, sizeof(sql),
130       "SELECT distinct(rf_shortname) as rf_shortname, rf_pk \
131         from license_ref,license_file,\
132              (SELECT distinct(pfile_fk) as PF from uploadtree \
133              where upload_fk=%d \
134              and uploadtree.lft BETWEEN %d and %d) as SS \
135              where PF=pfile_fk and agent_fk=%d and rf_fk=rf_pk",
136        puploadtree->upload_fk, puploadtree->lft, puploadtree->rgt,
137        bucketDefArray->nomos_agent_pk);
138 
139   result = PQexec(pgConn, sql);
140   if (fo_checkPQresult(pgConn, result, sql, fcnName, __LINE__)) return 0;
141   numLics = PQntuples(result);
142 
143   /* make int array of rf_pk's for this pfile */
144   pfile_rfpks = calloc(numLics+1, sizeof(int));
145   if (pfile_rfpks == 0)
146   {
147     printf("FATAL: out of memory allocating int array of %d rf_pk elements\n", numLics+1);
148     return 0;
149   }
150   for (licNumb=0; licNumb < numLics; licNumb++)
151     pfile_rfpks[licNumb] = atoi(PQgetvalue(result, licNumb, 1));
152 
153 
154 #ifdef BOBG
155 printf("bobg: fileName: %s\n", puploadtree->ufile_name);
156 #endif
157   isPkg = (ppackage->pkgname[0]) ? 1 : 0;
158   /* loop through all the bucket defs in this pool */
159   for (bucketDefArray = in_bucketDefArray; bucketDefArray->bucket_pk; bucketDefArray++)
160   {
161     /* if this def is restricted to package (applies_to='p'),
162        then skip if this is not a package.
163        NOTE DEPENDENCY ON PKG ANALYSIS!
164     */
165     if (bucketDefArray->applies_to == 'p')
166     {
167       if (!isPkg) continue;
168     }
169     else
170     {
171       /* If this is a container, see if any of its children are in
172          this bucket.  If so, then the container is in this bucket.
173       */
174       if ((!isPkg) && (IsContainer(puploadtree->ufile_mode)))
175       {
176         rv = childInBucket(pgConn, bucketDefArray, puploadtree);
177         if (rv == 1)
178         {
179           *bucket_pk_list = bucketDefArray->bucket_pk;
180           bucket_pk_list++;
181           match++;
182         }
183         else if (rv == -1) return 0; //error
184         continue;
185       }
186     }
187 
188 #ifdef BOBG
189 printf("bobg: check bucket_pk: %d\n", bucketDefArray->bucket_pk);
190 #endif
191     switch (bucketDefArray->bucket_type)
192     {
193       /***  1  MATCH_EVERY  ***/
194       case 1:
195         ppmatch_array = bucketDefArray->match_every;
196         if (!ppmatch_array) break;
197         while (*ppmatch_array)
198         {
199           /* is match_array contained in pfile_rfpks?  */
200           if (arrayAinB(*ppmatch_array, pfile_rfpks))
201           {
202             *bucket_pk_list = bucketDefArray->bucket_pk;
203             bucket_pk_list++;
204             match++;
205             break;
206           }
207           ++ppmatch_array;
208         }
209         break;
210 
211       /***  2  MATCH_ONLY  ***/
212       case 2:
213         if (numLics == 0) break;
214         foundmatch = 1;
215         /* loop through pfile licenses to see if they are all found in the match_only list  */
216         for (licNumb=0; licNumb < numLics; licNumb++)
217         {
218           /* if rf_pk doesn't match any value in match_only,
219              then pfile is not in this bucket              */
220           pmatch_array = bucketDefArray->match_only;
221           while (*pmatch_array)
222           {
223             if (pfile_rfpks[licNumb] == *pmatch_array) break;
224             pmatch_array++;
225           }
226           if (!*pmatch_array)
227           {
228             /* no match, so pfile is not in this bucket */
229             foundmatch = 0;
230             break;  /* break out of for loop */
231           }
232         }
233         if (foundmatch)
234         {
235           *bucket_pk_list = bucketDefArray->bucket_pk;
236           bucket_pk_list++;
237           match++;
238         }
239         break;
240 
241       /***  3  REGEX  ***/
242       case 3:  /* does this regex match any license names for this pfile */
243         if (matchAnyLic(result, numLics, &bucketDefArray->compRegex))
244         {
245           /* regex matched!  */
246           *bucket_pk_list = bucketDefArray->bucket_pk;
247           bucket_pk_list++;
248           match++;
249         }
250         break;
251 
252       /***  4  EXEC  ***/
253       case 4:
254         /* file to exec bucketDefArray->dataFilename
255          * Exec'd file returns 0 on true (file is in bucket).
256          * When a file is exec'd it can expect the following
257          * environment variables:
258          * FILENAME: name of file being checked
259          * LICENSES: pipe seperated list of licenses for this file.
260          * PKGVERS: Package version from pkg header
261          * VENDOR: Vendor from pkg header
262          * PKGNAME:  simple package name (e.g. "cup", "mozilla-mail", ...)
263                      of file being checked.  Only applies to packages.
264          * SRCPKGNAME:  Source package name
265          * UPLOADTREE_PK: uploadtree_pk
266          * PFILE_PK: pfile_pk
267          * PKGTYPE: 's' if source, 'b' if binary package, '' if not a package
268          */
269         /* put together complete file path to file */
270         snprintf(filepath, sizeof(filepath), "%s/bucketpools/%d/%s",
271                  PROJECTSTATEDIR, bucketDefArray->bucketpool_pk, bucketDefArray->dataFilename);
272 			if ((pid = fork()) < 0)
273       {
274         printf("FATAL: fork failure, %s\n", strerror(errno));
275 			}
276 			else
277       if (pid == 0)  /* in child */
278       {
279         /* use TMPDIR for working directory
280          */
281         if ((rv = chdir("/tmp")))
282         {
283           printf("FATAL: exec bucket couldn't cd to /tmp\n");
284           exit(1);
285         }
286 
287 				/* set up environment variables */
288         envnum = 0;
289         argv[0] = strdup(bucketDefArray->dataFilename);
290         argv[1] = 0;
291         sprintf(envbuf, "FILENAME=%s", puploadtree->ufile_name);
292         envp[envnum++] = strdup(envbuf);
293         /* create pipe seperated list of licenses */
294         strcpy(envbuf, "LICENSES=");
295         for (licNumb=0; licNumb < numLics; licNumb++)
296         {
297           if (envbuf[9]) strcat(envbuf, "|");
298           strcat(envbuf, PQgetvalue(result, licNumb, 0));
299         }
300         envp[envnum++] = strdup(envbuf);
301         sprintf(envbuf, "PKGVERS=%s", ppackage->pkgvers);
302         envp[envnum++] = strdup(envbuf);
303         sprintf(envbuf, "VENDOR=%s", ppackage->vendor);
304         envp[envnum++] =strdup(envbuf);
305         sprintf(envbuf, "PKGNAME=%s", ppackage->pkgname);
306         envp[envnum++] =strdup(envbuf);
307         sprintf(envbuf, "SRCPKGNAME=%s", ppackage->srcpkgname);
308         envp[envnum++] =strdup(envbuf);
309         sprintf(envbuf, "UPLOADTREE_PK=%d", puploadtree->uploadtree_pk);
310         envp[envnum++] =strdup(envbuf);
311         sprintf(envbuf, "PFILE_PK=%d", puploadtree->pfile_fk);
312         envp[envnum++] =strdup(envbuf);
313 
314         /* Only figure out PKGTYPE if this is a pkg
315            For Debian packages, check mimetype:
316              application/x-debian-package --  binary
317              application/x-debian-source  --  source
318            For RPM's,
319              if srcpkgname is not null,
320              then this is a binary package
321              else this is a source package
322          */
323         pkgtype = 0;
324         if (isPkg)
325         {
326           if ((strstr(ppackage->srcpkgname,"none")==0)
327               || (ppackage->srcpkgname[0]==0)) pkgtype='b';
328           else
329           {
330             snprintf(sql, sizeof(sql),
331                      "select pfile_mimetypefk from pfile where pfile_pk=%d",
332                      puploadtree->pfile_fk);
333             resultmime = PQexec(pgConn, sql);
334             if (fo_checkPQresult(pgConn, resultmime, sql, fcnName, __LINE__)) return 0;
335             mimetype = *(PQgetvalue(resultmime, 0, 0));
336             PQclear(resultmime);
337             if (mimetype == DEB_SOURCE) pkgtype = 's';
338             else if (mimetype == DEB_BINARY) pkgtype = 'b';
339             else pkgtype = 's';
340           }
341         }
342         sprintf(envbuf, "PKGTYPE=%c", pkgtype);
343         envp[envnum++] =strdup(envbuf);
344 
345         envp[envnum++] = 0;
346         execve(filepath, argv, envp);
347         printf("FATAL: buckets execve (%s) failed, %s\n", filepath, strerror(errno));
348         exit(1);
349 			}
350 
351       /* wait for exit */
352 			if (waitpid(pid, &rv, 0) < 0)
353       {
354         printf("FATAL: waitpid, %s\n", strerror(errno));
355         return 0;
356 			}
357 			if (WIFSIGNALED(rv))
358       {
359         printf("FATAL: child %d died from signal %d", pid, WTERMSIG(rv));
360         return 0;
361       }
362 			else
363       if (WIFSTOPPED(rv))
364       {
365         printf("FATAL: child %d stopped, signal %d", pid, WSTOPSIG(rv));
366         return 0;
367       }
368 			else
369       if (WIFEXITED(rv))
370       {
371 				if (WEXITSTATUS(rv) == 0)
372         {
373           *bucket_pk_list = bucketDefArray->bucket_pk;
374           bucket_pk_list++;
375           match++;
376 				}
377 			}
378       break;
379 
380       /***  5  REGEX-FILE  ***/
381       /* File format is:
382          {filetype1} {regex1} {op} {filetype2} {regex2}
383          filetype == 1 is filename
384          filetype == 2 is license
385          op to end of line is optional.
386          e.g. filename COPYRIGHT and license BSD.*clause
387       */
388       case 5:
389         regex_row = bucketDefArray->regex_row;
390         foundmatch = 0;
391         foundmatch2 = 0;
392         /* loop through each regex_row */
393         while (regex_row->ftype1)
394         {
395           /* switches do not have a default since values have already been validated
396              see init.c
397           */
398           switch (regex_row->ftype1)
399           {
400             case 1: // check regex against filename
401               foundmatch = !regexec(&regex_row->compRegex1, puploadtree->ufile_name, 0, 0, 0);
402               break;
403             case 2: // check regex against licenses
404               foundmatch = matchAnyLic(result, numLics, &regex_row->compRegex1);
405               break;
406           }
407 
408           /* no sense in evaluating last half if first have is a match and
409              op is an OR
410           */
411           if ((regex_row->op == 2) || !foundmatch)
412             if (regex_row->op)
413             {
414               switch (regex_row->ftype2)
415               {
416                 case 1: // check regex against filename
417                   foundmatch2 = !regexec(&regex_row->compRegex2, puploadtree->ufile_name, 0, 0, 0);
418                   break;
419                 case 2: // check regex against licenses
420                   foundmatch2 = matchAnyLic(result, numLics, &regex_row->compRegex2);
421                   break;
422               }
423             }
424 
425           switch (regex_row->op)
426           {
427             case 1: // AND
428               foundmatch = (foundmatch && foundmatch2) ? 1 : 0;
429               break;
430             case 2: // OR
431               foundmatch = (foundmatch || foundmatch2) ? 1 : 0;
432               break;
433             case 3: // Not
434               foundmatch = (foundmatch && !foundmatch2) ? 1 : 0;
435               break;
436           }
437 
438           if (foundmatch)
439           {
440             *bucket_pk_list = bucketDefArray->bucket_pk;
441             bucket_pk_list++;
442             match++;
443           }
444           regex_row++;
445         }
446         break;
447 
448       /*** 99 DEFAULT bucket. aka not in any other bucket ***/
449       case 99:
450         if (!match)
451         {
452           *bucket_pk_list = bucketDefArray->bucket_pk;
453           bucket_pk_list++;
454           match++;
455         }
456         break;
457 
458       /*** UNKNOWN BUCKET TYPE  ***/
459       default:
460         printf("FATAL: Unknown bucket type %d, exiting...\n",
461                 bucketDefArray->bucket_type);
462         exit(-1);
463     }
464 #ifdef BOBG
465 printf("bobg match: %d\n", match);
466 #endif
467     if (match && bucketDefArray->stopon == 'Y') break;
468   }
469 
470 #ifdef BOBG
471   printf("bobg exit GetLeafBuckets()\n");
472 #endif
473   free(pfile_rfpks);
474   PQclear(result);
475   return bucket_pk_list_start;
476 }
477