Securing access to Microsoft Exchange 2013 EAC

The coexistence of the Exchange 2013 Administration Console (EAC) with the other Exchange website virtual directories represents a considerable security vulnerability for any organization that installs it using the out-of-box defaults. Since most organizations need Outlook Anywhere and EWS to be Web-facing, and usually OWA too, the EAC will also end up being publicly accessible – inadvisable security practice in itself, even more so for another important reason that I will explain. The EAC uses the IIS Virtual Directory /ecp which has other non-admin functions for normal email users, so it is not really desirable to try to limit access to it. Besides, an Exchange Service Pack or Cumulative Update is quite likely to reset the Virtual Directory settings and permissions later anyway. Now that the Exchange Management Console application has been retired, it is not practical to completely disable EAC unless you especially enjoy PowerShell, so we need to find a way to harden the server.

The problem is that the Domain’s built-in Administrator account does not have the Active Directory account lockout policy applied to it, so the EAC can simply be brute-force attacked if this account has access to the EAC or indeed OWA. One mitigation against this vulnerability is to make sure that the domain’s built-in Administrator account has all Exchange remote access disabled, and that a separate user account is used for day-to-day management. It is probably best to also disable the mailbox entirely to reduce the chance of someone accidentally re-enabling any of this in the EAC later.

Set-CASMailbox Administrator -OWAEnabled $false -ECPEnabled $false -ActiveSyncEnabled $false -OWAforDevicesEnabled $false -EwsEnabled $false -ImapEnabled $false -PopEnabled $false

Disable-Mailbox Administrator

Note that merely disabling the Administrator mailbox (without the first step of amending the access) offers no protection – the ECP can still be accessed and you won’t be able to use the Set-CASMailbox cmdlet, since no mailbox object exists.

There is still a big security problem though. Since the authentication is being handled by IIS the usernames are not being screened, and so a user encounters an HTTP 403 error when they are barred from using ECP but have been authenticated successfully (even if you remove the Administrator account from the ‘Organization Management’ Exchange Security Group), so the brute-force attacker can saturate the server with logon requests and precisely determine when they have cracked the Domain Admin credentials. Although these credentials cannot be used remotely if the above mitigation steps have been taken, the attacker can still use them later to fully penetrate the organization via other means: social engineering, physically entering the building etc.

Microsoft’s recommended solution is to use Powershell to designate a whole CAS server’s ECP Virtual Directory for Internal use only (keeping ECP disabled on the public facing CAS servers). This is totally unworkable for most small-to-medium enterprises though.

The only valid mitigation therefore is to restrict access to the ECP virtual directory to local subnets – something that we had wanted to avoid, and which on first sight looks impossible (since it’s a Virtual Directory, not a Website that we can re-bind to a new IP address and firewall more restrictively). In order to do this you will need to install the IIS Security Feature ‘IP and Domain Restrictions’:

Install-Feature-IIS-IP-Domain-Security

For the Default Web Site’s /ecp Virtual Directory (which is the public one), configure IP Address and Domain Restrictions:

IIS-IP-Domain-Security-Settings

In there, click Edit Feature Settings… (in the right-hand pane) and set ‘Access for unspecified clients’ to Deny. Then use Add Allow Entry to define your permitted IP ranges.

As I mentioned in the opening paragraph though this will need to be checked after Service Packs and Cumulative Updates are applied to the Exchange Server, in case this configuration is lost.

In fact the same precautions against brute force attack of the Administrator account would also apply to earlier versions of Exchange, and for VPN connectivity – i.e. when AD accounts are being used for any public facing authentication, the built-in Administrator should never be granted remote access.

PowerShell for EAP-PEAP secured VPN on Windows 8.1

Simple VPN configurations can be deployed by Group Policy but EAP authentication settings cannot be configured like this, even using Windows 8.1 and Windows Server 2012 R2. Microsoft added some new PowerShell cmdlets to Windows 8.1 for configuring VPNs, but the worked examples do not appear to function for all the settings for PEAP connections, and they do not show a worked example of how you go about exporting and re-importing a connection’s XMLStream.

Defining the XML as a block within the script itself, even assigning it as data type XML does not seem to work. Not being particularly accustomed to PowerShell, the following script took a while to get right. I assigned it as a laptop startup script by GPO. If I need to modify the connection in future I can increment the version number since the script checks the local machine Registry for that, and will not install if the desired version marker is already present.

 
# VPN Connection EAP-PEAP VPN provisioning 
# patters 2013

# This script is loosely based on the EAP-TTLS one published by Microsoft at http://technet.microsoft.com/en-us/library/jj613766.aspx
# The worked examples on that page and at http://technet.microsoft.com/en-us/library/jj554822.aspx
# are rudimentary, and in some details for PEAP, incorrect. To set advanced options like the TrustedRootCAs and the
# the server identity checking warning, you *must* export a GUI-authored config as XML. Configuring XML attributes alone
# will not work because some of them are missing when creating a new connection, and adding them results in errors.


# Check for marker in the Registry, and quit if found
# Desired version is 1
$version = 1
$test = Get-ItemProperty "HKLM:\Software\MyCompany" "MyCompany VPN" -ErrorAction SilentlyContinue
If ($test -eq $null) {
       $test = 0
} else {
       $test = $test."MyCompany VPN"
}
If ($test -ge $version) {exit} 

# VPN Connection look-up to remove any previous installations
$isTestVpn = $false
$vpnConnections = Get-VpnConnection -AllUserConnection
If($vpnConnections.Name -eq "MyCompany VPN") {Remove-VpnConnection -Name "MyCompany VPN" -AllUserConnection -Confirm:$false -Force}
$vpnConnections = Get-VpnConnection
If($vpnConnections.Name -eq "MyCompany VPN") {Remove-VpnConnection -Name "MyCompany VPN" -Confirm:$false -Force}

Try
{
       #-------------------------------------------------
       #The following section documents the attempts to get this working manually before I got importing/exporting of XML working

       # http://technet.microsoft.com/en-us/library/jj554822.aspx says to use "New-EapConfiguration -Peap" here, but is wrong      
       #$a = New-EapConfiguration

       # Generate configuration XML for PEAP authentication method with EAP-MSCHAPv2 as its inner method
       #$b = New-EapConfiguration -Peap -VerifyServerIdentity -FastReconnect $true -TunnledEapAuthMethod $a.EapConfigXmlStream

       # Edit properties within the generated configuration XML
       #$c = $b.EapConfigXmlStream
       #$c.EapHostConfig.Config.Eap.EapType.ServerValidation.ServerNames = "vpn.mycompany.com"

       # Specify AddTrust Root CA for Comodo - This attribute is missing unless you create the connection using the GUI
       # The following appears to generate the XML correctly, but it won't be accepted by the Add-VpnConnection cmdlet
       #$c.EapHostConfig.Config.Eap.EapType.ServerValidation.SetAttribute("TrustedRootCA","02 fa f3 e2 91 43 54 68 60 78 57 69 4d f5 e4 5b 68 85 18 68")   

       # PeapExtensions settings are nested XML objects so setting them as string datatype will fail
       # see http://www.vistax64.com/powershell/173859-xml-property-text.html
       #$c.EapHostConfig.Config.Eap.EapType.PeapExtensions.PerformServerValidation."#text" = "true"
       #$c.EapHostConfig.Config.Eap.EapType.PeapExtensions.AcceptServerName."#text" = "true"
       # Once again this attribute is missing unless the connection is created using the GUI. Adding it does not work
       #$c.EapHostConfig.Config.Eap.EapType.PeapExtensions.PeapExtensionsV2.AllowPromptingWhenServerCANotFound."#text" = "true"      

       # Create the VPN connection ‘MyCompany VPN’ with the EAP configuration XML generated above
       #Add-VpnConnection -Name "MyCompany VPN" -ServerAddress "vpn.mycompany.com" -TunnelType Sstp -EncryptionLevel Maximum -AuthenticationMethod Eap -EapConfigXmlStream $c -AllUserConnection
       #-------------------------------------------------



       # FORTUNATELY THERE IS AN EASIER WAY (once you figure out PowerShell XML – why couldn’t MS have shown a worked example in the docs)...



       # Create your VPN configuration entry manually then export its XML like so:
       #$exportXML = (Get-VpnConnection -Name "My_VPN_Final" -AllUserConnection).EapConfigXmlStream
       #$exportXML.Save("${env:temp}\My_VPN_config.xml")

       $importXML = New-Object XML
       $importXML.Load("\\mycompany.com\data\Software\MyCompany VPN\MyCompany VPN.xml")
       Add-VpnConnection -Name "MyCompany VPN" -ServerAddress "vpn.mycompany.com" -TunnelType Sstp -EncryptionLevel Maximum -AuthenticationMethod Eap -EapConfigXmlStream $importXML -AllUserConnection
       
       # Leave a marker in the Registry
       If (-Not (Test-Path "HKLM:\Software\MyCompany")) {New-Item -Path "HKLM:\Software\MyCompany"}
       if (Get-ItemProperty "HKLM:\Software\MyCompany" "MyCompany VPN" -ErrorAction SilentlyContinue) {
              Set-ItemProperty -Path "HKLM:\Software\MyCompany" -Name "MyCompany VPN" -Value $version
       } else {
              New-ItemProperty -Path "HKLM:\Software\MyCompany" -Name "MyCompany VPN" -Value $version
       }

}
Catch
{
       Write-Host "Error in connection setup!"
       Write-Host $_.Exception.Message
       Throw
}

Deploying vCenter Server Appliance 5.1 with AD auth

In theory using the vCenter Server Appliance (hereafter vCSA) offers a number of big advantages over using vCenter. Firstly you don’t need to commit a Windows Server OS licence, secondly to manage your VMs you can use a Flash-enabled browser on any operating system (including on Surface RT!), and thirdly it should be a lot quicker to deploy.

On that last point, a number of configuration steps in the setup of the vCSA are counter-intuitive and can waste a substantial amount of time. This is because the vCSA defaults to a hostname of localhost.yourdomain.com. The various web services that the appliance runs (SSO, Lookup Service, Inventory Service, vSphere Web Client) interact with each other using SSL sessions and, although there’s a built-in method to make vCSA regenerate its self-signed certificates at boot time, at the time of writing this does not work once these web services have been configured.

