2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
10 # | Copyright Mathias Kettner 2016 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
29 from pwd
import getpwnam
30 from grp
import getgrnam
33 from contextlib
import contextmanager
34 from typing
import Generator
# pylint: disable=unused-import
36 from pathlib2
import Path
# pylint: disable=unused-import
38 import cmk
.utils
.store
39 from cmk
.utils
.exceptions
import MKGeneralException
42 def daemonize(user
=0, group
=0):
43 # do the UNIX double-fork magic, see Stevens' "Advanced
44 # Programming in the UNIX Environment" for details (ISBN 0201563177)
51 sys
.stderr
.write("Fork failed (#1): %d (%s)\n" % (e
.errno
, e
.strerror
))
54 # decouple from parent environment
55 # chdir -> don't prevent unmounting...
58 # Create new process group with the process as leader
61 # Set user/group depending on params
63 os
.setregid(getgrnam(group
)[2], getgrnam(group
)[2])
65 os
.setreuid(getpwnam(user
)[2], getpwnam(user
)[2])
73 sys
.stderr
.write("Fork failed (#2): %d (%s)\n" % (e
.errno
, e
.strerror
))
79 si
= os
.open("/dev/null", os
.O_RDONLY
)
80 so
= os
.open("/dev/null", os
.O_WRONLY
)
89 """Closes all file descriptors starting with "lowfd", ignoring errors
91 Deletes all open file descriptors greater than or equal to lowfd from the
92 per-process object reference table. Any errors encountered while closing
93 file descriptors are ignored.
95 Difference to os.closerange() is that this automatically determines the
96 highest fd number to close.
99 highfd
= os
.sysconf("SC_OPEN_MAX")
103 os
.closerange(lowfd
, highfd
)
106 # TODO: Change API and call sites to work with Path() objects
107 def lock_with_pid_file(path
):
108 # type: (str) -> None
110 Use this after daemonizing or in foreground mode to ensure there is only
113 if not cmk
.utils
.store
.try_aquire_lock(path
):
114 raise MKGeneralException("Failed to aquire PID file lock: "
115 "Another process is already running")
117 # Now that we have the lock we are allowed to write our pid to the file.
118 # The pid can then be used by the init script.
119 with
file(path
, "w") as f
:
120 f
.write("%d\n" % os
.getpid())
123 # TODO: Change API and call sites to work with Path() objects
124 def _cleanup_locked_pid_file(path
):
125 # type: (str) -> None
126 """Cleanup the lock + file acquired by the function above"""
127 if not cmk
.utils
.store
.have_lock(path
):
130 cmk
.utils
.store
.release_lock(path
)
139 def pid_file_lock(path
):
140 # type: (Path) -> Generator[None, None, None]
141 """Context manager for PID file based locking"""
142 lock_with_pid_file("%s" % path
)
146 _cleanup_locked_pid_file("%s" % path
)
149 def set_cmdline(cmdline
):
151 Change the process name and process command line on of the running process
152 This works at least with Python 2.x on Linux
154 argv
= ctypes
.POINTER(ctypes
.c_char_p
)()
155 argc
= ctypes
.c_int()
156 ctypes
.pythonapi
.Py_GetArgcArgv(ctypes
.byref(argc
), ctypes
.byref(argv
))
157 cmdlen
= sum([len(argv
[i
]) for i
in range(argc
.value
)]) + argc
.value
158 # TODO: This can probably be simplified...
159 _new_cmdline
= ctypes
.c_char_p(cmdline
.ljust(cmdlen
, '\0'))
161 set_procname(cmdline
)
164 def set_procname(cmdline
):
166 Change the process name of the running process
167 This works at least with Python 2.x on Linux
169 libc
= ctypes
.cdll
.LoadLibrary(ctypes
.util
.find_library('c'))
171 #argv = ctypes.POINTER(ctypes.c_char_p)()
173 # replace the command line, which is available via /proc/<pid>/cmdline.
174 # This is .e.g used by ps
175 #libc.memcpy(argv.contents, new_cmdline, cmdlen)
177 # replace the prctl name, which is available via /proc/<pid>/status.
178 # This is for example used by top and killall
179 #libc.prctl(15, new_cmdline, 0, 0, 0)
181 name_buffer
= ctypes
.create_string_buffer(len(cmdline
) + 1)
182 name_buffer
.value
= cmdline
183 libc
.prctl(15, ctypes
.byref(name_buffer
), 0, 0, 0)