This file is indexed.

/usr/lib/python2.7/dist-packages/Pegasus/tools/filelock.py is in pegasus-wms 4.4.0+dfsg-7.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
"""
filelock.py: Provides NFS-safe locking around a DB File.
"""

##
#  Copyright 2007-2010 University Of Southern California
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing,
#  software distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
##

# Revision : $Revision: 2012 $

import os.path
import time
import random
import anydbm
import atexit
import logging

# Keep list of files to delete
at_exit = {}

def intent_exit_handler():
    # Cleanup keys when exiting
    for key in at_exit:
        try:
            logger.debug("unlinking %s" % (key))
            os.unlink(key)
        except:
            logger.debug("error unlinking %s" % (key))

# Get logger object (initialized elsewhere)
logger = logging.getLogger()

class Intent:
    """
    The Intent class coordinates intentions between multiple
    concurrent site-selector instances. For this reason, it provides
    access to a DB file to record arbitrary scalar intentions
    into. The file is locked using NFS-safe (so is hoped) file
    locks.
    """

    def __init__(self):
        self.m_filename = None
        self.m_pid = os.getpid()
        self.m_count = {}

    def new(self, fn):
        """
        This function records the filename as the database file to
        either create or to connect to. If the file does not exist
        yet, it will not be created in the initialization
        function. However, some simple checks are employed to see, if
        the file will be creatable and/or writable, should it not
        exist.
        """
        # purpose: Initialize class
        # paramtr: $fn (IN): path to intent database file

        if os.path.isfile(fn):
            # File already exists
            if os.access(fn, os.R_OK) and os.access(fn, os.W_OK):
                # Good!
                self.m_filename = fn
                return True
            else:
                # Cannot read/write to file
                return False
        else:
            # File does not exist yet
            dir = os.path.dirname(fn)
            
            # Check if directory exists and is writable
            if os.path.exists(dir) and os.access(dir, os.W_OK):
                # Everything looks good!
                self.m_filename = fn
                return True

        # Failed
        return False

    def create_lockfile(self, fn):
        # Create a lock file NFS-reliably
        # warning: use create_lock, not this function
        # paramtr: $fn (IN): name of main file to lock
        # returns: 1 on success, 0 on failure to lock
        tolock = fn
        lock = "%s.lock" % (tolock)
        uniq = "%s.%d" % (tolock, self.m_pid)

        if os.path.isfile(uniq):
            logger.warn("Locking: open %s: file already exists" % (uniq))
            # os.unlink(uniq)
            return False

        if os.path.isfile(lock):
            logger.warn("Locking: open %s: file already exists" % (lock))
            # os.unlink(lock)
            return False

        try:
            my_lock = open(uniq, "w")
        except:
            logger.warn("Locking: open %s: creating file" % (uniq))
            return False
        else:
            at_exit[uniq] = 1
            my_lock.write("%d\n" % (self.m_pid))
            my_lock.close()

        try:
            os.link(uniq, lock)
        except:
            # Unable to create link, check error
            logger.warn("while locking %s" % (uniq))
            try:
                stats = os.stat(uniq)
            except:
                # Error, no need to do anything
                logger.warn("error trying to stat %s" % uniq)
                pass
            else:
                if stats.st_nlink == 2:
                    # Lock successful
                    logger.info("link-count locked")
                    at_exit[lock] = 1
                    os.unlink(uniq)
                    at_exit.pop(uniq)
                    return True
        else:
            # Created link
            logger.info("hardlinnk locked")
            at_exit[lock] = 1
            os.unlink(uniq)
            at_exit.pop(uniq)
            return True
        
        return False

    def break_lock(self, fn):
        # purpose: check for a dead lock file, and remove if dead
        # paramtr: $fn (IN): name of the file to create lock file for
        # returns: None if the lock is valid, 1..2 if it was forcefully
        #          removed, and 0, if it could not be removed.
        lock = "%s.lock" % (fn)

        # Let's open file and check its pid
        try:
            input_file = open(lock, 'r')
        except:
            pass
        else:
            file_pid = input_file.readline()
            file_pid = file_pid.strip()
            input_file.close()

            # Let's check if said pid is still around
            try:
                os.kill(int(file_pid), 0)
            except:
                # Process is not around anymore
                if file_pid.isdigit():
                    uniq = "%s.%d" % (fn, int(file_pid))
                    logger.info("lock-owner %d found dead, removing lock!" % (int(file_pid)))
                    os.unlink(lock)
                    try:
                        # Also try to remove uniq file
                        os.unlink(uniq)
                    except:
                        pass
                    # Lock should be broken now
                    return True
                else:
                    logger.warn("error: cannot determine process id from lock file!")
            else:
                logger.info("lock-owned %d still lives..." % (int(file_pid)))

        # Was not able to break lock
        return False

    def create_lock(self, fn):
        """
        This function attempts to create a file lock around the
        specified filename according to Linux conventions. It first
        creates a unique file using the process id as unique suffix,
        then attempts to hardlink said file to the filename plus
        suffix <.lock>. The attempt is randomly backed off to retry
        on failure to hardlink. Additionally, the link count is
        checked to detect hidden success.

        This is a blocking function, and may block indefinitely on
        dead-locks, despite occasional lock acquiry wake-ups.
        """
        # purpose: blockingly wait for lock file creation
        # paramtr: $fn (IN): name of file to create lock file for
        # returns: 1: lock was created.
        retries = 0
        to_wait = 0

        while not self.create_lockfile(fn):
            if retries > 10:
                # We waited enough, let's try to break the lock
                self.break_lock(fn)
                retries = 0 # Shouldn't be necessary, just in case
            else:
                # Let's wait for a little while
                to_wait = 5 * random.random()
                logger.info("lock on file %s is busy, retry %d, waiting %.1f s..." % (fn, retries, to_wait))
                time.sleep(to_wait)
                retries = retries + 1

        logger.info("obtained lock for %s" % (fn))
        return True

    def delete_lock(self, fn):
        """
        This static function deletes all lock files around the given
        filename.  It should be a fast function, as no waiting is
        required.
        """
        # purpose: removes a lock file NFS-reliably
        # paramtr: $fn (IN): name of main file to lock
        # returns: 1 or 2 on success, 0 on failure to unlock
        tolock = fn
        lock = "%s.lock" % (tolock)
        uniq = "%s.%d" % (tolock, self.m_pid)
        result = 0

        try:
            os.unlink(lock)
        except:
            pass
        else:
            result = result + 1
            at_exit.pop(lock)            

        try:
            os.unlink(uniq)
        except:
            pass
        else:
            result = result + 1
            at_exit.pop(uniq)            

        return result

    def filename(self):
        """
        This is a simple accessor function, returning the filename
        that was passed to the constructor.
        """
        # purpose: returns the name of the communication file
        return self.m_filename

    def dbtie(self, ro=False):
        """
        This member increases the lock count for the database file,
        and connects to the database file.

        The return value is the result of the open call. It may be
        None in case of failure to open the database.
        """
        # purpose: Lock a file and tie it to a hash
        # paramtr: $ro (opt. IN): if true, open in read-only mode
        # returns: None on error, underlying object otherwise

        # Create key if not already there
        if not self.m_count.has_key(self.m_pid):
            self.m_count[self.m_pid] = 0

        if self.m_count[self.m_pid] == 0:
            self.create_lock(self.m_filename)
            self.m_count[self.m_pid] = self.m_count[self.m_pid] + 1

        # Open database in read only or read/write mode
        if ro:
            my_mode = 'r'
        else:
            my_mode = 'c'

        try:
            my_db = anydbm.open(self.m_filename, my_mode)
        except:
            # Remove lock on failure to connect
            self.m_count[self.m_pid] = self.m_count[self.m_pid] - 1
            if self.m_count[self.m_pid] == 0:
                self.delete_lock(self.m_filename)
            return None

        return my_db

    def locked(self):
        """
        This function returns the reference count for locks on the
        file. Refernce counters are kept on a per-process basis. This
        is not thread safe.
        """
        # purpose: detects already tied databases
        # returns: reference count for lock
        if not self.m_count.has_key(self.m_pid):
            return 0
        
        return self.m_count[self.m_pid]

    def dbuntie(self, dbref):
        """
        This function closes the hash data base and relinquishes the
        lock. This method should only be called, if the previous dbtie
        operation was successful, similar to opening and closing file
        handles
        """
        # purpose: untie a hash and release the lock
        # paramtr: $dbref (I): reference to db to be closed
        # returns: -
        self.m_count[self.m_pid] = self.m_count[self.m_pid] - 1
        if self.m_count[self.m_pid] == 0:
            self.delete_lock(self.m_filename)

        # Close datbase
        try:
            dbref.close()
        except:
            logger.warn("Error closing %s database" % (m_filename))

    def clone(self):
        """
        This is a comprehensive function to copy all values from the
        database into memory. Please note that you can create nasty
        dead-locks this way
        """
        # purpose: obtains all current values into a copy
        # returns: a hash with key => value, may be empty
        #          if no keys in database, or None if error
        my_copy = {}
        my_db = self.dbtie()

        if my_db is not None:
            # Copy each key/value pair, converting the value to int
            for key in my_db.keys():
                my_copy[key] = int(my_db[key])

            # All done
            self.dbuntie(my_db)

            return my_copy

        return None

    def inc(self, key, incr=1):
        # purpose: increment the count for a site handle
        # paramtr: $key (IN): key of value to increment
        #          $incr (opt. IN): increment, defaults to 1
        # returns: new value, None on error
        if key is None:
            return None

        # Just in case key is not string
        key = str(key)

        my_db = self.dbtie()

        if my_db is not None:
            if my_db.has_key(key):
                val = int(my_db[key])
                val = val + incr
            else:
                val = incr

            # Write new value
            my_db[key] = str(val)

            # Done, disconnect from data base
            self.dbuntie(my_db)

            return val

        return None

    def dec(self, key, decr=1):
        # purpose: decrement the count for a site handle
        # paramtr: $key (IN): key of value to decrement
        #          $decr (opt. IN): decrement, defaults to 1
        # returns: new value, None in case of error
        if key is None:
            return None

        # Just in case key is not string
        key = str(key)

        my_db = self.dbtie()

        if my_db is not None:
            if my_db.has_key(key):
                val = int(my_db[key])
                val = val - decr
            else:
                val = decr

            # Write new value
            my_db[key] = str(val)

            # Done, disconnect from data base
            self.dbuntie(my_db)

            return val

        return None

# Register module exit handler
atexit.register(intent_exit_handler)

# Built-in testing
if __name__ == '__main__':
    a = Intent()
    a.new("/tmp/test1")

    # Test tie/untie
    b = a.dbtie()
    a.dbuntie(b)

    # Increment keys
    a.inc('usc')
    a.inc('usc')
    c = a.inc('usc')
    if c is None:
        print "Cannot get counter!"
    else:
        print "Counter is now %d" % (c)
    c = a.dec("usc", 3)
    if c is None:
        print "Cannot get counter!"
    else:
        print "Counter is now %d" % (c)

    my_dict = {}
    my_dict = a.clone()
    for key in my_dict.keys():
        print key, "-->", my_dict[key]
    print "done"