1 /*-*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Steven Hiscocks, Zbigniew Jędrzejewski-Szmek
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <structmember.h>
27 #include <systemd/sd-journal.h>
37 static PyTypeObject ReaderType;
39 static int set_error(int r, const char* path, const char* invalid_message) {
42 if (r == -EINVAL && invalid_message)
43 PyErr_SetString(PyExc_ValueError, invalid_message);
44 else if (r == -ENOMEM)
45 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
48 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
53 #if PY_MAJOR_VERSION >= 3
54 static PyTypeObject MonotonicType;
56 PyDoc_STRVAR(MonotonicType__doc__,
57 "A tuple of (timestamp, bootid) for holding monotonic timestamps");
59 static PyStructSequence_Field MonotonicType_fields[] = {
60 {(char*) "timestamp", (char*) "Time"},
61 {(char*) "bootid", (char*) "Unique identifier of the boot"},
65 static PyStructSequence_Desc Monotonic_desc = {
66 (char*) "journal.Monotonic",
73 static void Reader_dealloc(Reader* self)
75 sd_journal_close(self->j);
76 Py_TYPE(self)->tp_free((PyObject*)self);
79 PyDoc_STRVAR(Reader__doc__,
80 "Reader([flags | path]) -> ...\n\n"
81 "Reader allows filtering and retrieval of Journal entries.\n"
82 "Note: this is a low-level interface, and probably not what you\n"
83 "want, use systemd.journal.Reader instead.\n\n"
84 "Argument `flags` sets open flags of the journal, which can be one\n"
85 "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
86 "journal on local machine only; RUNTIME_ONLY opens only\n"
87 "volatile journal files; and SYSTEM_ONLY opens only\n"
88 "journal files of system services and the kernel.\n\n"
89 "Argument `path` is the directory of journal files. Note that\n"
90 "`flags` and `path` are exclusive.\n");
91 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
96 static const char* const kwlist[] = {"flags", "path", NULL};
97 if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
102 flags = SD_JOURNAL_LOCAL_ONLY;
105 PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
109 Py_BEGIN_ALLOW_THREADS
111 r = sd_journal_open_directory(&self->j, path, 0);
113 r = sd_journal_open(&self->j, flags);
116 return set_error(r, path, "Invalid flags or path");
119 PyDoc_STRVAR(Reader_fileno__doc__,
120 "fileno() -> int\n\n"
121 "Get a file descriptor to poll for changes in the journal.\n"
122 "This method invokes sd_journal_get_fd().\n"
123 "See man:sd_journal_get_fd(3).");
124 static PyObject* Reader_fileno(Reader *self, PyObject *args)
127 r = sd_journal_get_fd(self->j);
128 set_error(r, NULL, NULL);
131 return long_FromLong(r);
134 PyDoc_STRVAR(Reader_reliable_fd__doc__,
135 "reliable_fd() -> bool\n\n"
136 "Returns True iff the journal can be polled reliably.\n"
137 "This method invokes sd_journal_reliable_fd().\n"
138 "See man:sd_journal_reliable_fd(3).");
139 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
142 r = sd_journal_reliable_fd(self->j);
143 set_error(r, NULL, NULL);
146 return PyBool_FromLong(r);
149 PyDoc_STRVAR(Reader_close__doc__,
150 "close() -> None\n\n"
151 "Free resources allocated by this Reader object.\n"
152 "This method invokes sd_journal_close().\n"
153 "See man:sd_journal_close(3).");
154 static PyObject* Reader_close(Reader *self, PyObject *args)
159 sd_journal_close(self->j);
164 PyDoc_STRVAR(Reader___enter____doc__,
165 "__enter__() -> self\n\n"
166 "Part of the context manager protocol.\n"
168 static PyObject* Reader___enter__(PyObject *self, PyObject *args)
177 PyDoc_STRVAR(Reader___exit____doc__,
178 "__exit__(type, value, traceback) -> None\n\n"
179 "Part of the context manager protocol.\n"
180 "Closes the journal.\n");
181 static PyObject* Reader___exit__(Reader *self, PyObject *args)
185 sd_journal_close(self->j);
190 PyDoc_STRVAR(Reader_get_next__doc__,
191 "get_next([skip]) -> dict\n\n"
192 "Return dictionary of the next log entry. Optional skip value will\n"
193 "return the `skip`\\-th log entry.");
194 static PyObject* Reader_get_next(Reader *self, PyObject *args)
202 if (!PyArg_ParseTuple(args, "|L", &skip))
206 PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
210 Py_BEGIN_ALLOW_THREADS
212 r = sd_journal_next(self->j);
213 else if (skip == -1LL)
214 r = sd_journal_previous(self->j);
216 r = sd_journal_next_skip(self->j, skip);
217 else if (skip < -1LL)
218 r = sd_journal_previous_skip(self->j, -skip);
220 assert_not_reached("should not be here");
223 set_error(r, NULL, NULL);
226 else if (r == 0) /* EOF */
233 SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
234 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
235 const char *delim_ptr;
237 delim_ptr = memchr(msg, '=', msg_len);
239 PyErr_SetString(PyExc_OSError,
240 "journal gave us a field without '='");
244 key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
248 value = PyBytes_FromStringAndSize(
250 (const char*) msg + msg_len - (delim_ptr + 1) );
254 if (PyDict_Contains(dict, key)) {
255 PyObject *cur_value = PyDict_GetItem(dict, key);
257 if (PyList_CheckExact(cur_value)) {
258 r = PyList_Append(cur_value, value);
262 PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0);
266 r = PyList_Append(tmp_list, cur_value);
270 r = PyList_Append(tmp_list, value);
274 r = PyDict_SetItem(dict, key, tmp_list);
279 r = PyDict_SetItem(dict, key, value);
286 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
289 r = sd_journal_get_realtime_usec(self->j, &realtime);
290 if (set_error(r, NULL, NULL))
293 key = unicode_FromString("__REALTIME_TIMESTAMP");
297 assert_cc(sizeof(unsigned long long) == sizeof(realtime));
298 value = PyLong_FromUnsignedLongLong(realtime);
302 if (PyDict_SetItem(dict, key, value))
307 PyObject _cleanup_Py_DECREF_
308 *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL;
312 r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id);
313 if (set_error(r, NULL, NULL))
316 assert_cc(sizeof(unsigned long long) == sizeof(monotonic));
317 key = unicode_FromString("__MONOTONIC_TIMESTAMP");
318 timestamp = PyLong_FromUnsignedLongLong(monotonic);
319 bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
320 #if PY_MAJOR_VERSION >= 3
321 value = PyStructSequence_New(&MonotonicType);
323 value = PyTuple_New(2);
325 if (!key || !timestamp || !bytes || !value)
328 Py_INCREF(timestamp);
331 #if PY_MAJOR_VERSION >= 3
332 PyStructSequence_SET_ITEM(value, 0, timestamp);
333 PyStructSequence_SET_ITEM(value, 1, bytes);
335 PyTuple_SET_ITEM(value, 0, timestamp);
336 PyTuple_SET_ITEM(value, 1, bytes);
339 if (PyDict_SetItem(dict, key, value))
344 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
345 char _cleanup_free_ *cursor = NULL;
347 r = sd_journal_get_cursor(self->j, &cursor);
348 if (set_error(r, NULL, NULL))
351 key = unicode_FromString("__CURSOR");
355 value = PyBytes_FromString(cursor);
359 if (PyDict_SetItem(dict, key, value))
369 PyDoc_STRVAR(Reader_get_previous__doc__,
370 "get_previous([skip]) -> dict\n\n"
371 "Return dictionary of the previous log entry. Optional skip value\n"
372 "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip).");
373 static PyObject* Reader_get_previous(Reader *self, PyObject *args)
376 if (!PyArg_ParseTuple(args, "|L", &skip))
379 return PyObject_CallMethod((PyObject *)self, (char*) "get_next",
383 PyDoc_STRVAR(Reader_add_match__doc__,
384 "add_match(match) -> None\n\n"
385 "Add a match to filter journal log entries. All matches of different\n"
386 "fields are combined with logical AND, and matches of the same field\n"
387 "are automatically combined with logical OR.\n"
388 "Match is a string of the form \"FIELD=value\".");
389 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
393 if (!PyArg_ParseTuple(args, "s#", &match, &match_len))
396 r = sd_journal_add_match(self->j, match, match_len);
397 set_error(r, NULL, "Invalid match");
404 PyDoc_STRVAR(Reader_add_disjunction__doc__,
405 "add_disjunction() -> None\n\n"
406 "Inserts a logical OR between matches added before and afterwards.");
407 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
410 r = sd_journal_add_disjunction(self->j);
411 set_error(r, NULL, NULL);
417 PyDoc_STRVAR(Reader_flush_matches__doc__,
418 "flush_matches() -> None\n\n"
419 "Clear all current match filters.");
420 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
422 sd_journal_flush_matches(self->j);
426 PyDoc_STRVAR(Reader_seek_head__doc__,
427 "seek_head() -> None\n\n"
428 "Jump to the beginning of the journal.\n"
429 "This method invokes sd_journal_seek_head().\n"
430 "See man:sd_journal_seek_head(3).");
431 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
434 Py_BEGIN_ALLOW_THREADS
435 r = sd_journal_seek_head(self->j);
437 if (set_error(r, NULL, NULL))
442 PyDoc_STRVAR(Reader_seek_tail__doc__,
443 "seek_tail() -> None\n\n"
444 "Jump to the end of the journal.\n"
445 "This method invokes sd_journal_seek_tail().\n"
446 "See man:sd_journal_seek_tail(3).");
447 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
450 Py_BEGIN_ALLOW_THREADS
451 r = sd_journal_seek_tail(self->j);
453 if (set_error(r, NULL, NULL))
458 PyDoc_STRVAR(Reader_seek_realtime__doc__,
459 "seek_realtime(realtime) -> None\n\n"
460 "Seek to nearest matching journal entry to `realtime`. Argument\n"
461 "`realtime` can must be an integer unix timestamp.");
462 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
468 if (!PyArg_ParseTuple(args, "d", &timedouble))
471 timestamp = (uint64_t) (timedouble * 1.0E6);
472 if ((int64_t) timestamp < 0LL) {
473 PyErr_SetString(PyExc_ValueError, "Time must be a positive integer");
477 Py_BEGIN_ALLOW_THREADS
478 r = sd_journal_seek_realtime_usec(self->j, timestamp);
480 if (set_error(r, NULL, NULL))
485 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
486 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
487 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
488 "`monotonic` is an timestamp from boot in seconds.\n"
489 "Argument `bootid` is a string representing which boot the\n"
490 "monotonic time is reference to. Defaults to current bootid.");
491 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
499 if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid))
502 timestamp = (uint64_t) (timedouble * 1.0E6);
504 if ((int64_t) timestamp < 0LL) {
505 PyErr_SetString(PyExc_ValueError, "Time must be positive number");
510 r = sd_id128_from_string(bootid, &id);
511 if (set_error(r, NULL, "Invalid bootid"))
514 Py_BEGIN_ALLOW_THREADS
515 r = sd_id128_get_boot(&id);
517 if (set_error(r, NULL, NULL))
521 Py_BEGIN_ALLOW_THREADS
522 r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
524 if (set_error(r, NULL, NULL))
529 PyDoc_STRVAR(Reader_wait__doc__,
530 "wait([timeout]) -> state change (integer)\n\n"
531 "Wait for a change in the journal. Argument `timeout` specifies\n"
532 "the maximum number of seconds to wait before returning\n"
533 "regardless of wheter the journal has changed. If `timeout` is not given\n"
534 "or is 0, then block forever.\n"
535 "Will return constants: NOP if no change; APPEND if new\n"
536 "entries have been added to the end of the journal; and\n"
537 "INVALIDATE if journal files have been added or removed.");
538 static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds)
541 int64_t timeout = 0LL;
543 if (!PyArg_ParseTuple(args, "|L", &timeout))
546 Py_BEGIN_ALLOW_THREADS
547 r = sd_journal_wait(self->j,
548 timeout == 0 ? (uint64_t) -1 : timeout * 1E6);
550 if (set_error(r, NULL, NULL) < 0)
553 return long_FromLong(r);
556 PyDoc_STRVAR(Reader_seek_cursor__doc__,
557 "seek_cursor(cursor) -> None\n\n"
558 "Seek to journal entry by given unique reference `cursor`.");
559 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
564 if (!PyArg_ParseTuple(args, "s", &cursor))
567 Py_BEGIN_ALLOW_THREADS
568 r = sd_journal_seek_cursor(self->j, cursor);
570 if (set_error(r, NULL, "Invalid cursor"))
575 static PyObject* Reader_iter(PyObject *self)
581 static PyObject* Reader_iternext(PyObject *self)
584 Py_ssize_t dict_size;
586 dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
587 if (PyErr_Occurred())
589 dict_size = PyDict_Size(dict);
590 if ((int64_t) dict_size > 0LL) {
594 PyErr_SetNone(PyExc_StopIteration);
599 PyDoc_STRVAR(Reader_query_unique__doc__,
600 "query_unique(field) -> a set of values\n\n"
601 "Return a set of unique values appearing in journal for the\n"
602 "given `field`. Note this does not respect any journal matches.");
603 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
609 PyObject *value_set, *key, *value;
611 if (!PyArg_ParseTuple(args, "s", &query))
614 Py_BEGIN_ALLOW_THREADS
615 r = sd_journal_query_unique(self->j, query);
617 if (set_error(r, NULL, "Invalid field name"))
620 value_set = PySet_New(0);
621 key = unicode_FromString(query);
623 SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
624 const char *delim_ptr;
626 delim_ptr = memchr(uniq, '=', uniq_len);
627 value = PyBytes_FromStringAndSize(
629 (const char*) uniq + uniq_len - (delim_ptr + 1));
630 PySet_Add(value_set, value);
637 PyDoc_STRVAR(data_threshold__doc__,
638 "Threshold for field size truncation in bytes.\n\n"
639 "Fields longer than this will be truncated to the threshold size.\n"
640 "Defaults to 64Kb.");
642 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
647 r = sd_journal_get_data_threshold(self->j, &cvalue);
648 if (set_error(r, NULL, NULL))
651 return long_FromSize_t(cvalue);
654 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
658 PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
661 if (!long_Check(value)){
662 PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
665 r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
666 return set_error(r, NULL, NULL);
669 static PyGetSetDef Reader_getseters[] = {
670 {(char*) "data_threshold",
671 (getter) Reader_get_data_threshold,
672 (setter) Reader_set_data_threshold,
673 (char*) data_threshold__doc__,
678 static PyMethodDef Reader_methods[] = {
679 {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
680 {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
681 {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
682 {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
683 {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
684 {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
685 {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
686 {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
687 {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
688 {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
689 {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
690 {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
691 {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
692 {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
693 {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
694 {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
695 {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
696 {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
697 {NULL} /* Sentinel */
700 static PyTypeObject ReaderType = {
701 PyVarObject_HEAD_INIT(NULL, 0)
702 "_reader._Reader", /*tp_name*/
703 sizeof(Reader), /*tp_basicsize*/
705 (destructor)Reader_dealloc, /*tp_dealloc*/
712 0, /*tp_as_sequence*/
720 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
721 Reader__doc__, /* tp_doc */
724 0, /* tp_richcompare */
725 0, /* tp_weaklistoffset */
726 Reader_iter, /* tp_iter */
727 Reader_iternext, /* tp_iternext */
728 Reader_methods, /* tp_methods */
730 Reader_getseters, /* tp_getset */
733 0, /* tp_descr_get */
734 0, /* tp_descr_set */
735 0, /* tp_dictoffset */
736 (initproc) Reader_init, /* tp_init */
738 PyType_GenericNew, /* tp_new */
742 "Module that reads the systemd journal similar to journalctl."
744 #if PY_MAJOR_VERSION >= 3
745 static PyModuleDef _reader_module = {
746 PyModuleDef_HEAD_INIT,
750 NULL, NULL, NULL, NULL, NULL
754 #if PY_MAJOR_VERSION >= 3
755 static bool initialized = false;
758 #pragma GCC diagnostic push
759 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
762 #if PY_MAJOR_VERSION >= 3
772 if (PyType_Ready(&ReaderType) < 0)
773 #if PY_MAJOR_VERSION >= 3
779 #if PY_MAJOR_VERSION >= 3
780 m = PyModule_Create(&_reader_module);
785 PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
789 m = Py_InitModule3("_reader", NULL, SUMMARY);
794 Py_INCREF(&ReaderType);
795 #if PY_MAJOR_VERSION >= 3
796 Py_INCREF(&MonotonicType);
798 if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
799 #if PY_MAJOR_VERSION >= 3
800 PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
802 PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
803 PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
804 PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
805 PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
806 PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
807 PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
808 #if PY_MAJOR_VERSION >= 3
814 #if PY_MAJOR_VERSION >= 3
819 #pragma GCC diagnostic pop