]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/python-systemd/_reader.c
systemd-python: wrap sd_journal_add_conjunction
[thirdparty/systemd.git] / src / python-systemd / _reader.c
1 /*-*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Steven Hiscocks, Zbigniew Jędrzejewski-Szmek
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <Python.h>
23 #include <structmember.h>
24 #include <datetime.h>
25 #include <time.h>
26 #include <stdio.h>
27
28 #include <systemd/sd-journal.h>
29
30 #include "pyutil.h"
31 #include "macro.h"
32 #include "util.h"
33
34 typedef struct {
35 PyObject_HEAD
36 sd_journal *j;
37 } Reader;
38 static PyTypeObject ReaderType;
39
40 static int set_error(int r, const char* path, const char* invalid_message) {
41 if (r >= 0)
42 return r;
43 if (r == -EINVAL && invalid_message)
44 PyErr_SetString(PyExc_ValueError, invalid_message);
45 else if (r == -ENOMEM)
46 PyErr_SetString(PyExc_MemoryError, "Not enough memory");
47 else {
48 errno = -r;
49 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
50 }
51 return -1;
52 }
53
54
55 PyDoc_STRVAR(module__doc__,
56 "Class to reads the systemd journal similar to journalctl.");
57
58
59 #if PY_MAJOR_VERSION >= 3
60 static PyTypeObject MonotonicType;
61
62 PyDoc_STRVAR(MonotonicType__doc__,
63 "A tuple of (timestamp, bootid) for holding monotonic timestamps");
64
65 static PyStructSequence_Field MonotonicType_fields[] = {
66 {(char*) "timestamp", (char*) "Time"},
67 {(char*) "bootid", (char*) "Unique identifier of the boot"},
68 {} /* Sentinel */
69 };
70
71 static PyStructSequence_Desc Monotonic_desc = {
72 (char*) "journal.Monotonic",
73 MonotonicType__doc__,
74 MonotonicType_fields,
75 2,
76 };
77 #endif
78
79
80 static void Reader_dealloc(Reader* self)
81 {
82 sd_journal_close(self->j);
83 Py_TYPE(self)->tp_free((PyObject*)self);
84 }
85
86 PyDoc_STRVAR(Reader__doc__,
87 "_Reader([flags | path]) -> ...\n\n"
88 "_Reader allows filtering and retrieval of Journal entries.\n"
89 "Note: this is a low-level interface, and probably not what you\n"
90 "want, use systemd.journal.Reader instead.\n\n"
91 "Argument `flags` sets open flags of the journal, which can be one\n"
92 "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
93 "journal on local machine only; RUNTIME_ONLY opens only\n"
94 "volatile journal files; and SYSTEM_ONLY opens only\n"
95 "journal files of system services and the kernel.\n\n"
96 "Argument `path` is the directory of journal files. Note that\n"
97 "`flags` and `path` are exclusive.\n\n"
98 "_Reader implements the context manager protocol: the journal\n"
99 "will be closed when exiting the block.");
100 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
101 {
102 int flags = 0, r;
103 char *path = NULL;
104
105 static const char* const kwlist[] = {"flags", "path", NULL};
106 if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iz", (char**) kwlist,
107 &flags, &path))
108 return -1;
109
110 if (!flags)
111 flags = SD_JOURNAL_LOCAL_ONLY;
112 else
113 if (path) {
114 PyErr_SetString(PyExc_ValueError, "cannot use both flags and path");
115 return -1;
116 }
117
118 Py_BEGIN_ALLOW_THREADS
119 if (path)
120 r = sd_journal_open_directory(&self->j, path, 0);
121 else
122 r = sd_journal_open(&self->j, flags);
123 Py_END_ALLOW_THREADS
124
125 return set_error(r, path, "Invalid flags or path");
126 }
127
128
129 PyDoc_STRVAR(Reader_fileno__doc__,
130 "fileno() -> int\n\n"
131 "Get a file descriptor to poll for changes in the journal.\n"
132 "This method invokes sd_journal_get_fd().\n"
133 "See man:sd_journal_get_fd(3).");
134 static PyObject* Reader_fileno(Reader *self, PyObject *args)
135 {
136 int fd = sd_journal_get_fd(self->j);
137 set_error(fd, NULL, NULL);
138 if (fd < 0)
139 return NULL;
140 return long_FromLong(fd);
141 }
142
143
144 PyDoc_STRVAR(Reader_reliable_fd__doc__,
145 "reliable_fd() -> bool\n\n"
146 "Returns True iff the journal can be polled reliably.\n"
147 "This method invokes sd_journal_reliable_fd().\n"
148 "See man:sd_journal_reliable_fd(3).");
149 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
150 {
151 int r = sd_journal_reliable_fd(self->j);
152 set_error(r, NULL, NULL);
153 if (r < 0)
154 return NULL;
155 return PyBool_FromLong(r);
156 }
157
158
159 PyDoc_STRVAR(Reader_get_events__doc__,
160 "get_events() -> int\n\n"
161 "Returns a mask of poll() events to wait for on the file\n"
162 "descriptor returned by .fileno().\n\n"
163 "See man:sd_journal_get_events(3) for further discussion.");
164 static PyObject* Reader_get_events(Reader *self, PyObject *args)
165 {
166 int r = sd_journal_get_events(self->j);
167 set_error(r, NULL, NULL);
168 if (r < 0)
169 return NULL;
170 return long_FromLong(r);
171 }
172
173
174 PyDoc_STRVAR(Reader_get_timeout__doc__,
175 "get_timeout() -> int or None\n\n"
176 "Returns a timeout value for usage in poll(), the time since the\n"
177 "epoch of clock_gettime(2) in microseconds, or None if no timeout\n"
178 "is necessary.\n\n"
179 "The return value must be converted to a relative timeout in \n"
180 "milliseconds if it is to be used as an argument for poll().\n"
181 "See man:sd_journal_get_timeout(3) for further discussion.");
182 static PyObject* Reader_get_timeout(Reader *self, PyObject *args)
183 {
184 int r;
185 uint64_t t;
186
187 r = sd_journal_get_timeout(self->j, &t);
188 set_error(r, NULL, NULL);
189 if (r < 0)
190 return NULL;
191
192 if (t == (uint64_t) -1)
193 Py_RETURN_NONE;
194
195 assert_cc(sizeof(unsigned long long) == sizeof(t));
196 return PyLong_FromUnsignedLongLong(t);
197 }
198
199
200 PyDoc_STRVAR(Reader_get_timeout_ms__doc__,
201 "get_timeout_ms() -> int\n\n"
202 "Returns a timeout value suitable for usage in poll(), the value\n"
203 "returned by .get_timeout() converted to relative ms, or -1 if\n"
204 "no timeout is necessary.");
205 static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args)
206 {
207 int r;
208 uint64_t t;
209
210 r = sd_journal_get_timeout(self->j, &t);
211 set_error(r, NULL, NULL);
212 if (r < 0)
213 return NULL;
214
215 if (t == (uint64_t) -1)
216 return PyLong_FromLong(-1);
217 else {
218 struct timespec ts;
219 uint64_t n;
220 int msec;
221
222 clock_gettime(CLOCK_MONOTONIC, &ts);
223 n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
224 msec = t > n ? (int) ((t - n + 999) / 1000) : 0;
225
226 return PyLong_FromLong(msec);
227 }
228 }
229
230
231 PyDoc_STRVAR(Reader_close__doc__,
232 "close() -> None\n\n"
233 "Free resources allocated by this Reader object.\n"
234 "This method invokes sd_journal_close().\n"
235 "See man:sd_journal_close(3).");
236 static PyObject* Reader_close(Reader *self, PyObject *args)
237 {
238 assert(self);
239 assert(!args);
240
241 sd_journal_close(self->j);
242 self->j = NULL;
243 Py_RETURN_NONE;
244 }
245
246
247 PyDoc_STRVAR(Reader_get_usage__doc__,
248 "get_usage() -> int\n\n"
249 "Returns the total disk space currently used by journal\n"
250 "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was\n"
251 "passed when opening the journal this value will only reflect\n"
252 "the size of journal files of the local host, otherwise\n"
253 "of all hosts.\n\n"
254 "This method invokes sd_journal_get_usage().\n"
255 "See man:sd_journal_get_usage(3).");
256 static PyObject* Reader_get_usage(Reader *self, PyObject *args)
257 {
258 int r;
259 uint64_t bytes;
260
261 r = sd_journal_get_usage(self->j, &bytes);
262 if (set_error(r, NULL, NULL))
263 return NULL;
264
265 assert_cc(sizeof(unsigned long long) == sizeof(bytes));
266 return PyLong_FromUnsignedLongLong(bytes);
267 }
268
269
270 PyDoc_STRVAR(Reader___enter____doc__,
271 "__enter__() -> self\n\n"
272 "Part of the context manager protocol.\n"
273 "Returns self.\n");
274 static PyObject* Reader___enter__(PyObject *self, PyObject *args)
275 {
276 assert(self);
277 assert(!args);
278
279 Py_INCREF(self);
280 return self;
281 }
282
283 PyDoc_STRVAR(Reader___exit____doc__,
284 "__exit__(type, value, traceback) -> None\n\n"
285 "Part of the context manager protocol.\n"
286 "Closes the journal.\n");
287 static PyObject* Reader___exit__(Reader *self, PyObject *args)
288 {
289 assert(self);
290
291 sd_journal_close(self->j);
292 self->j = NULL;
293 Py_RETURN_NONE;
294 }
295
296
297 PyDoc_STRVAR(Reader_next__doc__,
298 "next([skip]) -> bool\n\n"
299 "Go to the next log entry. Optional skip value means to go to\n"
300 "the `skip`\\-th log entry.\n"
301 "Returns False if at end of file, True otherwise.");
302 static PyObject* Reader_next(Reader *self, PyObject *args)
303 {
304 int64_t skip = 1LL;
305 int r;
306
307 if (!PyArg_ParseTuple(args, "|L:next", &skip))
308 return NULL;
309
310 if (skip == 0LL) {
311 PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
312 return NULL;
313 }
314
315 Py_BEGIN_ALLOW_THREADS
316 if (skip == 1LL)
317 r = sd_journal_next(self->j);
318 else if (skip == -1LL)
319 r = sd_journal_previous(self->j);
320 else if (skip > 1LL)
321 r = sd_journal_next_skip(self->j, skip);
322 else if (skip < -1LL)
323 r = sd_journal_previous_skip(self->j, -skip);
324 else
325 assert_not_reached("should not be here");
326 Py_END_ALLOW_THREADS
327
328 set_error(r, NULL, NULL);
329 if (r < 0)
330 return NULL;
331 return PyBool_FromLong(r);
332 }
333
334 PyDoc_STRVAR(Reader_previous__doc__,
335 "previous([skip]) -> bool\n\n"
336 "Go to the previous log entry. Optional skip value means to \n"
337 "go to the `skip`\\-th previous log entry.\n"
338 "Returns False if at start of file, True otherwise.");
339 static PyObject* Reader_previous(Reader *self, PyObject *args)
340 {
341 int64_t skip = 1LL;
342 if (!PyArg_ParseTuple(args, "|L:previous", &skip))
343 return NULL;
344
345 return PyObject_CallMethod((PyObject *)self, (char*) "_next",
346 (char*) "L", -skip);
347 }
348
349
350 static int extract(const char* msg, size_t msg_len,
351 PyObject **key, PyObject **value) {
352 PyObject *k = NULL, *v;
353 const char *delim_ptr;
354
355 delim_ptr = memchr(msg, '=', msg_len);
356 if (!delim_ptr) {
357 PyErr_SetString(PyExc_OSError,
358 "journal gave us a field without '='");
359 return -1;
360 }
361
362 if (key) {
363 k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
364 if (!k)
365 return -1;
366 }
367
368 if (value) {
369 v = PyBytes_FromStringAndSize(delim_ptr + 1,
370 (const char*) msg + msg_len - (delim_ptr + 1));
371 if (!v) {
372 Py_XDECREF(k);
373 return -1;
374 }
375
376 *value = v;
377 }
378
379 if (key)
380 *key = k;
381
382 return 0;
383 }
384
385 PyDoc_STRVAR(Reader_get__doc__,
386 "get(str) -> str\n\n"
387 "Return data associated with this key in current log entry.\n"
388 "Throws KeyError is the data is not available.");
389 static PyObject* Reader_get(Reader *self, PyObject *args)
390 {
391 const char* field;
392 const void* msg;
393 size_t msg_len;
394 PyObject *value;
395 int r;
396
397 assert(self);
398 assert(args);
399
400 if (!PyArg_ParseTuple(args, "s:get", &field))
401 return NULL;
402
403 r = sd_journal_get_data(self->j, field, &msg, &msg_len);
404 if (r == -ENOENT) {
405 PyErr_SetString(PyExc_KeyError, field);
406 return NULL;
407 } else if (set_error(r, NULL, "field name is not valid"))
408 return NULL;
409
410 r = extract(msg, msg_len, NULL, &value);
411 if (r < 0)
412 return NULL;
413 return value;
414 }
415
416
417 PyDoc_STRVAR(Reader_get_all__doc__,
418 "_get_all() -> dict\n\n"
419 "Return dictionary of the current log entry.");
420 static PyObject* Reader_get_all(Reader *self, PyObject *args)
421 {
422 PyObject *dict;
423 const void *msg;
424 size_t msg_len;
425 int r;
426
427 dict = PyDict_New();
428 if (!dict)
429 return NULL;
430
431 SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
432 _cleanup_Py_DECREF_ PyObject *key = NULL, *value = NULL;
433
434 r = extract(msg, msg_len, &key, &value);
435 if (r < 0)
436 goto error;
437
438 if (PyDict_Contains(dict, key)) {
439 PyObject *cur_value = PyDict_GetItem(dict, key);
440
441 if (PyList_CheckExact(cur_value)) {
442 r = PyList_Append(cur_value, value);
443 if (r < 0)
444 goto error;
445 } else {
446 _cleanup_Py_DECREF_ PyObject *tmp_list = PyList_New(0);
447 if (!tmp_list)
448 goto error;
449
450 r = PyList_Append(tmp_list, cur_value);
451 if (r < 0)
452 goto error;
453
454 r = PyList_Append(tmp_list, value);
455 if (r < 0)
456 goto error;
457
458 r = PyDict_SetItem(dict, key, tmp_list);
459 if (r < 0)
460 goto error;
461 }
462 } else {
463 r = PyDict_SetItem(dict, key, value);
464 if (r < 0)
465 goto error;
466 }
467 }
468
469 return dict;
470
471 error:
472 Py_DECREF(dict);
473 return NULL;
474 }
475
476
477 PyDoc_STRVAR(Reader_get_realtime__doc__,
478 "get_realtime() -> int\n\n"
479 "Return the realtime timestamp for the current journal entry\n"
480 "in microseconds.\n\n"
481 "Wraps sd_journal_get_realtime_usec().\n"
482 "See man:sd_journal_get_realtime_usec(3).");
483 static PyObject* Reader_get_realtime(Reader *self, PyObject *args)
484 {
485 uint64_t timestamp;
486 int r;
487
488 assert(self);
489 assert(!args);
490
491 r = sd_journal_get_realtime_usec(self->j, &timestamp);
492 if (set_error(r, NULL, NULL))
493 return NULL;
494
495 assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
496 return PyLong_FromUnsignedLongLong(timestamp);
497 }
498
499
500 PyDoc_STRVAR(Reader_get_monotonic__doc__,
501 "get_monotonic() -> (timestamp, bootid)\n\n"
502 "Return the monotonic timestamp for the current journal entry\n"
503 "as a tuple of time in microseconds and bootid.\n\n"
504 "Wraps sd_journal_get_monotonic_usec().\n"
505 "See man:sd_journal_get_monotonic_usec(3).");
506 static PyObject* Reader_get_monotonic(Reader *self, PyObject *args)
507 {
508 uint64_t timestamp;
509 sd_id128_t id;
510 PyObject *monotonic, *bootid, *tuple;
511 int r;
512
513 assert(self);
514 assert(!args);
515
516 r = sd_journal_get_monotonic_usec(self->j, &timestamp, &id);
517 if (set_error(r, NULL, NULL))
518 return NULL;
519
520 assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
521 monotonic = PyLong_FromUnsignedLongLong(timestamp);
522 bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
523 #if PY_MAJOR_VERSION >= 3
524 tuple = PyStructSequence_New(&MonotonicType);
525 #else
526 tuple = PyTuple_New(2);
527 #endif
528 if (!monotonic || !bootid || !tuple) {
529 Py_XDECREF(monotonic);
530 Py_XDECREF(bootid);
531 Py_XDECREF(tuple);
532 return NULL;
533 }
534
535 #if PY_MAJOR_VERSION >= 3
536 PyStructSequence_SET_ITEM(tuple, 0, monotonic);
537 PyStructSequence_SET_ITEM(tuple, 1, bootid);
538 #else
539 PyTuple_SET_ITEM(tuple, 0, monotonic);
540 PyTuple_SET_ITEM(tuple, 1, bootid);
541 #endif
542
543 return tuple;
544 }
545
546 PyDoc_STRVAR(Reader_add_match__doc__,
547 "add_match(match) -> None\n\n"
548 "Add a match to filter journal log entries. All matches of different\n"
549 "fields are combined with logical AND, and matches of the same field\n"
550 "are automatically combined with logical OR.\n"
551 "Match is a string of the form \"FIELD=value\".");
552 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
553 {
554 char *match;
555 int match_len, r;
556 if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len))
557 return NULL;
558
559 r = sd_journal_add_match(self->j, match, match_len);
560 set_error(r, NULL, "Invalid match");
561 if (r < 0)
562 return NULL;
563
564 Py_RETURN_NONE;
565 }
566
567
568 PyDoc_STRVAR(Reader_add_disjunction__doc__,
569 "add_disjunction() -> None\n\n"
570 "Inserts a logical OR between matches added since previous\n"
571 "add_disjunction() or add_conjunction() and the next\n"
572 "add_disjunction() or add_conjunction().\n\n"
573 "See man:sd_journal_add_disjunction(3) for explanation.");
574 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
575 {
576 int r;
577 r = sd_journal_add_disjunction(self->j);
578 set_error(r, NULL, NULL);
579 if (r < 0)
580 return NULL;
581 Py_RETURN_NONE;
582 }
583
584
585 PyDoc_STRVAR(Reader_add_conjunction__doc__,
586 "add_conjunction() -> None\n\n"
587 "Inserts a logical AND between matches added since previous\n"
588 "add_disjunction() or add_conjunction() and the next\n"
589 "add_disjunction() or add_conjunction().\n\n"
590 "See man:sd_journal_add_disjunction(3) for explanation.");
591 static PyObject* Reader_add_conjunction(Reader *self, PyObject *args)
592 {
593 int r;
594 r = sd_journal_add_conjunction(self->j);
595 set_error(r, NULL, NULL);
596 if (r < 0)
597 return NULL;
598 Py_RETURN_NONE;
599 }
600
601
602 PyDoc_STRVAR(Reader_flush_matches__doc__,
603 "flush_matches() -> None\n\n"
604 "Clear all current match filters.");
605 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
606 {
607 sd_journal_flush_matches(self->j);
608 Py_RETURN_NONE;
609 }
610
611
612 PyDoc_STRVAR(Reader_seek_head__doc__,
613 "seek_head() -> None\n\n"
614 "Jump to the beginning of the journal.\n"
615 "This method invokes sd_journal_seek_head().\n"
616 "See man:sd_journal_seek_head(3).");
617 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
618 {
619 int r;
620 Py_BEGIN_ALLOW_THREADS
621 r = sd_journal_seek_head(self->j);
622 Py_END_ALLOW_THREADS
623 if (set_error(r, NULL, NULL))
624 return NULL;
625 Py_RETURN_NONE;
626 }
627
628
629 PyDoc_STRVAR(Reader_seek_tail__doc__,
630 "seek_tail() -> None\n\n"
631 "Jump to the end of the journal.\n"
632 "This method invokes sd_journal_seek_tail().\n"
633 "See man:sd_journal_seek_tail(3).");
634 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
635 {
636 int r;
637 Py_BEGIN_ALLOW_THREADS
638 r = sd_journal_seek_tail(self->j);
639 Py_END_ALLOW_THREADS
640 if (set_error(r, NULL, NULL))
641 return NULL;
642 Py_RETURN_NONE;
643 }
644
645
646 PyDoc_STRVAR(Reader_seek_realtime__doc__,
647 "seek_realtime(realtime) -> None\n\n"
648 "Seek to nearest matching journal entry to `realtime`. Argument\n"
649 "`realtime` in specified in seconds.");
650 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
651 {
652 uint64_t timestamp;
653 int r;
654
655 if (!PyArg_ParseTuple(args, "K:seek_realtime", &timestamp))
656 return NULL;
657
658 Py_BEGIN_ALLOW_THREADS
659 r = sd_journal_seek_realtime_usec(self->j, timestamp);
660 Py_END_ALLOW_THREADS
661 if (set_error(r, NULL, NULL))
662 return NULL;
663 Py_RETURN_NONE;
664 }
665
666
667 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
668 "seek_monotonic(monotonic[, bootid]) -> None\n\n"
669 "Seek to nearest matching journal entry to `monotonic`. Argument\n"
670 "`monotonic` is an timestamp from boot in microseconds.\n"
671 "Argument `bootid` is a string representing which boot the\n"
672 "monotonic time is reference to. Defaults to current bootid.");
673 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
674 {
675 char *bootid = NULL;
676 uint64_t timestamp;
677 sd_id128_t id;
678 int r;
679
680 if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", &timestamp, &bootid))
681 return NULL;
682
683 if (bootid) {
684 r = sd_id128_from_string(bootid, &id);
685 if (set_error(r, NULL, "Invalid bootid"))
686 return NULL;
687 } else {
688 Py_BEGIN_ALLOW_THREADS
689 r = sd_id128_get_boot(&id);
690 Py_END_ALLOW_THREADS
691 if (set_error(r, NULL, NULL))
692 return NULL;
693 }
694
695 Py_BEGIN_ALLOW_THREADS
696 r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
697 Py_END_ALLOW_THREADS
698 if (set_error(r, NULL, NULL))
699 return NULL;
700
701 Py_RETURN_NONE;
702 }
703
704
705 PyDoc_STRVAR(Reader_process__doc__,
706 "process() -> state change (integer)\n\n"
707 "Process events and reset the readable state of the file\n"
708 "descriptor returned by .fileno().\n\n"
709 "Will return constants: NOP if no change; APPEND if new\n"
710 "entries have been added to the end of the journal; and\n"
711 "INVALIDATE if journal files have been added or removed.\n\n"
712 "See man:sd_journal_process(3) for further discussion.");
713 static PyObject* Reader_process(Reader *self, PyObject *args)
714 {
715 int r;
716
717 assert(!args);
718
719 Py_BEGIN_ALLOW_THREADS
720 r = sd_journal_process(self->j);
721 Py_END_ALLOW_THREADS
722 if (set_error(r, NULL, NULL) < 0)
723 return NULL;
724
725 return long_FromLong(r);
726 }
727
728
729 PyDoc_STRVAR(Reader_wait__doc__,
730 "wait([timeout]) -> state change (integer)\n\n"
731 "Wait for a change in the journal. Argument `timeout` specifies\n"
732 "the maximum number of microseconds to wait before returning\n"
733 "regardless of wheter the journal has changed. If `timeout` is -1,\n"
734 "then block forever.\n\n"
735 "Will return constants: NOP if no change; APPEND if new\n"
736 "entries have been added to the end of the journal; and\n"
737 "INVALIDATE if journal files have been added or removed.\n\n"
738 "See man:sd_journal_wait(3) for further discussion.");
739 static PyObject* Reader_wait(Reader *self, PyObject *args)
740 {
741 int r;
742 int64_t timeout;
743
744 if (!PyArg_ParseTuple(args, "|L:wait", &timeout))
745 return NULL;
746
747 Py_BEGIN_ALLOW_THREADS
748 r = sd_journal_wait(self->j, timeout);
749 Py_END_ALLOW_THREADS
750 if (set_error(r, NULL, NULL) < 0)
751 return NULL;
752
753 return long_FromLong(r);
754 }
755
756
757 PyDoc_STRVAR(Reader_seek_cursor__doc__,
758 "seek_cursor(cursor) -> None\n\n"
759 "Seek to journal entry by given unique reference `cursor`.");
760 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
761 {
762 const char *cursor;
763 int r;
764
765 if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor))
766 return NULL;
767
768 Py_BEGIN_ALLOW_THREADS
769 r = sd_journal_seek_cursor(self->j, cursor);
770 Py_END_ALLOW_THREADS
771 if (set_error(r, NULL, "Invalid cursor"))
772 return NULL;
773 Py_RETURN_NONE;
774 }
775
776
777 PyDoc_STRVAR(Reader_get_cursor__doc__,
778 "get_cursor() -> str\n\n"
779 "Return a cursor string for the current journal entry.\n\n"
780 "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3).");
781 static PyObject* Reader_get_cursor(Reader *self, PyObject *args)
782 {
783 _cleanup_free_ char *cursor = NULL;
784 int r;
785
786 assert(self);
787 assert(!args);
788
789 r = sd_journal_get_cursor(self->j, &cursor);
790 if (set_error(r, NULL, NULL))
791 return NULL;
792
793 return unicode_FromString(cursor);
794 }
795
796
797 PyDoc_STRVAR(Reader_test_cursor__doc__,
798 "test_cursor(str) -> bool\n\n"
799 "Test whether the cursor string matches current journal entry.\n\n"
800 "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3).");
801 static PyObject* Reader_test_cursor(Reader *self, PyObject *args)
802 {
803 const char *cursor;
804 int r;
805
806 assert(self);
807 assert(args);
808
809 if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor))
810 return NULL;
811
812 r = sd_journal_test_cursor(self->j, cursor);
813 set_error(r, NULL, NULL);
814 if (r < 0)
815 return NULL;
816
817 return PyBool_FromLong(r);
818 }
819
820 PyDoc_STRVAR(Reader_query_unique__doc__,
821 "query_unique(field) -> a set of values\n\n"
822 "Return a set of unique values appearing in journal for the\n"
823 "given `field`. Note this does not respect any journal matches.");
824 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
825 {
826 char *query;
827 int r;
828 const void *uniq;
829 size_t uniq_len;
830 PyObject *value_set, *key, *value;
831
832 if (!PyArg_ParseTuple(args, "s:query_unique", &query))
833 return NULL;
834
835 Py_BEGIN_ALLOW_THREADS
836 r = sd_journal_query_unique(self->j, query);
837 Py_END_ALLOW_THREADS
838 if (set_error(r, NULL, "Invalid field name"))
839 return NULL;
840
841 value_set = PySet_New(0);
842 key = unicode_FromString(query);
843
844 SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
845 const char *delim_ptr;
846
847 delim_ptr = memchr(uniq, '=', uniq_len);
848 value = PyBytes_FromStringAndSize(
849 delim_ptr + 1,
850 (const char*) uniq + uniq_len - (delim_ptr + 1));
851 PySet_Add(value_set, value);
852 Py_DECREF(value);
853 }
854 Py_DECREF(key);
855 return value_set;
856 }
857
858
859 PyDoc_STRVAR(Reader_get_catalog__doc__,
860 "get_catalog() -> str\n\n"
861 "Retrieve a message catalog entry for the current journal entry.\n"
862 "Will throw IndexError if the entry has no MESSAGE_ID\n"
863 "and KeyError is the id is specified, but hasn't been found\n"
864 "in the catalog.\n\n"
865 "Wraps man:sd_journal_get_catalog(3).");
866 static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
867 {
868 int r;
869 _cleanup_free_ char *msg = NULL;
870
871 assert(self);
872 assert(!args);
873
874 Py_BEGIN_ALLOW_THREADS
875 r = sd_journal_get_catalog(self->j, &msg);
876 Py_END_ALLOW_THREADS
877 if (r == -ENOENT) {
878 const void* mid;
879 size_t mid_len;
880
881 r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
882 if (r == 0) {
883 const int l = sizeof("MESSAGE_ID");
884 assert(mid_len > l);
885 PyErr_Format(PyExc_KeyError, "%.*s", (int) mid_len - l,
886 (const char*) mid + l);
887 } else if (r == -ENOENT)
888 PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
889 else
890 set_error(r, NULL, NULL);
891 return NULL;
892 } else if (set_error(r, NULL, NULL))
893 return NULL;
894
895 return unicode_FromString(msg);
896 }
897
898
899 PyDoc_STRVAR(get_catalog__doc__,
900 "get_catalog(id128) -> str\n\n"
901 "Retrieve a message catalog entry for the given id.\n"
902 "Wraps man:sd_journal_get_catalog_for_message_id(3).");
903 static PyObject* get_catalog(PyObject *self, PyObject *args)
904 {
905 int r;
906 char *id_ = NULL;
907 sd_id128_t id;
908 _cleanup_free_ char *msg = NULL;
909
910 assert(!self);
911 assert(args);
912
913 if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
914 return NULL;
915
916 r = sd_id128_from_string(id_, &id);
917 if (set_error(r, NULL, "Invalid id128"))
918 return NULL;
919
920 Py_BEGIN_ALLOW_THREADS
921 r = sd_journal_get_catalog_for_message_id(id, &msg);
922 Py_END_ALLOW_THREADS
923 if (set_error(r, NULL, NULL))
924 return NULL;
925
926 return unicode_FromString(msg);
927 }
928
929
930 PyDoc_STRVAR(data_threshold__doc__,
931 "Threshold for field size truncation in bytes.\n\n"
932 "Fields longer than this will be truncated to the threshold size.\n"
933 "Defaults to 64Kb.");
934
935 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
936 {
937 size_t cvalue;
938 int r;
939
940 r = sd_journal_get_data_threshold(self->j, &cvalue);
941 if (set_error(r, NULL, NULL))
942 return NULL;
943
944 return long_FromSize_t(cvalue);
945 }
946
947 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
948 {
949 int r;
950 if (value == NULL) {
951 PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
952 return -1;
953 }
954 if (!long_Check(value)){
955 PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
956 return -1;
957 }
958 r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
959 return set_error(r, NULL, NULL);
960 }
961
962
963 PyDoc_STRVAR(closed__doc__,
964 "True iff journal is closed");
965 static PyObject* Reader_get_closed(Reader *self, void *closure)
966 {
967 return PyBool_FromLong(self->j == NULL);
968 }
969
970
971 static PyGetSetDef Reader_getsetters[] = {
972 {(char*) "data_threshold",
973 (getter) Reader_get_data_threshold,
974 (setter) Reader_set_data_threshold,
975 (char*) data_threshold__doc__,
976 NULL},
977 {(char*) "closed",
978 (getter) Reader_get_closed,
979 NULL,
980 (char*) closed__doc__,
981 NULL},
982 {} /* Sentinel */
983 };
984
985 static PyMethodDef Reader_methods[] = {
986 {"fileno", (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
987 {"reliable_fd", (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
988 {"get_events", (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__},
989 {"get_timeout", (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__},
990 {"get_timeout_ms", (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__},
991 {"close", (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
992 {"get_usage", (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__},
993 {"__enter__", (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
994 {"__exit__", (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
995 {"_next", (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__},
996 {"_previous", (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__},
997 {"_get", (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__},
998 {"_get_all", (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__},
999 {"_get_realtime", (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__},
1000 {"_get_monotonic", (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__},
1001 {"add_match", (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
1002 {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
1003 {"add_conjunction", (PyCFunction) Reader_add_conjunction, METH_NOARGS, Reader_add_conjunction__doc__},
1004 {"flush_matches", (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
1005 {"seek_head", (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
1006 {"seek_tail", (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
1007 {"seek_realtime", (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
1008 {"seek_monotonic", (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
1009 {"process", (PyCFunction) Reader_process, METH_NOARGS, Reader_process__doc__},
1010 {"wait", (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
1011 {"seek_cursor", (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
1012 {"_get_cursor", (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__},
1013 {"test_cursor", (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__},
1014 {"query_unique", (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
1015 {"get_catalog", (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
1016 {} /* Sentinel */
1017 };
1018
1019 static PyTypeObject ReaderType = {
1020 PyVarObject_HEAD_INIT(NULL, 0)
1021 "_reader._Reader", /*tp_name*/
1022 sizeof(Reader), /*tp_basicsize*/
1023 0, /*tp_itemsize*/
1024 (destructor)Reader_dealloc, /*tp_dealloc*/
1025 0, /*tp_print*/
1026 0, /*tp_getattr*/
1027 0, /*tp_setattr*/
1028 0, /*tp_compare*/
1029 0, /*tp_repr*/
1030 0, /*tp_as_number*/
1031 0, /*tp_as_sequence*/
1032 0, /*tp_as_mapping*/
1033 0, /*tp_hash */
1034 0, /*tp_call*/
1035 0, /*tp_str*/
1036 0, /*tp_getattro*/
1037 0, /*tp_setattro*/
1038 0, /*tp_as_buffer*/
1039 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1040 Reader__doc__, /* tp_doc */
1041 0, /* tp_traverse */
1042 0, /* tp_clear */
1043 0, /* tp_richcompare */
1044 0, /* tp_weaklistoffset */
1045 0, /* tp_iter */
1046 0, /* tp_iternext */
1047 Reader_methods, /* tp_methods */
1048 0, /* tp_members */
1049 Reader_getsetters, /* tp_getset */
1050 0, /* tp_base */
1051 0, /* tp_dict */
1052 0, /* tp_descr_get */
1053 0, /* tp_descr_set */
1054 0, /* tp_dictoffset */
1055 (initproc) Reader_init, /* tp_init */
1056 0, /* tp_alloc */
1057 PyType_GenericNew, /* tp_new */
1058 };
1059
1060 static PyMethodDef methods[] = {
1061 { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
1062 { NULL, NULL, 0, NULL } /* Sentinel */
1063 };
1064
1065 #if PY_MAJOR_VERSION >= 3
1066 static PyModuleDef module = {
1067 PyModuleDef_HEAD_INIT,
1068 "_reader",
1069 module__doc__,
1070 -1,
1071 methods,
1072 NULL, NULL, NULL, NULL
1073 };
1074 #endif
1075
1076 #if PY_MAJOR_VERSION >= 3
1077 static bool initialized = false;
1078 #endif
1079
1080 #pragma GCC diagnostic push
1081 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
1082
1083 PyMODINIT_FUNC
1084 #if PY_MAJOR_VERSION >= 3
1085 PyInit__reader(void)
1086 #else
1087 init_reader(void)
1088 #endif
1089 {
1090 PyObject* m;
1091
1092 PyDateTime_IMPORT;
1093
1094 if (PyType_Ready(&ReaderType) < 0)
1095 #if PY_MAJOR_VERSION >= 3
1096 return NULL;
1097 #else
1098 return;
1099 #endif
1100
1101 #if PY_MAJOR_VERSION >= 3
1102 m = PyModule_Create(&module);
1103 if (m == NULL)
1104 return NULL;
1105
1106 if (!initialized) {
1107 PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
1108 initialized = true;
1109 }
1110 #else
1111 m = Py_InitModule3("_reader", methods, module__doc__);
1112 if (m == NULL)
1113 return;
1114 #endif
1115
1116 Py_INCREF(&ReaderType);
1117 #if PY_MAJOR_VERSION >= 3
1118 Py_INCREF(&MonotonicType);
1119 #endif
1120 if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
1121 #if PY_MAJOR_VERSION >= 3
1122 PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
1123 #endif
1124 PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
1125 PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
1126 PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
1127 PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
1128 PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
1129 PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
1130 #if PY_MAJOR_VERSION >= 3
1131 Py_DECREF(m);
1132 return NULL;
1133 #endif
1134 }
1135
1136 #if PY_MAJOR_VERSION >= 3
1137 return m;
1138 #endif
1139 }
1140
1141 #pragma GCC diagnostic pop