X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fpython-systemd%2F_reader.c;h=4fe7847616a6fc70901eae12020b746738ef38f4;hp=7013fcfcef2f68934c0c9f335362e731d27a399d;hb=806bc1cb610b1795d62f59878703361c8b576b12;hpb=6210afbcff435c857458a908d730db176797a0f7 diff --git a/src/python-systemd/_reader.c b/src/python-systemd/_reader.c index 7013fcfce..4fe784761 100644 --- a/src/python-systemd/_reader.c +++ b/src/python-systemd/_reader.c @@ -30,23 +30,6 @@ #include "macro.h" #include "util.h" -#if PY_MAJOR_VERSION >=3 -# define unicode_FromStringAndSize PyUnicode_FromStringAndSize -# define unicode_FromString PyUnicode_FromString -# define long_FromLong PyLong_FromLong -# define long_FromSize_t PyLong_FromSize_t -# define long_Check PyLong_Check -# define long_AsLong PyLong_AsLong -#else -/* Python 3 type naming convention is used */ -# define unicode_FromStringAndSize PyString_FromStringAndSize -# define unicode_FromString PyString_FromString -# define long_FromLong PyInt_FromLong -# define long_FromSize_t PyInt_FromSize_t -# define long_Check PyInt_Check -# define long_AsLong PyInt_AsLong -#endif - typedef struct { PyObject_HEAD sd_journal *j; @@ -64,9 +47,14 @@ static int set_error(int r, const char* path, const char* invalid_message) { errno = -r; PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); } - return 1; + return -1; } + +PyDoc_STRVAR(module__doc__, + "Class to reads the systemd journal similar to journalctl."); + + #if PY_MAJOR_VERSION >= 3 static PyTypeObject MonotonicType; @@ -87,6 +75,7 @@ static PyStructSequence_Desc Monotonic_desc = { }; #endif + static void Reader_dealloc(Reader* self) { sd_journal_close(self->j); @@ -94,25 +83,34 @@ static void Reader_dealloc(Reader* self) } PyDoc_STRVAR(Reader__doc__, - "Reader([flags][,path]) -> ...\n\n" + "Reader([flags | path]) -> ...\n\n" "Reader allows filtering and retrieval of Journal entries.\n" + "Note: this is a low-level interface, and probably not what you\n" + "want, use systemd.journal.Reader instead.\n\n" "Argument `flags` sets open flags of the journal, which can be one\n" "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n" "journal on local machine only; RUNTIME_ONLY opens only\n" "volatile journal files; and SYSTEM_ONLY opens only\n" - "journal files of system services and the kernel.\n" + "journal files of system services and the kernel.\n\n" "Argument `path` is the directory of journal files. Note that\n" - "currently flags are ignored when `path` is present as they are\n" - "not relevant."); + "`flags` and `path` are exclusive.\n"); static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) { - int flags = SD_JOURNAL_LOCAL_ONLY, r; + int flags = 0, r; char *path = NULL; static const char* const kwlist[] = {"flags", "path", NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist, &flags, &path)) - return 1; + return -1; + + if (!flags) + flags = SD_JOURNAL_LOCAL_ONLY; + else + if (path) { + PyErr_SetString(PyExc_ValueError, "cannot use both flags and path"); + return -1; + } Py_BEGIN_ALLOW_THREADS if (path) @@ -124,6 +122,105 @@ static int Reader_init(Reader *self, PyObject *args, PyObject *keywds) return set_error(r, path, "Invalid flags or path"); } + +PyDoc_STRVAR(Reader_fileno__doc__, + "fileno() -> int\n\n" + "Get a file descriptor to poll for changes in the journal.\n" + "This method invokes sd_journal_get_fd().\n" + "See man:sd_journal_get_fd(3)."); +static PyObject* Reader_fileno(Reader *self, PyObject *args) +{ + int fd; + fd = sd_journal_get_fd(self->j); + set_error(fd, NULL, NULL); + if (fd < 0) + return NULL; + return long_FromLong(fd); +} + + +PyDoc_STRVAR(Reader_reliable_fd__doc__, + "reliable_fd() -> bool\n\n" + "Returns True iff the journal can be polled reliably.\n" + "This method invokes sd_journal_reliable_fd().\n" + "See man:sd_journal_reliable_fd(3)."); +static PyObject* Reader_reliable_fd(Reader *self, PyObject *args) +{ + int r; + r = sd_journal_reliable_fd(self->j); + set_error(r, NULL, NULL); + if (r < 0) + return NULL; + return PyBool_FromLong(r); +} + + +PyDoc_STRVAR(Reader_close__doc__, + "close() -> None\n\n" + "Free resources allocated by this Reader object.\n" + "This method invokes sd_journal_close().\n" + "See man:sd_journal_close(3)."); +static PyObject* Reader_close(Reader *self, PyObject *args) +{ + assert(self); + assert(!args); + + sd_journal_close(self->j); + self->j = NULL; + Py_RETURN_NONE; +} + + +PyDoc_STRVAR(Reader_get_usage__doc__, + "get_usage() -> int\n\n" + "Returns the total disk space currently used by journal" + "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was" + "passed when opening the journal this value will only reflect" + "the size of journal files of the local host, otherwise" + "of all hosts.\n\n" + "This method invokes sd_journal_get_usage().\n" + "See man:sd_journal_get_usage(3)."); +static PyObject* Reader_get_usage(Reader *self, PyObject *args) +{ + int r; + uint64_t bytes; + + r = sd_journal_get_usage(self->j, &bytes); + if (set_error(r, NULL, NULL)) + return NULL; + + assert_cc(sizeof(unsigned long long) == sizeof(bytes)); + return PyLong_FromUnsignedLongLong(bytes); +} + + +PyDoc_STRVAR(Reader___enter____doc__, + "__enter__() -> self\n\n" + "Part of the context manager protocol.\n" + "Returns self.\n"); +static PyObject* Reader___enter__(PyObject *self, PyObject *args) +{ + assert(self); + assert(!args); + + Py_INCREF(self); + return self; +} + +PyDoc_STRVAR(Reader___exit____doc__, + "__exit__(type, value, traceback) -> None\n\n" + "Part of the context manager protocol.\n" + "Closes the journal.\n"); +static PyObject* Reader___exit__(Reader *self, PyObject *args) +{ + assert(self); + + sd_journal_close(self->j); + self->j = NULL; + Py_RETURN_NONE; +} + + PyDoc_STRVAR(Reader_get_next__doc__, "get_next([skip]) -> dict\n\n" "Return dictionary of the next log entry. Optional skip value will\n" @@ -136,7 +233,7 @@ static PyObject* Reader_get_next(Reader *self, PyObject *args) int64_t skip = 1LL; int r; - if (!PyArg_ParseTuple(args, "|L", &skip)) + if (!PyArg_ParseTuple(args, "|L:_Reader.get_next", &skip)) return NULL; if (skip == 0LL) { @@ -303,6 +400,7 @@ error: return NULL; } + PyDoc_STRVAR(Reader_get_previous__doc__, "get_previous([skip]) -> dict\n\n" "Return dictionary of the previous log entry. Optional skip value\n" @@ -310,13 +408,14 @@ PyDoc_STRVAR(Reader_get_previous__doc__, static PyObject* Reader_get_previous(Reader *self, PyObject *args) { int64_t skip = 1LL; - if (!PyArg_ParseTuple(args, "|L", &skip)) + if (!PyArg_ParseTuple(args, "|L:_Reader.get_previous", &skip)) return NULL; return PyObject_CallMethod((PyObject *)self, (char*) "get_next", (char*) "L", -skip); } + PyDoc_STRVAR(Reader_add_match__doc__, "add_match(match) -> None\n\n" "Add a match to filter journal log entries. All matches of different\n" @@ -327,7 +426,7 @@ static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds { char *match; int match_len, r; - if (!PyArg_ParseTuple(args, "s#", &match, &match_len)) + if (!PyArg_ParseTuple(args, "s#:_Reader.add_match", &match, &match_len)) return NULL; r = sd_journal_add_match(self->j, match, match_len); @@ -338,6 +437,7 @@ static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_add_disjunction__doc__, "add_disjunction() -> None\n\n" "Inserts a logical OR between matches added before and afterwards."); @@ -351,6 +451,7 @@ static PyObject* Reader_add_disjunction(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_flush_matches__doc__, "flush_matches() -> None\n\n" "Clear all current match filters."); @@ -360,83 +461,53 @@ static PyObject* Reader_flush_matches(Reader *self, PyObject *args) Py_RETURN_NONE; } -PyDoc_STRVAR(Reader_seek__doc__, - "seek(offset[, whence]) -> None\n\n" - "Jump `offset` entries in the journal. Argument\n" - "`whence` defines what the offset is relative to:\n" - "os.SEEK_SET (default) from first match in journal;\n" - "os.SEEK_CUR from current position in journal;\n" - "and os.SEEK_END is from last match in journal."); -static PyObject* Reader_seek(Reader *self, PyObject *args, PyObject *keywds) -{ - int64_t offset; - int whence = SEEK_SET; - PyObject *result = NULL; - static const char* const kwlist[] = {"offset", "whence", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "L|i", (char**) kwlist, - &offset, &whence)) +PyDoc_STRVAR(Reader_seek_head__doc__, + "seek_head() -> None\n\n" + "Jump to the beginning of the journal.\n" + "This method invokes sd_journal_seek_head().\n" + "See man:sd_journal_seek_head(3)."); +static PyObject* Reader_seek_head(Reader *self, PyObject *args) +{ + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_head(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) return NULL; + Py_RETURN_NONE; +} - switch(whence) { - case SEEK_SET: { - int r; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_head(self->j); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) - return NULL; - - if (offset > 0LL) - result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", offset); - break; - } - case SEEK_CUR: - result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", offset); - break; - case SEEK_END: { - int r; - Py_BEGIN_ALLOW_THREADS - r = sd_journal_seek_tail(self->j); - Py_END_ALLOW_THREADS - if (set_error(r, NULL, NULL)) - return NULL; - result = PyObject_CallMethod((PyObject *)self, (char*) "get_next", - (char*) "L", offset < 0LL ? offset : -1LL); - break; - } - default: - PyErr_SetString(PyExc_ValueError, "Invalid value for whence"); - } - - Py_XDECREF(result); - if (PyErr_Occurred()) +PyDoc_STRVAR(Reader_seek_tail__doc__, + "seek_tail() -> None\n\n" + "Jump to the end of the journal.\n" + "This method invokes sd_journal_seek_tail().\n" + "See man:sd_journal_seek_tail(3)."); +static PyObject* Reader_seek_tail(Reader *self, PyObject *args) +{ + int r; + Py_BEGIN_ALLOW_THREADS + r = sd_journal_seek_tail(self->j); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) return NULL; Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_seek_realtime__doc__, "seek_realtime(realtime) -> None\n\n" "Seek to nearest matching journal entry to `realtime`. Argument\n" - "`realtime` can must be an integer unix timestamp."); + "`realtime` in specified in seconds."); static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) { - double timedouble; uint64_t timestamp; int r; - if (!PyArg_ParseTuple(args, "d", &timedouble)) + if (!PyArg_ParseTuple(args, "K:seek_realtime", ×tamp)) return NULL; - timestamp = (uint64_t) (timedouble * 1.0E6); - if ((int64_t) timestamp < 0LL) { - PyErr_SetString(PyExc_ValueError, "Time must be a positive integer"); - return NULL; - } - Py_BEGIN_ALLOW_THREADS r = sd_journal_seek_realtime_usec(self->j, timestamp); Py_END_ALLOW_THREADS @@ -445,30 +516,23 @@ static PyObject* Reader_seek_realtime(Reader *self, PyObject *args) Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_seek_monotonic__doc__, "seek_monotonic(monotonic[, bootid]) -> None\n\n" "Seek to nearest matching journal entry to `monotonic`. Argument\n" - "`monotonic` is an timestamp from boot in seconds.\n" + "`monotonic` is an timestamp from boot in microseconds.\n" "Argument `bootid` is a string representing which boot the\n" "monotonic time is reference to. Defaults to current bootid."); static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) { - double timedouble; char *bootid = NULL; uint64_t timestamp; sd_id128_t id; int r; - if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid)) + if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", ×tamp, &bootid)) return NULL; - timestamp = (uint64_t) (timedouble * 1.0E6); - - if ((int64_t) timestamp < 0LL) { - PyErr_SetString(PyExc_ValueError, "Time must be positive number"); - return NULL; - } - if (bootid) { r = sd_id128_from_string(bootid, &id); if (set_error(r, NULL, "Invalid bootid")) @@ -486,29 +550,30 @@ static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args) Py_END_ALLOW_THREADS if (set_error(r, NULL, NULL)) return NULL; + Py_RETURN_NONE; } + PyDoc_STRVAR(Reader_wait__doc__, "wait([timeout]) -> state change (integer)\n\n" "Wait for a change in the journal. Argument `timeout` specifies\n" - "the maximum number of seconds to wait before returning\n" - "regardless of wheter the journal has changed. If `timeout` is not given\n" - "or is 0, then block forever.\n" + "the maximum number of microseconds to wait before returning\n" + "regardless of wheter the journal has changed. If `timeout` is -1,\n" + "then block forever.\n\n" "Will return constants: NOP if no change; APPEND if new\n" "entries have been added to the end of the journal; and\n" "INVALIDATE if journal files have been added or removed."); -static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds) +static PyObject* Reader_wait(Reader *self, PyObject *args) { int r; - int64_t timeout = 0LL; + int64_t timeout; - if (!PyArg_ParseTuple(args, "|L", &timeout)) + if (!PyArg_ParseTuple(args, "|L:wait", &timeout)) return NULL; Py_BEGIN_ALLOW_THREADS - r = sd_journal_wait(self->j, - timeout == 0 ? (uint64_t) -1 : timeout * 1E6); + r = sd_journal_wait(self->j, timeout); Py_END_ALLOW_THREADS if (set_error(r, NULL, NULL) < 0) return NULL; @@ -516,6 +581,7 @@ static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds) return long_FromLong(r); } + PyDoc_STRVAR(Reader_seek_cursor__doc__, "seek_cursor(cursor) -> None\n\n" "Seek to journal entry by given unique reference `cursor`."); @@ -524,7 +590,7 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) const char *cursor; int r; - if (!PyArg_ParseTuple(args, "s", &cursor)) + if (!PyArg_ParseTuple(args, "s:_Reader.seek_cursor", &cursor)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -535,6 +601,7 @@ static PyObject* Reader_seek_cursor(Reader *self, PyObject *args) Py_RETURN_NONE; } + static PyObject* Reader_iter(PyObject *self) { Py_INCREF(self); @@ -559,6 +626,7 @@ static PyObject* Reader_iternext(PyObject *self) } } + PyDoc_STRVAR(Reader_query_unique__doc__, "query_unique(field) -> a set of values\n\n" "Return a set of unique values appearing in journal for the\n" @@ -571,7 +639,7 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) size_t uniq_len; PyObject *value_set, *key, *value; - if (!PyArg_ParseTuple(args, "s", &query)) + if (!PyArg_ParseTuple(args, "s:_Reader.query_unique", &query)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -597,6 +665,60 @@ static PyObject* Reader_query_unique(Reader *self, PyObject *args) return value_set; } + +PyDoc_STRVAR(Reader_get_catalog__doc__, + "get_catalog() -> str\n\n" + "Retrieve a message catalog entry for the current journal entry.\n" + "Wraps man:sd_journal_get_catalog(3)."); +static PyObject* Reader_get_catalog(Reader *self, PyObject *args) +{ + int r; + char _cleanup_free_ *msg = NULL; + + assert(self); + assert(!args); + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_get_catalog(self->j, &msg); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + + return unicode_FromString(msg); +} + + +PyDoc_STRVAR(get_catalog__doc__, + "get_catalog(id128) -> str\n\n" + "Retrieve a message catalog entry for the given id.\n" + "Wraps man:sd_journal_get_catalog_for_message_id(3)."); +static PyObject* get_catalog(PyObject *self, PyObject *args) +{ + int r; + char *id_ = NULL; + sd_id128_t id; + char _cleanup_free_ *msg = NULL; + + assert(!self); + assert(args); + + if (!PyArg_ParseTuple(args, "z:get_catalog", &id_)) + return NULL; + + r = sd_id128_from_string(id_, &id); + if (set_error(r, NULL, "Invalid id128")) + return NULL; + + Py_BEGIN_ALLOW_THREADS + r = sd_journal_get_catalog_for_message_id(id, &msg); + Py_END_ALLOW_THREADS + if (set_error(r, NULL, NULL)) + return NULL; + + return unicode_FromString(msg); +} + + PyDoc_STRVAR(data_threshold__doc__, "Threshold for field size truncation in bytes.\n\n" "Fields longer than this will be truncated to the threshold size.\n" @@ -629,27 +751,49 @@ static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closur return set_error(r, NULL, NULL); } -static PyGetSetDef Reader_getseters[] = { + +PyDoc_STRVAR(closed__doc__, + "True iff journal is closed"); +static PyObject* Reader_get_closed(Reader *self, void *closure) +{ + return PyBool_FromLong(self->j == NULL); +} + + +static PyGetSetDef Reader_getsetters[] = { {(char*) "data_threshold", (getter) Reader_get_data_threshold, (setter) Reader_set_data_threshold, (char*) data_threshold__doc__, NULL}, + {(char*) "closed", + (getter) Reader_get_closed, + NULL, + (char*) closed__doc__, + NULL}, {NULL} }; static PyMethodDef Reader_methods[] = { + {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__}, + {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__}, + {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__}, + {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__}, + {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__}, + {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__}, {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__}, {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__}, {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__}, {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__}, {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__}, - {"seek", (PyCFunction) Reader_seek, METH_VARARGS | METH_KEYWORDS, Reader_seek__doc__}, + {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__}, + {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__}, {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__}, {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__}, {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__}, {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__}, {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__}, + {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__}, {NULL} /* Sentinel */ }; @@ -683,7 +827,7 @@ static PyTypeObject ReaderType = { Reader_iternext, /* tp_iternext */ Reader_methods, /* tp_methods */ 0, /* tp_members */ - Reader_getseters, /* tp_getset */ + Reader_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ @@ -694,16 +838,19 @@ static PyTypeObject ReaderType = { PyType_GenericNew, /* tp_new */ }; -#define SUMMARY \ - "Module that reads the systemd journal similar to journalctl." +static PyMethodDef methods[] = { + { "get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__}, + { NULL, NULL, 0, NULL } /* Sentinel */ +}; #if PY_MAJOR_VERSION >= 3 -static PyModuleDef _reader_module = { +static PyModuleDef module = { PyModuleDef_HEAD_INIT, "_reader", - SUMMARY, + module__doc__, -1, - NULL, NULL, NULL, NULL, NULL + methods, + NULL, NULL, NULL, NULL }; #endif @@ -733,7 +880,7 @@ init_reader(void) #endif #if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&_reader_module); + m = PyModule_Create(&module); if (m == NULL) return NULL; @@ -742,7 +889,7 @@ init_reader(void) initialized = true; } #else - m = Py_InitModule3("_reader", NULL, SUMMARY); + m = Py_InitModule3("_reader", methods, module__doc__); if (m == NULL) return; #endif