HOWTO Install MongoDB for PHP on Mac OS X

15 Aug, 2011 — Apple, HOWTO

MongoDB is a document-oriented database (among other things), and it’s especially convenient that the native document format is JSON. For various ongoing pet projects, I figured I’d give it a try to avoid the overhead of creating/maintaining schemas and having to flatten down my JSON objects to fit a relational model.

These instructions work for 10.7 Lion, 10.6 Snow Leopard and 10.5 Leopard. The steps outlined here ensure that your MongoDB installation integrates well with the rest of the system (Apache, PHP & launchd), is started automatically when the machine starts up, and makes it easy to upgrade later.

I read a lot of conflicting instructions on the Web about how to install MongoDB, and they’re either incomplete (most of them skip the part about making MongoDB run automatically at startup), and none of them satisfied the following requirements I had:

  • I should not need to install any software that’s already present on a stock Mac OS X installation (Lion, Snow Leopard or Leopard, at the very least.)
  • It should be relatively easy to upgrade the base version of MongoDB, so installing it via a package manager would be preferable to installing it from source.
  • The MongoDB server should be started up automatically through system-standard tools such as launchd — not manually every time I need to test something.

This tutorial assumes a virgin Mac OS X installation. If you already have a component installed, simply skip that step.

  1. Install XCode from the Mac App Store. It’s needed for MacPorts.

  2. Install MacPorts from macports.org. MacPorts is a package manager for Mac OS X that makes it easy to install and configure a lot of open-source software.

    Although MongoDB binaries are available from the MongoDB web site, I strongly recommend using the MacPorts MongoDB port. MacPorts ensures dependencies are installed correctly, and provides an easy way to upgrade all outdated packages at once.

Install and configure MongoDB


  1. Install MongoDB. Open a Terminal, ensure you’re using an account with Administrator privileges, and type:

          sudo port install mongodb

    That’s it, MongoDB is now installed. But there’s lots more to do to get it working.

  2. Create directories required by MongoDB.

          # The data directory.
          sudo mkdir -p /var/lib/mongodb/
    
          # The logs directory.
          sudo mkdir -p /var/log/mongodb/
  3. Create a config file. Put this in a new file at /etc/mongodb.conf:
          # This is an example config file for MongoDB.
          # Place it at /etc/mongodb.conf
          # Based on a sample provided at
          # http://www.mongodb.org/display/DOCS/File+Based+Configuration
          dbpath = /var/lib/mongodb
          bind_ip = 127.0.0.1
          noauth = true
  4. Configure a launchd LaunchDaemon. Put this in a new file at /Library/LaunchDaemons/org.mongo.mongod.plist.

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
            "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
          <plist version="1.0">
          <dict>
            <key>Label</key>
            <string>org.mongodb.mongod</string>
            <key>ProgramArguments</key>
            <array>
              <string>/opt/local/bin/mongod</string>
              <string>run</string>
              <string>--config</string>
              <string>/etc/mongodb.conf</string>
            </array>
            <key>RunAtLoad</key>
            <true/>
            <key>KeepAlive</key>
            <true/>
            <key>WorkingDirectory</key>
            <string>/var/log/mongodb/</string>
            <key>StandardErrorPath</key>
            <string>/var/log/mongodb/output.log</string>
            <key>StandardOutPath</key>
            <string>/var/log/mongodb/output.log</string>
          </dict>
          </plist>
  5. Start MongoDB via launchd. Type in a Terminal window:

          sudo launchctl load /Library/LaunchDaemons/org.mongo.mongod.plist

You can stop reading now if all you need is MongoDB and don’t plan to use PHP with it. The MongoDB server will be automatically launched at startup.

Enable Apache and PHP5

  1. Start Apache. In System Preferences, go to Sharing, and then check the box next to “Web Sharing”.


  2. Enable PHP5 support in Apache. Open the Apache config file, /etc/apache2/httpd.conf, and locate the following line.

          # LoadModule php5_module

    Uncomment that line, so it reads:

          LoadModule php5_module
        
  3. Restart Apache. Type in a Terminal window:

          sudo apachectl graceful
        
  4. Test if PHP is working correctly. Create a new PHP file at /Library/WebServer/Documents/phpinfo.php and put in it:

          <?php
            phpinfo();
          ?>

    Browse to http://localhost/phpinfo.php, and you should see a long page showing your PHP configuration.

    Apache and PHP5 are now configured.