If you do not edit the hostname, you will not be able to enable AD authentication, and will likely encounter “Failed to connect to VMware Lookup Service https://yourhostname:7444/lookupservice/sdk – SSL certificate verification failed” when attempting to use the vSphere Web Client. However if after completing the initial setup wizard you configure the hostname to something meaningful and try to regenerate the SSL certificates, the vCSA will hang at boot time, displaying:

Waiting for network to come up (attempt 1 of 10)...
Appliance Name - VMware_vCenter_Server_Appliance
Configuration for eth0 found
The network for interface eth0 is managed internally, network properties ignored
DNS : x.x.x.x
Hostname or IP has changed. Regenerating the self-signed certificates.
Starting VMware vPostgres: ok
Waiting for the embedded database to start up: .[OK]

Furthermore, most of the potentially useful VMware KB articles assume a dedicated vCenter, rather than a vCSA. There seems to be no way out of this boot hang, and you will have to start deploying the vCSA all over again. The trick is to cancel out of the Setup wizard and change the hostname at the very start, before the various web services have been initialized at all.

I have outlined the successful setup process here as a reminder for future deployments:

  • When downloading the vCSA, download the .ova file. Ignore the other .vmdk and .ovf downloads, the .ova one will allow you to set up the IP address details at the time of deployment rather than having to change them later.
  • It is recommended to use Thick Provisioning for the disk, despite 120GB being overkill for typical small deployments.
  • Enter the chosen static IP address, network and DNS settings. Sadly they left out a hostname field here which would have saved a lot of grief.
  • Do not start the VM on completion of the wizard, then reduce the vCSA virtual machine RAM from 8GB down to 4GB – the supported minimum configuration for small deployments of less than 10 hosts.
  • As per the instructions displayed on the vCSA VM console, connect a web browser to port 5480 (the default credentials are root and vmware), and accept the EULA.
  • At this point quit the Setup wizard. Failure to do this will cause SSL certificate issues later.
  • In the Network tab change the hostname, in the Admin tab change the root password and enable SSL certificate regeneration, and finally in the System tab set the correct time zone then reboot.
  • Create a DNS A record for your vCSA.
  • After the reboot when you connect back to the admin console, the hostname in the certificate will remain as localhost.yourdomain.com – this is apparently normal.
  • Disable SSL certificate regeneration.
  • Start the Setup wizard again from the main vCenter Server tab Summary screen.
  • Select “Configure with default settings”.
  • Enable AD authentication (an Active Directory Computer account object will be created for your vCSA).
  • Despite having enabled Active Directory authentication, this will not work until the AD domain SSO Identity Source has been configured – you’ll see the error “A general system error occurred: Authorize Exception”. The VMware documentation mentions using the SSO Admin account to do this (admin@System-Domain) but this password is not defined by the user when deploying a vCSA. By default the root account is also an SSO admin, so log in to vSphere Web Client on port 9443 as root. Navigate to Administration > Sign-On and Discovery > Configuration > Add Identity Source:
    vCSA - Add SSO Identity Source
  • Enter the server URLs in the format ldap://dc1.mydomain.com
  • The DNs will probably both be cn=Users,dc=yourdomain,dc=com since you are only likely to need the Domain Admin user or Domain Admins group.
  • You need to set the Domain Alias to the NETBIOS domain name or else the vSphere Web Client plugin’s “use windows credentials” option will not work.
  • Set Authentication Type to Password and use a basic AD user account that you might use for photocopier scan-to-email directory lookups. Test the settings and save once working ok.
  • vCSA does not implicitly trust Domain Admins like a standard vCenter installation would, and the permissions are somewhat difficult to find in the vSphere Web Client. Navigate Home > vCenter > vCenter Servers > your vCSA > Manage tab > Permissions:
    vCSA edit vCenter permissions
  • Add your required AD groups to the role Administrator.
  • I had a further issue after all this, where connecting using vSphere Client with the “use windows credentials” option would result in the error “A General System error occured: Cannot get user info”. This is due to an omission in the vCSA which has been fixed in the latest download version (currently 5.1.0.10100-1123965). You can get around this by editing a config file on the vCSA.
  • One final reboot of the appliance is necessary to avoid permissions errors from the Inventory Service when logging into the vSphere Web Client with Windows session authentication. Be aware that the port 9443 web service can take several minutes to start, even after the console of the appliance has apparently finished booting.
  • In environments where you can’t afford the 120GB of disk space for the vCSA, you could use VMware Converter to V2V the appliance, resizing to say 40GB in the process.
 
 

Tunlr enable/disable script for Microsoft Surface

I recently bought a Microsoft Surface and I have been wanting to watch a few programmes on BBC iPlayer whilst out of the country for Christmas. I discovered Tunlr – a free media proxy service which allows access to Hulu and iPlayer regardless of geolocation. However, editing DNS server settings by hand is time consuming and awkward without using the trackpad, so I wanted a script to automate the task. This will also work for other proxy services such as unblock-us.com – just replace the DNS IPs in the script. I had previously written a quick script for changing IP configuration which used netsh commands but these don’t work on Windows RT. Some other PowerShell methods I found weren’t supported either but I did find new network settings cmdlets for the purpose that were added in Windows 8/RT.

The next problem was elevation to get sufficient rights to change the network settings. It transpires that the PowerShell and VBScript environments are heavily restricted in Windows RT, which prevents auto-prompting for elevation. Fortunately Windows RT does allow Run as Administrator from the right-click menu for .cmd scripts. If you’re using touch control, you just touch and hold then release for the right-click. The script will remind you if you forget to do this. Hover your mouse over the top right corner of the script below, and use the View Source button to save the following to your desktop as Tunlr.cmd:

@echo off

::Use Tunlr to watch streaming TV services regardless of geolocation
::Tunlr DNS servers redirect requests for well-known services via Tunlr's proxy servers
::Tunlr should only be used while watching streams to reduce server load
::More details at http://tunlr.net/

::Elevation cannot be automated on Windows RT since object creation is disabled for PowerShell and VBScript
::http://stackoverflow.com/questions/13504499/windows-rt-powershell-permissiondenied-on-new-object

ipconfig /all | find "142.54.177.158" > nul && (
  echo Disabling Tunlr...
  PowerShell -Command Set-DnsClientServerAddress -InterfaceAlias "WiFi" -ResetServerAddresses || (
    echo Right-click and re-run this script as Administrator
    pause
  )
) || (
  echo Enabling Tunlr...
  PowerShell -Command Set-DnsClientServerAddress -InterfaceAlias "WiFi" -ServerAddresses 142.54.177.158,209.141.56.79 || (
    echo Right-click and re-run this script as Administrator
    pause
  )
)

UAC elevation for Windows batch script

I recently needed to make an interactive batch script elevate for admin privileges. I found an example script by jagaroth, and then refined it to make it even more compact. It only writes out one temporary script file, and passes the rest of the required variables on the command line. It can cope with paths containing spaces. It was something of a shell escaping nightmare as you can see from line 14!

@echo off

::Windows XP doesn't have UAC so skip
for /f "tokens=3*" %%i in ('reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v ProductName ^| Find "ProductName"') do set WINVER=%%i %%j 
echo %WINVER% | find "XP" > nul && goto commands

::prompt for elevation
if "%1" == "UAC" goto elevation
(
  echo Set objShell = CreateObject^("Shell.Application"^)
  echo Set objFSO = CreateObject^("Scripting.FileSystemObject"^)
  echo strPath = objFSO.GetParentFolderName^(WScript.ScriptFullName^)
  echo If objFSO.FileExists^("%~0"^) Then
  echo   objShell.ShellExecute "cmd.exe", "/c """"%~0"" UAC ""%~dp0""""", "", "runas", 1
  echo Else
  echo   MsgBox "Script file not found"
  echo End If
) > "%TEMP%\UAC.vbs"
cscript //nologo "%TEMP%\UAC.vbs"
goto :eof
:elevation
del /q "%TEMP%\UAC.vbs"

:commands
::navigate back to this script's home folder
%~d2
cd "%~p2"

::put your main script here
echo 1st arg: %1
echo 2nd arg: %2
pause

Cross-compiling FFmpeg for Serviio with shared libraries on Synology NAS (Intel, ARMv5, ARMv7, and QorIQ CPUs)

Update 3 – Updated the guide for building FFmpeg 2.1, with libx264 which is required for Serviio 1.3. The patch for the libshine encoder is no longer needed now that it has been merged into FFmpeg. I’m proud to have suggested and tested that feature and contributed in some small way to FFmpeg!

Update 2 – Updated to allow build for ARMv7 CPU in Synology products using Armada370 SoC (currently the DS213j).

Update – I discovered that the cross-compiled ARMv5 build of FFmpeg cannot encode AC3 audio without severe artifacts. After a very long time spent recompiling with different options, trying static builds etc. I finally found that the Synology cross toolchain (GCC 4.2.1) appears to cause the problem. Compiling natively on a bootstrapped ARMv5 Synology system with GCC 4.2.3 produces a working binary. However, I had to spend a long time re-working the method, particularly since you can’t run automake to build libshine on the Synology due to a dependency on a threaded version of Perl, which is missing from the Optware repo, and in turn is very tricky to compile. I have appended the ARM shared build guide to the end of this post.

This method will build FFmpeg with portable shared libraries (linked with relative paths) using a Ubuntu Desktop 12 VM. The >10MB size of the static executable is kind of getting out of hand especially on embedded systems with very limited RAM. Using shared libs means many concurrent instances of FFmpeg can use broadly the same memory footprint, and package distribution binaries can be smaller – our target systems already have libmp3lame, libz, libssl, libcrypto, libfreetype, libexpat, and libpthread which add up to several megabytes. I’m also guessing that the OS will decide when to unload them from RAM which could help when FFmpeg is being launched repeatedly in a short period of time.

Although the unmodified source code will compile successfully for QorIQ CPUs, the FFmpeg binary will core dump when running any command on a video file. I contacted Synology Support for help with this issue. Their developers mentioned that they had needed to make numerous patches to the source of the version they used for DSM, and they guessed that the following one in particular was likely to be needed. This produced a working binary.

--- ffmpeg/libavcodec/ppc/dsputil_ppc.c	2012-02-17 18:20:07.000000000 +0000
+++ ffmpeg-patched/libavcodec/ppc/dsputil_ppc.c	2012-10-18 22:15:50.740992688 +0100
@@ -116,7 +116,11 @@
 
     /* below the constraint "b" seems to mean "Address base register"
        in gcc-3.3 / RS/6000 speaks. seems to avoid using r0, so.... */
