Commit 23cd94fe49 for asterisk.org

commit 23cd94fe49019283de3f8697ca7d64757b234ec3
Author: George Joseph <gjoseph@sangoma.com>
Date:   Mon Aug 31 13:05:45 2026 -0600

    res_geolocation: Create alembic scripts and squash a SEGV.

    If a profile contained a location_reference that pointed to a location that
    didn't exist, a segfault would occur when running the `geoloc show profiles`
    command. The command now prints a notice that there was an issue retrieving
    the profile as well as an ERROR log message with the explanation.

    UpgradeNote: An Alembic script has been added for res_geolocation that creates
    the geoloc_location and geoloc_profile tables.

diff --git a/contrib/ast-db-manage/config/versions/2b45fd748a4f_add_geolocation_tables.py b/contrib/ast-db-manage/config/versions/2b45fd748a4f_add_geolocation_tables.py
new file mode 100644
index 0000000000..232ab574e9
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/2b45fd748a4f_add_geolocation_tables.py
@@ -0,0 +1,74 @@
+"""Add geolocation tables
+
+Revision ID: 2b45fd748a4f
+Revises: 2285f2ace275
+Create Date: 2026-08-31 09:56:07.441814
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '2b45fd748a4f'
+down_revision = '2285f2ace275'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+AST_BOOL_NAME = 'ast_bool_values'
+AST_BOOL_VALUES = [ '0', '1',
+                    'off', 'on',
+                    'false', 'true',
+                    'no', 'yes' ]
+
+GEOLOC_LOCATION_FORMAT_NAME ='geoloc_location_format_values'
+GEOLOC_LOCATION_FORMAT_VALUES = ['<none>','civicAddress','GML','URI']
+
+GEOLOC_PROFILE_PIDF_ELEMENT_NAME = 'geoloc_profile_pidf_element_values'
+GEOLOC_PROFILE_PIDF_ELEMENT_VALUES = ['<none>','tuple', 'device', 'person']
+
+GEOLOC_PROFILE_PRECEDENCE_NAME = 'geoloc_profile_precedence_values'
+GEOLOC_PROFILE_PRECEDENCE_VALUES = ['prefer_incoming', 'prefer_config', 'discard_incoming','discard_config']
+
+def upgrade():
+    enum_format = ENUM(*GEOLOC_LOCATION_FORMAT_VALUES, name=GEOLOC_LOCATION_FORMAT_NAME, checkfirst=True)
+    enum_pidf_element = ENUM(*GEOLOC_PROFILE_PIDF_ELEMENT_VALUES, name=GEOLOC_PROFILE_PIDF_ELEMENT_NAME, checkfirst=True)
+    enum_precedence = ENUM(*GEOLOC_PROFILE_PRECEDENCE_VALUES, name=GEOLOC_PROFILE_PRECEDENCE_NAME, checkfirst=True)
+    ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)
+
+    op.create_table(
+        'geoloc_location',
+        sa.Column('id', sa.String(80), nullable=False, primary_key=True),
+        sa.Column('format', enum_format, nullable=True),
+        sa.Column('location_info', sa.String(2048), nullable=True),
+        sa.Column('location_source', sa.String(1024), nullable=True),
+        sa.Column('confidence', sa.String(2048), nullable=True),
+        sa.Column('method', sa.String(2048), nullable=True)
+    )
+
+    op.create_table(
+        'geoloc_profile',
+        sa.Column('id', sa.String(80), nullable=False, primary_key=True),
+        sa.Column('pidf_element', enum_pidf_element, nullable=True),
+        sa.Column('pidf_element_id', sa.String(80), nullable=True),
+        sa.Column('device_id', sa.String(1024), nullable=True),
+        sa.Column('location_reference', sa.String(80), nullable=True),
+        sa.Column('location_info_refinement', sa.String(2048), nullable=True),
+        sa.Column('location_variables', sa.String(2048), nullable=True),
+        sa.Column('usage_rules', sa.String(2048), nullable=True),
+        sa.Column('notes', sa.String(2048), nullable=True),
+        sa.Column('allow_routing_use', ast_bool_values),
+        sa.Column('suppress_empty_ca_elements', ast_bool_values),
+        sa.Column('profile_precedence', enum_precedence, nullable=True),
+        sa.Column('format', enum_format, nullable=True),
+        sa.Column('location_info', sa.String(2048), nullable=True),
+        sa.Column('location_source', sa.String(1024), nullable=True),
+        sa.Column('confidence', sa.String(2048), nullable=True),
+        sa.Column('method', sa.String(2048), nullable=True)
+    )
+
+def downgrade():
+    op.drop_table('geoloc_location')
+    op.drop_table('geoloc_profile')
+    sa.Enum(*GEOLOC_LOCATION_FORMAT_VALUES, name=GEOLOC_LOCATION_FORMAT_NAME).drop(op.get_bind(), checkfirst=True)
+    sa.Enum(*GEOLOC_PROFILE_PIDF_ELEMENT_VALUES, name=GEOLOC_PROFILE_PIDF_ELEMENT_NAME).drop(op.get_bind(), checkfirst=True)
+    sa.Enum(*GEOLOC_PROFILE_PRECEDENCE_VALUES, name=GEOLOC_PROFILE_PRECEDENCE_NAME).drop(op.get_bind(), checkfirst=True)
diff --git a/res/res_geolocation/geoloc_config.c b/res/res_geolocation/geoloc_config.c
index 2afc8bda2d..c9a97378cc 100644
--- a/res/res_geolocation/geoloc_config.c
+++ b/res/res_geolocation/geoloc_config.c
@@ -485,8 +485,13 @@ static char *geoloc_config_show_profiles(struct ast_cli_entry *e, int cmd, struc
 		struct ast_str *usage_rules_str = NULL;
 		struct ast_str *confidence_str = NULL;
 		struct ast_geoloc_eprofile *eprofile = ast_geoloc_eprofile_create_from_profile(profile);
+		if (!eprofile) {
+			ast_cli(a->fd, "\nid:                      %-s\n", ast_sorcery_object_get_id(profile));
+			ast_cli(a->fd, "  There was a problem retrieving the profile.  Check logs for errors.\n");
+			ao2_ref(profile, -1);
+			continue;
+		}
 		ao2_ref(profile, -1);
-
 		loc_str = ast_variable_list_join(eprofile->location_info, ",", "=", "\"", NULL);
 		resolved_str = ast_variable_list_join(eprofile->effective_location, ",", "=", "\"", NULL);

@@ -628,6 +633,19 @@ static int default_profile_create(const char *name)
 	char *id = ast_alloca(strlen(name) + 3 /* <, >, NULL */);

 	sprintf(id, "<%s>", name); /* Safe */
+
+	/*
+	 * If realtime is being used for profiles, we need to check if the default
+	 * exists before trying to create it because the act of creating it will
+	 * actually write it to the database.  If we try to write it again, the
+	 * primary key constraint will fail.
+	 */
+	profile = ast_sorcery_retrieve_by_id(geoloc_sorcery, "profile", id);
+	if (profile) {
+		ao2_ref(profile, -1);
+		return 1;
+	}
+
 	profile = ast_sorcery_alloc(geoloc_sorcery, "profile", id);
 	ast_assert_return(profile != NULL, 0);