chiark / gitweb /
move _cleanup_ attribute in front of the type
[elogind.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 before and afterwards.");
571 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
572 {
573     int r;
574     r = sd_journal_add_disjunction(self->j);
575     set_error(r, NULL, NULL);
576     if (r < 0)
577         return NULL;
578     Py_RETURN_NONE;
579 }
580
581
582 PyDoc_STRVAR(Reader_flush_matches__doc__,
583              "flush_matches() -> None\n\n"
584              "Clear all current match filters.");
585 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
586 {
587     sd_journal_flush_matches(self->j);
588     Py_RETURN_NONE;
589 }
590
591
592 PyDoc_STRVAR(Reader_seek_head__doc__,
593              "seek_head() -> None\n\n"
594              "Jump to the beginning of the journal.\n"
595              "This method invokes sd_journal_seek_head().\n"
596              "See man:sd_journal_seek_head(3).");
597 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
598 {
599     int r;
600     Py_BEGIN_ALLOW_THREADS
601     r = sd_journal_seek_head(self->j);
602     Py_END_ALLOW_THREADS
603     if (set_error(r, NULL, NULL))
604         return NULL;
605     Py_RETURN_NONE;
606 }
607
608
609 PyDoc_STRVAR(Reader_seek_tail__doc__,
610              "seek_tail() -> None\n\n"
611              "Jump to the end of the journal.\n"
612              "This method invokes sd_journal_seek_tail().\n"
613              "See man:sd_journal_seek_tail(3).");
614 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
615 {
616     int r;
617     Py_BEGIN_ALLOW_THREADS
618     r = sd_journal_seek_tail(self->j);
619     Py_END_ALLOW_THREADS
620     if (set_error(r, NULL, NULL))
621         return NULL;
622     Py_RETURN_NONE;
623 }
624
625
626 PyDoc_STRVAR(Reader_seek_realtime__doc__,
627              "seek_realtime(realtime) -> None\n\n"
628              "Seek to nearest matching journal entry to `realtime`. Argument\n"
629              "`realtime` in specified in seconds.");
630 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
631 {
632     uint64_t timestamp;
633     int r;
634
635     if (!PyArg_ParseTuple(args, "K:seek_realtime", &timestamp))
636         return NULL;
637
638     Py_BEGIN_ALLOW_THREADS
639     r = sd_journal_seek_realtime_usec(self->j, timestamp);
640     Py_END_ALLOW_THREADS
641     if (set_error(r, NULL, NULL))
642         return NULL;
643     Py_RETURN_NONE;
644 }
645
646
647 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
648              "seek_monotonic(monotonic[, bootid]) -> None\n\n"
649              "Seek to nearest matching journal entry to `monotonic`. Argument\n"
650              "`monotonic` is an timestamp from boot in microseconds.\n"
651              "Argument `bootid` is a string representing which boot the\n"
652              "monotonic time is reference to. Defaults to current bootid.");
653 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
654 {
655     char *bootid = NULL;
656     uint64_t timestamp;
657     sd_id128_t id;
658     int r;
659
660     if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", &timestamp, &bootid))
661         return NULL;
662
663     if (bootid) {
664         r = sd_id128_from_string(bootid, &id);
665         if (set_error(r, NULL, "Invalid bootid"))
666             return NULL;
667     } else {
668         Py_BEGIN_ALLOW_THREADS
669         r = sd_id128_get_boot(&id);
670         Py_END_ALLOW_THREADS
671         if (set_error(r, NULL, NULL))
672             return NULL;
673     }
674
675     Py_BEGIN_ALLOW_THREADS
676     r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
677     Py_END_ALLOW_THREADS
678     if (set_error(r, NULL, NULL))
679         return NULL;
680
681     Py_RETURN_NONE;
682 }
683
684
685 PyDoc_STRVAR(Reader_process__doc__,
686              "process() -> state change (integer)\n\n"
687              "Process events and reset the readable state of the file\n"
688              "descriptor returned by .fileno().\n\n"
689              "Will return constants: NOP if no change; APPEND if new\n"
690              "entries have been added to the end of the journal; and\n"
691              "INVALIDATE if journal files have been added or removed.\n\n"
692              "See man:sd_journal_process(3) for further discussion.");
693 static PyObject* Reader_process(Reader *self, PyObject *args)
694 {
695     int r;
696
697     assert(!args);
698
699     Py_BEGIN_ALLOW_THREADS
700     r = sd_journal_process(self->j);
701     Py_END_ALLOW_THREADS
702     if (set_error(r, NULL, NULL) < 0)
703         return NULL;
704
705     return long_FromLong(r);
706 }
707
708
709 PyDoc_STRVAR(Reader_wait__doc__,
710              "wait([timeout]) -> state change (integer)\n\n"
711              "Wait for a change in the journal. Argument `timeout` specifies\n"
712              "the maximum number of microseconds to wait before returning\n"
713              "regardless of wheter the journal has changed. If `timeout` is -1,\n"
714              "then block forever.\n\n"
715              "Will return constants: NOP if no change; APPEND if new\n"
716              "entries have been added to the end of the journal; and\n"
717              "INVALIDATE if journal files have been added or removed.\n\n"
718              "See man:sd_journal_wait(3) for further discussion.");
719 static PyObject* Reader_wait(Reader *self, PyObject *args)
720 {
721     int r;
722     int64_t timeout;
723
724     if (!PyArg_ParseTuple(args, "|L:wait", &timeout))
725         return NULL;
726
727     Py_BEGIN_ALLOW_THREADS
728     r = sd_journal_wait(self->j, timeout);
729     Py_END_ALLOW_THREADS
730     if (set_error(r, NULL, NULL) < 0)
731         return NULL;
732
733     return long_FromLong(r);
734 }
735
736
737 PyDoc_STRVAR(Reader_seek_cursor__doc__,
738              "seek_cursor(cursor) -> None\n\n"
739              "Seek to journal entry by given unique reference `cursor`.");
740 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
741 {
742     const char *cursor;
743     int r;
744
745     if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor))
746         return NULL;
747
748     Py_BEGIN_ALLOW_THREADS
749     r = sd_journal_seek_cursor(self->j, cursor);
750     Py_END_ALLOW_THREADS
751     if (set_error(r, NULL, "Invalid cursor"))
752         return NULL;
753     Py_RETURN_NONE;
754 }
755
756
757 PyDoc_STRVAR(Reader_get_cursor__doc__,
758              "get_cursor() -> str\n\n"
759              "Return a cursor string for the current journal entry.\n\n"
760              "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3).");
761 static PyObject* Reader_get_cursor(Reader *self, PyObject *args)
762 {
763     _cleanup_free_ char *cursor = NULL;
764     int r;
765
766     assert(self);
767     assert(!args);
768
769     r = sd_journal_get_cursor(self->j, &cursor);
770     if (set_error(r, NULL, NULL))
771         return NULL;
772
773     return unicode_FromString(cursor);
774 }
775
776
777 PyDoc_STRVAR(Reader_test_cursor__doc__,
778              "test_cursor(str) -> bool\n\n"
779              "Test whether the cursor string matches current journal entry.\n\n"
780              "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3).");
781 static PyObject* Reader_test_cursor(Reader *self, PyObject *args)
782 {
783     const char *cursor;
784     int r;
785
786     assert(self);
787     assert(args);
788
789     if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor))
790         return NULL;
791
792     r = sd_journal_test_cursor(self->j, cursor);
793     set_error(r, NULL, NULL);
794     if (r < 0)
795         return NULL;
796
797     return PyBool_FromLong(r);
798 }
799
800 PyDoc_STRVAR(Reader_query_unique__doc__,
801              "query_unique(field) -> a set of values\n\n"
802              "Return a set of unique values appearing in journal for the\n"
803              "given `field`. Note this does not respect any journal matches.");
804 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
805 {
806     char *query;
807     int r;
808     const void *uniq;
809     size_t uniq_len;
810     PyObject *value_set, *key, *value;
811
812     if (!PyArg_ParseTuple(args, "s:query_unique", &query))
813         return NULL;
814
815     Py_BEGIN_ALLOW_THREADS
816     r = sd_journal_query_unique(self->j, query);
817     Py_END_ALLOW_THREADS
818     if (set_error(r, NULL, "Invalid field name"))
819         return NULL;
820
821     value_set = PySet_New(0);
822     key = unicode_FromString(query);
823
824     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
825         const char *delim_ptr;
826
827         delim_ptr = memchr(uniq, '=', uniq_len);
828         value = PyBytes_FromStringAndSize(
829             delim_ptr + 1,
830             (const char*) uniq + uniq_len - (delim_ptr + 1));
831         PySet_Add(value_set, value);
832         Py_DECREF(value);
833     }
834     Py_DECREF(key);
835     return value_set;
836 }
837
838
839 PyDoc_STRVAR(Reader_get_catalog__doc__,
840              "get_catalog() -> str\n\n"
841              "Retrieve a message catalog entry for the current journal entry.\n"
842              "Will throw IndexError if the entry has no MESSAGE_ID\n"
843              "and KeyError is the id is specified, but hasn't been found\n"
844              "in the catalog.\n\n"
845              "Wraps man:sd_journal_get_catalog(3).");
846 static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
847 {
848     int r;
849     _cleanup_free_ char *msg = NULL;
850
851     assert(self);
852     assert(!args);
853
854     Py_BEGIN_ALLOW_THREADS
855     r = sd_journal_get_catalog(self->j, &msg);
856     Py_END_ALLOW_THREADS
857     if (r == -ENOENT) {
858         const void* mid;
859         size_t mid_len;
860
861         r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
862         if (r == 0) {
863             const int l = sizeof("MESSAGE_ID");
864             assert(mid_len > l);
865             PyErr_Format(PyExc_KeyError, "%.*s", (int) mid_len - l,
866                          (const char*) mid + l);
867         } else if (r == -ENOENT)
868             PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
869         else
870             set_error(r, NULL, NULL);
871         return NULL;
872     } else if (set_error(r, NULL, NULL))
873         return NULL;
874
875     return unicode_FromString(msg);
876 }
877
878
879 PyDoc_STRVAR(get_catalog__doc__,
880              "get_catalog(id128) -> str\n\n"
881              "Retrieve a message catalog entry for the given id.\n"
882              "Wraps man:sd_journal_get_catalog_for_message_id(3).");
883 static PyObject* get_catalog(PyObject *self, PyObject *args)
884 {
885     int r;
886     char *id_ = NULL;
887     sd_id128_t id;
888     _cleanup_free_ char *msg = NULL;
889
890     assert(!self);
891     assert(args);
892
893     if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
894         return NULL;
895
896     r = sd_id128_from_string(id_, &id);
897     if (set_error(r, NULL, "Invalid id128"))
898         return NULL;
899
900     Py_BEGIN_ALLOW_THREADS
901     r = sd_journal_get_catalog_for_message_id(id, &msg);
902     Py_END_ALLOW_THREADS
903     if (set_error(r, NULL, NULL))
904         return NULL;
905
906     return unicode_FromString(msg);
907 }
908
909
910 PyDoc_STRVAR(data_threshold__doc__,
911              "Threshold for field size truncation in bytes.\n\n"
912              "Fields longer than this will be truncated to the threshold size.\n"
913              "Defaults to 64Kb.");
914
915 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
916 {
917     size_t cvalue;
918     int r;
919
920     r = sd_journal_get_data_threshold(self->j, &cvalue);
921     if (set_error(r, NULL, NULL))
922         return NULL;
923
924     return long_FromSize_t(cvalue);
925 }
926
927 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
928 {
929     int r;
930     if (value == NULL) {
931         PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
932         return -1;
933     }
934     if (!long_Check(value)){
935         PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
936         return -1;
937     }
938     r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
939     return set_error(r, NULL, NULL);
940 }
941
942
943 PyDoc_STRVAR(closed__doc__,
944              "True iff journal is closed");
945 static PyObject* Reader_get_closed(Reader *self, void *closure)
946 {
947     return PyBool_FromLong(self->j == NULL);
948 }
949
950
951 static PyGetSetDef Reader_getsetters[] = {
952     {(char*) "data_threshold",
953      (getter) Reader_get_data_threshold,
954      (setter) Reader_set_data_threshold,
955      (char*) data_threshold__doc__,
956      NULL},
957     {(char*) "closed",
958      (getter) Reader_get_closed,
959      NULL,
960      (char*) closed__doc__,
961      NULL},
962     {} /* Sentinel */
963 };
964
965 static PyMethodDef Reader_methods[] = {
966     {"fileno",          (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
967     {"reliable_fd",     (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
968     {"get_events",      (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__},
969     {"get_timeout",     (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__},
970     {"get_timeout_ms",  (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__},
971     {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
972     {"get_usage",       (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__},
973     {"__enter__",       (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
974     {"__exit__",        (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
975     {"_next",           (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__},
976     {"_previous",       (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__},
977     {"_get",            (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__},
978     {"_get_all",        (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__},
979     {"_get_realtime",   (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__},
980     {"_get_monotonic",  (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__},
981     {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
982     {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
983     {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
984     {"seek_head",       (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
985     {"seek_tail",       (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
986     {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
987     {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
988     {"process",         (PyCFunction) Reader_process, METH_NOARGS, Reader_process__doc__},
989     {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
990     {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
991     {"_get_cursor",     (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__},
992     {"test_cursor",     (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__},
993     {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
994     {"get_catalog",     (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
995     {}  /* Sentinel */
996 };
997
998 static PyTypeObject ReaderType = {
999     PyVarObject_HEAD_INIT(NULL, 0)
1000     "_reader._Reader",                        /*tp_name*/
1001     sizeof(Reader),                           /*tp_basicsize*/
1002     0,                                        /*tp_itemsize*/
1003     (destructor)Reader_dealloc,               /*tp_dealloc*/
1004     0,                                        /*tp_print*/
1005     0,                                        /*tp_getattr*/
1006     0,                                        /*tp_setattr*/
1007     0,                                        /*tp_compare*/
1008     0,                                        /*tp_repr*/
1009     0,                                        /*tp_as_number*/
1010     0,                                        /*tp_as_sequence*/
1011     0,                                        /*tp_as_mapping*/
1012     0,                                        /*tp_hash */
1013     0,                                        /*tp_call*/
1014     0,                                        /*tp_str*/
1015     0,                                        /*tp_getattro*/
1016     0,                                        /*tp_setattro*/
1017     0,                                        /*tp_as_buffer*/
1018     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1019     Reader__doc__,                            /* tp_doc */
1020     0,                                        /* tp_traverse */
1021     0,                                        /* tp_clear */
1022     0,                                        /* tp_richcompare */
1023     0,                                        /* tp_weaklistoffset */
1024     0,                                        /* tp_iter */
1025     0,                                        /* tp_iternext */
1026     Reader_methods,                           /* tp_methods */
1027     0,                                        /* tp_members */
1028     Reader_getsetters,                        /* tp_getset */
1029     0,                                        /* tp_base */
1030     0,                                        /* tp_dict */
1031     0,                                        /* tp_descr_get */
1032     0,                                        /* tp_descr_set */
1033     0,                                        /* tp_dictoffset */
1034     (initproc) Reader_init,                   /* tp_init */
1035     0,                                        /* tp_alloc */
1036     PyType_GenericNew,                        /* tp_new */
1037 };
1038
1039 static PyMethodDef methods[] = {
1040         { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
1041         { NULL, NULL, 0, NULL }        /* Sentinel */
1042 };
1043
1044 #if PY_MAJOR_VERSION >= 3
1045 static PyModuleDef module = {
1046     PyModuleDef_HEAD_INIT,
1047     "_reader",
1048     module__doc__,
1049     -1,
1050     methods,
1051     NULL, NULL, NULL, NULL
1052 };
1053 #endif
1054
1055 #if PY_MAJOR_VERSION >= 3
1056 static bool initialized = false;
1057 #endif
1058
1059 #pragma GCC diagnostic push
1060 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
1061
1062 PyMODINIT_FUNC
1063 #if PY_MAJOR_VERSION >= 3
1064 PyInit__reader(void)
1065 #else
1066 init_reader(void)
1067 #endif
1068 {
1069     PyObject* m;
1070
1071     PyDateTime_IMPORT;
1072
1073     if (PyType_Ready(&ReaderType) < 0)
1074 #if PY_MAJOR_VERSION >= 3
1075         return NULL;
1076 #else
1077         return;
1078 #endif
1079
1080 #if PY_MAJOR_VERSION >= 3
1081     m = PyModule_Create(&module);
1082     if (m == NULL)
1083         return NULL;
1084
1085     if (!initialized) {
1086         PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
1087         initialized = true;
1088     }
1089 #else
1090     m = Py_InitModule3("_reader", methods, module__doc__);
1091     if (m == NULL)
1092         return;
1093 #endif
1094
1095     Py_INCREF(&ReaderType);
1096 #if PY_MAJOR_VERSION >= 3
1097     Py_INCREF(&MonotonicType);
1098 #endif
1099     if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
1100 #if PY_MAJOR_VERSION >= 3
1101         PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
1102 #endif
1103         PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
1104         PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
1105         PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
1106         PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
1107         PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
1108         PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY)) {
1109 #if PY_MAJOR_VERSION >= 3
1110         Py_DECREF(m);
1111         return NULL;
1112 #endif
1113     }
1114
1115 #if PY_MAJOR_VERSION >= 3
1116     return m;
1117 #endif
1118 }
1119
1120 #pragma GCC diagnostic pop