+    #if 1
+    __asm__ volatile("dcbz %0, %1" : : "r" (fakedata_middle), "r" (zero));
+    #else
     __asm__ volatile("dcbzl %0, %1" : : "b" (fakedata_middle), "r" (zero));
+    #endif
 
     for (i = 0; i < 1024 ; i ++) {
         if (fakedata[i] == (char)0)
 
#-----set up Synology toolchain
cd ~/Downloads
export DL_PATH="http://sourceforge.net/projects/dsgpl/files/DSM%204.1%20Tool%20Chains"

#----------------------------------------------------
#-----NOW PASTE ONE OF THE FOLLOWING FOUR BLOCKS TO THE TERMINAL, DEPENDING ON YOUR TARGET CPU TYPE




#-----Marvell Kirkwood mv6281/mv6282 CPU is based on the ARMv5TE core which has DSP and thumb instruction support
#-----http://www.arm.com/products/processors/technologies/dsp-simd.php
#-----http://www.marvell.com/embedded-processors/kirkwood/assets/88f6282-3_pb.pdf
wget "${DL_PATH}/Marvell%2088F628x%20Linux%202.6.32/gcc421_glibc25_88f6281-GPL.tgz"
tar xvzf gcc421_glibc25_88f6281-GPL.tgz
export CROSS_PREFIX=arm-none-linux-gnueabi
export TOOLCHAIN=/usr/local/${CROSS_PREFIX}
export TARGET=armle-unknown-linux
export MARCH="-march=armv5te"
export LAME_CONFIG="./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static --disable-decoder"
export SSL_CONFIG="./Configure.syno --prefix=${TOOLCHAIN} threads shared linux-elf-armle"
#-----libx264:
#-----http://forum.doom9.org/showthread.php?t=160584
#-----https://mailman.videolan.org/pipermail/x264-devel/2010-December/008093.html
#-----x264 builds with NEON by default because x264 is so slow without NEON (and on any non-NEON chip) as to be useless.
#-----You can of course compile with --disable-asm on such chips, but we don't do it by default because
#-----we don't feel it's necessary to actively support chips on which x264 would be basically useless
export X264_CONFIG="./configure --prefix=${TOOLCHAIN} --enable-shared --disable-opencl --disable-asm --enable-strip"
#-----FFmpeg:
#-----ARMv5TE CPU-specific options (based on inspecting 'configure' script https://github.com/FFmpeg/FFmpeg/blob/master/configure)
#-----pkg-config needs to be defined because with cross-prefix it assumes ${CROSS_PREFIX}-pkg-config which doesn't exist, and then librtmp won't be detected
#-----zbmv decoder won't cross compile for some reason so it is disabled: https://ffmpeg.org/trac/ffmpeg/ticket/1794
#-----something changed in the FFmpeg configure script and now vfp and neon optimizations have to be explicitly disabled for ARMv5TE, which doesn't have those features in any case
export FF_CONFIG="./configure --arch=arm --cpu=armv5te --enable-cross-compile --cross-prefix=${TOOLCHAIN}/bin/${CROSS_PREFIX}- --target-os=linux --prefix=${TOOLCHAIN} --enable-shared --disable-static --enable-pic --disable-ffplay --disable-ffserver --disable-vfp --disable-neon --disable-debug --enable-pthreads --enable-libshine --enable-librtmp --enable-libass --enable-libx264 --enable-gpl --disable-encoder=zmbv --pkg-config=pkg-config --extra-version=compiled_by_patters_for_Serviio"
#-----sudo apt-get automake
#-----sudo apt-get libtool
wget https://github.com/savonet/shine/zipball/master
unzip master
cd savonet-shine-*
./bootstrap
cd ..




#-----Marvell Armada 370 CPU is based on a dual issue ARMv7 core with VFPv3-16, but no NEON vector unit
#-----http://www.marvell.com/embedded-processors/armada-300/assets/Marvell_ARMADA_370_SoC.pdf
#-----http://www.arm.com/products/processors/technologies/vector-floating-point.php
wget "${DL_PATH}/Marvell%20armada%20370%20Linux%203.2.30/gcc464_glibc215_hard_armada370-GPL.tgz"
tar xvzf gcc464_glibc215_hard_armada370-GPL.tgz
#-----correct some missing header file links in the toolchain 
cp /usr/local/arm-marvell-linux-gnueabi/arm-marvell-linux-gnueabi/libc/thumb2/usr/include/asm/errno.h /usr/local/arm-marvell-linux-gnueabi/arm-marvell-linux-gnueabi/libc/usr/include/asm/errno.h
cp /usr/local/arm-marvell-linux-gnueabi/arm-marvell-linux-gnueabi/libc/thumb2/usr/include/asm/ioctl.h /usr/local/arm-marvell-linux-gnueabi/arm-marvell-linux-gnueabi/libc/usr/include/asm/ioctl.h
cp /usr/local/arm-marvell-linux-gnueabi/arm-marvell-linux-gnueabi/libc/thumb2/usr/include/asm/bitsperlong.h /usr/local/arm-marvell-linux-gnueabi/arm-marvell-linux-gnueabi/libc/usr/include/asm/bitsperlong.h
export CROSS_PREFIX=arm-marvell-linux-gnueabi
export TARGET=armle-unknown-linux
export TOOLCHAIN=/usr/local/${CROSS_PREFIX}
export MARCH="-mhard-float -mfpu=vfpv3-d16"
export LAME_CONFIG="./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static --disable-decoder"
export SSL_CONFIG="./Configure.syno --prefix=${TOOLCHAIN} threads shared no-asm linux-elf-armle"
#-----libx264:
#-----http://forum.doom9.org/showthread.php?t=160584
#-----https://mailman.videolan.org/pipermail/x264-devel/2010-December/008093.html
#-----x264 builds with NEON by default because x264 is so slow without NEON (and on any non-NEON chip) as to be useless.
#-----You can of course compile with --disable-asm on such chips, but we don't do it by default because
#-----we don't feel it's necessary to actively support chips on which x264 would be basically useless
export X264_CONFIG="./configure --prefix=${TOOLCHAIN} --host=${TARGET} --cross-prefix=${TOOLCHAIN}/bin/${CROSS_PREFIX}- --enable-shared --disable-opencl --disable-asm --enable-pic --enable-strip"
#-----FFmpeg:
#-----ARMv7 CPU-specific options (based on inspecting 'configure' script https://github.com/FFmpeg/FFmpeg/blob/master/configure)
#-----pkg-config needs to be defined because with cross-prefix it assumes ${CROSS_PREFIX}-pkg-config which doesn't exist, and then librtmp won't be detected
export FF_CONFIG="./configure --arch=arm --enable-cross-compile --cross-prefix=${TOOLCHAIN}/bin/${CROSS_PREFIX}- --target-os=linux --prefix=${TOOLCHAIN} --enable-shared --disable-static --enable-pic --disable-ffplay --disable-ffserver --disable-neon --enable-thumb --disable-debug --enable-pthreads --enable-libmp3lame --enable-librtmp --enable-libass --enable-libx264 --enable-gpl --pkg-config=pkg-config --extra-version=compiled_by_patters_for_Serviio"




#-----Intel CPUs used in Synology products are all 64bit and SSSE3 capable but existing DSM libs are 32bit, so use the i686 toolchain rather than x86_64
#-----libx264 requires yasm 1.20 which is not on Ubuntu apt-get
#-----https://trac.ffmpeg.org/wiki/UbuntuCompilationGuide
sudo apt-get remove yasm
wget http://www.tortall.net/projects/yasm/releases/yasm-1.2.0.tar.gz
tar xvzf yasm-1.2.0.tar.gz
cd yasm-1.2.0
./configure --prefix=/
make
sudo make install
cd..
wget "${DL_PATH}/Intel%20x86%20Linux%203.2.11%20%28Pineview%29/gcc421_glibc236_x86-GPL.tgz"
tar xvzf gcc421_glibc236_x86-GPL.tgz
export CROSS_PREFIX=i686-linux-gnu
export TOOLCHAIN=/usr/local/${CROSS_PREFIX}
export TARGET=i686-linux-gnu
export LAME_CONFIG="./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static --disable-decoder --enable-nasm"
export SSL_CONFIG="./Configure.syno --prefix=${TOOLCHAIN} threads shared linux-generic32"
export X264_CONFIG="./configure --prefix=${TOOLCHAIN} --host=${TARGET} --cross-prefix=${TOOLCHAIN}/bin/${CROSS_PREFIX}- --enable-shared --disable-opencl --enable-pic --enable-strip"
#-----FFmpeg:
#-----Intel CPU-specific options (based on inspecting 'configure' script https://github.com/FFmpeg/FFmpeg/blob/master/configure)
#-----pkg-config needs to be defined because with cross-prefix it assumes ${CROSS_PREFIX}-pkg-config which doesn't exist, and then librtmp won't be detected
export FF_CONFIG="./configure --arch=x86 --enable-ssse3 --enable-cross-compile --cross-prefix=${TOOLCHAIN}/bin/${CROSS_PREFIX}- --target-os=linux --prefix=${TOOLCHAIN} --enable-shared --disable-static --enable-pic --disable-ffplay --disable-ffserver --disable-debug --enable-pthreads --enable-libmp3lame --enable-librtmp --enable-libass --enable-libx264 --enable-gpl --pkg-config=pkg-config --extra-version=compiled_by_patters_for_Serviio"




#-----QorIQ P1022 CPU is based on a dual PowerPC e500v2 core with Signal Processing Engine (SPE) which is not a classic FPU design, but no AltiVec vector unit 
#-----Some QorIQ models have e500mc cores with true FPUs but these are not used in 2013 series Synology NAS
#-----http://en.wikipedia.org/wiki/QorIQ
#-----http://cache.freescale.com/files/32bit/doc/fact_sheet/QP1022FS.pdf?fpsp=1
wget "${DL_PATH}/PowerPC%20QorIQ%20Linux%202.6.32/gcc4374_eglibc2874_qoriq-GPL.tgz"
tar xvzf gcc4374_eglibc2874_qoriq-GPL.tgz
export CROSS_PREFIX=powerpc-none-linux-gnuspe
export TOOLCHAIN=/usr/local/${CROSS_PREFIX}
export TARGET=powerpc-unknown-linux
export MARCH="-mcpu=8548 -mhard-float -mfloat-gprs=double"
export LAME_CONFIG="./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static --disable-decoder"
export SSL_CONFIG="./Configure.syno --prefix=${TOOLCHAIN} threads shared linux-ppc"
#-----asm disabled since QorIQ has no AltiVec
export X264_CONFIG="./configure --prefix=${TOOLCHAIN} --host=${TARGET} --cross-prefix=${TOOLCHAIN}/bin/${CROSS_PREFIX}- --enable-shared --disable-opencl --disable-asm --enable-pic --enable-strip"
#-----FFmpeg:#-----PowerPC e500v2 CPU-specific options (based on inspecting 'configure' script https://github.com/FFmpeg/FFmpeg/blob/master/configure)
#-----pkg-config needs to be defined because with cross-prefix it assumes ${CROSS_PREFIX}-pkg-config which doesn't exist, and then librtmp won't be detected
export FF_CONFIG="./configure --arch=ppc --cpu=e500v2 --enable-cross-compile --cross-prefix=${TOOLCHAIN}/bin/${CROSS_PREFIX}- --target-os=linux --prefix=${TOOLCHAIN} --enable-shared --disable-static --enable-pic --disable-ffplay --disable-ffserver --disable-debug --enable-pthreads --enable-libmp3lame --enable-librtmp --enable-libass --enable-libx264 --enable-gpl --pkg-config=pkg-config --extra-version=compiled_by_patters_for_Serviio"




#----------------------------------------------------
#-----script continues

sudo mv ${CROSS_PREFIX} /usr/local
export AR=${TOOLCHAIN}/bin/${CROSS_PREFIX}-ar
export CC=${TOOLCHAIN}/bin/${CROSS_PREFIX}-gcc
export CXX=${TOOLCHAIN}/bin/${CROSS_PREFIX}-g++
export LD=${TOOLCHAIN}/bin/${CROSS_PREFIX}-ld
export LDSHARED="${TOOLCHAIN}/bin/${CROSS_PREFIX}-gcc -shared "
export RANLIB=${TOOLCHAIN}/bin/${CROSS_PREFIX}-ranlib
export CFLAGS="-I${TOOLCHAIN}/include -O3 ${MARCH}"
export LDFLAGS="-L${TOOLCHAIN}/lib"
export PKG_CONFIG_PATH="${TOOLCHAIN}/lib/pkgconfig"


#-----fetch Synology's DSM sources for certain libraries
wget https://dl.dropboxusercontent.com/u/1188556/DSM-source.tgz
tar xvzf DSM-source.tgz
cd DSM-source

#-----libz shared lib (toolchain only has static lib)
cd zlib-1.2
./configure --prefix=${TOOLCHAIN}
#-----owing to a bug in the configure script, libz cross compiles without a SONAME (check it with objdump -x)
#-----http://forums.gentoo.org/viewtopic-p-7156392.html
sed -i "s/\(^LDSHARED=.*$\)/\1 -Wl,-soname,libz.so.1,--version-script,zlib.map/" Makefile
make
make install
cd ..

#-----LAME shared lib
cd lame-398
$LAME_CONFIG
make
make install
cd ..

#-----shine shared lib for ARMv5
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] && mv ../savonet-shine-* .
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] && cd savonet-shine-*
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] && ./configure --prefix=${TOOLCHAIN} --host=${CROSS_PREFIX} --build=x86_64-linux-gnu --enable-shared --disable-static
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] && make
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] && make install
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] && cd ..

