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