// 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 ( "strconv" "strings" "golang.org/x/sys/unix" ) // A SeatName represents a set of hardware devices for a workspace; i.e. a // screen and keyboard. // // Multiple sessions may be associated with a seat (if // seat.CanMultiSession()), but only one session may be active on a // seat at a time. type SeatName string // A SessionNameWithUID is a just a SessionName with a User ID already // associated with it, so that a separate lookup for GetUser isn't // necessary. type SessionNameWithUID struct { SessionName User UserID } func (sess SessionNameWithUID) GetUser() (UserID, error) { return sess.User, nil } func (seat SeatName) isValid() bool { return valid_filename(string(seat)) } // GetSeats returns a list of all currently available local seats. func GetSeats() ([]SeatName, error) { strs, err := get_files_in_directory("/run/systemd/seats/") if err != nil { return nil, err } seats := make([]SeatName, len(strs)) for i := range strs { seats[i] = SeatName(strs[i]) } return seats, nil } // GetActive returns which session is currently active on this seat, // or nil if there is no currently active session. func (seat SeatName) GetActive() (sess SessionNameWithUID, err error) { if !seat.isValid() { err = unix.EINVAL return } env, err := parse_env_file("/run/systemd/seats/" + string(seat)) if err != nil { return } strName, ok := env["ACTIVE"] if !ok { err = unix.ENXIO return } strUid, ok := env["ACTIVE_UID"] if !ok { err = unix.ENXIO return } intUid, err := strconv.Atoi(strUid) if err != nil { return } sess.SessionName = SessionName(strName) sess.User = UserID(intUid) return } // GetSessions returns a list of all sessions associated with the // seat, whether they are active or not. func (seat SeatName) GetSessions() ([]SessionNameWithUID, error) { if !seat.isValid() { return nil, unix.EINVAL } env, err := parse_env_file("/run/systemd/seats/" + string(seat)) if err != nil { return nil, err } strSessions, ok := env["SESSIONS"] if !ok { return nil, unix.ENXIO } strUids, ok := env["UIDS"] if !ok { return nil, unix.ENXIO } arySessions := strings.Fields(strSessions) aryUids := strings.Fields(strUids) if len(arySessions) != len(aryUids) { return nil, unix.ENXIO } ret := make([]SessionNameWithUID, len(arySessions)) for i := 0; i < len(arySessions); i++ { uid, err := strconv.Atoi(aryUids[i]) if err != nil { return nil, err } ret[i].SessionName = SessionName(arySessions[i]) ret[i].User = UserID(uid) } return ret, nil } func (seat SeatName) can(cap string) (bool, error) { if !seat.isValid() { return false, unix.EINVAL } env, err := parse_env_file("/run/systemd/seats/" + string(seat)) if err != nil { return false, err } return parse_boolean(env["CAN_"+cap]) } // CanMultiSession returns whether this seat is capable of // multi-session, i.e. login sessions in parallel (with only one being // active at a time). func (seat SeatName) CanMultiSession() (bool, error) { return seat.can("MULTI_SESSION") } // CanTTY returns whether this seat provides TTY functionality, // i.e. is useful as a text console. func (seat SeatName) CanTTY() (bool, error) { return seat.can("TTY") } // CanGraphical returns whether this seat provides graphics // functionality, i.e. is useful as a graphics display. func (seat SeatName) CanGraphical() (bool, error) { return seat.can("GRAPHICAL") }