#-----openssl shared lib (Synology seem to have tweaked the source for each CPU)
cd openssl-1.0.x-${CROSS_PREFIX}
$SSL_CONFIG
make
make install
cd ..

#-----expat shared lib
cd expat-2.x
./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static
make
make install
cd ..

#-----freetype shared lib
cd freetype-2.3.7
./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static
make
make install
cd ..

#-----finished building DSM-bundled libs
cd ..


#-----fribidi shared lib
wget http://fribidi.org/download/fribidi-0.19.5.tar.bz2
tar xvjf fribidi-0.19.5.tar.bz2
cd fribidi-0.19.5
#-----patch fribidi source since it won't compile - some sort of page size function is no longer available
#-----error on ARMv5 and Intel: fribidi-run.c:70: error: 'PAGE_SIZE' undeclared (first use in this function)
#-----error on QorIQ: include/asm/kdump.h:41: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'void'
#-----clues found here:
#-----http://armbedded.eu/node/56
#-----http://lists.debian.org/debian-glibc/2006/10/msg00169.html
#-----http://my.safaribooksonline.com/book/operating-systems-and-server-administration/linux/0596009585/advanced-file-i-o/mapping_files_into_memory
[ "${CROSS_PREFIX}" == "arm-marvell-linux-gnueabi" ] || sed -i "s|include <asm/page.h>|include <unistd.h>\n#   define PAGE_SIZE sysconf(_SC_PAGESIZE)|" lib/common.h
./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static
make
make install
cd ..

#-----fontconfig shared lib
wget http://freedesktop.org/software/fontconfig/release/fontconfig-2.10.91.tar.gz
tar xvzf fontconfig-2.10.91.tar.gz
cd fontconfig-2.10.91
LDFLAGS=-Wl,-rpath,\'\$\$ORIGIN\' ./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static
#-----correct some non-standard library linking behaviour
sed -i "s/^hardcode_into_libs=yes/hardcode_into_libs=no/" libtool
make
make install
cd ..

#-----libass shared lib
wget http://libass.googlecode.com/files/libass-0.10.1.tar.gz
tar xvzf libass-0.10.1.tar.gz
cd libass-0.10.1
LDFLAGS=-Wl,-rpath,\'\$\$ORIGIN\' ./configure --prefix=${TOOLCHAIN} --host=${TARGET} --build=x86_64-linux-gnu --enable-shared --disable-static
#-----correct some non-standard library linking behaviour
sed -i "s/^hardcode_into_libs=yes/hardcode_into_libs=no/" libtool
make
make install
#-----Although libass would link correctly to older FFmpeg versions, this mod is now necessary:
#-----http://ffmpeg.org/pipermail/ffmpeg-user/2012-November/010906.html
sed -i -r "s/(^Libs: .*$)/\1 -lfontconfig -lfribidi -lfreetype -lexpat/" $TOOLCHAIN/lib/pkgconfig/libass.pc
cd ..

#-----librtmp shared lib
wget http://download.serviio.org/opensource/rtmpdump.tar.gz
tar xvzf rtmpdump.tar.gz
cd rtmpdump
make CROSS_COMPILE=${TOOLCHAIN}/bin/${CROSS_PREFIX}- SYS=posix prefix=${TOOLCHAIN} INC=-I${TOOLCHAIN}/include XLDFLAGS=-L${TOOLCHAIN}/lib
make install prefix=${TOOLCHAIN}
cd ..

#-----libx264 shared lib
wget http://download.serviio.org/opensource/last_x264.tar.bz2
tar xvfj last_x264.tar.bz2
cd x264-snapshot-20130529-2245
$X264_CONFIG
make
make install
cd ..

#-----FFmpeg
wget http://ffmpeg.org/releases/ffmpeg-2.1.tar.gz
tar xvzf ffmpeg-2.1.tar.gz
cd ffmpeg-2.1

#-----ARM-specific fixed point maths modification for mpeg audio encoder
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] && sed -i "s/^#define USE_FLOATS//" libavcodec/mpegaudioenc.c

#-----QorIQ-specific fix provided by Synology Support 
[ "${CROSS_PREFIX}" == "powerpc-none-linux-gnuspe" ] && wget https://dl.dropboxusercontent.com/u/1188556/ffmpeg-synology-qoriq.patch
[ "${CROSS_PREFIX}" == "powerpc-none-linux-gnuspe" ] && patch --verbose -p1 < ffmpeg-synology-qoriq.patch

#-----set library linker to use relative library search path (../lib and .)
#-----tricky shell escaping of $ORIGIN is required http://itee.uq.edu.au/~daniel/using_origin/
#-----http://stackoverflow.com/questions/6562403/i-dont-understand-wl-rpath-wl
#-----the use of -rpath-link appears to be necessary for compilation using the Armada370 toolchain
LDFLAGS=-Wl,-rpath,\'\$\$ORIGIN/../lib:\$\$ORIGIN\',-rpath-link,${TOOLCHAIN}/lib ${FF_CONFIG}

#-----external libs (librtmp, libass) don't seem to link correctly as shared libs
#-----on all toolchains except Marvell Kirkwood without this fix (check compiled binary with objdump -x)
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] || sed -r -i 's%(^LDFLAGS.*)\$\(LDFLAGS\)%\1-Wl,-rpath=z888z\$\$ORIGIN/../lib:\$\$ORIGINz888z%' common.mak; sed -i "s/z888z/'/g" common.mak

make
make install
cd ..

mkdir native-${CROSS_PREFIX}
cd native-${CROSS_PREFIX}
cp ${TOOLCHAIN}/bin/ffmpeg .
cp ${TOOLCHAIN}/lib/libass.so.4 .
cp ${TOOLCHAIN}/lib/libavcodec.so.55 .
cp ${TOOLCHAIN}/lib/libavdevice.so.55 .
cp ${TOOLCHAIN}/lib/libavfilter.so.3 .
cp ${TOOLCHAIN}/lib/libavformat.so.55 .
cp ${TOOLCHAIN}/lib/libavutil.so.52 .
cp ${TOOLCHAIN}/lib/libfontconfig.so.1 .
cp ${TOOLCHAIN}/lib/libfribidi.so.0 .
cp ${TOOLCHAIN}/lib/libpostproc.so.52 .
cp ${TOOLCHAIN}/lib/librtmp.so.0 .
cp ${TOOLCHAIN}/lib/libswresample.so.0 .
cp ${TOOLCHAIN}/lib/libswscale.so.2 .
cp ${TOOLCHAIN}/lib/libx264.so.133 .
[ "${CROSS_PREFIX}" == "arm-none-linux-gnueabi" ] && cp ${TOOLCHAIN}/lib/libshine.so.3 .
sudo chown root:root *
tar -cvzf ffmpeg-syno-${CROSS_PREFIX}.tgz *
mkdir oldDSM
cd oldDSM
cp ${TOOLCHAIN}/lib/libz.so.1 .
cp ${TOOLCHAIN}/lib/libcrypto.so.1.0.0 .
cp ${TOOLCHAIN}/lib/libssl.so.1.0.0 .
cp ${TOOLCHAIN}/lib/libmp3lame.so.0 .
cp ${TOOLCHAIN}/lib/libfreetype.so.6 .
cp ${TOOLCHAIN}/lib/libexpat.so.1 .
sudo chown root:root *
tar -cvzf ffmpeg-syno-${CROSS_PREFIX}-oldDSM.tgz *
cd ..
 

