PGOCaml compilation tip


OPAM sandboxing may bite you.

You got a strange build failure of OCaml applications with the error message following?:

Unix.Unix_error (20 | CstTag, "connect", "")

Yeah, cryptic. But you are not alone. Recently we have experienced this error and took some time to find out the cause.

Why it happens?

(You can just skip this part.)

It was due to OPAM sandboxing in our case. Quite likely your case is the same.

OPAM sandboxing compiles softwares in a sandboxed environment where file system is chroot’ed. It is a good measure to avoid a build process causes unexpeced mess. In the past, when the sandboxing was not in OPAM, some misconfigured OPAM package installation caused the removal of entire home directory. With the sandboxing you can protect your environment from buggy/malicious OPAM packages.

The sandboxing has a drawback, however. Since it chroot the filesystem, configuration processes which tries to find some resources on it may fail unexpectedly. The build failure, Unix.Unix_error (20 | CstTag, "connect", ""), is such an example. This is by an incorrect installation of PGOCaml, in the OPAM sandboxed environment.

PGOCaml provides a very unique OCaml code preprocessor which communicates with a PostgreSQL server to type-check SQL code in your OCaml program at compile time. For this purpose, the installation of PGOCaml tries to find the directory of the Unix domain socket of PosgreSQL server, which is usually found at /var/run/postgresql.

This directory does exist in Ubuntu (and other popular Linux distributions), but once in the OPAM sandbox, it is not visible from the build process. Not finding /var/run/postgresql, the configuration script of PGOCaml uses the default value instead, which is /tmp. PGOCaml is installed “successfully” with this misconfiguration.

The build failure happens when you try to compile OCaml applications using this PGOCaml preprocessing with the misconfiguration. The preprocessor tries to communicate with PosgreSQL server using a Unix domain socket under /tmp, but there is no such named socket there. It is under /var/run/postgresql.

Quick workaround

This is a quick fix:

  • Disable OPAM sandboxing
  • Reinstall PGOCaml (and packages depending on it)

Note that you are no longer protected by the sandboxing.

Method A: Disable OPAM sandboxing

Check your $OPAMROOT/config, which is normally $HOME/.opam/config. You should have the following 3 items at the end:

wrap-build-commands:
  ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"}
wrap-install-commands:
  ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"}
wrap-remove-commands:
  ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"}

Remove them. This disables the sandboxing.

Reinstall PGOCaml

Then, reinstall PGOCaml since your existing installation is misconfigured due to the sandbox:

$ opam reinstall pgocaml

This should also reinstall all the packages which may use it.

Method B: Keep sandboxing

You may not like the quick workaround, since disabling sandbox is a global configuration change.

  • Install PGOCaml without using OPAM
  • Install applications using PGOCaml without using OPAM

If you do not use OPAM to install your software, there is no sandboxing, therefore no chroot. You can properly configure PGOCaml and install it.

You also need to hand-compile your applications, which failed to compile with the error Unix.Unix_error (20 | CstTag, "connect", ""), since PGOCaml’s preprocessor runs in the build. Since the preprocessor wants to communicate with PostgreSQL server at compilation, you cannot hide the unix domain socket by sandboxing.

Method C: Extend sandboxing

There is a script sandbox.sh in your $OPAMROOT/opam-init/hooks/ (usually it is $HOME/.opam/opam-init/hooks/). This controls which directories are mounted/readable/writable to the build process. You can add the directories needed for the build. For PGOCaml, add lines of add_mounts ro "/var/run/postgresql" should be enough:

# This case-switch should remain identical between the different sandbox implems
COMMAND="$1"; shift
case "$COMMAND" in
    build)
        add_mounts ro "$OPAM_SWITCH_PREFIX"
        add_mounts rw "$PWD"
        add_mounts ro "/var/run/postgresql" # <--- this
        add_ccache_mount
        ;;
    install)
        add_mounts rw "$OPAM_SWITCH_PREFIX"
        add_mounts ro "$OPAM_SWITCH_PREFIX/.opam-switch"
        add_mounts ro "/var/run/postgresql" # <--- this
        add_mounts rw "$PWD"
        ;;
    remove)
        add_mounts rw "$OPAM_SWITCH_PREFIX"
        add_mounts ro "$OPAM_SWITCH_PREFIX/.opam-switch"
        [ "X${PWD#$OPAM_SWITCH_PREFIX/.opam-switch}" != "X${PWD}" ] && add_mounts rw "$PWD"
        ;;
    *)
        echo "$0: unknown command $COMMAND, must be one of 'build', 'install' or 'remove'" >&2
        exit 2
esac

After adding these mount configuration, rebuild PGOCaml. This should make PGOCaml configuration find /var/run/postgresql.

comments powered by Disqus