[systemd-commits] 4 commits - Makefile.am configure.ac make-man-index.py src/python-systemd

Lennart Poettering lennart at kemper.freedesktop.org
Wed Sep 12 19:26:41 PDT 2012


 Makefile.am                    |   32 +++++++++
 configure.ac                   |   27 ++++++--
 make-man-index.py              |   18 +++++
 src/python-systemd/Makefile    |    1 
 src/python-systemd/__init__.py |   18 +++++
 src/python-systemd/_journal.c  |  136 +++++++++++++++++++++++++++++++++++++++++
 src/python-systemd/journal.py  |  116 ++++++++++++++++++++++++++++++++++
 7 files changed, 343 insertions(+), 5 deletions(-)

New commits:
commit c4164442defa56dfa92a6b0fa5d49d8876e0ebb7
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Sep 13 04:25:59 2012 +0200

    python: fix error handling, and allocate argument array on the stack

diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c
index c305b77..eab9c29 100644
--- a/src/python-systemd/_journal.c
+++ b/src/python-systemd/_journal.c
@@ -21,6 +21,8 @@
 
 #include <Python.h>
 
+#include <alloca.h>
+
 #define SD_JOURNAL_SUPPRESS_LOCATION
 #include <systemd/sd-journal.h>
 
@@ -36,20 +38,13 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) {
         PyObject *ret = NULL;
         PyObject **encoded;
 
+        /* Allocate an array for the argument strings */
         argc = PyTuple_Size(args);
-
-        encoded = calloc(argc, sizeof(PyObject*));
-        if (!encoded) {
-                ret = PyErr_NoMemory();
-                goto out1;
-        }
+        encoded = alloca(argc * sizeof(PyObject*));
+        memset(encoded, 0, argc * sizeof(PyObject*));
 
         /* Allocate sufficient iovector space for the arguments. */
-        iov = malloc(argc * sizeof(struct iovec));
-        if (!iov) {
-                ret = PyErr_NoMemory();
-                goto out;
-        }
+        iov = alloca(argc * sizeof(struct iovec));
 
         /* Iterate through the Python arguments and fill the iovector. */
         for (i = 0; i < argc; ++i) {
@@ -70,17 +65,11 @@ static PyObject *journal_sendv(PyObject *self, PyObject *args) {
                 iov[i].iov_len = length;
         }
 
-        /* Clear errno, because sd_journal_sendv will not set it by
-           itself, unless an error occurs in one of the system calls. */
-        errno = 0;
-
         /* Send the iovector to the journal. */
         r = sd_journal_sendv(iov, argc);
-        if (r) {
-                if (errno)
-                        PyErr_SetFromErrno(PyExc_IOError);
-                else
-                        PyErr_SetString(PyExc_ValueError, "invalid message format");
+        if (r < 0) {
+                errno = -r;
+                PyErr_SetFromErrno(PyExc_IOError);
                 goto out;
         }
 
@@ -92,13 +81,6 @@ out:
         for (i = 0; i < argc; ++i)
                 Py_XDECREF(encoded[i]);
 
-        free(encoded);
-
-out1:
-        /* Free the iovector. The actual strings
-           are already managed by Python. */
-        free(iov);
-
         return ret;
 }
 
@@ -117,8 +99,10 @@ static PyObject* journal_stream_fd(PyObject *self, PyObject *args) {
                 return NULL;
 
         fd = sd_journal_stream_fd(identifier, priority, level_prefix);
-        if (fd < 0)
+        if (fd < 0) {
+                errno = -fd;
                 return PyErr_SetFromErrno(PyExc_IOError);
+        }
 
         return PyLong_FromLong(fd);
 }

commit 0aee68ad028e696934367045e652a65865c0de52
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Sep 13 04:16:10 2012 +0200

    python: reindent to follow coding style

diff --git a/Makefile.am b/Makefile.am
index be97193..8885ab8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3419,6 +3419,7 @@ _journal_la_SOURCES = \
 _journal_la_CFLAGS = \
 	$(AM_CFLAGS) \
         -fvisibility=default \
+	-Wno-missing-prototypes \
 	$(PYTHON_CFLAGS)
 
 _journal_la_LDFLAGS = \
diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c
index 64310a7..c305b77 100644
--- a/src/python-systemd/_journal.c
+++ b/src/python-systemd/_journal.c
@@ -24,135 +24,129 @@
 #define SD_JOURNAL_SUPPRESS_LOCATION
 #include <systemd/sd-journal.h>
 
-#include "macro.h"
-
 PyDoc_STRVAR(journal_sendv__doc__,
              "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n"
              "Send an entry to the journal."
-             );
-
-static PyObject *
-journal_sendv(PyObject *self, PyObject *args) {
-    struct iovec *iov = NULL;
-    int argc = PyTuple_Size(args);
-    int i, r;
-    PyObject *ret = NULL;
-
-    PyObject **encoded = calloc(argc, sizeof(PyObject*));
-    if (!encoded) {
-        ret = PyErr_NoMemory();
-        goto out1;
-    }
-
-    // Allocate sufficient iovector space for the arguments.
-    iov = malloc(argc * sizeof(struct iovec));
-    if (!iov) {
-        ret = PyErr_NoMemory();
-        goto out;
-    }
-
-    // Iterate through the Python arguments and fill the iovector.
-    for (i = 0; i < argc; ++i) {
-        PyObject *item = PyTuple_GetItem(args, i);
-        char *stritem;
-        Py_ssize_t length;
-
-        if (PyUnicode_Check(item)) {
-            encoded[i] = PyUnicode_AsEncodedString(item, "utf-8", "strict");
-            if (encoded[i] == NULL)
-                goto out;
-            item = encoded[i];
-        }
-        if (PyBytes_AsStringAndSize(item, &stritem, &length))
-            goto out;
+);
 
-        iov[i].iov_base = stritem;
-        iov[i].iov_len = length;
-    }
+static PyObject *journal_sendv(PyObject *self, PyObject *args) {
+        struct iovec *iov = NULL;
+        int argc;
+        int i, r;
+        PyObject *ret = NULL;
+        PyObject **encoded;
 
-    // Clear errno, because sd_journal_sendv will not set it by
-    // itself, unless an error occurs in one of the system calls.
-    errno = 0;
+        argc = PyTuple_Size(args);
 
-    // Send the iovector to the journal.
-    r = sd_journal_sendv(iov, argc);
+        encoded = calloc(argc, sizeof(PyObject*));
+        if (!encoded) {
+                ret = PyErr_NoMemory();
+                goto out1;
+        }
 
-    if (r) {
-        if (errno)
-            PyErr_SetFromErrno(PyExc_IOError);
-        else
-            PyErr_SetString(PyExc_ValueError, "invalid message format");
-        goto out;
-    }
+        /* Allocate sufficient iovector space for the arguments. */
+        iov = malloc(argc * sizeof(struct iovec));
+        if (!iov) {
+                ret = PyErr_NoMemory();
+                goto out;
+        }
 
-    // End with success.
-    Py_INCREF(Py_None);
-    ret = Py_None;
+        /* Iterate through the Python arguments and fill the iovector. */
+        for (i = 0; i < argc; ++i) {
+                PyObject *item = PyTuple_GetItem(args, i);
+                char *stritem;
+                Py_ssize_t length;
+
+                if (PyUnicode_Check(item)) {
+                        encoded[i] = PyUnicode_AsEncodedString(item, "utf-8", "strict");
+                        if (encoded[i] == NULL)
+                                goto out;
+                        item = encoded[i];
+                }
+                if (PyBytes_AsStringAndSize(item, &stritem, &length))
+                        goto out;
+
+                iov[i].iov_base = stritem;
+                iov[i].iov_len = length;
+        }
+
+        /* Clear errno, because sd_journal_sendv will not set it by
+           itself, unless an error occurs in one of the system calls. */
+        errno = 0;
+
+        /* Send the iovector to the journal. */
+        r = sd_journal_sendv(iov, argc);
+        if (r) {
+                if (errno)
+                        PyErr_SetFromErrno(PyExc_IOError);
+                else
+                        PyErr_SetString(PyExc_ValueError, "invalid message format");
+                goto out;
+        }
+
+        /* End with success. */
+        Py_INCREF(Py_None);
+        ret = Py_None;
 
 out:
-    for (i = 0; i < argc; ++i)
-        Py_XDECREF(encoded[i]);
+        for (i = 0; i < argc; ++i)
+                Py_XDECREF(encoded[i]);
 
-    free(encoded);
+        free(encoded);
 
 out1:
-    // Free the iovector. The actual strings
-    // are already managed by Python.
-    free(iov);
+        /* Free the iovector. The actual strings
+           are already managed by Python. */
+        free(iov);
 
-    return ret;
+        return ret;
 }
 
 PyDoc_STRVAR(journal_stream_fd__doc__,
              "stream_fd(identifier, priority, level_prefix) -> fd\n\n"
              "Open a stream to journal by calling sd_journal_stream_fd(3)."
-             );
-
-static PyObject*
-journal_stream_fd(PyObject *self, PyObject *args) {
-    const char* identifier;
-    int priority, level_prefix;
-    int fd;
-    if (!PyArg_ParseTuple(args, "sii:stream_fd",
-                          &identifier, &priority, &level_prefix))
-        return NULL;
-
-    fd = sd_journal_stream_fd(identifier, priority, level_prefix);
-    if (fd < 0)
-        return PyErr_SetFromErrno(PyExc_IOError);
-
-    return PyLong_FromLong(fd);
+);
+
+static PyObject* journal_stream_fd(PyObject *self, PyObject *args) {
+        const char* identifier;
+        int priority, level_prefix;
+        int fd;
+
+        if (!PyArg_ParseTuple(args, "sii:stream_fd",
+                              &identifier, &priority, &level_prefix))
+                return NULL;
+
+        fd = sd_journal_stream_fd(identifier, priority, level_prefix);
+        if (fd < 0)
+                return PyErr_SetFromErrno(PyExc_IOError);
+
+        return PyLong_FromLong(fd);
 }
 
 static PyMethodDef methods[] = {
-    {"sendv",  journal_sendv, METH_VARARGS, journal_sendv__doc__},
-    {"stream_fd", journal_stream_fd, METH_VARARGS,
-     journal_stream_fd__doc__},
-    {NULL, NULL, 0, NULL}        /* Sentinel */
+        { "sendv",  journal_sendv, METH_VARARGS, journal_sendv__doc__ },
+        { "stream_fd", journal_stream_fd, METH_VARARGS, journal_stream_fd__doc__ },
+        { NULL, NULL, 0, NULL }        /* Sentinel */
 };
 
 #if PY_MAJOR_VERSION < 3
 
-PyMODINIT_FUNC
-init_journal(void)
-{
-    (void) Py_InitModule("_journal", methods);
+PyMODINIT_FUNC init_journal(void) {
+        (void) Py_InitModule("_journal", methods);
 }
 
 #else
 
 static struct PyModuleDef module = {
-    PyModuleDef_HEAD_INIT,
-    "_journal", /* name of module */
-    NULL, /* module documentation, may be NULL */
-    0, /* size of per-interpreter state of the module */
-    methods
+        PyModuleDef_HEAD_INIT,
+        "_journal", /* name of module */
+        NULL, /* module documentation, may be NULL */
+        0, /* size of per-interpreter state of the module */
+        methods
 };
 
-PyMODINIT_FUNC
-PyInit__journal(void)
-{
-    return PyModule_Create(&module);
+PyMODINIT_FUNC PyInit__journal(void) {
+        return PyModule_Create(&module);
 }
 
 #endif
diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py
index 0f8a330..760d2db 100644
--- a/src/python-systemd/journal.py
+++ b/src/python-systemd/journal.py
@@ -24,89 +24,93 @@ from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
 from ._journal import sendv, stream_fd
 
 def _make_line(field, value):
-    if isinstance(value, bytes):
-        return field.encode('utf-8') + b'=' + value
-    else:
-        return field + '=' + value
+        if isinstance(value, bytes):
+                return field.encode('utf-8') + b'=' + value
+        else:
+                return field + '=' + value
 
 def send(MESSAGE, MESSAGE_ID=None,
          CODE_FILE=None, CODE_LINE=None, CODE_FUNC=None,
          **kwargs):
-    r"""Send a message to journald.
+        r"""Send a message to journald.
 
-    >>> journal.send('Hello world')
-    >>> journal.send('Hello, again, world', FIELD2='Greetings!')
-    >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef')
+        >>> journal.send('Hello world')
+        >>> journal.send('Hello, again, world', FIELD2='Greetings!')
+        >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef')
 
-    Value of the MESSAGE argument will be used for the MESSAGE= field.
+        Value of the MESSAGE argument will be used for the MESSAGE=
+        field.
 
-    MESSAGE_ID can be given to uniquely identify the type of message.
+        MESSAGE_ID can be given to uniquely identify the type of
+        message.
 
-    Other parts of the message can be specified as keyword arguments.
+        Other parts of the message can be specified as keyword
+        arguments.
 
-    Both MESSAGE and MESSAGE_ID, if present, must be strings, and will
-    be sent as UTF-8 to journal. Other arguments can be bytes, in
-    which case they will be sent as-is to journal.
+        Both MESSAGE and MESSAGE_ID, if present, must be strings, and
+        will be sent as UTF-8 to journal. Other arguments can be
+        bytes, in which case they will be sent as-is to journal.
 
-    CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to identify
-    the caller. Unless at least on of the three is given, values are
-    extracted from the stack frame of the caller of send(). CODE_FILE
-    and CODE_FUNC must be strings, CODE_LINE must be an integer.
+        CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to
+        identify the caller. Unless at least on of the three is given,
+        values are extracted from the stack frame of the caller of
+        send(). CODE_FILE and CODE_FUNC must be strings, CODE_LINE
+        must be an integer.
 
-    Other useful fields include PRIORITY, SYSLOG_FACILITY,
-    SYSLOG_IDENTIFIER, SYSLOG_PID.
-    """
+        Other useful fields include PRIORITY, SYSLOG_FACILITY,
+        SYSLOG_IDENTIFIER, SYSLOG_PID.
+        """
 
-    args = ['MESSAGE=' + MESSAGE]
+        args = ['MESSAGE=' + MESSAGE]
 
-    if MESSAGE_ID is not None:
-        args.append('MESSAGE_ID=' + MESSAGE_ID)
+        if MESSAGE_ID is not None:
+                args.append('MESSAGE_ID=' + MESSAGE_ID)
 
-    if CODE_LINE == CODE_FILE == CODE_FUNC == None:
-        CODE_FILE, CODE_LINE, CODE_FUNC = \
-            _traceback.extract_stack(limit=2)[0][:3]
-    if CODE_FILE is not None:
-        args.append('CODE_FILE=' + CODE_FILE)
-    if CODE_LINE is not None:
-        args.append('CODE_LINE={:d}'.format(CODE_LINE))
-    if CODE_FUNC is not None:
-        args.append('CODE_FUNC=' + CODE_FUNC)
+        if CODE_LINE == CODE_FILE == CODE_FUNC == None:
+                CODE_FILE, CODE_LINE, CODE_FUNC = \
+                        _traceback.extract_stack(limit=2)[0][:3]
+        if CODE_FILE is not None:
+                args.append('CODE_FILE=' + CODE_FILE)
+        if CODE_LINE is not None:
+                args.append('CODE_LINE={:d}'.format(CODE_LINE))
+        if CODE_FUNC is not None:
+                args.append('CODE_FUNC=' + CODE_FUNC)
 
-    args.extend(_make_line(key, val) for key, val in kwargs.items())
-    return sendv(*args)
+        args.extend(_make_line(key, val) for key, val in kwargs.items())
+        return sendv(*args)
 
 def stream(identifier, priority=LOG_DEBUG, level_prefix=False):
-    r"""Return a file object wrapping a stream to journal.
+        r"""Return a file object wrapping a stream to journal.
 
-    Log messages written to this file as simple newline sepearted
-    text strings are written to the journal.
+        Log messages written to this file as simple newline sepearted
+        text strings are written to the journal.
 
-    The file will be line buffered, so messages are actually sent
-    after a newline character is written.
+        The file will be line buffered, so messages are actually sent
+        after a newline character is written.
 
-    >>> stream = journal.stream('myapp')
-    >>> stream
-    <open file '<fdopen>', mode 'w' at 0x...>
-    >>> stream.write('message...\n')
+        >>> stream = journal.stream('myapp')
+        >>> stream
+        <open file '<fdopen>', mode 'w' at 0x...>
+        >>> stream.write('message...\n')
 
-    will produce the following message in the journal:
+        will produce the following message in the journal:
 
-    PRIORITY=7
-    SYSLOG_IDENTIFIER=myapp
-    MESSAGE=message...
+        PRIORITY=7
+        SYSLOG_IDENTIFIER=myapp
+        MESSAGE=message...
 
-    Using the interface with print might be more convinient:
+        Using the interface with print might be more convinient:
 
-    >>> from __future__ import print_function
-    >>> print('message...', file=stream)
+        >>> from __future__ import print_function
+        >>> print('message...', file=stream)
 
-    priority is the syslog priority, one of LOG_EMERG, LOG_ALERT,
-    LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG.
+        priority is the syslog priority, one of LOG_EMERG, LOG_ALERT,
+        LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG.
 
-    level_prefix is a boolean. If true, kernel-style log priority
-    level prefixes (such as '<1>') are interpreted. See sd-daemon(3)
-    for more information.
-    """
+        level_prefix is a boolean. If true, kernel-style log priority
+        level prefixes (such as '<1>') are interpreted. See
+        sd-daemon(3) for more information.
+        """
 
-    fd = stream_fd(identifier, priority, level_prefix)
-    return _os.fdopen(fd, 'w', 1)
+        fd = stream_fd(identifier, priority, level_prefix)
+        return _os.fdopen(fd, 'w', 1)

commit 6b91ae13f2b0792a21603861672594ed9bb9cd41
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Sep 13 04:05:28 2012 +0200

    python: change license to LGPL 2.1
    
    The original license has been MIT for this code, but David Strauss (its
    original author) agreed to relicense it to LGPL 2.1 for inclusion in
    systemd.

diff --git a/make-man-index.py b/make-man-index.py
index 8789d99..56f38ce 100755
--- a/make-man-index.py
+++ b/make-man-index.py
@@ -1,4 +1,22 @@
 #!/usr/bin/env python
+#  -*- Mode: python; indent-tabs-mode: nil -*- */
+#
+#  This file is part of systemd.
+#
+#  Copyright 2012 Lennart Poettering
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 
 from xml.etree.ElementTree import parse, Element, SubElement, tostring
 from sys import argv, stdout
diff --git a/src/python-systemd/__init__.py b/src/python-systemd/__init__.py
index e69de29..0d56b99 100644
--- a/src/python-systemd/__init__.py
+++ b/src/python-systemd/__init__.py
@@ -0,0 +1,18 @@
+#  -*- Mode: python; indent-tabs-mode: nil -*- */
+#
+#  This file is part of systemd.
+#
+#  Copyright 2012 David Strauss
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c
index 3e10981..64310a7 100644
--- a/src/python-systemd/_journal.c
+++ b/src/python-systemd/_journal.c
@@ -1,3 +1,24 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2012 David Strauss
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
 #include <Python.h>
 
 #define SD_JOURNAL_SUPPRESS_LOCATION
diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py
index 53e992b..0f8a330 100644
--- a/src/python-systemd/journal.py
+++ b/src/python-systemd/journal.py
@@ -1,3 +1,22 @@
+#  -*- Mode: python; indent-tabs-mode: nil -*- */
+#
+#  This file is part of systemd.
+#
+#  Copyright 2012 David Strauss
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+#  systemd is distributed in the hope that it will be useful, but
+#  WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public License
+#  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+
 import traceback as _traceback
 import os as _os
 from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,

commit 8d7e170a5230753d8406276f8b5598e5bb6766e6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Sep 13 04:01:18 2012 +0200

    python: integrate David Strauss' python-systemd package

diff --git a/Makefile.am b/Makefile.am
index 730db1d..be97193 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3408,6 +3408,37 @@ EXTRA_DIST += \
 
 # ------------------------------------------------------------------------------
 
+if HAVE_PYTHON_DEVEL
+
+pkgpyexec_LTLIBRARIES = \
+	_journal.la
+
+_journal_la_SOURCES = \
+	src/python-systemd/_journal.c
+
+_journal_la_CFLAGS = \
+	$(AM_CFLAGS) \
+        -fvisibility=default \
+	$(PYTHON_CFLAGS)
+
+_journal_la_LDFLAGS = \
+	$(AM_LDFLAGS) \
+	-shared \
+	-module \
+	-avoid-version
+
+_journal_la_LIBADD = \
+	$(PYTHON_LIBS) \
+	libsystemd-journal.la
+
+dist_pkgpyexec_PYTHON = \
+	src/python-systemd/journal.py \
+	src/python-systemd/__init__.py
+
+endif
+
+# ------------------------------------------------------------------------------
+
 SED_PROCESS = \
 	$(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
 	$(SED)	-e 's, at rootlibexecdir\@,$(rootlibexecdir),g' \
diff --git a/configure.ac b/configure.ac
index b132499..da55021 100644
--- a/configure.ac
+++ b/configure.ac
@@ -80,16 +80,31 @@ m4_ifdef([GOBJECT_INTROSPECTION_CHECK], [
 GOBJECT_INTROSPECTION_CHECK([1.31.1])
 ], [AM_CONDITIONAL([HAVE_INTROSPECTION], [false])])
 
-AC_CHECK_TOOL(OBJCOPY, objcopy)
-AC_CHECK_TOOL(STRINGS, strings)
-AC_CHECK_TOOL(GPERF, gperf)
+AC_PATH_TOOL(OBJCOPY, objcopy)
+AC_PATH_TOOL(STRINGS, strings)
+AC_PATH_TOOL(GPERF, gperf)
 if test -z "$GPERF" ; then
         AC_MSG_ERROR([*** gperf not found])
 fi
 
-# we use python only to build the man page index
+# we use python to build the man page index, and for systemd-python
+have_python=no
+have_python_devel=no
 AM_PATH_PYTHON(,, [:])
-AM_CONDITIONAL([HAVE_PYTHON], [test "$PYTHON" != :])
+if test "$PYTHON" != : ; then
+        have_python=yes
+        AC_PATH_PROG([PYTHON_CONFIG], python-config)
+
+        if test -n "$PYTHON_CONFIG" ; then
+                have_python_devel=yes
+                PYTHON_CFLAGS="`$PYTHON_CONFIG --cflags`"
+                PYTHON_LIBS="`$PYTHON_CONFIG --libs`"
+                AC_SUBST(PYTHON_CFLAGS)
+                AC_SUBST(PYTHON_LIBS)
+        fi
+fi
+AM_CONDITIONAL([HAVE_PYTHON], [test "$have_python" = "yes"])
+AM_CONDITIONAL([HAVE_PYTHON_DEVEL], [test "$have_python_devel" = "yes"])
 
 CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [\
         -pipe \
@@ -803,6 +818,8 @@ AC_MSG_RESULT([
         gudev:                   ${enable_gudev}
         gintrospection:          ${enable_introspection}
         keymap:                  ${enable_keymap}
+        Python:                  ${have_python}
+        Python Headers:          ${have_python_devel}
 
         prefix:                  ${prefix}
         rootprefix:              ${with_rootprefix}
diff --git a/src/python-systemd/Makefile b/src/python-systemd/Makefile
new file mode 120000
index 0000000..d0b0e8e
--- /dev/null
+++ b/src/python-systemd/Makefile
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
diff --git a/src/python-systemd/__init__.py b/src/python-systemd/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/python-systemd/_journal.c b/src/python-systemd/_journal.c
new file mode 100644
index 0000000..3e10981
--- /dev/null
+++ b/src/python-systemd/_journal.c
@@ -0,0 +1,137 @@
+#include <Python.h>
+
+#define SD_JOURNAL_SUPPRESS_LOCATION
+#include <systemd/sd-journal.h>
+
+#include "macro.h"
+
+PyDoc_STRVAR(journal_sendv__doc__,
+             "sendv('FIELD=value', 'FIELD=value', ...) -> None\n\n"
+             "Send an entry to the journal."
+             );
+
+static PyObject *
+journal_sendv(PyObject *self, PyObject *args) {
+    struct iovec *iov = NULL;
+    int argc = PyTuple_Size(args);
+    int i, r;
+    PyObject *ret = NULL;
+
+    PyObject **encoded = calloc(argc, sizeof(PyObject*));
+    if (!encoded) {
+        ret = PyErr_NoMemory();
+        goto out1;
+    }
+
+    // Allocate sufficient iovector space for the arguments.
+    iov = malloc(argc * sizeof(struct iovec));
+    if (!iov) {
+        ret = PyErr_NoMemory();
+        goto out;
+    }
+
+    // Iterate through the Python arguments and fill the iovector.
+    for (i = 0; i < argc; ++i) {
+        PyObject *item = PyTuple_GetItem(args, i);
+        char *stritem;
+        Py_ssize_t length;
+
+        if (PyUnicode_Check(item)) {
+            encoded[i] = PyUnicode_AsEncodedString(item, "utf-8", "strict");
+            if (encoded[i] == NULL)
+                goto out;
+            item = encoded[i];
+        }
+        if (PyBytes_AsStringAndSize(item, &stritem, &length))
+            goto out;
+
+        iov[i].iov_base = stritem;
+        iov[i].iov_len = length;
+    }
+
+    // Clear errno, because sd_journal_sendv will not set it by
+    // itself, unless an error occurs in one of the system calls.
+    errno = 0;
+
+    // Send the iovector to the journal.
+    r = sd_journal_sendv(iov, argc);
+
+    if (r) {
+        if (errno)
+            PyErr_SetFromErrno(PyExc_IOError);
+        else
+            PyErr_SetString(PyExc_ValueError, "invalid message format");
+        goto out;
+    }
+
+    // End with success.
+    Py_INCREF(Py_None);
+    ret = Py_None;
+
+out:
+    for (i = 0; i < argc; ++i)
+        Py_XDECREF(encoded[i]);
+
+    free(encoded);
+
+out1:
+    // Free the iovector. The actual strings
+    // are already managed by Python.
+    free(iov);
+
+    return ret;
+}
+
+PyDoc_STRVAR(journal_stream_fd__doc__,
+             "stream_fd(identifier, priority, level_prefix) -> fd\n\n"
+             "Open a stream to journal by calling sd_journal_stream_fd(3)."
+             );
+
+static PyObject*
+journal_stream_fd(PyObject *self, PyObject *args) {
+    const char* identifier;
+    int priority, level_prefix;
+    int fd;
+    if (!PyArg_ParseTuple(args, "sii:stream_fd",
+                          &identifier, &priority, &level_prefix))
+        return NULL;
+
+    fd = sd_journal_stream_fd(identifier, priority, level_prefix);
+    if (fd < 0)
+        return PyErr_SetFromErrno(PyExc_IOError);
+
+    return PyLong_FromLong(fd);
+}
+
+static PyMethodDef methods[] = {
+    {"sendv",  journal_sendv, METH_VARARGS, journal_sendv__doc__},
+    {"stream_fd", journal_stream_fd, METH_VARARGS,
+     journal_stream_fd__doc__},
+    {NULL, NULL, 0, NULL}        /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION < 3
+
+PyMODINIT_FUNC
+init_journal(void)
+{
+    (void) Py_InitModule("_journal", methods);
+}
+
+#else
+
+static struct PyModuleDef module = {
+    PyModuleDef_HEAD_INIT,
+    "_journal", /* name of module */
+    NULL, /* module documentation, may be NULL */
+    0, /* size of per-interpreter state of the module */
+    methods
+};
+
+PyMODINIT_FUNC
+PyInit__journal(void)
+{
+    return PyModule_Create(&module);
+}
+
+#endif
diff --git a/src/python-systemd/journal.py b/src/python-systemd/journal.py
new file mode 100644
index 0000000..53e992b
--- /dev/null
+++ b/src/python-systemd/journal.py
@@ -0,0 +1,93 @@
+import traceback as _traceback
+import os as _os
+from syslog import (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
+                    LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG)
+from ._journal import sendv, stream_fd
+
+def _make_line(field, value):
+    if isinstance(value, bytes):
+        return field.encode('utf-8') + b'=' + value
+    else:
+        return field + '=' + value
+
+def send(MESSAGE, MESSAGE_ID=None,
+         CODE_FILE=None, CODE_LINE=None, CODE_FUNC=None,
+         **kwargs):
+    r"""Send a message to journald.
+
+    >>> journal.send('Hello world')
+    >>> journal.send('Hello, again, world', FIELD2='Greetings!')
+    >>> journal.send('Binary message', BINARY=b'\xde\xad\xbe\xef')
+
+    Value of the MESSAGE argument will be used for the MESSAGE= field.
+
+    MESSAGE_ID can be given to uniquely identify the type of message.
+
+    Other parts of the message can be specified as keyword arguments.
+
+    Both MESSAGE and MESSAGE_ID, if present, must be strings, and will
+    be sent as UTF-8 to journal. Other arguments can be bytes, in
+    which case they will be sent as-is to journal.
+
+    CODE_LINE, CODE_FILE, and CODE_FUNC can be specified to identify
+    the caller. Unless at least on of the three is given, values are
+    extracted from the stack frame of the caller of send(). CODE_FILE
+    and CODE_FUNC must be strings, CODE_LINE must be an integer.
+
+    Other useful fields include PRIORITY, SYSLOG_FACILITY,
+    SYSLOG_IDENTIFIER, SYSLOG_PID.
+    """
+
+    args = ['MESSAGE=' + MESSAGE]
+
+    if MESSAGE_ID is not None:
+        args.append('MESSAGE_ID=' + MESSAGE_ID)
+
+    if CODE_LINE == CODE_FILE == CODE_FUNC == None:
+        CODE_FILE, CODE_LINE, CODE_FUNC = \
+            _traceback.extract_stack(limit=2)[0][:3]
+    if CODE_FILE is not None:
+        args.append('CODE_FILE=' + CODE_FILE)
+    if CODE_LINE is not None:
+        args.append('CODE_LINE={:d}'.format(CODE_LINE))
+    if CODE_FUNC is not None:
+        args.append('CODE_FUNC=' + CODE_FUNC)
+
+    args.extend(_make_line(key, val) for key, val in kwargs.items())
+    return sendv(*args)
+
+def stream(identifier, priority=LOG_DEBUG, level_prefix=False):
+    r"""Return a file object wrapping a stream to journal.
+
+    Log messages written to this file as simple newline sepearted
+    text strings are written to the journal.
+
+    The file will be line buffered, so messages are actually sent
+    after a newline character is written.
+
+    >>> stream = journal.stream('myapp')
+    >>> stream
+    <open file '<fdopen>', mode 'w' at 0x...>
+    >>> stream.write('message...\n')
+
+    will produce the following message in the journal:
+
+    PRIORITY=7
+    SYSLOG_IDENTIFIER=myapp
+    MESSAGE=message...
+
+    Using the interface with print might be more convinient:
+
+    >>> from __future__ import print_function
+    >>> print('message...', file=stream)
+
+    priority is the syslog priority, one of LOG_EMERG, LOG_ALERT,
+    LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG.
+
+    level_prefix is a boolean. If true, kernel-style log priority
+    level prefixes (such as '<1>') are interpreted. See sd-daemon(3)
+    for more information.
+    """
+
+    fd = stream_fd(identifier, priority, level_prefix)
+    return _os.fdopen(fd, 'w', 1)



More information about the systemd-commits mailing list