Years ago I built a media server on Solaris 11 11/11 with Fuppes. It
worked great, until I upgraded to 11.1, when it broke. As that was
pretty much all the box did, I rolled back to 11/11 and everything was
fine. But recently I decided that a three year old OS wasn’t on, and
moved to 11.2. Again, Fuppes broke. So much for binary compatibility. I
spent a weekend in mdb
and scat
, but with next to no C++ knowledge
and severely rusty debugging skills, I wasn’t able to fix the problem.
(A segfault caused by the HTTP server part of Fuppes.)
I messed about with alternatives, but I couldn’t get anything working on Solaris which I liked, so I was without a media server. I set one up on a Linux box, but that was too many boxes, and I don’t like Linux. Then I thought about getting a Raspberry Pi, and running that as a media server, NFS mounting the content from the Solaris box. Before I did that, I looked to make sure I could run a UPnP server on Raspberry Pi, and that led me to MiniDLNA. I looked at the source, and I thought “I bet that runs on Solaris”. It does. In fact, it turns out to be smaller, more stable, and with better functionality than Fuppes.
Building ffmpeg
MiniDLNA is a full-fledged media server. It deals in all manner of
things, and to do that, it uses ffmpeg
. So, get that and build it. At
the time of writing the latest and (presumably) greatest is 2.4.2, so I
got that.
ffmpeg
can deal with all the audio and video codecs under the Sun, but
all I want is something to stream FLACs to my DS. So, I’m going to
disable pretty much all functionality. If you want to serve video, that
will work just fine.
I’m building with GCC. Years ago I would have fought the good fight and done what I could to get it built with Sun Studio, but that war’s been lost, I’m too old to care these days.
I want FLAC and MP3 support, and I also want to display JPEG cover art, so I’m going to need those libraries.
# pkg install image/library/libexif codec/ogg-vorbis codec/flac
Don’t worry: they won’t pull in a load of junk and pollute your system.
We’re going to need the shared libraries later, and you have to ask for them.
$ ./configure --prefix=/usr/local/ffmpeg \
--enable-shared \
--disable-static \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-doc \
--disable-swscale-alpha \
--disable-swresample \
--disable-swscale \
--disable-postproc \
--disable-avfilter \
--disable-network \
--disable-dct \
--disable-dwt \
--disable-lsp \
--disable-mdct \
--disable-rdft \
--disable-fft \
--disable-faan \
--disable-pixelutils \
--disable-everything \
--disable-xlib \
--disable-debug
$ gmake -j4
That, shock of shocks, compiles cleanly for me first time and, as we’re compiling so little, it’s very quick. Installation, however, doesn’t work:
# gmake install
INSTALL libavdevice/libavdevice.a
find: cycle detected for /lib/secure/32/
...
find: cycle detected for /usr/lib/link_audit/32/
install: libavdevice.a was not found anywhere!
gmake: *** [install-libavdevice-static] Error 2
This is an easy fix though:
$ vim +101 config.mak
and change the =install
to =ginstall
. Then, assuming you have GNU
install
, you can gmake install
and all will be good.
Building MiniDLNA
Now, get MiniDLNA. I used version 1.1.4. I’d have like to have built a
static binary, and just dropped that into its own zone, but as Oracle
don’t give us static versions of libjpeg
, libsqlite3
and so on, that
would require a fair bit of legwork.
There are a couple of things I wanted to change. First, I hate that stupid Linux penguin and, by default, that is the icon you’ll get for your MiniDNLA server if you build on Solaris. Let’s honour SunOS’s BSD heritage by forcing it to usethe BSD daemon instead.
$ vi +1104 icons.c
and change the elif
to #elifdef __Solaris__
.
Next, stop MiniDLNA resizing cover art to 160x160 pixels. I believe the UPnP spec caps cover art at 500x500px, so let’s use that.
$ gsed -i 's/160/500/g' albumart.c
$ CFLAGS="-I/usr/local/ffmpeg/include" \
LDFLAGS="-L/usr/local/ffmpeg/lib" \
./configure --prefix=/usr/local/minidlna \
--with-os-name=Solaris --with-os-version=11.2
Now you have config.h
, add
#define __Solaris__
somewhere near the top, and try a gmake
. It will pretty quickly hit
the buffers with:
gmake[2]: Entering directory `/build/minidlna-1.1.4'
CC image_utils.o
image_utils.c:39:20: error: endian.h: No such file or directory
Which is a perfectly valid error, as Solaris doesn’t have endian.h
. Instead,
it keeps its definitions of endianness, alignment and whatnot, in isa_defs.h
.
So:
$ ggrep -rl '<endian.h>' . | while read f
> do
> gsed -i 's|<endian.h>|<sys/isa_defs.h>|' $f
> done
No, I’m not doing this properly. I’m not making patch files or adding proper includes. This is a purely selfish “make it work” approach.
Run gmake
again, and it’ll crap out with a load of undefined symbols. As is
so often the case with Solaris, we’ll have to tell the linker to use the socket
libraries. Less usually, we have to tell it to link against libsendfile
.
$ vim +407 Makefile
And add
-lsocket \
-lnsl \
-lsendfile \
to the minidlnad_LDADD
line. Run gmake
again, and it still won’t work:
Undefined first referenced
symbol in file
MAX minidlna.o
MIN upnphttp.o
What? I don’t recall those being in the standard library. Remember: we’re in
the world of Linux here, where bad code and no thought of side-effects rule.
You’re going to have to add macros for MAX
and MIN
which, I presume,
already exist in glibc
. (Though, of course, glibc is technically
nothing to do with Linux.)
The block of code which will do the fix needs to look something like this
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
and if you pop it into the top of config.h
it’ll get everywhere it
needs to. Run gmake
one last time and everything will be built.
gmake install
as root, and you have everything you need in
/usr/local/minidlna
and /usr/local/ffmpeg
. Pick those up, drop them
in a zone, and you’re pretty much done. Alternatively, package them up
with fpm
.
$ fpm -s dir -t solaris -v 2.4.2 -n SNLTDffmpeg /usr/local/ffmpeg
$ fpm -s dir -t solaris -v 1.1.4 -n SNLTDminidlna /usr/local/minidlna
Installing and Configuring
I moved my binaries to a clean, minimal zone, and I found a bunch of shared libs were missing, even after telling the runtime linker where FFMPEG was:
# crle -u -l /usr/local/ffmpeg/lib
$ find /usr/local/minidlna -follow -type f | xargs ldd 2>/dev/null | grep "not found" | sort -u
libexif.so.12 => (file not found)
libFLAC.so.8 => (file not found)
libid3tag.so.0 => (file not found)
libogg.so.0 => (file not found)
libvorbis.so.0 => (file not found)
To fix this, I just had to run the pkg
command from the top of the page.
I created a config file at /config/minidlna/minidlna.conf
with the
following contents:
media_dir=A,/storage/flac
friendly_name=tap-media
album_art_names=front.jpg
db_dir=/var/cache/minidlna
log_dir=/var/log
inotify=no
Then imported the following service manifest:
<pre>
<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type='manifest' name='export'>
<service name='snltd/minidlna' type='service' version='0'>
<create_default_instance enabled='true'/>
<single_instance/>
<dependency name='filesystem' grouping='require_all' restart_on='none' type='service'>
<service_fmri value='svc:/system/filesystem/local'/>
</dependency>
<dependency name='network' grouping='require_all' restart_on='none' type='service'>
<service_fmri value='svc:/network/initial'/>
</dependency>
<exec_method
name='start'
type='method'
exec='/usr/local/minidlna/sbin/minidlnad -u minidlna -f /config/minidlna/minidlna.conf'
timeout_seconds='30'/>
<exec_method name='stop' type='method' exec=':kill' timeout_seconds='30'>
</exec_method>
<stability value='Unstable'/>
<template>
<common_name>
<loctext xml:lang='C'>minidlna UPnP server</loctext>
</common_name>
</template>
</service>
</service_bundle>
</pre>
And I was off.
Update
I’ve written a Puppet module to do the installation.