networking
brl 10 years ago
parent e28f599224
commit eee260576b

@ -1,13 +1,13 @@
package oz package oz
import ( import (
"syscall"
"github.com/op/go-logging" "github.com/op/go-logging"
"os" "os"
"os/signal" "os/signal"
"syscall"
) )
func ReapChildProcs(log *logging.Logger, callback func(int, syscall.WaitStatus)) chan os.Signal { func ReapChildProcs(log *logging.Logger, callback func(int, syscall.WaitStatus)) chan os.Signal {
sigs := make(chan os.Signal, 3) sigs := make(chan os.Signal, 3)
signal.Notify(sigs, syscall.SIGCHLD) signal.Notify(sigs, syscall.SIGCHLD)
go func() { go func() {
@ -22,7 +22,7 @@ func ReapChildProcs(log *logging.Logger, callback func(int, syscall.WaitStatus)
func handleSIGCHLD(log *logging.Logger, callback func(int, syscall.WaitStatus)) { func handleSIGCHLD(log *logging.Logger, callback func(int, syscall.WaitStatus)) {
var wstatus syscall.WaitStatus var wstatus syscall.WaitStatus
for { for {
pid,err := syscall.Wait4(-1, &wstatus, syscall.WNOHANG, nil) pid, err := syscall.Wait4(-1, &wstatus, syscall.WNOHANG, nil)
switch err { switch err {
case syscall.ECHILD: case syscall.ECHILD:
return return

@ -1,4 +1,5 @@
package main package main
import ( import (
"github.com/subgraph/oz/oz-daemon" "github.com/subgraph/oz/oz-daemon"
) )

@ -1,7 +1,7 @@
package main package main
import "github.com/subgraph/oz/oz-init" import "github.com/subgraph/oz/oz-init"
func main() { func main() {
ozinit.Main() ozinit.Main()
} }

@ -1,12 +1,12 @@
package fs package fs
import ( import (
"errors"
"github.com/op/go-logging"
"io/ioutil" "io/ioutil"
"sort" "sort"
"strings" "strings"
"syscall" "syscall"
"errors"
"github.com/op/go-logging"
) )
func (fs *Filesystem) Cleanup() error { func (fs *Filesystem) Cleanup() error {
@ -18,7 +18,7 @@ func (fs *Filesystem) Cleanup() error {
fs.log.Info("Cleanup() called on filesystem at root %s", fs.root) fs.log.Info("Cleanup() called on filesystem at root %s", fs.root)
for { for {
mnts,err := getMountsBelow(fs.base) mnts, err := getMountsBelow(fs.base)
if err != nil { if err != nil {
return err return err
} }
@ -35,7 +35,7 @@ func (fs *Filesystem) Cleanup() error {
func (mnts mountEntries) unmountAll(log *logging.Logger) (bool, error) { func (mnts mountEntries) unmountAll(log *logging.Logger) (bool, error) {
reterr := error(nil) reterr := error(nil)
atLeastOne := false atLeastOne := false
for _,m := range mnts { for _, m := range mnts {
if err := syscall.Unmount(m.dir, 0); err != nil { if err := syscall.Unmount(m.dir, 0); err != nil {
log.Warning("Failed to unmount mountpoint %s: %v", m.dir, err) log.Warning("Failed to unmount mountpoint %s: %v", m.dir, err)
reterr = err reterr = err

@ -19,12 +19,12 @@ type directory struct {
} }
type Filesystem struct { type Filesystem struct {
log *logging.Logger log *logging.Logger
user *user.User user *user.User
name string name string
base string base string
root string root string
xpra string xpra string
userID string userID string
noDefaults bool noDefaults bool
noSysAndProc bool noSysAndProc bool
@ -73,10 +73,10 @@ func (fs *Filesystem) newItem(path, target string, readonly bool) (*mountItem, e
func NewFromProfile(profile *oz.Profile, user *user.User, log *logging.Logger) *Filesystem { func NewFromProfile(profile *oz.Profile, user *user.User, log *logging.Logger) *Filesystem {
fs := NewFilesystem(profile.Name, user, log) fs := NewFilesystem(profile.Name, user, log)
for _,wl := range profile.Whitelist { for _, wl := range profile.Whitelist {
fs.addWhitelist(wl.Path, wl.Path, wl.ReadOnly) fs.addWhitelist(wl.Path, wl.Path, wl.ReadOnly)
} }
for _,bl := range profile.Blacklist { for _, bl := range profile.Blacklist {
fs.addBlacklist(bl.Path) fs.addBlacklist(bl.Path)
} }
fs.noDefaults = profile.NoDefaults fs.noDefaults = profile.NoDefaults
@ -210,9 +210,9 @@ func copyFileInfo(info os.FileInfo, target string) error {
return nil return nil
} }
func createSubdirs(base string, uid,gid int, mode os.FileMode, subdirs ...string) error { func createSubdirs(base string, uid, gid int, mode os.FileMode, subdirs ...string) error {
dir := base dir := base
for _,sd := range subdirs { for _, sd := range subdirs {
dir = path.Join(dir, sd) dir = path.Join(dir, sd)
if err := os.Mkdir(dir, mode); err != nil && !os.IsExist(err) { if err := os.Mkdir(dir, mode); err != nil && !os.IsExist(err) {
return err return err

@ -2,12 +2,12 @@ package fs
import ( import (
"fmt" "fmt"
"github.com/op/go-logging"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall" "syscall"
"github.com/op/go-logging"
) )
type MountFlag int type MountFlag int

@ -1,13 +1,13 @@
package fs package fs
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"path"
"syscall"
"os/user" "os/user"
"path"
"strconv" "strconv"
"errors" "syscall"
) )
var basicBindDirs = []string{ var basicBindDirs = []string{
@ -44,7 +44,7 @@ func (fs *Filesystem) Setup() error {
if err := fs.createXpraDir(); err != nil { if err := fs.createXpraDir(); err != nil {
return err return err
} }
item,err := fs.newItem(fs.xpra, fs.xpra, false) item, err := fs.newItem(fs.xpra, fs.xpra, false)
if err != nil { if err != nil {
return err return err
} }
@ -60,7 +60,7 @@ func (fs *Filesystem) Setup() error {
} }
func (fs *Filesystem) createXpraDir() error { func (fs *Filesystem) createXpraDir() error {
uid,gid,err := userIds(fs.user) uid, gid, err := userIds(fs.user)
if err != nil { if err != nil {
return err return err
} }
@ -72,15 +72,15 @@ func (fs *Filesystem) createXpraDir() error {
} }
func userIds(user *user.User) (int, int, error) { func userIds(user *user.User) (int, int, error) {
uid,err := strconv.Atoi(user.Uid) uid, err := strconv.Atoi(user.Uid)
if err != nil { if err != nil {
return -1,-1, errors.New("failed to parse uid from user struct: "+ err.Error()) return -1, -1, errors.New("failed to parse uid from user struct: " + err.Error())
} }
gid,err := strconv.Atoi(user.Gid) gid, err := strconv.Atoi(user.Gid)
if err != nil { if err != nil {
return -1,-1, errors.New("failed to parse gid from user struct: "+ err.Error()) return -1, -1, errors.New("failed to parse gid from user struct: " + err.Error())
} }
return uid,gid,nil return uid, gid, nil
} }
func (fs *Filesystem) setupRootfs() error { func (fs *Filesystem) setupRootfs() error {

@ -1,9 +1,10 @@
package ipc package ipc
import ( import (
"reflect"
"errors" "errors"
"fmt" "fmt"
"github.com/op/go-logging" "github.com/op/go-logging"
"reflect"
) )
type handlerMap map[string]reflect.Value type handlerMap map[string]reflect.Value
@ -16,13 +17,13 @@ type msgDispatcher struct {
hmap handlerMap hmap handlerMap
} }
func createDispatcher(log *logging.Logger, handlers...interface{}) (*msgDispatcher, error) { func createDispatcher(log *logging.Logger, handlers ...interface{}) (*msgDispatcher, error) {
md := &msgDispatcher{ md := &msgDispatcher{
log: log, log: log,
msgs: make(chan *Message), msgs: make(chan *Message),
hmap: make(map[string]reflect.Value), hmap: make(map[string]reflect.Value),
} }
for _,h := range handlers { for _, h := range handlers {
if err := md.hmap.addHandler(h); err != nil { if err := md.hmap.addHandler(h); err != nil {
return nil, err return nil, err
} }
@ -55,9 +56,9 @@ func (md *msgDispatcher) runDispatcher() {
} }
func (handlers handlerMap) dispatch(m *Message) error { func (handlers handlerMap) dispatch(m *Message) error {
h,ok := handlers[m.Type] h, ok := handlers[m.Type]
if !ok { if !ok {
return errors.New("no handler found for message type:"+ m.Type) return errors.New("no handler found for message type:" + m.Type)
} }
return executeHandler(h, m) return executeHandler(h, m)
} }
@ -65,7 +66,7 @@ func (handlers handlerMap) dispatch(m *Message) error {
func executeHandler(h reflect.Value, m *Message) error { func executeHandler(h reflect.Value, m *Message) error {
var args [2]reflect.Value var args [2]reflect.Value
args[0] = reflect.ValueOf(m.Body) args[0] = reflect.ValueOf(m.Body)
args[1]= reflect.ValueOf(m) args[1] = reflect.ValueOf(m)
rs := h.Call(args[:]) rs := h.Call(args[:])
if len(rs) != 1 { if len(rs) != 1 {
@ -82,7 +83,7 @@ func (handlers handlerMap) addHandler(h interface{}) error {
if err != nil { if err != nil {
return err return err
} }
if _,ok := handlers[msgType]; ok{ if _, ok := handlers[msgType]; ok {
return fmt.Errorf("duplicate handler registered for message type '%s'", msgType) return fmt.Errorf("duplicate handler registered for message type '%s'", msgType)
} }
handlers[msgType] = reflect.ValueOf(h) handlers[msgType] = reflect.ValueOf(h)
@ -125,5 +126,3 @@ func typeCheckHandler(h interface{}) (string, error) {
} }
return string(in0.Field(0).Tag), nil return string(in0.Field(0).Tag), nil
} }

@ -1,27 +1,28 @@
package ipc package ipc
import ( import (
"testing"
"reflect"
"errors" "errors"
"reflect"
"testing"
) )
func TestTypeCheckHandler(t *testing.T) { func TestTypeCheckHandler(t *testing.T) {
type testStruct struct {} type testStruct struct{}
cases := []interface{} { cases := []interface{}{
"foo", "foo",
func(){}, func() {},
func(a,b int){}, func(a, b int) {},
func(a *testStruct, b *Message) {}, func(a *testStruct, b *Message) {},
func(a,b *int) error{return nil}, func(a, b *int) error { return nil },
func(a *testStruct, b int) error {return nil}, func(a *testStruct, b int) error { return nil },
func(a *testStruct, b Message) error {return nil}, func(a *testStruct, b Message) error { return nil },
func(a *testStruct, b *Message) int{return 0}, func(a *testStruct, b *Message) int { return 0 },
func(a *testStruct, b *Message, c int) error {return nil}, func(a *testStruct, b *Message, c int) error { return nil },
} }
for i,h := range cases { for i, h := range cases {
if _,err := typeCheckHandler(h); err == nil { if _, err := typeCheckHandler(h); err == nil {
t.Errorf("typeCheckHandler should return an error for case %d", i) t.Errorf("typeCheckHandler should return an error for case %d", i)
} }
} }
@ -29,7 +30,7 @@ func TestTypeCheckHandler(t *testing.T) {
func TestAddHandler(t *testing.T) { func TestAddHandler(t *testing.T) {
hmap := handlerMap(make(map[string]reflect.Value)) hmap := handlerMap(make(map[string]reflect.Value))
type testStruct struct{ type testStruct struct {
t int "tst" t int "tst"
} }
legit := func(ts *testStruct, m *Message) error { return nil } legit := func(ts *testStruct, m *Message) error { return nil }
@ -48,8 +49,12 @@ func TestAddHandler(t *testing.T) {
} }
func TestDispatch(t *testing.T) { func TestDispatch(t *testing.T) {
type testStruct struct{ t int "tester"} type testStruct struct {
type testStruct2 struct{ t int "tester2"} t int "tester"
}
type testStruct2 struct {
t int "tester2"
}
count := 0 count := 0
h1 := func(ts *testStruct, m *Message) error { h1 := func(ts *testStruct, m *Message) error {
count += 1 count += 1

@ -6,42 +6,42 @@ import (
"net" "net"
"syscall" "syscall"
"github.com/op/go-logging"
"reflect"
"fmt" "fmt"
"github.com/op/go-logging"
"io" "io"
"reflect"
) )
const maxFdCount = 3 const maxFdCount = 3
type MsgConn struct { type MsgConn struct {
log *logging.Logger log *logging.Logger
conn *net.UnixConn conn *net.UnixConn
buf [1024]byte buf [1024]byte
oob []byte oob []byte
disp *msgDispatcher disp *msgDispatcher
factory MsgFactory factory MsgFactory
isClosed bool isClosed bool
idGen <-chan int idGen <-chan int
respMan *responseManager respMan *responseManager
onClose func() onClose func()
} }
type MsgServer struct { type MsgServer struct {
disp *msgDispatcher disp *msgDispatcher
factory MsgFactory factory MsgFactory
listener *net.UnixListener listener *net.UnixListener
done chan bool done chan bool
idGen <- chan int idGen <-chan int
} }
func NewServer(address string, factory MsgFactory, log *logging.Logger, handlers ...interface{}) (*MsgServer, error) { func NewServer(address string, factory MsgFactory, log *logging.Logger, handlers ...interface{}) (*MsgServer, error) {
md,err := createDispatcher(log, handlers...) md, err := createDispatcher(log, handlers...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
listener,err := net.ListenUnix("unix", &net.UnixAddr{address, "unix"}) listener, err := net.ListenUnix("unix", &net.UnixAddr{address, "unix"})
if err != nil { if err != nil {
md.close() md.close()
return nil, err return nil, err
@ -49,31 +49,31 @@ func NewServer(address string, factory MsgFactory, log *logging.Logger, handlers
done := make(chan bool) done := make(chan bool)
idGen := newIdGen(done) idGen := newIdGen(done)
return &MsgServer{ return &MsgServer{
disp: md, disp: md,
factory: factory, factory: factory,
listener: listener, listener: listener,
done: done, done: done,
idGen: idGen, idGen: idGen,
}, nil }, nil
} }
func (s *MsgServer) Run() error { func (s *MsgServer) Run() error {
for { for {
conn,err := s.listener.AcceptUnix() conn, err := s.listener.AcceptUnix()
if err != nil { if err != nil {
s.disp.close() s.disp.close()
s.listener.Close() s.listener.Close()
return err return err
} }
if err := setPassCred(conn); err != nil { if err := setPassCred(conn); err != nil {
return errors.New("Failed to set SO_PASSCRED on accepted socket connection:"+ err.Error()) return errors.New("Failed to set SO_PASSCRED on accepted socket connection:" + err.Error())
} }
mc := &MsgConn{ mc := &MsgConn{
conn: conn, conn: conn,
disp: s.disp, disp: s.disp,
oob: createOobBuffer(), oob: createOobBuffer(),
factory: s.factory, factory: s.factory,
idGen: s.idGen, idGen: s.idGen,
respMan: newResponseManager(), respMan: newResponseManager(),
} }
go mc.readLoop() go mc.readLoop()
@ -82,22 +82,22 @@ func (s *MsgServer) Run() error {
} }
func Connect(address string, factory MsgFactory, log *logging.Logger, handlers ...interface{}) (*MsgConn, error) { func Connect(address string, factory MsgFactory, log *logging.Logger, handlers ...interface{}) (*MsgConn, error) {
md,err := createDispatcher(log, handlers...) md, err := createDispatcher(log, handlers...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn,err := net.DialUnix("unix", nil, &net.UnixAddr{address, "unix"}) conn, err := net.DialUnix("unix", nil, &net.UnixAddr{address, "unix"})
if err != nil { if err != nil {
return nil, err return nil, err
} }
done := make(chan bool) done := make(chan bool)
idGen := newIdGen(done) idGen := newIdGen(done)
mc := &MsgConn{ mc := &MsgConn{
conn: conn, conn: conn,
disp: md, disp: md,
oob: createOobBuffer(), oob: createOobBuffer(),
factory: factory, factory: factory,
idGen: idGen, idGen: idGen,
respMan: newResponseManager(), respMan: newResponseManager(),
onClose: func() { onClose: func() {
md.close() md.close()
@ -114,7 +114,7 @@ func newIdGen(done <-chan bool) <-chan int {
return ch return ch
} }
func idGenLoop(done <-chan bool, out chan <- int) { func idGenLoop(done <-chan bool, out chan<- int) {
current := int(1) current := int(1)
for { for {
select { select {
@ -142,7 +142,7 @@ func (mc *MsgConn) logger() *logging.Logger {
} }
func (mc *MsgConn) processOneMessage() bool { func (mc *MsgConn) processOneMessage() bool {
m,err := mc.readMessage() m, err := mc.readMessage()
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
mc.Close() mc.Close()
@ -216,7 +216,6 @@ func (mc *MsgConn) readMessage() (*Message, error) {
// conn.AddHandlers(fooHandler, simpleHandler) // conn.AddHandlers(fooHandler, simpleHandler)
// //
func (mc *MsgConn) AddHandlers(args ...interface{}) error { func (mc *MsgConn) AddHandlers(args ...interface{}) error {
for len(args) > 0 { for len(args) > 0 {
if err := mc.disp.hmap.addHandler(args[0]); err != nil { if err := mc.disp.hmap.addHandler(args[0]); err != nil {
@ -227,11 +226,11 @@ func (mc *MsgConn) AddHandlers(args ...interface{}) error {
return nil return nil
} }
func (mc *MsgConn) SendMsg(msg interface{}, fds... int) error { func (mc *MsgConn) SendMsg(msg interface{}, fds ...int) error {
return mc.sendMessage(msg, <-mc.idGen, fds...) return mc.sendMessage(msg, <-mc.idGen, fds...)
} }
func (mc *MsgConn) ExchangeMsg(msg interface{}, fds... int) (ResponseReader, error) { func (mc *MsgConn) ExchangeMsg(msg interface{}, fds ...int) (ResponseReader, error) {
id := <-mc.idGen id := <-mc.idGen
rr := mc.respMan.register(id) rr := mc.respMan.register(id)
@ -239,10 +238,10 @@ func (mc *MsgConn) ExchangeMsg(msg interface{}, fds... int) (ResponseReader, err
rr.Done() rr.Done()
return nil, err return nil, err
} }
return rr,nil return rr, nil
} }
func (mc *MsgConn) sendMessage(msg interface{}, msgID int, fds... int) error { func (mc *MsgConn) sendMessage(msg interface{}, msgID int, fds ...int) error {
msgType, err := getMessageType(msg) msgType, err := getMessageType(msg)
if err != nil { if err != nil {
return err return err
@ -272,9 +271,8 @@ func getMessageType(msg interface{}) (string, error) {
return string(t.Field(0).Tag), nil return string(t.Field(0).Tag), nil
} }
func (mc *MsgConn) newBaseMessage(msgType string, msgID int, body interface{}) (*BaseMsg, error) { func (mc *MsgConn) newBaseMessage(msgType string, msgID int, body interface{}) (*BaseMsg, error) {
bodyBytes,err := json.Marshal(body) bodyBytes, err := json.Marshal(body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -289,13 +287,12 @@ func (mc *MsgConn) sendRaw(data []byte, fds ...int) error {
if len(fds) > 0 { if len(fds) > 0 {
return mc.sendWithFds(data, fds) return mc.sendWithFds(data, fds)
} }
_,err := mc.conn.Write(data) _, err := mc.conn.Write(data)
return err return err
} }
func (mc *MsgConn) sendWithFds(data []byte, fds []int) error { func (mc *MsgConn) sendWithFds(data []byte, fds []int) error {
oob := syscall.UnixRights(fds...) oob := syscall.UnixRights(fds...)
_,_,err := mc.conn.WriteMsgUnix(data, oob, nil) _, _, err := mc.conn.WriteMsgUnix(data, oob, nil)
return err return err
} }

@ -1,8 +1,9 @@
package ipc package ipc
import ( import (
"testing"
"sync"
"os" "os"
"sync"
"testing"
) )
type TestMsg struct { type TestMsg struct {
@ -12,15 +13,17 @@ type TestMsg struct {
type testConnection struct { type testConnection struct {
server *MsgConn server *MsgConn
client *MsgConn client *MsgConn
wg sync.WaitGroup wg sync.WaitGroup
called bool called bool
} }
type testServer struct { type testServer struct {
conn *MsgConn conn *MsgConn
wg sync.WaitGroup wg sync.WaitGroup
} }
const testSocket = "@test" const testSocket = "@test"
var testFactory = NewMsgFactory(new(TestMsg)) var testFactory = NewMsgFactory(new(TestMsg))
func testConnect(handler func(*TestMsg, *Message) error) (*testConnection, error) { func testConnect(handler func(*TestMsg, *Message) error) (*testConnection, error) {
@ -51,7 +54,7 @@ func testConnect(handler func(*TestMsg, *Message) error) (*testConnection, error
} }
func runTest(t *testing.T, handler func(*TestMsg, *Message) error, tester func(*testConnection)) { func runTest(t *testing.T, handler func(*TestMsg, *Message) error, tester func(*testConnection)) {
tc,err := testConnect(handler) tc, err := testConnect(handler)
if err != nil { if err != nil {
t.Error("error setting up test connection:", err) t.Error("error setting up test connection:", err)
} }
@ -86,7 +89,7 @@ func TestUcred(t *testing.T) {
} }
func TestPassFDs(t *testing.T) { func TestPassFDs(t *testing.T) {
fds := []int{1,2} fds := []int{1, 2}
handler := func(tm *TestMsg, msg *Message) error { handler := func(tm *TestMsg, msg *Message) error {
if len(msg.Fds) != len(fds) { if len(msg.Fds) != len(fds) {
t.Errorf("Expecting %d descriptors, got %d", len(fds), len(msg.Fds)) t.Errorf("Expecting %d descriptors, got %d", len(fds), len(msg.Fds))
@ -97,4 +100,4 @@ func TestPassFDs(t *testing.T) {
tc.client.SendMsg(&TestMsg{}, fds...) tc.client.SendMsg(&TestMsg{}, fds...)
}) })
} }

@ -2,14 +2,12 @@ package ipc
import ( import (
"encoding/json" "encoding/json"
"syscall" "errors"
"fmt" "fmt"
"reflect" "reflect"
"errors" "syscall"
) )
func NewMsgFactory(msgTypes ...interface{}) MsgFactory { func NewMsgFactory(msgTypes ...interface{}) MsgFactory {
mf := (MsgFactory)(make(map[string]func() interface{})) mf := (MsgFactory)(make(map[string]func() interface{}))
for _, mt := range msgTypes { for _, mt := range msgTypes {
@ -24,7 +22,7 @@ func NewMsgFactory(msgTypes ...interface{}) MsgFactory {
type MsgFactory map[string](func() interface{}) type MsgFactory map[string](func() interface{})
func (mf MsgFactory) create(msgType string) (interface{}, error) { func (mf MsgFactory) create(msgType string) (interface{}, error) {
f,ok := mf[msgType] f, ok := mf[msgType]
if !ok { if !ok {
return nil, fmt.Errorf("cannot create msg type: %s", msgType) return nil, fmt.Errorf("cannot create msg type: %s", msgType)
} }
@ -52,19 +50,19 @@ func (mf MsgFactory) register(mt interface{}) error {
} }
type Message struct { type Message struct {
Type string Type string
MsgID int MsgID int
Body interface{} Body interface{}
Ucred *syscall.Ucred Ucred *syscall.Ucred
Fds []int Fds []int
mconn *MsgConn mconn *MsgConn
} }
type BaseMsg struct { type BaseMsg struct {
Type string Type string
MsgID int MsgID int
IsResponse bool IsResponse bool
Body json.RawMessage Body json.RawMessage
} }
func (mc *MsgConn) parseMessage(data []byte) (*Message, error) { func (mc *MsgConn) parseMessage(data []byte) (*Message, error) {
@ -72,7 +70,7 @@ func (mc *MsgConn) parseMessage(data []byte) (*Message, error) {
if err := json.Unmarshal(data, &base); err != nil { if err := json.Unmarshal(data, &base); err != nil {
return nil, err return nil, err
} }
body,err := mc.factory.create(base.Type) body, err := mc.factory.create(base.Type)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,11 +81,11 @@ func (mc *MsgConn) parseMessage(data []byte) (*Message, error) {
m.Type = base.Type m.Type = base.Type
m.MsgID = base.MsgID m.MsgID = base.MsgID
m.Body = body m.Body = body
return m,nil return m, nil
} }
func (m *Message) Free() { func (m *Message) Free() {
for _,fd := range m.Fds { for _, fd := range m.Fds {
syscall.Close(fd) syscall.Close(fd)
} }
m.Fds = nil m.Fds = nil
@ -117,6 +115,6 @@ func (m *Message) parseControlData(data []byte) error {
return nil return nil
} }
func (m *Message) Respond(msg interface{}, fds... int) error { func (m *Message) Respond(msg interface{}, fds ...int) error {
return m.mconn.sendMessage(msg, m.MsgID, fds...) return m.mconn.sendMessage(msg, m.MsgID, fds...)
} }

@ -1,7 +1,8 @@
package ipc package ipc
import ( import (
"time"
"sync" "sync"
"time"
) )
type ResponseReader interface { type ResponseReader interface {
@ -10,10 +11,10 @@ type ResponseReader interface {
} }
type responseWaiter struct { type responseWaiter struct {
rm *responseManager rm *responseManager
id int id int
timeout time.Time timeout time.Time
ch chan *Message ch chan *Message
} }
func (rw *responseWaiter) Chan() <-chan *Message { func (rw *responseWaiter) Chan() <-chan *Message {
@ -28,7 +29,7 @@ func (rw *responseWaiter) Done() {
} }
type responseManager struct { type responseManager struct {
lock sync.Locker lock sync.Locker
responseMap map[int]*responseWaiter responseMap map[int]*responseWaiter
} }
@ -64,7 +65,7 @@ func (rm *responseManager) handle(m *Message) bool {
return true return true
} }
func (rm *responseManager) removeById(id int, klose bool) *responseWaiter{ func (rm *responseManager) removeById(id int, klose bool) *responseWaiter {
rw := rm.responseMap[id] rw := rm.responseMap[id]
if rw == nil { if rw == nil {
return nil return nil
@ -75,4 +76,3 @@ func (rm *responseManager) removeById(id int, klose bool) *responseWaiter{
} }
return rw return rw
} }

@ -1,4 +1,5 @@
package ipc package ipc
import ( import (
"testing" "testing"
) )

@ -1,9 +1,10 @@
package daemon package daemon
import ( import (
"github.com/subgraph/oz/ipc"
"errors" "errors"
"strconv"
"fmt" "fmt"
"github.com/subgraph/oz/ipc"
"strconv"
) )
func clientConnect() (*ipc.MsgConn, error) { func clientConnect() (*ipc.MsgConn, error) {
@ -11,7 +12,7 @@ func clientConnect() (*ipc.MsgConn, error) {
} }
func clientSend(msg interface{}) (*ipc.Message, error) { func clientSend(msg interface{}) (*ipc.Message, error) {
c,err := clientConnect() c, err := clientConnect()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -21,17 +22,17 @@ func clientSend(msg interface{}) (*ipc.Message, error) {
return nil, err return nil, err
} }
resp := <- rr.Chan() resp := <-rr.Chan()
rr.Done() rr.Done()
return resp,nil return resp, nil
} }
func ListProfiles() ([]Profile, error) { func ListProfiles() ([]Profile, error) {
resp,err := clientSend(new(ListProfilesMsg)) resp, err := clientSend(new(ListProfilesMsg))
if err != nil { if err != nil {
return nil, err return nil, err
} }
body,ok := resp.Body.(*ListProfilesResp) body, ok := resp.Body.(*ListProfilesResp)
if !ok { if !ok {
return nil, errors.New("ListProfiles response was not expected type") return nil, errors.New("ListProfiles response was not expected type")
} }
@ -39,11 +40,11 @@ func ListProfiles() ([]Profile, error) {
} }
func ListSandboxes() ([]SandboxInfo, error) { func ListSandboxes() ([]SandboxInfo, error) {
resp,err := clientSend(&ListSandboxesMsg{}) resp, err := clientSend(&ListSandboxesMsg{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
body,ok := resp.Body.(*ListSandboxesResp) body, ok := resp.Body.(*ListSandboxesResp)
if !ok { if !ok {
return nil, errors.New("ListSandboxes response was not expected type") return nil, errors.New("ListSandboxes response was not expected type")
} }
@ -51,13 +52,13 @@ func ListSandboxes() ([]SandboxInfo, error) {
} }
func Launch(arg string) error { func Launch(arg string) error {
idx,name,err := parseProfileArg(arg) idx, name, err := parseProfileArg(arg)
if err != nil { if err != nil {
return err return err
} }
resp,err := clientSend(&LaunchMsg{ resp, err := clientSend(&LaunchMsg{
Index: idx, Index: idx,
Name: name, Name: name,
}) })
if err != nil { if err != nil {
return err return err
@ -74,13 +75,13 @@ func Launch(arg string) error {
} }
func Clean(arg string) error { func Clean(arg string) error {
idx,name,err := parseProfileArg(arg) idx, name, err := parseProfileArg(arg)
if err != nil { if err != nil {
return err return err
} }
resp,err := clientSend(&CleanMsg{ resp, err := clientSend(&CleanMsg{
Index: idx, Index: idx,
Name: name, Name: name,
}) })
if err != nil { if err != nil {
return err return err
@ -100,18 +101,18 @@ func parseProfileArg(arg string) (int, string, error) {
if len(arg) == 0 { if len(arg) == 0 {
return 0, "", errors.New("profile argument needed") return 0, "", errors.New("profile argument needed")
} }
if n,err := strconv.Atoi(arg); err == nil { if n, err := strconv.Atoi(arg); err == nil {
return n, "", nil return n, "", nil
} }
return 0, arg, nil return 0, arg, nil
} }
func Logs(count int, follow bool) (chan string, error) { func Logs(count int, follow bool) (chan string, error) {
c,err := clientConnect() c, err := clientConnect()
if err != nil { if err != nil {
return nil, err return nil, err
} }
rr,err := c.ExchangeMsg(&LogsMsg{Count: count, Follow: follow}) rr, err := c.ExchangeMsg(&LogsMsg{Count: count, Follow: follow})
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -1,6 +1,5 @@
package daemon package daemon
type Config struct { type Config struct {
profileDir string `json:"profile_dir"` profileDir string `json:"profile_dir"`
} }

@ -5,20 +5,20 @@ import (
"github.com/op/go-logging" "github.com/op/go-logging"
"github.com/subgraph/oz" "github.com/subgraph/oz"
"github.com/subgraph/oz/ipc"
"syscall"
"github.com/subgraph/oz/fs" "github.com/subgraph/oz/fs"
"github.com/subgraph/oz/ipc"
"os/user" "os/user"
"syscall"
) )
type daemonState struct { type daemonState struct {
log *logging.Logger log *logging.Logger
profiles oz.Profiles profiles oz.Profiles
sandboxes []*Sandbox sandboxes []*Sandbox
nextSboxId int nextSboxId int
nextDisplay int nextDisplay int
memBackend *logging.ChannelMemoryBackend memBackend *logging.ChannelMemoryBackend
backends []logging.Backend backends []logging.Backend
} }
func Main() { func Main() {
@ -41,7 +41,7 @@ func Main() {
func initialize() *daemonState { func initialize() *daemonState {
d := &daemonState{} d := &daemonState{}
d.initializeLogging() d.initializeLogging()
ps,err := oz.LoadProfiles("/var/lib/oz/cells.d") ps, err := oz.LoadProfiles("/var/lib/oz/cells.d")
if err != nil { if err != nil {
d.log.Fatalf("Failed to load profiles: %v", err) d.log.Fatalf("Failed to load profiles: %v", err)
} }
@ -53,11 +53,10 @@ func initialize() *daemonState {
return d return d
} }
func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) { func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) {
d.Debug("Child process pid=%d exited with status %d", pid, wstatus.ExitStatus()) d.Debug("Child process pid=%d exited with status %d", pid, wstatus.ExitStatus())
for _,sbox := range d.sandboxes { for _, sbox := range d.sandboxes {
if sbox.init.Process.Pid == pid { if sbox.init.Process.Pid == pid {
sbox.remove() sbox.remove()
return return
@ -67,22 +66,22 @@ func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) {
} }
func runServer(log *logging.Logger, args ...interface{}) error { func runServer(log *logging.Logger, args ...interface{}) error {
s,err := ipc.NewServer(SocketName, messageFactory, log, args...) s, err := ipc.NewServer(SocketName, messageFactory, log, args...)
if err != nil { if err != nil {
return err return err
} }
return s.Run() return s.Run()
} }
func (d * daemonState) handlePing(msg *PingMsg, m *ipc.Message) error { func (d *daemonState) handlePing(msg *PingMsg, m *ipc.Message) error {
d.Debug("received ping with data [%s]", msg.Data) d.Debug("received ping with data [%s]", msg.Data)
return m.Respond(&PingMsg{msg.Data}) return m.Respond(&PingMsg{msg.Data})
} }
func (d * daemonState) handleListProfiles(msg *ListProfilesMsg, m *ipc.Message) error { func (d *daemonState) handleListProfiles(msg *ListProfilesMsg, m *ipc.Message) error {
r := new(ListProfilesResp) r := new(ListProfilesResp)
index := 1 index := 1
for _,p := range d.profiles { for _, p := range d.profiles {
r.Profiles = append(r.Profiles, Profile{Index: index, Name: p.Name, Path: p.Path}) r.Profiles = append(r.Profiles, Profile{Index: index, Name: p.Name, Path: p.Path})
index += 1 index += 1
} }
@ -91,12 +90,12 @@ func (d * daemonState) handleListProfiles(msg *ListProfilesMsg, m *ipc.Message)
func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error { func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error {
d.Debug("Launch message received: %+v", msg) d.Debug("Launch message received: %+v", msg)
p,err := d.getProfileByIdxOrName(msg.Index, msg.Name) p, err := d.getProfileByIdxOrName(msg.Index, msg.Name)
if err != nil { if err != nil {
return m.Respond(&ErrorMsg{err.Error()}) return m.Respond(&ErrorMsg{err.Error()})
} }
d.Debug("Would launch %s", p.Name) d.Debug("Would launch %s", p.Name)
_,err = d.launch(p, m.Ucred.Uid, m.Ucred.Gid) _, err = d.launch(p, m.Ucred.Uid, m.Ucred.Gid)
if err != nil { if err != nil {
d.Warning("launch of %s failed: %v", p.Name, err) d.Warning("launch of %s failed: %v", p.Name, err)
return m.Respond(&ErrorMsg{err.Error()}) return m.Respond(&ErrorMsg{err.Error()})
@ -112,9 +111,9 @@ func (d *daemonState) getProfileByIdxOrName(index int, name string) (*oz.Profile
return d.profiles[index-1], nil return d.profiles[index-1], nil
} }
for _,p := range d.profiles { for _, p := range d.profiles {
if p.Name == name { if p.Name == name {
return p,nil return p, nil
} }
} }
return nil, fmt.Errorf("could not find profile name '%s'", name) return nil, fmt.Errorf("could not find profile name '%s'", name)
@ -129,7 +128,7 @@ func (d *daemonState) handleListSandboxes(list *ListSandboxesMsg, msg *ipc.Messa
} }
func (d *daemonState) handleClean(clean *CleanMsg, msg *ipc.Message) error { func (d *daemonState) handleClean(clean *CleanMsg, msg *ipc.Message) error {
p,err := d.getProfileByIdxOrName(clean.Index, clean.Name) p, err := d.getProfileByIdxOrName(clean.Index, clean.Name)
if err != nil { if err != nil {
return msg.Respond(&ErrorMsg{err.Error()}) return msg.Respond(&ErrorMsg{err.Error()})
} }
@ -140,7 +139,7 @@ func (d *daemonState) handleClean(clean *CleanMsg, msg *ipc.Message) error {
} }
} }
// XXX // XXX
u,_ := user.Current() u, _ := user.Current()
fs := fs.NewFromProfile(p, u, d.log) fs := fs.NewFromProfile(p, u, d.log)
if err := fs.Cleanup(); err != nil { if err := fs.Cleanup(); err != nil {
return msg.Respond(&ErrorMsg{err.Error()}) return msg.Respond(&ErrorMsg{err.Error()})
@ -160,4 +159,3 @@ func (d *daemonState) handleLogs(logs *LogsMsg, msg *ipc.Message) error {
msg.Respond(&OkMsg{}) msg.Respond(&OkMsg{})
return nil return nil
} }

@ -1,32 +1,32 @@
package daemon package daemon
import ( import (
"bufio"
"fmt"
"github.com/subgraph/oz" "github.com/subgraph/oz"
"github.com/subgraph/oz/fs" "github.com/subgraph/oz/fs"
"os/exec"
"syscall"
"fmt"
"io"
"bufio"
"os/user"
"github.com/subgraph/oz/xpra" "github.com/subgraph/oz/xpra"
"io"
"os" "os"
"os/exec"
"os/user"
"path" "path"
"syscall"
) )
const initPath = "/usr/local/bin/oz-init" const initPath = "/usr/local/bin/oz-init"
type Sandbox struct { type Sandbox struct {
daemon *daemonState daemon *daemonState
id int id int
display int display int
profile *oz.Profile profile *oz.Profile
init *exec.Cmd init *exec.Cmd
cred *syscall.Credential cred *syscall.Credential
fs *fs.Filesystem fs *fs.Filesystem
stderr io.ReadCloser stderr io.ReadCloser
addr string addr string
xpra *xpra.Xpra xpra *xpra.Xpra
} }
/* /*
@ -45,11 +45,11 @@ func createInitCommand(name, chroot string, uid uint32, display int) *exec.Cmd {
cmd := exec.Command(initPath) cmd := exec.Command(initPath)
cmd.Dir = "/" cmd.Dir = "/"
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{
Chroot: chroot, Chroot: chroot,
Cloneflags: initCloneFlags, Cloneflags: initCloneFlags,
} }
cmd.Env = []string{ cmd.Env = []string{
"INIT_PROFILE="+name, "INIT_PROFILE=" + name,
fmt.Sprintf("INIT_UID=%d", uid), fmt.Sprintf("INIT_UID=%d", uid),
} }
if display > 0 { if display > 0 {
@ -58,8 +58,8 @@ func createInitCommand(name, chroot string, uid uint32, display int) *exec.Cmd {
return cmd return cmd
} }
func (d *daemonState) launch(p *oz.Profile, uid,gid uint32) (*Sandbox, error) { func (d *daemonState) launch(p *oz.Profile, uid, gid uint32) (*Sandbox, error) {
u,err := user.LookupId(fmt.Sprintf("%d", uid)) u, err := user.LookupId(fmt.Sprintf("%d", uid))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to lookup user for uid=%d: %v", uid, err) return nil, fmt.Errorf("failed to lookup user for uid=%d: %v", uid, err)
} }
@ -74,7 +74,7 @@ func (d *daemonState) launch(p *oz.Profile, uid,gid uint32) (*Sandbox, error) {
} }
cmd := createInitCommand(p.Name, fs.Root(), uid, display) cmd := createInitCommand(p.Name, fs.Root(), uid, display)
pp,err := cmd.StderrPipe() pp, err := cmd.StderrPipe()
if err != nil { if err != nil {
fs.Cleanup() fs.Cleanup()
return nil, fmt.Errorf("error creating stderr pipe for init process: %v", err) return nil, fmt.Errorf("error creating stderr pipe for init process: %v", err)
@ -85,25 +85,25 @@ func (d *daemonState) launch(p *oz.Profile, uid,gid uint32) (*Sandbox, error) {
return nil, err return nil, err
} }
sbox := &Sandbox{ sbox := &Sandbox{
daemon: d, daemon: d,
id: d.nextSboxId, id: d.nextSboxId,
display: display, display: display,
profile: p, profile: p,
init: cmd, init: cmd,
cred: &syscall.Credential{Uid: uid, Gid: gid}, cred: &syscall.Credential{Uid: uid, Gid: gid},
fs: fs, fs: fs,
addr: path.Join(fs.Root(), "tmp", "oz-init-control"), addr: path.Join(fs.Root(), "tmp", "oz-init-control"),
stderr: pp, stderr: pp,
} }
go sbox.logMessages() go sbox.logMessages()
d.nextSboxId += 1 d.nextSboxId += 1
d.sandboxes = append(d.sandboxes, sbox) d.sandboxes = append(d.sandboxes, sbox)
return sbox,nil return sbox, nil
} }
func (sbox *Sandbox) remove() { func (sbox *Sandbox) remove() {
sboxes := []*Sandbox{} sboxes := []*Sandbox{}
for _,sb := range sbox.daemon.sandboxes { for _, sb := range sbox.daemon.sandboxes {
if sb == sbox { if sb == sbox {
sb.fs.Cleanup() sb.fs.Cleanup()
} else { } else {
@ -142,7 +142,7 @@ func (sbox *Sandbox) logLine(line string) {
func (sbox *Sandbox) getLogFunc(c byte) func(string, ...interface{}) { func (sbox *Sandbox) getLogFunc(c byte) func(string, ...interface{}) {
log := sbox.daemon.log log := sbox.daemon.log
switch(c) { switch c {
case 'D': case 'D':
return log.Debug return log.Debug
case 'I': case 'I':

@ -1,12 +1,12 @@
package daemon package daemon
import ( import (
"github.com/op/go-logging" "github.com/op/go-logging"
"github.com/subgraph/oz/ipc"
"log" "log"
"os" "os"
"github.com/subgraph/oz/ipc"
) )
func (d *daemonState) Debug(format string, args ...interface{}) { func (d *daemonState) Debug(format string, args ...interface{}) {
d.log.Debug(format, args...) d.log.Debug(format, args...)
} }
@ -38,9 +38,11 @@ func (d *daemonState) initializeLogging() {
} }
d.installBackends() d.installBackends()
} }
var format = logging.MustStringFormatter( var format = logging.MustStringFormatter(
"%{color}%{time:15:04:05} â–¶ %{level:.4s} %{id:03x}%{color:reset} %{message}", "%{color}%{time:15:04:05} â–¶ %{level:.4s} %{id:03x}%{color:reset} %{message}",
) )
func (d *daemonState) addBackend(be logging.Backend) { func (d *daemonState) addBackend(be logging.Backend) {
d.backends = append(d.backends, be) d.backends = append(d.backends, be)
d.installBackends() d.installBackends()
@ -48,7 +50,7 @@ func (d *daemonState) addBackend(be logging.Backend) {
func (d *daemonState) removeBackend(be logging.Backend) { func (d *daemonState) removeBackend(be logging.Backend) {
newBackends := []logging.Backend{} newBackends := []logging.Backend{}
for _,b := range d.backends { for _, b := range d.backends {
if b != be { if b != be {
newBackends = append(newBackends, b) newBackends = append(newBackends, b)
} }
@ -66,9 +68,9 @@ func (d *daemonState) installBackends() {
} }
type logFollower struct { type logFollower struct {
daemon *daemonState daemon *daemonState
wrapper logging.Backend wrapper logging.Backend
m *ipc.Message m *ipc.Message
} }
func (lf *logFollower) Log(level logging.Level, calldepth int, rec *logging.Record) error { func (lf *logFollower) Log(level logging.Level, calldepth int, rec *logging.Record) error {
@ -84,7 +86,7 @@ func (lf *logFollower) remove() {
} }
func (d *daemonState) followLogs(m *ipc.Message) { func (d *daemonState) followLogs(m *ipc.Message) {
be := &logFollower{m:m, daemon: d} be := &logFollower{m: m, daemon: d}
be.wrapper = logging.NewBackendFormatter(be, format) be.wrapper = logging.NewBackendFormatter(be, format)
d.addBackend(be.wrapper) d.addBackend(be.wrapper)
} }

@ -1,4 +1,5 @@
package daemon package daemon
import "github.com/subgraph/oz/ipc" import "github.com/subgraph/oz/ipc"
const SocketName = "@oz-control" const SocketName = "@oz-control"
@ -21,8 +22,8 @@ type ListProfilesMsg struct {
type Profile struct { type Profile struct {
Index int Index int
Name string Name string
Path string Path string
} }
type ListProfilesResp struct { type ListProfilesResp struct {
@ -31,7 +32,7 @@ type ListProfilesResp struct {
type LaunchMsg struct { type LaunchMsg struct {
Index int "Launch" Index int "Launch"
Name string Name string
} }
type ListSandboxesMsg struct { type ListSandboxesMsg struct {
@ -39,7 +40,7 @@ type ListSandboxesMsg struct {
} }
type SandboxInfo struct { type SandboxInfo struct {
Id int Id int
Address string Address string
Profile string Profile string
} }
@ -50,11 +51,11 @@ type ListSandboxesResp struct {
type CleanMsg struct { type CleanMsg struct {
Index int "Clean" Index int "Clean"
Name string Name string
} }
type LogsMsg struct { type LogsMsg struct {
Count int "Logs" Count int "Logs"
Follow bool Follow bool
} }
@ -63,16 +64,15 @@ type LogData struct {
} }
var messageFactory = ipc.NewMsgFactory( var messageFactory = ipc.NewMsgFactory(
new(PingMsg), new(PingMsg),
new(OkMsg), new(OkMsg),
new(ErrorMsg), new(ErrorMsg),
new(ListProfilesMsg), new(ListProfilesMsg),
new(ListProfilesResp), new(ListProfilesResp),
new(LaunchMsg), new(LaunchMsg),
new(ListSandboxesMsg), new(ListSandboxesMsg),
new(ListSandboxesResp), new(ListSandboxesResp),
new(CleanMsg), new(CleanMsg),
new(LogsMsg), new(LogsMsg),
new(LogData), new(LogData),
) )

@ -1,8 +1,9 @@
package ozinit package ozinit
import ( import (
"github.com/subgraph/oz/ipc"
"errors" "errors"
"fmt" "fmt"
"github.com/subgraph/oz/ipc"
) )
func clientConnect(addr string) (*ipc.MsgConn, error) { func clientConnect(addr string) (*ipc.MsgConn, error) {
@ -10,7 +11,7 @@ func clientConnect(addr string) (*ipc.MsgConn, error) {
} }
func clientSend(addr string, msg interface{}) (*ipc.Message, error) { func clientSend(addr string, msg interface{}) (*ipc.Message, error) {
c,err := clientConnect(addr) c, err := clientConnect(addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -20,13 +21,13 @@ func clientSend(addr string, msg interface{}) (*ipc.Message, error) {
return nil, err return nil, err
} }
resp := <- rr.Chan() resp := <-rr.Chan()
rr.Done() rr.Done()
return resp, nil return resp, nil
} }
func Ping(addr string) error { func Ping(addr string) error {
resp,err := clientSend(addr, new(PingMsg)) resp, err := clientSend(addr, new(PingMsg))
if err != nil { if err != nil {
return err return err
} }
@ -41,12 +42,12 @@ func Ping(addr string) error {
} }
func RunShell(addr, term string) (int, error) { func RunShell(addr, term string) (int, error) {
c,err := clientConnect(addr) c, err := clientConnect(addr)
if err != nil { if err != nil {
return 0, err return 0, err
} }
rr,err := c.ExchangeMsg(&RunShellMsg{Term: term}) rr, err := c.ExchangeMsg(&RunShellMsg{Term: term})
resp := <- rr.Chan() resp := <-rr.Chan()
rr.Done() rr.Done()
c.Close() c.Close()
if err != nil { if err != nil {
@ -54,7 +55,7 @@ func RunShell(addr, term string) (int, error) {
} }
switch body := resp.Body.(type) { switch body := resp.Body.(type) {
case *ErrorMsg: case *ErrorMsg:
return 0,errors.New(body.Msg) return 0, errors.New(body.Msg)
case *OkMsg: case *OkMsg:
if len(resp.Fds) == 0 { if len(resp.Fds) == 0 {
return 0, errors.New("RunShell message returned Ok, but no file descriptor received") return 0, errors.New("RunShell message returned Ok, but no file descriptor received")
@ -64,5 +65,3 @@ func RunShell(addr, term string) (int, error) {
return 0, fmt.Errorf("Unexpected message type received: %+v", body) return 0, fmt.Errorf("Unexpected message type received: %+v", body)
} }
} }

@ -1,34 +1,34 @@
package ozinit package ozinit
import ( import (
"os" "bufio"
"fmt"
"github.com/kr/pty"
"github.com/op/go-logging"
"github.com/subgraph/oz" "github.com/subgraph/oz"
"github.com/subgraph/oz/fs" "github.com/subgraph/oz/fs"
"github.com/subgraph/oz/ipc" "github.com/subgraph/oz/ipc"
"os/exec"
"syscall"
"github.com/op/go-logging"
"github.com/kr/pty"
"fmt"
"github.com/subgraph/oz/xpra" "github.com/subgraph/oz/xpra"
"io"
"os"
"os/exec"
"os/user" "os/user"
"strconv" "strconv"
"io"
"bufio"
"strings" "strings"
"syscall"
) )
const profileDirectory = "/var/lib/oz/cells.d" const profileDirectory = "/var/lib/oz/cells.d"
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
uid int uid int
gid int gid int
user *user.User user *user.User
display int display int
fs *fs.Filesystem fs *fs.Filesystem
} }
// 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
@ -38,7 +38,7 @@ func createLogger() *logging.Logger {
l := logging.MustGetLogger("oz-init") l := logging.MustGetLogger("oz-init")
be := logging.NewLogBackend(os.Stderr, "", 0) be := logging.NewLogBackend(os.Stderr, "", 0)
f := logging.MustStringFormatter("%{level:.1s} %{message}") f := logging.MustStringFormatter("%{level:.1s} %{message}")
fbe := logging.NewBackendFormatter(be,f) fbe := logging.NewBackendFormatter(be, f)
logging.SetBackend(fbe) logging.SetBackend(fbe)
return l return l
} }
@ -65,29 +65,29 @@ func parseArgs() *initState {
uidval := getvar("INIT_UID") uidval := getvar("INIT_UID")
dispval := os.Getenv("INIT_DISPLAY") dispval := os.Getenv("INIT_DISPLAY")
p,err := loadProfile(pname) p, err := loadProfile(pname)
if err != nil { if err != nil {
log.Error("Could not load profile %s: %v", pname, err) log.Error("Could not load profile %s: %v", pname, err)
os.Exit(1) os.Exit(1)
} }
uid,err := strconv.Atoi(uidval) uid, err := strconv.Atoi(uidval)
if err != nil { if err != nil {
log.Error("Could not parse INIT_UID argument (%s) into an integer: %v", uidval, err) log.Error("Could not parse INIT_UID argument (%s) into an integer: %v", uidval, err)
os.Exit(1) os.Exit(1)
} }
u,err := user.LookupId(uidval) u, err := user.LookupId(uidval)
if err != nil { if err != nil {
log.Error("Failed to look up user with uid=%s: %v", uidval, err) log.Error("Failed to look up user with uid=%s: %v", uidval, err)
os.Exit(1) os.Exit(1)
} }
gid,err := strconv.Atoi(u.Gid) gid, err := strconv.Atoi(u.Gid)
if err != nil { if err != nil {
log.Error("Failed to parse gid value (%s) from user struct: %v", u.Gid, err) log.Error("Failed to parse gid value (%s) from user struct: %v", u.Gid, err)
os.Exit(1) os.Exit(1)
} }
display := 0 display := 0
if dispval != "" { if dispval != "" {
d,err := strconv.Atoi(dispval) d, err := strconv.Atoi(dispval)
if err != nil { if err != nil {
log.Error("Unable to parse display (%s) into an integer: %v", dispval, err) log.Error("Unable to parse display (%s) into an integer: %v", dispval, err)
os.Exit(1) os.Exit(1)
@ -96,13 +96,13 @@ func parseArgs() *initState {
} }
return &initState{ return &initState{
log: log, log: log,
profile: p, profile: p,
uid: uid, uid: uid,
gid: gid, gid: gid,
user: u, user: u,
display: display, display: display,
fs: fs.NewFromProfile(p, u, log), fs: fs.NewFromProfile(p, u, log),
} }
} }
@ -127,7 +127,7 @@ func (st *initState) runInit() {
oz.ReapChildProcs(st.log, st.handleChildExit) oz.ReapChildProcs(st.log, st.handleChildExit)
s,err := ipc.NewServer(socketAddress, messageFactory, st.log, s, err := ipc.NewServer(socketAddress, messageFactory, st.log,
handlePing, handlePing,
st.handleRunShell, st.handleRunShell,
) )
@ -152,14 +152,14 @@ func (st *initState) startXpraServer() {
return return
} }
xpra := xpra.NewServer(&st.profile.XServer, uint64(st.display), workdir) xpra := xpra.NewServer(&st.profile.XServer, uint64(st.display), workdir)
p,err := xpra.Process.StderrPipe() p, err := xpra.Process.StderrPipe()
if err != nil { if err != nil {
st.log.Warning("Error creating stderr pipe for xpra output: %v", err) st.log.Warning("Error creating stderr pipe for xpra output: %v", err)
os.Exit(1) os.Exit(1)
} }
go st.readXpraOutput(p) go st.readXpraOutput(p)
xpra.Process.Env = []string{ xpra.Process.Env = []string{
"HOME="+ st.user.HomeDir, "HOME=" + st.user.HomeDir,
} }
xpra.Process.SysProcAttr = &syscall.SysProcAttr{} xpra.Process.SysProcAttr = &syscall.SysProcAttr{}
xpra.Process.SysProcAttr.Credential = &syscall.Credential{ xpra.Process.SysProcAttr.Credential = &syscall.Credential{
@ -192,11 +192,11 @@ func (st *initState) readXpraOutput(r io.ReadCloser) {
} }
func loadProfile(name string) (*oz.Profile, error) { func loadProfile(name string) (*oz.Profile, error) {
ps,err := oz.LoadProfiles(profileDirectory) ps, err := oz.LoadProfiles(profileDirectory)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _,p := range ps { for _, p := range ps {
if name == p.Name { if name == p.Name {
return p, nil return p, nil
} }
@ -228,7 +228,7 @@ func (st *initState) handleRunShell(rs *RunShellMsg, msg *ipc.Message) error {
cmd.Env = append(cmd.Env, "PATH=/usr/bin:/bin") cmd.Env = append(cmd.Env, "PATH=/usr/bin:/bin")
cmd.Env = append(cmd.Env, fmt.Sprintf("PS1=[%s] $ ", st.profile.Name)) cmd.Env = append(cmd.Env, fmt.Sprintf("PS1=[%s] $ ", st.profile.Name))
st.log.Info("Executing shell...") st.log.Info("Executing shell...")
f,err := ptyStart(cmd) f, err := ptyStart(cmd)
defer f.Close() defer f.Close()
if err != nil { if err != nil {
return msg.Respond(&ErrorMsg{err.Error()}) return msg.Respond(&ErrorMsg{err.Error()})
@ -238,7 +238,7 @@ func (st *initState) handleRunShell(rs *RunShellMsg, msg *ipc.Message) error {
} }
func ptyStart(c *exec.Cmd) (ptty *os.File, err error) { func ptyStart(c *exec.Cmd) (ptty *os.File, err error) {
ptty,tty, err := pty.Open() ptty, tty, err := pty.Open()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -261,4 +261,3 @@ func ptyStart(c *exec.Cmd) (ptty *os.File, err error) {
func (is *initState) handleChildExit(pid int, wstatus syscall.WaitStatus) { func (is *initState) handleChildExit(pid int, wstatus syscall.WaitStatus) {
is.log.Debug("Child process pid=%d exited with status %d", pid, wstatus.ExitStatus()) is.log.Debug("Child process pid=%d exited with status %d", pid, wstatus.ExitStatus())
} }

@ -1,4 +1,5 @@
package ozinit package ozinit
import "github.com/subgraph/oz/ipc" import "github.com/subgraph/oz/ipc"
type OkMsg struct { type OkMsg struct {
@ -23,4 +24,3 @@ var messageFactory = ipc.NewMsgFactory(
new(PingMsg), new(PingMsg),
new(RunShellMsg), new(RunShellMsg),
) )

@ -3,11 +3,11 @@ package main
import ( import (
"fmt" "fmt"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"os"
"github.com/subgraph/oz/oz-daemon" "github.com/subgraph/oz/oz-daemon"
"strconv"
"io"
"github.com/subgraph/oz/oz-init" "github.com/subgraph/oz/oz-init"
"io"
"os"
"strconv"
) )
func main() { func main() {
@ -17,40 +17,38 @@ func main() {
app.Usage = "command line interface to Oz sandboxes" app.Usage = "command line interface to Oz sandboxes"
app.Author = "Subgraph" app.Author = "Subgraph"
app.Email = "info@subgraph.com" app.Email = "info@subgraph.com"
app.Commands = []cli.Command { app.Commands = []cli.Command{
{ {
Name: "profiles", Name: "profiles",
Usage: "list available application profiles", Usage: "list available application profiles",
Action: handleProfiles, Action: handleProfiles,
}, },
{ {
Name: "launch", Name: "launch",
Usage: "launch an application profile", Usage: "launch an application profile",
Action: handleLaunch, Action: handleLaunch,
}, },
{ {
Name: "list", Name: "list",
Usage: "list running sandboxes", Usage: "list running sandboxes",
Action: handleList, Action: handleList,
}, },
{ {
Name: "shell", Name: "shell",
Usage: "start a shell in a running container", Usage: "start a shell in a running container",
Action: handleShell, Action: handleShell,
}, },
{ {
Name: "clean", Name: "clean",
Action: handleClean, Action: handleClean,
}, },
{ {
Name: "logs", Name: "logs",
Action: handleLogs, Action: handleLogs,
Flags: []cli.Flag { Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "f", Name: "f",
}, },
}, },
}, },
} }
@ -58,12 +56,12 @@ func main() {
} }
func handleProfiles(c *cli.Context) { func handleProfiles(c *cli.Context) {
ps,err := daemon.ListProfiles() ps, err := daemon.ListProfiles()
if err != nil { if err != nil {
fmt.Printf("Error listing profiles: %v\n", err) fmt.Printf("Error listing profiles: %v\n", err)
os.Exit(1) os.Exit(1)
} }
for i,p := range ps { for i, p := range ps {
fmt.Printf("%2d) %-30s %s\n", i+1, p.Name, p.Path) fmt.Printf("%2d) %-30s %s\n", i+1, p.Name, p.Path)
} }
} }
@ -81,7 +79,7 @@ func handleLaunch(c *cli.Context) {
} }
func handleList(c *cli.Context) { func handleList(c *cli.Context) {
sboxes,err := daemon.ListSandboxes() sboxes, err := daemon.ListSandboxes()
if err != nil { if err != nil {
fmt.Printf("Error listing running containers: %v\n", err) fmt.Printf("Error listing running containers: %v\n", err)
os.Exit(1) os.Exit(1)
@ -101,13 +99,13 @@ func handleShell(c *cli.Context) {
fmt.Println("Sandbox id argument needed") fmt.Println("Sandbox id argument needed")
os.Exit(1) os.Exit(1)
} }
id,err := strconv.Atoi(c.Args()[0]) id, err := strconv.Atoi(c.Args()[0])
if err != nil { if err != nil {
fmt.Println("Sandbox id argument must be an integer") fmt.Println("Sandbox id argument must be an integer")
os.Exit(1) os.Exit(1)
} }
sb,err := getSandboxById(id) sb, err := getSandboxById(id)
if err != nil { if err != nil {
fmt.Printf("Error retrieving sandbox list: %v\n", err) fmt.Printf("Error retrieving sandbox list: %v\n", err)
} }
@ -115,13 +113,13 @@ func handleShell(c *cli.Context) {
fmt.Printf("No sandbox found with id = %d\n", id) fmt.Printf("No sandbox found with id = %d\n", id)
} }
term := os.Getenv("TERM") term := os.Getenv("TERM")
fd,err := ozinit.RunShell(sb.Address, term) fd, err := ozinit.RunShell(sb.Address, term)
if err != nil { if err != nil {
fmt.Printf("start shell command failed: %v\n", err) fmt.Printf("start shell command failed: %v\n", err)
os.Exit(1) os.Exit(1)
} }
fmt.Println("Entering interactive shell?\n") fmt.Println("Entering interactive shell?\n")
st,err := SetRawTerminal(0) st, err := SetRawTerminal(0)
HandleResize(fd) HandleResize(fd)
f := os.NewFile(uintptr(fd), "") f := os.NewFile(uintptr(fd), "")
go io.Copy(f, os.Stdin) go io.Copy(f, os.Stdin)
@ -130,15 +128,14 @@ func handleShell(c *cli.Context) {
fmt.Println("done..") fmt.Println("done..")
} }
func getSandboxById(id int) (*daemon.SandboxInfo, error) { func getSandboxById(id int) (*daemon.SandboxInfo, error) {
sboxes,err := daemon.ListSandboxes() sboxes, err := daemon.ListSandboxes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, sb := range sboxes { for _, sb := range sboxes {
if id == sb.Id { if id == sb.Id {
return &sb,nil return &sb, nil
} }
} }
return nil, nil return nil, nil
@ -157,7 +154,7 @@ func handleClean(c *cli.Context) {
func handleLogs(c *cli.Context) { func handleLogs(c *cli.Context) {
follow := c.Bool("f") follow := c.Bool("f")
ch,err := daemon.Logs(0,follow) ch, err := daemon.Logs(0, follow)
if err != nil { if err != nil {
fmt.Println("Logs failed", err) fmt.Println("Logs failed", err)
os.Exit(1) os.Exit(1)

@ -1,17 +1,18 @@
package main package main
import ( import (
"syscall" "fmt"
"unsafe"
"os" "os"
"os/signal" "os/signal"
"fmt" "syscall"
"unsafe"
) )
type winsize struct { type winsize struct {
Height uint16 Height uint16
Width uint16 Width uint16
x uint16 x uint16
y uint16 y uint16
} }
type State struct { type State struct {
termios Termios termios Termios
@ -101,7 +102,7 @@ func HandleResize(fd int) {
} }
func handleSIGWINCH(fd int) { func handleSIGWINCH(fd int) {
ws,errno := GetWinsize(0) ws, errno := GetWinsize(0)
if errno != 0 { if errno != 0 {
fmt.Printf("error reading winsize: %v\n", errno.Error()) fmt.Printf("error reading winsize: %v\n", errno.Error())
return return
@ -109,4 +110,4 @@ func handleSIGWINCH(fd int) {
if errno := SetWinsize(uintptr(fd), ws); errno != 0 { if errno := SetWinsize(uintptr(fd), ws); errno != 0 {
fmt.Printf("error setting winsize: %v", errno.Error()) fmt.Printf("error setting winsize: %v", errno.Error())
} }
} }

@ -65,18 +65,18 @@ var loadedProfiles []*Profile
type Profiles []*Profile type Profiles []*Profile
func (ps Profiles) GetProfileByName(name string) (*Profile,error) { func (ps Profiles) GetProfileByName(name string) (*Profile, error) {
if loadedProfiles == nil { if loadedProfiles == nil {
ps ,err := LoadProfiles(defaultProfileDirectory) ps, err := LoadProfiles(defaultProfileDirectory)
if err != nil { if err != nil {
return nil, err return nil, err
} }
loadedProfiles = ps loadedProfiles = ps
} }
for _,p := range loadedProfiles { for _, p := range loadedProfiles {
if p.Name == name { if p.Name == name {
return p,nil return p, nil
} }
} }
return nil, nil return nil, nil

@ -1,9 +1,10 @@
package xpra package xpra
import ( import (
"github.com/subgraph/oz"
"fmt" "fmt"
"os"
"github.com/op/go-logging" "github.com/op/go-logging"
"github.com/subgraph/oz"
"os"
"os/exec" "os/exec"
"syscall" "syscall"
) )
@ -54,11 +55,11 @@ func prepareClientArgs(config *oz.XServerConf, display uint64, workdir string, l
return args return args
} }
func exists(path,label string, log *logging.Logger) bool { func exists(path, label string, log *logging.Logger) bool {
if path == "" { if path == "" {
return false return false
} }
if _,err := os.Stat(path); err != nil { if _, err := os.Stat(path); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
log.Notice("%s file missing at %s, ignored.", label, path) log.Notice("%s file missing at %s, ignored.", label, path)
} else { } else {

@ -1,7 +1,8 @@
package xpra package xpra
import ( import (
"github.com/subgraph/oz"
"fmt" "fmt"
"github.com/subgraph/oz"
"os" "os"
"os/exec" "os/exec"
) )
@ -37,4 +38,3 @@ func prepareServerArgs(config *oz.XServerConf, display uint64, workdir string) [
) )
return args return args
} }

@ -1,7 +1,8 @@
package xpra package xpra
import ( import (
"os/exec"
"github.com/subgraph/oz" "github.com/subgraph/oz"
"os/exec"
) )
type Xpra struct { type Xpra struct {
@ -18,7 +19,6 @@ type Xpra struct {
// Arguments passed to xpra command // Arguments passed to xpra command
xpraArgs []string xpraArgs []string
} }
var xpraDefaultArgs = []string{ var xpraDefaultArgs = []string{
@ -52,4 +52,3 @@ func getDefaultArgs(config *oz.XServerConf) []string {
return args return args
} }

Loading…
Cancel
Save