cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

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