You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
3.0 KiB

package xpra
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"os/user"
"path"
"strconv"
"syscall"
"github.com/subgraph/oz"
)
type Xpra struct {
// Server working directory where unix socket is created
WorkDir string
Config *oz.XServerConf
// Running xpra process
Process *exec.Cmd
// Display number
Display uint64
// Arguments passed to xpra command
xpraArgs []string
}
var xpraDefaultArgs = []string{
"--no-daemon",
"--mmap",
"--no-sharing",
"--bell",
"--system-tray",
"--xsettings",
//"--no-xsettings",
"--cursors",
"--encoding=rgb",
}
func getDefaultArgs(config *oz.XServerConf) []string {
args := []string{}
args = append(args, xpraDefaultArgs...)
if config.DisableClipboard {
args = append(args, "--no-clipboard")
} else {
args = append(args, "--clipboard")
}
switch config.AudioMode {
case oz.PROFILE_AUDIO_NONE, "":
args = append(args, "--no-microphone", "--no-speaker")
case oz.PROFILE_AUDIO_SPEAKER:
args = append(args, "--no-microphone", "--speaker")
case oz.PROFILE_AUDIO_FULL:
args = append(args, "--microphone", "--speaker")
}
if config.EnableNotifications {
args = append(args, "--notifications")
} else {
args = append(args, "--no-notifications")
}
return args
}
func (x *Xpra) Stop(cred *syscall.Credential) ([]byte, error) {
cmd := exec.Command("/usr/bin/xpra",
"--socket-dir="+x.WorkDir,
"stop",
fmt.Sprintf(":%d", x.Display),
)
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: cred,
}
cmd.Env = []string{"TMPDIR=" + x.WorkDir}
return cmd.Output()
}
func GetPath(u *user.User, name string) string {
return path.Join(u.HomeDir, ".Xoz", name)
}
func CreateDir(u *user.User, name string) (string, error) {
uid, gid, err := userIds(u)
if err != nil {
return "", err
}
dir := GetPath(u, name)
if err := createSubdirs(u.HomeDir, uid, gid, 0755, ".Xoz", name); err != nil {
return "", fmt.Errorf("failed to create xpra directory (%s): %v", dir, err)
}
return dir, nil
}
func createSubdirs(base string, uid, gid int, mode os.FileMode, subdirs ...string) error {
dir := base
for _, sd := range subdirs {
dir = path.Join(dir, sd)
if err := os.Mkdir(dir, mode); err != nil && !os.IsExist(err) {
return err
}
if err := os.Chown(dir, uid, gid); err != nil {
return err
}
}
return nil
}
func userIds(user *user.User) (int, int, error) {
uid, err := strconv.Atoi(user.Uid)
if err != nil {
return -1, -1, errors.New("failed to parse uid from user struct: " + err.Error())
}
gid, err := strconv.Atoi(user.Gid)
if err != nil {
return -1, -1, errors.New("failed to parse gid from user struct: " + err.Error())
}
return uid, gid, nil
}
func writeFakeProfile(cmd *exec.Cmd) error {
pi, err := cmd.StdinPipe()
if err != nil {
return nil
}
emptyProfile := new(oz.Profile)
emptyProfile.Seccomp.Mode = "blacklist"
emptyProfile.Seccomp.Enforce = true
jdata, err := json.Marshal(emptyProfile)
if err != nil {
return err
}
io.Copy(pi, bytes.NewBuffer(jdata))
pi.Close()
return nil
}