// get all homes created by uid or uid as a member func (this *MemberManager) getMemberAllHomes(domain string, uid int64) ([]int64, error) { SQL := fmt.Sprintf("SELECT hid FROM %s_home_members WHERE uid = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], uid[%d], err[%v]", domain, uid, err) return nil, err } defer stmt.Close() rows, err := stmt.Query(uid) if err != nil { log.Errorf("query all homes failed:domain[%s], uid[%d], err[%v]", domain, uid, err) return nil, err } var hid int64 list := make([]int64, 0) for rows.Next() { err = rows.Scan(&hid) if err != nil { log.Warningf("parse the result failed:domain[%s], uid[%d], err[%v]", domain, uid, err) return nil, err } list = append(list, hid) } return list, nil }
// modify device info all column can be modified func (this *DeviceManager) modifyDeviceInfo(checkStatus bool, domain string, did int64, key string, value interface{}) error { common.CheckParam(len(key) != 0) var SQL string if checkStatus { SQL = fmt.Sprintf("UPDATE %s_device_info SET %s = ? WHERE did = ? AND status = %d", domain, key, ACTIVE) } else { SQL = fmt.Sprintf("UPDATE %s_device_info SET %s = ? WHERE did = ?", domain, key) } stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return err } defer stmt.Close() result, err := stmt.Exec(value, did) if err != nil { log.Errorf("execute update failed:domain[%s], key[%s], value[%v], err[%v]", domain, key, value, err) return err } affect, err := result.RowsAffected() if err != nil { log.Warningf("get affected rows failed:err[%v]", err) return err } if affect != 1 { log.Warningf("update device info failed:domain[%s], key[%s], value[%v], err[%v]", domain, key, value, err) return common.ErrEntryNotExist } return nil }
//////////////////////////////////////////////////////////////////////////////////// // database related private interface //////////////////////////////////////////////////////////////////////////////////// func (this *HomeManager) modifyHome(domain string, hid int64, key string, value interface{}) error { SQL := fmt.Sprintf("UPDATE %s_home_info SET %s = ? WHERE hid = ?", domain, key) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return err } defer stmt.Close() result, err := stmt.Exec(value, hid) if err != nil { log.Errorf("execute update failed:sql[%s], err[%v]", SQL, err) return err } affect, err := result.RowsAffected() if err != nil { log.Warningf("affected rows failed:err[%v]", err) return err } if affect < 1 { log.Warningf("check affected rows failed:domain[%s], hid[%d], row[%d]", domain, hid, affect) return common.ErrAccountNotExist } else if affect > 1 { log.Errorf("check affected rows failed:domain[%s], key[%s], value[%v], hid[%d], row[%d]", domain, key, value, hid, affect) return common.ErrUnknown } return nil }
func (this *BindingProxy) ChangeDeviceBinding(did int64, domain, subDomain, deviceId string) error { if this.cacheOn { this.cache.Delete(domain, did) } SQL1 := fmt.Sprintf("UPDATE %s_device_mapping SET sub_domain = ?, device_id = ?, bind_token = NULL WHERE did = ?", domain) stmt, err := this.store.db.Prepare(SQL1) if err != nil { log.Errorf("prepare update mapping failed:domain[%s], device[%s:%s], err[%v]", domain, subDomain, deviceId, err) return err } defer stmt.Close() result, err := stmt.Exec(subDomain, deviceId, did) if err != nil { log.Errorf("execute update failed:domain[%s], device[%s:%s], err[%v]", domain, subDomain, deviceId, err) return err } affect, err := result.RowsAffected() if err != nil { log.Warningf("get affected rows failed:err[%v]", err) return err } if affect != 1 { log.Errorf("check affected rows failed:domain[%s], device[%s:%s], err[%v]", domain, subDomain, deviceId, err) return common.ErrEntryNotExist } return nil }
// the device point gateway start listen func (this *DeviceGatewayServer) Start(host string) error { common.Assert((this.connManager != nil) && (this.devManager != nil), "check param nil") addr, err := net.ResolveTCPAddr("tcp4", host) if err != nil { log.Errorf("resolve tcp addr error:err[%v]", err) return err } ln, err := net.ListenTCP("tcp", addr) if err != nil { log.Errorf("listen error:err[%v]", err) return err } var waitGroup sync.WaitGroup for { socket, err := ln.AcceptTCP() if err != nil { log.Errorf("accept error:err[%v]", err) continue } log.Infof("device connect start:addr[%s]", socket.RemoteAddr()) waitGroup.Add(1) go this.deviceRoutine(&waitGroup, socket, QueueLen) } waitGroup.Wait() return err }
func (this *MemberManager) getAllMembers(domain string, hid int64) ([]Member, error) { SQL := fmt.Sprintf("SELECT uid, hid, name, type, status FROM %s_home_members WHERE hid = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], hid[%d], err[%v]", domain, hid, err) return nil, err } defer stmt.Close() rows, err := stmt.Query(hid) if err != nil { log.Errorf("query all members failed:domain[%s], hid[%d], err[%v]", domain, hid, err) return nil, err } defer rows.Close() var member Member list := make([]Member, 0) for rows.Next() { err := rows.Scan(&member.uid, &member.hid, &member.memberName, &member.memberType, &member.status) if err != nil { log.Errorf("parse the uid failed:domain[%s], hid[%d], err[%v]", domain, hid, err) return nil, err } list = append(list, member) } return list, nil }
// modify member info all column can be modified func (this *MemberManager) modifyMemberInfo(domain string, hid, uid int64, key string, value interface{}) error { common.CheckParam(len(key) != 0) SQL := fmt.Sprintf("UPDATE %s_home_members SET %s = ? WHERE uid = ? AND hid = ?", domain, key) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return err } defer stmt.Close() result, err := stmt.Exec(value, uid, hid) if err != nil { log.Errorf("execute update failed:sql[%s], err[%v]", SQL, err) return err } affect, err := result.RowsAffected() if err != nil { log.Warningf("get affected rows failed:err[%v]", err) return err } if affect < 1 { log.Warningf("check affected rows failed:domain[%s], hid[%d], uid[%d], row[%d]", domain, hid, uid, affect) return common.ErrEntryNotExist } else if affect > 1 { log.Errorf("check affected rows failed:domain[%s], hid[%d], uid[%d], row[%d]", domain, hid, uid, affect) return common.ErrUnknown } return nil }
// handle device response message forward to the result channel func (this *Connection) handleDevResponse(packet *common.Message) { request, find := this.requestMap.Find(packet.Header.MsgId) if find { // reset to the old message id packet.Header.MsgId = request.(*DeviceCtrlRequest).oldMessageId defer func() { info := recover() if info != nil { log.Warningf("the response queue is closed:err[%v]", info) } _, exist := this.requestMap.Delete(packet.Header.MsgId) if !exist { log.Errorf("delete request failed:mid[%d], code[%d], dest[%s], gid[%s]", packet.Header.MsgId, packet.Header.MsgCode, this.socket.RemoteAddr(), this.gid.String()) } log.Infof("delete the request mapping succ:mid[%d], code[%d], dest[%s], gid[%s]", packet.Header.MsgId, packet.Header.MsgCode, this.socket.RemoteAddr(), this.gid.String()) }() // if closed will panic *(request.(*DeviceCtrlRequest).responseQueue) <- *packet } else { log.Errorf("check the dev response not find request:mid[%d], code[%d], dest[%s], gid[%s]", packet.Header.MsgId, packet.Header.MsgCode, this.socket.RemoteAddr(), this.gid.String()) } }
// if not find in database return nil + nil func (this *WarehouseProxy) GetDeviceInfo(domain, subDomain, deviceId string) (*BasicInfo, error) { if this.cacheOn { basic, find := this.cache.Get(domain, subDomain, deviceId) if find { log.Infof("get device basic info from cache:domain[%s], device[%s:%s]", domain, subDomain, deviceId) return basic, nil } } SQL := fmt.Sprintf("SELECT device_type, public_key, status FROM %s_device_warehouse WHERE sub_domain = ? AND device_id = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return nil, err } defer stmt.Close() basic := NewBasicInfo() err = stmt.QueryRow(subDomain, deviceId).Scan(&basic.deviceType, &basic.publicKey, &basic.status) if err != nil { if err == sql.ErrNoRows { log.Warningf("no find the device:domain[%s], device[%s:%s]", domain, subDomain, deviceId) return nil, nil } log.Errorf("query failed:domain[%s], device[%s:%s]", domain, subDomain, deviceId) return nil, err } basic.subDomain = subDomain basic.deviceId = deviceId if this.cacheOn { this.cache.Set(domain, basic) } return basic, nil }
func NewBindingManager(store *DeviceStorage) *BindingManager { warehouse := NewDeviceWarehouse(store) if warehouse == nil { log.Errorf("new device warehouse failed") return nil } proxy := newBindingProxy(store) if proxy == nil { log.Errorf("new binding proxy failed") return nil } return &BindingManager{store: store, warehouse: warehouse, proxy: proxy} }
func (this *HomeManager) deleteHome(domain string, hid int64) error { SQL := fmt.Sprintf("DELETE FROM %s_home_info WHERE hid = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return err } defer stmt.Close() _, err = stmt.Exec(hid) if err != nil { log.Errorf("insert a new home failed:domain[%s], hid[%d], err[%v]", domain, hid, err) return err } return nil }
func (this *WarehouseProxy) InsertDeviceInfo(domain, subDomain, deviceId, publicKey string, master bool) error { var SQL string if master { SQL = fmt.Sprintf("INSERT INTO %s_device_warehouse(sub_domain, device_id, device_type, public_key, status) VALUES(?,?,?,?,?)", domain) } else { SQL = fmt.Sprintf("INSERT INTO %s_device_warehouse(sub_domain, device_id, device_type, status) VALUES(?,?,?,?)", domain) } stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return err } defer stmt.Close() if master { _, err = stmt.Exec(subDomain, deviceId, MASTER, publicKey, ACTIVE) } else { _, err = stmt.Exec(subDomain, deviceId, NORMAL, ACTIVE) } if err != nil { log.Warningf("execute insert device[%s:%s] failed:domain[%s], err[%v]", subDomain, deviceId, domain, err) return err } if this.cacheOn { this.cache.Delete(domain, subDomain, deviceId) } return nil }
func (this *BindingProxy) IsBindingExist(domain string, did int64, exist *bool) error { if this.cacheOn { _, find := this.cache.Get(domain, did) if find { *exist = true return nil } } SQL := fmt.Sprintf("SELECT did FROM %s_device_mapping WHERE did = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], did[%d], err[%v]", domain, did, err) return err } defer stmt.Close() var value int64 err = stmt.QueryRow(did).Scan(&value) if err != nil { if err == sql.ErrNoRows { *exist = false } else { log.Warningf("get binding info failed:domain[%s], did[%d], err[%v]", domain, did, err) return err } } *exist = true return nil }
// get by device global key, if not exist return nil + nil func (this *BindingProxy) GetBindingInfo(domain, subDomain, deviceId string) (*BindingInfo, error) { SQL := fmt.Sprintf("SELECT did, bind_token, expire_time FROM %s_device_mapping WHERE sub_domain = ? AND device_id = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], device[%s:%s], err[%v]", domain, subDomain, deviceId, err) return nil, err } defer stmt.Close() bind := NewBindingInfo() err = stmt.QueryRow(subDomain, deviceId).Scan(&bind.did, &bind.grantToken, &bind.grantTime) if err != nil { if err != sql.ErrNoRows { log.Warningf("query binding info failed:domain[%s], device[%s:%s], err[%v]", domain, subDomain, deviceId, err) } else { err = common.ErrEntryNotExist } return nil, err } bind.subDomain = subDomain bind.deviceId = deviceId if this.cacheOn { this.cache.Set(domain, bind) } return bind, nil }
////////////////////////////////////////////////////////////////////////////// /// private interface related to database ////////////////////////////////////////////////////////////////////////////// // transaction rollback according to the error status func rollback(err *error, tx *sql.Tx) { if *err != nil { log.Infof("error occured rollback:err[%v]", *err) newErr := tx.Rollback() if newErr != nil { log.Errorf("rollback failed:err[%v]", newErr) } } }
func NewDeviceStorage(host, user, password, database string) *DeviceStorage { dns := fmt.Sprintf("%s:%s@tcp(%s)/%s", user, password, host, database) driver, err := sql.Open("mysql", dns) if err != nil { log.Errorf("open MySQL Driver failed:dns[%s], err[%v]", dns, err) return nil } return &DeviceStorage{db: driver, database: database} }
// get device info func (this *DeviceManager) getDeviceInfo(domain string, did int64, device *DeviceInfo) error { SQL := fmt.Sprintf("SELECT did, hid, name, status, master_did FROM %s_device_info WHERE did = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], did[%d], err[%v]", domain, did, err) return err } defer stmt.Close() err = stmt.QueryRow(did).Scan(&device.did, &device.hid, &device.deviceName, &device.status, &device.masterDid) if err != nil { if err == sql.ErrNoRows { return common.ErrEntryNotExist } else { log.Errorf("get deive info failed:domain[%s], did[%d]", domain, did) return err } } return nil }
func (this *WarehouseProxy) DeleteDeviceInfo(domain, subDomain, deviceId string) error { if this.cacheOn { this.cache.Delete(domain, subDomain, deviceId) } SQL := fmt.Sprintf("DELETE FROM %s_device_warehouse WHERE sub_domain = ? AND device_id = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return err } defer stmt.Close() _, err = stmt.Exec(subDomain, deviceId) if err != nil { log.Errorf("delete the device failed:domain[%s], subDomain[%s], deviceId[%s], err[%s]", domain, subDomain, deviceId, err) return err } return nil }
// just for unit test warning func (this *DeviceStorage) Clean(domain, table string) error { if this.database != "device_test" { log.Error("clean interface only can be used by test") return common.ErrNotAllowed } SQL := fmt.Sprintf("DELETE FROM %s_%s", domain, table) stmt, err := this.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], table[%s], err[%v]", domain, table, err) return err } defer stmt.Close() _, err = stmt.Exec() if err != nil { log.Errorf("delete failed:domain[%s], table[%s], err[%v]", domain, table, err) return err } return nil }
func (this *HomeManager) insertHome(domain string, uid int64, name string) (int64, error) { // TODO modify this in one transaction SQL := fmt.Sprintf("INSERT INTO %s_home_info(name, status, create_uid) VALUES(?,?,?)", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return -1, err } defer stmt.Close() result, err := stmt.Exec(name, ACTIVE, uid) if err != nil { log.Errorf("create new home failed:domain[%s], createUid[%d], name[%s], err[%v]", domain, uid, name, err) return -1, err } hid, err := result.LastInsertId() if err != nil { log.Errorf("get last insert id failed:domain[%s], createUid[%d], name[%s], err[%v]", domain, uid, name, err) return -1, err } return hid, nil }
func (this *DeviceGatewayServer) deviceRoutine(waitGroup *sync.WaitGroup, socket *net.TCPConn, maxLen int) { defer waitGroup.Done() conn := NewConnection(socket, maxLen, this.devManager) defer conn.Close() // step 1. shake hands with the dev connection deviceGid, err := conn.DeviceHandShake() if err != nil { if deviceGid != nil { log.Errorf("device handshake failed exit:addr[%s], gid[%s]", socket.RemoteAddr(), deviceGid.String()) return } log.Errorf("device handshake failed exit:addr[%s]", socket.RemoteAddr()) return } // step 2. set the socket option as keep alive socket.SetKeepAlive(true) socket.SetKeepAlivePeriod(time.Second * 30) // step 3. record the connection for forward manager err = this.connManager.Insert(deviceGid, conn) if err != nil { log.Errorf("insert the device connection Failed:addr[%s], gid[%s], err[%v]", socket.RemoteAddr(), deviceGid.String(), err) return } // for debug info log.Infof("device connection created:addr[%s], gid[%s]", socket.RemoteAddr(), deviceGid.String()) // step 4. loop forward all request and receive all response conn.Loop(waitGroup) // step 5. remove conn from device connection manager _, find := this.connManager.Delete(deviceGid) if !find { log.Errorf("delete device connection before close:addr[%s], gid[%s]", socket.RemoteAddr(), deviceGid.String()) } // for debug info log.Infof("device connection closed:addr[%s], gid[%s]", socket.RemoteAddr(), deviceGid.String()) }
// if find the record return basic info + nil, if not exist, return nil + nil func (this *DeviceWarehouse) Get(domain, subDomain, deviceId string) (*BasicInfo, error) { device, err := this.proxy.GetDeviceInfo(domain, subDomain, deviceId) if err != nil { log.Warningf("get device info failed:domain[%s], device[%s:%s]", domain, subDomain, deviceId) return nil, err } else if device == nil { log.Warningf("not find the device info:domain[%s], device[%s:%s]", domain, subDomain, deviceId) return nil, nil } if !device.Validate() { log.Errorf("validate device info failed:domain[%s], device[%s:%s]", domain, subDomain, deviceId) return nil, common.ErrInvalidDevice } return device, nil }
// delete all device info in home func (this *DeviceManager) deleteAllDevices(domain string, hid int64) error { SQL := fmt.Sprintf("DELETE FROM %s_device_info WHERE hid = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Warningf("prepare delete all devices of home failed:domain[%s], hid[%d], err[%v]", domain, hid, err) return err } defer stmt.Close() _, err = stmt.Exec(hid) if err != nil { log.Errorf("delete all device of home failed:domain[%s], hid[%d], err[%v]", domain, hid, err) return err } return nil }
func (this *MemberManager) deleteAllMembers(domain string, hid int64) error { SQL := fmt.Sprintf("DELETE FROM %s_home_members WHERE hid = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], hid[%d], err[%s]", domain, hid, err) return err } defer stmt.Close() _, err = stmt.Exec(hid) if err != nil { log.Warningf("delete all member failed:domain[%s], hid[%d]", domain, hid) return err } return nil }
func (this *MemberManager) insertMemberInfo(domain, member string, hid, uid int64) error { SQL := fmt.Sprintf("INSERT INTO %s_home_members(uid, hid, type, name, status) VALUES(?,?,?,?,?)", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], err[%s]", domain, err) return err } defer stmt.Close() _, err = stmt.Exec(uid, hid, NORMAL, member, ACTIVE) if err != nil { log.Warningf("insert the member failed:domain[%s], name[%s], uid[%d], hid[%d]", domain, member, uid, hid) return err } return nil }
// Loop wait dev empty reponse for the current request // WARNING: ALL request to device will be queueed or blocked // if not receive the empty reponse packet func (this *Connection) waitEmptyResponse(request *common.Message) { for !this.exit { var emptyPacket common.Message select { case emptyPacket = <-this.emptyRespQueue: // fmt.Println("find a empty response", emptyPacket.Header) // must wait the same empty message id if emptyPacket.Header.MsgId == request.Header.MsgId { return } else { log.Errorf("empty msg id not the same as last request:mid[%d], requestId[%d]", emptyPacket.Header.MsgId, request.Header.MsgId) } } } }
func (this *MemberManager) deleteOneMember(domain string, hid, uid int64) error { SQL := fmt.Sprintf("DELETE FROM %s_home_members WHERE uid = ? AND hid = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], hid[%d], err[%s]", domain, hid, err) return err } defer stmt.Close() // not check the affected rows _, err = stmt.Exec(uid, hid) if err != nil { log.Warningf("delete member failed:domain[%s], uid[%d], hid[%d]", domain, uid, hid) return err } return nil }
//////////////////////////////////////////////////////////////////////////////////// // database related private interface //////////////////////////////////////////////////////////////////////////////////// func (this *MemberManager) getMemberInfo(domain string, hid, uid int64, member *Member) error { SQL := fmt.Sprintf("SELECT uid, hid, name, type, status FROM %s_home_members WHERE uid = ? AND hid = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:domain[%s], hid[%d], err[%v]", domain, hid, err) return err } defer stmt.Close() err = stmt.QueryRow(uid, hid).Scan(&member.uid, &member.hid, &member.memberName, &member.memberType, &member.status) if err != nil { if err == sql.ErrNoRows { return common.ErrEntryNotExist } else { log.Warningf("query member info failed:domain[%s], hid[%d], err[%v]", domain, hid, err) return err } } return nil }
func (this *HomeManager) getHome(domain string, hid int64, home *Home) error { SQL := fmt.Sprintf("SELECT hid, name, status, create_uid FROM %s_home_info WHERE hid = ?", domain) stmt, err := this.store.db.Prepare(SQL) if err != nil { log.Errorf("prepare query failed:err[%v]", err) return err } defer stmt.Close() err = stmt.QueryRow(hid).Scan(&home.hid, &home.name, &home.status, &home.createUid) if err != nil { if err == sql.ErrNoRows { return common.ErrEntryNotExist } else { log.Warningf("get home info failed:domain[%s], hid[%d]", domain, hid) return err } } return nil }
// create a new home func (this *HomeManager) Create(domain string, uid int64, name string) error { common.CheckParam(this.store != nil) if len(name) <= 0 { log.Warningf("check the home name failed:uid[%d], name[%s]", uid, name) return common.ErrInvalidParam } hid, err := this.insertHome(domain, uid, name) if err != nil { log.Warningf("insert home failed:domain[%s], createUid[%d], name[%s]", domain, uid, name) return err } // insert member as creator type to the home_members member := NewMemberManager(this.store) err = member.AddOwner(domain, "owner", hid, uid) if err != nil { log.Errorf("add owner to the home failed:domain[%s], createUid[%d], name[%s]", domain, uid, name) return err } return nil }