1 /*
2   Copyright 2021 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 #include <package_module.h>
26 #include <pipes.h>
27 #include <exec_tools.h>
28 #include <signals.h>
29 #include <buffer.h>
30 #include <string_lib.h>
31 #include <path.h>
32 #include <actuator.h>
33 #include <file_lib.h>
34 #include <known_dirs.h>
35 #include <string_sequence.h>
36 #include <locks.h>
37 #include <rlist.h>
38 #include <policy.h>
39 #include <eval_context.h>
40 #include <changes_chroot.h>     /* RecordPkgOperationInChroot() */
41 #include <simulate_mode.h>      /* CHROOT_PKG_OPERATION_* */
42 
43 #define INVENTORY_LIST_BUFFER_SIZE 100 * 80 /* 100 entries with 80 characters
44                                              * per line */
45 
46 static bool UpdateSinglePackageModuleCache(EvalContext *ctx,
47                                     const PackageModuleWrapper *module_wrapper,
48                                     UpdateType type, bool force_update);
49 static void GetPackageModuleExecInfo(const PackageModuleBody *package_module, char **exec_path,
50                                      char **script_path, char **script_path_quoted, char **script_exec_opts);
51 static int NegotiateSupportedAPIVersion(PackageModuleWrapper *wrapper);
52 
53 
DeletePackageModuleWrapper(PackageModuleWrapper * wrapper)54 void DeletePackageModuleWrapper(PackageModuleWrapper *wrapper)
55 {
56     if (wrapper != NULL)
57     {
58         free(wrapper->path);
59         free(wrapper->script_path);
60         free(wrapper->script_path_quoted);
61         free(wrapper->script_exec_opts);
62         free(wrapper->name);
63         free(wrapper);
64     }
65 }
66 
NewPackageModuleWrapper(PackageModuleBody * package_module)67 PackageModuleWrapper *NewPackageModuleWrapper(PackageModuleBody *package_module)
68 {
69     assert(package_module && package_module->name);
70 
71     PackageModuleWrapper *wrapper = xmalloc(sizeof(PackageModuleWrapper));
72 
73     GetPackageModuleExecInfo(package_module, &(wrapper->path),
74                              &(wrapper->script_path), &(wrapper->script_path_quoted),
75                              &(wrapper->script_exec_opts));
76     wrapper->name = SafeStringDuplicate(package_module->name);
77     wrapper->package_module = package_module;
78 
79     if (wrapper->path == NULL)
80     {
81         Log(LOG_LEVEL_ERR, "No executable for the package module '%s'", package_module->name);
82         DeletePackageModuleWrapper(wrapper);
83         return NULL;
84     }
85 
86     /* Check if the given files exist and have the required permissions. */
87     if (wrapper->script_path != NULL)
88     {
89         /* we were given a path to a script to execute with the given
90          * interpreter */
91         const char *interpreter_path = wrapper->path;
92         const char *script_path = wrapper->script_path;
93 
94         if (access(interpreter_path, X_OK) != 0)
95         {
96             Log(LOG_LEVEL_ERR,
97                 "Cannot find package module interpreter at location '%s'"
98                 " or access to the file is restricted: %s",
99                 wrapper->path, GetErrorStr());
100             DeletePackageModuleWrapper(wrapper);
101             return NULL;
102         }
103         if (access(script_path, R_OK) != 0)
104         {
105             Log(LOG_LEVEL_ERR,
106                 "Cannot find package module script at location '%s'"
107                 " or access to the file is restricted: %s",
108                 wrapper->script_path, GetErrorStr());
109             DeletePackageModuleWrapper(wrapper);
110             return NULL;
111         }
112     }
113     else if (access(wrapper->path, X_OK) != 0)
114     {
115         /* no script path specified --> the given path has to be executable */
116         Log(LOG_LEVEL_ERR,
117             "Cannot find package module at location '%s' or access to file is restricted: %s",
118             wrapper->path, GetErrorStr());
119         DeletePackageModuleWrapper(wrapper);
120         return NULL;
121     }
122 
123     /* Negotiate API version */
124     wrapper->supported_api_version = NegotiateSupportedAPIVersion(wrapper);
125     if (wrapper->supported_api_version != 1)
126     {
127         Log(LOG_LEVEL_ERR,
128             "Unsupported package module wrapper API version: %d",
129             wrapper->supported_api_version);
130         DeletePackageModuleWrapper(wrapper);
131         return NULL;
132     }
133 
134     Log(LOG_LEVEL_DEBUG,
135         "Successfully created package module wrapper for '%s' package module.",
136         package_module->name);
137 
138     return wrapper;
139 }
140 
PackageWrapperCommunicate(const PackageModuleWrapper * wrapper,const char * args,const char * request,Rlist ** response)141 static int PackageWrapperCommunicate(const PackageModuleWrapper *wrapper, const char *args,
142                                      const char *request, Rlist **response)
143 {
144     char *all_args = NULL;
145     if (wrapper->script_path != NULL)
146     {
147         if (wrapper->script_exec_opts == NULL)
148         {
149             all_args = StringConcatenate(3, wrapper->script_path_quoted, " ", args);
150             args = all_args;
151         }
152         else
153         {
154             all_args = StringConcatenate(5, wrapper->script_exec_opts, " ",
155                                          wrapper->script_path_quoted, " ", args);
156             args = all_args;
157         }
158     }
159     int ret = PipeReadWriteData(wrapper->path, args, request, response,
160                                 PACKAGE_PROMISE_SCRIPT_TIMEOUT_SEC,
161                                 PACKAGE_PROMISE_TERMINATION_CHECK_SEC);
162     free(all_args);
163 
164     return ret;
165 }
166 
UpdatePackagesCache(EvalContext * ctx,bool force_update)167 void UpdatePackagesCache(EvalContext *ctx, bool force_update)
168 {
169     Log(LOG_LEVEL_DEBUG, "Updating package cache.");
170 
171     PackagePromiseGlobalLock package_lock =
172             AcquireGlobalPackagePromiseLock(ctx);
173 
174     if (package_lock.g_lock.lock == NULL)
175     {
176         Log(LOG_LEVEL_INFO, "Can not acquire global lock for package promise. "
177             "Skipping updating cache.");
178         return;
179     }
180 
181     Rlist *default_inventory = GetDefaultInventoryFromContext(ctx);
182 
183     for (const Rlist *rp = default_inventory; rp != NULL; rp = rp->next)
184     {
185         const char *pm_name =  RlistScalarValue(rp);
186 
187         PackageModuleBody *module = GetPackageModuleFromContext(ctx, pm_name);
188         if (!module)
189         {
190             Log(LOG_LEVEL_ERR,
191                 "Can not find body for package module: %s", pm_name);
192             continue;
193         }
194 
195         PackageModuleWrapper *module_wrapper = NewPackageModuleWrapper(module);
196 
197         if (!module_wrapper)
198         {
199             Log(LOG_LEVEL_ERR,
200                 "Can not set up wrapper for module: %s", pm_name);
201             continue;
202         }
203 
204         UpdateSinglePackageModuleCache(ctx, module_wrapper,
205                                        UPDATE_TYPE_INSTALLED, force_update);
206         UpdateSinglePackageModuleCache(ctx, module_wrapper,
207                                        force_update ? UPDATE_TYPE_UPDATES :
208                                            UPDATE_TYPE_LOCAL_UPDATES,
209                                        force_update);
210 
211         DeletePackageModuleWrapper(module_wrapper);
212     }
213     YieldGlobalPackagePromiseLock(package_lock);
214 }
215 
AcquireGlobalPackagePromiseLock(EvalContext * ctx)216 PackagePromiseGlobalLock AcquireGlobalPackagePromiseLock(EvalContext *ctx)
217 {
218     Bundle bundle = {.name = "package_global"};
219     BundleSection section = {.promise_type = "package_global",
220                              .parent_bundle = &bundle};
221     Promise pp = {.promiser = "package_global",
222                   .parent_section = &section};
223 
224     CfLock package_promise_global_lock;
225 
226     package_promise_global_lock =
227             AcquireLock(ctx, GLOBAL_PACKAGE_PROMISE_LOCK_NAME, VUQNAME, CFSTARTTIME,
228                         0, VEXPIREAFTER, &pp, false);
229 
230     return (PackagePromiseGlobalLock) {.g_lock = package_promise_global_lock,
231                                        .lock_ctx = ctx};
232 }
233 
YieldGlobalPackagePromiseLock(PackagePromiseGlobalLock lock)234 void YieldGlobalPackagePromiseLock(PackagePromiseGlobalLock lock)
235 {
236     Bundle bundle = {.name = "package_global"};
237     BundleSection section = {.promise_type = "package_global",
238                              .parent_bundle = &bundle};
239     Promise pp = {.promiser = "package_global",
240                   .parent_section = &section};
241 
242     YieldCurrentLockAndRemoveFromCache(lock.lock_ctx, lock.g_lock,
243                                        GLOBAL_PACKAGE_PROMISE_LOCK_NAME, &pp);
244 }
245 
ParseAndLogErrorMessage(const Rlist * data)246 static void ParseAndLogErrorMessage(const Rlist *data)
247 {
248     for (const Rlist *rp = data; rp != NULL; rp = rp->next)
249     {
250         char *line = RlistScalarValue(rp);
251 
252         if (StringStartsWith(line, "Error="))
253         {
254             Log(LOG_LEVEL_ERR, "package module: %s", line);
255         }
256         else if (StringStartsWith(line, "ErrorMessage="))
257         {
258             Log(LOG_LEVEL_ERR, "package module: %s", line);
259         }
260         else
261         {
262             Log(LOG_LEVEL_VERBOSE,
263                 "Unsupported response from package module: %s", line);
264         }
265     }
266 }
267 
FreePackageInfo(PackageInfo * package_info)268 static void FreePackageInfo(PackageInfo *package_info)
269 {
270     if (package_info)
271     {
272         free(package_info->arch);
273         free(package_info->name);
274         free(package_info->version);
275 
276         free(package_info);
277     }
278 }
279 
ParseOptions(Rlist * options)280 static char *ParseOptions(Rlist *options)
281 {
282     if (RlistIsNullList(options))
283     {
284         return SafeStringDuplicate("");
285     }
286 
287     Buffer *data = BufferNew();
288     for (Rlist *rp = options; rp != NULL; rp = rp->next)
289     {
290         char *value = RlistScalarValue(rp);
291         BufferAppendString(data, "options=");
292         BufferAppendString(data, value);
293         BufferAppendString(data, "\n");
294     }
295     return BufferClose(data);
296 }
297 
ParseAndCheckPackageDataReply(const Rlist * data)298 static PackageInfo *ParseAndCheckPackageDataReply(const Rlist *data)
299 {
300     PackageInfo * package_data = xcalloc(1, sizeof(PackageInfo));
301 
302     for (const Rlist *rp = data; rp != NULL; rp = rp->next)
303     {
304         const char *line = RlistScalarValue(rp);
305 
306         if (StringStartsWith(line, "PackageType="))
307         {
308             const char *type = line + strlen("PackageType=");
309             if (StringEqual(type, "file"))
310             {
311                 package_data->type = PACKAGE_TYPE_FILE;
312             }
313             else if (StringEqual(type, "repo"))
314             {
315                 package_data->type = PACKAGE_TYPE_REPO;
316             }
317             else
318             {
319                 Log(LOG_LEVEL_VERBOSE, "unsupported package type: %s", type);
320                 free(package_data);
321                 return NULL;
322             }
323         }
324         else if (StringStartsWith(line, "Name="))
325         {
326             if (package_data->name)
327             {
328                 /* Some error occurred as we already have name for
329                  * given package. */
330                 Log(LOG_LEVEL_ERR,
331                     "Extraneous package name line received: [%s] %s",
332                     line, package_data->name);
333                 free(package_data);
334                 return NULL;
335             }
336             package_data->name =
337                 SafeStringDuplicate(line + strlen("Name="));
338         }
339         else if (StringStartsWith(line, "Version="))
340         {
341             if (package_data->version)
342             {
343                 /* Some error occurred as we already have version for
344                  * given package. */
345                 Log(LOG_LEVEL_ERR,
346                     "Extraneous package version line received: [%s] %s",
347                     line, package_data->version);
348                 free(package_data);
349                 return NULL;
350             }
351             package_data->version =
352                 SafeStringDuplicate(line + strlen("Version="));
353         }
354         else if (StringStartsWith(line, "Architecture="))
355         {
356             if (package_data->arch)
357             {
358                 /* Some error occurred as we already have arch for
359                  * given package. */
360                Log(LOG_LEVEL_ERR,
361                    "Extraneous package architecture line received: [%s] %s",
362                    line, package_data->arch);
363                 free(package_data);
364                 return NULL;
365             }
366             package_data->arch =
367                 SafeStringDuplicate(line + strlen("Architecture="));
368         }
369         /* For handling errors */
370         else if (StringStartsWith(line, "Error="))
371         {
372             Log(LOG_LEVEL_ERR, "package module: %s", line);
373         }
374         else if (StringStartsWith(line, "ErrorMessage="))
375         {
376             Log(LOG_LEVEL_ERR, "package module: %s", line);
377         }
378         else
379         {
380             Log(LOG_LEVEL_VERBOSE,
381                 "Unsupported response from package module: %s", line);
382         }
383     }
384 
385     return package_data;
386 }
387 
NegotiateSupportedAPIVersion(PackageModuleWrapper * wrapper)388 static int NegotiateSupportedAPIVersion(PackageModuleWrapper *wrapper)
389 {
390     assert(wrapper);
391 
392     Log(LOG_LEVEL_DEBUG, "Getting supported API version.");
393 
394     int api_version = -1;
395 
396     Rlist *response = NULL;
397     if (PackageWrapperCommunicate(wrapper, "supports-api-version", "",
398                                   &response) != 0)
399     {
400         Log(LOG_LEVEL_INFO,
401             "Error occurred while getting supported API version.");
402         return -1;
403     }
404 
405     if (response)
406     {
407         if (RlistLen(response) == 1)
408         {
409             api_version = atoi(RlistScalarValue(response));
410             Log(LOG_LEVEL_DEBUG, "package wrapper API version: %d", api_version);
411         }
412         RlistDestroy(response);
413     }
414     return api_version;
415 }
416 
417 /* IMPORTANT: this might not return all the data we need like version
418               or architecture but package name MUST be known. */
419 static
GetPackageData(const char * name,const char * version,const char * architecture,Rlist * options,const PackageModuleWrapper * wrapper)420 PackageInfo *GetPackageData(const char *name, const char *version,
421                             const char *architecture, Rlist *options,
422                             const PackageModuleWrapper *wrapper)
423 {
424     assert(wrapper);
425 
426     Log(LOG_LEVEL_DEBUG, "Getting package '%s' data.", name);
427 
428     char *options_str = ParseOptions(options);
429     char *ver = version ?
430         StringFormat("Version=%s\n", version) : NULL;
431     char *arch = architecture ?
432         StringFormat("Architecture=%s\n", architecture) : NULL;
433 
434     char *request =
435             StringFormat("%sFile=%s\n%s%s", options_str, name,
436                          ver ? ver : "", arch ? arch : "");
437     free(ver);
438     free(arch);
439 
440     Rlist *response = NULL;
441     if (PackageWrapperCommunicate(wrapper, "get-package-data", request, &response) != 0)
442     {
443         Log(LOG_LEVEL_INFO, "Some error occurred while communicating with "
444             "package module while collecting package data.");
445         free(options_str);
446         free(request);
447         return NULL;
448     }
449 
450     PackageInfo *package_data = NULL;
451 
452     if (response)
453     {
454         package_data = ParseAndCheckPackageDataReply(response);
455         RlistDestroy(response);
456 
457         if (package_data)
458         {
459             /* At this point at least package name and type MUST be known
460              * (if no error) */
461             if (!package_data->name || package_data->type == PACKAGE_TYPE_NONE)
462             {
463                 Log(LOG_LEVEL_INFO, "Unknown package name or type.");
464                 FreePackageInfo(package_data);
465                 package_data = NULL;
466             }
467         }
468     }
469     free(options_str);
470     free(request);
471 
472     return package_data;
473 }
474 
GetPackageModuleExecInfo(const PackageModuleBody * package_module,char ** exec_path,char ** script_path,char ** script_path_quoted,char ** script_exec_opts)475 static void GetPackageModuleExecInfo(const PackageModuleBody *package_module, char **exec_path,
476                                      char **script_path, char **script_path_quoted,
477                                      char **script_exec_opts)
478 {
479 
480     assert(exec_path != NULL);
481     assert(script_path != NULL);
482     assert(script_path_quoted != NULL);
483 
484     char *package_module_path = NULL;
485 
486     if (package_module->module_path != NULL && !StringEqual(package_module->module_path, ""))
487     {
488         package_module_path = xstrdup(package_module->module_path);
489     }
490     else
491     {
492         package_module_path = StringFormat("%s%c%s%c%s%c%s", GetWorkDir(), FILE_SEPARATOR,
493                                            "modules", FILE_SEPARATOR, "packages", FILE_SEPARATOR,
494                                            package_module->name);
495     }
496 
497     if (package_module->interpreter && !StringEqual(package_module->interpreter, ""))
498     {
499         *script_path = package_module_path;
500         *script_path_quoted = Path_GetQuoted(*script_path);
501 
502         if (strchr(package_module->interpreter, ' ') == NULL)
503         {
504             /* No spaces in the 'interpreter' string, easy! Just interpreter path given, no opts. */
505             *exec_path = xstrdup(package_module->interpreter);
506             *script_exec_opts = NULL;
507         }
508         else
509         {
510             /* we need to split the interpreter command from the (potential)
511              * interpreter args */
512             ArgGetExecutableAndArgs(package_module->interpreter, exec_path, script_exec_opts);
513         }
514     }
515     else
516     {
517         *exec_path = package_module_path;
518         *script_path = NULL;
519         *script_path_quoted = NULL;
520         *script_exec_opts = NULL;
521     }
522 }
523 
IsPackageInCache(EvalContext * ctx,const PackageModuleWrapper * module_wrapper,const char * name,const char * ver,const char * arch)524 static int IsPackageInCache(EvalContext *ctx,
525                             const PackageModuleWrapper *module_wrapper,
526                             const char *name, const char *ver, const char *arch)
527 {
528     const char *version = ver;
529     /* Handle latest version in specific way for repo packages.
530      * Please note that for file packages 'latest' version is not supported
531      * and check against that is made in CheckPolicyAndPackageInfoMatch(). */
532     if (version && StringEqual(version, "latest"))
533     {
534         version = NULL;
535     }
536 
537     /* Make sure cache is updated. */
538     if (ctx)
539     {
540         if (!UpdateSinglePackageModuleCache(ctx, module_wrapper,
541                                             UPDATE_TYPE_INSTALLED, false))
542         {
543             Log(LOG_LEVEL_ERR, "Can not update cache.");
544         }
545     }
546 
547     CF_DB *db_cached;
548     if (!OpenSubDB(&db_cached, dbid_packages_installed,
549                    module_wrapper->package_module->name))
550     {
551         Log(LOG_LEVEL_INFO, "Can not open cache database.");
552         return -1;
553     }
554 
555     char *key = NULL;
556     if (version && arch)
557     {
558         key = StringFormat("N<%s>V<%s>A<%s>", name, version, arch);
559     }
560     else if (version)
561     {
562         key = StringFormat("N<%s>V<%s>", name, version);
563     }
564     else if (arch)
565     {
566         key = StringFormat("N<%s>A<%s>", name, arch);
567     }
568     else
569     {
570          key = StringFormat("N<%s>", name);
571     }
572 
573     int is_in_cache = 0;
574     char buff[1];
575 
576     Log(LOG_LEVEL_DEBUG, "Looking for key in installed packages cache: %s", key);
577 
578     if (ReadDB(db_cached, key, buff, 1))
579     {
580         /* Just make sure DB is not corrupted. */
581         if (buff[0] == '1')
582         {
583             is_in_cache = 1;
584         }
585         else
586         {
587             Log(LOG_LEVEL_INFO,
588                 "Seem to have corrupted data in cache database");
589             is_in_cache = -1;
590         }
591     }
592 
593     Log(LOG_LEVEL_DEBUG,
594         "Looking for package %s in cache returned: %d", name, is_in_cache);
595 
596     CloseDB(db_cached);
597 
598     return is_in_cache;
599 }
600 
WritePackageDataToDB(CF_DB * db_installed,const char * name,const char * ver,const char * arch,UpdateType type)601 void WritePackageDataToDB(CF_DB *db_installed,
602         const char *name, const char *ver, const char *arch,
603         UpdateType type)
604 {
605     char package_key[strlen(name) + strlen(ver) +
606                      strlen(arch) + 11];
607 
608     xsnprintf(package_key, sizeof(package_key),
609               "N<%s>", name);
610     if (type == UPDATE_TYPE_INSTALLED)
611     {
612         WriteDB(db_installed, package_key, "1", 1);
613         xsnprintf(package_key, sizeof(package_key),
614                 "N<%s>V<%s>", name, ver);
615         WriteDB(db_installed, package_key, "1", 1);
616         xsnprintf(package_key, sizeof(package_key),
617                 "N<%s>A<%s>", name, arch);
618         WriteDB(db_installed, package_key, "1", 1);
619         xsnprintf(package_key, sizeof(package_key),
620                 "N<%s>V<%s>A<%s>", name, ver, arch);
621         WriteDB(db_installed, package_key, "1", 1);
622     }
623     else if (HasKeyDB(db_installed, package_key, strlen(package_key) + 1))
624     {
625         /* type == UPDATE_TYPE_UPDATES || type == UPDATE_TYPE_LOCAL_UPDATES */
626         size_t val_size =
627                 ValueSizeDB(db_installed, package_key, strlen(package_key) + 1);
628         char buff[val_size + strlen(arch) + strlen(ver) + 8];
629 
630         ReadDB(db_installed, package_key, buff, val_size);
631         xsnprintf(buff + val_size, sizeof(package_key), "V<%s>A<%s>\n",
632                   ver, arch);
633         Log(LOG_LEVEL_DEBUG,
634             "Updating available updates key '%s' with value '%s'",
635             package_key, buff);
636 
637         WriteDB(db_installed, package_key, buff, strlen(buff));
638     }
639     else
640     {
641         /* type == UPDATE_TYPE_UPDATES || type == UPDATE_TYPE_LOCAL_UPDATES */
642         char buff[strlen(arch) + strlen(ver) + 8];
643         xsnprintf(buff, sizeof(package_key), "V<%s>A<%s>\n", ver, arch);
644         WriteDB(db_installed, package_key, buff, strlen(buff));
645     }
646 }
647 
UpdatePackagesDB(Rlist * data,const char * pm_name,UpdateType type)648 int UpdatePackagesDB(Rlist *data, const char *pm_name, UpdateType type)
649 {
650     assert(pm_name);
651 
652     bool have_error = false;
653 
654     CF_DB *db_cached;
655     dbid db_id = type == UPDATE_TYPE_INSTALLED ? dbid_packages_installed :
656                                                  dbid_packages_updates;
657     if (OpenSubDB(&db_cached, db_id, pm_name))
658     {
659         CleanDB(db_cached);
660 
661         Buffer *inventory_data = BufferNewWithCapacity(INVENTORY_LIST_BUFFER_SIZE);
662 
663         const char *package_data[3] = {NULL, NULL, NULL};
664 
665         for (const Rlist *rp = data; rp != NULL; rp = rp->next)
666         {
667             const char *line = RlistScalarValue(rp);
668 
669             if (StringStartsWith(line, "Name="))
670             {
671                 if (package_data[0])
672                 {
673                     if (package_data[1] && package_data[2])
674                     {
675                         WritePackageDataToDB(db_cached, package_data[0],
676                                              package_data[1], package_data[2],
677                                              type);
678 
679                         BufferAppendF(inventory_data, "%s,%s,%s\n",
680                                       package_data[0], package_data[1],
681                                       package_data[2]);
682                     }
683                     else
684                     {
685                         /* some error occurred */
686                         Log(LOG_LEVEL_VERBOSE,
687                                 "Malformed response from package module for package %s",
688                                 package_data[0]);
689                     }
690                     package_data[1] = NULL;
691                     package_data[2] = NULL;
692                 }
693 
694                 /* This must be the first entry on a list */
695                 package_data[0] = line + strlen("Name=");
696 
697             }
698             else if (StringStartsWith(line, "Version="))
699             {
700                 package_data[1] = line + strlen("Version=");
701             }
702             else if (StringStartsWith(line, "Architecture="))
703             {
704                 package_data[2] = line + strlen("Architecture=");
705             }
706             else if (StringStartsWith(line, "Error="))
707             {
708                 Log(LOG_LEVEL_ERR, "package module: %s", line);
709                 have_error = true;
710             }
711             else if (StringStartsWith(line, "ErrorMessage="))
712             {
713                 Log(LOG_LEVEL_ERR, "package module: %s", line);
714                 have_error = true;
715             }
716             else
717             {
718                  Log(LOG_LEVEL_ERR,
719                      "Unsupported response from package module: %s", line);
720                  have_error = true;
721             }
722         }
723         /* We should have one more entry left or empty 'package_data'. */
724         if (package_data[0] && package_data[1] && package_data[2])
725         {
726             WritePackageDataToDB(db_cached, package_data[0],
727                              package_data[1], package_data[2], type);
728 
729             BufferAppendF(inventory_data, "%s,%s,%s\n", package_data[0],
730                           package_data[1], package_data[2]);
731         }
732         else if (package_data[0] || package_data[1] || package_data[2])
733         {
734             Log(LOG_LEVEL_VERBOSE,
735                 "Malformed response from package manager: [%s:%s:%s]",
736                 package_data[0] ? package_data[0] : "",
737                 package_data[1] ? package_data[1] : "",
738                 package_data[2] ? package_data[2] : "");
739         }
740 
741         char *inventory_key = "<inventory>";
742         char *inventory_list = BufferClose(inventory_data);
743 
744         /* We can have empty list of installed software or available updates. */
745         if (!inventory_list)
746         {
747             WriteDB(db_cached, inventory_key, "\n", 1);
748         }
749         else
750         {
751             WriteDB(db_cached, inventory_key, inventory_list,
752                     strlen(inventory_list));
753             free(inventory_list);
754         }
755 
756         CloseDB(db_cached);
757         return have_error ? -1 : 0;
758     }
759     /* Unable to open database. */
760     return -1;
761 }
762 
763 
UpdateCache(Rlist * options,const PackageModuleWrapper * wrapper,UpdateType type)764 bool UpdateCache(Rlist* options, const PackageModuleWrapper *wrapper,
765                  UpdateType type)
766 {
767     assert(wrapper);
768 
769     Log(LOG_LEVEL_DEBUG, "Updating cache: %d", type);
770 
771     char *options_str = ParseOptions(options);
772     Rlist *response = NULL;
773 
774     const char *req_type = NULL;
775     if (type == UPDATE_TYPE_INSTALLED)
776     {
777         req_type = "list-installed";
778     }
779     else if (type == UPDATE_TYPE_UPDATES)
780     {
781         req_type = "list-updates";
782     }
783     else if (type == UPDATE_TYPE_LOCAL_UPDATES)
784     {
785         req_type = "list-updates-local";
786     }
787 
788     if (PackageWrapperCommunicate(wrapper, req_type, options_str, &response) != 0)
789     {
790         Log(LOG_LEVEL_VERBOSE, "Some error occurred while communicating with "
791                 "package module while updating cache.");
792         free(options_str);
793         return false;
794     }
795 
796     if (!response)
797     {
798         Log(LOG_LEVEL_DEBUG,
799             "Received empty packages list after requesting: %s", req_type);
800     }
801 
802     bool ret = true;
803 
804     /* We still need to update DB with empty data. */
805     if (UpdatePackagesDB(response, wrapper->name, type) != 0)
806     {
807         Log(LOG_LEVEL_INFO, "Error updating packages cache.");
808         ret = false;
809     }
810 
811     RlistDestroy(response);
812     free(options_str);
813     return ret;
814 }
815 
816 
ValidateChangedPackage(const NewPackages * policy_data,const PackageModuleWrapper * wrapper,const PackageInfo * package_info,NewPackageAction action_type)817 PromiseResult ValidateChangedPackage(const NewPackages *policy_data,
818                                      const PackageModuleWrapper *wrapper,
819                                      const PackageInfo *package_info,
820                                      NewPackageAction action_type)
821 {
822     assert(package_info && package_info->name);
823 
824     Log(LOG_LEVEL_DEBUG, "Validating package: %s", package_info->name);
825 
826     if (!UpdateCache(policy_data->package_options, wrapper,
827                      UPDATE_TYPE_INSTALLED))
828     {
829         Log(LOG_LEVEL_INFO,
830             "Can not update installed packages cache after package installation");
831         return PROMISE_RESULT_FAIL;
832     }
833 
834     if (!UpdateCache(policy_data->package_options, wrapper,
835                      UPDATE_TYPE_LOCAL_UPDATES))
836     {
837         Log(LOG_LEVEL_INFO,
838             "Can not update available updates cache after package installation");
839         return PROMISE_RESULT_FAIL;
840     }
841 
842     int is_in_cache = IsPackageInCache(NULL, wrapper, package_info->name,
843                                        package_info->version,
844                                        package_info->arch);
845     if (is_in_cache == 1)
846     {
847         return action_type == NEW_PACKAGE_ACTION_PRESENT ?
848             PROMISE_RESULT_CHANGE : PROMISE_RESULT_FAIL;
849     }
850     else if (is_in_cache == 0)
851     {
852         return action_type == NEW_PACKAGE_ACTION_PRESENT ?
853             PROMISE_RESULT_FAIL : PROMISE_RESULT_CHANGE;
854     }
855     else
856     {
857         Log(LOG_LEVEL_INFO,
858             "Some error occurred while reading installed packages cache.");
859         return PROMISE_RESULT_FAIL;
860     }
861 }
862 
RemovePackage(const char * name,Rlist * options,const char * version,const char * architecture,const PackageModuleWrapper * wrapper)863 PromiseResult RemovePackage(const char *name, Rlist* options,
864                             const char *version, const char *architecture,
865                             const PackageModuleWrapper *wrapper)
866 {
867     assert(wrapper);
868 
869     Log(LOG_LEVEL_DEBUG, "Removing package '%s'", name);
870 
871     char *options_str = ParseOptions(options);
872     char *ver = version ?
873         StringFormat("Version=%s\n", version) : NULL;
874     char *arch = architecture ?
875         StringFormat("Architecture=%s\n", architecture) : NULL;
876     char *request = StringFormat("%sName=%s\n%s%s",
877             options_str, name, ver ? ver : "", arch ? arch : "");
878 
879     PromiseResult res = PROMISE_RESULT_CHANGE;
880 
881     Rlist *error_message = NULL;
882     if (PackageWrapperCommunicate(wrapper, "remove", request, &error_message) != 0)
883     {
884         Log(LOG_LEVEL_INFO,
885             "Error communicating package module while removing package.");
886         res = PROMISE_RESULT_FAIL;
887     }
888     if (error_message)
889     {
890         ParseAndLogErrorMessage(error_message);
891         res = PROMISE_RESULT_FAIL;
892         RlistDestroy(error_message);
893     }
894 
895     free(request);
896     free(options_str);
897     free(ver);
898     free(arch);
899 
900     /* We assume that at this point package is removed correctly. */
901     return res;
902 }
903 
904 
InstallPackageGeneric(Rlist * options,PackageType type,const char * packages_list_formatted,const PackageModuleWrapper * wrapper)905 static PromiseResult InstallPackageGeneric(Rlist *options,
906         PackageType type, const char *packages_list_formatted,
907         const PackageModuleWrapper *wrapper)
908 {
909     assert(wrapper);
910 
911     Log(LOG_LEVEL_DEBUG,
912         "Installing %s type package: '%s'",
913         type == PACKAGE_TYPE_FILE ? "file" : "repo",
914         packages_list_formatted);
915 
916     char *options_str = ParseOptions(options);
917     char *request = StringFormat("%s%s", options_str, packages_list_formatted);
918 
919     PromiseResult res = PROMISE_RESULT_CHANGE;
920 
921     const char *package_install_command = NULL;
922     if (type == PACKAGE_TYPE_FILE)
923     {
924         package_install_command = "file-install";
925     }
926     else if (type == PACKAGE_TYPE_REPO)
927     {
928         package_install_command = "repo-install";
929     }
930     else
931     {
932         /* If we end up here something bad has happened. */
933         ProgrammingError("Unsupported package type");
934     }
935 
936     Log(LOG_LEVEL_DEBUG,
937         "Sending install command to package module: '%s'",
938         request);
939 
940     Rlist *error_message = NULL;
941     if (PackageWrapperCommunicate(wrapper, package_install_command, request, &error_message) != 0)
942     {
943         Log(LOG_LEVEL_INFO, "Some error occurred while communicating with "
944             "package module while installing package.");
945         res = PROMISE_RESULT_FAIL;
946     }
947     if (error_message)
948     {
949         ParseAndLogErrorMessage(error_message);
950         res = PROMISE_RESULT_FAIL;
951         RlistDestroy(error_message);
952     }
953 
954     free(request);
955     free(options_str);
956 
957     return res;
958 }
959 
InstallPackage(Rlist * options,PackageType type,const char * package_to_install,const char * version,const char * architecture,const PackageModuleWrapper * wrapper)960 static PromiseResult InstallPackage(Rlist *options,
961                                     PackageType type, const char *package_to_install,
962                                     const char *version, const char *architecture,
963                                     const PackageModuleWrapper *wrapper)
964 {
965     Log(LOG_LEVEL_DEBUG, "Installing package '%s'", package_to_install);
966 
967     char *ver = version ?
968         StringFormat("Version=%s\n", version) : NULL;
969     char *arch = architecture ?
970         StringFormat("Architecture=%s\n", architecture) : NULL;
971     char *request = NULL;
972 
973     PromiseResult res = PROMISE_RESULT_CHANGE;
974 
975     if (type == PACKAGE_TYPE_FILE)
976     {
977         request = StringFormat("File=%s\n%s%s",
978                                package_to_install,
979                                ver ? ver : "",
980                                arch ? arch : "");
981     }
982     else if (type == PACKAGE_TYPE_REPO)
983     {
984         request = StringFormat("Name=%s\n%s%s",
985                                package_to_install,
986                                ver ? ver : "",
987                                arch ? arch : "");
988     }
989     else
990     {
991         /* If we end up here something bad has happened. */
992         ProgrammingError("Unsupported package type");
993     }
994 
995     res = InstallPackageGeneric(options, type, request, wrapper);
996 
997     free(request);
998     free(ver);
999     free(arch);
1000 
1001     return res;
1002 }
1003 
FileInstallPackage(EvalContext * ctx,const Promise * pp,const Attributes * attr,const char * package_file_path,const PackageInfo * info,const PackageModuleWrapper * wrapper,int is_in_cache)1004 static PromiseResult FileInstallPackage(EvalContext *ctx,
1005                                         const Promise *pp,
1006                                         const Attributes *attr,
1007                                         const char *package_file_path,
1008                                         const PackageInfo *info,
1009                                         const PackageModuleWrapper *wrapper,
1010                                         int is_in_cache)
1011 {
1012     assert(attr != NULL);
1013 
1014     const NewPackages *policy_data = &(attr->new_packages);
1015 
1016     Log(LOG_LEVEL_DEBUG, "Installing file type package.");
1017 
1018     /* We have some packages matching file package promise in cache. */
1019     if (is_in_cache == 1)
1020     {
1021         Log(LOG_LEVEL_VERBOSE, "Package exists in cache. Skipping installation.");
1022         if (ChrootChanges())
1023         {
1024             RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_PRESENT, package_file_path, NULL, NULL);
1025         }
1026         return PROMISE_RESULT_NOOP;
1027     }
1028 
1029     PromiseResult res;
1030     if (MakingChanges(ctx, pp, attr, &res, "install file type package: %s", package_file_path))
1031     {
1032         if (ChrootChanges())
1033         {
1034             /* TODO: simulate file package installation */
1035             RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_INSTALL, package_file_path, NULL, NULL);
1036             return PROMISE_RESULT_CHANGE;
1037         }
1038         res = InstallPackage(policy_data->package_options,
1039                              PACKAGE_TYPE_FILE, package_file_path,
1040                              NULL, NULL, wrapper);
1041         if (res == PROMISE_RESULT_CHANGE)
1042         {
1043             Log(LOG_LEVEL_DEBUG, "Validating package: %s", package_file_path);
1044             return ValidateChangedPackage(policy_data, wrapper, info,
1045                                           NEW_PACKAGE_ACTION_PRESENT);
1046         }
1047     }
1048     return res;
1049 }
1050 
1051 
GetVersionsFromUpdates(EvalContext * ctx,const PackageInfo * info,const PackageModuleWrapper * module_wrapper)1052 static Seq *GetVersionsFromUpdates(EvalContext *ctx, const PackageInfo *info,
1053                                    const PackageModuleWrapper *module_wrapper)
1054 {
1055     assert(info && info->name);
1056 
1057     CF_DB *db_updates;
1058     dbid db_id = dbid_packages_updates;
1059     Seq *updates_list = NULL;
1060 
1061     /* Make sure cache is updated. */
1062     if (!UpdateSinglePackageModuleCache(ctx, module_wrapper,
1063                                         UPDATE_TYPE_UPDATES, false))
1064     {
1065         Log(LOG_LEVEL_INFO, "Can not update packages cache.");
1066     }
1067 
1068     if (OpenSubDB(&db_updates, db_id, module_wrapper->package_module->name))
1069     {
1070         char package_key[strlen(info->name) + 4];
1071 
1072         xsnprintf(package_key, sizeof(package_key),
1073                 "N<%s>", info->name);
1074 
1075         Log(LOG_LEVEL_DEBUG, "Looking for key in updates: %s", package_key);
1076 
1077         if (HasKeyDB(db_updates, package_key, sizeof(package_key)))
1078         {
1079             Log(LOG_LEVEL_DEBUG, "Found key in updates database");
1080 
1081             updates_list = SeqNew(3, FreePackageInfo);
1082             size_t val_size =
1083                     ValueSizeDB(db_updates, package_key, sizeof(package_key));
1084             char buff[val_size + 1];
1085             buff[val_size] = '\0';
1086 
1087             ReadDB(db_updates, package_key, buff, val_size);
1088             Seq* updates = SeqStringFromString(buff, '\n');
1089 
1090             for (size_t i = 0; i < SeqLength(updates); i++)
1091             {
1092                 char *package_line = SeqAt(updates, i);
1093                 Log(LOG_LEVEL_DEBUG, "Got line in updates database: '%s",
1094                     package_line);
1095 
1096                 char version[strlen(package_line)];
1097                 char arch[strlen(package_line)];
1098 
1099                 if (sscanf(package_line, "V<%[^>]>A<%[^>]>", version, arch) == 2)
1100                 {
1101                     PackageInfo *package = xcalloc(1, sizeof(PackageInfo));
1102 
1103                     package->name = SafeStringDuplicate(info->name);
1104                     package->version = SafeStringDuplicate(version);
1105                     package->arch = SafeStringDuplicate(arch);
1106                     SeqAppend(updates_list, package);
1107                 }
1108                 else
1109                 {
1110                     /* Some error occurred while scanning package updates. */
1111                     Log(LOG_LEVEL_INFO,
1112                         "Unable to parse available updates line: %s",
1113                         package_line);
1114                 }
1115             }
1116         }
1117         CloseDB(db_updates);
1118     }
1119     return updates_list;
1120 }
1121 
RepoInstall(EvalContext * ctx,const Promise * pp,const Attributes * attr,const PackageInfo * package_info,const PackageModuleWrapper * wrapper,int is_in_cache,bool * verified)1122 static PromiseResult RepoInstall(EvalContext *ctx,
1123                                  const Promise *pp,
1124                                  const Attributes *attr,
1125                                  const PackageInfo *package_info,
1126                                  const PackageModuleWrapper *wrapper,
1127                                  int is_in_cache,
1128                                  bool *verified)
1129 {
1130     assert(attr != NULL);
1131     assert(package_info != NULL);
1132 
1133     const NewPackages *policy_data = &(attr->new_packages);
1134 
1135     Log(LOG_LEVEL_DEBUG, "Installing repo type package: %d", is_in_cache);
1136     const char *const package_version = package_info->version;
1137     const char *const package_name = package_info->name;
1138     /* Package is not present in cache. */
1139     if (is_in_cache == 0)
1140     {
1141         /* Make sure cache is updated. */
1142         if (!UpdateSinglePackageModuleCache(ctx, wrapper,
1143                                             UPDATE_TYPE_UPDATES, false))
1144         {
1145             Log(LOG_LEVEL_INFO, "Can not update packages cache.");
1146         }
1147 
1148         const char *version = package_version;
1149         if (StringEqual(version, "latest"))
1150         {
1151             Log(LOG_LEVEL_DEBUG, "Clearing latest package version");
1152             version = NULL;
1153         }
1154         PromiseResult result = PROMISE_RESULT_FAIL;
1155         if (MakingChanges(ctx, pp, attr, &result, "install repo type package: %s", package_name))
1156         {
1157             if (ChrootChanges())
1158             {
1159                 /* TODO: simulate package installation */
1160                 RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_INSTALL, package_name,
1161                                            package_version, package_info->arch);
1162                 return PROMISE_RESULT_CHANGE;
1163             }
1164             *verified = false; /* Verification will be done in RepoInstallPackage(). */
1165             result = InstallPackage(policy_data->package_options, PACKAGE_TYPE_REPO,
1166                                     package_name, version, package_info->arch,
1167                                     wrapper);
1168         }
1169         return result;
1170     }
1171 
1172 
1173     /* We have some packages matching already installed at this point. */
1174 
1175 
1176     if (!StringEqual(package_version, "latest"))
1177     {
1178         /* No version or explicit version specified. */
1179         if (ChrootChanges())
1180         {
1181             RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_PRESENT, package_name,
1182                                        package_version, package_info->arch);
1183         }
1184         Log(LOG_LEVEL_VERBOSE, "Package '%s' already installed", package_name);
1185 
1186         return PROMISE_RESULT_NOOP;
1187     }
1188 
1189     /* We have 'latest' version in policy. */
1190 
1191     /* This can return more than one latest version if we have packages
1192      * with different architectures installed. */
1193     Seq *latest_versions = GetVersionsFromUpdates(ctx, package_info, wrapper);
1194     if (latest_versions == NULL)
1195     {
1196         if (ChrootChanges())
1197         {
1198             RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_PRESENT, package_name,
1199                                        package_version, package_info->arch);
1200         }
1201         Log(LOG_LEVEL_VERBOSE,
1202             "Package '%s' is already in the latest version. "
1203             "Skipping installation.", package_name);
1204 
1205         return PROMISE_RESULT_NOOP;
1206     }
1207 
1208     PromiseResult res = PROMISE_RESULT_NOOP;
1209     Buffer *install_buffer = BufferNew();
1210     Seq *packages_to_install = SeqNew(1, NULL);
1211 
1212     /* Loop through possible updates. */
1213     size_t length = SeqLength(latest_versions);
1214     for (size_t i = 0; i < length; i++)
1215     {
1216         PackageInfo *update_package = SeqAt(latest_versions, i);
1217 
1218         /* We can have multiple packages with different architectures
1219          * in updates available but we are interested only in updating
1220          * package with specific architecture. */
1221         if (package_info->arch &&
1222             !StringEqual(package_info->arch, update_package->arch))
1223         {
1224             Log(LOG_LEVEL_DEBUG,
1225                 "Skipping update check of package '%s' as updates"
1226                 "architecure doesn't match specified in policy: %s != %s.",
1227                 package_name, package_info->arch,
1228                 update_package->arch);
1229             continue;
1230         }
1231 
1232         const char *const update_version = update_package->version;
1233 
1234         Log(LOG_LEVEL_DEBUG,
1235             "Checking for package '%s' version '%s' in available updates",
1236             package_name, update_version);
1237 
1238         /* Just in case some package managers will report highest possible
1239          * version in updates list instead of removing entry if package is
1240          * already in the latest version. */
1241         const int update_in_cache = IsPackageInCache(
1242             ctx, wrapper, package_name, update_version, update_package->arch);
1243         if (update_in_cache == 1)
1244         {
1245             if (ChrootChanges())
1246             {
1247                 RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_PRESENT, package_name,
1248                                            update_version, update_package->arch);
1249             }
1250             Log(LOG_LEVEL_VERBOSE,
1251                 "Package version from updates matches one installed. "
1252                 "Skipping package installation.");
1253             res = PromiseResultUpdate(res, PROMISE_RESULT_NOOP);
1254             continue;
1255         }
1256         else if (update_in_cache == -1)
1257         {
1258             Log(LOG_LEVEL_INFO,
1259                 "Skipping package installation due to error with checking "
1260                 "packages cache.");
1261             res = PromiseResultUpdate(res, PROMISE_RESULT_FAIL);
1262             continue;
1263         }
1264         else
1265         {
1266             PromiseResult result = PROMISE_RESULT_FAIL;
1267             if (MakingChanges(ctx, pp, attr, &result, "install repo type package: %s",
1268                               package_name))
1269             {
1270                 if (ChrootChanges())
1271                 {
1272                     RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_INSTALL, package_name,
1273                                                update_version, update_package->arch);
1274                 }
1275                 else
1276                 {
1277                     /* Append package data to buffer. At the end all packages
1278                      * data that need to be updated will be sent to package module
1279                      * at once. This is important if we have package in more than
1280                      * one architecture. If we would update one after another we
1281                      * may end up with the ones doesn't matching default
1282                      * architecture being removed. */
1283                     BufferAppendF(install_buffer,
1284                                   "Name=%s\nVersion=%s\nArchitecture=%s\n",
1285                                   package_name,
1286                                   update_version,
1287                                   update_package->arch);
1288 
1289                     /* Here we are adding latest_versions elements to different
1290                      * seq. Make sure to not free those and not free latest_versions
1291                      * before we are done with packages_to_install.
1292                      * This is needed for later verification if package was
1293                      * installed correctly. */
1294                     SeqAppend(packages_to_install, update_package);
1295                 }
1296             }
1297             else
1298             {
1299                 res = PromiseResultUpdate(res, result);
1300                 continue;
1301             }
1302         }
1303     }
1304 
1305     char *install_formatted_list = BufferClose(install_buffer);
1306 
1307     if (install_formatted_list)
1308     {
1309         /* If we have some packages to install. */
1310         if (strlen(install_formatted_list) > 0)
1311         {
1312             Log(LOG_LEVEL_DEBUG,
1313                 "Formatted list of packages to be send to package module: "
1314                 "[%s]", install_formatted_list);
1315             res = InstallPackageGeneric(policy_data->package_options,
1316                                         PACKAGE_TYPE_REPO,
1317                                         install_formatted_list, wrapper);
1318 
1319             for (size_t i = 0; i < SeqLength(packages_to_install); i++)
1320             {
1321                 PackageInfo *to_verify = SeqAt(packages_to_install, i);
1322                 PromiseResult validate =
1323                     ValidateChangedPackage(policy_data, wrapper,
1324                                            to_verify,
1325                                            NEW_PACKAGE_ACTION_PRESENT);
1326                 Log(LOG_LEVEL_DEBUG,
1327                     "Validating package %s:%s:%s installation result: %d",
1328                     to_verify->name, to_verify->version,
1329                     to_verify->arch, validate);
1330                 res = PromiseResultUpdate(res, validate);
1331                 *verified = true;
1332             }
1333         }
1334         free(install_formatted_list);
1335     }
1336 
1337     SeqDestroy(packages_to_install);
1338     SeqDestroy(latest_versions);
1339     return res;
1340 }
1341 
RepoInstallPackage(EvalContext * ctx,const Promise * pp,const Attributes * attr,const PackageInfo * package_info,const PackageModuleWrapper * wrapper,int is_in_cache)1342 static PromiseResult RepoInstallPackage(EvalContext *ctx,
1343                                         const Promise *pp,
1344                                         const Attributes *attr,
1345                                         const PackageInfo *package_info,
1346                                         const PackageModuleWrapper *wrapper,
1347                                         int is_in_cache)
1348 {
1349     assert(attr != NULL);
1350 
1351     const NewPackages *policy_data = &(attr->new_packages);
1352 
1353     bool verified = false;
1354     PromiseResult res = RepoInstall(ctx, pp, attr, package_info, wrapper,
1355                                     is_in_cache, &verified);
1356 
1357     if (res == PROMISE_RESULT_CHANGE && !verified)
1358     {
1359         return ValidateChangedPackage(policy_data, wrapper, package_info,
1360                                       NEW_PACKAGE_ACTION_PRESENT);
1361     }
1362     return res;
1363 }
1364 
CheckPolicyAndPackageInfoMatch(const NewPackages * packages_policy,const PackageInfo * info)1365 static bool CheckPolicyAndPackageInfoMatch(const NewPackages *packages_policy,
1366                                            const PackageInfo *info)
1367 {
1368     if (packages_policy->package_version &&
1369         StringEqual(packages_policy->package_version, "latest"))
1370     {
1371         Log(LOG_LEVEL_WARNING, "Unsupported 'latest' version for package "
1372                 "promise of type file.");
1373         return false;
1374     }
1375 
1376     /* Check if file we are having matches what we want in policy. */
1377     if (info->arch && packages_policy->package_architecture &&
1378             !StringEqual(info->arch, packages_policy->package_architecture))
1379     {
1380         Log(LOG_LEVEL_WARNING,
1381             "Package arch and one specified in policy doesn't match: %s -> %s",
1382             info->arch, packages_policy->package_architecture);
1383         return false;
1384     }
1385 
1386     if (info->version && packages_policy->package_version &&
1387         !StringEqual(info->version, packages_policy->package_version))
1388     {
1389 
1390         Log(LOG_LEVEL_WARNING,
1391             "Package version and one specified in policy doesn't "
1392             "match: %s -> %s",
1393             info->version, packages_policy->package_version);
1394         return false;
1395     }
1396     return true;
1397 }
1398 
HandlePresentPromiseAction(EvalContext * ctx,const Promise * pp,const Attributes * attr,const PackageModuleWrapper * wrapper)1399 PromiseResult HandlePresentPromiseAction(EvalContext *ctx,
1400                                          const Promise *pp,
1401                                          const Attributes *attr,
1402                                          const PackageModuleWrapper *wrapper)
1403 {
1404     assert(pp != NULL);
1405     assert(attr != NULL);
1406 
1407     const char *package_name = pp->promiser;
1408     const NewPackages *policy_data = &(attr->new_packages);
1409 
1410     Log(LOG_LEVEL_DEBUG, "Starting evaluating present action promise.");
1411 
1412     /* Figure out what kind of package we are having. */
1413     PackageInfo *package_info = GetPackageData(package_name,
1414                                                policy_data->package_version,
1415                                                policy_data->package_architecture,
1416                                                policy_data->package_options,
1417                                                wrapper);
1418 
1419     PromiseResult result = PROMISE_RESULT_FAIL;
1420     if (package_info)
1421     {
1422         /* Check if data in policy matches returned by wrapper (files only). */
1423         if (package_info->type == PACKAGE_TYPE_FILE)
1424         {
1425 
1426             if (!CheckPolicyAndPackageInfoMatch(policy_data, package_info))
1427             {
1428                 Log(LOG_LEVEL_ERR, "Package data and policy doesn't match");
1429                 FreePackageInfo(package_info);
1430                 return PROMISE_RESULT_FAIL;
1431             }
1432         }
1433         else if (package_info->type == PACKAGE_TYPE_REPO)
1434         {
1435             /* We are expecting only package name to be returned by
1436              * 'get-package-data' in case of repo package */
1437             if (package_info->arch)
1438             {
1439                 Log(LOG_LEVEL_VERBOSE,
1440                     "Unexpected package architecture received from package module. Ignoring.");
1441                 free(package_info->arch);
1442                 package_info->arch = NULL;
1443             }
1444             if (package_info->version)
1445             {
1446                 Log(LOG_LEVEL_VERBOSE,
1447                     "Unexpected package version received from package module. Ignoring.");
1448                 free(package_info->version);
1449                 package_info->version = NULL;
1450             }
1451         }
1452 
1453         /* Fill missing data in package_info from policy. This will allow
1454          * to match cache against all known package details we are
1455          * interested in */
1456         if (!package_info->arch && policy_data->package_architecture)
1457         {
1458             package_info->arch =
1459                     SafeStringDuplicate(policy_data->package_architecture);
1460         }
1461         if (!package_info->version && policy_data->package_version)
1462         {
1463             package_info->version =
1464                     SafeStringDuplicate(policy_data->package_version);
1465         }
1466 
1467         /* Check if package exists in cache */
1468         int is_in_cache = IsPackageInCache(ctx, wrapper,
1469                                            package_info->name,
1470                                            package_info->version,
1471                                            package_info->arch);
1472 
1473         if (is_in_cache == -1)
1474         {
1475             Log(LOG_LEVEL_ERR, "Some error occurred while looking for package '%s' in cache.",
1476                 package_name);
1477             return PROMISE_RESULT_FAIL;
1478         }
1479 
1480         switch (package_info->type)
1481         {
1482             case PACKAGE_TYPE_FILE:
1483                 result = FileInstallPackage(ctx, pp, attr, package_name, package_info,
1484                                             wrapper, is_in_cache);
1485                 break;
1486             case PACKAGE_TYPE_REPO:
1487                 result = RepoInstallPackage(ctx, pp, attr, package_info,
1488                                             wrapper, is_in_cache);
1489                 break;
1490             default:
1491                 /* We shouldn't end up here. If we are having unsupported
1492                  package type this should be detected and handled
1493                  in ParseAndCheckPackageDataReply(). */
1494                 ProgrammingError("Unsupported package type");
1495         }
1496 
1497         FreePackageInfo(package_info);
1498     }
1499     else
1500     {
1501         Log(LOG_LEVEL_INFO, "Can not obtain package data for promise: %s",
1502             package_name);
1503     }
1504 
1505 
1506     Log(LOG_LEVEL_DEBUG, "Evaluating present action promise status: %c", result);
1507     return result;
1508 }
1509 
1510 
HandleAbsentPromiseAction(EvalContext * ctx,const Promise * pp,const Attributes * attr,const PackageModuleWrapper * wrapper)1511 PromiseResult HandleAbsentPromiseAction(EvalContext *ctx,
1512                                         const Promise *pp,
1513                                         const Attributes *attr,
1514                                         const PackageModuleWrapper *wrapper)
1515 {
1516     assert(pp != NULL);
1517     assert(attr != NULL);
1518 
1519     const char *package_name = pp->promiser;
1520     const NewPackages *policy_data = &(attr->new_packages);
1521 
1522     /* Check if we are not having 'latest' version. */
1523     if (policy_data->package_version &&
1524             StringEqual(policy_data->package_version, "latest"))
1525     {
1526         Log(LOG_LEVEL_ERR, "Package version 'latest' not supported for"
1527                 "absent package promise");
1528         return PROMISE_RESULT_FAIL;
1529     }
1530 
1531     /* Check if package exists in cache */
1532     int is_in_cache = IsPackageInCache(ctx, wrapper, package_name,
1533                                        policy_data->package_version,
1534                                        policy_data->package_architecture);
1535     if (is_in_cache == 1)
1536     {
1537         /* Remove package(s) */
1538         PromiseResult res;
1539         if (MakingChanges(ctx, pp, attr, &res, "remove package '%s'", package_name))
1540         {
1541             if (ChrootChanges())
1542             {
1543                 /* TODO: simulate removal */
1544                 RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_REMOVE, package_name,
1545                                            policy_data->package_version,
1546                                            policy_data->package_architecture);
1547                 return PROMISE_RESULT_CHANGE;
1548             }
1549             res = RemovePackage(package_name,
1550                     policy_data->package_options, policy_data->package_version,
1551                     policy_data->package_architecture, wrapper);
1552 
1553             if (res == PROMISE_RESULT_CHANGE)
1554             {
1555                 /* Check if package was removed. */
1556 
1557                 /* package_name is actually pp->promiser which is 'const char *'
1558                  * so we need to type-cast it. It's safe because pkg_info is
1559                  * passed as const *PackageInfo. */
1560                 const PackageInfo pkg_info = {
1561                     .name = (char *) package_name,
1562                     .version = policy_data->package_version,
1563                     .arch = policy_data->package_architecture
1564                 };
1565                 return ValidateChangedPackage(policy_data, wrapper, &(pkg_info),
1566                                               NEW_PACKAGE_ACTION_ABSENT);
1567             }
1568         }
1569 
1570         return res;
1571     }
1572     else if (is_in_cache == -1)
1573     {
1574         Log(LOG_LEVEL_ERR, "Error occurred while checking package '%s' "
1575             "existence in cache.", package_name);
1576         return PROMISE_RESULT_FAIL;
1577     }
1578     else
1579     {
1580         /* Package is not in cache which means it is already removed. */
1581         Log(LOG_LEVEL_DEBUG, "Package '%s' not installed. Skipping removing.",
1582             package_name);
1583         if (ChrootChanges())
1584         {
1585             RecordPkgOperationInChroot(CHROOT_PKG_OPERATION_ABSENT, package_name, policy_data->package_version,
1586                                        policy_data->package_architecture);
1587         }
1588         return PROMISE_RESULT_NOOP;
1589     }
1590 }
1591 
1592 
1593 /* IMPORTANT: This must be called under protection of
1594  * GLOBAL_PACKAGE_PROMISE_LOCK_NAME lock! */
UpdateSinglePackageModuleCache(EvalContext * ctx,const PackageModuleWrapper * module_wrapper,UpdateType type,bool force_update)1595 bool UpdateSinglePackageModuleCache(EvalContext *ctx,
1596                                     const PackageModuleWrapper *module_wrapper,
1597                                     UpdateType type, bool force_update)
1598 {
1599     assert(module_wrapper->package_module->name);
1600 
1601     Log(LOG_LEVEL_DEBUG,
1602         "Trying to%s update cache type: %d.",
1603         force_update ? " force" : "", type);
1604 
1605     if (!force_update)
1606     {
1607         if (module_wrapper->package_module->installed_ifelapsed == CF_NOINT ||
1608             module_wrapper->package_module->updates_ifelapsed == CF_NOINT)
1609         {
1610             Log(LOG_LEVEL_ERR,
1611                 "Invalid or missing arguments in package_module body '%s':  "
1612                 "query_installed_ifelapsed = %d query_updates_ifelapsed = %d",
1613                 module_wrapper->package_module->name,
1614                 module_wrapper->package_module->installed_ifelapsed,
1615                 module_wrapper->package_module->updates_ifelapsed);
1616             return false;
1617         }
1618     }
1619 
1620     Bundle bundle = {.name = "package_cache"};
1621     BundleSection section = {.promise_type = "package_cache",
1622                              .parent_bundle = &bundle};
1623     Promise pp = {.promiser = "package_cache",
1624                   .parent_section = &section};
1625 
1626     CfLock cache_updates_lock = {NULL, NULL, false};
1627     char cache_updates_lock_name[CF_BUFSIZE];
1628     int ifelapsed_time = -1;
1629 
1630     dbid dbid_val;
1631 
1632     if (type == UPDATE_TYPE_INSTALLED)
1633     {
1634         dbid_val = dbid_packages_installed;
1635         snprintf(cache_updates_lock_name, CF_BUFSIZE - 1,
1636                  "package-cache-installed-%s", module_wrapper->package_module->name);
1637         ifelapsed_time = module_wrapper->package_module->installed_ifelapsed;
1638     }
1639     else
1640     {
1641         dbid_val = dbid_packages_updates;
1642         snprintf(cache_updates_lock_name, CF_BUFSIZE - 1,
1643                 "package-cache-updates-%s", module_wrapper->package_module->name);
1644         ifelapsed_time = module_wrapper->package_module->updates_ifelapsed;
1645     }
1646 
1647     char *db_name = DBIdToSubPath(dbid_val, module_wrapper->name);
1648     struct stat statbuf;
1649     if (!force_update)
1650     {
1651         if (stat(db_name, &statbuf) == -1 && errno == ENOENT)
1652         {
1653             /* Force update if database file doesn't exist. Not strictly
1654              * necessary with the locks we have, but good to have for tests
1655              * that delete the database. */
1656             Log(LOG_LEVEL_VERBOSE,
1657                 "Forcing package list update due to missing database");
1658             force_update = true;
1659 
1660             /* When the cfengine database containing the cache of package updates
1661              * available is initialized we should use the network to get the
1662              * current list of updates available. It's not unlikely that the OS
1663              * does not yet have a local cache, which simply results in an error
1664              * getting a list of package updates available. */
1665             if (type == UPDATE_TYPE_LOCAL_UPDATES)
1666             {
1667                 type = UPDATE_TYPE_UPDATES;
1668             }
1669         }
1670 
1671         cache_updates_lock =
1672                 AcquireLock(ctx, cache_updates_lock_name, VUQNAME, CFSTARTTIME,
1673                             ifelapsed_time, VEXPIREAFTER, &pp, false);
1674     }
1675     free(db_name);
1676 
1677     bool ret = true;
1678 
1679     if (force_update || cache_updates_lock.lock != NULL)
1680     {
1681 
1682         /* Update available updates cache. */
1683         if (!UpdateCache(module_wrapper->package_module->options, module_wrapper, type))
1684         {
1685             Log(LOG_LEVEL_INFO,
1686                 "Some error occurred while updating available updates cache.");
1687             ret = false;
1688         }
1689         if (cache_updates_lock.lock != NULL)
1690         {
1691             YieldCurrentLock(cache_updates_lock);
1692         }
1693     }
1694     else
1695     {
1696         Log(LOG_LEVEL_VERBOSE, "Skipping %s package cache update.",
1697             type == UPDATE_TYPE_INSTALLED ?
1698                     "installed packages" : "available updates");
1699     }
1700     return ret;
1701 }
1702