networking
brl 10 years ago
parent e28f599224
commit eee260576b

@ -1,13 +1,13 @@
package oz
import (
"syscall"
"github.com/op/go-logging"
"os"
"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)
signal.Notify(sigs, syscall.SIGCHLD)
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)) {
var wstatus syscall.WaitStatus
for {
pid,err := syscall.Wait4(-1, &wstatus, syscall.WNOHANG, nil)
pid, err := syscall.Wait4(-1, &wstatus, syscall.WNOHANG, nil)
switch err {
case syscall.ECHILD:
return

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

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

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

@ -19,12 +19,12 @@ type directory struct {
}
type Filesystem struct {
log *logging.Logger
log *logging.Logger
user *user.User
name string
name string
base string
root string
xpra string
xpra string
userID string
noDefaults 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 {
fs := NewFilesystem(profile.Name, user, log)
for _,wl := range profile.Whitelist {
for _, wl := range profile.Whitelist {
fs.addWhitelist(wl.Path, wl.Path, wl.ReadOnly)
}
for _,bl := range profile.Blacklist {
for _, bl := range profile.Blacklist {
fs.addBlacklist(bl.Path)
}
fs.noDefaults = profile.NoDefaults
@ -210,9 +210,9 @@ func copyFileInfo(info os.FileInfo, target string) error {
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
for _,sd := range subdirs {
for _, sd := range subdirs {
dir = path.Join(dir, sd)
if err := os.Mkdir(dir, mode); err != nil && !os.IsExist(err) {
return err

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

@ -1,13 +1,13 @@
package fs
import (
"errors"
"fmt"
"os"
"path"
"syscall"
"os/user"
"path"
"strconv"
"errors"
"syscall"
)
var basicBindDirs = []string{
@ -44,7 +44,7 @@ func (fs *Filesystem) Setup() error {
if err := fs.createXpraDir(); err != nil {
return err
}
item,err := fs.newItem(fs.xpra, fs.xpra, false)
item, err := fs.newItem(fs.xpra, fs.xpra, false)
if err != nil {
return err
}
@ -60,7 +60,7 @@ func (fs *Filesystem) Setup() error {
}
func (fs *Filesystem) createXpraDir() error {
uid,gid,err := userIds(fs.user)
uid, gid, err := userIds(fs.user)
if err != nil {
return err
}
@ -72,15 +72,15 @@ func (fs *Filesystem) createXpraDir() error {
}
func userIds(user *user.User) (int, int, error) {
uid,err := strconv.Atoi(user.Uid)
uid, err := strconv.Atoi(user.Uid)
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 {
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 {

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

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

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

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

@ -2,14 +2,12 @@ package ipc
import (
"encoding/json"
"syscall"
"errors"
"fmt"
"reflect"
"errors"
"syscall"
)
func NewMsgFactory(msgTypes ...interface{}) MsgFactory {
mf := (MsgFactory)(make(map[string]func() interface{}))
for _, mt := range msgTypes {
@ -24,7 +22,7 @@ func NewMsgFactory(msgTypes ...interface{}) MsgFactory {
type MsgFactory map[string](func() interface{})
func (mf MsgFactory) create(msgType string) (interface{}, error) {
f,ok := mf[msgType]
f, ok := mf[msgType]
if !ok {
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 string
Type string
MsgID int
Body interface{}
Body interface{}
Ucred *syscall.Ucred
Fds []int
mconn *MsgConn
}
type BaseMsg struct {
Type string
MsgID int
Type string
MsgID int
IsResponse bool
Body json.RawMessage
Body json.RawMessage
}
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 {
return nil, err
}
body,err := mc.factory.create(base.Type)
body, err := mc.factory.create(base.Type)
if err != nil {
return nil, err
}
@ -83,11 +81,11 @@ func (mc *MsgConn) parseMessage(data []byte) (*Message, error) {
m.Type = base.Type
m.MsgID = base.MsgID
m.Body = body
return m,nil
return m, nil
}
func (m *Message) Free() {
for _,fd := range m.Fds {
for _, fd := range m.Fds {
syscall.Close(fd)
}
m.Fds = nil
@ -117,6 +115,6 @@ func (m *Message) parseControlData(data []byte) error {
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...)
}

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

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

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

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

@ -5,20 +5,20 @@ import (
"github.com/op/go-logging"
"github.com/subgraph/oz"
"github.com/subgraph/oz/ipc"
"syscall"
"github.com/subgraph/oz/fs"
"github.com/subgraph/oz/ipc"
"os/user"
"syscall"
)
type daemonState struct {
log *logging.Logger
profiles oz.Profiles
sandboxes []*Sandbox
nextSboxId int
log *logging.Logger
profiles oz.Profiles
sandboxes []*Sandbox
nextSboxId int
nextDisplay int
memBackend *logging.ChannelMemoryBackend
backends []logging.Backend
memBackend *logging.ChannelMemoryBackend
backends []logging.Backend
}
func Main() {
@ -41,7 +41,7 @@ func Main() {
func initialize() *daemonState {
d := &daemonState{}
d.initializeLogging()
ps,err := oz.LoadProfiles("/var/lib/oz/cells.d")
ps, err := oz.LoadProfiles("/var/lib/oz/cells.d")
if err != nil {
d.log.Fatalf("Failed to load profiles: %v", err)
}
@ -53,11 +53,10 @@ func initialize() *daemonState {
return d
}
func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) {
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 {
sbox.remove()
return
@ -67,22 +66,22 @@ func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) {
}
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 {
return err
}
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)
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)
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})
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 {
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 {
return m.Respond(&ErrorMsg{err.Error()})
}
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 {
d.Warning("launch of %s failed: %v", p.Name, err)
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
}
for _,p := range d.profiles {
for _, p := range d.profiles {
if p.Name == name {
return p,nil
return p, nil
}
}
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 {
p,err := d.getProfileByIdxOrName(clean.Index, clean.Name)
p, err := d.getProfileByIdxOrName(clean.Index, clean.Name)
if err != nil {
return msg.Respond(&ErrorMsg{err.Error()})
}
@ -140,7 +139,7 @@ func (d *daemonState) handleClean(clean *CleanMsg, msg *ipc.Message) error {
}
}
// XXX
u,_ := user.Current()
u, _ := user.Current()
fs := fs.NewFromProfile(p, u, d.log)
if err := fs.Cleanup(); err != nil {
return msg.Respond(&ErrorMsg{err.Error()})
@ -160,4 +159,3 @@ func (d *daemonState) handleLogs(logs *LogsMsg, msg *ipc.Message) error {
msg.Respond(&OkMsg{})
return nil
}

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

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

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

@ -1,8 +1,9 @@
package ozinit
import (
"github.com/subgraph/oz/ipc"
"errors"
"fmt"
"github.com/subgraph/oz/ipc"
)
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) {
c,err := clientConnect(addr)
c, err := clientConnect(addr)
if err != nil {
return nil, err
}
@ -20,13 +21,13 @@ func clientSend(addr string, msg interface{}) (*ipc.Message, error) {
return nil, err
}
resp := <- rr.Chan()
resp := <-rr.Chan()
rr.Done()
return resp, nil
}
func Ping(addr string) error {
resp,err := clientSend(addr, new(PingMsg))
resp, err := clientSend(addr, new(PingMsg))
if err != nil {
return err
}
@ -41,12 +42,12 @@ func Ping(addr string) error {
}
func RunShell(addr, term string) (int, error) {
c,err := clientConnect(addr)
c, err := clientConnect(addr)
if err != nil {
return 0, err
}
rr,err := c.ExchangeMsg(&RunShellMsg{Term: term})
resp := <- rr.Chan()
rr, err := c.ExchangeMsg(&RunShellMsg{Term: term})
resp := <-rr.Chan()
rr.Done()
c.Close()
if err != nil {
@ -54,7 +55,7 @@ func RunShell(addr, term string) (int, error) {
}
switch body := resp.Body.(type) {
case *ErrorMsg:
return 0,errors.New(body.Msg)
return 0, errors.New(body.Msg)
case *OkMsg:
if len(resp.Fds) == 0 {
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)
}
}

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

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

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

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

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

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

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

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

Loading…
Cancel
Save