Skip to content
A scene from Ireland

Tools

Performance improvements for wiggle.

My "wiggle" program which applies a patch to a different version of the file needs to compute the shortest edit path between two texts, which is the smallest set of additions and deletions that must be made to one file so as to produce the other. From this edit path it is easy to produce a list of sections that are the same or are different, and wiggle uses this to work out what changes need to be made, and where to make them.

When the two files being compared are large - on the order of tens of thousands of words - this process can start taking a long time. I recently started experiencing such a case fairly often, so it was clearly time to optimize the code. I managed to find two optimizations that don't change the result at all, and two others that degraded the result on large files in exchange for substantial speed improvements.

Wiggle 1.0

About 11 years ago I started writing "wiggle". I have finally released version 1.0.

Wiggle is a tool for applying patches with conflicts. I do quite a bit of applying patches to a release different to the one they were created for. This often works, and often fails completely so that the task must be done completely by hand.

Wiggles and Diffs at LCA

My second talk at LCA2013 - the first one accepted - was on "wiggle", my tool for applying patches that don't apply. In the presentation I wanted to explain how "diff" works - as I then wanted to explain why one of the things that wiggle does is more complex that a simple "diff". For this I came up with a simple animation that I presented as a series of "impress" slides. Some suggested I make them into an animated "gif", so I did. And here it is (click for a higher-res version):

Animation of Diff algorithm

Among the useful feedback I got about wiggle:

  • UTF-8 support would be good. This only applies to the way it breaks strings into words. Currently it only understand ASCII
  • Detecting patterns of "replace A with B" and looking for unreplaced copies of "A" in the original might be useful.

The slides in LibreOffice format are here and the recording of the talk is here

Metad - a daemon for controlling daemons

As I progress in winding up my position at the Computing Support Group at cse.unsw I'm looking for programs that I wrote which might be more widely useful and trying to make them available to the open source community.

One such is 'metad' which you can find in my git repository at git://neil.brown.name/metad or http://neil.brown.name/git/?p=metad.

metad is a daemon for managing other daemons. It is a bit like inetd in that it starts programs and waits for them to complete. However it isn't just starting programs based on network activity - that is possible, but was a later addition.

The main purpose is to run all other little daemons one seems to need and to allow those daemons to be stopped and started remotely. It may not be very interesting on a single-user machine, but it is wonderful when administering a network

suidrun - for providing setuid to customers when you want to mount with nosuid

We have several thousand customers, mostly students. Many of them have no idea what a setuid bit is, and don't really need to know. This has lead to several hundred setuid or setgid files that really should be set-id. This may not actually be a real security threat (a setuid image file cannot do much) but there is the potential for a security problem.

Also, allowing setuid files means that someone with temporary elevated privileges can (a workstation left logged-on) can easily elevant them to permanent privileges. This can be alleviated by reguilar scanning, but for this you need a lisdt of allowed setuid programs, and if you have decided to have such a list, there are better ways than scanning.

So I have written a little program that can give setuid functionality to customers whose homedirectory is on a filesystem that is mounted with nosetuid. The program requires all setuid programs to be recorded in a control file - /etc/suidrun.rc. Providing such files pass some simple tests, they can be run as though the setuid bit were really working.

The program in available under the GPL from http://www.cse.unsw.edu.au/~neilb/source/suidrun/.

See the man-page for more details.

Second draft

Having thought some more, I now have different ideas. Not totally different - maybe just more refined.

The idea of a queuing engine is still central. It should do queueing of mail items and little else.

A given queuing engine has a directory tree where it stores mail messages and control data (described below). A single process handles the whole queue but (optionally) does little of the real work.

Messages enter the queue via SMTP or LMTP. The xMTP server is fairly minimal. It only handles simple authentication and doesn't do any interesting check on addresses or content. It just bundles it all up and sticks it in the queue. This server should, therefore, not be exposed to the "Open Internet", or at least it should be configured to require authentication before accepting anything.

The xMTP service can be contacted on a TCP port or a Unix domain socket. The queue manager may fork a separate process to handle each connection. If this were done some simple IPC (e.g. pipe) would need to be set up so that the queue manager can get the names of files containing new messages.

The queue manager disposes of messages using (once again) SMTP or LMTP. Each recipient address for a message gets tagged with a destination which is initially "New". The destination maps through a config file to an xMTP service. This maybe identified by a program (to be conversed with over a socketpair), a unix domain socket, or a TCP/IP socket (possibly on another host). Each destination can specify a number of servers and each server might have other configuration such as authentication mechanism and service type (e.g. SMTP or LMTP).

