|
|
@ -15,20 +15,22 @@ import (
|
|
|
|
"os/user"
|
|
|
|
"os/user"
|
|
|
|
"strconv"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"sync"
|
|
|
|
"syscall"
|
|
|
|
"syscall"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const SocketAddress = "/tmp/oz-init-control"
|
|
|
|
const SocketAddress = "/tmp/oz-init-control"
|
|
|
|
|
|
|
|
|
|
|
|
type initState struct {
|
|
|
|
type initState struct {
|
|
|
|
log *logging.Logger
|
|
|
|
log *logging.Logger
|
|
|
|
profile *oz.Profile
|
|
|
|
profile *oz.Profile
|
|
|
|
config *oz.Config
|
|
|
|
config *oz.Config
|
|
|
|
uid int
|
|
|
|
uid int
|
|
|
|
gid int
|
|
|
|
gid int
|
|
|
|
user *user.User
|
|
|
|
user *user.User
|
|
|
|
display int
|
|
|
|
display int
|
|
|
|
fs *fs.Filesystem
|
|
|
|
fs *fs.Filesystem
|
|
|
|
|
|
|
|
xpraReady sync.WaitGroup
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// By convention oz-init writes log messages to stderr with a single character
|
|
|
|
// By convention oz-init writes log messages to stderr with a single character
|
|
|
@ -121,15 +123,18 @@ func (st *initState) runInit() {
|
|
|
|
st.log.Error("Error: setting up filesystem failed: %v\n", err)
|
|
|
|
st.log.Error("Error: setting up filesystem failed: %v\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
oz.ReapChildProcs(st.log, st.handleChildExit)
|
|
|
|
|
|
|
|
|
|
|
|
if st.profile.XServer.Enabled {
|
|
|
|
if st.profile.XServer.Enabled {
|
|
|
|
if st.display == 0 {
|
|
|
|
if st.display == 0 {
|
|
|
|
st.log.Error("Cannot start xpra because no display number was passed to oz-init")
|
|
|
|
st.log.Error("Cannot start xpra because no display number was passed to oz-init")
|
|
|
|
os.Exit(1)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
st.xpraReady.Add(1)
|
|
|
|
st.startXpraServer()
|
|
|
|
st.startXpraServer()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
st.xpraReady.Wait()
|
|
|
|
oz.ReapChildProcs(st.log, st.handleChildExit)
|
|
|
|
st.launchApplication()
|
|
|
|
|
|
|
|
|
|
|
|
s, err := ipc.NewServer(SocketAddress, messageFactory, st.log,
|
|
|
|
s, err := ipc.NewServer(SocketAddress, messageFactory, st.log,
|
|
|
|
handlePing,
|
|
|
|
handlePing,
|
|
|
@ -142,6 +147,7 @@ func (st *initState) runInit() {
|
|
|
|
if err := os.Chown(SocketAddress, st.uid, st.gid); err != nil {
|
|
|
|
if err := os.Chown(SocketAddress, st.uid, st.gid); err != nil {
|
|
|
|
st.log.Warning("Failed to chown oz-init control socket: %v", err)
|
|
|
|
st.log.Warning("Failed to chown oz-init control socket: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
os.Stderr.WriteString("OK\n")
|
|
|
|
|
|
|
|
|
|
|
|
if err := s.Run(); err != nil {
|
|
|
|
if err := s.Run(); err != nil {
|
|
|
|
st.log.Warning("MsgServer.Run() return err: %v", err)
|
|
|
|
st.log.Warning("MsgServer.Run() return err: %v", err)
|
|
|
@ -178,11 +184,13 @@ func (st *initState) startXpraServer() {
|
|
|
|
|
|
|
|
|
|
|
|
func (st *initState) readXpraOutput(r io.ReadCloser) {
|
|
|
|
func (st *initState) readXpraOutput(r io.ReadCloser) {
|
|
|
|
sc := bufio.NewScanner(r)
|
|
|
|
sc := bufio.NewScanner(r)
|
|
|
|
|
|
|
|
seenReady := false
|
|
|
|
for sc.Scan() {
|
|
|
|
for sc.Scan() {
|
|
|
|
line := sc.Text()
|
|
|
|
line := sc.Text()
|
|
|
|
if len(line) > 0 {
|
|
|
|
if len(line) > 0 {
|
|
|
|
if strings.Contains(line, "xpra is ready.") {
|
|
|
|
if strings.Contains(line, "xpra is ready.") && !seenReady {
|
|
|
|
os.Stderr.WriteString("XPRA READY\n")
|
|
|
|
seenReady = true
|
|
|
|
|
|
|
|
st.xpraReady.Done()
|
|
|
|
if !st.config.LogXpra {
|
|
|
|
if !st.config.LogXpra {
|
|
|
|
r.Close()
|
|
|
|
r.Close()
|
|
|
|
return
|
|
|
|
return
|
|
|
@ -195,6 +203,42 @@ func (st *initState) readXpraOutput(r io.ReadCloser) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (st *initState) launchApplication() {
|
|
|
|
|
|
|
|
cmd := exec.Command(st.profile.Path)
|
|
|
|
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
st.log.Warning("Failed to create stdout pipe: %v", err)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
stderr, err := cmd.StderrPipe()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
st.log.Warning("Failed to create stderr pipe: %v", err)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
|
|
|
|
|
|
cmd.SysProcAttr.Credential = &syscall.Credential{
|
|
|
|
|
|
|
|
Uid: uint32(st.uid),
|
|
|
|
|
|
|
|
Gid: uint32(st.gid),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Env = []string{
|
|
|
|
|
|
|
|
fmt.Sprintf("DISPLAY=:%d", st.display),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
|
|
|
|
st.log.Warning("Failed to start application (%s): %v", st.profile.Path, err)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
go st.readApplicationOutput(stdout, "stdout")
|
|
|
|
|
|
|
|
go st.readApplicationOutput(stderr, "stderr")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (st *initState) readApplicationOutput(r io.ReadCloser, label string) {
|
|
|
|
|
|
|
|
sc := bufio.NewScanner(r)
|
|
|
|
|
|
|
|
for sc.Scan() {
|
|
|
|
|
|
|
|
line := sc.Text()
|
|
|
|
|
|
|
|
st.log.Debug("(%s) %s", label, line)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func loadProfile(dir, name string) (*oz.Profile, error) {
|
|
|
|
func loadProfile(dir, name string) (*oz.Profile, error) {
|
|
|
|
ps, err := oz.LoadProfiles(dir)
|
|
|
|
ps, err := oz.LoadProfiles(dir)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|