1 #include <cf3.defs.h>
2
3 #include <rlist.h>
4 #include <generic_agent.h> // Syntax()
5 #include <cf-windows-functions.h>
6
7 static char AVAILABLE_PACKAGES_FILE_NAME[PATH_MAX];
8 static char INSTALLED_PACKAGES_FILE_NAME[PATH_MAX];
9
10 static const int MAX_PACKAGE_ENTRY_LENGTH = 256;
11
12 #define DEFAULT_ARCHITECTURE "x666"
13
14 #define Error(msg) fprintf(stderr, "%s:%d: %s", __FILE__, __LINE__, msg)
15
16 static const struct option OPTIONS[] =
17 {
18 {"clear-installed", no_argument, 0, 'c'},
19 {"clear-available", no_argument, 0, 'C'},
20 {"list-installed", no_argument, 0, 'l'},
21 {"list-available", no_argument, 0, 'L'},
22 {"populate-available", required_argument, 0, 'P'},
23 {"add", required_argument, 0, 'a'},
24 {"delete", required_argument, 0, 'd'},
25 {"reinstall", required_argument, 0, 'r'},
26 {"update", required_argument, 0, 'u'},
27 {"addupdate", required_argument, 0, 'U'},
28 {"verify", required_argument, 0, 'v'},
29 {NULL, 0, 0, '\0'}
30 };
31
32 static const char *HINTS[] =
33 {
34 "Clear all installed imaginary packages",
35 "Clear all available imaginary packages",
36 "List installed imarginary packages",
37 "List imaginary packages available to be installed",
38 "Add an imaginary package to list of available",
39 "Add an imaginary package",
40 "Delete a previously imagined package",
41 "Reinstall an imaginary package",
42 "Update a previously imagined package",
43 "Add or update an imaginary package",
44 "Verify a previously imagined package",
45 NULL
46 };
47
48 typedef struct
49 {
50 char *name;
51 char *version;
52 char *arch;
53 } Package;
54
55 typedef struct
56 {
57 char *name;
58 char *version;
59 char *arch;
60 } PackagePattern;
61
62 /******************************************************************************/
63
SerializePackage(Package * package)64 static char *SerializePackage(Package *package)
65 {
66 char *s;
67
68 xasprintf(&s, "%s:%s:%s", package->name, package->version, package->arch);
69 return s;
70 }
71
72 /******************************************************************************/
73
SerializePackagePattern(PackagePattern * pattern)74 static char *SerializePackagePattern(PackagePattern *pattern)
75 {
76 char *s;
77
78 xasprintf(&s, "%s:%s:%s", pattern->name ? pattern->name : "*",
79 pattern->version ? pattern->version : "*", pattern->arch ? pattern->arch : "*");
80 return s;
81 }
82
83 /******************************************************************************/
84
DeserializePackage(const char * entry)85 static Package *DeserializePackage(const char *entry)
86 {
87 Package *package = xcalloc(1, sizeof(Package));
88
89 char *entry_copy = xstrdup(entry);
90
91 package->name = strtok(entry_copy, ":");
92 package->version = strtok(NULL, ":");
93 package->arch = strtok(NULL, ":");
94
95 if (package->name == NULL || strcmp(package->name, "*") == 0
96 || package->version == NULL || strcmp(package->version, "*") == 0
97 || package->arch == NULL || strcmp(package->arch, "*") == 0)
98 {
99 fprintf(stderr, "Incomplete package specification: %s:%s:%s\n", package->name, package->version, package->arch);
100 exit(255);
101 }
102
103 return package;
104 }
105
106 /******************************************************************************/
107
IsWildcard(const char * str)108 static bool IsWildcard(const char *str)
109 {
110 return str && (strcmp(str, "*") == 0 || strcmp(str, "") == 0);
111 }
112
113 /******************************************************************************/
114
CheckWellformedness(const char * str)115 static void CheckWellformedness(const char *str)
116 {
117 if (str && strchr(str, '*') != NULL)
118 {
119 fprintf(stderr, "* is encountered in pattern not as wildcard: '%s'\n", str);
120 exit(255);
121 }
122 }
123
124 /******************************************************************************/
125
NewPackagePattern(const char * name,const char * version,const char * arch)126 static PackagePattern *NewPackagePattern(const char *name, const char *version, const char *arch)
127 {
128 PackagePattern *pattern = xcalloc(1, sizeof(PackagePattern));
129
130 pattern->name = name ? xstrdup(name) : NULL;
131 pattern->version = version ? xstrdup(version) : NULL;
132 pattern->arch = arch ? xstrdup(arch) : NULL;
133 return pattern;
134 }
135
136 /******************************************************************************/
137
DeserializePackagePattern(const char * entry)138 static PackagePattern *DeserializePackagePattern(const char *entry)
139 {
140 //PackagePattern *pattern = xcalloc(1, sizeof(PackagePattern));
141
142 char *entry_copy = xstrdup(entry);
143
144 char *name = strtok(entry_copy, ":");
145
146 if (IsWildcard(name))
147 {
148 name = NULL;
149 }
150 CheckWellformedness(name);
151
152 char *version = strtok(NULL, ":");
153
154 if (IsWildcard(version))
155 {
156 version = NULL;
157 }
158 CheckWellformedness(version);
159
160 char *arch = strtok(NULL, ":");
161
162 if (arch == NULL)
163 {
164 arch = DEFAULT_ARCHITECTURE;
165 }
166 else if (IsWildcard(arch))
167 {
168 arch = NULL;
169 }
170 CheckWellformedness(arch);
171
172 if (strtok(NULL, ":") != NULL)
173 {
174 fprintf(stderr, "Too many delimiters are encountered in pattern: %s\n", entry);
175 exit(255);
176 }
177
178 return NewPackagePattern(name, version, arch);
179 }
180
181 /******************************************************************************/
182
ReadPackageEntries(const char * database_filename)183 static Seq *ReadPackageEntries(const char *database_filename)
184 {
185 FILE *packages_file = fopen(database_filename, "r");
186 Seq *packages = SeqNew(1000, NULL);
187
188 if (packages_file != NULL)
189 {
190 char serialized_package[MAX_PACKAGE_ENTRY_LENGTH];
191
192 while (fscanf(packages_file, "%s\n", serialized_package) != EOF)
193 {
194 Package *package = DeserializePackage(serialized_package);
195
196 SeqAppend(packages, package);
197 }
198
199 fclose(packages_file);
200 }
201
202 return packages;
203 }
204
205 /******************************************************************************/
206
SavePackages(const char * database_filename,Seq * package_entries)207 static void SavePackages(const char *database_filename, Seq *package_entries)
208 {
209 FILE *packages_file = fopen(database_filename, "w");
210
211 for (size_t i = 0; i < SeqLength(package_entries); i++)
212 {
213 fprintf(packages_file, "%s\n", SerializePackage(SeqAt(package_entries, i)));
214 }
215
216 fclose(packages_file);
217 }
218
219 /******************************************************************************/
220
MatchAllVersions(const Package * p)221 static PackagePattern *MatchAllVersions(const Package *p)
222 {
223 return NewPackagePattern(p->name, NULL, p->arch);
224 }
225
226 /******************************************************************************/
227
MatchSame(const Package * p)228 static PackagePattern *MatchSame(const Package *p)
229 {
230 return NewPackagePattern(p->name, p->version, p->arch);
231 }
232
233 /******************************************************************************/
234
MatchPackage(PackagePattern * a,Package * b)235 static bool MatchPackage(PackagePattern *a, Package *b)
236 {
237 return (a->name == NULL || strcmp(a->name, b->name) == 0) &&
238 (a->version == NULL || strcmp(a->version, b->version) == 0) &&
239 (a->arch == NULL || strcmp(a->arch, b->arch) == 0);
240 }
241
242 /******************************************************************************/
243
FindPackages(const char * database_filename,PackagePattern * pattern)244 static Seq *FindPackages(const char *database_filename, PackagePattern *pattern)
245 {
246 Seq *db = ReadPackageEntries(database_filename);
247 Seq *matching = SeqNew(1000, NULL);
248
249 for (size_t i = 0; i < SeqLength(db); i++)
250 {
251 Package *package = SeqAt(db, i);
252
253 if (MatchPackage(pattern, package))
254 {
255 SeqAppend(matching, package);
256 }
257 }
258
259 return matching;
260 }
261
262 /******************************************************************************/
263
ShowPackages(FILE * out,Seq * package_entries)264 static void ShowPackages(FILE *out, Seq *package_entries)
265 {
266 for (size_t i = 0; i < SeqLength(package_entries); i++)
267 {
268 fprintf(out, "%s\n", SerializePackage(SeqAt(package_entries, i)));
269 }
270 }
271
272 /******************************************************************************/
273
ClearPackageList(const char * db_file_name)274 static void ClearPackageList(const char *db_file_name)
275 {
276 FILE *packages_file = fopen(db_file_name, "w");
277
278 if (packages_file == NULL)
279 {
280 fprintf(stderr, "fopen(%s): %s", INSTALLED_PACKAGES_FILE_NAME, strerror(errno));
281 exit(255);
282 }
283 fclose(packages_file);
284 }
285
286 /******************************************************************************/
287
ClearInstalledPackages(void)288 static void ClearInstalledPackages(void)
289 {
290 ClearPackageList(INSTALLED_PACKAGES_FILE_NAME);
291 }
292
293 /******************************************************************************/
294
ClearAvailablePackages(void)295 static void ClearAvailablePackages(void)
296 {
297 ClearPackageList(AVAILABLE_PACKAGES_FILE_NAME);
298 }
299
300 /******************************************************************************/
301
AddPackage(PackagePattern * pattern)302 static void AddPackage(PackagePattern *pattern)
303 {
304 fprintf(stderr, "Trying to install all packages matching pattern %s\n", SerializePackagePattern(pattern));
305
306 Seq *matching_available = FindPackages(AVAILABLE_PACKAGES_FILE_NAME, pattern);
307
308 if (SeqLength(matching_available) == 0)
309 {
310 fprintf(stderr, "Unable to find any package matching %s\n", SerializePackagePattern(pattern));
311 exit(EXIT_FAILURE);
312 }
313
314 for (size_t i = 0; i < SeqLength(matching_available); i++)
315 {
316 Package *p = SeqAt(matching_available, i);
317
318 PackagePattern *pat = MatchAllVersions(p);
319
320 if (SeqLength(FindPackages(INSTALLED_PACKAGES_FILE_NAME, pat)) > 0)
321 {
322 fprintf(stderr, "Package %s is already installed.\n", SerializePackage(p));
323 exit(EXIT_FAILURE);
324 }
325 }
326
327 Seq *installed_packages = ReadPackageEntries(INSTALLED_PACKAGES_FILE_NAME);
328
329 for (size_t i = 0; i < SeqLength(matching_available); i++)
330 {
331 Package *p = SeqAt(matching_available, i);
332
333 SeqAppend(installed_packages, p);
334 fprintf(stderr, "Successfully installed package %s\n", SerializePackage(p));
335 }
336
337 SavePackages(INSTALLED_PACKAGES_FILE_NAME, installed_packages);
338 exit(EXIT_SUCCESS);
339 }
340
341 /******************************************************************************/
342
PopulateAvailable(const char * arg)343 static void PopulateAvailable(const char *arg)
344 {
345 Package *p = DeserializePackage(arg);
346 PackagePattern *pattern = MatchSame(p);
347
348 if (SeqLength(FindPackages(AVAILABLE_PACKAGES_FILE_NAME, pattern)) > 0)
349 {
350 fprintf(stderr, "Skipping already available package %s\n", SerializePackage(p));
351 return;
352 }
353
354 Seq *available_packages = ReadPackageEntries(AVAILABLE_PACKAGES_FILE_NAME);
355
356 SeqAppend(available_packages, p);
357 SavePackages(AVAILABLE_PACKAGES_FILE_NAME, available_packages);
358 }
359
360 /******************************************************************************/
361
main(int argc,char * argv[])362 int main(int argc, char *argv[])
363 {
364 extern char *optarg;
365 int option_index = 0;
366 int c;
367
368 #ifdef __MINGW32__
369 InitializeWindows();
370 #endif
371
372 char *workdir = getenv("CFENGINE_TEST_OVERRIDE_WORKDIR");
373 char *tempdir = getenv("TEMP");
374
375 if (!workdir && !tempdir)
376 {
377 fprintf(stderr, "Please set either CFENGINE_TEST_OVERRIDE_WORKDIR or TEMP environment variables\n"
378 "to a valid directory.\n");
379 return 2;
380 }
381
382 xsnprintf(AVAILABLE_PACKAGES_FILE_NAME, 256,
383 "%s/cfengine-mock-package-manager-available", workdir ? workdir : tempdir);
384 xsnprintf(INSTALLED_PACKAGES_FILE_NAME, 256,
385 "%s/cfengine-mock-package-manager-installed", workdir ? workdir : tempdir);
386
387 while ((c = getopt_long(argc, argv, "", OPTIONS, &option_index)) != EOF)
388 {
389 PackagePattern *pattern = NULL;
390
391 switch (c)
392 {
393 case 'c':
394 ClearInstalledPackages();
395 break;
396
397 case 'C':
398 ClearAvailablePackages();
399 break;
400
401 case 'l':
402 {
403 Seq *installed_packages = ReadPackageEntries(INSTALLED_PACKAGES_FILE_NAME);
404 ShowPackages(stdout, installed_packages);
405 }
406 break;
407
408 case 'L':
409 {
410 Seq *available_packages = ReadPackageEntries(AVAILABLE_PACKAGES_FILE_NAME);
411 ShowPackages(stdout, available_packages);
412 }
413 break;
414
415 case 'a':
416 pattern = DeserializePackagePattern(optarg);
417 AddPackage(pattern);
418 break;
419
420 case 'P':
421 PopulateAvailable(optarg);
422 break;
423
424 /* case 'd': */
425 /* DeletePackage(pattern); */
426 /* break; */
427
428 /* case 'r': */
429 /* ReinstallPackage(pattern); */
430 /* break; */
431
432 /* case 'u': */
433 /* UpdatePackage(pattern); */
434 /* break; */
435
436 /* case 'U': */
437 /* AddUpdatePackage(pattern); */
438 /* break; */
439
440 /* case 'v': */
441 /* VerifyPackage(pattern); */
442 /* break; */
443
444 default:
445 {
446 Writer *w = FileWriter(stdout);
447 WriterWriteHelp(w, "mock-package-manager - pretend that you are managing packages!", OPTIONS, HINTS, NULL, false, false);
448 FileWriterDetach(w);
449 }
450 exit(EXIT_FAILURE);
451 }
452
453 }
454
455 return 0;
456 }
457