Blogs

Building a home media server with ZFS and a gaming virtual machine

Work has kept me busy lately so it's been a while since my last post... I have been doing lots of research and collecting lots of information over the holiday break and I'm happy to say that in the coming days I will be posting a new server setup guide, this time for a server that is capable of running redundant storage (ZFS RAIDZ2), sharing home media (Plex Media Server, SMB, AFP) as well as a full Windows 7 gaming rig simultaneously!

Windows runs in a virtual machine and is assigned it's own real graphics card from the host's hardware using the using the brand-new VFIO PCI passthrough technique with the VGA quirks enabled. This does require a motherboard and CPU with support for IOMMU, more commonly known as VT-d or AMD-Vi.

Rating: 

Setting the OS X kernel asynchronous I/O limits to avoid VirtualBox crashing OS X

I had the need to setup a new VM for software testing today, and I kept running into intermittent problems where VirtualBox would freeze and then an OS X kernel panic, freezing/crashing the entire machine.

Luckily, I had made a snapshot in the OS moments earlier to the crash so I had a safe place to revert to, but the crashes kept happing at seemingly random times.

I setup a looped execution of 'dmesg' to see what was going on just before the crash and saw this at the next freeze:

VBoxDrv: host_vmxon  -> vmx_use_count=1 rc=0
VBoxDrv: host_vmxoff -> vmx_use_count=0
VBoxDrv: host_vmxon  -> vmx_use_count=1 rc=0
aio_queue_async_request(): too many in flight for proc: 16.
aio_queue_async_request(): too many in flight for proc: 16.
aio_queue_async_request(): too many in flight for proc: 16.
aio_queue_async_request(): too many in flight for proc: 16.
aio_queue_async_request(): too many in flight for proc: 16.
aio_queue_async_request(): too many in flight for proc: 16.

The first VBoxDrv messages didn't pull anything interesting in Google, but the other messages did: Virtual Box ticket #11219 and this blog post.

It would appear that the default limits for the OS X kernel's asynchronous I/O are very, very low. VirtualBox likely exceeds them when your VM(s) are performing heavy disk I/O, hence the 'too many in flight' message in the logs.

Luckily for us, there's a quick and easy solution:

sudo sysctl -w  kern.aiomax=512 kern.aioprocmax=128 kern.aiothreads=16

then restart VirtualBox. These settings will apply until you reboot. To make the changes permanent, add/update the following lines in /etc/sysctl.conf:
kern.aiomax=512
kern.aioprocmax=128
kern.aiothreads=16

Note: you can probably set those limits even higher, as documentation for Sybase (by SAP) recommends values 2048 / 1024 / 16 when using its software.

Rating: 

Compiling the IMAP extension for PHP 5.3.15 with OS X 10.8.3

I have been testing the Drupal support module locally which features the ability to create tickets from email messages to an IMAP inbox. It requires the imap_open() PHP function provided by the imap PHP extension, which unfortunately is not included in the OS X builds of PHP.

ivucica has published a wonderful script to his blog that compiles the IMAP extension without having to recompile PHP entirely, but unfortunately it was not working for me and nobody else seemed to have my problem either. Compiling the imap library and PCRE went very smoothly, but when it came time to build the PHP extension this error appeared during ./configure:

checking whether build with IMAP works... no
configure: error: build test failed. Please check the config.log for details.

Well, crap. I check config.log and determine it's a linking failure:

