Apparently this has become the “how to do obscure things in FreeNAS jails” blog. I’ll include some notes at the end on setting up everything with icecast and rc.d scripts, but I’m assuming you arrived here because you want to install liquidsoap, and it’s not going well.
We’ll start with the official installation instructions. As of this writing, you should run this:
curl -LO https://github.com/ocaml/opam/releases/download/2.1.2/opam-2.1.2-x86_64-freebsd
mv ./opam* /usr/local/bin/opam
chmod +x /usr/local/bin/opam
opam --version
pkg install -y gmake patch ocaml ocaml-opam
opam init --no-setup
opam switch create 4.12.0
eval $(opam env --switch=4.12.0)
The Liquidsoap instructions are a little outdated at this point. You can run opam depext
, but ocaml says that opam install
does that (installing package dependencies) for you now. But it doesn’t quite work, because you’re here, right? Still, let’s give it a try:
opam install -y liquidsoap
...
[ERROR] The compilation of liquidsoap.2.1.0-1 failed at "./configure --prefix /root/.opam/4.12.0 --sbindir=/root/.opam/4.12.0/lib/liquidsoap/sbin --libexecdir=/root/.opam/4.12.0/lib/liquidsoap/libexec
--sysconfdir=/root/.opam/4.12.0/lib/liquidsoap/etc --sharedstatedir=/root/.opam/4.12.0/lib/liquidsoap/com --localstatedir=/root/.opam/4.12.0/lib/liquidsoap/var
--libdir=/root/.opam/4.12.0/lib/liquidsoap/lib --includedir=/root/.opam/4.12.0/lib/liquidsoap/include --datarootdir=/root/.opam/4.12.0/lib/liquidsoap/share
--with-bash-completion-dir=/root/.opam/4.12.0/lib/liquidsoap/etc/bash_completion.d --with-user=dummy --with-group=dummy".
#=== ERROR while compiling liquidsoap.2.1.0-1 =================================#
# context 2.1.2 | freebsd/x86_64 | ocaml-base-compiler.4.12.0 | https://opam.ocaml.org#49486377
# path ~/.opam/4.12.0/.opam-switch/build/liquidsoap.2.1.0-1
# command ~/.opam/4.12.0/.opam-switch/build/liquidsoap.2.1.0-1/./configure --prefix /root/.opam/4.12.0 --sbindir=/root/.opam/4.12.0/lib/liquidsoap/sbin --libexecdir=/root/.opam/4.12.0/lib/liquidsoap/libexec --sysconfdir=/root/.opam/4.12.0/lib/liquidsoap/etc --sharedstatedir=/root/.opam/4.12.0/lib/liquidsoap/com --localstatedir=/root/.opam/4.12.0/lib/liquidsoap/var --libdir=/root/.opam/4.12.0/lib/liquidsoap/lib --includedir=/root/.opam/4.12.0/lib/liquidsoap/include --datarootdir=/root/.opam/4.12.0/lib/liquidsoap/share --with-bash-completion-dir=/root/.opam/4.12.0/lib/liquidsoap/etc/bash_completion.d --with-user=dummy --with-group=dummy
# exit-code 1
# env-file ~/.opam/log/liquidsoap-82430-595887.env
# output-file ~/.opam/log/liquidsoap-82430-595887.out
### output ###
# /root/.opam/4.12.0/.opam-switch/build/liquidsoap.2.1.0-1/./configure: 5.0.0}: not found
# Cannot find file ./{.
# fatal: not a git repository (or any of the parent directories): .git
# checking for a BSD-compatible install... /usr/bin/install -c
# checking for GNU make... gmake
# checking build system type... x86_64-unknown-freebsd13.1
# checking host system type... x86_64-unknown-freebsd13.1
# configure: error: "OS freebsd13.1 is not supported"
There are a bunch of errors in there that might make you think this is a path issue or possibly a git issue, but no. The real error is configure: error: "OS freebsd13.1 is not supported"
. Why is not supported? Long story short, because they said so. What if we bypass that check and build it ourselves?
opam install -y liquidsoap 2>&1 | tee liquidsoap.out # capture the output so we can use it with scripts
cat liquidsoap.out | grep "# path" | awk '{ print $3 }'
cat > configure.patch << EOF
--- configure.old 2022-07-07 14:38:18.172368441 -0500
+++ configure 2022-07-07 14:38:50.099099880 -0500
@@ -3132,6 +3132,9 @@
darwin*)
build_os="osx"
;;
+ freebsd*)
+ build_os="bsd"
+ ;;
*)
as_fn_error $? "\"OS $host_os is not supported\"" "$LINENO" 5
;;
EOF
patch $(cat liquidsoap.out | grep "# path" | awk '{ print $3 }' | sed "s|~|$HOME|")/configure < configure.patch
cd $(cat liquidsoap.out | grep "# path" | awk '{ print $3 }' | sed "s|~|$HOME|")
./configure
That’s right. It works just fine. Sigh. Oh, but look closely at the output:
* Liquidsoap
- version : 2.1.0
* Supported input formats
- MP3 : no (requires mad)
- AAC : no (requires faad)
- FFmpeg : no (requires ffmpeg)
- Flac (native) : no (requires flac)
- Flac (ogg) : no (requires flac.ogg)
- Lastfm : no (requires lastfm)
- Opus : no (requires opus)
- Speex : no (requires speex)
- Theora : no (requires theora)
- Vorbis : no (requires vorbis)
- XML playlists : no (requires xmlplaylist)
* Supported output formats
- FDK-AAC : detected at runtime
- FFmpeg : no (requires ffmpeg)
- MP3 : detected at runtime
- MP3 (fixed-point) : no (requires shine)
- Opus : no (requires opus)
- SPEEX : no (requires speex)
- Theora : no (requires theora)
- Vorbis : no (requires vorbis)
* Tags
- charset detection : yes
- Taglib (ID3 tags) : no (requires taglib)
- Vorbis : no (requires vorbis)
* Input / output
- ALSA : no (requires alsa)
- AO : no (requires ao)
- FFmpeg : no (requires ffmpeg)
- Icecast/Shoutcast : no (requires cry)
- GStreamer : no (requires gstreamer)
- JACK : no (requires bjack)
- OSS : yes
- Portaudio : no (requires portaudio)
- Pulseaudio : no (requires pulseaudio)
- SRT : no (requires srt)
* Audio manipulation
- FFmpeg : no (requires ffmpeg)
- LADSPA : no (requires ladspa)
- Lilv : no (requires lilv)
- Samplerate : no (requires samplerate)
- SoundTouch : no (requires soundtouch)
* Video manipulation
- camlimages : no (requires camlimages)
- camlimages.ft : no (requires camlimages.freetype)
- FFmpeg : no (requires ffmpeg)
- frei0r : no (requires frei0r)
- SDL-image : no (requires tsdl-image)
- SDL-ttf : no (requires tsdl-ttf)
This supports… nothing. No input formats, no output formats. That’s not very useful. Lets install a few:
pkg install -y lame libsamplerate taglib libmad
opam install -y taglib mad lame vorbis cry samplerate ocurl
#=== ERROR while compiling lame.0.3.6 =========================================#
# context 2.1.2 | freebsd/x86_64 | ocaml-base-compiler.4.12.0 | https://opam.ocaml.org#49486377
# path ~/.opam/4.12.0/.opam-switch/build/lame.0.3.6
# command ~/.opam/4.12.0/bin/dune build -p lame -j 3 @install
# exit-code 1
# env-file ~/.opam/log/lame-22106-b6dc09.env
# output-file ~/.opam/log/lame-22106-b6dc09.out
### output ###
# compiling c program:
# [...]
# | return 0;
# | }
# run: cc -O2 -fno-strict-aliasing -fwrapv -fPIC -I /root/.opam/4.12.0/lib/ocaml -o /tmp/build_70a624_dune/ocaml-configurator17c755/c-test-0/test.exe /tmp/build_70a624_dune/ocaml-configurator17c755/c-test-0/test.c -lm -lmp3lame -lm
# -> process exited with code 1
# -> stdout:
# -> stderr:
# | /tmp/build_70a624_dune/ocaml-configurator17c755/c-test-0/test.c:2:10: fatal error: 'lame/lame.h' file not found
# | #include <lame/lame.h>
# | ^~~~~~~~~~~~~
# | 1 error generated.
# Fatal error: exception File "src/config/discover.ml", line 23, characters 6-12: Assertion failed
[ERROR] The compilation of conf-mad.1 failed at "pkg-config --exists mad".
#=== ERROR while compiling conf-mad.1 =========================================#
# context 2.1.2 | freebsd/x86_64 | ocaml-base-compiler.4.12.0 | https://opam.ocaml.org#49486377
# path ~/.opam/4.12.0/.opam-switch/build/conf-mad.1
# command /usr/local/bin/pkg-config --exists mad
# exit-code 1
# env-file ~/.opam/log/conf-mad-98214-3f06d6.env
# output-file ~/.opam/log/conf-mad-98214-3f06d6.out
The lame issue is because liquidsoap’s search paths don’t include /usr/local. That’s easy to fix. But … what? We just installed… oh, we installed *libmad*, not *mad*. Really? How can we fix this…
n -s /usr/local/lib/libmp3lame.a /usr/lib/libmp3lame.a
ln -s /usr/local/lib/libmp3lame.so /usr/lib/libmp3lame.so
ln -s /usr/local/lib/libmp3lame.so.0 /usr/lib/libmp3lame.so.0
ln -s /usr/local/lib/libmp3lame.so.0.0.0 /usr/lib/libmp3lame.so.0.0.0
mkdir /usr/include/lame
ln -s /usr/local/include/lame/lame.h /usr/include/lame/lame.h
cp /usr/local/libdata/pkgconfig/libmad.pc /usr/local/libdata/pkgconfig/mad.pc
opam install -y mad lame
Well. That was a hack. Let’s try configure again:
./configure
...
checking for ocaml mm module >= 0.8.1... configure: error: Not found.
Sigh.
opam install -y mm
./configure
* Liquidsoap
- version : 2.1.0
* Supported input formats
- MP3 : yes
- AAC : no (requires faad)
- FFmpeg : no (requires ffmpeg)
- Flac (native) : no (requires flac)
- Flac (ogg) : no (requires flac.ogg)
- Lastfm : no (requires lastfm)
- Opus : no (requires opus)
- Speex : no (requires speex)
- Theora : no (requires theora)
- Vorbis : yes
- XML playlists : no (requires xmlplaylist)
* Supported output formats
- FDK-AAC : detected at runtime
- FFmpeg : no (requires ffmpeg)
- MP3 : yes
- MP3 (fixed-point) : no (requires shine)
- Opus : no (requires opus)
- SPEEX : no (requires speex)
- Theora : no (requires theora)
- Vorbis : yes
* Tags
- charset detection : yes
- Taglib (ID3 tags) : yes
- Vorbis : yes
* Input / output
- ALSA : no (requires alsa)
- AO : no (requires ao)
- FFmpeg : no (requires ffmpeg)
- Icecast/Shoutcast : yes
- GStreamer : no (requires gstreamer)
- JACK : no (requires bjack)
- OSS : yes
- Portaudio : no (requires portaudio)
- Pulseaudio : no (requires pulseaudio)
- SRT : no (requires srt)
* Audio manipulation
- FFmpeg : no (requires ffmpeg)
- LADSPA : no (requires ladspa)
- Lilv : no (requires lilv)
- Samplerate : yes
- SoundTouch : no (requires soundtouch)
* Video manipulation
- camlimages : no (requires camlimages)
- camlimages.ft : no (requires camlimages.freetype)
- FFmpeg : no (requires ffmpeg)
- frei0r : no (requires frei0r)
- SDL-image : no (requires tsdl-image)
- SDL-ttf : no (requires tsdl-ttf)
Finally! By no means do we support all the things, but at least we have mp3 and Vorbis. Let’s build this!
gmake -j2
gmake install
cd /
liquidsoap --version
Congratulations, you have built liquidsoap with a few useful plugins. Hopefully this will help someone else install liquidsoap on FreeBSD. Or at least point them in the right direction.
Full Solution
These steps will get you a working liquidsoap+icecast radio station, but they are intended as an example, not an exact solution for you. Modify as needed.
Jail
Jails->Add
Name, Jail Type: default, Release (choose the latest)
DHCP Autoconfigure IPv4, VNET
Submit
Start the jail
From shell:
jls
sudo jexec # /bin/sh where # is the JID of the jail you created
adduser
Username: media
Full name: media
Uid (Leave empty for default):
Login group [media]:
Login group is media. Invite media into other groups? []:
Login class [default]:
Shell (sh csh tcsh nologin) [sh]:
Home directory [/home/media]:
Home directory permissions (Leave empty for default):
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]: yes
Lock out the account after creation? [no]:
Username : media
Password : <blank>
Full Name : media
Uid : 1001
Class :
Groups : media
Home : /home/media
Home Mode :
Shell : /bin/sh
Locked : no
OK? (yes/no): yes
adduser: INFO: Successfully added (media) to the user database.
Add another user? (yes/no): no
Goodbye!
Icecast
pkg install -y icecast mime-support
ln -s /usr/local/etc/mime.types /etc/mime.types
service icecast enable
cat > icecast.patch << EOF
--- /usr/local/etc/icecast.xml.sample 2022-06-30 16:53:40.000000000 -0500
+++ /usr/local/etc/icecast.xml 2022-07-08 10:18:31.352888230 -0500
@@ -234,11 +234,9 @@
<security>
<chroot>0</chroot>
- <!--
<changeowner>
- <user>nobody</user>
- <group>nogroup</group>
+ <user>media</user>
+ <group>media</group>
</changeowner>
- -->
</security>
</icecast>
EOF
cp /usr/local/etc/icecast.xml.sample /usr/local/etc/icecast.xml
patch /usr/local/etc/icecast.xml < icecast.patch
mkdir /var/log/icecast
chown media:media /var/log/icecast
service icecast start
service icecast status
LiquidSoap
curl -LO https://github.com/ocaml/opam/releases/download/2.1.2/opam-2.1.2-x86_64-freebsd
mv ./opam* /usr/local/bin/opam
chmod +x /usr/local/bin/opam
opam --version
pkg install -y gmake patch ocaml ocaml-opam
opam init --no-setup
opam switch create 4.12.0
eval $(opam env --switch=4.12.0)
pkg install -y lame libsamplerate taglib libmad
n -s /usr/local/lib/libmp3lame.a /usr/lib/libmp3lame.a
ln -s /usr/local/lib/libmp3lame.so /usr/lib/libmp3lame.so
ln -s /usr/local/lib/libmp3lame.so.0 /usr/lib/libmp3lame.so.0
ln -s /usr/local/lib/libmp3lame.so.0.0.0 /usr/lib/libmp3lame.so.0.0.0
mkdir /usr/include/lame
ln -s /usr/local/include/lame/lame.h /usr/include/lame/lame.h
cp /usr/local/libdata/pkgconfig/libmad.pc /usr/local/libdata/pkgconfig/mad.pc
opam install -y taglib vorbis cry samplerate ocurl
opam install -y mad lame
opam install -y liquidsoap 2>&1 | tee liquidsoap.out # this should fail
cat liquidsoap.out | grep "# path" | awk '{ print $3 }'
cat > configure.patch << EOF
--- configure.old 2022-07-07 14:38:18.172368441 -0500
+++ configure 2022-07-07 14:38:50.099099880 -0500
@@ -3132,6 +3132,9 @@
darwin*)
build_os="osx"
;;
+ freebsd*)
+ build_os="bsd"
+ ;;
*)
as_fn_error $? "\"OS $host_os is not supported\"" "$LINENO" 5
;;
EOF
patch $(cat liquidsoap.out | grep "# path" | awk '{ print $3 }' | sed "s|~|$HOME|")/configure < configure.patch
cd $(cat liquidsoap.out | grep "# path" | awk '{ print $3 }' | sed "s|~|$HOME|")
./configure
gmake -j3
gmake install
cd /
liquidsoap --version
Configure LiquidSoap
liquidsoap 'set("init.allow_root",true)
output.icecast(%mp3.vbr(internal_quality=0),
host = "localhost", port = 8000,
password = "hackme", mount="liq.mp3",
mksafe(playlist(mode="normal", reload=1, reload_mode="rounds", "playlist.pls")))'
#!/usr/local/bin/liquidsoap
set("init.allow_root",true)
# log.file.path.set("/var/log/streaming.log")
src = mksafe(playlist(mode="normal", reload=1, reload_mode="rounds", "playlist.pls"))
output.icecast(%mp3.vbr(internal_quality=0,id3v2=true),
host = "localhost", port = 8000,
password = "hackme", mount="classical.mp3",
src)
output.icecast(%mp3.vbr(internal_quality=4,samplerate=32000,id3v2=true),
host = "localhost", port = 8000,
password = "hackme", mount="classical-lofi.mp3",
src)
#!/bin/sh
# /usr/local/etc/rc.d/streaming
# PROVIDE: streaming
# REQUIRE: DAEMON icecast
# BEFORE: LOGIN
# KEYWORD: shutdown
# Add the following line to /etc/rc.conf to enable `streaming'.
#
#streaming_enable="YES"
#
. /etc/rc.subr
name="streaming"
rcvar=streaming_enable
command="/streams.liq"
command_args=""
extra_commands=""
load_rc_config $name
run_rc_command "$1"
Final thoughts
If you’re hosting a radio station for yourself, I recommend installing tailscale in the jail (pkg install tailscal
e; service tailscaled enable; service tailscaled start; tailscale up;
– it’s that easy!).
I also wrote a little go script to create a playlist by randomizing directories, in case that’s useful to anyone. That way the symphonies and other multi-part pieces stay together.