~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/coreos/go-systemd/util/util.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 CoreOS, Inc.
 
2
//
 
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
 
6
//
 
7
//     http://www.apache.org/licenses/LICENSE-2.0
 
8
//
 
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.
 
14
 
 
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.
 
19
package util
 
20
 
 
21
// #cgo LDFLAGS: -ldl
 
22
// #include <stdlib.h>
 
23
// #include <dlfcn.h>
 
24
// #include <sys/types.h>
 
25
// #include <unistd.h>
 
26
//
 
27
// int
 
28
// my_sd_pid_get_owner_uid(void *f, pid_t pid, uid_t *uid)
 
29
// {
 
30
//   int (*sd_pid_get_owner_uid)(pid_t, uid_t *);
 
31
//
 
32
//   sd_pid_get_owner_uid = (int (*)(pid_t, uid_t *))f;
 
33
//   return sd_pid_get_owner_uid(pid, uid);
 
34
// }
 
35
//
 
36
// int
 
37
// my_sd_pid_get_unit(void *f, pid_t pid, char **unit)
 
38
// {
 
39
//   int (*sd_pid_get_unit)(pid_t, char **);
 
40
//
 
41
//   sd_pid_get_unit = (int (*)(pid_t, char **))f;
 
42
//   return sd_pid_get_unit(pid, unit);
 
43
// }
 
44
//
 
45
// int
 
46
// my_sd_pid_get_slice(void *f, pid_t pid, char **slice)
 
47
// {
 
48
//   int (*sd_pid_get_slice)(pid_t, char **);
 
49
//
 
50
//   sd_pid_get_slice = (int (*)(pid_t, char **))f;
 
51
//   return sd_pid_get_slice(pid, slice);
 
52
// }
 
53
//
 
54
// int
 
55
// am_session_leader()
 
56
// {
 
57
//   return (getsid(0) == getpid());
 
58
// }
 
59
import "C"
 
60
import (
 
61
        "errors"
 
62
        "fmt"
 
63
        "io/ioutil"
 
64
        "os"
 
65
        "strings"
 
66
        "syscall"
 
67
        "unsafe"
 
68
)
 
69
 
 
70
var ErrSoNotFound = errors.New("unable to open a handle to libsystemd")
 
71
 
 
72
// libHandle represents an open handle to the systemd C library
 
73
type libHandle struct {
 
74
        handle  unsafe.Pointer
 
75
        libname string
 
76
}
 
77
 
 
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)
 
81
        }
 
82
        return nil
 
83
}
 
84
 
 
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{
 
91
                // systemd < 209
 
92
                "libsystemd-login.so",
 
93
                "libsystemd-login.so.0",
 
94
 
 
95
                // systemd >= 209 merged libsystemd-login into libsystemd proper
 
96
                "libsystemd.so",
 
97
                "libsystemd.so.0",
 
98
        } {
 
99
                libname := C.CString(name)
 
100
                defer C.free(unsafe.Pointer(libname))
 
101
                handle := C.dlopen(libname, C.RTLD_LAZY)
 
102
                if handle != nil {
 
103
                        h := &libHandle{
 
104
                                handle:  handle,
 
105
                                libname: name,
 
106
                        }
 
107
                        return h, nil
 
108
                }
 
109
        }
 
110
        return nil, ErrSoNotFound
 
111
}
 
112
 
 
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) {
 
118
        var h *libHandle
 
119
        h, err = getHandle()
 
120
        if err != nil {
 
121
                return
 
122
        }
 
123
        defer func() {
 
124
                if err1 := h.Close(); err1 != nil {
 
125
                        err = err1
 
126
                }
 
127
        }()
 
128
 
 
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")
 
134
                return
 
135
        }
 
136
 
 
137
        var s string
 
138
        sl := C.CString(s)
 
139
        defer C.free(unsafe.Pointer(sl))
 
140
 
 
141
        ret := C.my_sd_pid_get_slice(sd_pid_get_slice, 0, &sl)
 
142
        if ret < 0 {
 
143
                err = fmt.Errorf("error calling sd_pid_get_slice: %v", syscall.Errno(-ret))
 
144
                return
 
145
        }
 
146
 
 
147
        return C.GoString(sl), nil
 
148
}
 
149
 
 
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.
 
154
//
 
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"`)
 
162
//
 
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) {
 
167
        var h *libHandle
 
168
        h, err = getHandle()
 
169
        if err != nil {
 
170
                return
 
171
        }
 
172
        defer func() {
 
173
                if err1 := h.Close(); err1 != nil {
 
174
                        err = err1
 
175
                }
 
176
        }()
 
177
 
 
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")
 
183
                return
 
184
        }
 
185
 
 
186
        var uid C.uid_t
 
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)
 
191
        switch {
 
192
        case errno >= 0:
 
193
                ret = false
 
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
 
204
        default:
 
205
                err = fmt.Errorf("error calling sd_pid_get_owner_uid: %v", syscall.Errno(-errno))
 
206
        }
 
207
        return
 
208
}
 
209
 
 
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) {
 
215
        var h *libHandle
 
216
        h, err = getHandle()
 
217
        if err != nil {
 
218
                return
 
219
        }
 
220
        defer func() {
 
221
                if err1 := h.Close(); err1 != nil {
 
222
                        err = err1
 
223
                }
 
224
        }()
 
225
 
 
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")
 
231
                return
 
232
        }
 
233
 
 
234
        var s string
 
235
        u := C.CString(s)
 
236
        defer C.free(unsafe.Pointer(u))
 
237
 
 
238
        ret := C.my_sd_pid_get_unit(sd_pid_get_unit, 0, &u)
 
239
        if ret < 0 {
 
240
                err = fmt.Errorf("error calling sd_pid_get_unit: %v", syscall.Errno(-ret))
 
241
                return
 
242
        }
 
243
 
 
244
        unit = C.GoString(u)
 
245
        return
 
246
}
 
247
 
 
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")
 
254
        if err != nil {
 
255
                return false
 
256
        }
 
257
        return fi.IsDir()
 
258
}
 
259
 
 
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")
 
266
        if err != nil {
 
267
                return "", fmt.Errorf("failed to read /etc/machine-id: %v", err)
 
268
        }
 
269
        return strings.TrimSpace(string(machineID)), nil
 
270
}