Managing system and user services with perp - December 2, 2013

Background

Recently I've been looking into various init alternatives to systemd, and came across a couple of service supervisors (for example perp and s6) that mostly do the job but are missing various bits like a script to do early system initialization that make them difficult to use as a drop-in replacement.

So I created a collection of stuff to make this easier. I focused on perp because seemed nice to use it was already packaged in arbor (thanks alip!). However, most of this should apply to other service supervisors as well.

Replacing init

Since perp was not designed to run as init (just to supervise services), I created an extremely simple init that spawns /etc/rc, which is expected to exec into a service supervisor. It also kills all processes, syncs and then reboots on USR1 and powers off on USR2. Under s6, this should be unnecessary and you should be able to set init=/etc/rc.

Installation

Here are some instructions for trying this out on Exherbo:

  1. Install supervisor-extras[perp] (this should pull in perp)
cave resolve supervisor-extras

NOTE: If you enable udev for supervisor-extras, make sure that you have configured perp to start udevd, otherwise rc.local will wait for /run/udev/control indefinitely.

  1. Create /etc/rc.conf from sample and modify to your liking.
cp /usr/share/doc/supervisor-extras-scm/rc.conf.sample.perp /etc/rc.conf
vim /etc/rc.conf
  1. If you want to perform any one-time system initialization, create a script in /etc/rc.local.d/. Remember to make it executable!

  2. Set init provider to supervisor-extras (or, alternatively, add init=/sbin/supervisor-extras.init).

eclectic init set supervisor-extras
  1. Make sure your kernel has automount devtmpfs support (CONFIG_DEVTMPFS and CONFIG_DEVTMPFS_MOUNT).

  2. You probably also want to set up a udevd service, which is available in my repository of services described below.

  3. Reboot and hope things work. If things don't work and you changed your init with eclectic, you can set init=/sbin/systemd.init to boot up with systemd.

You can check the status of services with perpls(8) and stop/start them with perpctl(8).

System Services

