/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos-ctl

  • Committer: Teddy Hogeborn
  • Date: 2016-06-28 18:46:40 UTC
  • Revision ID: teddy@recompile.se-20160628184640-22uh54op9nw0908b
mandos-ctl: Dump booleans as booleans in --dump-json output.

* mandos-ctl (main): Detect dbus.Boolean objects and recast them to
                     regular Python bool() objects to make them show
                     as boolean values in dump output.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
4
4
# Mandos Monitor - Control and monitor the Mandos server
5
5
6
 
# Copyright © 2008-2015 Teddy Hogeborn
7
 
# Copyright © 2008-2015 Björn Påhlsson
 
6
# Copyright © 2008-2016 Teddy Hogeborn
 
7
# Copyright © 2008-2016 Björn Påhlsson
8
8
9
9
# This program is free software: you can redistribute it and/or modify
10
10
# it under the terms of the GNU General Public License as published by
39
39
import os
40
40
import collections
41
41
import doctest
 
42
import json
42
43
 
43
44
import dbus
44
45
 
72
73
server_path = "/"
73
74
server_interface = domain + ".Mandos"
74
75
client_interface = domain + ".Mandos.Client"
75
 
version = "1.6.9"
76
 
 
 
76
version = "1.7.10"
 
77
 
 
78
 
 
79
try:
 
80
    dbus.OBJECT_MANAGER_IFACE
 
81
except AttributeError:
 
82
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
77
83
 
78
84
def milliseconds_to_string(ms):
79
85
    td = datetime.timedelta(0, 0, 0, ms)
111
117
    # avoid excessive use of external libraries.
112
118
    
113
119
    # New type for defining tokens, syntax, and semantics all-in-one
114
 
    Token = collections.namedtuple("Token",
115
 
                                   ("regexp", # To match token; if
116
 
                                              # "value" is not None,
117
 
                                              # must have a "group"
118
 
                                              # containing digits
119
 
                                    "value",  # datetime.timedelta or
120
 
                                              # None
121
 
                                    "followers")) # Tokens valid after
122
 
                                                  # this token
 
120
    Token = collections.namedtuple("Token", (
 
121
        "regexp",  # To match token; if "value" is not None, must have
 
122
                   # a "group" containing digits
 
123
        "value",   # datetime.timedelta or None
 
124
        "followers"))           # Tokens valid after this token
123
125
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
124
126
    # the "duration" ABNF definition in RFC 3339, Appendix A.
125
127
    token_end = Token(re.compile(r"$"), None, frozenset())
178
180
                break
179
181
        else:
180
182
            # No currently valid tokens were found
181
 
            raise ValueError("Invalid RFC 3339 duration")
 
183
            raise ValueError("Invalid RFC 3339 duration: {!r}"
 
184
                             .format(duration))
182
185
    # End token found
183
186
    return value
184
187
 
186
189
def string_to_delta(interval):
187
190
    """Parse a string and return a datetime.timedelta
188
191
    
189
 
    >>> string_to_delta("7d")
 
192
    >>> string_to_delta('7d')
190
193
    datetime.timedelta(7)
191
 
    >>> string_to_delta("60s")
 
194
    >>> string_to_delta('60s')
192
195
    datetime.timedelta(0, 60)
193
 
    >>> string_to_delta("60m")
 
196
    >>> string_to_delta('60m')
194
197
    datetime.timedelta(0, 3600)
195
 
    >>> string_to_delta("24h")
 
198
    >>> string_to_delta('24h')
196
199
    datetime.timedelta(1)
197
 
    >>> string_to_delta("1w")
 
200
    >>> string_to_delta('1w')
198
201
    datetime.timedelta(7)
199
 
    >>> string_to_delta("5m 30s")
 
202
    >>> string_to_delta('5m 30s')
200
203
    datetime.timedelta(0, 330)
201
204
    """
202
205
    
278
281
                        help="Select all clients")
279
282
    parser.add_argument("-v", "--verbose", action="store_true",
280
283
                        help="Print all fields")
 
284
    parser.add_argument("-j", "--dump-json", action="store_true",
 
285
                        help="Dump client data in JSON format")
281
286
    parser.add_argument("-e", "--enable", action="store_true",
282
287
                        help="Enable client")
283
288
    parser.add_argument("-d", "--disable", action="store_true",
326
331
    if has_actions(options) and not (options.client or options.all):
327
332
        parser.error("Options require clients names or --all.")
328
333
    if options.verbose and has_actions(options):
329
 
        parser.error("--verbose can only be used alone or with"
330
 
                     " --all.")
 
334
        parser.error("--verbose can only be used alone.")
 
335
    if options.dump_json and (options.verbose or has_actions(options)):
 
336
        parser.error("--dump-json can only be used alone.")
331
337
    if options.all and not has_actions(options):
332
338
        parser.error("--all requires an action.")
333
 
 
 
339
    
334
340
    if options.check:
335
341
        fail_count, test_count = doctest.testmod()
336
342
        sys.exit(os.EX_OK if fail_count == 0 else 1)
344
350
    
345
351
    mandos_serv = dbus.Interface(mandos_dbus_objc,
346
352
                                 dbus_interface = server_interface)
 
353
    mandos_serv_object_manager = dbus.Interface(
 
354
        mandos_dbus_objc, dbus_interface = dbus.OBJECT_MANAGER_IFACE)
347
355
    
348
356
    #block stderr since dbus library prints to stderr
349
357
    null = os.open(os.path.devnull, os.O_RDWR)
352
360
    os.close(null)
353
361
    try:
354
362
        try:
355
 
            mandos_clients = mandos_serv.GetAllClientsWithProperties()
 
363
            mandos_clients = { path: ifs_and_props[client_interface]
 
364
                               for path, ifs_and_props in
 
365
                               mandos_serv_object_manager
 
366
                               .GetManagedObjects().items()
 
367
                               if client_interface in ifs_and_props }
356
368
        finally:
357
369
            #restore stderr
358
370
            os.dup2(stderrcopy, sys.stderr.fileno())
359
371
            os.close(stderrcopy)
360
 
    except dbus.exceptions.DBusException:
361
 
        print("Access denied: Accessing mandos server through dbus.",
362
 
              file=sys.stderr)
 
372
    except dbus.exceptions.DBusException as e:
 
373
        print("Access denied: Accessing mandos server through D-Bus: {}"
 
374
              .format(e), file=sys.stderr)
363
375
        sys.exit(1)
364
376
    
365
377
    # Compile dict of (clients: properties) to process
381
393
                sys.exit(1)
382
394
    
383
395
    if not has_actions(options) and clients:
384
 
        if options.verbose:
 
396
        if options.verbose or options.dump_json:
385
397
            keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
386
398
                        "Created", "Interval", "Host", "Fingerprint",
387
399
                        "CheckerRunning", "LastEnabled",
392
404
        else:
393
405
            keywords = defaultkeywords
394
406
        
395
 
        print_clients(clients.values(), keywords)
 
407
        if options.dump_json:
 
408
            json.dump({client["Name"]: {key:
 
409
                                        bool(client[key])
 
410
                                        if isinstance(client[key],
 
411
                                                      dbus.Boolean)
 
412
                                        else client[key]
 
413
                                        for key in keywords }
 
414
                       for client in clients.values() },
 
415
                      fp = sys.stdout, indent = 4,
 
416
                      separators = (',', ': '))
 
417
            print()
 
418
        else:
 
419
            print_clients(clients.values(), keywords)
396
420
    else:
397
421
        # Process each client in the list by all selected options
398
422
        for client in clients: