ominfra 0.0.0.dev125__py3-none-any.whl → 0.0.0.dev127__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/clouds/aws/auth.py +1 -1
- ominfra/deploy/_executor.py +1 -1
- ominfra/deploy/poly/_main.py +1 -1
- ominfra/pyremote/_runcommands.py +1 -1
- ominfra/scripts/journald2aws.py +2 -2
- ominfra/scripts/supervisor.py +1825 -1217
- ominfra/supervisor/collections.py +52 -0
- ominfra/supervisor/context.py +2 -336
- ominfra/supervisor/datatypes.py +1 -63
- ominfra/supervisor/dispatchers.py +22 -338
- ominfra/supervisor/dispatchersimpl.py +342 -0
- ominfra/supervisor/groups.py +33 -110
- ominfra/supervisor/groupsimpl.py +86 -0
- ominfra/supervisor/inject.py +45 -13
- ominfra/supervisor/main.py +1 -1
- ominfra/supervisor/pipes.py +83 -0
- ominfra/supervisor/poller.py +6 -3
- ominfra/supervisor/privileges.py +65 -0
- ominfra/supervisor/processes.py +18 -0
- ominfra/supervisor/{process.py → processesimpl.py} +99 -317
- ominfra/supervisor/setup.py +38 -0
- ominfra/supervisor/setupimpl.py +261 -0
- ominfra/supervisor/signals.py +24 -16
- ominfra/supervisor/spawning.py +31 -0
- ominfra/supervisor/spawningimpl.py +347 -0
- ominfra/supervisor/supervisor.py +54 -78
- ominfra/supervisor/types.py +122 -39
- ominfra/supervisor/users.py +64 -0
- {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/RECORD +34 -23
- {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev125.dist-info → ominfra-0.0.0.dev127.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import abc
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
|
6
|
+
K = ta.TypeVar('K')
|
7
|
+
V = ta.TypeVar('V')
|
8
|
+
|
9
|
+
|
10
|
+
class KeyedCollectionAccessors(abc.ABC, ta.Generic[K, V]):
|
11
|
+
@property
|
12
|
+
@abc.abstractmethod
|
13
|
+
def _by_key(self) -> ta.Mapping[K, V]:
|
14
|
+
raise NotImplementedError
|
15
|
+
|
16
|
+
def __iter__(self) -> ta.Iterator[V]:
|
17
|
+
return iter(self._by_key.values())
|
18
|
+
|
19
|
+
def __len__(self) -> int:
|
20
|
+
return len(self._by_key)
|
21
|
+
|
22
|
+
def __contains__(self, key: K) -> bool:
|
23
|
+
return key in self._by_key
|
24
|
+
|
25
|
+
def __getitem__(self, key: K) -> V:
|
26
|
+
return self._by_key[key]
|
27
|
+
|
28
|
+
def get(self, key: K, default: ta.Optional[V] = None) -> ta.Optional[V]:
|
29
|
+
return self._by_key.get(key, default)
|
30
|
+
|
31
|
+
def items(self) -> ta.Iterator[ta.Tuple[K, V]]:
|
32
|
+
return iter(self._by_key.items())
|
33
|
+
|
34
|
+
|
35
|
+
class KeyedCollection(KeyedCollectionAccessors[K, V]):
|
36
|
+
def __init__(self, items: ta.Iterable[V]) -> None:
|
37
|
+
super().__init__()
|
38
|
+
|
39
|
+
by_key: ta.Dict[K, V] = {}
|
40
|
+
for v in items:
|
41
|
+
if (k := self._key(v)) in by_key:
|
42
|
+
raise KeyError(f'key {k} of {v} already registered by {by_key[k]}')
|
43
|
+
by_key[k] = v
|
44
|
+
self.__by_key = by_key
|
45
|
+
|
46
|
+
@property
|
47
|
+
def _by_key(self) -> ta.Mapping[K, V]:
|
48
|
+
return self.__by_key
|
49
|
+
|
50
|
+
@abc.abstractmethod
|
51
|
+
def _key(self, v: V) -> K:
|
52
|
+
raise NotImplementedError
|
ominfra/supervisor/context.py
CHANGED
@@ -1,34 +1,17 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
import errno
|
3
|
-
import fcntl
|
4
|
-
import grp
|
5
3
|
import os
|
6
|
-
import pwd
|
7
|
-
import re
|
8
|
-
import resource
|
9
|
-
import stat
|
10
4
|
import typing as ta
|
11
|
-
import warnings
|
12
5
|
|
13
6
|
from omlish.lite.logs import log
|
14
7
|
|
15
8
|
from .configs import ServerConfig
|
16
|
-
from .datatypes import gid_for_uid
|
17
|
-
from .datatypes import name_to_uid
|
18
|
-
from .exceptions import NoPermissionError
|
19
|
-
from .exceptions import NotExecutableError
|
20
|
-
from .exceptions import NotFoundError
|
21
9
|
from .poller import Poller
|
22
10
|
from .states import SupervisorState
|
23
11
|
from .types import Process
|
24
12
|
from .types import ServerContext
|
25
|
-
from .
|
13
|
+
from .types import ServerEpoch
|
26
14
|
from .utils import mktempfile
|
27
|
-
from .utils import real_exit
|
28
|
-
from .utils import try_unlink
|
29
|
-
|
30
|
-
|
31
|
-
ServerEpoch = ta.NewType('ServerEpoch', int)
|
32
15
|
|
33
16
|
|
34
17
|
class ServerContextImpl(ServerContext):
|
@@ -48,16 +31,6 @@ class ServerContextImpl(ServerContext):
|
|
48
31
|
self._pid_history: ta.Dict[int, Process] = {}
|
49
32
|
self._state: SupervisorState = SupervisorState.RUNNING
|
50
33
|
|
51
|
-
if config.user is not None:
|
52
|
-
uid = name_to_uid(config.user)
|
53
|
-
self._uid: ta.Optional[int] = uid
|
54
|
-
self._gid: ta.Optional[int] = gid_for_uid(uid)
|
55
|
-
else:
|
56
|
-
self._uid = None
|
57
|
-
self._gid = None
|
58
|
-
|
59
|
-
self._unlink_pidfile = False
|
60
|
-
|
61
34
|
@property
|
62
35
|
def config(self) -> ServerConfig:
|
63
36
|
return self._config
|
@@ -81,15 +54,7 @@ class ServerContextImpl(ServerContext):
|
|
81
54
|
def pid_history(self) -> ta.Dict[int, Process]:
|
82
55
|
return self._pid_history
|
83
56
|
|
84
|
-
|
85
|
-
def uid(self) -> ta.Optional[int]:
|
86
|
-
return self._uid
|
87
|
-
|
88
|
-
@property
|
89
|
-
def gid(self) -> ta.Optional[int]:
|
90
|
-
return self._gid
|
91
|
-
|
92
|
-
##
|
57
|
+
#
|
93
58
|
|
94
59
|
def waitpid(self) -> ta.Tuple[ta.Optional[int], ta.Optional[int]]:
|
95
60
|
# Need pthread_sigmask here to avoid concurrent sigchld, but Python doesn't offer in Python < 3.4. There is
|
@@ -109,166 +74,6 @@ class ServerContextImpl(ServerContext):
|
|
109
74
|
pid, sts = None, None
|
110
75
|
return pid, sts
|
111
76
|
|
112
|
-
def set_uid_or_exit(self) -> None:
|
113
|
-
"""
|
114
|
-
Set the uid of the supervisord process. Called during supervisord startup only. No return value. Exits the
|
115
|
-
process via usage() if privileges could not be dropped.
|
116
|
-
"""
|
117
|
-
|
118
|
-
if self.uid is None:
|
119
|
-
if os.getuid() == 0:
|
120
|
-
warnings.warn(
|
121
|
-
'Supervisor is running as root. Privileges were not dropped because no user is specified in the '
|
122
|
-
'config file. If you intend to run as root, you can set user=root in the config file to avoid '
|
123
|
-
'this message.',
|
124
|
-
)
|
125
|
-
else:
|
126
|
-
msg = drop_privileges(self.uid)
|
127
|
-
if msg is None:
|
128
|
-
log.info('Set uid to user %s succeeded', self.uid)
|
129
|
-
else: # failed to drop privileges
|
130
|
-
raise RuntimeError(msg)
|
131
|
-
|
132
|
-
def set_rlimits_or_exit(self) -> None:
|
133
|
-
"""
|
134
|
-
Set the rlimits of the supervisord process. Called during supervisord startup only. No return value. Exits
|
135
|
-
the process via usage() if any rlimits could not be set.
|
136
|
-
"""
|
137
|
-
|
138
|
-
limits = []
|
139
|
-
|
140
|
-
if hasattr(resource, 'RLIMIT_NOFILE'):
|
141
|
-
limits.append({
|
142
|
-
'msg': (
|
143
|
-
'The minimum number of file descriptors required to run this process is %(min_limit)s as per the '
|
144
|
-
'"minfds" command-line argument or config file setting. The current environment will only allow '
|
145
|
-
'you to open %(hard)s file descriptors. Either raise the number of usable file descriptors in '
|
146
|
-
'your environment (see README.rst) or lower the minfds setting in the config file to allow the '
|
147
|
-
'process to start.'
|
148
|
-
),
|
149
|
-
'min': self.config.minfds,
|
150
|
-
'resource': resource.RLIMIT_NOFILE,
|
151
|
-
'name': 'RLIMIT_NOFILE',
|
152
|
-
})
|
153
|
-
|
154
|
-
if hasattr(resource, 'RLIMIT_NPROC'):
|
155
|
-
limits.append({
|
156
|
-
'msg': (
|
157
|
-
'The minimum number of available processes required to run this program is %(min_limit)s as per '
|
158
|
-
'the "minprocs" command-line argument or config file setting. The current environment will only '
|
159
|
-
'allow you to open %(hard)s processes. Either raise the number of usable processes in your '
|
160
|
-
'environment (see README.rst) or lower the minprocs setting in the config file to allow the '
|
161
|
-
'program to start.'
|
162
|
-
),
|
163
|
-
'min': self.config.minprocs,
|
164
|
-
'resource': resource.RLIMIT_NPROC,
|
165
|
-
'name': 'RLIMIT_NPROC',
|
166
|
-
})
|
167
|
-
|
168
|
-
for limit in limits:
|
169
|
-
min_limit = limit['min']
|
170
|
-
res = limit['resource']
|
171
|
-
msg = limit['msg']
|
172
|
-
name = limit['name']
|
173
|
-
|
174
|
-
soft, hard = resource.getrlimit(res) # type: ignore
|
175
|
-
|
176
|
-
# -1 means unlimited
|
177
|
-
if soft < min_limit and soft != -1: # type: ignore
|
178
|
-
if hard < min_limit and hard != -1: # type: ignore
|
179
|
-
# setrlimit should increase the hard limit if we are root, if not then setrlimit raises and we print
|
180
|
-
# usage
|
181
|
-
hard = min_limit # type: ignore
|
182
|
-
|
183
|
-
try:
|
184
|
-
resource.setrlimit(res, (min_limit, hard)) # type: ignore
|
185
|
-
log.info('Increased %s limit to %s', name, min_limit)
|
186
|
-
except (resource.error, ValueError):
|
187
|
-
raise RuntimeError(msg % dict( # type: ignore # noqa
|
188
|
-
min_limit=min_limit,
|
189
|
-
res=res,
|
190
|
-
name=name,
|
191
|
-
soft=soft,
|
192
|
-
hard=hard,
|
193
|
-
))
|
194
|
-
|
195
|
-
def cleanup(self) -> None:
|
196
|
-
if self._unlink_pidfile:
|
197
|
-
try_unlink(self.config.pidfile)
|
198
|
-
self._poller.close()
|
199
|
-
|
200
|
-
def cleanup_fds(self) -> None:
|
201
|
-
# try to close any leaked file descriptors (for reload)
|
202
|
-
start = 5
|
203
|
-
os.closerange(start, self.config.minfds)
|
204
|
-
|
205
|
-
def clear_auto_child_logdir(self) -> None:
|
206
|
-
# must be called after realize()
|
207
|
-
child_logdir = self.config.child_logdir
|
208
|
-
fnre = re.compile(rf'.+?---{self.config.identifier}-\S+\.log\.?\d{{0,4}}')
|
209
|
-
try:
|
210
|
-
filenames = os.listdir(child_logdir)
|
211
|
-
except OSError:
|
212
|
-
log.warning('Could not clear child_log dir')
|
213
|
-
return
|
214
|
-
|
215
|
-
for filename in filenames:
|
216
|
-
if fnre.match(filename):
|
217
|
-
pathname = os.path.join(child_logdir, filename)
|
218
|
-
try:
|
219
|
-
os.remove(pathname)
|
220
|
-
except OSError:
|
221
|
-
log.warning('Failed to clean up %r', pathname)
|
222
|
-
|
223
|
-
def daemonize(self) -> None:
|
224
|
-
self._poller.before_daemonize()
|
225
|
-
self._daemonize()
|
226
|
-
self._poller.after_daemonize()
|
227
|
-
|
228
|
-
def _daemonize(self) -> None:
|
229
|
-
# To daemonize, we need to become the leader of our own session (process) group. If we do not, signals sent to
|
230
|
-
# our parent process will also be sent to us. This might be bad because signals such as SIGINT can be sent to
|
231
|
-
# our parent process during normal (uninteresting) operations such as when we press Ctrl-C in the parent
|
232
|
-
# terminal window to escape from a logtail command. To disassociate ourselves from our parent's session group we
|
233
|
-
# use os.setsid. It means "set session id", which has the effect of disassociating a process from is current
|
234
|
-
# session and process group and setting itself up as a new session leader.
|
235
|
-
#
|
236
|
-
# Unfortunately we cannot call setsid if we're already a session group leader, so we use "fork" to make a copy
|
237
|
-
# of ourselves that is guaranteed to not be a session group leader.
|
238
|
-
#
|
239
|
-
# We also change directories, set stderr and stdout to null, and change our umask.
|
240
|
-
#
|
241
|
-
# This explanation was (gratefully) garnered from
|
242
|
-
# http://www.cems.uwe.ac.uk/~irjohnso/coursenotes/lrc/system/daemons/d3.htm
|
243
|
-
|
244
|
-
pid = os.fork()
|
245
|
-
if pid != 0:
|
246
|
-
# Parent
|
247
|
-
log.debug('supervisord forked; parent exiting')
|
248
|
-
real_exit(0)
|
249
|
-
|
250
|
-
# Child
|
251
|
-
log.info('daemonizing the supervisord process')
|
252
|
-
if self.config.directory:
|
253
|
-
try:
|
254
|
-
os.chdir(self.config.directory)
|
255
|
-
except OSError as err:
|
256
|
-
log.critical("can't chdir into %r: %s", self.config.directory, err)
|
257
|
-
else:
|
258
|
-
log.info('set current directory: %r', self.config.directory)
|
259
|
-
|
260
|
-
os.dup2(0, os.open('/dev/null', os.O_RDONLY))
|
261
|
-
os.dup2(1, os.open('/dev/null', os.O_WRONLY))
|
262
|
-
os.dup2(2, os.open('/dev/null', os.O_WRONLY))
|
263
|
-
|
264
|
-
os.setsid()
|
265
|
-
|
266
|
-
os.umask(self.config.umask)
|
267
|
-
|
268
|
-
# XXX Stevens, in his Advanced Unix book, section 13.3 (page 417) recommends calling umask(0) and closing unused
|
269
|
-
# file descriptors. In his Network Programming book, he additionally recommends ignoring SIGHUP and forking
|
270
|
-
# again after the setsid() call, for obscure SVR4 reasons.
|
271
|
-
|
272
77
|
def get_auto_child_log_name(self, name: str, identifier: str, channel: str) -> str:
|
273
78
|
prefix = f'{name}-{channel}---{identifier}-'
|
274
79
|
logfile = mktempfile(
|
@@ -277,142 +82,3 @@ class ServerContextImpl(ServerContext):
|
|
277
82
|
dir=self.config.child_logdir,
|
278
83
|
)
|
279
84
|
return logfile
|
280
|
-
|
281
|
-
def write_pidfile(self) -> None:
|
282
|
-
pid = os.getpid()
|
283
|
-
try:
|
284
|
-
with open(self.config.pidfile, 'w') as f:
|
285
|
-
f.write(f'{pid}\n')
|
286
|
-
except OSError:
|
287
|
-
log.critical('could not write pidfile %s', self.config.pidfile)
|
288
|
-
else:
|
289
|
-
self._unlink_pidfile = True
|
290
|
-
log.info('supervisord started with pid %s', pid)
|
291
|
-
|
292
|
-
|
293
|
-
def drop_privileges(user: ta.Union[int, str, None]) -> ta.Optional[str]:
|
294
|
-
"""
|
295
|
-
Drop privileges to become the specified user, which may be a username or uid. Called for supervisord startup
|
296
|
-
and when spawning subprocesses. Returns None on success or a string error message if privileges could not be
|
297
|
-
dropped.
|
298
|
-
"""
|
299
|
-
|
300
|
-
if user is None:
|
301
|
-
return 'No user specified to setuid to!'
|
302
|
-
|
303
|
-
# get uid for user, which can be a number or username
|
304
|
-
try:
|
305
|
-
uid = int(user)
|
306
|
-
except ValueError:
|
307
|
-
try:
|
308
|
-
pwrec = pwd.getpwnam(user) # type: ignore
|
309
|
-
except KeyError:
|
310
|
-
return f"Can't find username {user!r}"
|
311
|
-
uid = pwrec[2]
|
312
|
-
else:
|
313
|
-
try:
|
314
|
-
pwrec = pwd.getpwuid(uid)
|
315
|
-
except KeyError:
|
316
|
-
return f"Can't find uid {uid!r}"
|
317
|
-
|
318
|
-
current_uid = os.getuid()
|
319
|
-
|
320
|
-
if current_uid == uid:
|
321
|
-
# do nothing and return successfully if the uid is already the current one. this allows a supervisord
|
322
|
-
# running as an unprivileged user "foo" to start a process where the config has "user=foo" (same user) in
|
323
|
-
# it.
|
324
|
-
return None
|
325
|
-
|
326
|
-
if current_uid != 0:
|
327
|
-
return "Can't drop privilege as nonroot user"
|
328
|
-
|
329
|
-
gid = pwrec[3]
|
330
|
-
if hasattr(os, 'setgroups'):
|
331
|
-
user = pwrec[0]
|
332
|
-
groups = [grprec[2] for grprec in grp.getgrall() if user in grprec[3]]
|
333
|
-
|
334
|
-
# always put our primary gid first in this list, otherwise we can lose group info since sometimes the first
|
335
|
-
# group in the setgroups list gets overwritten on the subsequent setgid call (at least on freebsd 9 with
|
336
|
-
# python 2.7 - this will be safe though for all unix /python version combos)
|
337
|
-
groups.insert(0, gid)
|
338
|
-
try:
|
339
|
-
os.setgroups(groups)
|
340
|
-
except OSError:
|
341
|
-
return 'Could not set groups of effective user'
|
342
|
-
|
343
|
-
try:
|
344
|
-
os.setgid(gid)
|
345
|
-
except OSError:
|
346
|
-
return 'Could not set group id of effective user'
|
347
|
-
|
348
|
-
os.setuid(uid)
|
349
|
-
|
350
|
-
return None
|
351
|
-
|
352
|
-
|
353
|
-
def make_pipes(stderr=True) -> ta.Mapping[str, int]:
|
354
|
-
"""
|
355
|
-
Create pipes for parent to child stdin/stdout/stderr communications. Open fd in non-blocking mode so we can
|
356
|
-
read them in the mainloop without blocking. If stderr is False, don't create a pipe for stderr.
|
357
|
-
"""
|
358
|
-
|
359
|
-
pipes: ta.Dict[str, ta.Optional[int]] = {
|
360
|
-
'child_stdin': None,
|
361
|
-
'stdin': None,
|
362
|
-
'stdout': None,
|
363
|
-
'child_stdout': None,
|
364
|
-
'stderr': None,
|
365
|
-
'child_stderr': None,
|
366
|
-
}
|
367
|
-
|
368
|
-
try:
|
369
|
-
stdin, child_stdin = os.pipe()
|
370
|
-
pipes['child_stdin'], pipes['stdin'] = stdin, child_stdin
|
371
|
-
|
372
|
-
stdout, child_stdout = os.pipe()
|
373
|
-
pipes['stdout'], pipes['child_stdout'] = stdout, child_stdout
|
374
|
-
|
375
|
-
if stderr:
|
376
|
-
stderr, child_stderr = os.pipe()
|
377
|
-
pipes['stderr'], pipes['child_stderr'] = stderr, child_stderr
|
378
|
-
|
379
|
-
for fd in (pipes['stdout'], pipes['stderr'], pipes['stdin']):
|
380
|
-
if fd is not None:
|
381
|
-
flags = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NDELAY
|
382
|
-
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
|
383
|
-
|
384
|
-
return pipes # type: ignore
|
385
|
-
|
386
|
-
except OSError:
|
387
|
-
for fd in pipes.values():
|
388
|
-
if fd is not None:
|
389
|
-
close_fd(fd)
|
390
|
-
raise
|
391
|
-
|
392
|
-
|
393
|
-
def close_parent_pipes(pipes: ta.Mapping[str, int]) -> None:
|
394
|
-
for fdname in ('stdin', 'stdout', 'stderr'):
|
395
|
-
fd = pipes.get(fdname)
|
396
|
-
if fd is not None:
|
397
|
-
close_fd(fd)
|
398
|
-
|
399
|
-
|
400
|
-
def close_child_pipes(pipes: ta.Mapping[str, int]) -> None:
|
401
|
-
for fdname in ('child_stdin', 'child_stdout', 'child_stderr'):
|
402
|
-
fd = pipes.get(fdname)
|
403
|
-
if fd is not None:
|
404
|
-
close_fd(fd)
|
405
|
-
|
406
|
-
|
407
|
-
def check_execv_args(filename, argv, st) -> None:
|
408
|
-
if st is None:
|
409
|
-
raise NotFoundError(f"can't find command {filename!r}")
|
410
|
-
|
411
|
-
elif stat.S_ISDIR(st[stat.ST_MODE]):
|
412
|
-
raise NotExecutableError(f'command at {filename!r} is a directory')
|
413
|
-
|
414
|
-
elif not (stat.S_IMODE(st[stat.ST_MODE]) & 0o111):
|
415
|
-
raise NotExecutableError(f'command at {filename!r} is not executable')
|
416
|
-
|
417
|
-
elif not os.access(filename, os.X_OK):
|
418
|
-
raise NoPermissionError(f'no permission to run command {filename!r}')
|
ominfra/supervisor/datatypes.py
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
# ruff: noqa: UP007
|
2
|
-
import grp
|
3
2
|
import logging
|
4
3
|
import os
|
5
|
-
import pwd
|
6
|
-
import signal
|
7
4
|
import typing as ta
|
8
5
|
|
9
6
|
|
@@ -36,43 +33,7 @@ def logfile_name(val):
|
|
36
33
|
return existing_dirpath(val)
|
37
34
|
|
38
35
|
|
39
|
-
|
40
|
-
try:
|
41
|
-
uid = int(name)
|
42
|
-
except ValueError:
|
43
|
-
try:
|
44
|
-
pwdrec = pwd.getpwnam(name)
|
45
|
-
except KeyError:
|
46
|
-
raise ValueError(f'Invalid user name {name}') # noqa
|
47
|
-
uid = pwdrec[2]
|
48
|
-
else:
|
49
|
-
try:
|
50
|
-
pwd.getpwuid(uid) # check if uid is valid
|
51
|
-
except KeyError:
|
52
|
-
raise ValueError(f'Invalid user id {name}') # noqa
|
53
|
-
return uid
|
54
|
-
|
55
|
-
|
56
|
-
def name_to_gid(name: str) -> int:
|
57
|
-
try:
|
58
|
-
gid = int(name)
|
59
|
-
except ValueError:
|
60
|
-
try:
|
61
|
-
grprec = grp.getgrnam(name)
|
62
|
-
except KeyError:
|
63
|
-
raise ValueError(f'Invalid group name {name}') # noqa
|
64
|
-
gid = grprec[2]
|
65
|
-
else:
|
66
|
-
try:
|
67
|
-
grp.getgrgid(gid) # check if gid is valid
|
68
|
-
except KeyError:
|
69
|
-
raise ValueError(f'Invalid group id {name}') # noqa
|
70
|
-
return gid
|
71
|
-
|
72
|
-
|
73
|
-
def gid_for_uid(uid: int) -> int:
|
74
|
-
pwrec = pwd.getpwuid(uid)
|
75
|
-
return pwrec[3]
|
36
|
+
##
|
76
37
|
|
77
38
|
|
78
39
|
def octal_type(arg: ta.Union[str, int]) -> int:
|
@@ -144,29 +105,6 @@ byte_size = SuffixMultiplier({
|
|
144
105
|
})
|
145
106
|
|
146
107
|
|
147
|
-
# all valid signal numbers
|
148
|
-
SIGNUMS = [getattr(signal, k) for k in dir(signal) if k.startswith('SIG')]
|
149
|
-
|
150
|
-
|
151
|
-
def signal_number(value: ta.Union[int, str]) -> int:
|
152
|
-
try:
|
153
|
-
num = int(value)
|
154
|
-
|
155
|
-
except (ValueError, TypeError):
|
156
|
-
name = value.strip().upper() # type: ignore
|
157
|
-
if not name.startswith('SIG'):
|
158
|
-
name = f'SIG{name}'
|
159
|
-
|
160
|
-
num = getattr(signal, name, None) # type: ignore
|
161
|
-
if num is None:
|
162
|
-
raise ValueError(f'value {value!r} is not a valid signal name') # noqa
|
163
|
-
|
164
|
-
if num not in SIGNUMS:
|
165
|
-
raise ValueError(f'value {value!r} is not a valid signal number')
|
166
|
-
|
167
|
-
return num
|
168
|
-
|
169
|
-
|
170
108
|
class RestartWhenExitUnexpected:
|
171
109
|
pass
|
172
110
|
|