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 <verify_new_packages.h>
26 #include <package_module.h>
27 #include <logging.h>
28 #include <string_lib.h>
29 #include <locks.h>
30 #include <ornaments.h>
31 #include <promises.h> /* PromiseRef */
32
NewPackagePromiseSanityCheck(const Attributes * a)33 static bool NewPackagePromiseSanityCheck(const Attributes *a)
34 {
35 assert(a != NULL);
36 if (!a->new_packages.module_body || !a->new_packages.module_body->name)
37 {
38 Log(LOG_LEVEL_ERR, "Can not find package module body in policy.");
39 return false;
40 }
41
42 if (a->new_packages.module_body->updates_ifelapsed == CF_NOINT ||
43 a->new_packages.module_body->installed_ifelapsed == CF_NOINT)
44 {
45 Log(LOG_LEVEL_ERR,
46 "Invalid or missing arguments in package_module body '%s': "
47 "query_installed_ifelapsed = %d query_updates_ifelapsed = %d",
48 a->new_packages.module_body->name,
49 a->new_packages.module_body->installed_ifelapsed,
50 a->new_packages.module_body->updates_ifelapsed);
51 return false;
52 return false;
53 }
54
55 if (a->new_packages.package_policy == NEW_PACKAGE_ACTION_NONE)
56 {
57 Log(LOG_LEVEL_ERR, "Unsupported package policy in package promise.");
58 return false;
59 }
60 return true;
61 }
62
HandleNewPackagePromiseType(EvalContext * ctx,const Promise * pp,const Attributes * a)63 PromiseResult HandleNewPackagePromiseType(EvalContext *ctx, const Promise *pp, const Attributes *a)
64 {
65 assert(a != NULL);
66 Log(LOG_LEVEL_DEBUG, "New package promise handler");
67
68
69 if (!NewPackagePromiseSanityCheck(a))
70 {
71 cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a,
72 "New package promise failed sanity check.");
73 return PROMISE_RESULT_FAIL;
74 }
75
76 PromiseBanner(ctx, pp);
77
78 PackagePromiseGlobalLock global_lock = AcquireGlobalPackagePromiseLock(ctx);
79
80 CfLock package_promise_lock;
81 char promise_lock[CF_BUFSIZE];
82 snprintf(promise_lock, sizeof(promise_lock), "new-package-%s-%s",
83 pp->promiser, a->new_packages.module_body->name);
84
85 if (global_lock.g_lock.lock == NULL)
86 {
87 Log(LOG_LEVEL_DEBUG, "Skipping promise execution due to global packaging locking.");
88 return PROMISE_RESULT_SKIPPED;
89 }
90
91 package_promise_lock =
92 AcquireLock(ctx, promise_lock, VUQNAME, CFSTARTTIME,
93 a->transaction.ifelapsed, a->transaction.expireafter, pp, false);
94 if (package_promise_lock.lock == NULL)
95 {
96 YieldGlobalPackagePromiseLock(global_lock);
97
98 Log(LOG_LEVEL_DEBUG, "Skipping promise execution due to promise-specific package locking.");
99 return PROMISE_RESULT_SKIPPED;
100 }
101
102 PackageModuleWrapper *package_module =
103 NewPackageModuleWrapper(a->new_packages.module_body);
104
105 if (package_module == NULL)
106 {
107 cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a,
108 "Some error occurred while contacting package module for promise '%s'",
109 pp->promiser);
110
111 YieldCurrentLock(package_promise_lock);
112 YieldGlobalPackagePromiseLock(global_lock);
113
114 return PROMISE_RESULT_FAIL;
115 }
116
117 PromiseResult result = PROMISE_RESULT_FAIL;
118
119 switch (a->new_packages.package_policy)
120 {
121 case NEW_PACKAGE_ACTION_ABSENT:
122 result = HandleAbsentPromiseAction(ctx, pp, a, package_module);
123
124 switch (result)
125 {
126 case PROMISE_RESULT_FAIL:
127 cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a,
128 "Error removing package '%s'", pp->promiser);
129 break;
130 case PROMISE_RESULT_CHANGE:
131 cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a,
132 "Successfully removed package '%s'", pp->promiser);
133 break;
134 case PROMISE_RESULT_NOOP:
135 /* Properly logged in HandleAbsentPromiseAction() */
136 cfPS(ctx, LOG_LEVEL_NOTHING, PROMISE_RESULT_NOOP, pp, a, NULL);
137 break;
138 case PROMISE_RESULT_WARN:
139 /* Properly logged in HandleAbsentPromiseAction() */
140 cfPS(ctx, LOG_LEVEL_NOTHING, PROMISE_RESULT_WARN, pp, a, NULL);
141 break;
142 default:
143 ProgrammingError("Absent promise action evaluation returned"
144 " unsupported result: %d", result);
145 break;
146 }
147 break;
148 case NEW_PACKAGE_ACTION_PRESENT:
149 result = HandlePresentPromiseAction(ctx, pp, a, package_module);
150
151 switch (result)
152 {
153 case PROMISE_RESULT_FAIL:
154 cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a,
155 "Error installing package '%s'", pp->promiser);
156 break;
157 case PROMISE_RESULT_CHANGE:
158 cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a,
159 "Successfully installed package '%s'", pp->promiser);
160 break;
161 case PROMISE_RESULT_NOOP:
162 cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a,
163 "Package '%s' already installed", pp->promiser);
164 break;
165 case PROMISE_RESULT_WARN:
166 /* Properly logged in HandlePresentPromiseAction() */
167 cfPS(ctx, LOG_LEVEL_NOTHING, PROMISE_RESULT_WARN, pp, a, NULL);
168 break;
169 default:
170 ProgrammingError("Present promise action evaluation returned"
171 " unsupported result: %d", result);
172 break;
173 }
174
175 break;
176 case NEW_PACKAGE_ACTION_NONE:
177 default:
178 ProgrammingError("Unsupported package action: %d", a->new_packages.package_policy);
179 break;
180 }
181
182 DeletePackageModuleWrapper(package_module);
183
184 YieldCurrentLock(package_promise_lock);
185 YieldGlobalPackagePromiseLock(global_lock);
186
187 return result;
188 }
189