User:Rplante

From PlugWiki
Jump to: navigation, search

Contents

Ray Plante: Creating a Plug Media Server

The purpose of this page is to record the steps I have taken to turn a SheevaPlug out of the box into a home media server. Once this effort is complete, I expect to migrate the generally useful information into a application how-to.

The Media Server: Goals

My interest in the SheevaPlug is in deploying a low-power, always-on media server for my home. In particular, I want it to provide:

  • a central archive of all my family's digital music, photos, and video
  • streaming music to all computers and home stereo
  • a general web server and wiki for electronic documents available on my home network
  • a code repository for personal projects
  • back-up storage for other computers in the home

I plan to enable this via the following:

I'll note that I'm not interested at the moment so much in video streaming or DVR-like capabilities. Furthermore, I intend my applications to operate solely for my home network; outside access is not desired.

System Specs

This system started out with:

  • the SheevaPlug Development Kit (US edition) from Globalscale (shipped Aug. 2009)
  • 750 GB external USB2 drive (Western Digital My Book, Essential Edition)
  • original Ubuntu Linux 9.04 pre-installed on the plug

Initial Setup

Plugging in the Plug

It doesn't get much easier that this: I...

  1. used the shipped ethernet cable to connect the plug to a port in my home router
  2. removed the US wall socket adapter and plugged in the shipped power cord
  3. I plugged the power cord into my power strip.

[Add photos and notes about removing adapter and plugging in cord]

OS Setup

I started by following the instructions in the New Plugger How To (which is reasonably consistent with QuickStart). That is, I...

  • used ssh to log in as root
  • fixed the /etc/dhcp3/dhclient.conf and /etc/rc.local files
    • my /etc/rc.local file
    • note that the pre-installed Ubuntu has vi and nano as editors.
  • upgraded Ubuntu

I also changed the root password, but didn't actually do it until after I had formatted the external hard drive. (I wanted to have reboot a few times before changing the password.)

External Harddrive Setup

My drive came formatted with a FAT32 filesystem. I wanted to repartition it with two Linux (ext3) partitions:

ext-usr (6 GB)
to eventually be mounted as /usr; this will provide me with plenty of room for installing Ubuntu packages
ext-data (remainder)
to hold the music, data, backups, etc.

The hardware setup was trivial: I plugged connect the drive to power, and then I connected the drive to the plug with the USB cable shipped with the drive. This drive does not power up (as indicated by its front panel light) until it is connected to its host computer.

