-
Notifications
You must be signed in to change notification settings - Fork 8
/
tcllauncher-support.tcl.in
292 lines (227 loc) · 6.02 KB
/
tcllauncher-support.tcl.in
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
#
# tcllauncher-support.tcl - support and standalone routines for tcllauncher
#
#
# NB if editing file file make sure you edit tcllauncher-support.tcl.in
#
package require Tclx
namespace eval ::tcllauncher {
#
# require_group - require a certain group ID, exit with message to stderr if not
#
proc require_group {group} {
if {[id group] == $group} {
return
}
# see if we can set to that group, maybe we're root?
if {[catch {id group $group} result] == 1} {
puts stderr "requires and can't set to group '$group': $result"
exit 254
}
return
}
#
# require_user - require a certain user ID, exit with message to stderr if not
#
proc require_user {user} {
if {[id user] == $user} {
return
}
# see if we can set to that group, maybe we're root?
if {[catch {id user $user} result] == 1} {
puts stderr "requires and can't set to user '$user': $result"
exit 253
}
return
}
#
# require_user_and_group - require the invoker to either be of a certain
# user and group or if they're superuser or some kind of equivalent,
# force this process to have the specified user (uid) and group (gid)
#
proc require_user_and_group {user group} {
# try group first because if we're root we might not be after setting
# user
require_group $group
require_user $user
}
#
# daemonize - rough tclx-based copy of BSD 4.4's "daemon" library routine
#
# usage: daemonize ?-noclose? ?-nochdir?
#
# detaches the process from the controlling terminal by forking, having
# the child become a process group leader, changing directory to / (by
# default) and closing and reopening stdin, stdout and stderr to and
# from /dev/null.
#
proc daemonize {args} {
set doClose 1
set doChdir 1
foreach arg $args {
switch $arg {
"-noclose" {
set doClose 0
}
"-nochdir" {
set doChdir 0
}
default {
error "unrecognized option: $arg"
}
}
}
set pid [fork]
if {$pid != 0} {
exit 0
}
id process group set
if {$doChdir} {
cd "/"
}
if {$doClose} {
# they didn't say -noclose so close stdin, stdout, stderr and
# repoint to /dev/null
set fp [open /dev/null RDWR]
dup $fp stdin
dup $fp stdout
dup $fp stderr
close $fp
} else {
# they set -noclose but let's make sure stdin, stdout and stderr
# exist and if they don't, let's gin them up from /dev/null
if {[llength [file channels std*]] == 3} {
# they all exist, we're good
return
}
set fp [open /dev/null RDWR]
# regenerate the list of standard handles because the fp file we
# just opened will have become one of them by opening it
set stdHandles [file channels std*]
# for all the stdio handles that don't have some kind of file
# or socket or device associated with them, associate /dev/null
foreach handle [list stdin stdout stderr] {
# if the handle doesn't exist...
if {[lsearch $stdHandles $handle] < 0} {
dup $fp $handle
}
}
close $fp
}
return
}
#
# pidfile_verify - insane checks of pid file
#
proc pidfile_verify {} {
variable pfh
if {[catch {fstat $pfh(fp)} stat] == 1} {
error "programming error: $stat"
}
set dev [keylget stat dev]
set ino [keylget stat ino]
if {$dev != $pfh(dev) || $ino != $pfh(ino)} {
error "programming error: pidfile dev $dev ino $ino doesn't match prior dev $pfh(dev) ino $pfh(ino)"
}
return 0
}
#
# pidfile_read - given a path and the name of a pid variable, set the
# PID into the variable
#
proc pidfile_read {path _pid} {
variable pfh
upvar $_pid pid
set fp [open $path "RDONLY"]
set pid [read -nonewline $fp]
close $fp
set pfh(path) $path
}
#
# pidfile_open - given an optional path to a file and optional permissions,
# open the file, try to lock it, get its contents. Return the pid contained
# therein if there is one and the lock failed. (Somebody's already got the
# pid.)
#
# else you've got the lock and call pidfile_write to get your pid in there
#
proc pidfile_open {{path ""} {mode 0600}} {
variable pfh
if {$path == ""} {
set pidfile /var/run/$::argv0.pid
} else {
set pidfile $path
}
set pfh(path) $pidfile
# Open the PID file and obtain exclusive lock.
# We truncate PID file here only to remove old PID immediately,
# PID file will be truncated again in pidfile_write(), so
# pidfile_write() can be called multiple times.
set fp [open $pidfile "RDWR CREAT" $mode]
# try to lock the file
if {![flock -write -nowait $fp]} {
# failed to lock the file, read it for the pid of the owner
set pid [read -nonewline $fp]
# if we can get an integer out of it, return that
if {[scan $pid %d pid] > 0} {
close $fp
return $pid
}
}
# i got the lock
# can fstat really fail on a file i have open?
set stat [fstat $fp]
set pfh(fp) $fp
set pfh(dev) [keylget stat dev]
set pfh(ino) [keylget stat ino]
return 0
}
#
# pidfile_mtime - return the mtime of the pidfile, returns -1 if
# "file mtime" failed.
#
proc pidfile_mtime {} {
variable pfh
if {[catch {file mtime $pfh(path)} catchResult] == 1} {
# some kind of error statting the file
return -1
}
# catchResult is the mtime of the file
return $catchResult
}
#
# pidfile_write - write my pid into the pid file
#
proc pidfile_write {} {
variable pfh
pidfile_verify
set fp $pfh(fp)
if {![flock -write -nowait $fp]} {
puts stderr "Unable to obtain lock on pidfile '$pfh(path)' for pidfile_write"
exit 252
}
ftruncate -fileid $fp 0
puts $fp [pid]
flush $fp
}
#
# pidfile_close - close the pid file
#
proc pidfile_close {} {
variable pfh
pidfile_verify
close $pfh(fp)
}
#
# pidfile_remove - remove the pidfile, unlock the lock, and close it
#
proc pidfile_remove {} {
variable pfh
pidfile_verify
file delete $pfh(path)
funlock $pfh(fp)
close $pfh(fp)
}
} ;# namespace tcllauncher
package provide @PACKAGE_NAME@ @PACKAGE_VERSION@
# vim: set ts=8 sw=4 sts=4 noet :