Native compile for ARMv5 systems

Here is the method to natively compile FFmpeg with libshine and libass, with shared libraries rather than static. It transpires that the cross compiled build has a broken AC3 encoder which produces crackling and muffled audio streams. The guide assumes a bootstrapped NAS with the optware-devel tools installed. See my much earlier blog post for details of how to set that up, since there are some pitfalls. I haven’t run through and tested the whole sequence – I have assembled this from hundreds of lines of notes I made while working – but I’m publishing it here to help others, and to refer to in future when I need to build newer FFmpeg revisions for future Serviio releases.

#-----on a bootstrapped system that's since had its DSM upgraded the path has been reset
#export PATH=/opt/bin:/opt/sbin:$PATH

#-----fetch headers for zlib, OpenSSL, LAME, freetype2, and expat libraries obtained by compiling the DSM 4.1 included versions from source, plus ncurses headers from Synology ARMv5 cross toolchain
cd /tmp
mkdir include
cd include
wget --no-check-certificate https://dl.dropboxusercontent.com/u/1188556/synology-armv5te-headers.tgz
tar xvzf synology-armv5te-headers.tgz

#-----this archive also contains pkgconfig definitions for DSM's existing versions of libcrypto, libssl and freetype2, edited to fit requirements
#-----this will allow librtmp to link to DSM's existing OpenSSL libraries, and libass to link to DSM's bundled freetype2 and expat libraries
cp *.pc /opt/lib/pkgconfig
cd /volume1/@tmp

#-----fetch savonet-shine source which has already had the bootstrap script run on a Linux system with automake
#-----on the Linux system you would need to run:
#sudo apt-get automake
#sudo apt-get libtool
#wget https://github.com/savonet/shine/zipball/master
#unzip master
#cd savonet-shine-*
#./bootstrap
#cd ..
#-----but here's one I made earlier:
wget --no-check-certificate https://dl.dropboxusercontent.com/u/1188556/savonet-shine-bc9e3b6.tgz
tar xvzf savonet-shine-bc9e3b6.tgz
cd savonet-shine-bc9e3b6
./configure --prefix=/opt --enable-shared --disable-static
make
#-----don't worry about the repeated message #warning NO MULT FILE FOR ARCHITECTURE - USING GENERIC MATH
#-----the include for mult_sarm_gcc.h (optimized ARM maths in assembler) is commented out in src/lib/types.h
#-----the comment mentions that fixing this is on the to-do list, so it can't be helped
make install
cd ..

#-----fetch libfdk-aac fixed point math AAC encoder
#-----binaries linked to it cannot be redistributed under GPL
#-----so it is excluded from the FFmpeg I built for the Serviio package
wget http://sourceforge.net/projects/opencore-amr/files/fdk-aac/fdk-aac-0.1.2.tar.gz
tar xvzf fdk-aac-0.1.2.tar.gz
cd fdk-aac-0.1.2
./configure --prefix=/opt --disable-static --enable-shared
make
make install
cd ..

#-----fetch libRTMP source
wget http://download.serviio.org/opensource/rtmpdump.tar.gz
tar xvzf rtmpdump.tar.gz
cd rtmpdump
#-----the following options will force it to use the existing OpenSSL libs thanks to the imported headers
make SYS=posix prefix=/opt INC=-I/tmp/include XLDFLAGS=-L/lib
make install prefix=/opt
cp -R /opt/include/librtmp /tmp/include
#-----remove unsupported URL line from pkgconfig/librtmp.pc
#-----pkg-config --exists --print-errors librtmp
sed -i -e '/^URL/d' /opt/lib/pkgconfig/librtmp.pc
cd ..

#-----fetch fribidi source
wget http://fribidi.org/download/fribidi-0.19.5.tar.bz2
tar xvjf fribidi-0.19.5.tar.bz2
cd fribidi-0.19.5
#-----patch fribidi source since it won't compile on ARMv5
#-----fribidi-run.c:70: error: 'PAGE_SIZE' undeclared (first use in this function)
#-----clues found here:
#-----http://armbedded.eu/node/56
#-----http://lists.debian.org/debian-glibc/2006/10/msg00169.html
#-----http://my.safaribooksonline.com/book/operating-systems-and-server-administration/linux/0596009585/advanced-file-i-o/mapping_files_into_memory
sed -i "s|include <asm/page.h>|include <unistd.h>\n#   define PAGE_SIZE sysconf(_SC_PAGESIZE)|" lib/common.h
./configure --prefix=/opt --enable-shared --disable-static
make
make install
cd ..

#-----fetch fontconfig source
wget http://freedesktop.org/software/fontconfig/release/fontconfig-2.10.91.tar.gz
tar xvzf fontconfig-2.10.91.tar.gz
cd fontconfig-2.10.91
LDFLAGS=-Wl,-rpath='$$ORIGIN' ./configure --prefix=/opt --enable-shared --disable-static
#-----correct some non-standard library linking behaviour
sed -i "s/^hardcode_into_libs=yes/hardcode_into_libs=no/" libtool
sed -i "s|^sys_lib_search_path_spec="/opt/lib/gcc/arm-none-linux-gnueabi/4.2.3 /opt/arm-none-linux-gnueabi/lib /opt/lib /lib /usr/lib"|sys_lib_search_path_spec="/lib /usr/lib /opt/lib/gcc/arm-none-linux-gnueabi/4.2.3 /opt/arm-none-linux-gnueabi/lib /opt/lib"|" libtool
make
make install
cd ..

#-----fetch libass source
wget http://libass.googlecode.com/files/libass-0.10.1.tar.gz
tar xvzf libass-0.10.1.tar.gz
cd libass-0.10.1
export CFLAGS=-I/tmp/include
LDFLAGS=-Wl,-rpath='$$ORIGIN' ./configure --prefix=/opt --enable-shared --disable-static
#-----correct some non-standard library linking behaviour
sed -i "s/^hardcode_into_libs=yes/hardcode_into_libs=no/" libtool
sed -i "s|^sys_lib_search_path_spec="/opt/lib/gcc/arm-none-linux-gnueabi/4.2.3 /opt/arm-none-linux-gnueabi/lib /opt/lib /lib /usr/lib"|sys_lib_search_path_spec="/lib /usr/lib /opt/lib/gcc/arm-none-linux-gnueabi/4.2.3 /opt/arm-none-linux-gnueabi/lib /opt/lib"|" libtool
make
make install
cd ..

#-----libx264 shared lib
wget http://download.serviio.org/opensource/last_x264.tar.bz2
tar xvfj last_x264.tar.bz2
cd x264-snapshot-20130529-2245
sed -i "s/^\${SRCPATH}\/version\.sh/bash \${SRCPATH}\/version\.sh/" ./configure
#-----http://forum.doom9.org/showthread.php?t=160584
#-----https://mailman.videolan.org/pipermail/x264-devel/2010-December/008093.html
#-----x264 builds with NEON by default because x264 is so slow without NEON (and on any non-NEON chip) as to be useless.
#-----You can of course compile with --disable-asm on such chips, but we don't do it by default because
#-----we don't feel it's necessary to actively support chips on which x264 would be basically useless
bash ./configure --prefix=/opt --enable-shared --disable-opencl --disable-asm
make
make install
cd ..

#-----FFmpeg
wget http://ffmpeg.org/releases/ffmpeg-2.1.tar.gz
tar xvzf ffmpeg-2.1.tar.gz
cd ffmpeg-2.1

#-----ARMv5-specific fixed point maths modification for mpeg audio encoder, mentioned in same ticket
sed -i "s/^#define USE_FLOATS//" libavcodec/mpegaudioenc.c

#-----FFmpeg configure parameters for native compile with shared libraries
#-----something changed in the FFmpeg configure script and now vfp and neon optimizations have to be explicitly disabled for ARMv5TE, which doesn't have those features in any case
export FF_CONFIG="./configure --arch=arm --cpu=armv5te --prefix=/opt --extra-cflags=-I/tmp/include --enable-shared --disable-static --enable-pic --disable-ffplay --disable-ffserver --disable-vfp --disable-neon --disable-debug --enable-pthreads --enable-libshine --enable-libfdk_aac --enable-librtmp --enable-libass --enable-libx264 --enable-gpl --enable-nonfree --extra-version=compiled_by_patters_for_Serviio"

#-----set library linker to use relative library search path (../lib:.)
#-----tricky shell escaping of $ORIGIN is required http://itee.uq.edu.au/~daniel/using_origin/
#-----http://stackoverflow.com/questions/6562403/i-dont-understand-wl-rpath-wl
LDFLAGS=-Wl,-rpath=\'\$\$ORIGIN/../lib:\$\$ORIGIN\' ${FF_CONFIG}
make
make install

mkdir /volume1/@tmp/binaries
cd /volume1/@tmp/binaries
cp /opt/bin/ffmpeg .
cp /opt/lib/libass.so.4 .
cp /opt/lib/libavcodec.so.55 .
cp /opt/lib/libavdevice.so.55 .
cp /opt/lib/libavfilter.so.3 .
cp /opt/lib/libavformat.so.55 .
cp /opt/lib/libavutil.so.52 .
cp /opt/lib/libfontconfig.so.1 .
cp /opt/lib/libfribidi.so.0 .
cp /opt/lib/libpostproc.so.52 .
cp /opt/lib/librtmp.so.0 .
cp /opt/lib/libshine.so.3 .
cp /opt/lib/libfdk-aac.so.0 .
cp /opt/lib/libswresample.so.0 .
cp /opt/lib/libswscale.so.2 .
cp /opt/lib/libx264.so.133 .
tar -cvzf ffmpeg-syno-armv5tel.tgz *
mkdir oldDSM
cd oldDSM
cp /lib/libz.so.1 .
cp /lib/libcrypto.so.1.0.0 .
cp /lib/libssl.so.1.0.0 .
cp /lib/libmp3lame.so.0 .
cp /lib/libfreetype.so.6 .
cp /lib/libexpat.so.1 .
tar -cvzf ffmpeg-syno-armv5tel-oldDSM.tgz *
 

