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)
156 sd_journal_close(self->j);
161 PyDoc_STRVAR(Reader_get_next__doc__,
162 "get_next([skip]) -> dict\n\n"
163 "Return dictionary of the next log entry. Optional skip value will\n"
164 "return the `skip`\\-th log entry.");
165 static PyObject* Reader_get_next(Reader *self, PyObject *args)
173 if (!PyArg_ParseTuple(args, "|L", &skip))
177 PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
181 Py_BEGIN_ALLOW_THREADS
183 r = sd_journal_next(self->j);
184 else if (skip == -1LL)
185 r = sd_journal_previous(self->j);
187 r = sd_journal_next_skip(self->j, skip);
188 else if (skip < -1LL)
189 r = sd_journal_previous_skip(self->j, -skip);
191 assert_not_reached("should not be here");
194 set_error(r, NULL, NULL);
197 else if (r == 0) /* EOF */
204 SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
205 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
206 const char *delim_ptr;
208 delim_ptr = memchr(msg, '=', msg_len);
210 PyErr_SetString(PyExc_OSError,
211 "journal gave us a field without '='");
215 key = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
219 value = PyBytes_FromStringAndSize(
221 (const char*) msg + msg_len - (delim_ptr + 1) );
225 if (PyDict_Contains(dict, key)) {
226 PyObject *cur_value = PyDict_GetItem(dict, key);
228 if (PyList_CheckExact(cur_value)) {
229 r = PyList_Append(cur_value, value);
233 PyObject _cleanup_Py_DECREF_ *tmp_list = PyList_New(0);
237 r = PyList_Append(tmp_list, cur_value);
241 r = PyList_Append(tmp_list, value);
245 r = PyDict_SetItem(dict, key, tmp_list);
250 r = PyDict_SetItem(dict, key, value);
257 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
260 r = sd_journal_get_realtime_usec(self->j, &realtime);
261 if (set_error(r, NULL, NULL))
264 key = unicode_FromString("__REALTIME_TIMESTAMP");
268 assert_cc(sizeof(unsigned long long) == sizeof(realtime));
269 value = PyLong_FromUnsignedLongLong(realtime);
273 if (PyDict_SetItem(dict, key, value))
278 PyObject _cleanup_Py_DECREF_
279 *key = NULL, *timestamp = NULL, *bytes = NULL, *value = NULL;
283 r = sd_journal_get_monotonic_usec(self->j, &monotonic, &id);
284 if (set_error(r, NULL, NULL))
287 assert_cc(sizeof(unsigned long long) == sizeof(monotonic));
288 key = unicode_FromString("__MONOTONIC_TIMESTAMP");
289 timestamp = PyLong_FromUnsignedLongLong(monotonic);
290 bytes = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
291 #if PY_MAJOR_VERSION >= 3
292 value = PyStructSequence_New(&MonotonicType);
294 value = PyTuple_New(2);
296 if (!key || !timestamp || !bytes || !value)
299 Py_INCREF(timestamp);
302 #if PY_MAJOR_VERSION >= 3
303 PyStructSequence_SET_ITEM(value, 0, timestamp);
304 PyStructSequence_SET_ITEM(value, 1, bytes);
306 PyTuple_SET_ITEM(value, 0, timestamp);
307 PyTuple_SET_ITEM(value, 1, bytes);
310 if (PyDict_SetItem(dict, key, value))
315 PyObject _cleanup_Py_DECREF_ *key = NULL, *value = NULL;
316 char _cleanup_free_ *cursor = NULL;
318 r = sd_journal_get_cursor(self->j, &cursor);
319 if (set_error(r, NULL, NULL))
322 key = unicode_FromString("__CURSOR");
326 value = PyBytes_FromString(cursor);
330 if (PyDict_SetItem(dict, key, value))
340 PyDoc_STRVAR(Reader_get_previous__doc__,
341 "get_previous([skip]) -> dict\n\n"
342 "Return dictionary of the previous log entry. Optional skip value\n"
343 "will return the -`skip`\\-th log entry. Equivalent to get_next(-skip).");
344 static PyObject* Reader_get_previous(Reader *self, PyObject *args)
347 if (!PyArg_ParseTuple(args, "|L", &skip))
350 return PyObject_CallMethod((PyObject *)self, (char*) "get_next",
354 PyDoc_STRVAR(Reader_add_match__doc__,
355 "add_match(match) -> None\n\n"
356 "Add a match to filter journal log entries. All matches of different\n"
357 "fields are combined with logical AND, and matches of the same field\n"
358 "are automatically combined with logical OR.\n"
359 "Match is a string of the form \"FIELD=value\".");
360 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
364 if (!PyArg_ParseTuple(args, "s#", &match, &match_len))
367 r = sd_journal_add_match(self->j, match, match_len);
368 set_error(r, NULL, "Invalid match");
375 PyDoc_STRVAR(Reader_add_disjunction__doc__,
376 "add_disjunction() -> None\n\n"
377 "Inserts a logical OR between matches added before and afterwards.");
378 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
381 r = sd_journal_add_disjunction(self->j);
382 set_error(r, NULL, NULL);
388 PyDoc_STRVAR(Reader_flush_matches__doc__,
389 "flush_matches() -> None\n\n"
390 "Clear all current match filters.");
391 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
393 sd_journal_flush_matches(self->j);
397 PyDoc_STRVAR(Reader_seek_head__doc__,
398 "seek_head() -> None\n\n"
399 "Jump to the beginning of the journal.\n"
400 "This method invokes sd_journal_seek_head().\n"
401 "See man:sd_journal_seek_head(3).");
402 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
405 Py_BEGIN_ALLOW_THREADS
406 r = sd_journal_seek_head(self->j);
408 if (set_error(r, NULL, NULL))
413 PyDoc_STRVAR(Reader_seek_tail__doc__,
414 "seek_tail() -> None\n\n"
415 "Jump to the end of the journal.\n"
416 "This method invokes sd_journal_seek_tail().\n"
417 "See man:sd_journal_seek_tail(3).");
418 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
421 Py_BEGIN_ALLOW_THREADS
422 r = sd_journal_seek_tail(self->j);
424 if (set_error(r, NULL, NULL))
429 PyDoc_STRVAR(Reader_seek_realtime__doc__,
430 "seek_realtime(realtime) -> None\n\n"
431 "Seek to nearest matching journal entry to `realtime`. Argument\n"
432 "`realtime` can must be an integer unix timestamp.");
433 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
439 if (!PyArg_ParseTuple(args, "d", &timedouble))
442 timestamp = (uint64_t) (timedouble * 1.0E6);
443 if ((int64_t) timestamp < 0LL) {
444 PyErr_SetString(PyExc_ValueError, "Time must be a positive integer");
448 Py_BEGIN_ALLOW_THREADS
449 r = sd_journal_seek_realtime_usec(self->j, timestamp);
451 if (set_error(r, NULL, NULL))
456 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
457 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
458 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
459 "`monotonic` is an timestamp from boot in seconds.\n"
460 "Argument `bootid` is a string representing which boot the\n"
461 "monotonic time is reference to. Defaults to current bootid.");
462 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
470 if (!PyArg_ParseTuple(args, "d|z", &timedouble, &bootid))
473 timestamp = (uint64_t) (timedouble * 1.0E6);
475 if ((int64_t) timestamp < 0LL) {
476 PyErr_SetString(PyExc_ValueError, "Time must be positive number");
481 r = sd_id128_from_string(bootid, &id);
482 if (set_error(r, NULL, "Invalid bootid"))
485 Py_BEGIN_ALLOW_THREADS
486 r = sd_id128_get_boot(&id);
488 if (set_error(r, NULL, NULL))
492 Py_BEGIN_ALLOW_THREADS
493 r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
495 if (set_error(r, NULL, NULL))
500 PyDoc_STRVAR(Reader_wait__doc__,
501 "wait([timeout]) -> state change (integer)\n\n"
502 "Wait for a change in the journal. Argument `timeout` specifies\n"
503 "the maximum number of seconds to wait before returning\n"
504 "regardless of wheter the journal has changed. If `timeout` is not given\n"
505 "or is 0, then block forever.\n"
506 "Will return constants: NOP if no change; APPEND if new\n"
507 "entries have been added to the end of the journal; and\n"
508 "INVALIDATE if journal files have been added or removed.");
509 static PyObject* Reader_wait(Reader *self, PyObject *args, PyObject *keywds)
512 int64_t timeout = 0LL;
514 if (!PyArg_ParseTuple(args, "|L", &timeout))
517 Py_BEGIN_ALLOW_THREADS
518 r = sd_journal_wait(self->j,
519 timeout == 0 ? (uint64_t) -1 : timeout * 1E6);
521 if (set_error(r, NULL, NULL) < 0)
524 return long_FromLong(r);
527 PyDoc_STRVAR(Reader_seek_cursor__doc__,
528 "seek_cursor(cursor) -> None\n\n"
529 "Seek to journal entry by given unique reference `cursor`.");
530 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
535 if (!PyArg_ParseTuple(args, "s", &cursor))
538 Py_BEGIN_ALLOW_THREADS
539 r = sd_journal_seek_cursor(self->j, cursor);
541 if (set_error(r, NULL, "Invalid cursor"))
546 static PyObject* Reader_iter(PyObject *self)
552 static PyObject* Reader_iternext(PyObject *self)
555 Py_ssize_t dict_size;
557 dict = PyObject_CallMethod(self, (char*) "get_next", (char*) "");
558 if (PyErr_Occurred())
560 dict_size = PyDict_Size(dict);
561 if ((int64_t) dict_size > 0LL) {
565 PyErr_SetNone(PyExc_StopIteration);
570 PyDoc_STRVAR(Reader_query_unique__doc__,
571 "query_unique(field) -> a set of values\n\n"
572 "Return a set of unique values appearing in journal for the\n"
573 "given `field`. Note this does not respect any journal matches.");
574 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
580 PyObject *value_set, *key, *value;
582 if (!PyArg_ParseTuple(args, "s", &query))
585 Py_BEGIN_ALLOW_THREADS
586 r = sd_journal_query_unique(self->j, query);
588 if (set_error(r, NULL, "Invalid field name"))
591 value_set = PySet_New(0);
592 key = unicode_FromString(query);
594 SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
595 const char *delim_ptr;
597 delim_ptr = memchr(uniq, '=', uniq_len);
598 value = PyBytes_FromStringAndSize(
600 (const char*) uniq + uniq_len - (delim_ptr + 1));
601 PySet_Add(value_set, value);
608 PyDoc_STRVAR(data_threshold__doc__,
609 "Threshold for field size truncation in bytes.\n\n"
610 "Fields longer than this will be truncated to the threshold size.\n"
611 "Defaults to 64Kb.");
613 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
618 r = sd_journal_get_data_threshold(self->j, &cvalue);
619 if (set_error(r, NULL, NULL))
622 return long_FromSize_t(cvalue);
625 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
629 PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
632 if (!long_Check(value)){
633 PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
636 r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
637 return set_error(r, NULL, NULL);
640 static PyGetSetDef Reader_getseters[] = {
641 {(char*) "data_threshold",
642 (getter) Reader_get_data_threshold,
643 (setter) Reader_set_data_threshold,
644 (char*) data_threshold__doc__,
649 static PyMethodDef Reader_methods[] = {
650 {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
651 {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
652 {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
653 {"get_next", (PyCFunction) Reader_get_next, METH_VARARGS, Reader_get_next__doc__},
654 {"get_previous", (PyCFunction) Reader_get_previous, METH_VARARGS, Reader_get_previous__doc__},
655 {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
656 {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
657 {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
658 {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
659 {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
660 {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
661 {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
662 {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
663 {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
664 {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
665 {NULL} /* Sentinel */
668 static PyTypeObject ReaderType = {
669 PyVarObject_HEAD_INIT(NULL, 0)
670 "_reader._Reader", /*tp_name*/
671 sizeof(Reader), /*tp_basicsize*/
673 (destructor)Reader_dealloc, /*tp_dealloc*/
680 0, /*tp_as_sequence*/
688 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
689 Reader__doc__, /* tp_doc */
692 0, /* tp_richcompare */
693 0, /* tp_weaklistoffset */
694 Reader_iter, /* tp_iter */
695 Reader_iternext, /* tp_iternext */
696 Reader_methods, /* tp_methods */
698 Reader_getseters, /* tp_getset */
701 0, /* tp_descr_get */
702 0, /* tp_descr_set */
703 0, /* tp_dictoffset */
704 (initproc) Reader_init, /* tp_init */
706 PyType_GenericNew, /* tp_new */
710 "Module that reads the systemd journal similar to journalctl."
712 #if PY_MAJOR_VERSION >= 3
713 static PyModuleDef _reader_module = {
714 PyModuleDef_HEAD_INIT,
718 NULL, NULL, NULL, NULL, NULL
722 #if PY_MAJOR_VERSION >= 3
723 static bool initialized = false;
726 #pragma GCC diagnostic push
727 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
730 #if PY_MAJOR_VERSION >= 3
740 if (PyType_Ready(&ReaderType) < 0)
741 #if PY_MAJOR_VERSION >= 3
747 #if PY_MAJOR_VERSION >= 3
748 m = PyModule_Create(&_reader_module);
753 PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
757 m = Py_InitModule3("_reader", NULL, SUMMARY);
762 Py_INCREF(&ReaderType);
763 #if PY_MAJOR_VERSION >= 3
764 Py_INCREF(&MonotonicType);
766 if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
767 #if PY_MAJOR_VERSION >= 3
768 PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
770 PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
771 PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
772 PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
773 PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
774 PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
775 PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
776 #if PY_MAJOR_VERSION >= 3
782 #if PY_MAJOR_VERSION >= 3
787 #pragma GCC diagnostic pop