/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: 2014-07-25 22:44:20 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: teddy@recompile.se-20140725224420-4a5ct2ptt0hsc92z
Require Python 2.7.

This is in preparation for the eventual move to Python 3, which will
happen as soon as all Python modules required by Mandos are available.
The mandos-ctl and mandos-monitor programs are already portable
between Python 2.6 and Python 3 without changes; this change will
bring the requirement up to Python 2.7.

* INSTALL (Prerequisites/Libraries/Mandos Server): Document
                                                   requirement of
                                                   Python 2.7; remove
                                                   Python-argparse
                                                   which is in the
                                                   Python 2.7 standard
                                                   library.
* debian/control (Source: mandos/Build-Depends-Indep): Depend on
                                                       exactly the
                                                       python2.7
                                                       package and all
                                                       the Python 2.7
                                                       versions of the
                                                       python modules.
  (Package: mandos/Depends): - '' - but still depend on python (<=2.7)
                            and the generic versions of the Python
                            modules; this is for mandos-ctl and
                            mandos-monitor, both of which are
                            compatible with Python 3, and use
                            #!/usr/bin/python.
* mandos: Use #!/usr/bin/python2.7 instead of #!/usr/bin/python.

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-2014 Teddy Hogeborn
 
7
# Copyright © 2008-2014 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
42
42
 
43
43
import dbus
44
44
 
45
 
if sys.version_info.major == 2:
 
45
if sys.version_info[0] == 2:
46
46
    str = unicode
47
47
 
48
48
locale.setlocale(locale.LC_ALL, "")
64
64
    "ApprovalDelay": "Approval Delay",
65
65
    "ApprovalDuration": "Approval Duration",
66
66
    "Checker": "Checker",
67
 
    "ExtendedTimeout": "Extended Timeout"
68
 
}
 
67
    "ExtendedTimeout" : "Extended Timeout"
 
68
    }
69
69
defaultkeywords = ("Name", "Enabled", "Timeout", "LastCheckedOK")
70
70
domain = "se.recompile"
71
71
busname = domain + ".Mandos"
72
72
server_path = "/"
73
73
server_interface = domain + ".Mandos"
74
74
client_interface = domain + ".Mandos.Client"
75
 
version = "1.6.9"
 
75
version = "1.6.7"
76
76
 
 
77
def timedelta_to_milliseconds(td):
 
78
    """Convert a datetime.timedelta object to milliseconds"""
 
79
    return ((td.days * 24 * 60 * 60 * 1000)
 
80
            + (td.seconds * 1000)
 
81
            + (td.microseconds // 1000))
77
82
 
78
83
def milliseconds_to_string(ms):
79
84
    td = datetime.timedelta(0, 0, 0, ms)
80
 
    return ("{days}{hours:02}:{minutes:02}:{seconds:02}".format(
81
 
        days = "{}T".format(td.days) if td.days else "",
82
 
        hours = td.seconds // 3600,
83
 
        minutes = (td.seconds % 3600) // 60,
84
 
        seconds = td.seconds % 60))
 
85
    return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
 
86
            .format(days = "{0}T".format(td.days) if td.days else "",
 
87
                    hours = td.seconds // 3600,
 
88
                    minutes = (td.seconds % 3600) // 60,
 
89
                    seconds = td.seconds % 60,
 
90
                    ))
85
91
 
86
92
 
87
93
def rfc3339_duration_to_delta(duration):
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 "value" is not None, must have
116
 
                   # a "group" containing digits
117
 
        "value",   # datetime.timedelta or None
118
 
        "followers"))           # Tokens valid after this token
 
120
    Token = collections.namedtuple("Token",
 
121
                                   ("regexp", # To match token; if
 
122
                                              # "value" is not None,
 
123
                                              # must have a "group"
 
124
                                              # containing digits
 
125
                                    "value",  # datetime.timedelta or
 
126
                                              # None
 
127
                                    "followers")) # Tokens valid after
 
128
                                                  # this token
119
129
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
120
130
    # the "duration" ABNF definition in RFC 3339, Appendix A.
121
131
    token_end = Token(re.compile(r"$"), None, frozenset())
122
132
    token_second = Token(re.compile(r"(\d+)S"),
123
133
                         datetime.timedelta(seconds=1),
124
 
                         frozenset((token_end, )))
 
134
                         frozenset((token_end,)))
125
135
    token_minute = Token(re.compile(r"(\d+)M"),
126
136
                         datetime.timedelta(minutes=1),
127
137
                         frozenset((token_second, token_end)))
143
153
                       frozenset((token_month, token_end)))
144
154
    token_week = Token(re.compile(r"(\d+)W"),
145
155
                       datetime.timedelta(weeks=1),
146
 
                       frozenset((token_end, )))
 
156
                       frozenset((token_end,)))
147
157
    token_duration = Token(re.compile(r"P"), None,
148
158
                           frozenset((token_year, token_month,
149
159
                                      token_day, token_time,
151
161
    # Define starting values
152
162
    value = datetime.timedelta() # Value so far
153
163
    found_token = None
154
 
    followers = frozenset((token_duration, )) # Following valid tokens
 
164
    followers = frozenset((token_duration,)) # Following valid tokens
155
165
    s = duration                # String left to parse
156
166
    # Loop until end token is found
157
167
    while found_token is not token_end:
174
184
                break
175
185
        else:
176
186
            # No currently valid tokens were found
177
 
            raise ValueError("Invalid RFC 3339 duration: {!r}"
178
 
                             .format(duration))
 
187
            raise ValueError("Invalid RFC 3339 duration")
179
188
    # End token found
180
189
    return value
181
190
 
183
192
def string_to_delta(interval):
184
193
    """Parse a string and return a datetime.timedelta
185
194
    
186
 
    >>> string_to_delta('7d')
 
195
    >>> string_to_delta("7d")
187
196
    datetime.timedelta(7)
188
 
    >>> string_to_delta('60s')
 
197
    >>> string_to_delta("60s")
189
198
    datetime.timedelta(0, 60)
190
 
    >>> string_to_delta('60m')
 
199
    >>> string_to_delta("60m")
191
200
    datetime.timedelta(0, 3600)
192
 
    >>> string_to_delta('24h')
 
201
    >>> string_to_delta("24h")
193
202
    datetime.timedelta(1)
194
 
    >>> string_to_delta('1w')
 
203
    >>> string_to_delta("1w")
195
204
    datetime.timedelta(7)
196
 
    >>> string_to_delta('5m 30s')
 
205
    >>> string_to_delta("5m 30s")
197
206
    datetime.timedelta(0, 330)
198
207
    """
199
208
    
220
229
            value += datetime.timedelta(0, 0, 0, int(num))
221
230
    return value
222
231
 
223
 
 
224
232
def print_clients(clients, keywords):
225
233
    def valuetostring(value, keyword):
226
234
        if type(value) is dbus.Boolean:
232
240
    
233
241
    # Create format string to print table rows
234
242
    format_string = " ".join("{{{key}:{width}}}".format(
235
 
        width = max(len(tablewords[key]),
236
 
                    max(len(valuetostring(client[key], key))
237
 
                        for client in clients)),
238
 
        key = key)
239
 
                             for key in keywords)
 
243
            width = max(len(tablewords[key]),
 
244
                        max(len(valuetostring(client[key],
 
245
                                              key))
 
246
                            for client in
 
247
                            clients)),
 
248
            key = key) for key in keywords)
240
249
    # Print header line
241
250
    print(format_string.format(**tablewords))
242
251
    for client in clients:
243
 
        print(format_string.format(**{
244
 
            key: valuetostring(client[key], key)
245
 
            for key in keywords }))
246
 
 
 
252
        print(format_string.format(**dict((key,
 
253
                                           valuetostring(client[key],
 
254
                                                         key))
 
255
                                          for key in keywords)))
247
256
 
248
257
def has_actions(options):
249
258
    return any((options.enable,
265
274
                options.approve,
266
275
                options.deny))
267
276
 
268
 
 
269
277
def main():
270
278
    parser = argparse.ArgumentParser()
271
279
    parser.add_argument("--version", action="version",
272
 
                        version = "%(prog)s {}".format(version),
 
280
                        version = "%(prog)s {0}".format(version),
273
281
                        help="show version number and exit")
274
282
    parser.add_argument("-a", "--all", action="store_true",
275
283
                        help="Select all clients")
336
344
        bus = dbus.SystemBus()
337
345
        mandos_dbus_objc = bus.get_object(busname, server_path)
338
346
    except dbus.exceptions.DBusException:
339
 
        print("Could not connect to Mandos server", file=sys.stderr)
 
347
        print("Could not connect to Mandos server",
 
348
              file=sys.stderr)
340
349
        sys.exit(1)
341
350
    
342
351
    mandos_serv = dbus.Interface(mandos_dbus_objc,
363
372
    clients={}
364
373
    
365
374
    if options.all or not options.client:
366
 
        clients = { bus.get_object(busname, path): properties
367
 
                    for path, properties in mandos_clients.items() }
 
375
        clients = dict((bus.get_object(busname, path), properties)
 
376
                       for path, properties in
 
377
                       mandos_clients.items())
368
378
    else:
369
379
        for name in options.client:
370
 
            for path, client in mandos_clients.items():
 
380
            for path, client in mandos_clients.iteritems():
371
381
                if client["Name"] == name:
372
382
                    client_objc = bus.get_object(busname, path)
373
383
                    clients[client_objc] = client
374
384
                    break
375
385
            else:
376
 
                print("Client not found on server: {!r}"
 
386
                print("Client not found on server: {0!r}"
377
387
                      .format(name), file=sys.stderr)
378
388
                sys.exit(1)
379
389
    
380
390
    if not has_actions(options) and clients:
381
391
        if options.verbose:
382
 
            keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
383
 
                        "Created", "Interval", "Host", "Fingerprint",
384
 
                        "CheckerRunning", "LastEnabled",
385
 
                        "ApprovalPending", "ApprovedByDefault",
 
392
            keywords = ("Name", "Enabled", "Timeout",
 
393
                        "LastCheckedOK", "Created", "Interval",
 
394
                        "Host", "Fingerprint", "CheckerRunning",
 
395
                        "LastEnabled", "ApprovalPending",
 
396
                        "ApprovedByDefault",
386
397
                        "LastApprovalRequest", "ApprovalDelay",
387
398
                        "ApprovalDuration", "Checker",
388
399
                        "ExtendedTimeout")
393
404
    else:
394
405
        # Process each client in the list by all selected options
395
406
        for client in clients:
396
 
            
397
407
            def set_client_prop(prop, value):
398
408
                """Set a Client D-Bus property"""
399
409
                client.Set(client_interface, prop, value,
400
410
                           dbus_interface=dbus.PROPERTIES_IFACE)
401
 
            
402
411
            def set_client_prop_ms(prop, value):
403
412
                """Set a Client D-Bus property, converted
404
413
                from a string to milliseconds."""
405
414
                set_client_prop(prop,
406
 
                                string_to_delta(value).total_seconds()
407
 
                                * 1000)
408
 
            
 
415
                                timedelta_to_milliseconds
 
416
                                (string_to_delta(value)))
409
417
            if options.remove:
410
418
                mandos_serv.RemoveClient(client.__dbus_object_path__)
411
419
            if options.enable:
455
463
                client.Approve(dbus.Boolean(False),
456
464
                               dbus_interface=client_interface)
457
465
 
458
 
 
459
466
if __name__ == "__main__":
460
467
    main()