//! 逻辑处理,转发消息到logic service int ffgate_t::route_logic_msg(const message_t& msg_, socket_ptr_t sock_) { session_data_t* session_data = sock_->get_data<session_data_t>(); LOGTRACE((FFGATE, "ffgate_t::route_logic_msg session_id[%s]", session_data->id())); client_info_t& client_info = m_client_set[session_data->id()]; if (client_info.request_queue.size() == MAX_MSG_QUEUE_SIZE) { //! 消息队列超限,关闭sock sock_->close(); return 0; } route_logic_msg_t::in_t msg; msg.session_id = session_data->id(); msg.cmd = msg_.get_cmd(); msg.body = msg_.get_body(); if (client_info.request_queue.empty()) { m_ffrpc->call(client_info.alloc_logic_service, msg, ffrpc_ops_t::gen_callback(&ffgate_t::route_logic_msg_callback, this, session_data->id(), sock_)); } else { client_info.request_queue.push(msg); } LOGTRACE((FFGATE, "ffgate_t::route_logic_msg end ok alloc_logic_service[%s]", client_info.alloc_logic_service)); return 0; }
void reactor_t::on_message(const std::shared_ptr<channel<io::socket<tcp>>>& channel_, const message_t& message) { slot_map_t::const_iterator slot = m_slots.find(message.id()); if(slot == m_slots.end()) { COCAINE_LOG_WARNING(m_log, "dropping an unknown type %d message", message.id()); return; } std::string response; try { response = (*slot->second)(message.args()); } catch(const std::exception& e) { COCAINE_LOG_ERROR( m_log, "unable to process type %d message - %s", message.id(), e.what() ); return; } if(!response.empty()) { channel_->wr->stream()->write(response.data(), response.size()); } }
void locator_t::on_message(const key_type& key, const message_t& message) { std::string uuid; std::tie(uuid, std::ignore, std::ignore) = key; switch(message.id()) { case event_traits<io::rpc::chunk>::id: { std::string chunk; message.as<io::rpc::chunk>(chunk); msgpack::unpacked unpacked; msgpack::unpack(&unpacked, chunk.data(), chunk.size()); auto dump = unpacked.get().as<synchronize_result_type>(); auto diff = m_router->update_remote(uuid, dump); for(auto it = diff.second.begin(); it != diff.second.end(); ++it) { m_gateway->cleanup(uuid, it->first); } for(auto it = diff.first.begin(); it != diff.first.end(); ++it) { m_gateway->consume(uuid, it->first, it->second); } } break; case event_traits<io::rpc::error>::id: case event_traits<io::rpc::choke>::id: { COCAINE_LOG_INFO(m_log, "node '%s' has been shut down", uuid); auto removed = m_router->remove_remote(uuid); for(auto it = removed.begin(); it != removed.end(); ++it) { m_gateway->cleanup(uuid, it->first); } // NOTE: It is dangerous to remove the channel while the message is still being // processed, so we defer it via reactor_t::post(). m_reactor.post(deferred_erase_action<decltype(m_remotes)> { m_remotes, key }); } break; default: COCAINE_LOG_ERROR(m_log, "dropped unknown type %d synchronization message", message.id()); } }
void engine_t::on_control(const message_t& message) { std::lock_guard<std::mutex> pool_guard(m_pool_mutex); switch(message.id()) { case event_traits<control::report>::id: { collector_t collector; Json::Value info(Json::objectValue); size_t active = std::count_if( m_pool.cbegin(), m_pool.cend(), std::bind<bool>(std::ref(collector), _1) ); info["load-median"] = static_cast<Json::LargestUInt>(collector.median()); info["queue"]["capacity"] = static_cast<Json::LargestUInt>(m_profile.queue_limit); info["queue"]["depth"] = static_cast<Json::LargestUInt>(m_queue.size()); info["sessions"]["pending"] = static_cast<Json::LargestUInt>(collector.sum()); info["slaves"]["active"] = static_cast<Json::LargestUInt>(active); info["slaves"]["capacity"] = static_cast<Json::LargestUInt>(m_profile.pool_limit); info["slaves"]["idle"] = static_cast<Json::LargestUInt>(m_pool.size() - active); info["state"] = describe[static_cast<int>(m_state)]; m_channel->wr->write<control::info>(0UL, info); } break; case event_traits<control::terminate>::id: { // Prepare for the shutdown. migrate(states::stopping); // NOTE: This message is needed to wake up the app's event loop, which is blocked // in order to allow the stream to flush the message queue. m_channel->wr->write<control::terminate>(0UL); } break; default: COCAINE_LOG_ERROR(m_log, "dropping unknown type %d control message", message.id()); } }
//! 当有消息到来,被回调 int ffbroker_t::handle_msg_impl(const message_t& msg_, socket_ptr_t sock_) { uint16_t cmd = msg_.get_cmd(); LOGTRACE((BROKER, "ffbroker_t::handle_msg_impl cmd<%u> begin", cmd)); ffslot_t::callback_t* cb = m_ffslot.get_callback(cmd); if (cb) { try { ffslot_msg_arg arg(msg_.get_body(), sock_); cb->exe(&arg); LOGTRACE((BROKER, "ffbroker_t::handle_msg_impl cmd<%u> end ok", cmd)); return 0; } catch(exception& e_) { LOGERROR((BROKER, "ffbroker_t::handle_msg_impl exception<%s>", e_.what())); return -1; } } return -1; }
//! 验证sessionid int ffgate_t::verify_session_id(const message_t& msg_, socket_ptr_t sock_) { string ip = socket_op_t::getpeername(sock_->socket()); LOGTRACE((FFGATE, "ffgate_t::verify_session_id session_key[%s], ip[%s]", msg_.get_body(), ip)); if (ip.empty()) { sock_->close(); return -1; } session_data_t* session_data = new session_data_t(); sock_->set_data(session_data); //! 还未通过验证 m_wait_verify_set.insert(sock_); session_verify_t::in_t msg; msg.session_key = msg_.get_body(); msg.online_time = session_data->online_time; msg.gate_name = m_gate_name; msg.ip = ip; m_ffrpc->call(DEFAULT_LOGIC_SERVICE, msg, ffrpc_ops_t::gen_callback(&ffgate_t::verify_session_callback, this, sock_)); LOGTRACE((FFGATE, "ffgate_t::verify_session_id end ok")); return 0; }
//! 验证sessionid int ffgate_t::verify_session_id(const message_t& msg_, socket_ptr_t sock_) { string ip = socket_op_t::getpeername(sock_->socket()); LOGTRACE((FFGATE, "ffgate_t::verify_session_id session_key len=%u, ip[%s]", msg_.get_body().size(), ip)); if (ip.empty()) { sock_->close(); return -1; } session_data_t* session_data = new session_data_t(this->alloc_id()); sock_->set_data(session_data); m_wait_verify_set[session_data->socket_id].sock = sock_; session_first_entere_t::in_t msg; msg.cmd = msg_.get_cmd(); msg.socket_id = session_data->socket_id; msg.msg_body = msg_.get_body(); msg.gate_name = m_gate_name; msg.ip = ip; m_ffrpc->call(DEFAULT_LOGIC_SERVICE, msg, ffrpc_ops_t::gen_callback(&ffgate_t::verify_session_callback, this, session_data->socket_id)); LOGTRACE((FFGATE, "ffgate_t::verify_session_id end ok")); return 0; }
//! 处理消息 int ffgate_t::handle_msg(const message_t& msg_, socket_ptr_t sock_) { session_data_t* session_data = sock_->get_data<session_data_t>(); if (NULL == session_data)//! 还未验证sessionid { return verify_session_id(msg_, sock_); } else if (false == session_data->is_valid()) { //! sessionid再未验证之前,client是不能发送消息的 route_logic_msg_t::in_t msg; msg.session_id = session_data->id(); msg.cmd = msg_.get_cmd(); msg.body = msg_.get_body(); m_wait_verify_set[session_data->socket_id].request_queue.push(msg); return 0; } else { return route_logic_msg(msg_, sock_); } return 0; }
void actor_t::on_message(int fd, const message_t& message) { auto it = m_channels.find(fd); if(it == m_channels.end()) { return; } m_dispatch->invoke(message, std::make_shared<upstream_t>( it->second, message.band() )); }
void session_t::invoke(const message_t& message) { channel_map_t::const_iterator lb, ub; channel_map_t::key_type index = message.band(); std::shared_ptr<channel_t> channel; { auto locked = channels.synchronize(); std::tie(lb, ub) = locked->equal_range(index); if(lb == ub) { if(!prototype || index <= max_channel) { return; } // NOTE: Checking whether channel number is always higher than the previous channel number // is similar to an infinite TIME_WAIT timeout for TCP sockets. It might be not the best // aproach, but since we have 2^64 possible channels, unlike 2^16 ports for sockets, it is // fit to avoid stray messages. max_channel = index; std::tie(lb, std::ignore) = locked->insert({index, std::make_shared<channel_t>( prototype, std::make_shared<basic_upstream_t>(shared_from_this(), index) )}); } // NOTE: The virtual channel pointer is copied here so that if the slot decides to close the // virtual channel, it won't destroy it inside the channel_t::invoke(). Instead, it will be // destroyed when this function scope is exited, liberating us from thinking of some voodoo // magic to handle it. channel = lb->second; } channel->invoke(message); }
void session_t::invoke(const message_t& message) { channel_map_t::const_iterator lb, ub; channel_map_t::key_type index = message.band(); std::shared_ptr<channel_t> channel; { auto locked = channels.synchronize(); std::tie(lb, ub) = locked->equal_range(index); if(lb == ub) { if(!prototype || index <= max_channel) { return; } max_channel = index; auto upstream = std::make_shared<upstream_t>(shared_from_this(), index); upstream->auto_revoke(); std::tie(lb, std::ignore) = locked->insert({ index, std::make_shared<channel_t>(prototype, upstream) }); } // NOTE: The virtual channel pointer is copied here so that if the slot decides to close the // virtual channel, it won't destroy it inside the channel_t::invoke(). Instead, it will be // destroyed when this function scope is exited, liberating us from thinking of some voodoo // magic to handle it. channel = lb->second; } channel->invoke(message); }
//! Convert string to message data. inline void str_to_msg(const std::string& str, message_t& msg) { msg.rebuild(str.size()); memcpy(msg.data(), str.data(), str.size()); }
//! Convert message data to string. inline std::string msg_to_str(message_t& msg) { return std::string(static_cast<char*>(msg.data()), msg.size()); }
int ffrpc_t::handle_msg(const message_t& msg_, socket_ptr_t sock_) { msg_tool_t msg_tool; try { msg_tool.decode(msg_.get_body()); logdebug((MSG_BUS, "ffrpc_t::handle_msg: cmd[%u], msgid[%u], msg_name<%s>, sgid[%u], sig[%u]", msg_.get_cmd(), msg_tool.get_msg_id(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); lock_guard_t lock(m_mutex); if (msg_tool.get_group_id() == 0 && msg_tool.get_service_id() == 0) { switch (msg_tool.get_msg_id()) { //! 增加新的服务组名称 case rpc_msg_cmd_e::PUSH_ADD_SERVICE_GROUP: { push_add_service_group_t::in_t in; in.decode(msg_.get_body()); if (m_service_map.find(in.sgid) == m_service_map.end()) { rpc_service_group_t* rsg = new rpc_service_group_t(in.name, in.sgid); m_service_map[in.sgid] = rsg; } loginfo((MSG_BUS, "ffrpc_t::handle_msg: rpc_service_g_t this<%p> sgid [%u], name<%s>", this, in.sgid, in.name.c_str())); }break; //! 增加新的服务实例 case rpc_msg_cmd_e::PUSH_ADD_SERVICE: { push_add_service_t::in_t in; in.decode(msg_.get_body()); rpc_service_group_t* rsg = get_service_group(in.sgid); if (rsg && NULL == rsg->get_service(in.sid)) { rsg->add_service(in.sid, new rpc_service_t(this, in.sgid, in.sid)); } else { logerror((MSG_BUS, "ffrpc_t::handle_msg: rpc_service_t none this<%p>, sgid cmd[%u], msg_name<%s>, sgid[%u], sig[%u] size[%u]", this, msg_.get_cmd(), msg_tool.get_name().c_str(), in.sgid, in.sid, m_service_map.size())); } }break; //! 增加新的消息名称和id的映射 case rpc_msg_cmd_e::PUSH_ADD_MSG: { push_add_msg_t::in_t in; in.decode(msg_.get_body()); singleton_t<msg_name_store_t>::instance().add_msg(in.name, in.msg_id); }break; //! 调用open接口时,broker返回此消息,同步服务接口数据 case rpc_msg_cmd_e::PUSH_INIT_DATA: { push_init_data_t::in_t in; in.decode(msg_.get_body()); process_sync_data(in); loginfo((MSG_BUS, "ffrpc_t::handle_msg: SYNC_ALL_SERVICE_RET this<%p>", this)); }break; default: { rpc_service_t::callback_guard_t cb(m_broker_service->del_callback(msg_tool.get_uuid())); lock.unlock(); cb.exe(msg_.get_body()); }break; } return 0; } rpc_service_group_t* rsg = get_service_group(msg_tool.get_group_id()); if (NULL == rsg) { logerror((MSG_BUS, "ffrpc_t::handle_msg: none sgid cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); return -1; } rpc_service_t* rs = rsg->get_service(msg_tool.get_service_id()); if (NULL == rs) { logerror((MSG_BUS, "ffrpc_t::handle_msg: none sid cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); return -1; } if (rpc_msg_cmd_e::CALL_INTERFACE == msg_.get_cmd()) { msg_process_func_i* func = rs->get_interface_func(msg_tool.get_msg_id()); lock.unlock(); rs->call_interface(func, msg_.get_body(), sock_); } else if (rpc_msg_cmd_e::INTREFACE_CALLBACK == msg_.get_cmd()) { rpc_service_t::callback_guard_t cb(rs->del_callback(msg_tool.get_uuid())); lock.unlock(); cb.exe(msg_.get_body()); } else { logerror((MSG_BUS, "ffrpc_t::handle_msg: none cmd==>cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); return -1; } } catch (exception& e_) { logerror((MSG_BUS, "ffrpc_t::handle_msg: exception<%s> cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", e_.what(), msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); return -1; } return 0; }
int broker_service_t::handle_msg(const message_t& msg_, socket_ptr_t sock_) { lock_guard_t lock(m_mutex); msg_tool_t msg_tool; try{ msg_tool.decode(msg_.get_body()); }catch(exception& e_) { logerror((BROKER, "broker_service_t::handle_msg except<%s>", e_.what())); return -1; } logtrace((BROKER, "broker_service_t::handle_msg begin... cmd[%u], name[%s], sgid[%u], sid[%u], msgid[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id(), msg_tool.get_msg_id())); if (msg_tool.get_group_id() == 0 && msg_tool.get_service_id() == 0) { if (msg_tool.get_msg_id() == rpc_msg_cmd_e::CREATE_SERVICE_GROUP) { create_service_group_t::in_t in; in.decode(msg_.get_body()); rpc_callcack_t<create_service_group_t::out_t> rcb; rcb.init_data(rpc_msg_cmd_e::INTREFACE_CALLBACK, 0, 0, in.get_uuid()); rcb.set_socket(sock_); create_service_group(in, rcb); } else if (msg_tool.get_msg_id() == rpc_msg_cmd_e::CREATE_SERVICE) { create_service_t::in_t in; in.decode(msg_.get_body()); rpc_callcack_t<create_service_t::out_t> rcb; rcb.init_data(rpc_msg_cmd_e::INTREFACE_CALLBACK, 0, 0, in.get_uuid()); rcb.set_socket(sock_); create_service(in, rcb); } else if (msg_tool.get_msg_id() == rpc_msg_cmd_e::REG_INTERFACE) { reg_interface_t::in_t in; in.decode(msg_.get_body()); rpc_callcack_t<reg_interface_t::out_t> rcb; rcb.init_data(rpc_msg_cmd_e::INTREFACE_CALLBACK, 0, 0, in.get_uuid()); rcb.set_socket(sock_); reg_interface(in, rcb); } else if (msg_tool.get_msg_id() == rpc_msg_cmd_e::SYNC_ALL_SERVICE) { loginfo((BROKER, "broker_service_t::handle_msg begin... cmd[%u], name[%s], sgid[%u], sid[%u], msgid[%u] sock[%p]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id(), msg_tool.get_msg_id(), sock_)); sync_all_service_t::in_t in; in.decode(msg_.get_body()); rpc_callcack_t<sync_all_service_t::out_t> rcb; rcb.init_data(rpc_msg_cmd_e::INTREFACE_CALLBACK, 0, 0, in.get_uuid()); rcb.set_socket(sock_); sync_all_service(in, rcb); m_all_sockets.insert(sock_); } else if (msg_tool.get_msg_id() == rpc_msg_cmd_e::PUSH_INIT_DATA) { push_init_data_t::in_t in; in.decode(msg_.get_body()); process_sync_data(in); } else if (msg_tool.get_msg_id() == rpc_msg_cmd_e::REG_SLAVE_BROKER) { reg_slave_broker_t::in_t in; in.decode(msg_.get_body()); loginfo((BROKER, "broker_service_t::handle_msg REG_SLAVE_BROKER node_id[%u]", in.node_id)); service_obj_map_t::iterator it = m_service_obj_mgr.begin(); for (; it != m_service_obj_mgr.end(); ++it) { map<uint16_t, service_obj_t>::iterator it2 = it->second.service_objs.begin(); for (; it2 != it->second.service_objs.end(); ++it2) { if (it2->second.node_id == in.node_id) { it2->second.socket_ptr = sock_; } } } sock_->set_data(new socket_session_info_t(in.node_id)); m_all_sockets.insert(sock_); } else if (msg_tool.get_msg_id() == rpc_msg_cmd_e::PUSH_ADD_SERVICE_GROUP) { push_add_service_group_t::in_t in; in.decode(msg_.get_body()); service_obj_mgr_t obj_mgr; obj_mgr.id = in.sgid; obj_mgr.name = in.name; m_service_obj_mgr.insert(make_pair(obj_mgr.id, obj_mgr)); } else if (msg_tool.get_msg_id() == rpc_msg_cmd_e::PUSH_ADD_SERVICE) { push_add_service_t::in_t in; in.decode(msg_.get_body()); loginfo((BROKER, "broker_service_t::handle_msg PUSH_ADD_SERVICE node_id[%u]", in.node_id)); map<uint16_t, service_obj_mgr_t>::iterator it = m_service_obj_mgr.find(in.sgid); if (it == m_service_obj_mgr.end()) { logerror((BROKER, "broker_service_t::handle_msg failed... sgid<%u> not exist", in.sgid)); } else { service_obj_t obj; obj.name = it->second.name; obj.group_id = in.sgid; obj.id = in.sid; obj.socket_ptr = find_socket_by_node(in.node_id); obj.node_id = in.node_id; it->second.service_objs[in.sid] = obj; } } else if (msg_tool.get_msg_id() == rpc_msg_cmd_e::PUSH_ADD_MSG) { push_add_msg_t::in_t in; in.decode(msg_.get_body()); singleton_t<msg_name_store_t>::instance().add_msg(in.name, in.msg_id); } } else { service_obj_map_t::iterator obj_mgr_it = m_service_obj_mgr.find(msg_tool.get_group_id()); if (obj_mgr_it == m_service_obj_mgr.end()) { logerror((BROKER, "broker_service_t::handle_msg sgid not found cmd[%u], name[%s], sgid[%u], sid[%u], msgid[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id(), msg_tool.get_msg_id())); return -1; } map<uint16_t, service_obj_t>::iterator sobj_it = obj_mgr_it->second.service_objs.find(msg_tool.get_service_id()); if (sobj_it == obj_mgr_it->second.service_objs.end()) { logerror((BROKER, "broker_service_t::handle_msg sid not found cmd[%u], name[%s], sgid[%u], sid[%u], msgid[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id(), msg_tool.get_msg_id())); return -1; } switch (msg_.get_cmd()) { case rpc_msg_cmd_e::CALL_INTERFACE: { sobj_it->second.async_call(msg_tool, msg_.get_body(), sock_); }break; case rpc_msg_cmd_e::INTREFACE_CALLBACK: { sobj_it->second.interface_callback(msg_tool, msg_.get_body()); }break; } } return 0; }
int msg_bus_t::handle_msg(const message_t& msg_, socket_ptr_t sock_) { lock_guard_t lock(m_mutex); msg_tool_t msg_tool; try { msg_tool.decode(msg_.get_body()); logdebug((MSG_BUS, "msg_bus_t::handle_msg: cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); if (msg_tool.get_group_id() == 0 && msg_tool.get_service_id() == 0) { switch (msg_tool.get_msg_id()) { case rpc_msg_cmd_e::PUSH_ADD_SERVICE_GROUP: { push_add_service_group_t::in_t in; in.decode(msg_.get_body()); rpc_service_group_t* rsg = new rpc_service_group_t(this, in.name, in.sgid); m_service_map[rsg->get_id()] = rsg; }break; case rpc_msg_cmd_e::PUSH_ADD_SERVICE: { push_add_service_t::in_t in; in.decode(msg_.get_body()); rpc_service_group_t* rsg = get_service_group(in.sgid); rsg->add_service(in.sid, new rpc_service_t(this, in.sgid, in.sid)); }break; case rpc_msg_cmd_e::PUSH_ADD_MSG: { push_add_msg_t::in_t in; in.decode(msg_.get_body()); singleton_t<msg_name_store_t>::instance().add_msg(in.name, in.msg_id); }break; default: { m_broker_service->interface_callback(msg_tool.get_uuid(), msg_.get_body()); }break; } return 0; } rpc_service_group_t* rsg = get_service_group(msg_tool.get_group_id()); if (NULL == rsg) { logerror((MSG_BUS, "msg_bus_t::handle_msg: none sgid cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); return -1; } rpc_service_t* rs = rsg->get_service(msg_tool.get_service_id()); if (NULL == rs) { logerror((MSG_BUS, "msg_bus_t::handle_msg: none sid cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); return -1; } if (rpc_msg_cmd_e::CALL_INTERFACE == msg_.get_cmd()) { rs->call_interface(msg_tool.get_msg_id(), msg_.get_body(), sock_); } else if (rpc_msg_cmd_e::INTREFACE_CALLBACK == msg_.get_cmd()) { rs->interface_callback(msg_tool.get_uuid(), msg_.get_body()); } else { logerror((MSG_BUS, "msg_bus_t::handle_msg: none cmd==>cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); return -1; } } catch (exception& e_) { logerror((MSG_BUS, "msg_bus_t::handle_msg: exception<%s> cmd[%u], msg_name<%s>, sgid[%u], sig[%u]", e_.what(), msg_.get_cmd(), msg_tool.get_name().c_str(), msg_tool.get_group_id(), msg_tool.get_service_id())); return -1; } return 0; }