Skip to content
Snippets Groups Projects
Select Git revision
  • 66c2b9207adc841b65d8b3d11570a69f81fa725c
  • master default
  • dbck-q-n-d-link
  • foutput-text_stat-override
  • generations
  • text-stat-sha256
  • use-nettle
  • import-nettle
  • refactor-cached_get_text
  • refactor-cached_get_text-part-2
  • add-text_store
  • introduce-generation_position
  • remove-reclamation
  • dbfile-temp-filenames
  • sstrdup
  • dbfile_open_read-check-magic
  • adns_dist
  • liboop_dist
  • search
  • isc
  • dbdbckmultiplechoice
  • last.cvs.revision
  • 2.1.2
  • 2.1.1
  • 2.1.0
  • adns_1_0
  • liboop_0_9
  • 2.0.7
  • search_bp
  • 2.0.6
  • 2.0.5
  • isc_1_01
  • Protocol-A-10.4
  • 2.0.4
  • 2.0.3
  • 2.0.2
  • 2.0.1
  • 2.0.0
  • isc_1_00
  • isc_merge_1999_05_01
  • isc_merge_1999_04_21
41 results

connections.c

Blame
  • locksuite.py 7.59 KiB
    # Lock the test suite.
    # Copyright (C) 2002  Lysator Academic Computer Association.
    #
    # This file is part of the LysKOM server.
    # 
    # LysKOM is free software; you can redistribute it and/or modify it
    # under the terms of the GNU General Public License as published by 
    # the Free Software Foundation; either version 1, or (at your option) 
    # any later version.
    # 
    # LysKOM is distributed in the hope that it will be useful, but WITHOUT
    # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    # for more details.
    # 
    # You should have received a copy of the GNU General Public License
    # along with LysKOM; see the file COPYING.  If not, write to
    # Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
    # or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
    # MA 02139, USA.
    #
    # Please mail bug reports to bug-lyskom@lysator.liu.se.
    
    # This program is used by the test suite to ensure that only one test
    # is running at a time.  It obtains two locks:
    #
    #   - first, a socket bound to a specific port.  This lock ensures
    #     that only a single test is run on this computer.  If two tests
    #     were run at the same time, they would interfere with each other,
    #     since static port is used by the test suite.
    #
    #   - secondly, a symlink in the current working directory, that
    #     points to the host and pid of this process.  This lock ensures
    #     that the test suite isn't used by two processes over a shared
    #     filesystem (such as NFS).
    #
    # This process communicates with the test suite on stdin/stdout.
    # It understands a single command: "exit\n".  It can send these
    # responses:
    #
    # "locking...\n": this is sent as soon as the process is started.
    #
    # "waiting: socket $USER $CWD\n": if the socket lock is already taken,
    #     this message will be printed, with $USER replaced by the
    #     username and $CWD with the current working directory of the
    #     process that already holds the lock.  This message may be issued
    #     several times, but never with the same values of both $USER and
    #     $CWD.
    #
    # "waiting: file $LOCK $HOST:$PID\n": if the symlink lock is already
    #     taken, this message will be printed, with $LOCK replaced by the
    #     full path to the symlink lock file, $HOST replaced by the host
    #     name and $PID by the pid of the process holding the lock.  This
    #     message may be issued several times, but never with the same
    #     values of bot $HOST and $PID.
    #
    # "failed: file $LOCK $HOST $PID\n": the symlink lock may be a stale
    #     lock.  If $HOST is the local host, the program can check for
    #     stale locks and break them.  However, it it is held by a remote
    #     host, that isn't possible.  After waiting an hour for a lock
    #     that is still held by the same remote host and pid, this program
    #     will print this message and stop trying to obtain the lock.  If
    #     this happens, the process should send the "exit\n" command.
    #
    # "locked\n": the lock has been successfully obtained.
    #
    # "queued: socket $USER $CWD\n": this is printed when the lock is
    #     held, and another process wants it.  The lock is still held.
    #
    # "bye\n": this is sent as a response to the "exit\n" command.  If any
    #     locks were held, they are now released.  After printing this
    #     response, the process will exit.
    
    import socket
    import select
    import sys
    import errno
    import string
    import os
    import select
    import getpass
    import time
    
    LOCKNAME = "locksuite.lock"
    
    __reported_queued = {}
    
    def try_socket():
        s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try:
            s.bind(('', 53263))
        except socket.error, e:
            if e[0] != errno.EADDRINUSE:
                raise
            return None
        s.listen(3)
        return s
    
    def try_symlink(hostname, pid):
        """Try to lock the symlink.
    
        Return values:
          None: ok
          'host:pid': the lock was already held.
        """
    
        fn = "%s:%d" % (hostname, pid)
        while 1:
            try:
                os.symlink(fn, LOCKNAME)
                return None
            except os.error, e:
                if e.errno != errno.EEXIST:
                    raise
            locker = None
            try:
                locker = os.readlink(LOCKNAME)
            except os.error, e:
                if e.errno != errno.ENOENT:
                    raise
            if locker != None:
                if locker[:len(hostname)] != hostname:
                    # Locked by a foreign host.
                    return locker
                oldpid = locker[len(hostname):]
    
                if len(oldpid) < 2 or oldpid[0] != ":":
                    # Broken lock file.  Return it anyhow.
                    return locker
    
                try:
                    os.kill(string.atoi(oldpid[1:]), 0)
                    # Lock owner still living.
                    return locker
                except os.error, e:
                    if e.errno == errno.EPERM:
                        # Lock owner still living.
                        return locker
                    elif e.errno == errno.ESRCH:
                        os.unlink(LOCKNAME)
                    else:
                        raise
            time.sleep(1) # Just in case...
    
    def myhostname():
        host = socket.gethostname()
        try:
            fqdn = socket.gethostbyaddr(host)
            return fqdn[0]
        except socket.error:
            return host
    
    def my_id():
        return "%s %s" % (getpass.getuser(), os.getcwd())
    
    def poll_input(s, timeout):
        global __reported_queued
    
        pend = [sys.stdin]
        if s != None:
            pend.append(s)
        (r, w, e) = select.select(pend, [], [], timeout)
        if sys.stdin in r:
            line = sys.stdin.readline() # Ignore the result.
            sys.exit(0)
        if s in r:
            (other, addr) = s.accept()
            other.send(my_id())
            their_id = other.recv(100)
            if not __reported_queued.has_key(their_id):
                print "queued: socket", their_id
                sys.stdout.flush()
                __reported_queued[their_id] = None
            other.close()
            
    
    def get_socket_lock():
        reported_sockets = {}
        while 1:
            s = try_socket()
            if s != None:
                return s
            holder = get_holder()
            if holder != None and not reported_sockets.has_key(holder):
                print "waiting: socket", holder
                sys.stdout.flush()
                reported_sockets[holder] = None
            poll_input(None, 1.0)
    
    
    def get_holder():
        s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try:
            s.connect(('', 53263))
        except socket.error:
            return None
        s.send(my_id())
        their_id = s.recv(100)
        s.close()
        return their_id
    
    def get_link_lock():
        reported_symlinks = {}
        start = time.time()
        host = myhostname()
        pid = os.getpid()
        while time.time() < start + 3600:
            lnk = try_symlink(host, pid)
            if lnk == None:
                return LOCKNAME
            if not reported_symlinks.has_key(lnk):
                print "waiting: file", os.path.join(os.getcwd(), LOCKNAME), lnk
                sys.stdout.flush()
                reported_symlinks[lnk] = None
                start = time.time()
            time.sleep(5)
        print "failed: file", os.path.join(os.getcwd(), LOCKNAME), lnk
        return None
    
    def main():
        print "locking..."
        sys.stdout.flush()
        host = myhostname()
    
        s = None
        link = None
        try:
            s = get_socket_lock()
            link = get_link_lock()
            if link != None:
                print "locked"
            sys.stdout.flush()
            while 1:
                poll_input(s, 3600)
        finally:
            if s != None:
                s.close()
            if link != None:
                os.unlink(link)
            print "bye"
            sys.stdout.flush()
    
    if __name__ == '__main__':
        main()