Bundling odbc drivers

I am bundling an app that requires odbc driver files in the snap. The snapcraft.yaml should bundle all application files and the required drivers:

name: my-app
version: git
summary: -
description: -

grade: devel
confinement: devmode

apps:
  my-app:
    command: my-app

parts:
  odbc-config:
    source: resources
    plugin: dump
    organize:
      'odbcinst.ini' : /etc/odbcinst.ini
  my-app:
    after: [odbc-config]
    plugin: nodejs
    node-engine: 8.8.1
    source: .
    stage-packages:
      - unixodbc
      - unixodbc-dev
      - odbc-postgresql
    build-packages:
      - build-essential
      - python
      - unixodbc
      - unixodbc-dev
      - odbc-postgresql                   // <- this step installs the driver files
  odbc-driver:
    after: [my-app]
    source: /usr/lib/x86_64-linux-gnu/odbc
    plugin: dump
    organize:
      'libodbcpsqlS.so': /usr/lib/x86_64-linux-gnu/odbc/
      'psqlodbca.so': /usr/lib/x86_64-linux-gnu/odbc/

the build is successful and the local install works. when running the app the application can’t find the driver files though:

[unixODBC][Driver Manager]Can't open lib '/usr/lib/x86_64-linux-gnu/odbc/psqlodbca.so' : file not found","state":"01000"}],"error":"[node-odbc] SQL_ERROR","message":"[unixODBC][Driver Manager]Can't open lib '/usr/lib/x86_64-linux-gnu/odbc/psqlodbca.so' : file not found","state":"01000"

This is a relocation issue, you have to somehow make unixODBC load the library under $SNAP.

Yes, this is what this thread is about. How to correctly bundle and load the odbc drivers of unixODBC. Any suggestion?

set the environment variable ODBCINI to something like $SNAP_USER_DATA/.odbc.ini and fill that file with content similar to:

[PostgreSQL]
Description     = PostgreSQL driver for Linux & Win32
Driver          = <replace-with-snap-path>/usr/lib/x86_64-linux-gnu/odbc/psqlodbca.so
Setup           = <replace-with-snap-path>/usr/lib/x86_64-linux-gnu/odbc/libodbcpsqlS.so

You can do this with a wrapper script similar to:

#!/bin/bash

if [ "$SNAP_ARCH" == "amd64" ]; then
  ARCH="x86_64-linux-gnu"
elif [ "$SNAP_ARCH" == "armhf" ]; then
  ARCH="arm-linux-gnueabihf"
elif [ "$SNAP_ARCH" == "arm64" ]; then
  ARCH="aarch64-linux-gnu"
else
  ARCH="$SNAP_ARCH-linux-gnu"
fi

export ODBCINI="$SNAP_USER_DATA/.odbc.ini"

if [ ! -f "$ODBCINI" ]; then
    cat <<EOF > "$ODBCINI"
[PostgreSQL]
Description     = PostgreSQL driver for Linux & Win32
Driver          = ${SNAP/$SNAP_REVISION/current}/usr/lib/$ARCH/odbc/psqlodbca.so
Setup           = ${SNAP/$SNAP_REVISION/current}/usr/lib/$ARCH/odbc/libodbcpsqlS.so
EOF
fi

exec "$@"

Note, that I haven’t tested that this works correctly.

2 Likes

It doesn’t look like the file you’re generating depends on the target system or will change from run to run, so it would probably be better to generate the odbc.ini file at package build time, and then just use something like the following to set the environment variable:

environment:
    ODBCINI: "$SNAP/odbc.ini"
2 Likes

I suggested to write it out in a wrapper because of the requirement to know where the snap is installed. While it is convention to use /snap/snapname/current this isn’t guaranteed afaik?

It really depends on which mount namespace you’re talking about. It is true that a snap’s data files might be mounted under /var/lib/snapd/snap on some distributions, but that is invisible to strictly confined snaps. The mount namespace constructed for strictly confined snaps always puts the snap’s data in /snap.

1 Like

Thanks for the breakdown guys. One clarification: Shouldn’t the drivers installed with the packages listed under stage-packages be bundled with the snap? What exactly gets included by stage-packages if not the .so files?

Every file provided by the packages, with the same consolidation by the stage and prime keywords.

Thanks. I guess there is no need to copy over .so files manually then.

What’s the right way to trigger this script at the build? During ‘override-{build | pull | stage}’ the env var $SNAP_USER_DATA seems to point somewhere else (like /root/snap/snapcraft/1871/) as during the actual snap run (like /home/me/snap/my-app/x4). Means the ini file is not located where the snap run expects it.

do like @jamesh suggested, because it’s simpler:

parts:
    odbc-config:
        plugin: nil
        override-pull: |
            cat <<EOF > odbc.ini
            [PostgreSQL]
            Description     = PostgreSQL driver for Linux & Win32
            Driver          = /snap/$SNAPCRAFT_PROJECT_NAME/current/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/odbc/psqlodbca.so
            Setup           = /snap/$SNAPCRAFT_PROJECT_NAME/current/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/odbc/libodbcpsqlS.so
            EOF
        override-build: |
            install -D -m644 -t $SNAPCRAFT_PART_INSTALL/etc odbc.ini

    my-app:
        after: [odbc-config]
        ...

    // odbc-driver is superfluous, it is handled by my-app's stage-packages

apps:
    my-app:
        command: my-app
        environment:
            ODBCINI: $SNAP/etc/odbc.ini
1 Like