1
// Copyright 2015 CoreOS, Inc.
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
7
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
15
// Package util contains utility functions related to systemd that applications
16
// can use to check things like whether systemd is running. Note that some of
17
// these functions attempt to manually load systemd libraries at runtime rather
18
// than linking against them.
22
// #include <stdlib.h>
24
// #include <sys/types.h>
25
// #include <unistd.h>
28
// my_sd_pid_get_owner_uid(void *f, pid_t pid, uid_t *uid)
30
// int (*sd_pid_get_owner_uid)(pid_t, uid_t *);
32
// sd_pid_get_owner_uid = (int (*)(pid_t, uid_t *))f;
33
// return sd_pid_get_owner_uid(pid, uid);
37
// my_sd_pid_get_unit(void *f, pid_t pid, char **unit)
39
// int (*sd_pid_get_unit)(pid_t, char **);
41
// sd_pid_get_unit = (int (*)(pid_t, char **))f;
42
// return sd_pid_get_unit(pid, unit);
46
// my_sd_pid_get_slice(void *f, pid_t pid, char **slice)
48
// int (*sd_pid_get_slice)(pid_t, char **);
50
// sd_pid_get_slice = (int (*)(pid_t, char **))f;
51
// return sd_pid_get_slice(pid, slice);
55
// am_session_leader()
57
// return (getsid(0) == getpid());
70
var ErrSoNotFound = errors.New("unable to open a handle to libsystemd")
72
// libHandle represents an open handle to the systemd C library
73
type libHandle struct {
78
func (h *libHandle) Close() error {
79
if r := C.dlclose(h.handle); r != 0 {
80
return fmt.Errorf("error closing %v: %d", h.libname, r)
85
// getHandle tries to get a handle to a systemd library (.so), attempting to
86
// access it by several different names and returning the first that is
87
// successfully opened. Callers are responsible for closing the handler.
88
// If no library can be successfully opened, an error is returned.
89
func getHandle() (*libHandle, error) {
90
for _, name := range []string{
92
"libsystemd-login.so",
93
"libsystemd-login.so.0",
95
// systemd >= 209 merged libsystemd-login into libsystemd proper
99
libname := C.CString(name)
100
defer C.free(unsafe.Pointer(libname))
101
handle := C.dlopen(libname, C.RTLD_LAZY)
110
return nil, ErrSoNotFound
113
// GetRunningSlice attempts to retrieve the name of the systemd slice in which
114
// the current process is running.
115
// This function is a wrapper around the libsystemd C library; if it cannot be
116
// opened, an error is returned.
117
func GetRunningSlice() (slice string, err error) {
124
if err1 := h.Close(); err1 != nil {
129
sym := C.CString("sd_pid_get_slice")
130
defer C.free(unsafe.Pointer(sym))
131
sd_pid_get_slice := C.dlsym(h.handle, sym)
132
if sd_pid_get_slice == nil {
133
err = fmt.Errorf("error resolving sd_pid_get_slice function")
139
defer C.free(unsafe.Pointer(sl))
141
ret := C.my_sd_pid_get_slice(sd_pid_get_slice, 0, &sl)
143
err = fmt.Errorf("error calling sd_pid_get_slice: %v", syscall.Errno(-ret))
147
return C.GoString(sl), nil
150
// RunningFromSystemService tries to detect whether the current process has
151
// been invoked from a system service. The condition for this is whether the
152
// process is _not_ a user process. User processes are those running in session
153
// scopes or under per-user `systemd --user` instances.
155
// To avoid false positives on systems without `pam_systemd` (which is
156
// responsible for creating user sessions), this function also uses a heuristic
157
// to detect whether it's being invoked from a session leader process. This is
158
// the case if the current process is executed directly from a service file
159
// (e.g. with `ExecStart=/this/cmd`). Note that this heuristic will fail if the
160
// command is instead launched in a subshell or similar so that it is not
161
// session leader (e.g. `ExecStart=/bin/bash -c "/this/cmd"`)
163
// This function is a wrapper around the libsystemd C library; if this is
164
// unable to successfully open a handle to the library for any reason (e.g. it
165
// cannot be found), an errr will be returned
166
func RunningFromSystemService() (ret bool, err error) {
173
if err1 := h.Close(); err1 != nil {
178
sym := C.CString("sd_pid_get_owner_uid")
179
defer C.free(unsafe.Pointer(sym))
180
sd_pid_get_owner_uid := C.dlsym(h.handle, sym)
181
if sd_pid_get_owner_uid == nil {
182
err = fmt.Errorf("error resolving sd_pid_get_owner_uid function")
187
errno := C.my_sd_pid_get_owner_uid(sd_pid_get_owner_uid, 0, &uid)
188
serrno := syscall.Errno(-errno)
189
// when we're running from a unit file, sd_pid_get_owner_uid returns
190
// ENOENT (systemd <220) or ENXIO (systemd >=220)
194
case serrno == syscall.ENOENT, serrno == syscall.ENXIO:
195
// Since the implementation of sessions in systemd relies on
196
// the `pam_systemd` module, using the sd_pid_get_owner_uid
197
// heuristic alone can result in false positives if that module
198
// (or PAM itself) is not present or properly configured on the
199
// system. As such, we also check if we're the session leader,
200
// which should be the case if we're invoked from a unit file,
201
// but not if e.g. we're invoked from the command line from a
202
// user's login session
203
ret = C.am_session_leader() == 1
205
err = fmt.Errorf("error calling sd_pid_get_owner_uid: %v", syscall.Errno(-errno))
210
// CurrentUnitName attempts to retrieve the name of the systemd system unit
211
// from which the calling process has been invoked. It wraps the systemd
212
// `sd_pid_get_unit` call, with the same caveat: for processes not part of a
213
// systemd system unit, this function will return an error.
214
func CurrentUnitName() (unit string, err error) {
221
if err1 := h.Close(); err1 != nil {
226
sym := C.CString("sd_pid_get_unit")
227
defer C.free(unsafe.Pointer(sym))
228
sd_pid_get_unit := C.dlsym(h.handle, sym)
229
if sd_pid_get_unit == nil {
230
err = fmt.Errorf("error resolving sd_pid_get_unit function")
236
defer C.free(unsafe.Pointer(u))
238
ret := C.my_sd_pid_get_unit(sd_pid_get_unit, 0, &u)
240
err = fmt.Errorf("error calling sd_pid_get_unit: %v", syscall.Errno(-ret))
248
// IsRunningSystemd checks whether the host was booted with systemd as its init
249
// system. This functions similarly to systemd's `sd_booted(3)`: internally, it
250
// checks whether /run/systemd/system/ exists and is a directory.
251
// http://www.freedesktop.org/software/systemd/man/sd_booted.html
252
func IsRunningSystemd() bool {
253
fi, err := os.Lstat("/run/systemd/system")
260
// GetMachineID returns a host's 128-bit machine ID as a string. This functions
261
// similarly to systemd's `sd_id128_get_machine`: internally, it simply reads
262
// the contents of /etc/machine-id
263
// http://www.freedesktop.org/software/systemd/man/sd_id128_get_machine.html
264
func GetMachineID() (string, error) {
265
machineID, err := ioutil.ReadFile("/etc/machine-id")
267
return "", fmt.Errorf("failed to read /etc/machine-id: %v", err)
269
return strings.TrimSpace(string(machineID)), nil