--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/_utmp.c Fri Mar 21 01:53:51 2008 +0200
@@ -0,0 +1,57 @@
+#include <Python.h>
+
+#include <utmp.h>
+
+#include <stdio.h>
+
+PyObject *parse (PyObject *self, PyObject *args) {
+ char *bytes;
+ size_t size;
+
+ struct utmp *item;
+
+ /* not unicode */
+ if (!PyArg_ParseTuple(args, "t#", &bytes, &size))
+ return NULL;
+
+ if (size != sizeof(struct utmp)) {
+ PyErr_SetString(PyExc_ValueError, "given buffer is of the wrong length");
+ return NULL;
+ }
+
+ item = (struct utmp *) bytes;
+
+ /* parse utmp from bytes to result */
+ return Py_BuildValue("hIs#s#s#s#(hh)i(ii)s#",
+ item->ut_type, item->ut_pid,
+ item->ut_line, sizeof(item->ut_line),
+ item->ut_id, sizeof(item->ut_id),
+ item->ut_user, sizeof(item->ut_user),
+ item->ut_host, sizeof(item->ut_host),
+ item->ut_exit.e_termination, item->ut_exit.e_exit,
+ item->ut_session,
+ item->ut_tv.tv_sec, item->ut_tv.tv_usec,
+ item->ut_addr_v6, sizeof(item->ut_addr_v6)
+ );
+}
+
+PyObject *size (PyObject *self, PyObject *args) {
+ /* return the size of an UTMP struct */
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ return PyInt_FromSsize_t(sizeof(struct utmp));
+}
+
+static PyMethodDef module_methods[] = {
+ {"parse", parse, METH_VARARGS, "parse a utmp struct from a byte string"},
+ {"size", size, METH_VARARGS, "return the size of an utmp record in bytes"},
+ {NULL}
+};
+
+PyMODINIT_FUNC init_utmp(void) {
+ PyObject *m;
+
+ m = Py_InitModule3("_utmp", module_methods, "utmp struct parsing");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/setup.py Fri Mar 21 01:53:51 2008 +0200
@@ -0,0 +1,10 @@
+from distutils.core import setup, Extension
+
+_utmp = Extension('_utmp', sources=['_utmp.c'])
+
+setup(
+ name = "FixBot",
+ version = "0.1",
+ description = "IRC bot for sysadmin things",
+ ext_modules = [_utmp],
+)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/utmp.py Fri Mar 21 01:53:51 2008 +0200
@@ -0,0 +1,50 @@
+import socket
+
+import _utmp
+
+class WtmpEntry (object) :
+ _ATTRS = (
+ "type", "pid", "line", "id", "user", "host", "exit", "session", "tv", "addr_v6"
+ )
+
+ def __str__ (self) :
+ return " ".join("%s=%s" % (key, getattr(self, key)) for key in self._ATTRS)
+
+def read_entry (file) :
+ bytes = file.read(_utmp.size())
+
+ if not bytes :
+ return
+
+ result = _utmp.parse(bytes)
+
+ wtmp = WtmpEntry()
+
+ for name, field in zip(wtmp._ATTRS, result) :
+ if isinstance(field, str) and name not in ("addr_v6", ) :
+ field = field.rstrip("\0")
+
+ setattr(wtmp, name, field)
+
+ wtmp.addr_v6 = socket.inet_ntop(socket.AF_INET6, wtmp.addr_v6)
+
+ return wtmp
+
+def read_entries (file) :
+ while True :
+ wtmp = read_entry(file)
+
+ if wtmp :
+ yield wtmp
+ else :
+ return
+
+def test_main () :
+ fh = open("/var/log/wtmp", "r")
+
+ for item in read_entries(fh) :
+ print item
+
+if __name__ == '__main__' :
+ test_main()
+