1 /*
2 Copyright 2020 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
NewPackagePromiseSanityCheck(const Attributes * a)32 static bool NewPackagePromiseSanityCheck(const Attributes *a)
33 {
34 assert(a != NULL);
35 if (!a->new_packages.module_body || !a->new_packages.module_body->name)
36 {
37 Log(LOG_LEVEL_ERR, "Can not find package module body in policy.");
38 return false;
39 }
40
41 if (a->new_packages.module_body->updates_ifelapsed == CF_NOINT ||
42 a->new_packages.module_body->installed_ifelapsed == CF_NOINT)
43 {
44 Log(LOG_LEVEL_ERR,
45 "Invalid or missing arguments in package_module body '%s': "
46 "query_installed_ifelapsed = %d query_updates_ifelapsed = %d",
47 a->new_packages.module_body->name,
48 a->new_packages.module_body->installed_ifelapsed,
49 a->new_packages.module_body->updates_ifelapsed);
50 return false;
51 return false;
52 }
53
54 if (a->new_packages.package_policy == NEW_PACKAGE_ACTION_NONE)
55 {
56 Log(LOG_LEVEL_ERR, "Unsupported package policy in package promise.");
57 return false;
58 }
59 return true;
60 }
61
HandleNewPackagePromiseType(EvalContext * ctx,const Promise * pp,const Attributes * a,char ** promise_log_msg,LogLevel * log_lvl)62 PromiseResult HandleNewPackagePromiseType(EvalContext *ctx, const Promise *pp,
63 const Attributes *a, char **promise_log_msg,
64 LogLevel *log_lvl)
65 {
66 assert(a != NULL);
67 Log(LOG_LEVEL_DEBUG, "New package promise handler");
68
69
70 if (!NewPackagePromiseSanityCheck(a))
71 {
72 *promise_log_msg =
73 SafeStringDuplicate("New package promise failed sanity check.");
74 *log_lvl = LOG_LEVEL_ERR;
75 return PROMISE_RESULT_FAIL;
76 }
77
78 PromiseBanner(ctx, pp);
79
80 PackagePromiseGlobalLock global_lock = AcquireGlobalPackagePromiseLock(ctx);
81
82 CfLock package_promise_lock;
83 char promise_lock[CF_BUFSIZE];
84 snprintf(promise_lock, sizeof(promise_lock), "new-package-%s-%s",
85 pp->promiser, a->new_packages.module_body->name);
86
87 if (global_lock.g_lock.lock == NULL)
88 {
89 *promise_log_msg =
90 SafeStringDuplicate("Can not acquire global lock for package "
91 "promise. Skipping promise evaluation");
92 *log_lvl = LOG_LEVEL_INFO;
93
94 return PROMISE_RESULT_SKIPPED;
95 }
96
97 package_promise_lock =
98 AcquireLock(ctx, promise_lock, VUQNAME, CFSTARTTIME,
99 a->transaction.ifelapsed, a->transaction.expireafter, pp, false);
100 if (package_promise_lock.lock == NULL)
101 {
102 Log(LOG_LEVEL_DEBUG, "Skipping promise execution due to locking.");
103 YieldGlobalPackagePromiseLock(global_lock);
104
105 *promise_log_msg =
106 StringFormat("Can not acquire lock for '%s' package promise. "
107 "Skipping promise evaluation", pp->promiser);
108 *log_lvl = LOG_LEVEL_VERBOSE;
109
110 return PROMISE_RESULT_SKIPPED;
111 }
112
113 PackageModuleWrapper *package_module =
114 NewPackageModuleWrapper(a->new_packages.module_body);
115
116 if (!package_module)
117 {
118 *promise_log_msg =
119 StringFormat("Some error occurred while contacting package "
120 "module - promise: %s", pp->promiser);
121 *log_lvl = LOG_LEVEL_ERR;
122
123 YieldCurrentLock(package_promise_lock);
124 YieldGlobalPackagePromiseLock(global_lock);
125
126 return PROMISE_RESULT_FAIL;
127 }
128
129 PromiseResult result = PROMISE_RESULT_FAIL;
130
131 switch (a->new_packages.package_policy)
132 {
133 case NEW_PACKAGE_ACTION_ABSENT:
134 result = HandleAbsentPromiseAction(ctx, pp->promiser,
135 &a->new_packages,
136 package_module,
137 a->transaction.action);
138
139 switch (result)
140 {
141 case PROMISE_RESULT_FAIL:
142 *log_lvl = LOG_LEVEL_ERR;
143 *promise_log_msg =
144 StringFormat("Error removing package '%s'",
145 pp->promiser);
146 break;
147 case PROMISE_RESULT_CHANGE:
148 *log_lvl = LOG_LEVEL_INFO;
149 *promise_log_msg =
150 StringFormat("Successfully removed package '%s'",
151 pp->promiser);
152 break;
153 case PROMISE_RESULT_NOOP:
154 *log_lvl = LOG_LEVEL_VERBOSE;
155 *promise_log_msg =
156 StringFormat("Package '%s' was not installed",
157 pp->promiser);
158 break;
159 case PROMISE_RESULT_WARN:
160 *log_lvl = LOG_LEVEL_WARNING;
161 *promise_log_msg =
162 StringFormat("Package '%s' needs to be removed,"
163 "but only warning was promised",
164 pp->promiser);
165 break;
166 default:
167 ProgrammingError(
168 "Absent promise action evaluation returned "
169 "unsupported result: %d",
170 result);
171 break;
172 }
173 break;
174 case NEW_PACKAGE_ACTION_PRESENT:
175 result = HandlePresentPromiseAction(ctx, pp->promiser,
176 &a->new_packages,
177 package_module,
178 a->transaction.action);
179
180 switch (result)
181 {
182 case PROMISE_RESULT_FAIL:
183 *log_lvl = LOG_LEVEL_ERR;
184 *promise_log_msg =
185 StringFormat("Error installing package '%s'",
186 pp->promiser);
187 break;
188 case PROMISE_RESULT_CHANGE:
189 *log_lvl = LOG_LEVEL_INFO;
190 *promise_log_msg =
191 StringFormat("Successfully installed package '%s'",
192 pp->promiser);
193 break;
194 case PROMISE_RESULT_NOOP:
195 *log_lvl = LOG_LEVEL_VERBOSE;
196 *promise_log_msg =
197 StringFormat("Package '%s' already installed",
198 pp->promiser);
199 break;
200 case PROMISE_RESULT_WARN:
201 *log_lvl = LOG_LEVEL_WARNING;
202 *promise_log_msg =
203 StringFormat("Package '%s' needs to be installed,"
204 "but only warning was promised",
205 pp->promiser);
206 break;
207 default:
208 ProgrammingError(
209 "Present promise action evaluation returned "
210 "unsupported result: %d",
211 result);
212 break;
213 }
214
215 break;
216 case NEW_PACKAGE_ACTION_NONE:
217 default:
218 ProgrammingError("Unsupported package action: %d",
219 a->new_packages.package_policy);
220 break;
221 }
222
223 DeletePackageModuleWrapper(package_module);
224
225 YieldCurrentLock(package_promise_lock);
226 YieldGlobalPackagePromiseLock(global_lock);
227
228 return result;
229 }
230