Before repartitioning the disk, I backup up the shipped contents of the disk. (I don't know why.) Still logged in as root, I first mounted it, and then tar-ed up the contents:

root@debian:~# mount -t vfat /dev/sda1 /media
root@debian:~# df
Filesystem           1K-blocks      Used Available Use% Mounted on
rootfs                  519168    171620    347548  34% /
tmpfs                   257816         0    257816   0% /lib/init/rw
varrun                  257816       236    257580   1% /var/run
varlock                 257816         0    257816   0% /var/lock
udev                    257816        12    257804   1% /dev
tmpfs                   257816         0    257816   0% /dev/shm
tmpfs                   257816         0    257816   0% /var/cache/apt
/dev/sda1            732393152    762112 731631040   1% /media
root@debian:~# cd /media
root@debian:/media# tar czf wd-dist-bkup.tar.gz orig_files_in_this_dir

Note that the only storage space I have to write the tar-file to is on the disk I'm backing up; thus, I listed the individual files (using * wildcards where I could) on the tar command-line so that tar doesn't attempt to package up the tar file I'm writing to. I then used `scp` to transfer the tar file to another computer (where I will forget about it). Afterward, I unmounted the drive:

 cd             # to return to my home directory off of the mounted drive
 umount /media 

I used the cfdisk command to do the partitioning:

 cfdisk /dev/sda

This brought up a curses-based interactive screen in my terminal window which listed the factory installed partition table. The cdisk man page explains this command including the key sequences for driving it; however its use is pretty intuitive. You can do everything with:

  • the left and right arrow keys navigate the actions listed at the bottom
  • the up and down arrow keys navigate the partitions

To partition, I ...

  1. Selected "Delete" to delete the sole pre-installed VFAT partition
  2. Selected "New" to add my first partition. I chose this to be a "primary" partition with a size of 6000 MB.
  3. Selected "New" to add my second partition. This was also a "primary" partition. For the size, I hit enter to accept the default which was the remainder of free space.
  4. Selected "Write" to write the partition table to the disk
  5. Selected "Quit" to end the partitioning process.

Note that the "Write" action is a destructive one: it writes over the previous table and makes the previous filesystem unaccessible. Note also that if you want more than four partitions, the last one must an "extended" type.

Next, I created ext3 filesystems on each partition:

root@debian:~# mkfs.ext3 /dev/sda1
mke2fs 1.41.4 (27-Jan-2009)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
366480 inodes, 1463915 blocks
73195 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=1501560832
45 block groups
32768 blocks per group, 32768 fragments per group
8144 inodes per group
Superblock backups stored on blocks: 
        32768, 98304, 163840, 229376, 294912, 819200, 884736
_
Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
_
This filesystem will be automatically checked every 25 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
_
root@debian:~# mkfs.ext3 /dev/sda2
...

After creating my filesystems, I added labels to the partitions with the e2label command (just to make things seem organized):

root@debian:~# e2label /dev/sda1 ext-usr
root@debian:~# e2label /dev/sda2 ext-data
root@debian:~# e2label /dev/sda2          # displays current label
ext-data
Note that I could have set the labels while creating the filesystem:
mkfs.ext3 -L ext-data /dev/sda2

I can now see the final partition table by running cfdisk /dev/sda and then quitting before any further actions can be taken. The table was displayed like this:

                         cfdisk (util-linux-ng 2.14.2)

                              Disk Drive: /dev/sda
                       Size: 750156374016 bytes, 750.1 GB
             Heads: 255   Sectors per Track: 63   Cylinders: 91201

    Name        Flags      Part Type  FS Type          [Label]        Size (MB)
 ------------------------------------------------------------------------------
    sda1                    Primary   Linux ext3       [ext-usr]        5996.23
    sda2                    Primary   Linux ext3       [ext-data]     744157.54



     [ Bootable ]  [  Delete  ]  [   Help   ]  [ Maximize ]  [  Print   ]
     [   Quit   ]  [   Type   ]  [  Units   ]  [  Write   ]

                 Toggle bootable flag of the current partition

Mounting Disk Filesystems

The next goal was to mount the newly created filesystems. In particular, I want to mount the first partition as /usr. However, before I could do that, I had to copy the current contents of the original /usr on the plug's local storage onto my external partition.

First, I temporarily mounted my ext-usr partition onto /media and then use tar to copy the contents of /usr:

root@debian:~# mount -t ext3 /dev/sda1 /media        # mount the partition on /media
root@debian:~# ls /media
lost+found
root@debian:~# cd /usr
root@debian:~# tar cf - * | (cd /media; tar xf -)    # copy the files
root@debian:~# ls /media
bin        games    lib      local       README  src
COPYRIGHT  include  libexec  lost+found  sbin    var
etc        javaws   LICENSE  plugin      share   Welcome.html
root@debian:~# umount /media                         # unmount the partition


Next, I edited my /etc/fstab so that external partitions can be mounted at boot time; I added these two lines:

/dev/sda1 /usr  ext3  defaults,rw 0 1
/dev/sda2 /data ext3  defaults,rw 0 1

This sets up the first 6 GB partition to be mounted as /usr and the second larger one on /data. For the second one to mount, I need to make sure its mount point exists:

 mkdir /data

Now to test my entries, I can mount the drives manually:

root@debian:~# mount -t ext3 /dev/sda2 /data
root@debian:~# mount -t ext3 /dev/sda1 /usr
root@debian:~# df
Filesystem           1K-blocks      Used Available Use% Mounted on
rootfs                  519168    171768    347400  34% /
tmpfs                   257816         0    257816   0% /lib/init/rw
varrun                  257816       232    257584   1% /var/run
varlock                 257816         0    257816   0% /var/lock
udev                    257816        16    257800   1% /dev
tmpfs                   257816         0    257816   0% /dev/shm
tmpfs                   257816         0    257816   0% /var/cache/apt
/dev/sda1              5763616    415240   5055596   8% /usr
/dev/sda2            715312580    201788 678774976   1% /data
root@debian:~# less /etc/fstab               # hit q to quit

Running /usr/bin/less confirms that /usr/bin is okay.

Note, of course, that the contents of /usr on the plug's local storage is now hidden by the newly mount partition from the external drive. I'm going to leave the original /usr data in place so that the plug is still usable when the external drive is not connected.

The ultimate test is to reboot the system and see that the drives get mounted automatically. Before rebooting, though, I confirmed via another terminal window that I can still log into the system. Then I rebooted:

 shutdown -r now

In a few minutes, I was able to log back into the plug as root, and the drives were properly mounted as shown in the above df output. I can now start installing Ubuntu packages with abandon! (Well, without worry.)

Adding users

Note: I'm about to reveal that I prefer tcsh as my shell. This is not installed by default, so I should do that first:
apt-get install tcsh

As with all systems, its best to reserve the use of root to only when I actually need it (you know, for those times I'm up late hacking when I should be sleeping and I accidentally...). So I'll need at least one user account.

Because I want parts of the /data drive to be exported to other computer as NFS volumes with read-write access, I'm adding accounts for my family, too. In particular, I'll want the user ID numbers for the plug users to match those on the other computers (as much as possible). If you don't care about NFS (or rather having remote write access), you don't have to bother trying to get those numbers.

Discovering User IDs: For Linux, this is trivial: on my workstation, I grep for my username in the `/etc/passwd` file:
rplante@mybox: grep rplante /etc/passwd
rplante:x:500:500:Raymond Plante,,,:/home/rplante:/usr/bin/tcsh

That first number, 500, is my User ID.

My wife has Mac, though; which in normal operations does not use the `/etc/passwd` file. (Macs are a bit of a mystery to me.) I can use python, however, to get that number:

mary# echo 'import os; print os.getuid()' | python -
505


Before I create accounts, I want to make sure users have plenty of room for stuff, so I'll be putting their home directories on the /data disk, rather on the plug's local storage under /. To do this I'm linking /home to /data/home:

  cd /
  rmdir /home
  mkdir /data/home
  ln -s /data/home home

Really, this was optional, because when adding a user account, one explicitly gives a home directory. This directory can be anywhere, including under /data/home; however, just as a matter of preference, I like my system to look like a "typical" Linux system to avoid confusion.

Now we are ready to add a user using adduser:

root@debian:~# adduser --home /home/rplante --shell /usr/bin/tcsh --uid 500  rplante
Adding user `rplante' ...
Adding new group `rplante' (500) ...
Adding new user `rplante' (500) with group `rplante' ...
Creating home directory `/home/rplante' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: my_password
Retype new UNIX password: my_password
passwd: password updated successfully
Changing the user information for rplante
Enter the new value, or press ENTER for the default
        Full Name []: Raymond Plante
        Room Number []: return
        Work Phone []: return
        Home Phone []: return
        Other []: 
Is the information correct? [Y/n] Y

You'll notice I explicitly set the user ID with the --uid option; if you don't care about what number is used, you can leave this option off. If prefer bash as your shell (or don't care), you can leave off the --shell option. Finally, you'll notice that the command prompted me for my full name and a bunch of irrelevant information. I typed my name, but just hit return for the rest.

Here's what this command did:

  • added an entry for rplante to /etc/passwd,
  • created a group called rplante (also with the group ID, 500) and assign user rplante to it, and
  • created the home directory (and put some initial files in it, which are not relevent to me because they're for bash users)

Want proof?

root@debian:~# grep rplante /etc/passwd
rplante:x:500:500:Raymond Plante,,,:/home/rplante:/usr/bin/tcsh
root@debian:~# grep rplante /etc/group
rplante:x:500:
root@debian:~# ls -a /home/rplante/
.  ..  .bash_logout  .bashrc  .profile

Changing the IP Address and Hostname

I want to be able to log into the plug from other computers by a name rather than its IP address (which can change, since it comes set-up for DHCP). The simplest way to enable this is to change the plug to take a static IP address and then change the hostname of my choosing.

I'm not sure why I left this little task to the last step of setting up the basic system. It was part paranoia, perhaps, as I had read of at least one instance of someone not being able to log back into their plug after changing the hostname. (Certainly there's a danger of this if I screwed up chaning the IP address.) Perhaps, it's because its the step I understand the least. How this is handled can vary across Linux distributions. The procedure I show here, I basically put together from helpful pages found from Googling.

First we'll change the IP address, which can be done by editing the `/etc/network/interface` file. This file configures each network interface on the system; with a plug, this is the loopback device (`lo`) and the ethernet device (`eth0`). It's the latter one that needs to be updated to set the IP address. In particular, the 2nd section looks like this out-of-the-box:

auto eth0
iface eth0 inet dhcp

Edit this file to change the address setting type from DHCP to a static address, and then set the actual address. Here's what this section of my /etc/network/interfaces looks like:

auto eth0
iface eth0 inet static
address 192.168.1.12       <-- the static address
netmask 255.255.255.0
gateway 192.168.1.1

Note that I had already configured my home router to start its DHCP addresses with `192.168.1.100`; this leaves 99 addresses available for static addresses.

With this change, I crossed my fingers and rebooted (`shutdown -r now`). In a few minutes, I was able to log in again using the new IP address.

To change the hostname involved editing `/etc/hostname` file. This file is ultimately the one that determines the hostname when Ubuntu reboots. It's only contents is the name of the host (without a domain part). Out-of-the-box, this file contains "debian"; change this to whatever name you wish.

On next reboot, you will find the name update. (Try running the `hostname` command.) If you want the change to take place immediately, simply run the init script that consumes this file at boot time:

/etc/init.d/hostname.sh
Note: Some tips on the net talk about doing a `sudo hostname newname` to change the hostname. This, however, will only be effective until reboot and not change the name permanently. The above mentioned `/etc/init.d/hostname.sh` essentially runs `hostname` at boot time based on the contents of `/etc/hostname`.


[editing in progress]

Installing Miscellaneous Packages

This is a good time to install packages one expects to need (using apt-get install ...). This might include a database, backup software, or the GNU compiler, gcc. In the sections below, I try to call out the specific packages that are needed by a particular application, but note that these lists could be incomplete. Typically, I figured out what was needed when installing the application initially failed. Since I installed these applications in a particular order (not necessarily the one order they are presented), it is possible that I missed noting a dependency for an application because I installed it already for another application. Thus, if you are following one of my recipes but it looks like a prerequisite package is still missing, consult the apt-get commands for the other packages to see if the missing package might be there.

Note also that because I have a large external drive attached--in particular because I put /usr on that drive--I don't worry about installing packages I may not need. If you do not have a similar setup, you will have to be careful about how you use the half gigabyte of internal storage available to your plug.


Now we have the basic system setup, and we're ready to start setting up the services listed in the goals above.

Setting up the Services

Sharing Filesytems With NFS

Audio Streaming: Icecast2

After copying my music collection to library to plug's external drive, I was ready to try streaming. Icecast2 was the first streaming software I tried running because it looked like the easiest to get going. It indeed turned out to be easy, and it was a thrill listening to the first playlist from my laptop. As we'll see though, it's not the ultimate solution for my needs.

Icecast is a flexible streaming server which focuses on sending a source stream of data out to remote listeners. To actually stream something, one needs to actually attach a so-called "client" that provides the source stream. There are several such clients available, each with specialties of live sources vs. pre-recorded and source format. My audio collection is exclusively in MP3 format, so I chose ezstream.

The software is all installable as Ubuntu packages:

 apt-get install icecast2 ezstream

Installing icecast2 will automatically install a user called icecast2 appropriate for running the icecast2 server.

The icecast web site has an array of links to documentation. I used the very nice manual, Icecast Installation and Management, A Guide to Open Source Audio Streaming by Kerry Cox. If you use this resource, note the following:

  • The section "Icecast2 XML Configuration" section under "Examining the XML Files" covers configuration.
  • Ubuntu installs the Icecast2 server configuration into /etc/icecast2/icecast.xml
    • At the end of the configuration file, in the <security> section, I set the user and group to icecast2 and icecast, respectively.
  • To start the server as normal system service, I also had to edit /etc/default/icecast: I changed the last line to:
 ENABLE=true
  • I started the service by typing (as root):
 /etc/init.d/icecast2 start

Once the server was running, I was able to access the web administration portal at http://myserver:8000. Everything looked fine.

The next step was to configure and run the icecast client, ezstream. The section "Ezstream" under "Alternative Streamers" in the manual covers this. This involved making a copy of one of the configuration files under /usr/share/doc/ezstream/examples and editing it. I did this using my non-root login in my home directory. Note the following:

  • the "url" is, of course, the URL that the stream will be accessible from.
  • the "sourcepassword" must match the <source-password> set in the icecast server configuration.
  • the "filename" is the path to either an MP3 file or an m3u playlist to play.

I already had m3u playlists for all of my albums so I was able to use an existing one. If you are not familiar with them already, an m3u playlist is simply a file containing a list of file paths to MP3 files, one per line, in the order that they should be played. Note that if the paths are relative, they will be taken by the ezstream client to be relative to the directory where ezstream is run.

Starting the stream is as simple as:

 ezstream -c ~/ezstream.xml

where ~/ezstream.xml was the file I had just edited. As it starts to stream the playlist, Ezstream will print a messaages indicating the MP3 selection being played.

Any application capable of listening to internet radio should be able to listen to it. I used the Totem Movie Player, connecting by selecting "File - Open Location..." and entering the URL I had configured in the ezstream.xml file. In seconds, a familiar album was playing on my laptop.

While this was thrilling demonstration, this is obviously not the most convenient way to remotely listen to my collection. However, one can imagine a pretty simple CGI web server script that...

  • allows me to browse and select titles from my MP3 colleciton
  • writes an m3u playlist containing my selections on the fly (if necessary)
  • writes an ezstream config file out on the fly
  • starts the ezstream client to play the playlist

Audio Streaming: SqueezeCenter

SqueezeCenter is the streaming server used by the Squeezebox line of products. It looks like it should have a nice web interface, but I was not able to get the server working. Nevertheless, this section records what I did and where I gave up.

[editing in progress]

One can install the SqueezeCenter software via a Ubuntu Debian distribution file, available from the SqueezeCenter download page, but it requires a number of prerequisite packages:

 apt-get install mysql-server-5.0 libmysqlclient15-dev mysql-client-5.0 zlib1g-dev \
                 libdbi-perl libdbd-mysql-perl libterm-readkey-perl                \
                 mysql-server-core-5.0 libhtml-template-perl libplrpc-perl         \
                 linux-libc-dev libnet-daemon-perl libexpat1 libexpat1-dev         \
                 libgd2-xpm libgd2-xpm-dev gcc g++

Included in this list is the gcc compiler, which is needed to build some Perl modules a little later.

With these packages installed, I was able to install the Debian file:

 dpkg --install squeezecenter_7.3.3_all.deb


It was after trying to run the squeezecenter server that I discovered I needed some additional Perl modules. When I started the server via the init.d script:

 /etc/init.d/sqeezecenter start

It apparently started fine, but the web page at http://localhost:9000/ would not respond. Looking at the log, /var/log/squeezecenter/server.log, reveals that the script was failing and continually trying to restart itself. ps revealed what was executed:

 /usr/sbin/squeezecenter_safe /usr/sbin/squeezecenter-server --prefsdir /var/lib/squeezecenter/prefs \
 --logdir /var/log/squeezecenter/ --cachedir /var/lib/squeezecenter/cache --charset=utf8

I ran the server manually without the wrapper script, /usr/sbin/squeezecenter_safe, and saw the error messages indicating missing Perl modules. The message recommended a script to install these, which I followed:

 /usr/share/squeezecenter/Bin/build-perl-modules.pl DBD::mysql DBI XML::Parser::Expat \
              HTML::Parser JSON::XS Compress::Zlib Digest::SHA1 YAML::Syck GD 

(Normally, I would prefer installing these via apt-get, but it wasn't obvious which Ubuntu packages contained the Perl modules I needed.) Now this did not actually install these products successfully. I don't know where some of them got installed, but it wasn't in a place where squeezecenter could find them. It did at least do me the favor of downloading the perl packages for me. The above mentioned build-perl-modules.pl script asked me where you want to download them to, which it does and an unpacks them. Afterward, I changed into each of the module distribution directories and ran the usual build and installation procedures:

 perl Makefile.PL
 make 
 make install

In addition to the modules mentioned above, I also had to download and install Encode::Detect (version 1.00!--v1.01 did not work). (I also installed Data::Dump to run the Encode::Detect tests via make test.)

After all this, I manually started the squeezecenter server and ran into my last problem. After some delay, the server failed with this error message:

 [09-09-11 10:12:27.0589] main::init (270) Starting SqueezeCenter (v7.3.3, r27044, Mon Jun 15   
 15:12:32 PDT 2009)
 [09-09-11 10:12:27.1021] Slim::Schema::throw_exception (288) Error: Can't find source for MetaInformation
 [09-09-11 10:12:27.1076] Slim::Schema::throw_exception (288) Backtrace: 
    frame 0: Slim::Utils::Log::logBacktrace (/usr/share/perl5/Slim/Schema.pm line 288)
    frame 1: Slim::Schema::throw_exception (/usr/share/squeezecenter/CPAN/DBIx/Class/Schema.pm line 150)
    frame 2: DBIx::Class::Schema::source (/usr/share/squeezecenter/CPAN/DBIx/Class/Schema.pm line 190)
    frame 3: DBIx::Class::Schema::resultset (/usr/share/perl5/Slim/Schema.pm line 602)
    frame 4: Slim::Schema::single (/usr/share/perl5/Slim/Music/Import.pm line 660)
    frame 5: Slim::Music::Import::stillScanning (/usr/sbin/squeezecenter-server line 997)
    frame 6: main::cleanup (/usr/share/perl5/Slim/bootstrap.pm line 389)
    frame 7: Slim::bootstrap::sigint (/usr/share/perl5/Slim/bootstrap.pm line 423)
    frame 8: Slim::bootstrap::theEND (/usr/sbin/squeezecenter-server line 1053)
    frame 9: main::END (/usr/sbin/squeezecenter-server line 828)
    frame 10: (eval) (/usr/sbin/squeezecenter-server line 828)

Googling the highlighted message shows that others have encountered this, but I didn't actually see a solution.

Audio Streaming: Firefly

Firefly is another open-source media streaming server with a web interface. The Roku Soundbridge products support this server.

This application can be installed readily via an Ubuntu package:

 apt-get install mt-daapd

Note: this version is not working; am trying to build from source; editing in progress.

Building from SVN

Tricky.

References:

Prerequisites:

buildtools:

 apt-get install gcc automake autoconf libtool

libraries:

 apt-get install sqlite3 libsqlite3-dev libogg0 libogg-dev libvorbis0a libvorbis-dev \
                 libavutil49 libavutil-dev libavcodec52 libavcodec-dev libavformat552 \
                 libavformat-dev gettext

Getting source code:

Check out from the [http://sourceforge.net/projects/mt-daapd/develop mt-daapd sourceforge code repository:

 svn co https://mt-daapd.svn.sourceforge.net/svnroot/mt-daapd/trunk mt-daapd-trunk

Fixes:

Audio Streaming: Music Player Daemon (MPD)

This server connects sound sources--namely an audio collection--to various players. My interest is in sending the music over the home network.