Currently, the only service provided by supervisor-extras is getty on tty1. If you'd like to enable getty on other TTY's, you can run cp -r /etc/perp/getty@tty1 /etc/perp/getty@ttyX (a symlink won't work because perp uses the sticky bit on these directories to mark whether they are enabled; the actual rc.main is located in /etc/perp/.config). You also must set the sticky bit (chmod +t) on service directories for perp to start them by default. To learn more about perp service directories, see perpetrate(8).

I have a collection of scripts in my perp-services repository for other packages (dhcpcd, udevd, sshd, dbus, wpa_supplicant), and if there is enough interest, we could add perp options to these packages to install them. There is a generic rc.log located in /etc/perp/.default/rc.log, which you can create a symlink to from <my-service>/rc.log.

User Services

Without systemd and a typical desktop environment, it takes a bit of work to get user services set up appropriately, especially since user services (like dbus and gnome-keyring-daemon) tend to get started automagically when they are needed (using temporary socket paths that aren't propagated through your entire environment). Since perp does not do any system initialization, it is well-suited to manage your user services as well.

supervisor-extras includes some scripts for starting user-service supervisors as well. To enable user services for user steve,

cd /etc/perp
mkdir user-services@steve
ln -s ../.user-services/rc.{main,log} user-services@steve/

This system-service will exec into ~steve/.services/.start/run, connected to the logger in ~steve/.services/.start/log.

Setting up your service directory

A good starting point skeleton for your service directory can be found in the user directory of perp-services.

cp -r perp-services/user ~/.services

Make sure to look through the service directory and remove any that you don't want, and enable the ones you do want (with chmod +t).

NOTE: Both gnome-keyring-daemon and pulseaudio depend upon dbus, so if you enable either of those, make sure dbus is enabled as well.

Additionally, you probably want to set up some environment variables. I put these in ~/.env so that I can use them in zsh (by sourcing it from ~/.zshenv), as well as perp service scripts (.services/.start/run sources it before it execs perpd).

# ~/.env

export PERP_BASE=~/.services
export XDG_RUNTIME_DIR=/run/user/${UID}

# dbus
export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/dbus/user_bus_socket"

# gnome-keyring-daemon
export GNOME_KEYRING_CONTROL="${XDG_RUNTIME_DIR}/keyring"
export SSH_AUTH_SOCK="${XDG_RUNTIME_DIR}/keyring/ssh"
export GPG_AGENT_INFO="${XDG_RUNTIME_DIR}/keyring/gpg:0:1"

As with the system services, there is a generic rc.log in .default/log. This log script starts ${LOG_CMD}, as set in /etc/rc.conf, using logging directories ~/.log/<service>. Create a symlink to it in your service directory to enable logging for that service.

Example Process Tree

Here is the process tree of my system after setting things up as described in this post:

init───perpboot─┬─perpd─┬─5*[agetty]
                │       ├─dbus-daemon
                │       ├─dhcpcd
                │       ├─login───zsh───swc-launch───velox─┬─X
                │       │                                  └─st-wl───zsh───pstree
                │       ├─perpd─┬─dbus-daemon
                │       │       ├─gnome-keyring-d───4*[{gnome-keyring-d}]
                │       │       ├─mpd───6*[{mpd}]
                │       │       ├─pulseaudio───2*[{pulseaudio}]
                │       │       └─2*[tinylog]
                │       ├─sshd
                │       ├─4*[tinylog]
                │       ├─udevd
                │       └─wpa_supplicant
                └─tinylog

Looks pretty neat and tidy to me!

Closing Thoughts

I hope this post was useful to you in some way. If you have any comments or questions, feel free to email me or comment below.

This is just one example of how to connect various bits into a robust and easy-to-use initialization and service management system. There are of course other ways that yield similar results, but this setup is working quite nicely for me :)

Velox version 0.0.3 - February 14, 2010

I thought it was about time for a new release of velox, so here is velox-0.0.3.

You can download velox-0.0.3 here: velox-0.0.3.tar.bz2.

What's New?

Features

Preliminary EWMH Support

Using Arnaud Fontaine's excellent XCB EWMH library, I started adding support for EWMH. This is still a work in progress and only a few properties are supported right now, but I will improve this over time.

Currently, the following EWMH atoms are supported:

  • _NET_SUPPORTED
  • _NET_CLIENT_LIST
  • _NET_DESKTOP_GEOMETRY
  • _NET_DESKTOP_VIEWPORT
  • _NET_SUPPORTING_WM_CHECK
  • _NET_WM_STRUT
  • _NET_WM_STRUT_PARTIAL

My current goal is to get EWMH to a point where external panel applications like fbpanel will run.

I am anxious for Arnaud to officially release his library so it is easier for others to install.

Alsa Mixer Module

I've added an alsa mixer module which adjusts volume through key bindings. The audio device and mixer are configurable via velox.conf. The default key bindings for this module are as follows:

  • XF86AudioMute: Mute the mixer
  • XF86AudioRaiseVolume: Raise the volume by 5%
  • XF86AudioLowerVolume: Lower the volume by 5%

Miscellaneous

I've also worked on several other features including:

  • A more flexible hooks system. This was very helpful for implementing some EWMH features because the properties need to be updated when certain things happen.
  • An xsession file so that display managers such as kdm give velox as an option when logging in.
  • Cleaning up printing/debug info, and made debug info optional
  • Layouts now arrange the windows on a given area of the screen. This allows the possibility for features such as overscan adjustment or side panels.

Bug Fixes

Better defaults

I decided that not everybody uses urxvt and mpd, or wants a wallpaper set from a folder in their home directory, so I have adjusted the default configuration files hoping that it will now work for a larger proportion of systems.

Miscellaneous

  • The MPD module now attempts to reconnect if the connection has an error.
  • Cleanup after a fatal error.
  • Free allocated colors when exiting.
  • Stop using deprecated names for atoms.
  • Add some comments

Velox version 0.0.2 - February 1, 2010

Today, I released version 0.0.2 of velox. Despite being only a couple of days away from 0.0.1, I have implemented some cool new features, and fixed some annoying bugs.

You can download it here: velox-0.0.2.tar.bz2.

What's New?

Features

I'll start off with features because they are more exciting.

New MPD Module

I added an mpd module which connects to an mpd daemon and binds the XF86Audio* keys to the appropriate functions in mpd.

Module Configurabity

All of the modules are now configurable! (Well, at least ones that make sense). They are configurable in the following ways:

  • layout_tile: The default state (master factor, master count, column count) for the tile layout.
  • spawn: All commands run by the spawn module are set in the configuration file.
  • wallpaper: The path to look for wallpapers.
  • mpd: The mpd daemon host, port, and timeout values.

Any module which wishes to be configurable can do so by implementing a configure function of one argument, a yaml_document_t *. The document that gets passed is retrieved from the documents following the first in velox.yaml.

Here is the configuration for an example module:

%YAML 1.1
---
# ...
# blah blah, all the usual stuff goes here
# ...
--- !velox:example_module # The subsequent documents should be tagged like
                          # this with the module name.
# Anything you want goes here. This will be the contents of the document
# passed to configure
...

Miscellaneous

There have also been numerous miscellaneous improvements.

  • Window border size is now configurable
  • Keybinding functions can now have arguments passed to them
  • Use a standard pointer for the default cursor. (Sorry Gumby!)
  • The default keybindings are now optimized for a qwerty keyboard. (I wanted to appeal to the masses :P)
  • Modules can now be enabled or disabled at runtime

Bug Fixes

  • Applications like gVim, now work correctly. The problem was due to the incorrect handling of configure request events.
  • Various memory leaks are now fixed. A quick run through valgrind showed me where I did not free xcb replies. Most of these are now corrected.
  • Keybindings which used XCB_MOD_MASK_ANY now behave correctly.

Introducing velox! - January 30, 2010

Introduction

For the past couple months, I have been working on a tiling window manager called velox. Velox aims to be a little more featureful than dwm, and slightly smaller than awesome. Like awesome, I am using the xcb library for communicating with the X server.

Name

Velox started out as mwm, but I soon found that this name was also used for the Motif Window Manager. I eventually settled on velox, a latin word meaning swift or rapid, which I hope to be a prominent feature of velox.

Goals

These are several key aspects that I tried to focus on during the development of velox.

No dependencies on external languages for extensions

Many related window managers turn to other languages to allow customization. However, this adds another layer of bloat and in my opinion can be avoided through the use of modules. Configuration in velox is strictly setting values. If you want to add any functionality you should do so through a module.

Easy to use and robust support for modules

I think this is where velox differs from most window managers I have looked at. Velox tries to use modules where appropriate for any functionality a user might want to modify. This includes things like layouts and starting applications.

Currently, velox comes with the following modules:

  • layout_tile: The standard tiling layout, similar to awesome, dwm, and xmonad.
  • layout_grid: A grid layout which tries to give each window an equal amount of space, similar to the one included in xmonad. This layout is essentially a tile layout without a master and an automatically adjusted number of columns.
  • spawn: A module which uses keybindings to run certain commands. Currently, these commands are not configurable, but I plan on adding that functionality in the near future.
  • wallpaper: A module which uses feh to set the wallpaper using a random image from a wallpaper directory.
  • mpd: This module establishes a connection with mpd, and binds the multimedia keys to perform their expected functionality.

Clean and consise source code

I've tried to keep velox's source very organized and consistent throughout its development. I hope that the source remains this way and velox itself will continue to include only core functionality.

Use xcb effectively

Throughout the source of velox, I have tried to use xcb effectively by making requests early, and checking replies as late as I could. This leads to increased efficiency because xcb is an asynchronous library.

Current Status

I released version 0.0.1 of velox yesterday, but I still feel it needs a bit of work before people start using it. I have been using velox as my window manager for well over a month now, and my biggest grievance is a little bit of quirky behavior which I hope to correct soon.

Velox currently has absolutely no support for multiple monitors and refuses to touch anything but the first one. I will be getting a new laptop soon, which I can use for testing a multiple monitor setup, but this is not one of my top priorities.

Floating window support for velox is limited at best. There is no way to manually move or resize floating windows at the moment. I hope to work on this is in the near future.

However, I really think that velox is starting to finally come together, and has the potential to become a nice alternative to other tiling window managers.

Sorting torrent files on the fly with rTorrent - December 28, 2009

In rTorrent, you can watch certain directories for torrents and load and start them automatically.

schedule = watch_directory,10,10,load_start_verbose=torrents/*.torrent

Now, this is pretty simple to do with pretty much any client, but we can take this even further by performing certain actions on a torrent depending on where its from. rTorrent 0.8.5 added several helpful commands including execute_capture which returns the output (stdout) of the specified command.

I wanted to sort my torrents based on the tracker announce url, so I wrote a ruby script which, when called with the name of a torrent file, returns the host of the url with the TLD stripped.

#!/usr/bin/ruby

require 'rubygems'

require 'bcodec'
require 'uri'

tracker_uri = URI.parse(BCodec.decode(open(ARGV[0], "r"))["announce"])
print tracker_uri.host.split(".")[-2]

One thing to be careful about is to make sure the command you run does not output a newline character, because execute_command will capture this as well.

I wanted rTorrent to set the download directory to the output of this script, so I saved the script to torrent-tracker.rb and put it in my $PATH, then used execute_capture.

schedule = watch_directory,10,10,"load_start_verbose=torrents/*.torrent,
    \"d.set_directory=
        \\\"$execute_capture={torrent-tracker.rb,$d.get_tied_to_file=}\\\"\""

After looking through the raw list of rTorrent commands, I thought I'd see what other neat things I could do. Until now, I hadn't experimented with rTorrent views. These allow you to display different groups of torrents, organized by whatever you want. This seemed ideal for showing torrents from a specific tracker so I thought I'd give it a try.

First, you need to tell rTorrent which views you want to use.

group.insert_persistent_view = tracker1
group.insert_persistent_view = tracker2
group.insert_persistent_view = tracker3

You can now automatically set torrents visible on certain views by adding an extra command to your directory watch schedule.

schedule = watch_directory,10,10,"load_start_verbose=torrents/*.torrent,
    \"d.set_directory=
        \\\"$execute_capture={torrent-tracker.rb,$d.get_tied_to_file=}\\\"\",
    \"d.set_custom=
        tracker,
        \\\"$execute_capture={torrent-tracker.rb,$d.get_tied_to_file=}\\\"\""

You can show certain views in rTorrent by running the command (Ctrl-X in rTorrent) ui.current_view.set. For example, if you wanted to only show torrents from tracker1, run ui.current_view.set=tracker1.

I also decided it was worth having a custom variable associated with each torrent whose value was the tracker the torrent was using. This too can be accomplished using new commands from rTorrent 0.8.5, d.set_custom and d.get_custom.

This can also be set automatically in the schedule command.

schedule = watch_directory,10,10,"load_start_verbose=torrents/*.torrent,
    \"d.set_directory=
        \\\"$execute_capture={torrent-tracker.rb,$d.get_tied_to_file=}\\\"\",
    \"d.set_custom=
        tracker,
        \\\"$execute_capture={torrent-tracker.rb,$d.get_tied_to_file=}\\\"\",
    \"view.set_visible=
        \\\"$execute_capture={torrent-tracker.rb,$d.get_tied_to_file=}\\\"\""

Now, the tracker field can be extracted from any torrent by running the command print=$d.get_custom=tracker. This custom variable can probably simplify things a bit in the schedule command, but I am fine with things the way they are now.

This opens the door to many other interesting ideas including sorting torrents by identifying the content through the file name (movie, software, tv, etc). I might experiment with this later on.

First post on my new blog - December 28, 2009

This is my first post on the blog I set up with jekyll. Jekyll is a powerful static site generator that makes it easy to maintain a website, and allows you to write content using simple markup languages rather than messing with html.

I learned of jekyll through Exherbo developer alip's blog. It seemed ideal for my needs, so I decided to convert my website for trickle from PHP and Texy! to jekyll. I was very impressed and interested in jekyll's blogging abilities, so I created this page.

Jekyll is very neat in that you can set up a git repository on a simple shared web host to generate the site each time it gets pushed to. I won't go into the details because they are available all over the internet, but it makes website maintenance very convenient.

I'm not really sure what's in store for this blog, but I figured some people might find the stuff I've been working on interesting, and it seemed like a good platform to organize and talk about my ideas.

Valid XHTML 1.0 StrictValid CSS!
Generated on Thu, 12 Dec 2013 20:28:12 UTC