Posts Tagged ‘UNIX’

Word Press post from shell

Thursday, June 28th, 2007

One of the most annoying things about this whole weblog business is having to write your posts in the browser. I can’t stand entering any more than a few sentences in a web browser text area. Not only because web browsers are so unstable, prone to crashing, or that its so easy to press the wrong key and kill the window or go back in the history and lose what you’ve written - but that you are forced to use the crummy browser text field component. I use Vi for most of my editing these days, and I have grown very fond of the movement keys (hjlk). Using these means I don’t have to move my hands from the keyboard to move the cursor, which reduces strain and increases speed. Of course I miss many other features, such as on-the-fly spell-checking (although Firefox 2+ has this), word completion, auto-save, auto-format, syntax highlighting, and bracket matching also.

I initially assumed that WordPress is too poorly thought-out to have something clever like an XML-RPC interface. Well, thankfully, I was wrong. WordPress in fact supports the Blogger, Moveable Type and a bunch of other XML-RPC interfaces. This meant I wasn’t going to have to screen-scrape and hack together some horrible and brittle HTTP client.

I do a fair bit of work in Python for my job, and I’ve come to rather like the language for whipping together smallish programs. Especially tasks involving processing files or URLS, Python particularly excels for these. While it would not be a big deal to use the Python XML-RPC library directly myself (its in the Python standard library since version 2.2), I figured I would use someone else’s code if at all possible. After a bit of searching, I found wordpresslib. This little .py file is available under the LGPL license and makes it trivial to perform a variety of Word Press operations from Python.

My rather trivial program simply allows you to submit a post in draft or published form from the shell. It accepts input either on STDIN or a specified file. You can also set a post title. For the hell of it, I’ve put it under a BSD license and packaged it up in a little tarball with wordress.py available for download here. Before using it, you need to set three variables in main.py - the URL of your Word Press install, your user and your password. Sample run:


$ echo this is a post | ./main.py -t 'a test post'

If you are seeing this post, that is proof that it works. There are many more features which could be implemented of course. Maybe I’ll bother maybe I won’t. I’ll see if I get the itch! Feel free to send me patches of course ;-)

UNIX, tell me if I can execute this file

Thursday, June 28th, 2007

A surprisingly complicated question on a UNIX system, in fact. UNIX files have three relevant execution bits. One for owner, group and other. To test if a file is executable at all, you can stat it, and simply test the mode bit-field - consider this short C program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <sys/stat.h>
#include <sys/types.h>
 
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
 
int
main(int argc, char **argv)
{
        struct stat sb;
 
        if (argc < 2)
                errx(1, "supply a filename");
 
        if (stat(argv[1], &sb) == -1)
                err(1, "stat %s", argv[1]);
 
        if (sb.st_mode 
            & (S_IXUSR|S_IXGRP|S_IXOTH))
                printf("%s is executable by "
                    "owner, group or other\n",
                     argv[1]);
        else
                printf("%s is not executable\n",
                    argv[1]);
 
        exit(0);
}

Now we run it against some very common UNIX files:


$ ./isexec $(which sh)
/bin/sh is executable by owner, group or other
$ ./isexec /etc/passwd
/etc/passwd is not executable

This still doesn’t tell us if the file is executable by our user. To find out, we must first find our relevant user ids and all our group memberships. We can do this quite nicely with the getresuid and getgroups functions. You may ask why I use getresuid() instead of the superficially simpler getuid() function. The answer is that the value getuid() returns can be ambiguous, getresuid() is much more explicit about what the real, effective and saved user IDs are. It makes code clearer and reduces the opportunity for subtle bugs. We add code like the following to the start of our program:

1
2
3
4
5
6
7
8
        gid_t mygroups[NGROUPS_MAX];
        uid_t r, e, s;
 
        if (getgroups(0, mygroups) == -1)
                err(1, "getgroups failure");
        if ((ngroups = getresuid(&r, &e, &s))
            == -1)
                err(1, "getresuid failure");

Now we have our uid and our group ids, we can test against the owner, group and mode bit-field of the file. If we are the owner, and the owner execute bit is not set, we may not execute it - even if the group or other execute bits are set in our favour. Same with group - if we are in the same group as the file, but group execute bit is not set, we may not execute it - unless we are the owner and the owner execute bit is set. This is may seem a little bit complicated to code at first. The important thing is to get the order right:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
        if (euid == sb.st_uid)
                if (sb.st_mode & S_IXUSR)
                        goto exec;
                else
                        goto noexec;
 
         for (j = 0; j < ngroups; j++)
                if (mygroups[j] == sb.st_gid)
                        if (sb.st_mode & S_IXGRP)
                                goto exec;
                        else
                                goto noexec;
         if (sb.st_mode & S_IXOTH)
                 goto exec;
         goto noexec;
exec:
         printf("file is executable by you\n");
         return (0);
noexec:
         printf("file is not executable by you\n");
         return (0);

The use of the goto keyword here in fact increases legibility, since it means you don’t have to have a whole bunch of ugly conditionals.

You may wonder why I bothered to write about this subject. Well, the answer is that I needed such an algorithm in order to add auto-completion to the cwm window manager. I committed the code to OpenBSD’s Xenocara tree a few days ago, so that now the exec dialog scans the default PATH, determines which files you may execute, and populates the execution menu with these values, such that you get auto-completion (aka type-ahead-find) when executing programs.