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 = §ion};
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 = §ion};
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 = §ion};
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