Install and Configure the PHP5 MongoDB driver.

  1. Install PEAR. PEAR is the PHP Extension and Application Repository, from which we’ll install the PHP5 MongoDB driver.

          $ cd /tmp
          $ wget http://pear.php.net/go-pear.phar
          $ sudo php -d detect_unicode=0 go-pear.phar

    You’ll be prompted with a default file layout. I changed this to install it in /usr/local/pear instead of under /Users/Admin.

          Below is a suggested file layout for your new PEAR installation.  To
          change individual locations, type the number in front of the
          directory.  Type 'all' to change all of them or simply press Enter to
          accept these locations.
    
           1. Installation base ($prefix)                   : /Users/Admin/pear
           2. Temporary directory for processing            : /tmp/pear/install
           3. Temporary directory for downloads             : /tmp/pear/install
           4. Binaries directory                            : /Users/Admin/pear/bin
           5. PHP code directory ($php_dir)                 : /Users/Admin/pear/share/pear
           6. Documentation directory                       : /Users/Admin/pear/docs
           7. Data directory                                : /Users/Admin/pear/data
           8. User-modifiable configuration files directory : /Users/Admin/pear/cfg
           9. Public Web Files directory                    : /Users/Admin/pear/www
          10. Tests directory                               : /Users/Admin/pear/tests
          11. Name of configuration file                    : /Users/Admin/.pearrc
    
          1-11, 'all' or Enter to continue:

    At this prompt, type 1

          (Use $prefix as a shortcut for '/Users/Admin/pear', etc.)
          Installation base ($prefix) [/Users/Admin/pear] :

    At this prompt, type /usr/local/pear

          Below is a suggested file layout for your new PEAR installation.  To
          change individual locations, type the number in front of the
          directory.  Type 'all' to change all of them or simply press Enter to
          accept these locations.
    
           1. Installation base ($prefix)                   : /usr/local/pear
           2. Temporary directory for processing            : /tmp/pear/install
           3. Temporary directory for downloads             : /tmp/pear/install
           4. Binaries directory                            : /usr/local/pear/bin
           5. PHP code directory ($php_dir)                 : /usr/local/pear/share/pear
           6. Documentation directory                       : /usr/local/pear/docs
           7. Data directory                                : /usr/local/pear/data
           8. User-modifiable configuration files directory : /usr/local/pear/cfg
           9. Public Web Files directory                    : /usr/local/pear/www
          10. Tests directory                               : /usr/local/pear/tests
          11. Name of configuration file                    : /Users/Admin/.pearrc
    
          1-11, 'all' or Enter to continue:

    At this prompt, type 11

          (Use $prefix as a shortcut for '/usr/local/pear', etc.)
          Name of configuration file [/Users/Admin/.pearrc] :

    At this prompt, type /etc/pearrc

          Below is a suggested file layout for your new PEAR installation.  To
          change individual locations, type the number in front of the
          directory.  Type 'all' to change all of them or simply press Enter to
          accept these locations.
    
           1. Installation base ($prefix)                   : /usr/local/pear
           2. Temporary directory for processing            : /tmp/pear/install
           3. Temporary directory for downloads             : /tmp/pear/install
           4. Binaries directory                            : /usr/local/pear/bin
           5. PHP code directory ($php_dir)                 : /usr/local/pear/share/pear
           6. Documentation directory                       : /usr/local/pear/docs
           7. Data directory                                : /usr/local/pear/data
           8. User-modifiable configuration files directory : /usr/local/pear/cfg
           9. Public Web Files directory                    : /usr/local/pear/www
          10. Tests directory                               : /usr/local/pear/tests
          11. Name of configuration file                    : /etc/pearrc
    
          1-11, 'all' or Enter to continue:

    At this prompt, type <Enter>

          Would you like to alter php.ini </private/etc/php.ini>? [Y/n] :

    At this prompt, type Y.

  2. Install the PHP MongoDB driver.

          sudo /usr/local/pear/bin/pecl install mongo
  3. Configure the system PHP to load that extension. Add this line to your php.ini. (If you don’t know where your php.ini file is located, go back to the phpinfo page that you created earlier, and you’ll find it on that page.)

          extension=mongo.so
  4. Restart Apache. To enable PHP5 with MongoDB support, restart Apache.

          sudo apachectl graceful

Test your installation.

  1. Create a simple test script. Create a PHP file at /Library/WebServer/Documents/mongodb.php

          <?php
            $mongoDB = new Mongo();
            $database = $mongoDB->selectDB("example");
            $collection = $database->createCollection('TestCollection');
            $collection->insert(array(test => 'Test OK'));
    
            $retrieved = $collection->find();
            foreach ($retrieved as $obj) {
              echo($obj['test']);
            }
          ?>
  2. Test the installation. Browse to http://localhost/mongodb.php. If you see the following output, then congratulations, MongoDB is properly installed and configured to work with PHP on your Mac. Happy developing!

          Test OK