Here’s how the library dependencies look. There are no links to any libraries in /opt, and DSM-bundled libraries in /lib are linked to where possible. All the other new libraries are kept within the Serviio lib folder (../lib relative to the ffmpeg binary).

/$ ldd /volume1/@appstore/Serviio/bin/ffmpeg
        libavdevice.so.55 => /volume1/@appstore/Serviio/bin/../lib/libavdevice.so.55 (0x40026000)
        libavfilter.so.3 => /volume1/@appstore/Serviio/bin/../lib/libavfilter.so.3 (0x40039000)
        libavformat.so.55 => /volume1/@appstore/Serviio/bin/../lib/libavformat.so.55 (0x40112000)
        libavcodec.so.55 => /volume1/@appstore/Serviio/bin/../lib/libavcodec.so.55 (0x40271000)
        libpostproc.so.52 => /volume1/@appstore/Serviio/bin/../lib/libpostproc.so.52 (0x4110c000)
        libswresample.so.0 => /volume1/@appstore/Serviio/bin/../lib/libswresample.so.0 (0x4111e000)
        libswscale.so.2 => /volume1/@appstore/Serviio/bin/../lib/libswscale.so.2 (0x4113b000)
        libavutil.so.52 => /volume1/@appstore/Serviio/bin/../lib/libavutil.so.52 (0x4118c000)
        libx264.so.133 => /volume1/@appstore/Serviio/bin/../lib/libx264.so.133 (0x411f3000)
        libm.so.6 => /lib/libm.so.6 (0x41346000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x413f5000)
        libc.so.6 => /lib/libc.so.6 (0x41415000)
        librt.so.1 => /lib/librt.so.1 (0x41544000)
        libass.so.4 => /volume1/@appstore/Serviio/bin/../lib/libass.so.4 (0x41553000)
        librtmp.so.0 => /volume1/@appstore/Serviio/bin/../lib/librtmp.so.0 (0x41585000)
        libz.so.1 => /lib/libz.so.1 (0x415a5000)
        libshine.so.3 => /volume1/@appstore/Serviio/bin/../lib/libshine.so.3 (0x415c2000)
        libfdk-aac.so.0 => /volume1/@appstore/Serviio/bin/../lib/libfdk-aac.so.0 (0x415d4000)
        /lib/ld-linux.so.3 (0x40000000)
        libfreetype.so.6 => /lib/libfreetype.so.6 (0x41678000)
        libfribidi.so.0 => /volume1/@appstore/Serviio/bin/../lib/libfribidi.so.0 (0x41737000)
        libfontconfig.so.1 => /volume1/@appstore/Serviio/bin/../lib/libfontconfig.so.1 (0x41756000)
        libssl.so.1.0.0 => /lib/libssl.so.1.0.0 (0x4178d000)
        libcrypto.so.1.0.0 => /lib/libcrypto.so.1.0.0 (0x417db000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x4196f000)
        libexpat.so.1 => /lib/libexpat.so.1 (0x41983000)
        libdl.so.2 => /lib/libdl.so.2 (0x419aa000)
/$

Bliss album art manager package for Synology NAS

bliss-UI

 

Bliss is a Java application written by Dan Gravell which can manage file names, tags and album art for your music collection and which can also enforce their consistency. It is designed to be left running once installed so that albums you add later will have these same policies applied to them automatically. It supports a wide range of music formats, and effortlessly deals with very large collections – apparently even ones containing quite obscure recordings. My own collection didn’t really put this to the test, since it doesn’t contain bootlegs, live sets or rarities plus I had also already obtained cover art for it (from back when CoverFlow first graced the screen of my iPhone 2G).

I could see from referrals to this blog that people were asking Dan for a Synology package which he didn’t have the time to investigate, and so I thought that it would make an interesting little project, especially since a NAS is the ideal device to run Bliss on. Although there was already a Howto post on the Synology forums, that guide only really covered getting the basic functionality of Bliss up and running – it was missing the best bits: the filesystem watching and audio fingerprinting features. These depend on natively compiled binaries, but Bliss doesn’t support running on ARM or PowerPC CPUs, and its Intel binaries don’t work on Synology either (the usual problem – glibc too old). Getting these working provided precisely the sort of challenge I like. Not only were they difficult to compile, but getting them integrated into the various OSGi ‘bundles’ that make up Bliss was quite involved too.

Bliss uses an open source library called Chromaprint, itself part of the wider Acoustid project. The aim is to scan an audio file, to produce a fingerprint of the sound, and then to compare this against an open online database such as MusicBrainz.org to identify music regardless of compression codec used. Its author Lukáš Lalinský explains how it works. It turns out that Chromaprint uses some complex maths functions that FFmpeg can provide (specifically Fourier Transform), and FFmpeg’s shared libraries are already included with Synology DSM. I was able to download the sources that were used to build DSM, which then allowed me to build Chromaprint linked to those existing libraries – resulting in a minuscule 84KB build of fpcalc, rather than the statically compiled ones for various OS and CPU architectures included with Bliss, which weigh in at several megabytes. I think I’m finally ‘getting’ what open source is all about, which is nice since that was my objective in experimenting with my NAS. To prevent fpcalc building and linking to its dynamic library libchromaprint.so and to get it to detect FFmpeg properly I had to carefully inspect the Makefiles to find the correct build syntax:
FFMPEG_DIR=/usr/local/i686-linux-gnu cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=NO .
FFMPEG_DIR is the base folder from which CMake will look for lib/libavcodec.so and include/libavcodec/avfft.h.

It’s a bit of a nuisance that on ARM you first have to compile CMake because it doesn’t exist on the Optware ipkg repository, but it was straightforward enough to build. To build the Intel binaries I cross-compiled on a Ubuntu Desktop 12 virtual machine using the Synology toolchain (see the Synology DSM 3rd party developer guide for details).

For watching the filesystem Bliss uses JNotify to hook into the Linux kernel’s inotify subsystem. Getting this compiled was tricky. It seems no one has reported successfully compiling it for an ARM CPU, and JNotify’s author Omry Yadan wasn’t aware of anyone doing this either. The problem is that compilation halts with these errors:

In file included from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
../inotify-syscalls.h:35:1: error: "__NR_inotify_init" redefined
In file included from /opt/include/sys/syscall.h:25,
                 from ../inotify-syscalls.h:4,
                 from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
/opt/include/asm/unistd.h:344:1: error: this is the location of the previous definition
In file included from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
../inotify-syscalls.h:36:1: error: "__NR_inotify_add_watch" redefined
In file included from /opt/include/sys/syscall.h:25,
                 from ../inotify-syscalls.h:4,
                 from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
/opt/include/asm/unistd.h:345:1: error: this is the location of the previous definition
In file included from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
../inotify-syscalls.h:37:1: error: "__NR_inotify_rm_watch" redefined
In file included from /opt/include/sys/syscall.h:25,
                 from ../inotify-syscalls.h:4,
                 from ../net_contentobjects_jnotify_linux_JNotify_linux.c:43:
/opt/include/asm/unistd.h:346:1: error: this is the location of the previous definition

By searching in a very generic way for a solution I found this post on stackoverflow.com which helped me to patch the header inotify-syscalls.h to get around the problem. Since there is no JDK for ARM embedded systems I included the headers from OpenJDK6 which I took from a Ubuntu VM.

Compiling on Intel also required a fix. I was getting the same error as featured in this post on the JNotify user forum on SourceForge.net:
expected specifier-qualifier-list before ‘pid_t’

Despite some people’s apparent success simply rearranging the order of the includes in net_contentobjects_jnotify_linux_JNotify_linux.c this didn’t help me. I’m not sure quite how I stumbled upon it, but I found the solution staring at me on this page:
The size_t, ssize_t, uid_t, gid_t, off_t and pid_t types are defined as described in sys/types.h

I inserted an additional include in for sys/types.h and it compiled ok. It’s worth pointing out that, although all Intel Synology units are x86-64 architecture, the Oracle JRE for Embedded is x86 (32 bit), so I used the i686 toolchain. Synology DSM’s FFmpeg shared libraries are also 32 bit so my build of fpcalc needed to comply with this. Bliss nonetheless expects the binary to be called fpcalc_linux64 since Bliss is detecting the underlying Linux CPU architecture.

Once I had got past the obstacle of compiling the native code, I needed to liaise back and forth with Dan to understand how Bliss was dealing with its libraries and how I could replace the built-in versions. Originally this was quite a kludge but Dan has since abstracted out the native binaries into their own OSGI bundle fragments which makes things a lot easier, and should allow Bliss to survive an in-app update. The Synology package provides the following architecture-specific jar files (with corresponding edits to their manifest). Thank you to Dan for all the quick answers!

  • net.contentobjects.jnotify.linux.amd64-0.94.0.jar
  • net.contentobjects.jnotify.linux.ARM_le-0.94.0.jar
  • net.contentobjects.jnotify.linux.PowerPC-0.94.0.jar
  • com.elsten.bliss.policy.tag.auto.linux.amd64-1.0.0.jar – contains fpcalc.
  • com.elsten.bliss.policy.tag.auto.linux.ARM_le-1.0.0.jar
  • com.elsten.bliss.policy.tag.auto.linux.PowerPC-1.0.0.jar

Here are my fixes to JNotify to get the native library part compiled, since they may help someone else:

 

Patch for native compile of JNotify 0.94 on ARM Synology

diff -crB jnotify-vanilla/inotify-syscalls.h jnotify-arm/inotify-syscalls.h
*** jnotify-vanilla/inotify-syscalls.h	2005-11-30 15:07:56.000000000 +0000
--- jnotify-arm/inotify-syscalls.h	2012-09-14 02:43:44.032130098 +0100
***************
*** 32,40 ****
  # define __NR_inotify_add_watch	152
  # define __NR_inotify_rm_watch	156
  #elif defined (__arm__)
! # define __NR_inotify_init	316
! # define __NR_inotify_add_watch	317
! # define __NR_inotify_rm_watch	318
  #elif defined (__SH4__)
  # define __NR_inotify_init	290
  # define __NR_inotify_add_watch	291
--- 32,46 ----
  # define __NR_inotify_add_watch	152
  # define __NR_inotify_rm_watch	156
  #elif defined (__arm__)
