Merged libpts into libimcv
[strongswan.git] / src / libimcv / swid / swid_inventory.c
1 /*
2 * Copyright (C) 2013-2014 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "swid_inventory.h"
17 #include "swid_tag.h"
18 #include "swid_tag_id.h"
19
20 #include <collections/linked_list.h>
21 #include <bio/bio_writer.h>
22 #include <utils/debug.h>
23
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <libgen.h>
29 #include <errno.h>
30
31 typedef struct private_swid_inventory_t private_swid_inventory_t;
32
33 /**
34 * Private data of a swid_inventory_t object.
35 *
36 */
37 struct private_swid_inventory_t {
38
39 /**
40 * Public swid_inventory_t interface.
41 */
42 swid_inventory_t public;
43
44 /**
45 * Full SWID tags or just SWID tag IDs
46 */
47 bool full_tags;
48
49 /**
50 * List of SWID tags or tag IDs
51 */
52 linked_list_t *list;
53 };
54
55 /**
56 * Read SWID tags issued by the swid_generator tool
57 */
58 static status_t read_swid_tags(private_swid_inventory_t *this, FILE *file)
59 {
60 swid_tag_t *tag;
61 bio_writer_t *writer;
62 chunk_t tag_encoding, tag_file_path = chunk_empty;
63 bool more_tags = TRUE, last_newline, end_of_tag;
64 char line[8192];
65 size_t len;
66
67 while (more_tags)
68 {
69 last_newline = TRUE;
70 end_of_tag = FALSE;
71 writer = bio_writer_create(512);
72 do
73 {
74 if (!fgets(line, sizeof(line), file))
75 {
76 more_tags = FALSE;
77 end_of_tag = TRUE;
78 break;
79 }
80 len = strlen(line);
81
82 if (last_newline && line[0] == '\n')
83 {
84 end_of_tag = TRUE;
85 break;
86 }
87 else
88 {
89 last_newline = (line[len-1] == '\n');
90 writer->write_data(writer, chunk_create(line, len));
91 }
92 }
93 while (!end_of_tag);
94
95 tag_encoding = writer->get_buf(writer);
96
97 if (tag_encoding.len > 1)
98 {
99 /* remove trailing newline if present */
100 if (tag_encoding.ptr[tag_encoding.len - 1] == '\n')
101 {
102 tag_encoding.len--;
103 }
104 DBG3(DBG_IMC, " %.*s", tag_encoding.len, tag_encoding.ptr);
105
106 tag = swid_tag_create(tag_encoding, tag_file_path);
107 this->list->insert_last(this->list, tag);
108 }
109 writer->destroy(writer);
110 }
111
112 return SUCCESS;
113 }
114
115 /**
116 * Read SWID tag or software IDs issued by the swid_generator tool
117 */
118 static status_t read_swid_tag_ids(private_swid_inventory_t *this, FILE *file)
119 {
120 swid_tag_id_t *tag_id;
121 chunk_t tag_creator, unique_sw_id, tag_file_path = chunk_empty;
122 char line[BUF_LEN];
123
124 while (TRUE)
125 {
126 char *separator;
127 size_t len;
128
129 if (!fgets(line, sizeof(line), file))
130 {
131 return SUCCESS;
132 }
133 len = strlen(line);
134
135 /* remove trailing newline if present */
136 if (len > 0 && line[len - 1] == '\n')
137 {
138 len--;
139 }
140 DBG3(DBG_IMC, " %.*s", len, line);
141
142 separator = strchr(line, '_');
143 if (!separator)
144 {
145 DBG1(DBG_IMC, "separation of regid from unique software ID failed");
146 return FAILED;
147 }
148 tag_creator = chunk_create(line, separator - line);
149 separator++;
150
151 unique_sw_id = chunk_create(separator, len - (separator - line));
152 tag_id = swid_tag_id_create(tag_creator, unique_sw_id, tag_file_path);
153 this->list->insert_last(this->list, tag_id);
154 }
155 }
156
157 static status_t generate_tags(private_swid_inventory_t *this, char *generator,
158 swid_inventory_t *targets, bool pretty, bool full)
159 {
160 FILE *file;
161 char command[BUF_LEN];
162 char doc_separator[] = "'\n\n'";
163
164 status_t status = SUCCESS;
165
166 if (targets->get_count(targets) == 0)
167 {
168 /* Assemble the SWID generator command */
169 if (this->full_tags)
170 {
171 snprintf(command, BUF_LEN, "%s swid --doc-separator %s%s%s",
172 generator, doc_separator, pretty ? " --pretty" : "",
173 full ? " --full" : "");
174 }
175 else
176 {
177 snprintf(command, BUF_LEN, "%s software-id", generator);
178 }
179
180 /* Open a pipe stream for reading the SWID generator output */
181 file = popen(command, "r");
182 if (!file)
183 {
184 DBG1(DBG_IMC, "failed to run swid_generator command");
185 return NOT_SUPPORTED;
186 }
187
188 if (this->full_tags)
189 {
190 DBG2(DBG_IMC, "SWID tag generation by package manager");
191 status = read_swid_tags(this, file);
192 }
193 else
194 {
195 DBG2(DBG_IMC, "SWID tag ID generation by package manager");
196 status = read_swid_tag_ids(this, file);
197 }
198 pclose(file);
199 }
200 else if (this->full_tags)
201 {
202 swid_tag_id_t *tag_id;
203 enumerator_t *enumerator;
204
205 enumerator = targets->create_enumerator(targets);
206 while (enumerator->enumerate(enumerator, &tag_id))
207 {
208 char software_id[BUF_LEN];
209 chunk_t tag_creator, unique_sw_id;
210
211 tag_creator = tag_id->get_tag_creator(tag_id);
212 unique_sw_id = tag_id->get_unique_sw_id(tag_id, NULL);
213 snprintf(software_id, BUF_LEN, "%.*s_%.*s",
214 tag_creator.len, tag_creator.ptr,
215 unique_sw_id.len, unique_sw_id.ptr);
216
217 /* Assemble the SWID generator command */
218 snprintf(command, BUF_LEN, "%s swid --software-id %s%s%s",
219 generator, software_id, pretty ? " --pretty" : "",
220 full ? " --full" : "");
221
222 /* Open a pipe stream for reading the SWID generator output */
223 file = popen(command, "r");
224 if (!file)
225 {
226 DBG1(DBG_IMC, "failed to run swid_generator command");
227 return NOT_SUPPORTED;
228 }
229 status = read_swid_tags(this, file);
230 pclose(file);
231
232 if (status != SUCCESS)
233 {
234 break;
235 }
236 }
237 enumerator->destroy(enumerator);
238 }
239
240 return status;
241 }
242
243 static bool collect_tags(private_swid_inventory_t *this, char *pathname,
244 swid_inventory_t *targets)
245 {
246 char *rel_name, *abs_name;
247 struct stat st;
248 bool success = FALSE;
249 enumerator_t *enumerator;
250
251 enumerator = enumerator_create_directory(pathname);
252 if (!enumerator)
253 {
254 DBG1(DBG_IMC, "directory '%s' can not be opened, %s",
255 pathname, strerror(errno));
256 return FALSE;
257 }
258 DBG2(DBG_IMC, "entering %s", pathname);
259
260 while (enumerator->enumerate(enumerator, &rel_name, &abs_name, &st))
261 {
262 char * start, *stop;
263 chunk_t tag_creator;
264 chunk_t unique_sw_id = chunk_empty, tag_file_path = chunk_empty;
265
266 if (!strstr(rel_name, "regid."))
267 {
268 continue;
269 }
270 if (S_ISDIR(st.st_mode))
271 {
272 /* In case of a targeted request */
273 if (targets->get_count(targets))
274 {
275 enumerator_t *target_enumerator;
276 swid_tag_id_t *tag_id;
277 bool match = FALSE;
278
279 target_enumerator = targets->create_enumerator(targets);
280 while (target_enumerator->enumerate(target_enumerator, &tag_id))
281 {
282 if (chunk_equals(tag_id->get_tag_creator(tag_id),
283 chunk_from_str(rel_name)))
284 {
285 match = TRUE;
286 break;
287 }
288 }
289 target_enumerator->destroy(target_enumerator);
290
291 if (!match)
292 {
293 continue;
294 }
295 }
296
297 if (!collect_tags(this, abs_name, targets))
298 {
299 goto end;
300 }
301 continue;
302 }
303
304 /* parse the regid filename into its components */
305 start = rel_name;
306 stop = strchr(start, '_');
307 if (!stop)
308 {
309 DBG1(DBG_IMC, " %s", rel_name);
310 DBG1(DBG_IMC, " '_' separator not found");
311 goto end;
312 }
313 tag_creator = chunk_create(start, stop-start);
314 start = stop + 1;
315
316 stop = strstr(start, ".swidtag");
317 if (!stop)
318 {
319 DBG1(DBG_IMC, " %s", rel_name);
320 DBG1(DBG_IMC, " swidtag postfix not found");
321 goto end;
322 }
323 unique_sw_id = chunk_create(start, stop-start);
324 tag_file_path = chunk_from_str(abs_name);
325
326 /* In case of a targeted request */
327 if (targets->get_count(targets))
328 {
329 chunk_t target_unique_sw_id, target_tag_creator;
330 enumerator_t *target_enumerator;
331 swid_tag_id_t *tag_id;
332 bool match = FALSE;
333
334 target_enumerator = targets->create_enumerator(targets);
335 while (target_enumerator->enumerate(target_enumerator, &tag_id))
336 {
337 target_unique_sw_id = tag_id->get_unique_sw_id(tag_id, NULL);
338 target_tag_creator = tag_id->get_tag_creator(tag_id);
339
340 if (chunk_equals(target_unique_sw_id, unique_sw_id) &&
341 chunk_equals(target_tag_creator, tag_creator))
342 {
343 match = TRUE;
344 break;
345 }
346 }
347 target_enumerator->destroy(target_enumerator);
348
349 if (!match)
350 {
351 continue;
352 }
353 }
354 DBG2(DBG_IMC, " %s", rel_name);
355
356 if (this->full_tags)
357 {
358 swid_tag_t *tag;
359 chunk_t *xml_tag;
360
361 xml_tag = chunk_map(abs_name, FALSE);
362 if (!xml_tag)
363 {
364 DBG1(DBG_IMC, " opening '%s' failed: %s", abs_name,
365 strerror(errno));
366 goto end;
367 }
368
369 tag = swid_tag_create(*xml_tag, tag_file_path);
370 this->list->insert_last(this->list, tag);
371 chunk_unmap(xml_tag);
372 }
373 else
374 {
375 swid_tag_id_t *tag_id;
376
377 tag_id = swid_tag_id_create(tag_creator, unique_sw_id, tag_file_path);
378 this->list->insert_last(this->list, tag_id);
379 }
380 }
381 success = TRUE;
382
383 end:
384 enumerator->destroy(enumerator);
385 DBG2(DBG_IMC, "leaving %s", pathname);
386
387 return success;
388 }
389
390 METHOD(swid_inventory_t, collect, bool,
391 private_swid_inventory_t *this, char *directory, char *generator,
392 swid_inventory_t *targets, bool pretty, bool full)
393 {
394 /**
395 * Tags are generated by a package manager
396 */
397 generate_tags(this, generator, targets, pretty, full);
398
399 /**
400 * Collect swidtag files by iteratively entering all directories in
401 * the tree under the "directory" path.
402 */
403 return collect_tags(this, directory, targets);
404 }
405
406 METHOD(swid_inventory_t, add, void,
407 private_swid_inventory_t *this, void *item)
408 {
409 this->list->insert_last(this->list, item);
410 }
411
412 METHOD(swid_inventory_t, get_count, int,
413 private_swid_inventory_t *this)
414 {
415 return this->list->get_count(this->list);
416 }
417
418 METHOD(swid_inventory_t, create_enumerator, enumerator_t*,
419 private_swid_inventory_t *this)
420 {
421 return this->list->create_enumerator(this->list);
422 }
423
424 METHOD(swid_inventory_t, destroy, void,
425 private_swid_inventory_t *this)
426 {
427 if (this->full_tags)
428 {
429 this->list->destroy_offset(this->list, offsetof(swid_tag_t, destroy));
430 }
431 else
432 {
433 this->list->destroy_offset(this->list, offsetof(swid_tag_id_t, destroy));
434 }
435 free(this);
436 }
437
438 /**
439 * See header
440 */
441 swid_inventory_t *swid_inventory_create(bool full_tags)
442 {
443 private_swid_inventory_t *this;
444
445 INIT(this,
446 .public = {
447 .collect = _collect,
448 .add = _add,
449 .get_count = _get_count,
450 .create_enumerator = _create_enumerator,
451 .destroy = _destroy,
452 },
453 .full_tags = full_tags,
454 .list = linked_list_create(),
455 );
456
457 return &this->public;
458 }