If this worked well for you, or if it doesn’t work for your specific configuration, please let me know via the comments, and perhaps we can troubleshoot.

HOWTO Make your Mac speak over the Web

15 Jan, 2009 — Apple, Funny, HOWTO

Randall Munroe’s XKCD has inspired interesting product features in the past. A recent one has sent a lot of Mac users scurrying to set up an audio doorbell on their Mac Minis.

Here’s how you can do it.

The Source Code

<?php
if (isset($_GET['say'])) {
  $cmd = sprintf('say "%s"', preg_replace('/[^\w\d ]/', '', $_GET['say']));
  `$cmd`;

} else if (isset($_GET['up'])) {
  $cmd = 'set output_vol to output volume of (get volume settings)
  set volume output volume (output_vol + 10)';
  `osascript -e "$cmd"`;

} else  if (isset($_GET['down'])) {
  $cmd = 'set output_vol to output volume of (get volume settings)
  set volume output volume (output_vol - 10)';
  `osascript -e "$cmd"`;
}
?>
<form action="<?= $_SERVER['PHP_SELF'] ?>" method="get">
  <p><input type="text" name="say">
  <input type="submit" value="Say"></p>
  <p>Volume:
  <input type="submit" name="up" value="Up">
  <input type="submit" name="down" value="Down"></p>
</form>
<p>Hey Randall, here's how you
  <a href="http://xkcd.com/530/">change the volume</a>.<br/>
  &ndash;<a href="http://manas.tungare.name/">Manas</a>.
</p>

Where to Put It

Copy the code to a new file, name it “say.php” (or whatever else you want to call it) and put it in Macintosh HD /Library/WebServer/Documents/. Remember, this is the top-level /Library directory, not the one under your own user account. You also need to make sure that Web Sharing has been turned on under System Preferences > Sharing.

How to Use It

Open a browser, and type in:

http://localhost/say.php

from your own machine.

To do this from another machine connected to the same router, use the Bonjour name of your Mac (this can be found under System Preferences > Sharing.) E.g.

http://Manas-Desktop.local/say.php

To do this from a machine outside your router, you need to have configured your router correctly. And if you can do that, you don’t need me to tell how to do the rest of it.

Bonus feature: this script also lets you increase and decrease the volume. Perhaps then, Randall’s roommate might have heard him.

Screenshot

Warning: Security Issues

To achieve what this script does, it needs to take input from the Web and use it in a command that executes in a shell. The input is sanitized and I believe that such a risk is minimal. However, if you’re concerned about this possibility, do not install this script. You are responsible for what you do with this script and your machine. If you’re really concerned, put it inside an authenticated session.

Update: Added a command injection filter suggested by Mac OS X Hints user skicker.

HOWTO Setup WebDAV on Mac OS X

10 Jul, 2008 — Apple, HOWTO

Setting up WebDAV on Leopard

