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 <ornaments.h>
26
27 #include <string_lib.h>
28 #include <rlist.h>
29 #include <logging.h>
30 #include <fncall.h>
31 #include <promises.h> /* PromiseID */
32
33
34 /**
35 * @brief Like StringAppend(), but replace characters '*' and '#' with their visible counterparts.
36 * @param buffer Buffer to be used.
37 * @param src Constant string to append
38 * @param n Total size of dst buffer. The string will be truncated if this is exceeded.
39 */
StringAppendPromise(char * dst,const char * src,size_t n)40 static bool StringAppendPromise(char *dst, const char *src, size_t n)
41 {
42 size_t i, j;
43
44 if (n == 0)
45 {
46 return false;
47 }
48
49 n--;
50 for (i = 0; i < n && dst[i]; i++)
51 {
52 }
53 for (j = 0; i < n && src[j]; i++, j++)
54 {
55 const char ch = src[j];
56 switch (ch)
57 {
58 case CF_MANGLED_NS:
59 dst[i] = ':';
60 break;
61
62 case CF_MANGLED_SCOPE:
63 dst[i] = '.';
64 break;
65
66 default:
67 dst[i] = ch;
68 break;
69 }
70 }
71 dst[i] = '\0';
72 return (i < n || !src[j]);
73 }
74
75 /**
76 * @brief Like @c BufferAppendPromiseStr, but if @c str contains newlines
77 * and is longer than 2*N+3, then only copy an abbreviated version
78 * consisting of the first and last N characters, separated by @c `...`
79 * @param buffer Buffer to be used.
80 * @param str Constant string to append
81 * @param n Total size of dst buffer. The string will be truncated if this is exceeded.
82 * @param max_fragment Max. length of initial/final segment of @c str to keep
83 * @note 2*max_fragment+3 is the maximum length of the appended string (excl. terminating NULL)
84 *
85 */
StringAppendAbbreviatedPromise(char * dst,const char * src,size_t n,const size_t max_fragment)86 static bool StringAppendAbbreviatedPromise(char *dst, const char *src, size_t n,
87 const size_t max_fragment)
88 {
89 /* check if `src` contains a new line (may happen for "insert_lines") */
90 const char *const nl = strchr(src, '\n');
91 if (nl == NULL)
92 {
93 return StringAppendPromise(dst, src, n);
94 }
95 else
96 {
97 /* `src` contains a newline: abbreviate it by taking the first and last few characters */
98 static const char sep[] = "...";
99 char abbr[sizeof(sep) + 2 * max_fragment];
100 const size_t head = (nl > src + max_fragment) ? max_fragment : (size_t) (nl - src);
101 const char * last_line = strrchr(src, '\n') + 1;
102 assert(last_line); /* not max_fragmentULL, we know we have at least one '\n' */
103 const size_t tail = strlen(last_line);
104 if (tail > max_fragment)
105 {
106 last_line += tail - max_fragment;
107 }
108 memcpy(abbr, src, head);
109 strcpy(abbr + head, sep);
110 strcat(abbr, last_line);
111 return StringAppendPromise(dst, abbr, n);
112 }
113 }
114
115
116 /*********************************************************************/
117
118
SpecialTypeBanner(TypeSequence type,int pass)119 void SpecialTypeBanner(TypeSequence type, int pass)
120 {
121 if (type == TYPE_SEQUENCE_CONTEXTS)
122 {
123 Log(LOG_LEVEL_VERBOSE, "C: .........................................................");
124 Log(LOG_LEVEL_VERBOSE, "C: BEGIN classes / conditions (pass %d)", pass);
125 }
126 if (type == TYPE_SEQUENCE_VARS)
127 {
128 Log(LOG_LEVEL_VERBOSE, "V: .........................................................");
129 Log(LOG_LEVEL_VERBOSE, "V: BEGIN variables (pass %d)", pass);
130 }
131 }
132
PromiseBanner(EvalContext * ctx,const Promise * pp)133 void PromiseBanner(EvalContext *ctx, const Promise *pp)
134 {
135 char handle[CF_MAXVARSIZE];
136 const char *sp;
137
138 if ((sp = PromiseGetHandle(pp)) || (sp = PromiseID(pp)))
139 {
140 strlcpy(handle, sp, CF_MAXVARSIZE);
141 }
142 else
143 {
144 strcpy(handle, "");
145 }
146
147 Log(LOG_LEVEL_VERBOSE, "P: .........................................................");
148
149 if (strlen(handle) > 0)
150 {
151 Log(LOG_LEVEL_VERBOSE, "P: BEGIN promise '%s' of type \"%s\" (pass %d)", handle, PromiseGetPromiseType(pp), EvalContextGetPass(ctx));
152 }
153 else
154 {
155 Log(LOG_LEVEL_VERBOSE, "P: BEGIN un-named promise of type \"%s\" (pass %d)", PromiseGetPromiseType(pp), EvalContextGetPass(ctx));
156 }
157
158 const size_t n = 2*CF_MAXFRAGMENT + 3;
159 char pretty_promise_name[n+1];
160 pretty_promise_name[0] = '\0';
161 StringAppendAbbreviatedPromise(pretty_promise_name, pp->promiser, n, CF_MAXFRAGMENT);
162 Log(LOG_LEVEL_VERBOSE, "P: Promiser/affected object: '%s'", pretty_promise_name);
163
164 Rlist *params = NULL;
165 char *varclass;
166 FnCall *fp;
167
168 if ((params = EvalContextGetBundleArgs(ctx)))
169 {
170 Writer *w = StringWriter();
171 RlistWrite(w, params);
172 Log(LOG_LEVEL_VERBOSE, "P: From parameterized bundle: %s(%s)", PromiseGetBundle(pp)->name, StringWriterData(w));
173 WriterClose(w);
174 }
175 else
176 {
177 Log(LOG_LEVEL_VERBOSE, "P: Part of bundle: %s", PromiseGetBundle(pp)->name);
178 }
179
180 Log(LOG_LEVEL_VERBOSE, "P: Base context class: %s", pp->classes);
181
182 if ((varclass = PromiseGetConstraintAsRval(pp, "if", RVAL_TYPE_SCALAR)) || (varclass = PromiseGetConstraintAsRval(pp, "ifvarclass", RVAL_TYPE_SCALAR)))
183 {
184 Log(LOG_LEVEL_VERBOSE, "P: \"if\" class condition: %s", varclass);
185 }
186 else if ((fp = (FnCall *)PromiseGetConstraintAsRval(pp, "if", RVAL_TYPE_FNCALL)) || (fp = (FnCall *)PromiseGetConstraintAsRval(pp, "ifvarclass", RVAL_TYPE_FNCALL)))
187 {
188 Writer *w = StringWriter();
189 FnCallWrite(w, fp);
190 Log(LOG_LEVEL_VERBOSE, "P: \"if\" class condition: %s", StringWriterData(w));
191 }
192 else if ((varclass = PromiseGetConstraintAsRval(pp, "unless", RVAL_TYPE_SCALAR)))
193 {
194 Log(LOG_LEVEL_VERBOSE, "P: \"unless\" class condition: %s", varclass);
195 }
196 else if ((fp = (FnCall *)PromiseGetConstraintAsRval(pp, "unless", RVAL_TYPE_FNCALL)))
197 {
198 Writer *w = StringWriter();
199 FnCallWrite(w, fp);
200 Log(LOG_LEVEL_VERBOSE, "P: \"unless\" class condition: %s", StringWriterData(w));
201 }
202
203 Log(LOG_LEVEL_VERBOSE, "P: Stack path: %s", EvalContextStackToString(ctx));
204
205 if (pp->comment)
206 {
207 Log(LOG_LEVEL_VERBOSE, "P:\n");
208 Log(LOG_LEVEL_VERBOSE, "P: Comment: %s", pp->comment);
209 }
210 }
211
Legend()212 void Legend()
213 {
214 Log(LOG_LEVEL_VERBOSE, "----------------------------------------------------------------");
215 Log(LOG_LEVEL_VERBOSE, "PREFIX LEGEND:");
216 Log(LOG_LEVEL_VERBOSE, " V: variable or parameter new definition in scope");
217 Log(LOG_LEVEL_VERBOSE, " C: class/context new definition ");
218 Log(LOG_LEVEL_VERBOSE, " B: bundle start/end execution marker");
219 Log(LOG_LEVEL_VERBOSE, " P: promise execution output ");
220 Log(LOG_LEVEL_VERBOSE, " A: accounting output ");
221 Log(LOG_LEVEL_VERBOSE, " T: time measurement for stated object (promise or bundle)");
222 Log(LOG_LEVEL_VERBOSE, "----------------------------------------------------------------");
223 }
224
Banner(const char * s)225 void Banner(const char *s)
226 {
227 Log(LOG_LEVEL_VERBOSE, "----------------------------------------------------------------");
228 Log(LOG_LEVEL_VERBOSE, " %s ", s);
229 Log(LOG_LEVEL_VERBOSE, "----------------------------------------------------------------");
230
231 }
232
BundleBanner(const Bundle * bp,const Rlist * params)233 void BundleBanner(const Bundle *bp, const Rlist *params)
234 {
235 Log(LOG_LEVEL_VERBOSE, "B: *****************************************************************");
236
237 if (params)
238 {
239 Writer *w = StringWriter();
240 RlistWrite(w, params);
241 Log(LOG_LEVEL_VERBOSE, "B: BEGIN bundle %s(%s)", bp->name, StringWriterData(w));
242 WriterClose(w);
243 }
244 else
245 {
246 Log(LOG_LEVEL_VERBOSE, "B: BEGIN bundle %s", bp->name);
247 }
248
249 Log(LOG_LEVEL_VERBOSE, "B: *****************************************************************");
250 }
251
EndBundleBanner(const Bundle * bp)252 void EndBundleBanner(const Bundle *bp)
253 {
254 if (bp == NULL)
255 {
256 return;
257 }
258
259 Log(LOG_LEVEL_VERBOSE, "B: *****************************************************************");
260 Log(LOG_LEVEL_VERBOSE, "B: END bundle %s", bp->name);
261 Log(LOG_LEVEL_VERBOSE, "B: *****************************************************************");
262 }
263