Right.. that was my first time sending a patch via e-mail for a few years.. apologies if I've got things wrong!<div><br></div><div>I need help with the Makefile stuff. I'm afraid I've never written a program using the configure/make/make install system before, so I really have no idea how to write the makefile. What I can say is..</div>

<div><br></div><div><a href="http://lxc.pl">lxc.pl</a> should be installed in bindir as 'lxc'</div><div>lib/LXC/Commands.pm should be in whatever directory would correspond to /usr/share/perl5 on a debian system.</div>

<div>The files in upstart/ should be installed in /etc/init on ubuntu type systems.</div><div>The other files are probably irrelevant.</div><div><br></div><div>All feedback most welcome of course, and I shall re-submit patches as required.</div>

<div><br></div><div>Cheers,</div><div>Nigel</div><div><br><div class="gmail_quote">On 1 May 2011 15:55, Nigel McNie <span dir="ltr"><<a href="mailto:nigel@mcnie.name">nigel@mcnie.name</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">

This was first developed seperately from lxc itself, at<br>
<a href="https://github.com/nigelmcnie/lxc-simple" target="_blank">https://github.com/nigelmcnie/lxc-simple</a><br>
<br>
This patch adds the porcelain script etc, but doesn't integrate with the<br>
build process in anyway. Therefore, <a href="http://lxc.pl" target="_blank">lxc.pl</a> and friends won't actually be<br>
installed with make install - yet.<br>
<br>
Signed-off-by: Nigel McNie <<a href="mailto:nigel@mcnie.name">nigel@mcnie.name</a>><br>
---<br>
 MAINTAINERS                       |    7 +<br>
 src/porcelain/CREDITS             |    8 +<br>
 src/porcelain/README.md           |   90 ++++++<br>
 src/porcelain/TODO                |    2 +<br>
 src/porcelain/lib/LXC/Commands.pm |  591 +++++++++++++++++++++++++++++++++++++<br>
 src/porcelain/<a href="http://lxc.pl" target="_blank">lxc.pl</a>              |  335 +++++++++++++++++++++<br>
 6 files changed, 1033 insertions(+), 0 deletions(-)<br>
 create mode 100644 src/porcelain/CREDITS<br>
 create mode 100644 src/porcelain/README.md<br>
 create mode 100644 src/porcelain/TODO<br>
 create mode 100644 src/porcelain/lib/LXC/Commands.pm<br>
 create mode 100755 src/porcelain/<a href="http://lxc.pl" target="_blank">lxc.pl</a><br>
<br>
diff --git a/MAINTAINERS b/MAINTAINERS<br>
index 306c07e..f2e8c40 100644<br>
--- a/MAINTAINERS<br>
+++ b/MAINTAINERS<br>
@@ -10,3 +10,10 @@ Mail patches to : <a href="mailto:dlezcano@fr.ibm.com">dlezcano@fr.ibm.com</a><br>
 Mailing lists   : <a href="mailto:lxc-devel@sourceforge.net">lxc-devel@sourceforge.net</a>, <a href="mailto:containers@lists.osdl.org">containers@lists.osdl.org</a><br>
 Web page        : <a href="http://lxc.sourceforge.net" target="_blank">lxc.sourceforge.net</a><br>
 GIT location    : <a href="http://lxc.git.sourceforge.net/" target="_blank">http://lxc.git.sourceforge.net/</a><br>