The good news is that all the bits and pieces of software that you need to run a WebDAV server on Mac OS X 10.5 Leopard are already installed. You only need to configure them correctly and turn them on. Some experience with Terminal is preferred, and you should be familiar with executing UNIX commands. Let’s start!

  1. Start Apache. (If you haven’t already) You will need to enable Web Sharing, since the WebDAV service will be provided by Apache, the web server on Mac OS X. You do not necessarily need to have a web site running, but you will need to activate and run Apache. Go to System Preferences > Sharing, and turn on the box labeled Web Sharing.
    Mac OS X Preferences Screenshot -- Enabling Web Sharing
  2. Enable WebDAV support in Apache. Edit the file /etc/apache2/httpd.conf, (remember to use sudo to edit it) and locate this line:
    LoadModule dav_module libexec/apache2/mod_dav.so

    Make sure it is not commented (there should be no "#" at the beginning of the line.) Then locate this line (towards the bottom of the file):

    Include /private/etc/apache2/extra/httpd-dav.conf

    Again, make sure it is not commented out. It is disabled by default, so you need to remove the "#" from this line.

  3. Configure WebDAV. Next, edit the file /etc/apache2/extra/httpd-dav.conf. Add a section in it to create our new WebDAV share. Here’s what the new section should look like. As a security precaution, you should also go ahead and delete the /usr/uploads share that is set by default.
    Alias /webdav "/Library/WebServer/WebDAV"
    
    <Directory "/Library/WebServer/WebDAV">
      Dav On
    
      Order Allow,Deny
      Allow from all
    
      AuthType Basic
      AuthName WebDAV-Realm
      AuthUserFile "/usr/webdav.passwd"
    
      <LimitExcept GET OPTIONS>
        require user YourUserName
      </LimitExcept>
    </Directory>
    

    On line 1, the name following the Alias keyword is the URL you’d like for your new WebDAV share. If you want the share to be located at http://your-server-name/your-fancy-webdav-share, then line 1 should read:

    Alias /your-fancy-webdav-share "/Library/WebServer/WebDAV"

    On line 9, we specify the authentication scheme as Basic, not Digest. The security conscious will note that this sends unencrypted passwords over plain text. In my tests, OmniFocus was not able to communicate with the server with the Digest authentication scheme. Remember not to use a particularly important password for this account.

    On line 14, substitute the username you would like to use for your WebDAV account. Note this down, because you will need this again in the next step.

  4. Create user accounts and passwords. Use the htpasswd tool to create your password file.
    sudo htpasswd -c /usr/webdav.passwd "YourUserName"
    New password:
    Re-type new password:
    Adding password for user YourUserName
  5. Create the necessary directories.
    sudo mkdir -p /Library/WebServer/WebDAV
    sudo mkdir -p /usr/var
  6. Setup permissions correctly.
    sudo chown -R www:www /Library/WebServer/WebDAV
    sudo chown -R www:www /usr/var
    sudo chgrp www /usr/webdav.passwd
  7. Restart Apache gracefully.
    sudo apachectl graceful
  8. Test your server. Optionally, you can test your WebDAV configuration using litmus, a WebDAV server test tool. It is distributed as source code with no binaries, so you will need to compile it first, for which you will need Apple’s Developer Tools. You can test your server manually by using a graphical client such as Goliath. Try uploading a file and see if you can access it again.

That’s it, you can now point OmniFocus to http://your-server-name/webdav and provide the credentials you created earlier. With this setup, you will immediately be able to access your WebDAV server over your local network. If your machine has a static public IP address, you will also be able to sync from outside your local network.

If, on the other hand, your machine is behind a router, you will need to configure port forwarding on your router. If you do not have a static IP, you will need to set a dynamic hostname via services like DynDNS.

Possible Error Messages

This is by no means a zero error configuration, and sometimes things might go wrong. Here are some of the common error messages and how to fix the relevant errors:

  • The locks could not be queried for verification against a possible "If:" header.
    No such file or directory: Could not open property database
    The web server is not able to access the password file. In our example, you need to ensure that /usr/webdav.passwd can be read by the Apache user, www. To do that, run the following chmod command.

    sudo chgrp www /usr/webdav.passwd
  • Client used wrong authentication scheme: /webdav/
    You probably set the Authentication scheme to Digest instead of Basic. Try changing to Basic. Also note that you need to regenerate the password file using htpasswd instead of htdigest.

OmniFocus and WebDAV

To use WebDAV with OmniFocus, simply configure it as in the screenshots below.

Omnifocus Sync Settings

OmniFocus WebDAV Authentication

A hat tip to Vivek for helping test these instructions on a clean Leopard installation.

SSH Port Forwarding on Mac OS X

30 May, 2008 — Apple, HOWTO

After spending about an hour configuring what should, in theory, be a simple matter, I figured I’d write a blog post that might one day save another soul an hour or so from his or her life. So, for good karma, basically. In the past, I have set up port forwarding on Linux, Mac OS X and Windows, so I was a little worried that it took me about an hour trying to appease the SSH deities (and daemons).

