sw-collector: strip arch suffix from package names
[strongswan.git] / src / libimcv / plugins / imc_swima / sw_collector / sw_collector_db.c
1 /*
2 * Copyright (C) 2017 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 "sw_collector_db.h"
17
18 #include "swima/swima_event.h"
19
20 typedef struct private_sw_collector_db_t private_sw_collector_db_t;
21
22 /**
23 * Private data of an sw_collector_db_t object.
24 */
25 struct private_sw_collector_db_t {
26
27 /**
28 * Public members of sw_collector_db_state_t
29 */
30 sw_collector_db_t public;
31
32 /**
33 * Epoch
34 */
35 uint32_t epoch;
36
37 /**
38 * Event ID of last event stored in database
39 */
40 uint32_t last_eid;
41
42 /**
43 * Software collector database
44 */
45 database_t *db;
46
47 };
48
49 METHOD(sw_collector_db_t, add_event, uint32_t,
50 private_sw_collector_db_t *this, char *timestamp)
51 {
52 uint32_t eid = 0;
53
54 if (this->db->execute(this->db, &eid,
55 "INSERT INTO events (epoch, timestamp) VALUES (?, ?)",
56 DB_UINT, this->epoch, DB_TEXT, timestamp) != 1)
57 {
58 DBG1(DBG_IMC, "unable to insert event into database");
59 return 0;
60 }
61
62 return eid;
63 }
64
65 METHOD(sw_collector_db_t, get_last_event, bool,
66 private_sw_collector_db_t *this, uint32_t *eid, uint32_t *epoch,
67 char **last_time)
68 {
69 char *timestamp;
70 enumerator_t *e;
71
72 e = this->db->query(this->db,
73 "SELECT id, epoch, timestamp FROM events ORDER BY timestamp DESC",
74 DB_UINT, DB_UINT, DB_TEXT);
75 if (!e)
76 {
77 DBG1(DBG_IMC, "database query for event failed");
78 return FALSE;
79 }
80 if (e->enumerate(e, eid, epoch, &timestamp))
81 {
82 if (last_time)
83 {
84 *last_time = strdup(timestamp);
85 }
86 }
87 else
88 {
89 *eid = 0;
90 }
91 e->destroy(e);
92
93 return TRUE;
94 }
95
96 METHOD(sw_collector_db_t, add_sw_event, bool,
97 private_sw_collector_db_t *this, uint32_t eid, uint32_t sw_id,
98 uint8_t action)
99 {
100 if (this->db->execute(this->db, NULL,
101 "INSERT INTO sw_events (eid, sw_id, action) VALUES (?, ?, ?)",
102 DB_UINT, eid, DB_UINT, sw_id, DB_UINT, action) != 1)
103 {
104 DBG1(DBG_IMC, "unable to insert sw_event into database");
105 return FALSE;
106 }
107
108 return TRUE;
109 }
110
111 METHOD(sw_collector_db_t, set_sw_id, uint32_t,
112 private_sw_collector_db_t *this, char *name, char *package, char *version,
113 uint8_t source, bool installed)
114 {
115 uint32_t sw_id;
116
117 if (this->db->execute(this->db, &sw_id,
118 "INSERT INTO sw_identifiers "
119 "(name, package, version, source, installed) VALUES (?, ?, ?, ?, ?)",
120 DB_TEXT, name, DB_TEXT, package, DB_TEXT, version, DB_UINT, source,
121 DB_UINT, installed) != 1)
122 {
123 DBG1(DBG_IMC, "unable to insert sw_id into database");
124 return 0;
125 }
126
127 return sw_id;
128 }
129
130 METHOD(sw_collector_db_t, get_sw_id, uint32_t,
131 private_sw_collector_db_t *this, char *name, char **package, char **version,
132 uint8_t *source, bool *installed)
133 {
134 char *sw_package, *sw_version;
135 uint32_t sw_id = 0, sw_source, sw_installed;
136 enumerator_t *e;
137
138 /* Does software identifier already exist in database? */
139 e = this->db->query(this->db,
140 "SELECT id, package, version, source, installed "
141 "FROM sw_identifiers WHERE name = ?",
142 DB_TEXT, name, DB_UINT, DB_TEXT, DB_TEXT, DB_UINT, DB_UINT);
143 if (!e)
144 {
145 DBG1(DBG_IMC, "database query for sw_identifier failed");
146 return 0;
147 }
148 if (e->enumerate(e, &sw_id, &sw_package, &sw_version, &sw_source,
149 &sw_installed))
150 {
151 if (package)
152 {
153 *package = strdup(sw_package);
154 }
155 if (version)
156 {
157 *version = strdup(sw_version);
158 }
159 if (source)
160 {
161 *source = sw_source;
162 }
163 if (installed)
164 {
165 *installed = sw_installed;
166 }
167 }
168 e->destroy(e);
169
170 return sw_id;
171 }
172
173 METHOD(sw_collector_db_t, get_sw_id_count, uint32_t,
174 private_sw_collector_db_t *this, sw_collector_db_query_t type)
175 {
176 uint32_t count, installed;
177 enumerator_t *e;
178
179 if (type == SW_QUERY_ALL)
180 {
181 e = this->db->query(this->db,
182 "SELECT COUNT(installed) FROM sw_identifiers", DB_UINT);
183 }
184 else
185 {
186 installed = (type == SW_QUERY_INSTALLED);
187 e = this->db->query(this->db,
188 "SELECT COUNT(installed) FROM sw_identifiers WHERE installed = ?",
189 DB_UINT, installed, DB_UINT);
190 }
191
192 if (!e)
193 {
194 DBG1(DBG_IMC, "database query for sw_identifier count failed");
195 return 0;
196 }
197 if (!e->enumerate(e, &count))
198 {
199 count = 0;
200 }
201 e->destroy(e);
202
203 return count;
204 }
205
206 METHOD(sw_collector_db_t, update_sw_id, bool,
207 private_sw_collector_db_t *this, uint32_t sw_id, char *name, char *version,
208 bool installed)
209 {
210 int res;
211
212 if (name && version)
213 {
214 res = this->db->execute(this->db, NULL,
215 "UPDATE sw_identifiers SET name = ?, version = ?, installed = ? "
216 "WHERE id = ?", DB_TEXT, name, DB_TEXT, version, DB_UINT, installed,
217 DB_UINT, sw_id);
218 }
219 else
220 {
221 res = this->db->execute(this->db, NULL,
222 "UPDATE sw_identifiers SET installed = ? WHERE id = ?",
223 DB_UINT, installed, DB_UINT, sw_id);
224 }
225 if (res != 1)
226 {
227 DBG1(DBG_IMC, "unable to update software identifier in database");
228 return FALSE;
229 }
230 return TRUE;
231 }
232
233 METHOD(sw_collector_db_t, update_package, int,
234 private_sw_collector_db_t *this, char *package_filter, char *package)
235 {
236 int count;
237
238 count = this->db->execute(this->db, NULL,
239 "UPDATE sw_identifiers SET package = ? WHERE package LIKE ?",
240 DB_TEXT, package, DB_TEXT, package_filter);
241 if (count < 0)
242 {
243 DBG1(DBG_IMC, "unable to update package name in database");
244 }
245
246 return count;
247 }
248
249 METHOD(sw_collector_db_t, create_sw_enumerator, enumerator_t*,
250 private_sw_collector_db_t *this, sw_collector_db_query_t type, char *package)
251 {
252 enumerator_t *e;
253 u_int installed;
254
255 if (type == SW_QUERY_ALL)
256 {
257 if (package)
258 {
259 e = this->db->query(this->db,
260 "SELECT id, name, package, version, installed "
261 "FROM sw_identifiers WHERE package = ? ORDER BY name ASC",
262 DB_TEXT, package, DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
263 }
264 else
265 {
266 e = this->db->query(this->db,
267 "SELECT id, name, package, version, installed "
268 "FROM sw_identifiers ORDER BY name ASC",
269 DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
270 }
271 }
272 else
273 {
274 installed = (type == SW_QUERY_INSTALLED);
275
276 if (package)
277 {
278 e = this->db->query(this->db,
279 "SELECT id, name, package, version, installed "
280 "FROM sw_identifiers WHERE package = ? AND installed = ? "
281 "ORDER BY name ASC", DB_TEXT, package, DB_UINT, installed,
282 DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
283 }
284 else
285 {
286 e = this->db->query(this->db,
287 "SELECT id, name, package, version, installed "
288 "FROM sw_identifiers WHERE installed = ? ORDER BY name ASC",
289 DB_UINT, installed, DB_UINT, DB_TEXT, DB_TEXT, DB_TEXT, DB_UINT);
290 }
291 }
292 if (!e)
293 {
294 DBG1(DBG_IMC, "database query for sw_identifier count failed");
295 return NULL;
296 }
297
298 return e;
299 }
300
301 METHOD(sw_collector_db_t, destroy, void,
302 private_sw_collector_db_t *this)
303 {
304 this->db->destroy(this->db);
305 free(this);
306 }
307
308 /**
309 * Described in header.
310 */
311 sw_collector_db_t *sw_collector_db_create(char *uri)
312 {
313 private_sw_collector_db_t *this;
314 uint32_t first_eid, last_eid;
315 char *first_time;
316
317 INIT(this,
318 .public = {
319 .add_event = _add_event,
320 .get_last_event = _get_last_event,
321 .add_sw_event = _add_sw_event,
322 .set_sw_id = _set_sw_id,
323 .get_sw_id = _get_sw_id,
324 .get_sw_id_count = _get_sw_id_count,
325 .update_sw_id = _update_sw_id,
326 .update_package = _update_package,
327 .create_sw_enumerator = _create_sw_enumerator,
328 .destroy = _destroy,
329 },
330 .db = lib->db->create(lib->db, uri),
331 );
332
333 if (!this->db)
334 {
335 DBG1(DBG_IMC, "opening database URI '%s' failed", uri);
336 return NULL;
337 }
338
339 /* Retrieve last event in database */
340 if (!get_last_event(this, &last_eid, &this->epoch, NULL))
341 {
342 destroy(this);
343 return NULL;
344 }
345
346 /* Create random epoch and first event if no events exist yet */
347 if (!last_eid)
348 {
349 rng_t *rng;
350
351 rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
352 if (!rng ||
353 !rng->get_bytes(rng, sizeof(uint32_t), (uint8_t*)&this->epoch))
354 {
355 DESTROY_IF(rng);
356 destroy(this);
357 DBG1(DBG_IMC, "generating random epoch value failed");
358 return NULL;
359 }
360 rng->destroy(rng);
361
362 /* strongTNC workaround - limit epoch to 31 bit unsigned integer */
363 this->epoch &= 0x7fffffff;
364
365 /* Create first event when the OS was installed */
366 first_time = lib->settings->get_str(lib->settings,
367 "sw-collector.first_time", "0000-00-00T00:00:00Z");
368 first_eid = add_event(this, first_time);
369 if (!first_eid)
370 {
371 destroy(this);
372 return NULL;
373 }
374 DBG0(DBG_IMC, "First-Date: %s, eid = %u, epoch = %u",
375 first_time, first_eid, this->epoch);
376 }
377
378 return &this->public;
379 }