! # ifndef __NR_inotify_init
! #  define __NR_inotify_init     316
! # endif
! # ifndef __NR_inotify_add_watch
! #  define __NR_inotify_add_watch 317
! # endif
! # ifndef __NR_inotify_rm_watch
! #  define __NR_inotify_rm_watch 318
! # endif
  #elif defined (__SH4__)
  # define __NR_inotify_init	290
  # define __NR_inotify_add_watch	291
diff -crB jnotify-vanilla/Release/subdir.mk jnotify-arm/Release/subdir.mk
*** jnotify-vanilla/Release/subdir.mk	2011-02-28 18:07:20.000000000 +0000
--- jnotify-arm/Release/subdir.mk	2012-09-14 02:29:00.000000000 +0100
***************
*** 17,23 ****
  %.o: ../%.c
  	@echo 'Building file: $<'
  	@echo 'Invoking: GCC C Compiler'
! 	gcc -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux -O3 -Wall -Werror -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"$@" "$<"
  	@echo 'Finished building: $<'
  	@echo ' '
  
--- 17,23 ----
  %.o: ../%.c
  	@echo 'Building file: $<'
  	@echo 'Invoking: GCC C Compiler'
! 	gcc -I/volume1/public/temp/jdk_include/ -I/volume1/public/temp/jdk_include/linux -O3 -Wall -Werror -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"$@" "$<"
  	@echo 'Finished building: $<'
  	@echo ' '
 

Patch for cross compile of JNotify 0.94 for other Synology CPU architectures, using Ubuntu Desktop 12

diff -crB jnotify-vanilla/net_contentobjects_jnotify_linux_JNotify_linux.c jnotify-i686/net_contentobjects_jnotify_linux_JNotify_linux.c
*** jnotify-vanilla/net_contentobjects_jnotify_linux_JNotify_linux.c	2011-02-28 18:07:20.000000000 +0000
--- jnotify-xcomp/net_contentobjects_jnotify_linux_JNotify_linux.c	2012-09-14 00:41:53.455010206 +0100
***************
*** 36,41 ****
--- 36,42 ----
  #include <sys/time.h>
  #include <sys/select.h>
  #include <sys/ioctl.h>
+ #include <sys/types.h>
  #include <errno.h>
  #include <stdio.h>
  #include <unistd.h>
diff -crB jnotify-vanilla/Release/makefile jnotify-i686/Release/makefile
*** jnotify-vanilla/Release/makefile	2011-02-28 18:07:20.000000000 +0000
--- jnotify-xcomp/Release/makefile	2012-09-14 00:37:56.475007855 +0100
***************
*** 28,34 ****
  libjnotify.so: $(OBJS) $(USER_OBJS)
  	@echo 'Building target: $@'
  	@echo 'Invoking: GCC C Linker'
! 	gcc -shared -o"libjnotify.so" $(OBJS) $(USER_OBJS) $(LIBS)
  	@echo 'Finished building target: $@'
  	@echo ' '
  
--- 28,34 ----
  libjnotify.so: $(OBJS) $(USER_OBJS)
  	@echo 'Building target: $@'
  	@echo 'Invoking: GCC C Linker'
! 	$(CC) -shared -o"libjnotify.so" $(OBJS) $(USER_OBJS) $(LIBS)
  	@echo 'Finished building target: $@'
  	@echo ' '
  
diff -crB jnotify-vanilla/Release/subdir.mk jnotify-i686/Release/subdir.mk
*** jnotify-vanilla/Release/subdir.mk	2011-02-28 18:07:20.000000000 +0000
--- jnotify-xcomp/Release/subdir.mk	2012-09-14 00:37:33.835007731 +0100
***************
*** 17,23 ****
  %.o: ../%.c
  	@echo 'Building file: $<'
  	@echo 'Invoking: GCC C Compiler'
! 	gcc -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux -O3 -Wall -Werror -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"$@" "$<"
  	@echo 'Finished building: $<'
  	@echo ' '
  
--- 17,23 ----
  %.o: ../%.c
  	@echo 'Building file: $<'
  	@echo 'Invoking: GCC C Compiler'
! 	$(CC) -I$(TOOLCHAIN)/include -O3 -Wall -Werror -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"$@" "$<"
  	@echo 'Finished building: $<'
  	@echo ' '
 
 

Package instructions

The package can be installed on an unmodified NAS – no hacking or bootstrapping is required. Only Marvell Kirkwood, Intel, and Freescale QorIQ PowerPC CPUs are supported, so please check which CPU your NAS has.

  • Firstly, in the DSM User control panel enable the User Home service.
  • Open the DSM Package Center. In Settings -> Package Sources add my package repository URL which is http://packages.pcloadletter.co.uk
  • Install either one of my Java SE for Embedded Synology packages (Java 6 or Java 7). Only Marvell Kirkwood, Freescale QorIQ, or Intel CPUs are supported. Please read all the notes and instructions on that page carefully.
  • Install Bliss using DSM’s Package Center.
  • The rest is self-explanatory.
 

Package scripts

For information, here are the package scripts so you can see what it’s going to do. You can get more information about how packages work by reading the Synology Package wiki.

installer.sh

#!/bin/sh

#--------BLISS installer script
#--------package maintained at pcloadletter.co.uk

DOWNLOAD_FILE="latest-linux-version"
DOWNLOAD_PATH="http://www.blisshq.com/app"
DOWNLOAD_URL="${DOWNLOAD_PATH}/${DOWNLOAD_FILE}"
DAEMON_USER="`echo ${SYNOPKG_PKGNAME} | awk {'print tolower($_)'}`"
DAEMON_PASS="`openssl rand 12 -base64 2>nul`"
DAEMON_ID="${SYNOPKG_PKGNAME} daemon user"
ENGINE_SCRIPT="bliss.sh"
MIGRATION_FOLDER="${DAEMON_USER}_data_mig"
SYNO_CPU_ARCH="`uname -m`"
NATIVE_BINS_URL="http://packages.pcloadletter.co.uk/downloads/bliss-native-${SYNO_CPU_ARCH}.tgz"   
NATIVE_BINS_FILE="`echo ${NATIVE_BINS_URL} | sed -r "s%^.*/(.*)%\1%"`"
INSTALL_FILES="${DOWNLOAD_URL}"
source /etc/profile

TEMP_FOLDER="`find / -maxdepth 2 -name '@tmp' | head -n 1`"
APP_TEMP="${TEMP_FOLDER}/${SYNOPKG_PKGNAME}"
DAEMON_HOME="`cat /etc/passwd | grep "${DAEMON_ID}" | cut -f6 -d':'`"
PRIMARY_VOLUME="/`echo $TEMP_FOLDER | cut -f2 -d'/'`"


preinst ()
{
  if [ -z ${JAVA_HOME} ]; then
    echo "Java is not installed or not properly configured. JAVA_HOME is not defined. "
    echo "Download and install the Java Synology package from http://wp.me/pVshC-z5"
    exit 1
  fi
  
  if [ ! -f ${JAVA_HOME}/bin/java ]; then
    echo "Java is not installed or not properly configured. The Java binary could not be located. "
    echo "Download and install the Java Synology package from http://wp.me/pVshC-z5"
    exit 1
  fi
  
  #is the User Home service enabled?
  UH_SERVICE=maybe
  synouser --add userhometest Testing123 "User Home test user" 0 "" ""
  UHT_HOMEDIR=`cat /etc/passwd | sed -r '/User Home test user/!d;s/^.*:User Home test user:(.*):.*$/\1/'`
  if echo $UHT_HOMEDIR | grep '/var/services/homes/' > /dev/null; then
    if [ ! -d $UHT_HOMEDIR ]; then
      UH_SERVICE=false
    fi
  fi
  synouser --del userhometest
  #remove home directory (needed since DSM 4.1)
  [ -e /var/services/homes/userhometest ] && rm -r /var/services/homes/userhometest
  if [ ${UH_SERVICE} == "false" ]; then
    echo "The User Home service is not enabled. Please enable this feature in the User control panel in DSM."
    exit 1
  fi
  #fetch the URL for the latest installer version from blisshq.com  
  _download
  DOWNLOAD_URL=`cat ${TEMP_FOLDER}/${DOWNLOAD_FILE}`
  INSTALL_FILES="${DOWNLOAD_URL} ${NATIVE_BINS_URL}"
  #now fetch the installer
  _download
  exit 0
}

_download ()
{
  cd ${TEMP_FOLDER}
  for WGET_URL in ${INSTALL_FILES}
  do
    WGET_FILENAME="`echo ${WGET_URL} | sed -r "s%^.*/(.*)%\1%"`"
    [ -f ${TEMP_FOLDER}/${WGET_FILENAME} ] && rm ${TEMP_FOLDER}/${WGET_FILENAME}
    wget ${WGET_URL}
    if [[ $? != 0 ]]; then
      if [ -d ${PUBLIC_FOLDER} ] && [ -f ${PUBLIC_FOLDER}/${DOWNLOAD_FILE} ]; then
        cp ${PUBLIC_FOLDER}/${DOWNLOAD_FILE} ${TEMP_FOLDER}
      else     
        echo "There was a problem downloading ${WGET_FILENAME} from the official download link, "
        echo "which was \"${WGET_URL}\" "
        echo "Alternatively, you may download this file manually and place it in the 'public' shared folder. "
        exit 1
      fi
    fi
  done
}