The command itself is just a single line; the devil is in the parameters. I’m splitting the command over several lines and adding line numbers to illustrate the details and separate the parts of the long-ish command for easier explanation. Feel free to type it all on a single line (after removing the line numbers and the line-break markers ("\") of course!)

1.     ssh \
2.       -L local_port:service_host:service_port \
3.       -p ssh_server_port \
4.       -l ssh_server_username \
5.       -N \
6.       ssh_server_host

Parameters

Now for the various parameters used in the command above. Some of them may be omitted if the defaults are used, but I have included all of them in the example above to cover the most general case.

local_port

The port on your local machine that your local program expects to be able to connect to. If this is one of the reserved ports (i.e., under 1023), you will have to run your ssh tunnel command as root (using sudo). Ports above 1024 are freely available for any user to listen on.

service_host

The fully-qualified domain name or the IP address of the server that is hosting the service that you wish to connect to. For example, if this is a web site, it could be google.com or yahoo.com. It does not have to be under your control, nor does it have to be the machine that you’re SSHing into. It is just any host on the Internet that you can access from ssh_server_host. Often this is a server you are not allowed to access from your own machine, e.g. a chat server or IRC server. Or you may wish to hide the fact from the administrator of your local network that you are connecting to this server (e.g. when you’re out at a coffee shop on a sniffable insecure wireless network, or in a country with laws forbidding access to free information.)

Important: If you’re trying to access a service running on the same machine as ssh_server_host, remember to use 127.0.0.1, not localhost. What’s the difference, you say? Well, since IPv6 is here to stay, localhost can map to either 127.0.0.1 (IPv4) or ::1/128 (IPv6). If your applications aren’t all IPv6-compliant, this can cause some headache. Hopefully, we will all be on IPv6 in the near future, but till then, this is a way to make things work. If you’re trying to use IPv6, you need to use local_port/service_host/service_port (slashes instead of colons.)

service_port

The port number on which the desired service is running. Here are some common port numbers:

Service Port
Web: HTTP 80
Web over SSL: HTTPS 443
Outgoing email: SMTP 25
Incoming email: POP3 110
Incoming email: IMAP 143
VNC 5900
iTunes Music Sharing 3689

ssh_server_host

The machine that you’re SSHing into. This is the one that is running sshd, the SSH daemon.

ssh_server_port

The port number on which the SSH daemon is listening on ssh_server_host. This is most likely 22; you should only use a different value if your sysadmin has told you that the SSH server is running on another port (or if you’re a sysadmin yourself and you set up your SSH server to run on a non-standard port for security through obscurity.)

ssh_server_username

The username you would use to connect to ssh_server_host in a regular SSH session. This may or may not be the same as the username you currently use on your local machine.

The Entire Command, Line by Line

  1. Line 1 simply calls the ssh program;
  2. Line 2 sets up the port forwarding. The -L parameter specifies that this is a remote-to-local tunnel. If you wanted to create a local-to-remote tunnel, you’d have used -R instead of -L. The next three parameters are from our list above, separated by colons. (Use slashes instead of colons for IPv6.) If you want to set up multiple tunnels from the same host, simply repeat line 2 as many times as you’d like, once for each set of local_port:service_host:service_port.
  3. Line 3 selects a port on the ssh_server_host to connect to. Omit this line if you’re connecting to the default port 22.
  4. Line 4 specifies the username to use on the ssh_server_host. It is also possible to use the ssh_server_username@ssh_server_host syntax instead of the -l parameter.
  5. Line 5 indicates to ssh that no commands be run on the remote system. Since you’re using this SSH connection simply for tunneling, this is a useful option to set.
  6. Line 6 contains the most basic parameter of this entire process. Please don’t get this wrong.

Common Errors and Solutions

Problem Solution
Error message: channel 3: open failed: connect failed: Connection refused Change localhost to 127.0.0.1 in the ssh -L parameter.
Cannot listen on port X on local machine because of network policies. Try to use another port locally. Ports such as 3306 (MySQL) may have been left open. These are good to use for SSH tunneling if you aren’t already running MySQL.
Error message: Privileged ports can only be forwarded by root. Use a port above 1024, or try to set up the SSH tunnel as root.
Error message: bind: Address already in use
channel_setup_fwd_listener: cannot listen to port: xxxx
Could not request local forwarding.
Some local server process is already listening on the local port you’re trying to forward to. Pick a different local port and configure your program to connect to th at port instead. If your program cannot be configured to listen to a different port, try to find what server process is occupying that port (netstat -a on Linux or lsof -i -P on Mac OS X) and stop it. Retry setting up the tunnel.
I want other hosts on my network to be able to use the tunnel I established. (By default, only local clients can connect to SSH tunnels established this way.) Use the -g option when setting up the tunnel. Realize that this is insecure, but it may make sense in certain scenarios.
I don’t know what local port is available for me to use. Linux: netstat -a | grep LISTEN
Mac OS X: lsof -i -P | grep LISTEN
will show you the ports that are in use. Generally, you can pick any that’s not already taken. To make sure you’re not breaking some other unknown protocol, check the IANA Well-known Port Numbers list and pick one that’s not taken.

If you’ve not been able to debug this so far, try passing the -v parameter to ssh to see verbose output. Add another -v for more verbose output.

If you’re reading this, and come across any specific source of trouble, please let me know so I can add it to this mini HOWTO.

Apple’s 1984 Shareholders’ Meeting

23 Oct, 2007 — Apple, Video

A video from Apple’s 1984 Shareholders’ Meeting seems appropriate today.

Next Page »