Starting Out the Right Way

Structure of a Control Panel

Now that we’ve established a need for an open source control panel geared towards limitless virtual hosts, as well as edge-case technologies, we can start designing our application.

But, control panels are tricky beasts. The entire notion of a control panel is one that runs irrespective of the installed system, as it needs to control the system and remain available even under conditions which render the installed system useless. The other central piece of a control panel is that it must run as “root” or at least a privileged enough user to control all necessary parts of the system.

So, how do we make a system that can run even if the LAMP stack is broken or off-line? Well, we need to compile and setup a separate stack and install it in a separate location. Traditionally, people would compile a special version of Apache which runs with the set-uid privileges, allowing it to run as the owner of the file being executed. Thus, if the executing PHP script is owned by root, it will execute as root and be allowed to perform service restarts and config file editing.

We would also need our own version of a scripting language so that it our specific version remains constant even if the system’s default language is upgraded. Compiling a standard version of PHP adds to our required software stack’s complexity.

Couple this with a separate installation of MySQL which has to run on a separate port with separate privileges from the system’s default, and you have a compilation and configuration nightmare. In order to eliminate the complexities of such a heavy-weight software stack we will eliminate the A and M from LAMP and simply run a LP stack. Using the following technologies we will only have to compile one C application for any target platform:

  • PHP
  • SQLite
  • Nanoweb
  • Cognifty Framework

Nanoweb is a PHP web server written entirely in PHP. It requires the extension “pcntl” to serve pages at an acceptable level, but since we are compiling our own standard version of PHP this shouldn’t be a problem.

SQLite is a database engine in a library. The client library has enough code to execute SQL and store the results in a flat file. This reduces the complexity of deploying a separate SQL server just to store some users and configuration settings. The power of a control panel comes from the complex business logic, not the complex data relations. SQLite should provide enough power to serve our needs and also provide a familiar SQL interface to our Web app control panel.

Cognifty is chosen because it is probably the only PHP Web framework to come with standard user and group libraries. Allowing users to access only portions of the control panel is key, and spending time reinventing the wheel by building login screens, user SQL tables, and group permission checks is too much of a waste of time. Also, Cognifty is one of the fastest frameworks with the lowest overhead, which we will need if we are also running our Web server in PHP to allow for less stress on the system.

Preparing the build

The build process for our software stack is going to be the most daunting task. Traditionally, PHP applications do not start out with a build process. Only if the project becomes successful and maintenance becomes too time consuming do people start looking to inject a build process on top of their project. This project requires a build process from the start so that we can have a customized PHP installation with all of our requirements guaranteed deployable.

We will start with a skeleton SVN directory setup like this:

build/
configs/
libs/
patches/
run-root/
scripts/
src/
    modules/

The build directory will be a holding cell for our code when we are organizing things for packaging and deployment. The libs directory will store external libraries like the PHP source, nanoweb, and Cognifty. The patches directory will hold any patches we need to our source libraries.

The run-root directory is intended to be an intermediate step between the raw source packaged in tar.gz files and our final build, ready for deploying on a server. Most of the life of the code will be spent in “run-root” as we are developing and testing the modules. Nothing in run-root should be committed to SVN. The src directory is intended to be where our unique source modules for the embedded server live and are committed to SVN. When building our application, we will make changes directly in src/modules/ and the running Cognfity server will be configured to look directly in that directory for all of its front-end modules.

The remainder of the directories are merely to provide support for the build process itself.

Import this project into your SVN repo under the project name “niftyserver” with the following command: svn import . svn://server/my_repo/niftyserver Then delete the source directory, move up a directory, and checkout a working copy with: svn co svn://server/my_repo/niftyserver

[UPDATE: I put all the code into subversion in case you want to follow along the easy way.]
svn co http://niftyphp.svn.sourceforge.net/niftyphp/niftyserver/tags/tutorial-p1 niftyserver

Ant

The build process itself will be driven by Apache’s ant project. So, start by downloading and installing ant.

Ant searches for a file called build.xml by default. The file is written in XML and consists of a number of targets. Targets can be combined together to form larger units of work. For our purposes we will need the following build targets:

  1. Extract PHP
  2. Compile PHP
  3. Install PHP (into the run-root)
  4. Extract nanoweb
  5. Patch nanoweb
  6. Install nanoweb (into the run-root)
  7. Extract cognifty
  8. Install cognifty (into the run-root)
  9. Build
  10. Package

Below is a shell ant script. Save this file as "build.xml" in your project's root directory.

<?xml version="1.0"?>
<project name="NiftyServer" default="run-root" basedir=".">
  <description>
      The embedded PHP NiftServer project
  </description>

<!-- set global properties for this build -->
<property file="build.properties" />
<property name="build" location="build" />
<property name="libs" location="libs" />
<property name="src" location="src" />
<property name="patches"  location="patches" />
<property name="scripts" location="scripts" />
<property name="run-root" location="run-root" />
</project>