package ipc import ( "encoding/json" "errors" "fmt" "reflect" "syscall" ) func NewMsgFactory(msgTypes ...interface{}) MsgFactory { mf := (MsgFactory)(make(map[string]func() interface{})) for _, mt := range msgTypes { if err := mf.register(mt); err != nil { defaultLog.Fatalf("failed adding (%T) in NewMsgFactory: %v", mt, err) return nil } } return mf } type MsgFactory map[string](func() interface{}) func (mf MsgFactory) create(msgType string) (interface{}, error) { f, ok := mf[msgType] if !ok { return nil, fmt.Errorf("cannot create msg type: %s", msgType) } return f(), nil } func (mf MsgFactory) register(mt interface{}) error { t := reflect.TypeOf(mt) if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() != reflect.Struct { return errors.New("not a structure") } if t.NumField() == 0 || len(t.Field(0).Tag) == 0 { return errors.New("no tag on first field of structure") } tag := string(t.Field(0).Tag) mf[tag] = func() interface{} { v := reflect.New(t) return v.Interface() } return nil } type Message struct { Type string MsgID int Body interface{} Ucred *syscall.Ucred Fds []int mconn *MsgConn } type BaseMsg struct { Type string MsgID int IsResponse bool Body json.RawMessage } func (mc *MsgConn) parseMessage(data []byte) (*Message, error) { var base BaseMsg if err := json.Unmarshal(data, &base); err != nil { return nil, err } body, err := mc.factory.create(base.Type) if err != nil { return nil, err } if err := json.Unmarshal(base.Body, body); err != nil { return nil, err } m := new(Message) m.Type = base.Type m.MsgID = base.MsgID m.Body = body return m, nil } func (m *Message) Free() { for _, fd := range m.Fds { syscall.Close(fd) } m.Fds = nil } func (m *Message) parseControlData(data []byte) error { cmsgs, err := syscall.ParseSocketControlMessage(data) if err != nil { return err } for _, cmsg := range cmsgs { switch cmsg.Header.Type { case syscall.SCM_CREDENTIALS: cred, err := syscall.ParseUnixCredentials(&cmsg) if err != nil { return err } m.Ucred = cred case syscall.SCM_RIGHTS: fds, err := syscall.ParseUnixRights(&cmsg) if err != nil { return err } m.Fds = fds } } return nil } func (m *Message) Respond(msg interface{}, fds ...int) error { return m.mconn.sendMessage(msg, m.MsgID, fds...) }