A service may accept or reject (temporarily or permanently) a message, or may request that it be delivered elsewhere. This last is done using a slight extension to LMTP. As well as "551 user not local, please try user@domain", we allow "551 @destination:user@domain" to allow the server to indication a new service to send the message to. The RCPT TO command can also result in a multi-line 551 response which lists an arbitrary large number of addresses to forward the mail to.

So, how would it all work?

Incoming mail would be handled by a separate Internet-facing SMTP server which understands TLS and SMTP/AUTH and SPF and performs a number of other ad-hoc checks on the incoming mail. It will open an xMTP connection to an address-verify service and verify each incoming address. It will also open a connection to the queueing service. Those addresses which pass, and the mail message, will be forwarded to the queuing engine. The mail message will have a "Received:" and possible "Received-SPF:" header added to the top.

If the xMTP server determines that the incoming mail is a submission (From a locally authenticated source), extra header modification may be performed in-line with local policy, and non-local addresses will be accepted.

/usr/sbin/sendmail will simply open a unix-domain socket to the queueing system, pass SCM_CREDENTIALS, and then send over the relevant information.... or maybe it should be to the incoming smtp server.

The "New" destination would normally be connected to a service which processes the recipient addresses and determines whether they are suitable for local deliver, remote delivery, or something else such as delivery to some special-purpose service. As such it would reject all addresses and so never get to see the message body. The queueing engine would requeue for the alternate service and try again. If a rejection without redirection was received, the address would be queued for the special "Error" destination which may result in the a bounce being generated.

A service might be MX-SMTP which performs MX lookup on domain in addresses and starts SMTP transaction to send mail to its destination. This would be a LMTP service so that it can report on the status of each address after transmission.

A service might a local delivery service which simply copies the mail item into the mailbox for each addressee.

Each service can be configured with an error-report services and time range and with a retry time-range. If a temporary failure is reported, the address is requeued for later and possible the message is delivered (with status ignored) to the error-report service.

queue format

The queue of messages would be stored in a directory tree. One subdirectorytree contains control messages, on for each item. Others contain the actual message which is never modified by the queue system.

The control messages list the filename of the actual message, the From and To addresses, and possibly other metadata. Each To address has a sequence number. When a To address is changed (e.g. by 551) a new line is added to the file with the same sequence number. Thus to find the set of current addresses, the file needs to be parsed from start to finish, and the last address with any sequence number is kept. Sequence numbers may be dotted numbers, so that if address 2 is replaced by two addresses they will be 2.1 and 2.2.

For each address there may also be a next-attempt time.

The queue manager reads through all messages and creates a list of the file names and the time when the file should next be processed. When that time comes, the queue manager will (optionally) for a child process which will read through the control message, determine which destination to try next and which addresses to pass, and will make the connection and try it out. Any results are appended to the control file. When the child finishes, the message will be re-parsed by the parent and either it will be deleted if it is finished with, or queued for a later time if that is appropriate.

reflections

With a system like this, it should be easy to distribute much of a mailsystem across several machines. It is also easy to distribute it across several authority domains (uids) so that compromises with be very limited in effect.

Providing that it isn't too hard to write a new LMTP server, it should be easy to add interesting functionality simply by writing and configuring a new services.

Some functionality that might be wanted included vacation messages, mailing lists, heuristic-mailaddress-matching.

It might also be useful to run a mailqueue engine for a single user. This would be appropriate for situation where a computer might be used by multiple users who have different mail accounts that need to be authenticated to. The per-user queue would be configured to send mail to a specific "smarthost" with suitable authentication. The queue manager would need to be run whenever the user logs in, or maybe all the time in the background. Currently this sort of functionality (queueing per-user mail for delivery when the internet is next connected) is included in various Mail User Agents. It would be nice if it could be a separate system.

First draft of thoughts

As well as obvious goals like simplicity and modularity, I would like to make sure it distributed well over multiple servers if there was a need for that.

One part of this would be to have totally separate queue for mail items at different stages of processing.

vpatch design notes

In my Linux kernel development work, I often apply patches to files that have changed since the patch was created. Sometimes these patches fail to apply for trivial reasons.

About a year ago I wrote a program called to help apply these patches, and it has been very useful. However the solution isn't perfect. The problem is that I cannot see what is going on.

When I apply a patch that fails, I would like to be able to see exactly why it failed, and what wiggle would do to fix it. But to do that I currently have to grovel around in various files. I would be nice if this was more automatic.

What I am envisaging is a new patch application tool. Let's call it vpatch.