From 0b296562d46fe6a8af0ad50df08d3000f89867e1 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 17 Apr 2017 11:05:23 -0400 Subject: sd_login: Write notes on cgroup structure; realize how to simplify the code. I'm not sure that the most appropriate error is returned in all cases. --- sd_login/.gitignore | 2 - sd_login/Makefile | 27 ---- sd_login/doc.go | 2 - sd_login/logind_session.go | 13 ++ sd_login/logind_user.go | 4 + sd_login/notes.org | 48 +++++++ sd_login/systemd_cgroup.go | 216 ++++++++++++++++++++++++++++++++ sd_login/systemd_cgroup_generic.go | 163 ------------------------ sd_login/systemd_cgroup_skip.go | 102 --------------- sd_login/systemd_cgroup_skip_gen.go.gen | 41 ------ sd_login/systemd_cgroup_systemd.go | 105 ---------------- sd_login/systemd_process.go | 171 +++++++++++++++++++++++++ sd_login/systemd_process.go.gen | 76 ----------- sd_login/util.go | 2 +- sd_login/util_valid.go | 18 +-- 15 files changed, 463 insertions(+), 527 deletions(-) delete mode 100644 sd_login/.gitignore delete mode 100644 sd_login/Makefile create mode 100644 sd_login/systemd_cgroup.go delete mode 100644 sd_login/systemd_cgroup_generic.go delete mode 100644 sd_login/systemd_cgroup_skip.go delete mode 100755 sd_login/systemd_cgroup_skip_gen.go.gen delete mode 100644 sd_login/systemd_cgroup_systemd.go create mode 100644 sd_login/systemd_process.go delete mode 100755 sd_login/systemd_process.go.gen diff --git a/sd_login/.gitignore b/sd_login/.gitignore deleted file mode 100644 index 5ad2264..0000000 --- a/sd_login/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/systemd_process.go -/systemd_cgroup_skip_gen.go diff --git a/sd_login/Makefile b/sd_login/Makefile deleted file mode 100644 index 763ab3c..0000000 --- a/sd_login/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) 2016-2017 Luke Shumaker -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -files.src.gen += systemd_process.go systemd_cgroup_skip_gen.go - -files.generate: $(files.src.gen) -maintainer-clean: - rm -f -- $(files.src.gen) -.PHONY: files.generate maintainer-clean - -%.go: %.go.gen - ./$^ > $@ - -systemd_cgroup_skip_gen.go: systemd_cgroup_skip.go - -.DELETE_ON_ERROR: diff --git a/sd_login/doc.go b/sd_login/doc.go index 00e7250..7bef4fe 100644 --- a/sd_login/doc.go +++ b/sd_login/doc.go @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:generate make - // Package sd_login introspects session and login information from // systemd, logind, and machined. // diff --git a/sd_login/logind_session.go b/sd_login/logind_session.go index 5c2c5ca..c48d619 100644 --- a/sd_login/logind_session.go +++ b/sd_login/logind_session.go @@ -14,9 +14,22 @@ package sd_login +import ( + "strings" +) + // A SessionName represents a login session. type SessionName string +func (name SessionName) isValid() bool { + id := string(name) + if id == "" { + return false + } + + return strings.TrimLeft(id, letters+digits) == "" +} + type SessionState int const ( diff --git a/sd_login/logind_user.go b/sd_login/logind_user.go index 8d20d4b..fe908e0 100644 --- a/sd_login/logind_user.go +++ b/sd_login/logind_user.go @@ -20,6 +20,10 @@ import ( type UserID int +func (uid UserID) isValid() bool { + return uid >= 0 +} + type UserState int const ( diff --git a/sd_login/notes.org b/sd_login/notes.org index 0627142..38e866b 100644 --- a/sd_login/notes.org +++ b/sd_login/notes.org @@ -8,6 +8,54 @@ * requested metadata on object is missing → -ENODATA */ +The format of a systemd cgroup path is: + + /prefix.../slice/slice/slice/unit/extra... + +Where + - there may be 0 or more slices + - `prefix...` may be an arbitrary number/arrangement of path segments + - `extra...` may be an arbitrary number/arrangement of path segments + +If there is more than one slice in a path, then the rightmost slice is +the one that we mean when we say "the slice". + +We will refer to everything under `prefix...` as a "tree" (my term). +Because `prefix...` and `extra...` may be arbitrary, we can have +multiple of trees nested inside eachother. + +Because `prefix...` may be arbitrary, we need to know how to skip over +it; how to get to "our" tree. + +For the system cgroup tree, we do this by looking at the cgroup of PID +1 and then trimming a designated suffix from it to get back to the +root of the tree. + +For user cgroup trees, `prefix...` is a *unit* under the system cgroup +tree, where the unit matches either `user@UID.service` or +`session-SESSION.scope`. + +A container may nest its cgroup tree inside of a unit also. Because +the container will have its own PID namespace, it will have its own +PID 1, and be able to inspect the cgroup of PID 1, just as the host +system does. + + +| Thing | | | +|-----------+------------------------------------------------+-------| +| Cgroup | - | | +|-----------+------------------------------------------------+-------| +| Unit | | | +| Slice | (/$X.slice){0,} or "-.slice" | | +|-----------+------------------------------------------------+-------| +| UserUnit | SkipUserPrefix.GetUnit | | +| UserSlice | SkipUserPrefix.GetSlice | | +|-----------+------------------------------------------------+-------| +| Session | scanf("session-${SessionName}.scope") | Unit | +| Owner | scanf("user-${UserID}.slice") | Slice | +| Machine | readlink("/run/systemd/machines/unit:${Unit}") | Unit | + + * PID ** get_... *** session diff --git a/sd_login/systemd_cgroup.go b/sd_login/systemd_cgroup.go new file mode 100644 index 0000000..29ff38f --- /dev/null +++ b/sd_login/systemd_cgroup.go @@ -0,0 +1,216 @@ +// Copyright (C) 2016-2017 Luke Shumaker +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sd_login + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + + "golang.org/x/sys/unix" +) + +type _Cgroup string + +type _CgroupTree string +type _CgroupLeaf string + +var cgVersion_cache uint + +func cgVersion() uint { + if cgVersion_cache == 0 { + fs, err := statfs("/sys/fs/cgroup/") + if err != nil { + return 0 + } + if fs.Type == magic_CGROUP2_SUPER { + cgVersion_cache = 2 + } else if fs.Type == magic_TMPFS { + fs, err = statfs("/sys/fs/cgroup/systemd/") + if err != nil { + return 0 + } + if fs.Type == magic_CGROUP2_SUPER { + cgVersion_cache = 2 + } else { + cgVersion_cache = 1 + } + } + } + return cgVersion_cache +} + +// getCgroup returns the cgroup path of the process, relative to the +// root of the cgroup hierarchy. +// +// In cgroup v2, there is a only one cgroup hierarchy, so the behavior +// is obvious. +// +// However, in cgroup v1, there were multiple cgroup hierarchies, so +// this function must decide which hierarchy to use. We chooses the +// first hierarchy with either the "name=systemd" controller or the +// "name=elogind" controller attached to it[1]. If no such hierarchy +// exists, then an error is returned. +// +// However, it is possible to generally use cgroup v1, but use a +// single (named) v2 hierarchy alongside many v1 hierarchies. In this +// case, we use the v2 hierarchy iff it is named "systemd", otherwise +// we use the cgroup v1 behavior. +// +// [1]: The "first" in that sentence is worrying; shouldn't the choice +// of hierarchy not depend on the undefined order that controllers are +// listed in? Well, a controller may be attached to only one +// hierarchy at a time. So there is only an ambiguity for "first" to +// come in if both "name=systemd" and "name=elogind" controllers +// exist. Systemd and elogind cannot be used together, so this isn't +// a concern. +func (pid ProcessID) getCgroup() (_Cgroup, error) { + cgVer := cgVersion() + + cgroupFilename := fmt.Sprintf("/proc/%d/cgroup", pid) + + f, err := os.Open(cgroupFilename) + if err != nil { + return "", err + } + defer f.Close() + + bf := bufio.NewReader(f) + + for { + line, err := bf.ReadString('\n') + if err == io.EOF { + break + } + if err != nil { + return "", err + } + line = strings.TrimSuffix(line, "\n") + + parts := strings.SplitN(line, ":", 3) + if len(parts) != 3 { + continue + } + + hierarchy := parts[0] + controllers := parts[1] + path := _Cgroup(parts[2]) + + switch cgVer { + case 1: + for _, controller := range strings.Split(controllers, ",") { + if controller == "name=systemd" || controller == "name=elogind" { + return path, nil + } + } + case 2: + if hierarchy != "0" { + continue + } + return path, nil + } + } + return "", unix.ENODATA +} + +// cgGetRootPath determines the cgroup that all other cgroups belong +// to. The common case is just "/", but it could be something else if +// we are inside of a container, but have a view of the entier cgroup +// hierarchy. +func cgGetRootPath() (_CgroupTree, error) { + cgroup, err := ProcessID(1).getCgroup() + if err != nil { + return "/", err + } + + cgTree := _CgroupTree(trimOneSuffix(string(cgroup), + "/init.scope", // modern systemd + "/system.slice", // legacy systemd + "/system", // even more legacy systemd + )) + + return cgTree, nil +} + +func cgUnescape(s string) string { + return strings.TrimPrefix(s, "_") +} + +func cgDecodeUnit(unit string) string { + if len(unit) < 3 { + // because "X.Y" is at least 3 characters + return "" + } + unit = cgUnescape(unit) + if valid_unit_name(unit)&(unit_name_plain|unit_name_instance) == 0 { + return "" + } + return unit +} + +func (tree _CgroupTree) Parse(cgroup _Cgroup) (leaf _CgroupLeaf) { + rest, ok := path_startswith(string(cgroup), string(tree)) + if ok { + return _CgroupLeaf(rest) + } else { + return "" + } +} + +// Even if there is an invalid unit, a slice might still be returned. +func (leaf _CgroupLeaf) Parse() (slice, unit string, extra _CgroupLeaf) { + strLeaf := string(leaf) + + slice = "-.slice" + for { + part, rest := split2(strings.TrimLeft(strLeaf, "/"), '/') + if valid_slice_name(part) { + slice = part + strLeaf = rest + } else { + break + } + } + slice = cgDecodeUnit(slice) + if slice == "" { + return + } + + unit, extraStr := split2(strings.TrimLeft(strLeaf, "/"), '/') + unit = cgDecodeUnit(unit) + if unit == "" { + return + } + + extra = _CgroupLeaf(extraStr) + return +} + +func (leaf _CgroupLeaf) GetSlice() string { + slice, _, _ := leaf.Parse() + return slice +} + +func (leaf _CgroupLeaf) GetUnit() string { + _, unit, _ := leaf.Parse() + return unit +} + +func (leaf _CgroupLeaf) GetRest() _CgroupLeaf { + _, _, extra := leaf.Parse() + return extra +} diff --git a/sd_login/systemd_cgroup_generic.go b/sd_login/systemd_cgroup_generic.go deleted file mode 100644 index 86757d1..0000000 --- a/sd_login/systemd_cgroup_generic.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (C) 2016-2017 Luke Shumaker -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sd_login - -import ( - "bufio" - "fmt" - "io" - "os" - "strings" - - "golang.org/x/sys/unix" -) - -type _Cgroup string - -var cgVersion_cache uint - -func cgVersion() uint { - if cgVersion_cache == 0 { - fs, err := statfs("/sys/fs/cgroup/") - if err != nil { - return 0 - } - if fs.Type == magic_CGROUP2_SUPER { - cgVersion_cache = 2 - } else if fs.Type == magic_TMPFS { - // XXX: systemd-specific cgroup v1 logic - fs, err = statfs("/sys/fs/cgroup/systemd/") - if err != nil { - return 0 - } - if fs.Type == magic_CGROUP2_SUPER { - cgVersion_cache = 2 - } else { - cgVersion_cache = 1 - } - } - } - return cgVersion_cache -} - -// getCgroup returns the cgroup path of the process, relative to the -// root of the cgroup hierarchy. -// -// In cgroup v2, there is a only one cgroup hierarchy, so the behavior -// is obvious. -// -// However, in cgroup v1, there were multiple cgroup hierarchies, so -// this function must decide which hierarchy to use. We chooses the -// first hierarchy with either the "name=systemd" controller or the -// "name=elogind" controller attached to it[1]. If no such hierarchy -// exists, then an error is returned. -// -// However, it is possible to generally use cgroup v1, but use a -// single (named) v2 hierarchy alongside many v1 hierarchies. In this -// case, we use the v2 hierarchy iff it is named "systemd", otherwise -// we use the cgroup v1 behavior. -// -// [1]: The "first" in that sentence is worrying; shouldn't the choice -// of hierarchy not depend on the undefined order that controllers are -// listed in? Well, a controller may be attached to only one -// hierarchy at a time. So there is only an ambiguity for "first" to -// come in if both "name=systemd" and "name=elogind" controllers -// exist. Systemd and elogind cannot be used together, so this isn't -// a concern. -// -// BUG(lukeshu): ProcessID.getCgroup: Has systemd-specific logic. However, -// it is only for "legacy" cgroup v1 compatibility; the cgroup v2 -// logic is totally implementation-agnostic. Unfortunately(?), no -// distro seems to be using cgroup v2 (introduced in Linux 4.5) yet by -// default. -func (pid ProcessID) getCgroup() (_Cgroup, error) { - cgVer := cgVersion() - - var cgroupFilename string - if pid == 0 { - cgroupFilename = "/proc/self/cgroup" - } else { - cgroupFilename = fmt.Sprintf("/proc/%d/cgroup", pid) - } - - f, err := os.Open(cgroupFilename) - if err != nil { - return "", err - } - defer f.Close() - - bf := bufio.NewReader(f) - - for { - line, err := bf.ReadString('\n') - if err == io.EOF { - break - } - if err != nil { - return "", err - } - line = strings.TrimSuffix(line, "\n") - - parts := strings.SplitN(line, ":", 3) - if len(parts) != 3 { - continue - } - - hierarchy := parts[0] - controllers := parts[1] - path := _Cgroup(parts[2]) - - switch cgVer { - case 1: - for _, controller := range strings.Split(controllers, ",") { - if controller == "name=systemd" || controller == "name=elogind" { - return path, nil - } - } - case 2: - if hierarchy != "0" { - continue - } - return path, nil - } - } - return "", unix.ENODATA -} - -// cgGetRootPath determines the cgroup that all other cgroups belong -// to. The common case is just "/", but it could be something else if -// we are inside of a container, but have a view of the entier cgroup -// hierarchy. -// -// BUG(lukeshu): cgGetRootPath: works correctly on systemd and -// elogind, but I'm not sure it's general. -func cgGetRootPath() (_Cgroup, error) { - cgpath, err := ProcessID(1).getCgroup() - if err != nil { - return "/", err - } - - cgpath = _Cgroup(trimOneSuffix(string(cgpath), - "/init.scope", // modern systemd - "/system.slice", // legacy systemd - "/system", // even more legacy systemd - )) - - return cgpath, nil -} - -func cgUnescape(s string) string { - return strings.TrimPrefix(s, "_") -} diff --git a/sd_login/systemd_cgroup_skip.go b/sd_login/systemd_cgroup_skip.go deleted file mode 100644 index e6e9042..0000000 --- a/sd_login/systemd_cgroup_skip.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 2016-2017 Luke Shumaker -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sd_login - -// The "skip:" comments in this file are used to automagically -// generate helper functions. The lack of a space between "//" and -// "skip:" is important - -import ( - "strconv" - "strings" -) - -func (cgroup _Cgroup) SkipPath(prefix _Cgroup) (_Cgroup, bool) { - //skip: SkipPath(prefix _Cgroup) : SkipPath(prefix) - rest, ok := path_startswith(string(cgroup), string(prefix)) - if ok { - return _Cgroup(rest), true - } else { - return cgroup, false - } -} - -// Skip (*.slice){1,} -func (cgroup _Cgroup) SkipSlices() (_Cgroup, bool) { - //skip: SkipSlices() : SkipSlices() - cg := string(cgroup) - skipped := false - for { - cg = strings.TrimLeft(cg, "/") - part, rest := split2(cg, '/') - if !valid_slice_name(part) { - return _Cgroup(cg), skipped - } - skipped = true - cg = rest - } -} - -// Skip user@*.service -func (cgroup _Cgroup) SkipUserManager() (_Cgroup, bool) { - //skip: SkipUserManager() : SkipUserManager() - part, rest := split2(strings.TrimLeft(string(cgroup), "/"), '/') - uid_str, ok := trimPrefixSuffix(part, "user@", ".service") - if !ok { - return cgroup, false - } - _, err := strconv.Atoi(uid_str) - if err != nil { - return cgroup, false - } - return _Cgroup(rest), true -} - -// Skip session-*.scope -func (cgroup _Cgroup) SkipSession() (_Cgroup, bool) { - //skip: SkipSession() : SkipSession() - part, rest := split2(strings.TrimLeft(string(cgroup), "/"), '/') - session, ok := trimPrefixSuffix(part, "session-", ".scope") - if !ok { - return cgroup, false - } - if !valid_session_name(session) { - return cgroup, false - } - return _Cgroup(rest), true -} - -// Skip (/*.slice){0,}/(user@*.service|session-*.scope) -func (cgroup _Cgroup) SkipUserPrefix() (_Cgroup, bool) { - //skip: SkipUserPrefix() : SkipUserPrefix() - cgroup, _ = cgroup.SkipSlices() - cgroup, ok := cgroup.SkipUserManager() - if ok { - return cgroup, ok - } - return cgroup.SkipSession() -} - -// Skip cgGetRootPath -func (cgroup _Cgroup) SkipSystemPrefix() (_Cgroup, bool) { - //skip: SkipSystemPrefix() : SkipSystemPrefix() - - rootpath, err := cgGetRootPath() - if err != nil { - return cgroup, false - } - - return cgroup.SkipPath(rootpath) -} diff --git a/sd_login/systemd_cgroup_skip_gen.go.gen b/sd_login/systemd_cgroup_skip_gen.go.gen deleted file mode 100755 index 26c515a..0000000 --- a/sd_login/systemd_cgroup_skip_gen.go.gen +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# Copyright (C) 2017 Luke Shumaker -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{ - printf '//' - printf ' %q' "$0" "$@" - printf '\n// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n' - - echo package sd_login - - grep -o '//skip:.*' "$1" | cut -d: -f2- | while read -r line; do - sig=$(echo $(cut -d: -f1 <<<"$line")) - cal=$(echo $(cut -d: -f2 <<<"$line")) - cat < -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sd_login - -import ( - "os" - "strconv" - "strings" -) - -// XXX: logind -func (cgroup _Cgroup) GetSession() SessionName { - unit := cgroup.GetUnit() - - session, ok := trimPrefixSuffix(unit, "session-", ".scope") - if !ok || !valid_session_name(session) { - return "" - } - - return SessionName(session) -} - -func (cgroup _Cgroup) GetOwnerUser() UserID { - slice := cgroup.GetSlice() - - uid_str, ok := trimPrefixSuffix(slice, "user-", ".slice") - if !ok { - return -1 - } - - uid, err := strconv.Atoi(uid_str) - if err != nil { - return -1 - } - - return UserID(uid) -} - -// XXX: machined -func (cgroup _Cgroup) GetMachine() MachineName { - unit := cgroup.GetUnit() - if unit == "" { - return "" - } - - machine, err := os.Readlink("/run/systemd/machines/unit:" + unit) - if err != nil { - return "" - } - return MachineName(machine) -} - -// XXX: systemd -func (cgroup _Cgroup) decodeUnit() string { - unit, _ := split2(string(cgroup), '/') - if len(unit) < 3 { - return "" - } - unit = cgUnescape(unit) - if valid_unit_name(unit)&(unit_name_plain|unit_name_instance) == 0 { - return "" - } - return unit -} - -func (cgroup _Cgroup) GetUnit() string { - unit := cgroup.MaybeSkipSlices().decodeUnit() - if strings.HasSuffix(unit, ".slice") { - return "" - } - return unit -} -func (cgroup _Cgroup) GetUserUnit() string { - return cgroup.MustSkipUserPrefix().GetUnit() -} -func (cgroup _Cgroup) GetSlice() string { - cg := string(cgroup) - n := 0 - for { - cg = strings.TrimLeft(cg, "/") - part, rest := split2(cg, '/') - if !valid_slice_name(part) { - if n == 0 { - return "-.slice" - } - return _Cgroup(cg).decodeUnit() - } - cg = rest - } -} -func (cgroup _Cgroup) GetUserSlice() string { - return cgroup.MustSkipUserPrefix().GetSlice() -} diff --git a/sd_login/systemd_process.go b/sd_login/systemd_process.go new file mode 100644 index 0000000..672eb2e --- /dev/null +++ b/sd_login/systemd_process.go @@ -0,0 +1,171 @@ +// ./systemd_process.go.gen +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package sd_login + +import ( + "net" + "os" + "strconv" + + "golang.org/x/sys/unix" +) + +// A ProcessID represents a process. +type ProcessID int + +func (pid ProcessID) isValid() bool { + return pid >= 1 +} + +func GetPeer(conn *net.UnixConn) (ProcessID, error) { + ucred, err := getpeercred(conn) + if err != nil { + return -1, err + } + return ProcessID(ucred.Pid), nil +} + +func (pid ProcessID) getSysCgroupLeaf() _CgroupLeaf { + cgroup, err := pid.getCgroup() + if err != nil { + return "" + } + cgTree, err := cgGetRootPath() + if err != nil { + return "" + } + return cgTree.Parse(cgroup) +} + +func (pid ProcessID) GetSession() (v SessionName, err error) { + if !pid.isValid() { + err = unix.EINVAL + return + } + + vStr, ok := trimPrefixSuffix(pid.getSysCgroupLeaf().GetUnit(), "session-", ".scope") + _v := SessionName(vStr) + + if !ok || _v.isValid() { + v = _v + } else { + err = unix.ENXIO + } + return +} + +func (pid ProcessID) GetOwner() (v UserID, err error) { + if !pid.isValid() { + err = unix.EINVAL + return + } + + uidStr, ok := trimPrefixSuffix(pid.getSysCgroupLeaf().GetSlice(), "user-", ".slice") + if !ok { + err = unix.ENXIO + return + } + uidInt, err := strconv.Atoi(uidStr) + if err != nil { + err = unix.ENXIO + return + } + _v := UserID(uidInt) + + if _v.isValid() { + v = _v + } else { + err = unix.ENXIO + } + return +} + +func (pid ProcessID) GetMachine() (v MachineName, err error) { + if !pid.isValid() { + err = unix.EINVAL + return + } + + unit := pid.getSysCgroupLeaf().GetUnit() + if unit == "" { + err = unix.ENXIO + return + } + machine, err := os.Readlink("/run/systemd/machines/unit:" + unit) + if err != nil { + return + } + _v := MachineName(machine) + + if _v.isValid() { + v = _v + } else { + err = unix.ENXIO + } + return +} + +func (pid ProcessID) GetSlice() (v string, err error) { + if !pid.isValid() { + err = unix.EINVAL + return + } + + v = pid.getSysCgroupLeaf().GetSlice() + if v == "" { + err = unix.ENXIO + } + return +} + +func (pid ProcessID) GetUnit() (v string, err error) { + if !pid.isValid() { + err = unix.EINVAL + return + } + + v = pid.getSysCgroupLeaf().GetUnit() + if v == "" { + err = unix.ENXIO + } + return +} + +func (pid ProcessID) GetUserSlice() (v string, err error) { + if !pid.isValid() { + err = unix.EINVAL + return + } + + sysLeaf := pid.getSysCgroupLeaf() + if !valid_user_tree(sysLeaf.GetUnit()) { + err = unix.ENXIO + } + userLeaf := sysLeaf.GetRest() + v = userLeaf.GetSlice() + + if v == "" { + err = unix.ENXIO + } + return +} + +func (pid ProcessID) GetUserUnit() (v string, err error) { + if !pid.isValid() { + err = unix.EINVAL + return + } + + sysLeaf := pid.getSysCgroupLeaf() + if !valid_user_tree(sysLeaf.GetUnit()) { + err = unix.ENXIO + } + userLeaf := sysLeaf.GetRest() + v = userLeaf.GetUnit() + + if v == "" { + err = unix.ENXIO + } + return +} diff --git a/sd_login/systemd_process.go.gen b/sd_login/systemd_process.go.gen deleted file mode 100755 index cadbfc6..0000000 --- a/sd_login/systemd_process.go.gen +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -# Copyright (C) 2016-2017 Luke Shumaker -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{ - printf '//' - printf ' %q' "$0" "$@" - printf '\n// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n' - - cat <= 0 -} - -func GetPeer(conn *net.UnixConn) (ProcessID, error) { - ucred, err := getpeercred(conn) - if err != nil { - return -1, err - } - return ProcessID(ucred.Pid), nil -} - -EOF - - data=( - 'GetSession SessionName' - 'GetOwnerUser UserID' - 'GetMachine MachineName' - - 'GetUnit string' - 'GetUserUnit string' - 'GetSlice string' - 'GetUserSlice string' - ) - for item in "${data[@]}"; do - read Func Type <<<"$item" - cat <