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
|
-- Copyright (c) 2024 Florian Fischer. All rights reserved.
--
-- vis-lockfiles 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 3 of the License, or (at your option) any later
-- version.
--
-- vis-lockfiles 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
-- vis-lockfiles found in the LICENSE file.
-- If not, see <https://www.gnu.org/licenses/>.
-- Helper to execute a command and capture its output
local function capture_cmd(cmd)
local p = assert(io.popen(cmd, 'r'))
local s = assert(p:read('*a'))
local success, _, status = p:close()
if not success then
local err = cmd .. ' failed with exit code: ' .. status
vis:info('vis-lockfiles: ' .. err)
end
return s
end
local function lock_file_path(lock_dir, path)
-- escape '%' first
local filename = path:gsub('%%', '%%25'):gsub('/', '%%2F')
return lock_dir .. '/' .. filename .. '.lock'
end
-- determine vis' pid
local vis_pid
do
local vis_proc_file = io.open('/proc/self/stat', 'r')
if vis_proc_file then
vis_pid = vis_proc_file:read('*n')
vis_proc_file:close()
else -- fallback if /proc/self/stat is not available
local out = capture_cmd('sh -c "echo $PPID"')
vis_pid = tonumber(out)
end
end
assert(vis_pid)
local function pid_lives(pid)
local tst = io.open('/proc/' .. pid .. '/stat')
if tst then
tst:close()
return true
end
return false
end
-- returns PID owning lock (possibly vis_pid)
local function create_lock_file(lock_dir, lfile_path)
local lock_pid
local lock_file = io.open(lfile_path)
if lock_file then
lock_pid = lock_file:read('*n')
lock_file:close()
if lock_pid == vis_pid or pid_lives(lock_pid) then
return lock_pid
end
end
-- create the lock file with our pid
lock_file = io.open(lfile_path, 'w')
if not lock_file then
-- retry, this time asserting success; first ensure directory exists
assert(os.execute('mkdir -p "' .. lock_dir .. '"'))
lock_file = assert(io.open(lfile_path, 'w'))
end
lock_file:write(vis_pid)
lock_file:close()
-- detect a possible race
lock_file = assert(io.open(lfile_path))
lock_pid = tonumber(lock_file:read('*n'))
lock_file:close()
return lock_pid
end
local function delete_lock_file(lfile_path)
local success, err = os.remove(lfile_path)
if not success then
vis:info('vis-lockfiles: ' .. err)
end
end
return {
vis_pid = vis_pid,
capture_cmd = capture_cmd,
lock_file_path = lock_file_path,
delete_lock_file = delete_lock_file,
create_lock_file = create_lock_file,
}
|