+<br>
+Porcelain Maintainer<br>
+--------------------<br>
+<br>
+Person          : Nigel McNie<br>
+Mail patches to : <a href="mailto:nigel@mcnie.name">nigel@mcnie.name</a><br>
+Mailing list    : <a href="mailto:lxc-devel@sourceforge.net">lxc-devel@sourceforge.net</a><br>
diff --git a/src/porcelain/CREDITS b/src/porcelain/CREDITS<br>
new file mode 100644<br>
index 0000000..0485730<br>
--- /dev/null<br>
+++ b/src/porcelain/CREDITS<br>
@@ -0,0 +1,8 @@<br>
+Credits<br>
+=======<br>
+<br>
+This lxc command is mostly written by Shoptime Software. Some parts are<br>
+contributed by others, those people (and their contributions, if significant)<br>
+are listed here.<br>
+<br>
+[nobody yet! who wants to be first!]<br>
diff --git a/src/porcelain/README.md b/src/porcelain/README.md<br>
new file mode 100644<br>
index 0000000..8158c2e<br>
--- /dev/null<br>
+++ b/src/porcelain/README.md<br>
@@ -0,0 +1,90 @@<br>
+Getting started with lxc<br>
+========================<br>
+<br>
+1. Install dependencies<br>
+-----------------------<br>
+<br>
+    apt-get install lxc debootstrap libpasswd-unix-perl libfile-slurp-perl \<br>
+                    libmoose-perl libpath-class-perl libconfig-inifiles-perl<br>
+<br>
+2. Set up networking<br>
+--------------------<br>
+<br>
+Your containers need a way to talk to the interweb. One way is a bridge.<br>
+<br>
+A bridge is basically like a switch. You can plug your ``eth0`` into it, and<br>
+also each container. Then they can all talk to each other and the outside<br>
+world.<br>
+<br>
+Put something like this in ``/etc/network/interfaces``:<br>
+<br>
+    auto lo<br>
+    iface lo inet loopback<br>
+<br>
+    iface eth0 inet manual<br>
+<br>
+    auto br0<br>
+    iface br0 inet dhcp<br>
+        bridge_ports eth0<br>
+        bridge_stp off<br>
+        post-up /usr/sbin/brctl setfd br0 0<br>
+<br>
+If you have a static lease, configure br0 with that information instead of the<br>
+dhcp line::<br>
+<br>
+    iface br0 inet static<br>
+        address [your ip]<br>
+        netmask [your netmask]<br>
+        broadcast [your broadcast]<br>
+        gateway [your gateway]<br>
+        bridge_ports eth0<br>
+        bridge_stp off<br>
+        post-up /usr/sbin/brctl setfd br0 0<br>
+<br>
+3. Put info about networking setup in a file for later use<br>
+----------------------------------------------------------<br>
+<br>
+When containers are created, we need to tell them how to access the network.<br>
+Put this in ``/etc/lxc/lxc.conf``:<br>
+<br>
+    lxc.network.type=veth<br>
+    lxc.network.link=br0<br>
+    lxc.network.flags=up<br>
+<br>
+4. You're ready!<br>
+----------------<br>
+<br>
+    # Summary of commands<br>
+    lxc --help<br>
+<br>
+    # Create a container called 'test'<br>
+    lxc test create<br>
+<br>
+    # Start the container<br>
+    lxc test start<br>
+<br>
+    # Enter it<br>
+    lxc test enter<br>
+<br>
+    # Stop the container<br>
+    lxc test stop<br>
+<br>
+    # Destroy it (you'll be asked to confirm)<br>
+    lxc test destroy<br>
+<br>
+5. Starting containers automatically on boot<br>
+--------------------------------------------<br>
+<br>
+On ubuntu, install the upstart jobs:<br>
+<br>
+    cp upstart/lxc-* /etc/init<br>
+<br>
+And when you create your containers, use -a to flag them as ones that should be<br>
+automatically started on boot.<br>
+<br>
+If you have a container you already made that you want to start on boot:<br>
+<br>
+    touch /var/lib/lxc/[name]/autostart<br>
+<br>
+Good luck!<br>
+ -- Nigel McNie & Martyn Smith<br>
diff --git a/src/porcelain/TODO b/src/porcelain/TODO<br>
new file mode 100644<br>
index 0000000..58c07b7<br>
--- /dev/null<br>
+++ b/src/porcelain/TODO<br>
@@ -0,0 +1,2 @@<br>
+Graceful cleanup if program is ctrl-C'd while in lxc-create<br>
+lxc [name] enter using lxc-attach (relies on lxc-attach being implemented upstream)<br>
diff --git a/src/porcelain/lib/LXC/Commands.pm b/src/porcelain/lib/LXC/Commands.pm<br>
new file mode 100644<br>
index 0000000..e9b8b09<br>
--- /dev/null<br>
+++ b/src/porcelain/lib/LXC/Commands.pm<br>
@@ -0,0 +1,591 @@<br>
+# lxc - Wrapper around lxc utils to make managing containers easier<br>
+# Copyright © 2011 Shoptime Software<br>
+#<br>
+# This package is free software; you can redistribute it and/or<br>
+# modify it under the terms of the GNU Lesser General Public<br>
+# License as published by the Free Software Foundation; either<br>
+# version 2 of the License, or (at your option) any later version.<br>
+#<br>
+# This package is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU<br>
+# Lesser General Public License for more details.<br>
+#<br>
+# You should have received a copy of the GNU Lesser General Public<br>
+# License along with this package; if not, write to the Free Software<br>
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA<br>
+<br>
+package LXC::Commands;<br>
+use Moose;<br>
+<br>
+use File::Slurp;<br>
+use Passwd::Unix;<br>
+use Sysadm::Install qw(tap);<br>
+use Time::HiRes qw(usleep);<br>
+<br>
+=head1 NAME<br>
+<br>
+LXC::Commands - commands for LXC<br>
+<br>
+=head1 DESCRIPTION<br>
+<br>
+Contains routines that back the LXC commands. Normally, you would access these<br>
+through the 'lxc' script, but you can include this module and use them directly<br>
+if you wish.<br>
+<br>
+=head1 METHODS<br>
+<br>
+=cut<br>
+<br>
+<br>
+has lxc_dir => (<br>
+    is => 'rw',<br>
+    isa => 'Path::Class::Dir',<br>
+    required => 1,<br>
+);<br>
+<br>
+<br>
+=head2 create<br>
+<br>
+Creates a new container.<br>
+<br>
+Takes a hash with the following keys:<br>
+<br>
+=over 4<br>
+<br>
+=item name<br>
+<br>
+The name of the container to create.<br>
+<br>
+=item autostart<br>
+<br>
+Whether the container should be flagged to be automatically started on boot.<br>
+<br>
+=item install_user<br>
+<br>
+If true, bind mounts /home into the container. Also, if this script was invoked<br>
+by C<sudo>, it creates an account and group in the container for that user,<br>
+using the same details as on the host (e.g. same password).<br>
+<br>
+=item mirror<br>
+<br>
+The mirror to use to download packages.<br>
+<br>
+=item start<br>
+<br>
+Whether to start the container once created.<br>
+<br>
+=item template<br>
+<br>
+The template to use to create the container (see C<lxc-create>'s C<--template><br>
+option).<br>
+<br>
+=back<br>
+<br>
+=cut<br>
+<br>
+sub create {<br>
+    my ($self, %args) = @_;<br>
+    my $name = $args{name} || die "Must specify a name for the container to be created\n";<br>
+<br>
+    system('lxc-create',<br>
+        '-n', $name,                    # TODO: check for invalid name first?<br>
+        '-f', '/etc/lxc/lxc.conf',      # TODO: this is for networking stuff<br>
+        '-t', $args{template} // 'ubuntu',<br>
+    ) == 0<br>
+        or die "lxc-create failed with exit code $?\n";<br>
+<br>
+    my $container_cfgroot = $self->lxc_dir->subdir($name);<br>
+    my $container_root    = $self->lxc_dir->subdir($name . '/rootfs/');<br>
+<br>
+    # Dump autostart file down if asked for<br>
+    write_file($container_cfgroot->file('autostart')->stringify, '') if $args{autostart};<br>
+<br>
+    # Install our own /etc/network/interfaces<br>
+    my $interfaces_content = q(<br>
+auto lo<br>
+iface lo inet loopback<br>
+<br>
+auto eth0<br>
+iface eth0 inet dhcp<br>
+    # NOTE: if you're making your own interface definition, leave this line in<br>
+    # place so that 'lxc [name] enter' will work<br>
+    up ip a s dev eth0 | grep 'inet\W' | awk '{print $2}' | cut -f 1 -d '/' > /lxc-ip<br>
+    down rm /lxc-ip<br>
+);<br>
+    write_file($container_root->file('etc/network/interfaces')->stringify, $interfaces_content);<br>
+<br>
+    # Bindmount homedir and install user account if asked for<br>
+    if ( $args{install_user} ) {<br>
+        # TODO naturally, we could grab this information from a config file<br>
+        if ( exists $ENV{SUDO_USER} ) {<br>
+            my $user  = $ENV{SUDO_USER};<br>
+            my $group = $ENV{SUDO_USER};<br>
+<br>
+            mkdir "$container_root/home/$user";<br>
+            append_file($container_cfgroot->file('fstab')->stringify,<br>
+                sprintf("/home/$user           %s         auto bind 0 0\n", "$container_root/home/$user"));<br>
+<br>
+            my $hostpw = Passwd::Unix->new(<br>
+                passwd => '/etc/passwd',<br>
+                shadow => '/etc/shadow',<br>
+                group  => '/etc/group',<br>
+                backup => 0,<br>
+            );<br>
+            my @userinfo  = $hostpw->user($user);<br>
+            my @groupinfo = $hostpw->group($group);<br>
+<br>
+            my $containerpw = Passwd::Unix->new(<br>
+                passwd => $container_root->file('etc/passwd'),<br>
+                shadow => $container_root->file('etc/shadow'),<br>
+                group  => $container_root->file('etc/group'),<br>
+                backup => 0,<br>
+            );<br>
+            $containerpw->user($user, @userinfo);<br>
+            $containerpw->group($group, @groupinfo);<br>
+        }<br>
+        else {<br>
+            print "Could not establish what user to install, skipping\n";<br>
+        }<br>
+    }<br>
+<br>
+    if ( $args{mirror} ) {<br>
+        my $mirror = $args{mirror};<br>
+        my $apt_sources_file = $container_root->file('etc/apt/sources.list')->stringify;<br>
+<br>
+        my $contents = read_file($apt_sources_file);<br>
+        $contents =~ s/<a href="http://archive.ubuntu.com/$mirror/g" target="_blank">archive.ubuntu.com/$mirror/g</a>;<br>
+        write_file($apt_sources_file, $contents);<br>
+<br>
+        system('chroot', $container_root, 'apt-get', 'update');<br>
+    }<br>
+<br>
+    system('chroot', $container_root, 'apt-get', 'install', '-y', '--force-yes', 'gpgv');<br>
+    system('chroot', $container_root, 'apt-get', 'update');<br>
+<br>
+    $self->start(name => $name) if $args{start};<br>
+}<br>
+<br>
+<br>
+=head2 destroy<br>
+<br>
+Destroys a container, stopping it first if necessary.<br>
+<br>
+Takes a hash with the following keys:<br>
+<br>
+=over 4<br>
+<br>
+=item name<br>
+<br>
+The name of the container to destroy.<br>
+<br>
+=back<br>
+<br>
+=cut<br>
+<br>
+sub destroy {<br>
+    my ($self, %args) = @_;<br>
+    my $name = $args{name} || die "Must specify what container to destroy\n";<br>
+    $self->check_valid_container($name);<br>
+<br>
+    if ( $self->status(name => $name, brief => 1) eq 'running' ) {<br>
+        $self->stop(name => $name);<br>
+    }<br>
+<br>
+    print "Destroying $name... ";<br>
+    system('lxc-destroy',<br>
+        '-n', $name,<br>
+    );<br>
+    print "done\n";<br>
+}<br>
+<br>
+<br>
+=head2 start<br>
+<br>
+Starts a container.<br>
+<br>
+Takes a hash with the following keys:<br>
+<br>
+=over 4<br>
+<br>
+=item name<br>
+<br>
+The name of the container to start.<br>
+<br>
+=back<br>
+<br>
+=cut<br>
+<br>
+sub start {<br>
+    my ($self, %args) = @_;<br>
+    my $name = $args{name} || die "Must specify what container to start\n";<br>
+    $self->check_valid_container($name);<br>
+<br>
+    die "Container '$name' IS started\n" if $self->status(name => $name, brief => 1) eq 'running';<br>
+<br>
+    print "Starting $name... ";<br>
+    system('lxc-start',<br>
+        '-n', $name,<br>
+        '-d',<br>
+    );<br>
+    system('lxc-wait',<br>
+        '-n', $name,<br>
+        '-s', 'RUNNING',<br>
+    );<br>
+<br>
+    # NOTE: this will go away once Martyn works out a smarter way of handling<br>
+    # container networking<br>
+    for (1..100) {<br>
+        last if -f $self->lxc_dir->file("$name/rootfs/lxc-ip");<br>
+        if ( $_ == 100 ) {<br>
+            print "Could not confirm container started, check with 'lxc $name enter'\n";<br>
+            return;<br>
+        }<br>
+        usleep 100_000;<br>
+    }<br>
+<br>
+    print "done\n";<br>
+}<br>
+<br>
+<br>
+=head2 stop<br>
+<br>
+Gracefully stops a container.<br>
+<br>
+This runs 'halt' in the container and then waits until all processes have<br>
+exited, or some time has passed. If processes are still around after that time,<br>
+it murders them.<br>
+<br>
+Takes a hash with the following keys:<br>
+<br>
+=over 4<br>
+<br>
+=item name<br>
+<br>
+The name of the container to stop.<br>
+<br>
+=back<br>
+<br>
+=cut<br>
+<br>
+sub stop {<br>
+    my ($self, %args) = @_;<br>
+    my $name = $args{name} || die "Must specify what container to stop\n";<br>
+    $self->check_valid_container($name);<br>
+<br>
+    die "Container '$name' IS stopped\n" if $self->status(name => $name, brief => 1) eq 'stopped';<br>
+<br>
+    print "Stopping $name... ";<br>
+<br>
+    $self->enter(<br>
+        name    => $name,<br>
+        command => [ qw(halt) ],<br>
+    );<br>
+    unlink $self->lxc_dir->file("$name/rootfs/lxc-ip");<br>
+<br>
+    # Now we wait until all the processes go away<br>
+    my $timeout = 20;<br>
+    my $unresponsive = 1;<br>
+    for (1..$timeout*10) {<br>
+        my ($stdout, $stderr, $return_code) = tap(<br>
+            'lxc-ps',<br>
+            '--lxc',<br>
+            'ax',<br>
+        );<br>
+        print STDERR $stderr;<br>
+        exit $return_code if $return_code;<br>
+<br>
+        my $count = grep { $_ =~ /^\Q$name\E/ } split "\n", $stdout;<br>
+        unless ( $count ) {<br>
+            $unresponsive = 0;<br>
+            last;<br>
+        }<br>
+<br>
+        usleep 100_000;<br>
+    }<br>
+<br>
+    if ( $unresponsive ) {<br>
+        print "WARNING: Container '$name' still wasn't shut down after $timeout seconds, forcing it... ";<br>
+        system('lxc-stop',<br>
+            '-n', $name,<br>
+        );<br>
+        system('lxc-wait',<br>
+            '-n', $name,<br>
+            '-s', 'STOPPED',<br>
+        );<br>
+    }<br>
+<br>
+    print "done\n";<br>
+}<br>
+<br>
+<br>
+=head2 restart<br>
+<br>
+Restarts a container.<br>
+<br>
+This issues a stop, if the container is running, and then a start. After this,<br>
+the container will be running even if it wasn't before.<br>
+<br>
+Takes a hash with the following keys:<br>
+<br>
+=over 4<br>
+<br>
+=item name<br>
+<br>
+The name of the container to restart.<br>
+<br>
+=back<br>
+<br>
+=cut<br>
+<br>
+sub restart {<br>
+    my ($self, %args) = @_;<br>
+    my $name = $args{name} || die "Must specify what container to restart\n";<br>
+    $self->check_valid_container($name);<br>
+<br>
+    if ( $self->status(name => $name, brief => 1) eq 'stopped' ) {<br>
+        print "Container '$name' already stopped\n";<br>
+    }<br>
+    else {<br>
+        $self->stop(name => $name);<br>
+    }<br>
+<br>
+    $self->start(name => $name);<br>
+}<br>
+<br>
+<br>
+=head2 enter<br>
+<br>
+Gives you a shell in the container.<br>
+<br>
+Note: until C<lxc-attach> is implemented in the kernel (which it still wasn't<br>
+as of 2.6.38rc1), we hack this functionality by using ssh instead.<br>
+<br>
+For that we need to know the IP of the guest, which it provides by dumping it<br>
+to /lxc-ip (in the container) when the interface is brought up.<br>
+<br>
+If ssh isn't running or the IP isn't written to that file, we can't get a<br>
+shell. It would be much nicer if lxc-attach worked!<br>
+<br>
+Takes a hash with the following keys:<br>
+<br>
+=over 4<br>
+<br>
+=item name<br>
+<br>
+The name of the container to get a shell in.<br>
+<br>
+=item command<br>
+<br>
+An arrayref containing a command and arguments to run in the container<br>
+(optional).<br>
+<br>
+=back<br>
+<br>
+=cut<br>
+<br>
+sub enter {<br>
+    my ($self, %args) = @_;<br>
+    my $name = $args{name} || die "Must specify what container to get a shell in\n";<br>
+    $self->check_valid_container($name);<br>
+    $args{command} //= [];<br>
+<br>
+    die "Container '$name' is stopped\n" if $self->status(name => $name, brief => 1) eq 'stopped';<br>
+<br>
+    my $ip_file = $self->lxc_dir->file("$name/rootfs/lxc-ip")->stringify;<br>
+    die "Could not determine IP to ssh to (maybe networking isn't up in '$name' yet)\n" unless -f $ip_file;<br>
+    my $ip = read_file($ip_file);<br>
+    chomp $ip;<br>
+    die "No IP available for container '$name'" unless $ip;<br>
+    die "Could not determine IP to ssh to" unless $ip =~ m{^\d+\.\d+\.\d+\.\d+$};<br>
+<br>
+    my $host_key = read_file($self->lxc_dir->file("$name/rootfs/etc/ssh/ssh_host_rsa_key.pub")->stringify);<br>
+    $host_key = (split /\s+/, $host_key)[1];<br>
+<br>
+    # Generate an ssh keypair unless one already exists<br>
+    my $ssh_key_file = $self->lxc_dir->file("$name/ssh.key");<br>
+    unless ( -f $ssh_key_file ) {<br>
+        my ($stdout, $stderr, $return_code) = tap(<br>
+            'ssh-keygen',<br>
+            '-f' => $ssh_key_file,<br>
+            '-P' => '',<br>
+        );<br>
+        print STDERR $stderr;<br>
+        exit $return_code if $return_code;<br>
+    }<br>
+<br>
+    # Write out a known hosts file based on the ssh host key of the guest<br>
+    write_file($self->lxc_dir->file("$name/ssh.known_hosts")->stringify, "$ip ssh-rsa $host_key\n");<br>
+<br>
+    # Ensure root has the appropriate authorized_keys file in place<br>
+    system('mkdir', '-p', $self->lxc_dir->file("$name/rootfs/root/.ssh"));<br>
+    system('cp', $self->lxc_dir->file("$name/ssh.key.pub"), $self->lxc_dir->file("$name/rootfs/root/.ssh/authorized_keys"));<br>
+<br>
+    system('ssh',<br>
+        '-l' => 'root',<br>
+        '-i' => $self->lxc_dir->file("$name/ssh.key"),<br>
+        '-o' => 'UserKnownHostsFile=' . $self->lxc_dir->file("$name/ssh.known_hosts"),<br>
+        $ip,<br>
+        @{$args{command}}<br>
+    );<br>
+}<br>
+<br>
+<br>
+=head2 console<br>
+<br>
+Gives you a console in the container.<br>
+<br>
+Note you can only grab ONE console. Really, 'enter' is the better command to be<br>
+using, but if networking is down in the container, you'll have to use this.<br>
+<br>
+Takes a hash with the following keys:<br>
+<br>
+=over 4<br>
+<br>
+=item name<br>
+<br>
+The name of the container to get a console in.<br>
+<br>
+=back<br>
+<br>
+=cut<br>
+<br>
+sub console {<br>
+    my ($self, %args) = @_;<br>
+    my $name = $args{name} || die "Must specify what container to get a console in\n";<br>
+    $self->check_valid_container($name);<br>
+<br>
+    die "Container '$name' is stopped\n" if $self->status(name => $name, brief => 1) eq 'stopped';<br>
+<br>
+    my $lockfile = $self->lxc_dir->file("$name/console-lock")->stringify;<br>
+<br>
+    die "You already have the console for this container open elsewhere\n" if -f $lockfile;<br>
+    write_file($lockfile, "locked by pid $$\n");<br>
+<br>
+    system('lxc-console',<br>
+        '-n', $name,<br>
+    );<br>
+<br>
+    unlink $lockfile;<br>
+}<br>
+<br>
+<br>
+=head2 status<br>
+<br>
+Gives status information about one or all containers.<br>
+<br>
+Takes a hash with the following keys:<br>
+<br>
+=over 4<br>
+<br>
+=item name<br>
+<br>
+The name of the container to get status information for (optional).<br>
+<br>
+=item brief<br>
+<br>
+Boolean, whether to output brief (machine readable) information (optional).<br>
+<br>
+=back<br>
+<br>
+=cut<br>
+<br>
+sub status {<br>
+    my ($self, %args) = @_;<br>
+<br>
+    if ( $args{name} ) {<br>
+        my $name = $args{name};<br>
+        $self->check_valid_container($name);<br>
+<br>
+        if ( $args{brief} ) {<br>
+            my ($status, $stderr, $return_code) = tap('lxc-info', '-n', $name);<br>
+            if ( $status =~ m{^'\Q$name\E' is ([A-Z]+)$} ) {<br>
+                return lc $1;<br>
+            }<br>
+            print STDERR $stderr;<br>
+            die "Could not get status for container\n";<br>
+        }<br>
+<br>
+        # TODO would be nice to provide more detail here<br>
+        system('lxc-info',<br>
+            '-n', $name<br>
+        );<br>
+        return;<br>
+    }<br>
+<br>
+    # Status for all containers<br>
+    my $lxc_dir = $self->lxc_dir;<br>
+    for my $dir (<$lxc_dir/*>) {<br>
+        if ( -d $dir && $dir =~ m{/([^/]+)$} ) {<br>
+            system('lxc-info',<br>
+                '-n', $1,<br>
+            );<br>
+        }<br>
+    }<br>
+}<br>
+<br>
+<br>
+=head2 autostart<br>
+<br>
+Starts all containers that have a file called 'autostart' in their lxc config<br>
+directory.<br>
+<br>
+=cut<br>
+<br>
+sub autostart {<br>
+    my ($self, %args) = @_;<br>
+<br>
+    my $lxc_dir = $self->lxc_dir;<br>
+    for my $dir (<$lxc_dir/*>) {<br>
+        if ( -d $dir && $dir =~ m{/([^/]+)$} && -f "$dir/autostart" ) {<br>
+            # Try to start, but don't bail out if it's not possible<br>
+            eval {<br>
+                $self->start(name => $1);<br>
+            };<br>
+            print STDERR $@ if $@;<br>
+        }<br>
+    }<br>
+}<br>
+<br>
+<br>
+=head2 stopall<br>
+<br>
+Stops all containers.<br>
+<br>
+=cut<br>
+<br>
+sub stopall {<br>
+    my ($self, %args) = @_;<br>
+<br>
+    my $lxc_dir = $self->lxc_dir;<br>
+    for my $dir (<$lxc_dir/*>) {<br>
+        if ( -d $dir && $dir =~ m{/([^/]+)$} ) {<br>
+            # Try to stop, but don't bail out if we couldn't stop one<br>
+            eval {<br>
+                $self->stop(name => $1) if $self->status(name => $1, brief => 1) eq 'running';<br>
+            };<br>
+            print STDERR $@ if $@;<br>
+        }<br>
+    }<br>
+}<br>
+<br>
+<br>
+=head2 check_valid_container<br>
+<br>
+Given a container name, checks if the name refers to an existing container.<br>
+<br>
+=cut<br>
+<br>
+sub check_valid_container {<br>
+    my ($self, $name) = @_;<br>
+    die "No such container '$name'\n" unless -d $self->lxc_dir->subdir($name);<br>
+}<br>
+<br>
+<br>
+=head1 AUTHOR<br>
+<br>
+Shoptime Software<br>
+<br>
+=cut<br>
+<br>
+1;<br>
diff --git a/src/porcelain/<a href="http://lxc.pl" target="_blank">lxc.pl</a> b/src/porcelain/<a href="http://lxc.pl" target="_blank">lxc.pl</a><br>
new file mode 100755<br>
index 0000000..a52ccb2<br>
--- /dev/null<br>
+++ b/src/porcelain/<a href="http://lxc.pl" target="_blank">lxc.pl</a><br>
@@ -0,0 +1,335 @@<br>
+#!/usr/bin/env perl<br>
+#<br>
+# lxc - Wrapper around lxc utils to make managing containers easier<br>
+# Copyright © 2011 Shoptime Software<br>
+#<br>
+# This package is free software; you can redistribute it and/or<br>
+# modify it under the terms of the GNU Lesser General Public<br>
+# License as published by the Free Software Foundation; either<br>
+# version 2 of the License, or (at your option) any later version.<br>
+#<br>
+# This package is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU<br>
+# Lesser General Public License for more details.<br>
+#<br>
+# You should have received a copy of the GNU Lesser General Public<br>
+# License along with this package; if not, write to the Free Software<br>
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA<br>
+<br>
+use warnings;<br>
+use strict;<br>
+use 5.010;<br>
+<br>
+use Carp;<br>
+use Config::IniFiles;<br>
+use FindBin;<br>
+use lib "$FindBin::Bin/lib";<br>
+use Getopt::Long qw(GetOptions);<br>
+use LXC::Commands;<br>
+use Path::Class;<br>
+use Pod::Usage;<br>
+use Sysadm::Install qw(ask);<br>
+<br>
+our $VERSION = '0.1.0';<br>
+<br>
+<br>
+<br>
+my (%opt);<br>
+<br>
+# If running the 'exec' command, hide the command from our option parsing<br>
+my @exec_args;<br>
+if ( scalar @ARGV > 2 && $ARGV[1] eq 'exec' ) {<br>
+    @exec_args = splice(@ARGV, 2);<br>
+}<br>
+<br>
+if (!GetOptions(\%opt,<br>
+    'help|?',<br>
+    'version',<br>
+<br>
+    # Other options will go here, as individual commands want them<br>
+<br>
+    # Only used by 'create'<br>
+    'a|autostart',<br>
+    'm|mirror=s',<br>
+    'n|no-start',<br>
+    't|template=s',<br>
+    'u|user-from-host',<br>
+)) {<br>
+    pod2usage(-exitval => 1, -verbose => 0);<br>
+}<br>
+<br>
+push @ARGV, @exec_args;<br>
+<br>
+<br>
+# Actions that don't involve a command<br>
+pod2usage(-exitval => 0, -verbose => 1) if $opt{help};<br>
+if ( $opt{version} ) {<br>
+    system('lxc-version');<br>
+    print "lxc control script: $VERSION\n";<br>
+    exit 0;<br>
+}<br>
+<br>
+<br>
+# Configure app<br>
+my $cfg;<br>
+my @searchpath = (<br>
+    '/etc/lxc/lxc-simple.ini',<br>
+    "$FindBin::Bin/lxc-simple.ini",<br>
+);<br>
+foreach my $filename ( @searchpath ) {<br>
+    if ( -f $filename ) {<br>
+        $cfg = Config::IniFiles->new(-file => $filename);<br>
+        last;<br>
+    }<br>
+}<br>
+<br>
+my $app = LXC::Commands->new(<br>
+    lxc_dir    => Path::Class::dir($cfg ? $cfg->val('paths', 'lxc_dir') : '/var/lib/lxc'),<br>
+);<br>
+<br>
+<br>
+# Run command!<br>
+my $name    = shift;<br>
+my $command = shift;<br>
+<br>
+if ( defined $command ) {<br>
+    pod2usage(-exitval => 0, -verbose => 0) unless $name;<br>
+}<br>
+else {<br>
+    # For commands that don't have to operate on containers (e.g. 'status')<br>
+    $command = $name;<br>
+    $name    = undef;<br>
+}<br>
+<br>
+unless ($> == 0 || $< == 0) { die "You must be root\n" }<br>
+<br>
+given ( $command ) {<br>
+    when ( 'create' ) {<br>
+        $app->create(<br>
+            name           => $name,<br>
+            autostart      => $opt{a},<br>
+            install_user   => $opt{u},<br>
+            mirror         => $opt{m},<br>
+            start          => !$opt{n},<br>
+            template       => $opt{t},<br>
+        );<br>
+    }<br>
+    when ( 'destroy' ) {<br>
+        $app->check_valid_container($name);<br>
+<br>
+        my $input = ask("Are you sure you want to destroy '$name'?", 'n');<br>
+        die "Aborted\n" unless $input =~ m{^y}i;<br>
+<br>
+        $app->destroy(<br>
+            name => $name,<br>
+        );<br>
+    }<br>
+    when ( 'start' ) {<br>
+        $app->start(<br>
+            name => $name,<br>
+        );<br>
+    }<br>
+    when ( 'stop' ) {<br>
+        $app->stop(<br>
+            name => $name,<br>
+        );<br>
+    }<br>
+    when ( 'restart' ) {<br>
+        $app->restart(<br>
+            name => $name,<br>
+        );<br>
+    }<br>
+    when ( 'enter' ) {<br>
+        $app->enter(<br>
+            name => $name,<br>
+        );<br>
+    }<br>
+    when ( 'console' ) {<br>
+        $app->console(<br>
+            name => $name,<br>
+        );<br>
+    }<br>
+    when ( 'exec' ) {<br>
+        $app->enter(<br>
+            name    => $name,<br>
+            command => \@ARGV,<br>
+        );<br>
+    }<br>
+    when ( 'status' ) {<br>
+        $app->status(<br>
+            name => $name,<br>
+        );<br>
+    }<br>
+    when ( 'autostart' ) {<br>
+        $app->autostart;<br>
+    }<br>
+    when ( 'stopall' ) {<br>
+        $app->stopall;<br>
+    }<br>
+    default {<br>
+        die "No such command.\n\nTry $0 --help\n";<br>
+    }<br>
+}<br>
+<br>
+<br>
+__END__<br>
+<br>
+=head1 NAME<br>
+<br>
+lxc - Wrapper around lxc utils to make managing containers easier<br>
+<br>
+=head1 SYNOPSIS<br>
+<br>
+    lxc [name] [command]     # When operating on a container<br>
+    lxc [command]            # For some commands<br>
+<br>
+    Commands:<br>
+<br>
+     lxc [name] create [-u] --template=[lucid|maverick|etc...]<br>
+     lxc [name] destroy<br>
+     lxc [name] start|stop|restart<br>
+     lxc [name] enter<br>
+     lxc [name] exec command [args]<br>
+     lxc [name] console<br>
+     lxc [name] status<br>
+     lxc status<br>
+<br>
+=head1 DESCRIPTION<br>
+<br>
+C<lxc> wraps around the low-level commands for controlling linux containers, to<br>
+make it easier to manage containers for the common case - which is creating<br>
+containers that work in a similar fashion to vservers or jails.<br>
+<br>
+=head1 OPTIONS<br>
+<br>
+=over 4<br>
+<br>
+=item B<-h|--help><br>
+<br>
+Display this documentation.<br>
+<br>
+=item B<--version><br>
+<br>
+Display the version of this script, and the version of C<lxc> installed on your<br>
+system.<br>
+<br>
+=back<br>
+<br>
+=head1 COMMANDS<br>
+<br>
+=head2 lxc [name] create<br>
+<br>
+Creates a new container. Will also start it unless you pass C<-n>.<br>
+<br>
+You will probably want to use the template option to specify the distribution<br>
+your container should be.<br>
+<br>
+Take note of C<-u> - it can be useful if you want to set up a container for<br>
+developing software in.<br>
+<br>
+=head3 Options<br>
+<br>
+=over 4<br>
+<br>
+=item B<-a|--autostart><br>
+<br>
+Flag this container as one that should be automatically started on boot.<br>
+<br>
+=item B<-m|--mirror><br>
+<br>
+Specify an apt mirror to use inside the container (regretfully, not used for<br>
+downloading the container yet - upstream needs to offer this feature).<br>
+<br>
+=item B<-n|--no-start><br>
+<br>
+Don't start the container once created (the default is to start it).<br>
+<br>
+=item B<-t|--template><br>
+<br>
+Specify an LXC template to use to create the container with. This is passed<br>
+through to C<lxc-create>.<br>
+<br>
+=item B<-u|--user-from-host><br>
+<br>
+If you invoke C<create> via sudo and use this option, it will bind mount /home<br>
+into the container and create a user account for you.<br>
+<br>
+The user account will have the same password as your account on the host.<br>
+<br>
+This option is useful when you want to create a container for developing<br>
+software in. You can use your IDE/editor setup/VCS that you have already<br>
+configured on the host, and the bind mount means the container can see all your<br>
+code.<br>
+<br>
+=back<br>
+<br>
+=head2 lxc [name] destroy<br>
+<br>
+Destroys a container. You'll be asked to confirm first. This operation cannot<br>
+be undone!<br>
+<br>
+=head2 lxc [name] start<br>
+<br>
+Starts a container. It waits until networking is up in the container, which<br>
+means C<enter> will work.<br>
+<br>
+=head2 lxc [name] stop<br>
+<br>
+Gracefully shuts down a container (unlike the rather brutal L<lxc-stop><br>
+command).<br>
+<br>
+=head2 lxc [name] restart<br>
+<br>
+Stops a container, if it's running, then starts it.<br>
+<br>
+=head2 lxc [name] enter<br>
+<br>
+Gives you a shell inside the container.<br>
+<br>
+Under the hood, this is currently implemented with ssh, until kernels with<br>
+L<lxc-attach> support are widely available.<br>
+<br>
+=head2 lxc [name] exec command [args]<br>
+<br>
+Executes the command in the container.<br>
+<br>
+=head2 lxc [name] console<br>
+<br>
+Connects you to C<tty1> in the container.<br>
+<br>
+Note that you can only do this from one place at a time, however this command<br>
+is just a layer over L<lxc-console>, so you could get more if you wanted.<br>
+However, in most cases, you'll just want to use L<lxc [name] enter> instead.<br>
+<br>
+The one time this is useful is if networking is down inside the container.<br>
+<br>
+=head2 lxc [name] status<br>
+<br>
+Tells you the status of the container.<br>
+<br>
+Currently, this is limited to whether it's running or not. This is just a<br>
+wrapper around L<lxc-info>.<br>
+<br>
+=head2 lxc status<br>
+<br>
+Tells you the status of all containers.<br>
+<br>
+=head2 lxc autostart<br>
+<br>
+Starts all containers that have a file called 'autostart' in their lxc config<br>
+directory (usually /var/lib/lxc/[name]).<br>
+<br>
+This is most useful for starting containers automatically on boot.<br>
+<br>
+=head2 lxc stopall<br>
+<br>
+Stops all containers.<br>
+<br>
+This is most useful for stopping containers automatically on shutdown.<br>
+<br>
+=head1 AUTHOR<br>
+<br>
+Shoptime Software and others; see CREDITS<br>
+<br>
+=cut<br>
<font color="#888888">--<br>
1.7.1<br>
<br>
</font></blockquote></div><br><br clear="all"><br>-- <br>Regards,<br>Nigel McNie<br><a href="http://nigel.mcnie.name/" target="_blank">http://nigel.mcnie.name/</a> | +64 27 469 6038 | <a href="http://twitter.com/nigelmcnie" target="_blank">http://twitter.com/nigelmcnie</a> | <a href="http://about.me/nigel" target="_blank">http://about.me/nigel</a><br>

My latest blog post: <a href="http://nigel.mcnie.name/blog/fixing-website-performance-issues-server-resources" target="_blank">http://nigel.mcnie.name/blog/fixing-website-performance-issues-server-resources</a><br>
</div>