postinst ()
{
  #create daemon user
  synouser --add ${DAEMON_USER} ${DAEMON_PASS} "${DAEMON_ID}" 0 "" ""
  
  #determine the daemon user homedir and save that variable in the user's profile
  #this is needed because new users seem to inherit a HOME value of /root which they have no permissions for
  DAEMON_HOME="`cat /etc/passwd | grep "${DAEMON_ID}" | cut -f6 -d':'`"
  su - ${DAEMON_USER} -s /bin/sh -c "echo export HOME=\'${DAEMON_HOME}\' >> .profile"

  #set working folder (/tmp is very small on Synology)
  #and reduce Java prefs system polling frequency to allow hibernation
  su - ${DAEMON_USER} -s /bin/sh -c "echo export VMARGS=\'-Djava.io.tmpdir=${APP_TEMP} -Djava.util.prefs.syncInterval=86400\' >> .profile"
  
  #once again fetch the URL for the latest installer version from blisshq.com
  #and use it to determine the filename we downloaded during preinst
  _download
  DOWNLOAD_FILE=`cat ${TEMP_FOLDER}/${DOWNLOAD_FILE} | sed -r "s%^.*/(.*)%\1%"`

  #run the installer
  cd ${TEMP_FOLDER}
  echo INSTALL_PATH=${SYNOPKG_PKGDEST} > ${TEMP_FOLDER}/bliss-synology.properties
  java -jar ${TEMP_FOLDER}/${DOWNLOAD_FILE} -options ${TEMP_FOLDER}/bliss-synology.properties
  rm ${TEMP_FOLDER}/bliss-synology.properties
  rm ${TEMP_FOLDER}/${DOWNLOAD_FILE}
  sed -i "s%^#!/bin/bash%#!/bin/sh%" ${SYNOPKG_PKGDEST}/bin/${ENGINE_SCRIPT}
  
  #stow jar files containing Synology versions of native code
  #(libjnotify, and fpcalc from the Chromaprint audio fingerprinting library)
  mkdir ${SYNOPKG_PKGDEST}/syno-native
  cd ${SYNOPKG_PKGDEST}/syno-native
  tar xzf ${TEMP_FOLDER}/${NATIVE_BINS_FILE}

  #change owner of folder tree and temp files
  chown -R ${DAEMON_USER} ${SYNOPKG_PKGDEST}
  chown -R ${DAEMON_USER} ${DAEMON_HOME} 
  [ -d ${APP_TEMP} ] && chown -R ${DAEMON_USER} ${APP_TEMP}

  exit 0
}


preuninst ()
{
  #make sure daemon is stopped
  /var/packages/${SYNOPKG_PKGNAME}/scripts/start-stop-status stop
  sleep 6
  
  exit 0
}


postuninst ()
{
  #remove daemon user
  synouser --del ${DAEMON_USER}
  
  #clean up temp
  [ -d ${TEMP_FOLDER}/Bliss ] && rm -r ${TEMP_FOLDER}/Bliss

  exit 0
}


preupgrade ()
{
  #make sure daemon is stopped
  /var/packages/${SYNOPKG_PKGNAME}/scripts/start-stop-status stop
  sleep 6
  
  #if config data exists back it up
  if [ -d ${DAEMON_HOME}/.bliss ]; then
    mkdir ${SYNOPKG_PKGDEST}/../${DAEMON_USER}_data_migration
    mv ${DAEMON_HOME}/.bliss ${SYNOPKG_PKGDEST}/../${DAEMON_USER}_data_migration
    mv ${DAEMON_HOME}/.java ${SYNOPKG_PKGDEST}/../${DAEMON_USER}_data_migration
  fi

  exit 0
}


postupgrade ()
{
  #use the migrated config data from the previous version
  if [ -d ${SYNOPKG_PKGDEST}/../${DAEMON_USER}_data_migration/.bliss ]; then
    mv ${SYNOPKG_PKGDEST}/../${DAEMON_USER}_data_migration/.bliss ${DAEMON_HOME}
    mv ${SYNOPKG_PKGDEST}/../${DAEMON_USER}_data_migration/.java ${DAEMON_HOME}
    rmdir ${SYNOPKG_PKGDEST}/../${DAEMON_USER}_data_migration
    
    #daemon user has been deleted and recreated so we need to reset ownership (new UID)
    chown -R ${DAEMON_USER} ${DAEMON_HOME}
  fi
  
  exit 0
}
 

start-stop-status.sh

#!/bin/sh

#--------BLISS start-stop-status script
#--------package maintained at pcloadletter.co.uk

DAEMON_USER="`echo ${SYNOPKG_PKGNAME} | awk {'print tolower($_)'}`"
DAEMON_ID="${SYNOPKG_PKGNAME} daemon user"
ENGINE_SCRIPT="bliss.sh"
PIDFILE="${SYNOPKG_PKGDEST}/${SYNOPKG_PKGNAME}.pid"
SYNO_CPU_ARCH="`uname -m`"

_findpid ()
{
  ps -w | grep "^ *[0-9]* ${DAEMON_USER} .*java.*-Djava\.io\.tmpdir=/volume1/@tmp/Bliss" | grep -v "grep" | awk '{ print $1 }'
}


case $1 in
  start)
    PID=`_findpid`
    if [ -n "$PID" ]; then
      echo daemon is already running with pid $PID
      exit 1;
    fi
    DAEMON_HOME="`cat /etc/passwd | grep "${DAEMON_ID}" | cut -f6 -d':'`"
    
    #set the current timezone for Java so that log timestamps are accurate
    #we need to use the modern timezone names so that Java can figure out DST
    SYNO_TZ=`cat /etc/synoinfo.conf | grep timezone | cut -f2 -d'"'`
    SYNO_TZ=`grep "^${SYNO_TZ}" /usr/share/zoneinfo/Timezone/tzname | sed -e "s/^.*= //"`
    grep "^export TZ" ${DAEMON_HOME}/.profile > /dev/null \
     && sed -i "s%^export TZ=.*$%export TZ='${SYNO_TZ}'%" ${DAEMON_HOME}/.profile \
     || echo export TZ=\'${SYNO_TZ}\' >> ${DAEMON_HOME}/.profile
    
    #update the package version number in case of an in-app update
    #find must write out to a file because vars cannot be defined in subshells
    #http://mywiki.wooledge.org/BashFAQ/024
    find ${SYNOPKG_PKGDEST}/felix-cache -name "*.info" > /tmp/bliss-v-check.txt
    while IFS="" read -r FILE_TO_PARSE; do
      if [ -e ${FILE_TO_PARSE} ]; then
        if grep "com.elsten.bliss.bundle" ${FILE_TO_PARSE} > /dev/null; then
          BLISS_BUNDLE_DIR="`dirname ${FILE_TO_PARSE}`"
        fi
      fi
    done < /tmp/bliss-v-check.txt
    #read current version
    BLISS_VERSION=`grep "version" /var/packages/${SYNOPKG_PKGNAME}/INFO | sed -r "s/^.*([0-9]{8}).*$/\1/"`
    if [ -d ${BLISS_BUNDLE_DIR} ]; then
      find ${BLISS_BUNDLE_DIR} -name *.jar > /tmp/bliss-v-check.txt
      while IFS="" read -r FILE_TO_PARSE; do
        if [ -e ${FILE_TO_PARSE} ]; then
          DETECTED_VERSION=`unzip -p ${FILE_TO_PARSE} META-INF/MANIFEST.MF | grep Bundle-Version | cut -f4 -d'.' |  cut -c1-8`
        fi
        if [ ${DETECTED_VERSION} -gt ${BLISS_VERSION} ]; then
          BLISS_VERSION=${DETECTED_VERSION}
        fi
      done < /tmp/bliss-v-check.txt
    fi
    rm /tmp/bliss-v-check.txt
    sed -r -i "s/^version=\"[0-9]{8}/version=\"${BLISS_VERSION}/" /var/packages/Bliss/INFO
    
    #update the CPU-specific repository customizations (in case of an in-app update)
    if [ ${SYNO_CPU_ARCH} == "armv5tel" ]; then
      sed -i "s/jnotify\.linux\.x86/jnotify\.linux\.ARM_le/g" ${SYNOPKG_PKGDEST}/bliss-bundle/repository.xml
      sed -i "s/policy\.tag\.auto\.linux\.x86/policy\.tag\.auto\.linux\.ARM_le/g" ${SYNOPKG_PKGDEST}/bliss-bundle/repository.xml
    fi
    if [ ${SYNO_CPU_ARCH} == "ppc" ]; then
      sed -i "s/jnotify\.linux\.x86/jnotify\.linux\.PowerPC/g" ${SYNOPKG_PKGDEST}/bliss-bundle/repository.xml
      sed -i "s/policy\.tag\.auto\.linux\.x86/policy\.tag\.auto\.linux\.PowerPC/g" ${SYNOPKG_PKGDEST}/bliss-bundle/repository.xml
    fi
    
    #overwrite native lib bundles with syno versions (in case of an in-app update)
    cp ${SYNOPKG_PKGDEST}/syno-native/* ${SYNOPKG_PKGDEST}/bliss-bundle

    #start daemon in background mode
    echo "Starting ${SYNOPKG_PKGNAME} ... "
    su - ${DAEMON_USER} -s /bin/sh -c "cd ${SYNOPKG_PKGDEST}/bin && ./${ENGINE_SCRIPT} > ${SYNOPKG_PKGDEST}/bliss.out 2>&1 &"
    sleep 2
    PID=`_findpid`
    if [ -n "$PID" ]; then
      echo $PID > $PIDFILE
      echo "OK"
    else
      echo "FAIL"
      exit 1
    fi

    #set up symlink for the DSM GUI
    if [ -d /usr/syno/synoman/webman/3rdparty ]; then
      ln -s ${SYNOPKG_PKGDEST}/DSM/${SYNOPKG_PKGNAME} /usr/syno/synoman/webman/3rdparty/${SYNOPKG_PKGNAME}
    fi
   
    exit 0
  ;;

  stop)
    echo -n "Stopping daemon ... "
    if [ -f $PIDFILE ] ; then
      kill `cat $PIDFILE`
      sleep 10
    fi
    PID=`_findpid`
    if [ -n "$PID" ]; then
      echo "Still running, killing PID=$PID ... "
      kill -9 $PID
    fi
    rm -f $PIDFILE
    echo "OK"
    
    #remove DSM icon symlink
    if [ -e /usr/syno/synoman/webman/3rdparty/${SYNOPKG_PKGNAME} ]; then
      rm /usr/syno/synoman/webman/3rdparty/${SYNOPKG_PKGNAME}
    fi

    exit 0
  ;;

  status)
    PID=`_findpid`
    if [ -n "$PID" ]; then
      exit 0
    else
      exit 1
    fi
  ;;

  log)
    echo "${SYNOPKG_PKGDEST}/bliss.out"
    exit 0
  ;;
esac
 

Changelog:

  • 20130213-0009 Updated to Bliss 20130213, and will correctly report version in Package Center after an in-app update
  • 20130131-0008 Updated to Bliss 20130131
  • 20121112-0007 Fixes for DSM 4.2
  • 20121112-006 Updated to Bliss 20121112
  • 20121019-005 Updated to Bliss 20121019
  • 20121002-004 Updated to Bliss 20121002
  • 20120830-003 Added support for Freescale QorIQ PowerPC CPUs used in some Synology x13 series products, PowerPC processors in previous Synology generations with older glibc versions are not supported
  • 20120830-002 Hopefully fixed Java prefs polling issue that prevented NAS hibernation
  • 20120830-001 initial public release