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