blob: 6fc9604bcd058b6aa2ae75ba7656a46c3ff20f3b [file] [log] [blame]
Harald Welte936f6722016-05-03 18:51:18 +02001/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
Harald Welteaabae9e2016-04-28 12:48:14 +020020#include <signal.h>
Harald Weltee687be52016-05-03 18:49:27 +020021#include <errno.h>
Harald Welteaabae9e2016-04-28 12:48:14 +020022
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +010023#include <getopt.h>
24
Harald Weltee72cf552016-04-28 07:18:49 +020025#include <osmocom/core/msgb.h>
26#include <osmocom/core/logging.h>
27#include <osmocom/core/application.h>
28#include <osmocom/gsm/gsup.h>
Max2fc63a62016-12-20 16:49:20 +010029#include <osmocom/gsm/apn.h>
Neels Hofmeyr627de842016-12-19 13:16:06 +010030#include <osmocom/gsm/gsm48_ie.h>
Harald Weltee72cf552016-04-28 07:18:49 +020031
32#include "db.h"
33#include "logging.h"
34#include "gsup_server.h"
Harald Weltee687be52016-05-03 18:49:27 +020035#include "gsup_router.h"
Harald Weltee72cf552016-04-28 07:18:49 +020036#include "rand.h"
37
38static struct db_context *g_dbc;
39
Harald Weltee687be52016-05-03 18:49:27 +020040/***********************************************************************
41 * Send Auth Info handling
42 ***********************************************************************/
43
Harald Weltee72cf552016-04-28 07:18:49 +020044/* process an incoming SAI request */
45static int rx_send_auth_info(struct osmo_gsup_conn *conn,
46 const struct osmo_gsup_message *gsup)
47{
48 struct osmo_gsup_message gsup_out;
49 struct msgb *msg_out;
50 int rc;
51
52 /* initialize return message structure */
53 memset(&gsup_out, 0, sizeof(gsup_out));
Harald Weltee72cf552016-04-28 07:18:49 +020054 memcpy(&gsup_out.imsi, &gsup->imsi, sizeof(gsup_out.imsi));
55
56 rc = db_get_auc(g_dbc, gsup->imsi, gsup_out.auth_vectors,
57 ARRAY_SIZE(gsup_out.auth_vectors),
Harald Welte9be0d2f2016-06-10 17:34:02 +020058 gsup->rand, gsup->auts);
Harald Weltecfc752b2016-05-05 16:38:14 +020059 if (rc < 0) {
Harald Weltee72cf552016-04-28 07:18:49 +020060 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
Harald Weltecfc752b2016-05-05 16:38:14 +020061 gsup_out.cause = GMM_CAUSE_NET_FAIL;
62 } else if (rc == 0) {
63 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR;
64 gsup_out.cause = GMM_CAUSE_IMSI_UNKNOWN;
Harald Welte15db8262016-05-05 16:50:39 +020065 } else {
66 gsup_out.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT;
67 gsup_out.num_auth_vectors = rc;
Harald Weltee72cf552016-04-28 07:18:49 +020068 }
69
Harald Weltee687be52016-05-03 18:49:27 +020070 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
Harald Weltee72cf552016-04-28 07:18:49 +020071 osmo_gsup_encode(msg_out, &gsup_out);
72 return osmo_gsup_conn_send(conn, msg_out);
73}
74
Harald Weltee687be52016-05-03 18:49:27 +020075/***********************************************************************
76 * LU Operation State / Structure
77 ***********************************************************************/
78
79static LLIST_HEAD(g_lu_ops);
80
81#define CANCEL_TIMEOUT_SECS 30
82#define ISD_TIMEOUT_SECS 30
83
84enum lu_state {
85 LU_S_NULL,
86 LU_S_LU_RECEIVED,
87 LU_S_CANCEL_SENT,
88 LU_S_CANCEL_ACK_RECEIVED,
89 LU_S_ISD_SENT,
90 LU_S_ISD_ACK_RECEIVED,
91 LU_S_COMPLETE,
92};
93
94static const struct value_string lu_state_names[] = {
95 { LU_S_NULL, "NULL" },
96 { LU_S_LU_RECEIVED, "LU RECEIVED" },
97 { LU_S_CANCEL_SENT, "CANCEL SENT" },
98 { LU_S_CANCEL_ACK_RECEIVED, "CANCEL-ACK RECEIVED" },
99 { LU_S_ISD_SENT, "ISD SENT" },
100 { LU_S_ISD_ACK_RECEIVED, "ISD-ACK RECEIVED" },
101 { LU_S_COMPLETE, "COMPLETE" },
102 { 0, NULL }
103};
104
105struct lu_operation {
106 /*! entry in global list of location update operations */
107 struct llist_head list;
108 /*! to which gsup_server do we belong */
109 struct osmo_gsup_server *gsup_server;
110 /*! state of the location update */
111 enum lu_state state;
112 /*! CS (false) or PS (true) Location Update? */
113 bool is_ps;
114 /*! currently running timer */
115 struct osmo_timer_list timer;
116
117 /*! subscriber related to this operation */
118 struct hlr_subscriber subscr;
119 /*! peer VLR/SGSN starting the request */
120 uint8_t *peer;
121};
122
Neels Hofmeyr6eed3222016-12-11 01:21:49 +0100123void lu_op_tx_insert_subscr_data(struct lu_operation *luop);
124
Harald Weltee687be52016-05-03 18:49:27 +0200125void lu_op_statechg(struct lu_operation *luop, enum lu_state new_state)
126{
127 enum lu_state old_state = luop->state;
128
129 DEBUGP(DMAIN, "LU OP state change: %s -> ",
130 get_value_string(lu_state_names, old_state));
131 DEBUGPC(DMAIN, "%s\n",
132 get_value_string(lu_state_names, new_state));
133
134 luop->state = new_state;
135}
136
137struct lu_operation *lu_op_by_imsi(const char *imsi)
138{
139 struct lu_operation *luop;
140
141 llist_for_each_entry(luop, &g_lu_ops, list) {
142 if (!strcmp(imsi, luop->subscr.imsi))
143 return luop;
144 }
145 return NULL;
146}
147
148/* Send a msgb to a given address using routing */
149int osmo_gsup_addr_send(struct osmo_gsup_server *gs,
150 const uint8_t *addr, size_t addrlen,
151 struct msgb *msg)
152{
153 struct osmo_gsup_conn *conn;
154
155 conn = gsup_route_find(gs, addr, addrlen);
156 if (!conn) {
157 DEBUGP(DMAIN, "Cannot find route for addr %s\n", addr);
158 msgb_free(msg);
159 return -ENODEV;
160 }
161
162 return osmo_gsup_conn_send(conn, msg);
163}
164
Harald Welte99909272016-05-05 18:24:15 +0200165/* Transmit a given GSUP message for the given LU operation */
Harald Weltee687be52016-05-03 18:49:27 +0200166static void _luop_tx_gsup(struct lu_operation *luop,
167 const struct osmo_gsup_message *gsup)
168{
169 struct msgb *msg_out;
170
171 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP LUOP");
172 osmo_gsup_encode(msg_out, gsup);
173
174 osmo_gsup_addr_send(luop->gsup_server, luop->peer,
175 talloc_total_size(luop->peer),
176 msg_out);
177}
178
179/*! Transmit UPD_LOC_ERROR and destroy lu_operation */
180void lu_op_tx_error(struct lu_operation *luop, enum gsm48_gmm_cause cause)
181{
182 struct osmo_gsup_message gsup;
183
184 DEBUGP(DMAIN, "%s: LU OP Tx Error (cause=%u)\n",
185 luop->subscr.imsi, cause);
186
187 memset(&gsup, 0, sizeof(gsup));
188 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR;
Neels Hofmeyrec1b9592016-12-11 01:22:23 +0100189 strncpy((char*)&gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi));
Harald Weltee687be52016-05-03 18:49:27 +0200190 gsup.imsi[sizeof(gsup.imsi)-1] = '\0';
191 gsup.cause = cause;
192
193 _luop_tx_gsup(luop, &gsup);
194
195 llist_del(&luop->list);
196 talloc_free(luop);
197}
198
Harald Welte99909272016-05-05 18:24:15 +0200199/* timer call-back in case LU operation doesn't receive an response */
Harald Weltee687be52016-05-03 18:49:27 +0200200static void lu_op_timer_cb(void *data)
201{
202 struct lu_operation *luop = data;
203
204 DEBUGP(DMAIN, "LU OP timer expired in state %s\n",
205 get_value_string(lu_state_names, luop->state));
206
207 switch (luop->state) {
208 case LU_S_CANCEL_SENT:
209 break;
210 case LU_S_ISD_SENT:
211 break;
212 default:
213 break;
214 }
215
216 lu_op_tx_error(luop, GMM_CAUSE_NET_FAIL);
217}
218
219/*! Transmit UPD_LOC_RESULT and destroy lu_operation */
220void lu_op_tx_ack(struct lu_operation *luop)
221{
222 struct osmo_gsup_message gsup;
223
224 memset(&gsup, 0, sizeof(gsup));
225 gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT;
226 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
227 //FIXME gsup.hlr_enc;
228
229 _luop_tx_gsup(luop, &gsup);
230
231 llist_del(&luop->list);
232 talloc_free(luop);
233}
234
235/*! Send Cancel Location to old VLR/SGSN */
236void lu_op_tx_cancel_old(struct lu_operation *luop)
237{
238 struct osmo_gsup_message gsup;
239
240 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED);
241
242 memset(&gsup, 0, sizeof(gsup));
243 gsup.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST;
244 //gsup.cause = FIXME;
245 //gsup.cancel_type = FIXME;
246
247 _luop_tx_gsup(luop, &gsup);
248
249 lu_op_statechg(luop, LU_S_CANCEL_SENT);
250 osmo_timer_schedule(&luop->timer, CANCEL_TIMEOUT_SECS, 0);
251}
252
253/*! Receive Cancel Location Result from old VLR/SGSN */
254void lu_op_rx_cancel_old_ack(struct lu_operation *luop,
255 const struct osmo_gsup_message *gsup)
256{
257 OSMO_ASSERT(luop->state == LU_S_CANCEL_SENT);
258 /* FIXME: Check for spoofing */
259
260 osmo_timer_del(&luop->timer);
261
262 /* FIXME */
263
264 lu_op_tx_insert_subscr_data(luop);
265}
266
267/*! Transmit Insert Subscriber Data to new VLR/SGSN */
268void lu_op_tx_insert_subscr_data(struct lu_operation *luop)
269{
270 struct osmo_gsup_message gsup;
Max2fc63a62016-12-20 16:49:20 +0100271 uint8_t apn[APN_MAXLEN];
Neels Hofmeyr627de842016-12-19 13:16:06 +0100272 uint8_t msisdn_enc[43]; /* TODO use constant; TS 24.008 10.5.4.7 */
Max2fc63a62016-12-20 16:49:20 +0100273 int l;
Harald Weltee687be52016-05-03 18:49:27 +0200274
275 OSMO_ASSERT(luop->state == LU_S_LU_RECEIVED ||
276 luop->state == LU_S_CANCEL_ACK_RECEIVED);
277
278 memset(&gsup, 0, sizeof(gsup));
279 gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_REQUEST;
280 strncpy(gsup.imsi, luop->subscr.imsi, sizeof(gsup.imsi)-1);
Neels Hofmeyr627de842016-12-19 13:16:06 +0100281
282 l = gsm48_encode_bcd_number(msisdn_enc, sizeof(msisdn_enc), 0,
283 luop->subscr.msisdn);
284 if (l < 1) {
285 LOGP(DMAIN, LOGL_ERROR,
286 "%s: Error: cannot encode MSISDN '%s'\n",
287 luop->subscr.imsi, luop->subscr.msisdn);
288 lu_op_tx_error(luop, GMM_CAUSE_PROTO_ERR_UNSPEC);
289 return;
290 }
291 gsup.msisdn_enc = msisdn_enc;
292 gsup.msisdn_enc_len = l;
293
Harald Welte99909272016-05-05 18:24:15 +0200294 /* FIXME: deal with encoding the following data */
Harald Weltee687be52016-05-03 18:49:27 +0200295 gsup.hlr_enc;
296
297 if (luop->is_ps) {
Max2fc63a62016-12-20 16:49:20 +0100298 /* FIXME: PDP infos - use more fine-grained access control
299 instead of wildcard APN */
300 l = osmo_apn_from_str(apn, sizeof(apn), "*");
301 if (l > 0) {
302 gsup.pdp_infos[0].apn_enc = apn;
303 gsup.pdp_infos[0].apn_enc_len = l;
304 gsup.pdp_infos[0].have_info = 1;
305 gsup.num_pdp_infos = 1;
306 /* FIXME: use real value: */
307 gsup.pdp_infos[0].context_id = 1;
308 }
Harald Weltee687be52016-05-03 18:49:27 +0200309 }
310
311 /* Send ISD to new VLR/SGSN */
312 _luop_tx_gsup(luop, &gsup);
313
314 lu_op_statechg(luop, LU_S_ISD_SENT);
315 osmo_timer_schedule(&luop->timer, ISD_TIMEOUT_SECS, 0);
316}
317
318/*! Receive Insert Subscriber Data Result from new VLR/SGSN */
319static void lu_op_rx_insert_subscr_data_ack(struct lu_operation *luop,
320 const struct osmo_gsup_message *gsup)
321{
322 OSMO_ASSERT(luop->state == LU_S_ISD_SENT);
323 /* FIXME: Check for spoofing */
324
325 osmo_timer_del(&luop->timer);
326
327 /* Subscriber_Present_HLR */
328 /* CS only: Check_SS_required? -> MAP-FW-CHECK_SS_IND.req */
329
330 /* Send final ACK towards inquiring VLR/SGSN */
331 lu_op_tx_ack(luop);
332}
333
334/*! Receive GSUP message for given \ref lu_operation */
335void lu_op_rx_gsup(struct lu_operation *luop,
336 const struct osmo_gsup_message *gsup)
337{
338 switch (gsup->message_type) {
339 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
340 /* FIXME */
341 break;
342 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
343 lu_op_rx_insert_subscr_data_ack(luop, gsup);
344 break;
345 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
346 /* FIXME */
347 break;
348 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
349 lu_op_rx_cancel_old_ack(luop, gsup);
350 break;
351 default:
352 LOGP(DMAIN, LOGL_ERROR, "Unhandled GSUP msg_type 0x%02x\n",
353 gsup->message_type);
354 break;
355 }
356}
357
358static struct lu_operation *lu_op_alloc(struct osmo_gsup_server *srv)
359{
360 struct lu_operation *luop;
361
362 luop = talloc_zero(srv, struct lu_operation);
363 OSMO_ASSERT(luop);
364 luop->gsup_server = srv;
365 luop->timer.cb = lu_op_timer_cb;
366 luop->timer.data = luop;
367
368 return luop;
369}
370
371/*! Receive Update Location Request, creates new \ref lu_operation */
372static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
373 const struct osmo_gsup_message *gsup)
374{
375 int rc;
Harald Weltee687be52016-05-03 18:49:27 +0200376 struct lu_operation *luop;
377 struct hlr_subscriber *subscr;
378 uint8_t *peer_addr;
379
380 rc = osmo_gsup_conn_ccm_get(conn, &peer_addr, IPAC_IDTAG_SERNR);
381 if (rc < 0) {
382 LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
383 return rc;
384 }
385
386 luop = lu_op_alloc(conn->server);
387 luop->peer = talloc_memdup(luop, peer_addr, rc);
388 lu_op_statechg(luop, LU_S_LU_RECEIVED);
389 subscr = &luop->subscr;
390 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
391 luop->is_ps = true;
392 llist_add(&luop->list, &g_lu_ops);
393
394 /* Roughly follwing "Process Update_Location_HLR" of TS 09.02 */
395
396 /* check if subscriber is known at all */
397 rc = db_subscr_get(g_dbc, gsup->imsi, subscr);
398 if (rc < 0) {
399 /* Send Error back: Subscriber Unknown in HLR */
400 strcpy(luop->subscr.imsi, gsup->imsi);
401 lu_op_tx_error(luop, GMM_CAUSE_IMSI_UNKNOWN);
402 return 0;
403 }
404
Harald Welte99909272016-05-05 18:24:15 +0200405 /* Check if subscriber is generally permitted on CS or PS
406 * service (as requested) */
Harald Welte53b86782016-05-05 21:04:11 +0200407 if (!luop->is_ps && !subscr->nam_cs) {
Harald Weltee687be52016-05-03 18:49:27 +0200408 lu_op_tx_error(luop, GMM_CAUSE_PLMN_NOTALLOWED);
409 return 0;
Harald Welte53b86782016-05-05 21:04:11 +0200410 } else if (luop->is_ps && !subscr->nam_ps) {
Harald Weltee687be52016-05-03 18:49:27 +0200411 lu_op_tx_error(luop, GMM_CAUSE_GPRS_NOTALLOWED);
412 return 0;
413 }
414
415 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
416
417#if 0
418 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
419 if (luop->is_ps == false &&
420 strcmp(subscr->vlr_number, vlr_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200421 lu_op_tx_cancel_old(luop);
422 } else if (luop->is_ps == true &&
423 strcmp(subscr->sgsn_number, sgsn_number)) {
Harald Weltee687be52016-05-03 18:49:27 +0200424 lu_op_tx_cancel_old(luop);
425 } else
426#endif
427 {
428 /* TODO: Subscriber allowed to roam in PLMN? */
429 /* TODO: Update RoutingInfo */
430 /* TODO: Reset Flag MS Purged (cs/ps) */
431 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
432 lu_op_tx_insert_subscr_data(luop);
433 }
434 return 0;
435}
436
Harald Welteb18f0e02016-05-05 21:03:03 +0200437static int rx_purge_ms_req(struct osmo_gsup_conn *conn,
438 const struct osmo_gsup_message *gsup)
439{
440 struct osmo_gsup_message gsup_reply = {0};
441 struct msgb *msg_out;
442 bool is_ps = false;
443 int rc;
444
445 LOGP(DAUC, LOGL_INFO, "%s: Purge MS (%s)\n", gsup->imsi,
446 is_ps ? "PS" : "CS");
447
448 memcpy(gsup_reply.imsi, gsup->imsi, sizeof(gsup_reply.imsi));
449
450 if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS)
451 is_ps = true;
452
453 /* FIXME: check if the VLR that sends the purge is the same that
454 * we have on record. Only update if yes */
455
456 /* Perform the actual update of the DB */
457 rc = db_subscr_purge(g_dbc, gsup->imsi, is_ps);
458
459 if (rc == 1)
460 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_RESULT;
461 else if (rc == 0) {
462 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
463 gsup_reply.cause = GMM_CAUSE_IMSI_UNKNOWN;
464 } else {
465 gsup_reply.message_type = OSMO_GSUP_MSGT_PURGE_MS_ERROR;
466 gsup_reply.cause = GMM_CAUSE_NET_FAIL;
467 }
468
469 msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP AUC response");
470 osmo_gsup_encode(msg_out, &gsup_reply);
471 return osmo_gsup_conn_send(conn, msg_out);
472}
473
Harald Weltee72cf552016-04-28 07:18:49 +0200474static int read_cb(struct osmo_gsup_conn *conn, struct msgb *msg)
475{
476 static struct osmo_gsup_message gsup;
477 int rc;
478
Harald Weltee687be52016-05-03 18:49:27 +0200479 rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200480 if (rc < 0) {
481 LOGP(DMAIN, LOGL_ERROR, "error in GSUP decode: %d\n", rc);
482 return rc;
483 }
484
485 switch (gsup.message_type) {
486 /* requests sent to us */
487 case OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST:
488 rx_send_auth_info(conn, &gsup);
489 break;
490 case OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST:
Harald Weltee687be52016-05-03 18:49:27 +0200491 rx_upd_loc_req(conn, &gsup);
Harald Weltee72cf552016-04-28 07:18:49 +0200492 break;
Harald Welteb18f0e02016-05-05 21:03:03 +0200493 case OSMO_GSUP_MSGT_PURGE_MS_REQUEST:
494 rx_purge_ms_req(conn, &gsup);
495 break;
Harald Weltee72cf552016-04-28 07:18:49 +0200496 /* responses to requests sent by us */
497 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
Harald Weltee72cf552016-04-28 07:18:49 +0200498 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
Harald Weltee687be52016-05-03 18:49:27 +0200499 case OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR:
500 case OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT:
501 {
502 struct lu_operation *luop = lu_op_by_imsi(gsup.imsi);
503 if (!luop) {
504 LOGP(DMAIN, LOGL_ERROR, "GSUP message %u for "
505 "unknown IMSI %s\n", gsup.message_type,
506 gsup.imsi);
507 break;
508 }
509 lu_op_rx_gsup(luop, &gsup);
510 }
Harald Weltee72cf552016-04-28 07:18:49 +0200511 break;
512 default:
513 LOGP(DMAIN, LOGL_DEBUG, "Unhandled GSUP message type %u\n",
514 gsup.message_type);
515 break;
516 }
Harald Welte5341b5d2016-04-28 12:48:39 +0200517 msgb_free(msg);
Harald Weltee72cf552016-04-28 07:18:49 +0200518 return 0;
519}
520
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100521static void print_usage()
522{
523 printf("Usage: osmo-hlr\n");
524}
525
526static void print_help()
527{
528 printf(" -h --help This text.\n");
529 printf(" -l --database db-name The database to use.\n");
530 printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n");
531 printf(" -D --daemonize Fork the process into a background daemon.\n");
532 printf(" -s --disable-color Do not print ANSI colors in the log\n");
533 printf(" -T --timestamp Prefix every log line with a timestamp.\n");
534 printf(" -e --log-level number Set a global loglevel.\n");
535}
536
537static struct {
538 const char *db_file;
539 bool daemonize;
540} cmdline_opts = {
541 .db_file = "hlr.db",
542 .daemonize = false,
543};
544
545static void handle_options(int argc, char **argv)
546{
547 while (1) {
548 int option_index = 0, c;
549 static struct option long_options[] = {
550 {"help", 0, 0, 'h'},
551 {"database", 1, 0, 'l'},
552 {"debug", 1, 0, 'd'},
553 {"daemonize", 0, 0, 'D'},
554 {"disable-color", 0, 0, 's'},
555 {"log-level", 1, 0, 'e'},
556 {"timestamp", 0, 0, 'T'},
557 {0, 0, 0, 0}
558 };
559
560 c = getopt_long(argc, argv, "hl:d:Dse:T",
561 long_options, &option_index);
562 if (c == -1)
563 break;
564
565 switch (c) {
566 case 'h':
567 print_usage();
568 print_help();
569 exit(0);
570 case 'l':
571 cmdline_opts.db_file = optarg;
572 break;
573 case 'd':
574 log_parse_category_mask(osmo_stderr_target, optarg);
575 break;
576 case 'D':
577 cmdline_opts.daemonize = 1;
578 break;
579 case 's':
580 log_set_use_color(osmo_stderr_target, 0);
581 break;
582 case 'e':
583 log_set_log_level(osmo_stderr_target, atoi(optarg));
584 break;
585 case 'T':
586 log_set_print_timestamp(osmo_stderr_target, 1);
587 break;
588 default:
589 /* catch unknown options *as well as* missing arguments. */
590 fprintf(stderr, "Error in command line options. Exiting.\n");
591 exit(-1);
592 break;
593 }
594 }
595}
596
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100597static void *hlr_ctx = NULL;
Harald Welteaabae9e2016-04-28 12:48:14 +0200598static struct osmo_gsup_server *gs;
599
600static void signal_hdlr(int signal)
601{
602 switch (signal) {
603 case SIGINT:
604 LOGP(DMAIN, LOGL_NOTICE, "Terminating due to SIGINT\n");
605 osmo_gsup_server_destroy(gs);
606 db_close(g_dbc);
607 log_fini();
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100608 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200609 exit(0);
610 break;
611 case SIGUSR1:
612 LOGP(DMAIN, LOGL_DEBUG, "Talloc Report due to SIGUSR1\n");
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100613 talloc_report_full(hlr_ctx, stderr);
Harald Welteaabae9e2016-04-28 12:48:14 +0200614 break;
615 }
616}
Harald Weltee72cf552016-04-28 07:18:49 +0200617
618int main(int argc, char **argv)
619{
Harald Weltee72cf552016-04-28 07:18:49 +0200620 int rc;
621
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100622 hlr_ctx = talloc_named_const(NULL, 1, "OsmoHLR");
623 msgb_talloc_ctx_init(hlr_ctx, 0);
Harald Welteaabae9e2016-04-28 12:48:14 +0200624
Harald Weltee72cf552016-04-28 07:18:49 +0200625 rc = osmo_init_logging(&hlr_log_info);
626 if (rc < 0) {
627 fprintf(stderr, "Error initializing logging\n");
628 exit(1);
629 }
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100630
631 handle_options(argc, argv);
632
Harald Weltee72cf552016-04-28 07:18:49 +0200633 LOGP(DMAIN, LOGL_NOTICE, "hlr starting\n");
634
635 rc = rand_init();
636 if (rc < 0) {
637 LOGP(DMAIN, LOGL_FATAL, "Error initializing random source\n");
638 exit(1);
639 }
640
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100641 g_dbc = db_open(hlr_ctx, cmdline_opts.db_file);
Harald Weltee72cf552016-04-28 07:18:49 +0200642 if (!g_dbc) {
643 LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
644 exit(1);
645 }
646
Neels Hofmeyrca43e302017-01-30 13:18:23 +0100647 gs = osmo_gsup_server_create(hlr_ctx, NULL, 2222, read_cb);
Harald Weltee72cf552016-04-28 07:18:49 +0200648 if (!gs) {
649 LOGP(DMAIN, LOGL_FATAL, "Error starting GSUP server\n");
650 exit(1);
651 }
652
Harald Welteaabae9e2016-04-28 12:48:14 +0200653 osmo_init_ignore_signals();
654 signal(SIGINT, &signal_hdlr);
655 signal(SIGUSR1, &signal_hdlr);
656
Neels Hofmeyr7f9491f2017-01-30 13:30:47 +0100657 if (cmdline_opts.daemonize) {
658 rc = osmo_daemonize();
659 if (rc < 0) {
660 perror("Error during daemonize");
661 exit(1);
662 }
663 }
Harald Welteaabae9e2016-04-28 12:48:14 +0200664
Harald Weltee72cf552016-04-28 07:18:49 +0200665 while (1) {
666 osmo_select_main(0);
667 }
668
669 db_close(g_dbc);
670
671 log_fini();
672
673 exit(0);
674}