configure: program exited with status 1
configure: failed program was:
| /* confdefs.h */
| #define PACKAGE_NAME ""
| #define PACKAGE_TARNAME ""
| #define PACKAGE_VERSION ""
| #define PACKAGE_STRING ""
| #define PACKAGE_BUGREPORT ""
| #define PACKAGE_URL ""
| #define COMPILE_DL_IMAP 1
| #define HAVE_IMAP 1
| #define HAVE_IMAP2000 1
| #define HAVE_IMAP2004 1
| #define HAVE_NEW_MIME2TEXT 1
| #define HAVE_LIBPAM 1
| #define HAVE_IMAP_KRB 1
| #define HAVE_IMAP_SSL 1
| /* end confdefs.h.  */
|
|
| #if defined(__GNUC__) && __GNUC__ >= 4
| # define PHP_IMAP_EXPORT __attribute__ ((visibility("default")))
| #else
| # define PHP_IMAP_EXPORT
| #endif
|
|       PHP_IMAP_EXPORT void mm_log(void){}
|       PHP_IMAP_EXPORT void mm_dlog(void){}
|       PHP_IMAP_EXPORT void mm_flags(void){}
|       PHP_IMAP_EXPORT void mm_fatal(void){}
|       PHP_IMAP_EXPORT void mm_critical(void){}
|       PHP_IMAP_EXPORT void mm_nocritical(void){}
|       PHP_IMAP_EXPORT void mm_notify(void){}
|       PHP_IMAP_EXPORT void mm_login(void){}
|       PHP_IMAP_EXPORT void mm_diskerror(void){}
|       PHP_IMAP_EXPORT void mm_status(void){}
|       PHP_IMAP_EXPORT void mm_lsub(void){}
|       PHP_IMAP_EXPORT void mm_list(void){}
|       PHP_IMAP_EXPORT void mm_exists(void){}
|       PHP_IMAP_EXPORT void mm_searched(void){}
|       PHP_IMAP_EXPORT void mm_expunged(void){}
|       void rfc822_output_address_list(void);
|       void (*f)(void);
|       char foobar () {f = rfc822_output_address_list;}
|
|     char foobar();
|     int main() {
|       foobar();
|       return 0;
|     }
|
configure:6808: result: no
configure:6819: checking whether build with IMAP works
configure:6863: cc -o conftest -g -O2   conftest.c  -Wl,-rpath,/usr/local/imap-2007f/lib -L/usr/local/imap-2007f/lib -lc-client -lpam  -lkrb5  >&5
Undefined symbols for architecture x86_64:
  "_BIO_free", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_BIO_new_mem_buf", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_BIO_new_socket", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_ERR_error_string", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_genkey in libc-client.a(osdep.o)
  "_ERR_get_error", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_genkey in libc-client.a(osdep.o)
  "_ERR_load_crypto_strings", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_EVP_PKEY_free", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_PEM_read_bio_PrivateKey", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_PEM_read_bio_X509", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_RAND_seed", referenced from:
      _ssl_onceonlyinit in libc-client.a(osdep.o)
  "_RSA_generate_key", referenced from:
      _ssl_genkey in libc-client.a(osdep.o)
  "_SSL_CTX_ctrl", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_free", referenced from:
      _ssl_abort in libc-client.a(osdep.o)
  "_SSL_CTX_load_verify_locations", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_new", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_set_cipher_list", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_CTX_set_default_verify_paths", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_set_tmp_rsa_callback", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_CTX_set_verify", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_use_PrivateKey", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_use_RSAPrivateKey_file", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_CTX_use_certificate", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_CTX_use_certificate_chain_file", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_accept", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_ctrl", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_free", referenced from:
      _ssl_abort in libc-client.a(osdep.o)
  "_SSL_get_error", referenced from:
      _ssl_getdata in libc-client.a(osdep.o)
      _ssl_sout in libc-client.a(osdep.o)
  "_SSL_get_fd", referenced from:
      _ssl_getdata in libc-client.a(osdep.o)
      _ssl_server_input_wait in libc-client.a(osdep.o)
  "_SSL_get_peer_certificate", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_library_init", referenced from:
      _ssl_onceonlyinit in libc-client.a(osdep.o)
  "_SSL_load_error_strings", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_new", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_pending", referenced from:
      _ssl_getdata in libc-client.a(osdep.o)
      _ssl_server_input_wait in libc-client.a(osdep.o)
  "_SSL_read", referenced from:
      _ssl_getdata in libc-client.a(osdep.o)
      _ssl_server_input_wait in libc-client.a(osdep.o)
  "_SSL_set_bio", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_set_connect_state", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_set_fd", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_SSL_shutdown", referenced from:
      _ssl_abort in libc-client.a(osdep.o)
  "_SSL_state", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSL_write", referenced from:
      _ssl_start in libc-client.a(osdep.o)
      _ssl_sout in libc-client.a(osdep.o)
  "_SSLv23_client_method", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_SSLv23_server_method", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_TLSv1_client_method", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_TLSv1_server_method", referenced from:
      _ssl_server_init in libc-client.a(osdep.o)
  "_X509_NAME_oneline", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_X509_STORE_CTX_get_current_cert", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_X509_STORE_CTX_get_error", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_X509_free", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_X509_get_ext_d2i", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_X509_get_subject_name", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_X509_verify_cert_error_string", referenced from:
      _ssl_open_verify in libc-client.a(osdep.o)
  "_sk_num", referenced from:
      _ssl_start in libc-client.a(osdep.o)
  "_sk_value", referenced from:
      _ssl_start in libc-client.a(osdep.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I couldn't figure out why it wasn't picking up the symbols from libssl, even when manually trying to compile said file and adding a -lssl flag.

After an hour of struggling with it and my debugging efforts going nowhere, I try adding -lcrypto for the hell of it and it works!

tl;dr, if you get this error then simply replace the following line of the aformentioned script:

./configure --with-imap=/usr/local/imap-2007f --with-kerberos --with-imap-ssl

With the following line that adds the required linker flags:
LDFLAGS="-lssl -lcrypto" ./configure --with-imap=/usr/local/imap-2007f --with-kerberos --with-imap-ssl

That's it!

Rating: 

Attaching MBR or GPT disk partitions to a VirtualBox VM

On one of my development machines I triple-boot so that I can test in a variety of operating environments. My default is OS X and the other day I wanted to write documentation using DocBook. I knew that my Linux installation already had publican installed and ready to go (publican is a wonderful documentation tool written by Red Hat to facilitate the publishing of DocBook-based documentation) and I really did not want to setup a new Fedora VM that duplicated a setup I already had.

In reality, a new VM would not be all that much effort, but I though it would be really handy to know how to boot a VM up from a physical partition and this way I could kill two birds with one stone. After some research, it seemed using VMWare's VMDK (Virtual Machine Disk Format) images was the most appropriate, as the metadata could be tricked into pointing to physical partitions instead of files. Unfortunately, the tools VirtualBox provide to create them are not compatible with GPT disk tables out of the box. This blog post does mention a trick to convert the VMDK metadata to match the GPT disk layout, but it failed consistently when I tried.

I then, by fluke, found Dave Vasilevsky's vmdk-raw-parts script on GitHub which worked flawlessly! Its usage is well documented so I will not bore you with the details, but with that said after using it to create my custom VMDK, the only change required was to run chmod 666 /dev/diskXsY to grant my user write permissions to the disk (prevents you from having to start VirtualBox as root).

Some Extra Troubleshooting

These details are completely irrelevant to the above, but I wanted to mention them here for others wanting to run the same setup:

  • If you need USB pass-through, remember to set your VCPUs to 1; set it to anything higher and USB stops functioning.
  • You'll need to redo the 'chmod' when ever you reboot your machine, as the permissions on the dev node get reset upon boot.
  • During the trial-and-error process of getting this to work, I often needed to recreate my VMDK and reload it. VirtualBox will moan about mismatched UUIDs... The simple fix is to open File > Virtual Media Manager... to delete then re-add your VMDK file.
Rating: 

Syncing your iOS device (either iPod, iPhone or iPad) with multiple iTunes libraries

With Apple's changes surrounding iCloud, managing media on iOS devices has become much easier and it is no longer absolutely bound to a single computer. However, I have still found it tricky to sync a single iOS device with multiple computers (different iTunes libraries) without having to entirely wipe the device first.

I synchronize my iTunes library between two computers regularly, so both machines have up-to-date copy of all media files. Furthermore, I set them up with the same username (but different hostname), so the iTunes media file paths are identical between machines.

Searching Apple forums revealed this solution by turingtest2: apparently, each iTunes library is assigned a randomly-generated "library ID". This identifier is stored on the iOS device, so if it doens't match when connecting to a new computer, it will request to wipe the device before proceeding with the sync. Simply copying my entire ~/Music folder from one machine to the other was enough copy all the iTunes metadata (including the library ID) and my device now happily synchronizes on either machine. For those wanting to save time and not copy their entire ~/Music folder, see the forum post for the exact files you'll need to copy.

Rating: