/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

  • Committer: Teddy Hogeborn
  • Date: 2016-03-09 21:23:21 UTC
  • Revision ID: teddy@recompile.se-20160309212321-2qlkzj9tecepc8xq
Server: Add Python 3 compatibility

Add Python 3 compatibility by not using the python-avahi module.  Also
fix miscellaneous things which differs in Python 3.  Especially hard
to fix is loading and saving clients data between Python 3 and 2,
since pickle formats have problems with strings.

* INSTALL: Remove python-avahi (and change python-gobject to
  python-gi, which is preferred now).
* debian/control (Source: mandos/Build-Depends-Indep): Remove
  "python-avahi".
* mandos: Wrap future_builtins import in try-except clause.  Do not
  import avahi module.  Use codecs.decode(..., "base64) instead of
  .decode("base64).  Use .keys(), .values(), and .items() instead of
  .iterkeys(), .itervalues(), and .iteritems().
  (alternate_dbus_interfaces/wrapper): Python 3 still requires the
  "black magic", but luckily it still works.  The Python 3 type()
  constructor requires first argument to be a string, not a byte
  string.
  (copy_function): New.  Use throughout.
  (Avahi, avahi): New class and global variable.
  (GnuTLS._need_version): Changed to be a byte string.
  (main): Decode byte strings loaded from pickle file.
  (main/cleanup): Dump using pickle prototoc 2 which Python 2 can
  read.

Show diffs side-by-side

added added

removed removed

Lines of Context:
79
79
 
80
80
import dbus
81
81
import dbus.service
82
 
from gi.repository import GLib
 
82
try:
 
83
    from gi.repository import GObject
 
84
except ImportError:
 
85
    import gobject as GObject
83
86
from dbus.mainloop.glib import DBusGMainLoop
84
87
import ctypes
85
88
import ctypes.util
97
100
if sys.version_info.major == 2:
98
101
    str = unicode
99
102
 
100
 
version = "1.7.6"
 
103
version = "1.7.5"
101
104
stored_state_file = "clients.pickle"
102
105
 
103
106
logger = logging.getLogger()
754
757
    checker:    subprocess.Popen(); a running checker process used
755
758
                                    to see if the client lives.
756
759
                                    'None' if no process is running.
757
 
    checker_callback_tag: a GLib event source tag, or None
 
760
    checker_callback_tag: a GObject event source tag, or None
758
761
    checker_command: string; External command which is run to check
759
762
                     if client lives.  %() expansions are done at
760
763
                     runtime with vars(self) as dict, so that for
761
764
                     instance %(name)s can be used in the command.
762
 
    checker_initiator_tag: a GLib event source tag, or None
 
765
    checker_initiator_tag: a GObject event source tag, or None
763
766
    created:    datetime.datetime(); (UTC) object creation
764
767
    client_structure: Object describing what attributes a client has
765
768
                      and is used for storing the client at exit
766
769
    current_checker_command: string; current running checker_command
767
 
    disable_initiator_tag: a GLib event source tag, or None
 
770
    disable_initiator_tag: a GObject event source tag, or None
768
771
    enabled:    bool()
769
772
    fingerprint: string (40 or 32 hexadecimal digits); used to
770
773
                 uniquely identify the client
926
929
        if not quiet:
927
930
            logger.info("Disabling client %s", self.name)
928
931
        if getattr(self, "disable_initiator_tag", None) is not None:
929
 
            GLib.source_remove(self.disable_initiator_tag)
 
932
            GObject.source_remove(self.disable_initiator_tag)
930
933
            self.disable_initiator_tag = None
931
934
        self.expires = None
932
935
        if getattr(self, "checker_initiator_tag", None) is not None:
933
 
            GLib.source_remove(self.checker_initiator_tag)
 
936
            GObject.source_remove(self.checker_initiator_tag)
934
937
            self.checker_initiator_tag = None
935
938
        self.stop_checker()
936
939
        self.enabled = False
937
940
        if not quiet:
938
941
            self.send_changedstate()
939
 
        # Do not run this again if called by a GLib.timeout_add
 
942
        # Do not run this again if called by a GObject.timeout_add
940
943
        return False
941
944
    
942
945
    def __del__(self):
946
949
        # Schedule a new checker to be started an 'interval' from now,
947
950
        # and every interval from then on.
948
951
        if self.checker_initiator_tag is not None:
949
 
            GLib.source_remove(self.checker_initiator_tag)
950
 
        self.checker_initiator_tag = GLib.timeout_add(
 
952
            GObject.source_remove(self.checker_initiator_tag)
 
953
        self.checker_initiator_tag = GObject.timeout_add(
951
954
            int(self.interval.total_seconds() * 1000),
952
955
            self.start_checker)
953
956
        # Schedule a disable() when 'timeout' has passed
954
957
        if self.disable_initiator_tag is not None:
955
 
            GLib.source_remove(self.disable_initiator_tag)
956
 
        self.disable_initiator_tag = GLib.timeout_add(
 
958
            GObject.source_remove(self.disable_initiator_tag)
 
959
        self.disable_initiator_tag = GObject.timeout_add(
957
960
            int(self.timeout.total_seconds() * 1000), self.disable)
958
961
        # Also start a new checker *right now*.
959
962
        self.start_checker()
995
998
        if timeout is None:
996
999
            timeout = self.timeout
997
1000
        if self.disable_initiator_tag is not None:
998
 
            GLib.source_remove(self.disable_initiator_tag)
 
1001
            GObject.source_remove(self.disable_initiator_tag)
999
1002
            self.disable_initiator_tag = None
1000
1003
        if getattr(self, "enabled", False):
1001
 
            self.disable_initiator_tag = GLib.timeout_add(
 
1004
            self.disable_initiator_tag = GObject.timeout_add(
1002
1005
                int(timeout.total_seconds() * 1000), self.disable)
1003
1006
            self.expires = datetime.datetime.utcnow() + timeout
1004
1007
    
1059
1062
                args = (pipe[1], subprocess.call, command),
1060
1063
                kwargs = popen_args)
1061
1064
            self.checker.start()
1062
 
            self.checker_callback_tag = GLib.io_add_watch(
1063
 
                pipe[0].fileno(), GLib.IO_IN,
 
1065
            self.checker_callback_tag = GObject.io_add_watch(
 
1066
                pipe[0].fileno(), GObject.IO_IN,
1064
1067
                self.checker_callback, pipe[0], command)
1065
 
        # Re-run this periodically if run by GLib.timeout_add
 
1068
        # Re-run this periodically if run by GObject.timeout_add
1066
1069
        return True
1067
1070
    
1068
1071
    def stop_checker(self):
1069
1072
        """Force the checker process, if any, to stop."""
1070
1073
        if self.checker_callback_tag:
1071
 
            GLib.source_remove(self.checker_callback_tag)
 
1074
            GObject.source_remove(self.checker_callback_tag)
1072
1075
            self.checker_callback_tag = None
1073
1076
        if getattr(self, "checker", None) is None:
1074
1077
            return
1829
1832
    
1830
1833
    def approve(self, value=True):
1831
1834
        self.approved = value
1832
 
        GLib.timeout_add(int(self.approval_duration.total_seconds()
1833
 
                             * 1000), self._reset_approved)
 
1835
        GObject.timeout_add(int(self.approval_duration.total_seconds()
 
1836
                                * 1000), self._reset_approved)
1834
1837
        self.send_changedstate()
1835
1838
    
1836
1839
    ## D-Bus methods, signals & properties
2046
2049
                if (getattr(self, "disable_initiator_tag", None)
2047
2050
                    is None):
2048
2051
                    return
2049
 
                GLib.source_remove(self.disable_initiator_tag)
2050
 
                self.disable_initiator_tag = GLib.timeout_add(
 
2052
                GObject.source_remove(self.disable_initiator_tag)
 
2053
                self.disable_initiator_tag = GObject.timeout_add(
2051
2054
                    int((self.expires - now).total_seconds() * 1000),
2052
2055
                    self.disable)
2053
2056
    
2073
2076
            return
2074
2077
        if self.enabled:
2075
2078
            # Reschedule checker run
2076
 
            GLib.source_remove(self.checker_initiator_tag)
2077
 
            self.checker_initiator_tag = GLib.timeout_add(
 
2079
            GObject.source_remove(self.checker_initiator_tag)
 
2080
            self.checker_initiator_tag = GObject.timeout_add(
2078
2081
                value, self.start_checker)
2079
2082
            self.start_checker() # Start one now, too
2080
2083
    
2484
2487
        gnutls_priority GnuTLS priority string
2485
2488
        use_dbus:       Boolean; to emit D-Bus signals or not
2486
2489
    
2487
 
    Assumes a GLib.MainLoop event loop.
 
2490
    Assumes a GObject.MainLoop event loop.
2488
2491
    """
2489
2492
    
2490
2493
    def __init__(self, server_address, RequestHandlerClass,
2515
2518
    
2516
2519
    def add_pipe(self, parent_pipe, proc):
2517
2520
        # Call "handle_ipc" for both data and EOF events
2518
 
        GLib.io_add_watch(
 
2521
        GObject.io_add_watch(
2519
2522
            parent_pipe.fileno(),
2520
 
            GLib.IO_IN | GLib.IO_HUP,
 
2523
            GObject.IO_IN | GObject.IO_HUP,
2521
2524
            functools.partial(self.handle_ipc,
2522
2525
                              parent_pipe = parent_pipe,
2523
2526
                              proc = proc))
2527
2530
                   proc = None,
2528
2531
                   client_object=None):
2529
2532
        # error, or the other end of multiprocessing.Pipe has closed
2530
 
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
 
2533
        if condition & (GObject.IO_ERR | GObject.IO_HUP):
2531
2534
            # Wait for other process to exit
2532
2535
            proc.join()
2533
2536
            return False
2554
2557
                parent_pipe.send(False)
2555
2558
                return False
2556
2559
            
2557
 
            GLib.io_add_watch(
 
2560
            GObject.io_add_watch(
2558
2561
                parent_pipe.fileno(),
2559
 
                GLib.IO_IN | GLib.IO_HUP,
 
2562
                GObject.IO_IN | GObject.IO_HUP,
2560
2563
                functools.partial(self.handle_ipc,
2561
2564
                                  parent_pipe = parent_pipe,
2562
2565
                                  proc = proc,
2992
2995
        # Close all input and output, do double fork, etc.
2993
2996
        daemon()
2994
2997
    
2995
 
    # multiprocessing will use threads, so before we use GLib we need
2996
 
    # to inform GLib that threads will be used.
2997
 
    GLib.threads_init()
 
2998
    # multiprocessing will use threads, so before we use GObject we
 
2999
    # need to inform GObject that threads will be used.
 
3000
    GObject.threads_init()
2998
3001
    
2999
3002
    global main_loop
3000
3003
    # From the Avahi example code
3001
3004
    DBusGMainLoop(set_as_default=True)
3002
 
    main_loop = GLib.MainLoop()
 
3005
    main_loop = GObject.MainLoop()
3003
3006
    bus = dbus.SystemBus()
3004
3007
    # End of Avahi example code
3005
3008
    if use_dbus:
3063
3066
                                      else key): value
3064
3067
                                     for key, value in
3065
3068
                                     bytes_clients_data.items() }
3066
 
                    del bytes_clients_data
3067
3069
                    for key in clients_data:
3068
3070
                        value = { (k.decode("utf-8")
3069
3071
                                   if isinstance(k, bytes) else k): v
3081
3083
                            if isinstance(value[k], bytes):
3082
3084
                                value[k] = value[k].decode("utf-8")
3083
3085
                    ## old_client_settings
3084
 
                    # .keys()
 
3086
                    # .keys
3085
3087
                    old_client_settings = {
3086
3088
                        (key.decode("utf-8")
3087
3089
                         if isinstance(key, bytes)
3088
3090
                         else key): value
3089
3091
                        for key, value in
3090
3092
                        bytes_old_client_settings.items() }
3091
 
                    del bytes_old_client_settings
3092
3093
                    # .host
3093
3094
                    for value in old_client_settings.values():
3094
 
                        if isinstance(value["host"], bytes):
3095
 
                            value["host"] = (value["host"]
3096
 
                                             .decode("utf-8"))
 
3095
                        value["host"] = value["host"].decode("utf-8")
3097
3096
            os.remove(stored_state_path)
3098
3097
        except IOError as e:
3099
3098
            if e.errno == errno.ENOENT:
3200
3199
        del pidfile
3201
3200
        del pidfilename
3202
3201
    
3203
 
    for termsig in (signal.SIGHUP, signal.SIGTERM):
3204
 
        GLib.unix_signal_add(GLib.PRIORITY_HIGH, termsig,
3205
 
                             lambda: main_loop.quit() and False)
 
3202
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
 
3203
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
3206
3204
    
3207
3205
    if use_dbus:
3208
3206
        
3419
3417
                sys.exit(1)
3420
3418
            # End of Avahi example code
3421
3419
        
3422
 
        GLib.io_add_watch(tcp_server.fileno(), GLib.IO_IN,
3423
 
                          lambda *args, **kwargs:
3424
 
                          (tcp_server.handle_request
3425
 
                           (*args[2:], **kwargs) or True))
 
3420
        GObject.io_add_watch(tcp_server.fileno(), GObject.IO_IN,
 
3421
                             lambda *args, **kwargs:
 
3422
                             (tcp_server.handle_request
 
3423
                              (*args[2:], **kwargs) or True))
3426
3424
        
3427
3425
        logger.debug("Starting main loop")
3428
3426
        main_loop.run()