blob: 058a12cbb7ca179c2e1d77abcb8fc8fead93d336 [file] [log] [blame]
Neels Hofmeyr73d14af2017-10-24 23:26:53 +02001/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
2 *
3 * All Rights Reserved
4 *
5 * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include <stdlib.h>
23#include <signal.h>
24#include <stdio.h>
25#include <getopt.h>
26#include <inttypes.h>
27#include <string.h>
Keithde50b202020-08-31 16:38:32 +020028#include <errno.h>
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020029
30#include <osmocom/core/logging.h>
31#include <osmocom/core/application.h>
32
Neels Hofmeyr2f758032019-11-20 00:37:07 +010033#include <osmocom/hlr/logging.h>
34#include <osmocom/hlr/db.h>
35#include <osmocom/hlr/rand.h>
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020036
37struct hlr_db_tool_ctx {
38 /* DB context */
39 struct db_context *dbc;
40};
41
42struct hlr_db_tool_ctx *g_hlr_db_tool_ctx;
43
44static struct {
45 const char *db_file;
46 bool bootstrap;
47 const char *import_nitb_db;
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +010048 bool db_upgrade;
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020049} cmdline_opts = {
50 .db_file = "hlr.db",
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +010051 .db_upgrade = false,
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020052};
53
Harald Welte7a476532022-11-03 11:38:41 +010054static void print_help(void)
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020055{
56 printf("\n");
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +010057 printf("Usage: osmo-hlr-db-tool [-l <hlr.db>] [create|import-nitb-db <nitb.db>]\n");
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020058 printf(" -l --database db-name The OsmoHLR database to use, default '%s'.\n",
59 cmdline_opts.db_file);
60 printf(" -h --help This text.\n");
61 printf(" -d option --debug=DMAIN:DDB:DAUC Enable debugging.\n");
62 printf(" -s --disable-color Do not print ANSI colors in the log\n");
63 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
64 printf(" -e --log-level number Set a global loglevel.\n");
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +010065 printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020066 printf(" -V --version Print the version of OsmoHLR-db-tool.\n");
67 printf("\n");
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +010068 printf("Commands:\n");
69 printf("\n");
70 printf(" create Create an empty OsmoHLR database.\n");
71 printf(" (All commands imply this if none exists yet.)\n");
72 printf("\n");
Neels Hofmeyrc82e6ad2017-11-10 16:58:42 +010073 printf(" import-nitb-db <nitb.db> Add OsmoNITB db's subscribers to OsmoHLR db.\n");
Keithde50b202020-08-31 16:38:32 +020074 printf(" Be aware that the import is somewhat lossy, only the IMSI,\n");
75 printf(" MSISDN, IMEI, nam_cs/ps, 2G auth data and last seen LU are set.\n");
76 printf(" The most recently associated IMEI from the Equipment table is used.\n");
Neels Hofmeyr73d14af2017-10-24 23:26:53 +020077}
78
79static void print_version(int print_copyright)
80{
81 printf("OsmoHLR-db-tool version %s\n", PACKAGE_VERSION);
82 if (print_copyright)
83 printf("\n"
84 "Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH\n"
85 "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\n"
86 "This is free software: you are free to change and redistribute it.\n"
87 "There is NO WARRANTY, to the extent permitted by law.\n"
88 "\n");
89}
90
91static void handle_options(int argc, char **argv)
92{
93 const char *cmd;
94
95 while (1) {
96 int option_index = 0, c;
97 static struct option long_options[] = {
98 {"help", 0, 0, 'h'},
99 {"database", 1, 0, 'l'},
100 {"debug", 1, 0, 'd'},
101 {"disable-color", 0, 0, 's'},
102 {"timestamp", 0, 0, 'T'},
103 {"log-level", 1, 0, 'e'},
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +0100104 {"db-upgrade", 0, 0, 'U' },
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200105 {"version", 0, 0, 'V' },
106 {0, 0, 0, 0}
107 };
108
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +0100109 c = getopt_long(argc, argv, "hl:d:sTe:UV",
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200110 long_options, &option_index);
111 if (c == -1)
112 break;
113
114 switch (c) {
115 case 'h':
116 print_help();
117 exit(EXIT_SUCCESS);
118 case 'l':
119 cmdline_opts.db_file = optarg;
120 break;
121 case 'd':
122 log_parse_category_mask(osmo_stderr_target, optarg);
123 break;
124 case 's':
125 log_set_use_color(osmo_stderr_target, 0);
126 break;
127 case 'T':
128 log_set_print_timestamp(osmo_stderr_target, 1);
129 break;
130 case 'e':
131 log_set_log_level(osmo_stderr_target, atoi(optarg));
132 break;
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +0100133 case 'U':
134 cmdline_opts.db_upgrade = true;
135 break;
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200136 case 'V':
137 print_version(1);
138 exit(EXIT_SUCCESS);
139 break;
140 default:
141 /* catch unknown options *as well as* missing arguments. */
142 fprintf(stderr, "Error in command line options. Exiting.\n");
143 exit(EXIT_FAILURE);
144 break;
145 }
146 }
147
148 if (argc - optind <= 0) {
149 fprintf(stderr, "Error: You must specify a command.\n");
150 print_help();
151 exit(EXIT_FAILURE);
152 }
153
154 cmd = argv[optind++];
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200155
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +0100156 if (!strcmp(cmd, "create")) {
157 /* Nothing to do, just run the main program to open the database without running any
158 * action, which will bootstrap all tables. */
159 } else if (!strcmp(cmd, "import-nitb-db")) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200160 if (argc - optind < 1) {
161 fprintf(stderr, "You must specify an input db file\n");
162 print_help();
163 exit(EXIT_FAILURE);
164 }
165 cmdline_opts.import_nitb_db = argv[optind++];
166 } else {
167 fprintf(stderr, "Error: Unknown command `%s'\n", cmd);
168 print_help();
169 exit(EXIT_FAILURE);
170 }
Neels Hofmeyr8db49062017-11-10 16:59:49 +0100171
172 if (argc - optind > 0) {
173 fprintf(stderr, "Too many arguments: '%s'\n", argv[optind]);
174 print_help();
175 exit(EXIT_FAILURE);
176 }
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200177}
178
179static void signal_hdlr(int signal)
180{
181 switch (signal) {
182 case SIGINT:
183 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
184 db_close(g_hlr_db_tool_ctx->dbc);
185 log_fini();
186 talloc_report_full(g_hlr_db_tool_ctx, stderr);
187 exit(EXIT_SUCCESS);
188 break;
189 case SIGUSR1:
190 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
191 talloc_report_full(g_hlr_db_tool_ctx, stderr);
192 break;
193 }
194}
195
196sqlite3 *open_nitb_db(const char *filename)
197{
198 int rc;
199 sqlite3 *nitb_db = NULL;
200
201 rc = sqlite3_open(filename, &nitb_db);
202 if (rc != SQLITE_OK) {
203 LOGP(DDB, LOGL_ERROR, "Unable to open OsmoNITB DB %s; rc = %d\n", filename, rc);
204 return NULL;
205 }
206
207 return nitb_db;
208}
209
210enum nitb_stmt {
211 NITB_SELECT_SUBSCR,
212 NITB_SELECT_AUTH_KEYS,
213};
214
215static const char *nitb_stmt_sql[] = {
216 [NITB_SELECT_SUBSCR] =
Keithde50b202020-08-31 16:38:32 +0200217 "SELECT s.imsi, s.id, s.extension, s.authorized,"
218 " SUBSTR(e.imei,0,15), STRFTIME('%s', s.expire_lu)"
219 " FROM Subscriber s LEFT JOIN"
220 " (SELECT imei, subscriber_id, MAX(Equipment.updated) AS updated"
221 " FROM Equipment,EquipmentWatch"
222 " WHERE Equipment.id = EquipmentWatch.equipment_id"
223 " GROUP BY EquipmentWatch.subscriber_id) e"
224 " ON e.subscriber_id = s.id"
225 " ORDER by s.id",
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200226 [NITB_SELECT_AUTH_KEYS] =
227 "SELECT algorithm_id, a3a8_ki from authkeys"
228 " WHERE subscriber_id = $subscr_id",
229};
230
231sqlite3_stmt *nitb_stmt[ARRAY_SIZE(nitb_stmt_sql)] = {};
232
Keithde50b202020-08-31 16:38:32 +0200233enum hlr_db_stmt {
234 HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI,
235};
236
237static const char *hlr_db_stmt_sql[] = {
238 [HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI] =
239 "UPDATE subscriber SET last_lu_seen = datetime($last_lu, 'unixepoch') WHERE imsi = $imsi",
240};
241
242sqlite3_stmt *hlr_db_stmt[ARRAY_SIZE(hlr_db_stmt_sql)] = {};
243
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200244size_t _dbd_decode_binary(const unsigned char *in, unsigned char *out);
245
Keithde50b202020-08-31 16:38:32 +0200246/*! Set a subscriber's LU timestamp in the HLR database.
247 * In normal operations there is never any need to explicitly
248 * update the value of last_lu_seen, so this function can live here.
249 *
250 * \param[in,out] dbc database context.
251 * \param[in] imsi ASCII string of IMSI digits
252 * \param[in] imei ASCII string of identifier digits, or NULL to remove the IMEI.
253 * \returns 0 on success, -ENOENT when the given subscriber does not exist,
254 * -EIO on database errors.
255 */
256int db_subscr_update_lu_by_imsi(struct db_context *dbc, const char* imsi, const int last_lu)
257{
258 int rc, ret = 0;
259
260 sqlite3_stmt *stmt = hlr_db_stmt[HLR_DB_STMT_SET_IMPLICIT_LU_BY_IMSI];
261
262 if (!db_bind_text(stmt, "$imsi", imsi))
263 return -EIO;
264 if (last_lu && !db_bind_int(stmt, "$last_lu", last_lu))
265 return -EIO;
266
267 /* execute the statement */
268 rc = sqlite3_step(stmt);
269 if (rc != SQLITE_DONE) {
270 LOGP(DAUC, LOGL_ERROR, "Update last_lu_seen for subscriber IMSI='%s': SQL Error: %s\n", imsi,
271 sqlite3_errmsg(dbc->db));
272 ret = -EIO;
273 goto out;
274 }
275
276 /* verify execution result */
277 rc = sqlite3_changes(dbc->db);
278 if (!rc) {
279 LOGP(DAUC, LOGL_ERROR, "Cannot update last_lu_seen for subscriber IMSI='%s': no such subscriber\n", imsi);
280 ret = -ENOENT;
281 } else if (rc != 1) {
282 LOGP(DAUC, LOGL_ERROR, "Update last_lu_seen for subscriber IMSI='%s': SQL modified %d rows (expected 1)\n",
283 imsi, rc);
284 ret = -EIO;
285 }
286
287out:
288 db_remove_reset(stmt);
289 return ret;
290}
291
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200292void import_nitb_subscr_aud(sqlite3 *nitb_db, const char *imsi, int64_t nitb_id, int64_t hlr_id)
293{
294 int rc;
295 struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
296 sqlite3_stmt *stmt;
297
298 int count = 0;
299
300 stmt = nitb_stmt[NITB_SELECT_AUTH_KEYS];
301 if (!db_bind_int(stmt, NULL, nitb_id))
302 return;
303
304 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
305 const void *blob;
306 unsigned int blob_size;
307 static unsigned char buf[4096];
308 static char ki[128];
309 int decoded_size;
310 struct sub_auth_data_str aud2g = {
311 .type = OSMO_AUTH_TYPE_GSM,
312 .algo = OSMO_AUTH_ALG_NONE,
313 .u.gsm.ki = ki,
314 };
315
316 aud2g.algo = sqlite3_column_int(stmt, 0);
317
318 if (count) {
319 LOGP(DDB, LOGL_ERROR,
320 "Warning: subscriber has more than one auth key,"
321 " importing only the first key, for IMSI=%s\n",
322 imsi);
323 break;
324 }
325
326 blob = sqlite3_column_blob(stmt, 1);
327 blob_size = sqlite3_column_bytes(stmt, 1);
328
329 if (blob_size > sizeof(buf)) {
330 LOGP(DDB, LOGL_ERROR,
331 "OsmoNITB import to %s: Cannot import auth data for IMSI %s:"
332 " too large blob: %u\n",
333 dbc->fname, imsi, blob_size);
334 db_remove_reset(stmt);
335 continue;
336 }
337
338 decoded_size = _dbd_decode_binary(blob, buf);
339 osmo_strlcpy(ki, osmo_hexdump_nospc(buf, decoded_size), sizeof(ki));
340
341 db_subscr_update_aud_by_id(dbc, hlr_id, &aud2g);
342 count ++;
343 }
344
345 if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
346 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
347 " during stmt '%s'",
348 rc, sqlite3_errmsg(nitb_db),
349 nitb_stmt_sql[NITB_SELECT_AUTH_KEYS]);
350 }
351
352 db_remove_reset(stmt);
353}
354
355void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
356{
357 struct db_context *dbc = g_hlr_db_tool_ctx->dbc;
358 int rc;
359 struct hlr_subscriber subscr;
360
361 int64_t nitb_id;
362 int64_t imsi;
363 char imsi_str[32];
364 bool authorized;
Keithde50b202020-08-31 16:38:32 +0200365 int last_lu_int;
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200366
367 imsi = sqlite3_column_int64(stmt, 0);
368
Stefan Sperling705b61b2018-12-07 12:44:50 +0100369 snprintf(imsi_str, sizeof(imsi_str), "%" PRId64, imsi);
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200370
Oliver Smithcd2af5e2019-03-06 13:17:39 +0100371 rc = db_subscr_create(dbc, imsi_str, DB_SUBSCR_FLAG_NAM_CS | DB_SUBSCR_FLAG_NAM_PS);
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100372 if (rc < 0) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200373 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: failed to create IMSI %s: %d: %s\n",
374 dbc->fname,
375 imsi_str,
376 rc,
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100377 strerror(-rc));
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200378 /* on error, still attempt to continue */
379 }
380
381 nitb_id = sqlite3_column_int64(stmt, 1);
382 copy_sqlite3_text_to_buf(subscr.msisdn, stmt, 2);
383 authorized = sqlite3_column_int(stmt, 3) ? true : false;
Keithde50b202020-08-31 16:38:32 +0200384 copy_sqlite3_text_to_buf(subscr.imei, stmt, 4);
385 /* Default periodic LU was 30 mins and the expire_lu
386 * was twice that + 1 min
387 */
388 last_lu_int = sqlite3_column_int(stmt, 5) - 3660;
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200389
390 db_subscr_update_msisdn_by_imsi(dbc, imsi_str, subscr.msisdn);
Keithde50b202020-08-31 16:38:32 +0200391 /* In case the subscriber was somehow never seen, invent an IMEI */
392 if (strlen(subscr.imei) == 14)
393 db_subscr_update_imei_by_imsi(dbc, imsi_str, subscr.imei);
394 db_subscr_update_lu_by_imsi(dbc, imsi_str, last_lu_int);
395
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200396 db_subscr_nam(dbc, imsi_str, authorized, true);
397 db_subscr_nam(dbc, imsi_str, authorized, false);
398
399 /* find the just created id */
400 rc = db_subscr_get_by_imsi(dbc, imsi_str, &subscr);
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100401 if (rc < 0) {
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200402 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB import to %s: created IMSI %s,"
403 " but failed to get new subscriber id: %d: %s\n",
404 dbc->fname,
405 imsi_str,
406 rc,
Neels Hofmeyr87a04b62017-11-07 13:20:44 +0100407 strerror(-rc));
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200408 return;
409 }
410
411 OSMO_ASSERT(!strcmp(imsi_str, subscr.imsi));
412
413 import_nitb_subscr_aud(nitb_db, imsi_str, nitb_id, subscr.id);
414}
415
416int import_nitb_db(void)
417{
418 int i;
419 int ret;
420 int rc;
421 const char *sql;
422 sqlite3_stmt *stmt;
423
424 sqlite3 *nitb_db = open_nitb_db(cmdline_opts.import_nitb_db);
425
426 if (!nitb_db)
427 return -1;
428 ret = 0;
429
430 for (i = 0; i < ARRAY_SIZE(nitb_stmt_sql); i++) {
431 sql = nitb_stmt_sql[i];
432 rc = sqlite3_prepare_v2(nitb_db, sql, -1, &nitb_stmt[i], NULL);
433 if (rc != SQLITE_OK) {
434 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: Unable to prepare SQL statement '%s'\n", sql);
435 ret = -1;
436 goto out_free;
437 }
438 }
439
Keithde50b202020-08-31 16:38:32 +0200440 for (i = 0; i < ARRAY_SIZE(hlr_db_stmt_sql); i++) {
441 sql = hlr_db_stmt_sql[i];
442 rc = sqlite3_prepare_v2(g_hlr_db_tool_ctx->dbc->db, hlr_db_stmt_sql[i], -1,
443 &hlr_db_stmt[i], NULL);
444 if (rc != SQLITE_OK) {
445 LOGP(DDB, LOGL_ERROR, "OsmoHLR DB: Unable to prepare SQL statement '%s'\n", sql);
446 ret = -1;
447 goto out_free;
448 }
449 }
450
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200451 stmt = nitb_stmt[NITB_SELECT_SUBSCR];
452
453 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
454 import_nitb_subscr(nitb_db, stmt);
455 /* On failure, carry on with the rest. */
456 }
457 if (rc != SQLITE_DONE) {
458 LOGP(DDB, LOGL_ERROR, "OsmoNITB DB: SQL error: (%d) %s,"
459 " during stmt '%s'",
460 rc, sqlite3_errmsg(nitb_db),
461 nitb_stmt_sql[NITB_SELECT_SUBSCR]);
462 goto out_free;
463 }
464
465 db_remove_reset(stmt);
466 sqlite3_finalize(stmt);
467
468out_free:
469 sqlite3_close(nitb_db);
470 return ret;
471}
472
473int main(int argc, char **argv)
474{
475 int rc;
476 int (*main_action)(void);
Keithde50b202020-08-31 16:38:32 +0200477 int i;
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200478 main_action = NULL;
479
480 g_hlr_db_tool_ctx = talloc_zero(NULL, struct hlr_db_tool_ctx);
481 OSMO_ASSERT(g_hlr_db_tool_ctx);
482 talloc_set_name_const(g_hlr_db_tool_ctx, "OsmoHLR-db-tool");
483
Pau Espin Pedrol51530312018-04-17 15:07:06 +0200484 rc = osmo_init_logging2(g_hlr_db_tool_ctx, &hlr_log_info);
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200485 if (rc < 0) {
486 fprintf(stderr, "Error initializing logging\n");
487 exit(EXIT_FAILURE);
488 }
489
490 handle_options(argc, argv);
491
492 if (cmdline_opts.import_nitb_db) {
493 if (main_action)
494 goto too_many_actions;
495 main_action = import_nitb_db;
496 }
Neels Hofmeyr0959e8b2017-11-10 16:58:00 +0100497 /* Future: add more main_actions, besides import-nitb-db, here.
498 * For command 'create', no action is required. */
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200499
500 /* Just in case any db actions need randomness */
501 rc = rand_init();
502 if (rc < 0) {
503 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
504 exit(EXIT_FAILURE);
505 }
506
Stefan Sperling8f3a7cc2018-11-27 12:10:45 +0100507 g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file, true, cmdline_opts.db_upgrade);
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200508 if (!g_hlr_db_tool_ctx->dbc) {
509 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
510 exit(EXIT_FAILURE);
511 }
512
513 osmo_init_ignore_signals();
514 signal(SIGINT, &signal_hdlr);
515 signal(SIGUSR1, &signal_hdlr);
516
517 rc = 0;
518 if (main_action)
519 rc = (*main_action)();
520
Keithde50b202020-08-31 16:38:32 +0200521 /* db_close will only finalize statments in g_hlr_db_tool_ctx->dbc->stmt
522 * it is ok to call finalize on NULL */
523 for (i = 0; i < ARRAY_SIZE(hlr_db_stmt); i++) {
524 sqlite3_finalize(hlr_db_stmt[i]);
525 }
Neels Hofmeyr73d14af2017-10-24 23:26:53 +0200526 db_close(g_hlr_db_tool_ctx->dbc);
527 log_fini();
528 exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
529
530too_many_actions:
531 fprintf(stderr, "Too many actions requested.\n");
532 log_fini();
533 exit(EXIT_FAILURE);
534}
535
536/* stubs */
537void lu_op_alloc_conn(void) { OSMO_ASSERT(0); }
538void lu_op_tx_del_subscr_data(void) { OSMO_ASSERT(0); }
539void lu_op_free(void) { OSMO_ASSERT(0); }