/** * Process a slave replication registration message. * * We store the various bits of information the slave gives us and generate * a reply message. * * @param router The router instance * @param slave The slave server * @param queue The BINLOG_DUMP packet * @return Non-zero if data was sent */ static int blr_slave_register(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { GWBUF *resp; uint8_t *ptr; int len, slen; ptr = GWBUF_DATA(queue); len = extract_field(ptr, 24); ptr += 4; // Skip length and sequence number if (*ptr++ != COM_REGISTER_SLAVE) return 0; slave->serverid = extract_field(ptr, 32); ptr += 4; slen = *ptr++; if (slen != 0) { slave->hostname = strndup((char *)ptr, slen); ptr += slen; } else slave->hostname = NULL; slen = *ptr++; if (slen != 0) { ptr += slen; slave->user = strndup((char *)ptr, slen); } else slave->user = NULL; slen = *ptr++; if (slen != 0) { slave->passwd = strndup((char *)ptr, slen); ptr += slen; } else slave->passwd = NULL; slave->port = extract_field(ptr, 16); ptr += 2; slave->rank = extract_field(ptr, 32); /* * Now construct a response */ if ((resp = gwbuf_alloc(11)) == NULL) return 0; ptr = GWBUF_DATA(resp); encode_value(ptr, 7, 24); // Payload length ptr += 3; *ptr++ = 1; // Sequence number encode_value(ptr, 0, 24); ptr += 3; encode_value(ptr, slave->serverid, 32); slave->state = BLRS_REGISTERED; return slave->dcb->func.write(slave->dcb, resp); }
unsigned int NetGameNetworkData::encode_value(unsigned char *d, const NetGameEventValue &value) { switch (value.get_type()) { case NetGameEventValue::null: *d = 1; return 1; case NetGameEventValue::uinteger: *d = 2; *reinterpret_cast<unsigned int*>(d + 1) = value.to_uinteger(); return 5; case NetGameEventValue::integer: *d = 3; *reinterpret_cast<int*>(d + 1) = value.to_integer(); return 5; case NetGameEventValue::number: *d = 4; *reinterpret_cast<float*>(d + 1) = value.to_number(); return 5; case NetGameEventValue::boolean: *d = value.to_boolean() ? 6 : 5; return 1; case NetGameEventValue::string: { std::string s = value.to_string(); *d = 7; *reinterpret_cast<unsigned short*>(d + 1) = s.length(); memcpy(d + 3, s.data(), s.length()); return 3 + s.length(); } case NetGameEventValue::complex: { d[0] = 8; unsigned l = 1; for (unsigned int i = 0; i < value.get_member_count(); i++) l += encode_value(d + l, value.get_member(i)); d[l] = 0; l++; return l; } case NetGameEventValue::ucharacter: *d = 9; *reinterpret_cast<unsigned char*>(d + 1) = value.to_ucharacter(); return 2; case NetGameEventValue::character: *d = 10; *reinterpret_cast<char*>(d + 1) = value.to_character(); return 2; case NetGameEventValue::binary: { DataBuffer s = value.to_binary(); *d = 11; *reinterpret_cast<unsigned short*>(d + 1) = s.get_size(); memcpy(d + 3, s.get_data(), s.get_size()); return 3 + s.get_size(); } default: throw Exception("Unknown game event value type"); } }
DataBuffer NetGameNetworkData::encode_event(const NetGameEvent &e) { unsigned int length = 3 + e.get_name().length(); for (unsigned int i = 0; i < e.get_argument_count(); i++) length += get_encoded_length(e.get_argument(i)); DataBuffer data(length + 2); *data.get_data<unsigned short>() = length; unsigned char *d = data.get_data<unsigned char>() + 2; // Write name (2 + name length) unsigned int name_length = e.get_name().length(); *reinterpret_cast<unsigned short*>(d) = name_length; d += 2; memcpy(d, e.get_name().data(), name_length); d += name_length; for (unsigned int i = 0; i < e.get_argument_count(); i++) d += encode_value(d, e.get_argument(i)); // Write end marker *d = 0; return data; }
/** * Send a response to a "SELECT UNIX_TIMESTAMP()" request. This differs from the other * requests since we do not save a copy of the original interaction with the master * and simply replay it. We want to always send the current time. We have stored a typcial * response, which gives us the schema information normally returned. This is sent to the * client and then we add a dynamic part that will insert the current timestamp data. * Finally we send a preprepaed EOF packet to end the response stream. * * @param router The binlog router instance * @param slave The slave server to which we are sending the response * @return Non-zero if data was sent */ static int blr_slave_send_timestamp(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { GWBUF *pkt; char timestamp[20]; uint8_t *ptr; int len, ts_len; sprintf(timestamp, "%ld", time(0)); ts_len = strlen(timestamp); len = sizeof(timestamp_def) + sizeof(timestamp_eof) + 5 + ts_len; if ((pkt = gwbuf_alloc(len)) == NULL) return 0; ptr = GWBUF_DATA(pkt); memcpy(ptr, timestamp_def, sizeof(timestamp_def)); // Fixed preamble ptr += sizeof(timestamp_def); encode_value(ptr, ts_len + 1, 24); // Add length of data packet ptr += 3; *ptr++ = 0x04; // Sequence number in response *ptr++ = ts_len; // Length of result string strncpy((char *)ptr, timestamp, ts_len); // Result string ptr += ts_len; memcpy(ptr, timestamp_eof, sizeof(timestamp_eof)); // EOF packet to terminate result return slave->dcb->func.write(slave->dcb, pkt); }
string t_header::encode_env(void) const { string s("SIP_"); s += toupper(replace_char(header_name, '-', '_')); s += '='; s += encode_value(); return s; }
/** * Send a "fake" format description event to the newly connected slave * * @param router The router instance * @param slave The slave to send the event to */ static void blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { BLFILE *file; REP_HEADER hdr; GWBUF *record, *head; uint8_t *ptr; uint32_t chksum; if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) return; if ((record = blr_read_binlog(router, file, 4, &hdr)) == NULL) { blr_close_binlog(router, file); return; } blr_close_binlog(router, file); head = gwbuf_alloc(5); ptr = GWBUF_DATA(head); encode_value(ptr, hdr.event_size + 1, 24); // Payload length ptr += 3; *ptr++ = slave->seqno++; *ptr++ = 0; // OK head = gwbuf_append(head, record); ptr = GWBUF_DATA(record); encode_value(ptr, time(0), 32); // Overwrite timestamp ptr += 13; encode_value(ptr, 0, 32); // Set next position to 0 /* * Since we have changed the timestamp we must recalculate the CRC * * Position ptr to the start of the event header, * calculate a new checksum * and write it into the header */ ptr = GWBUF_DATA(record) + hdr.event_size - 4; chksum = crc32(0L, NULL, 0); chksum = crc32(chksum, GWBUF_DATA(record), hdr.event_size - 4); encode_value(ptr, chksum, 32); slave->dcb->func.write(slave->dcb, head); }
/** * Populate a header structure for a replication message from a GWBUF structure. * * @param pkt The incoming packet in a GWBUF chain * @param hdr The packet header to populate * @return A pointer to the first byte following the event header */ uint8_t * blr_build_header(GWBUF *pkt, REP_HEADER *hdr) { uint8_t *ptr; ptr = GWBUF_DATA(pkt); encode_value(ptr, hdr->payload_len, 24); ptr += 3; *ptr++ = hdr->seqno; *ptr++ = hdr->ok; encode_value(ptr, hdr->timestamp, 32); ptr += 4; *ptr++ = hdr->event_type; encode_value(ptr, hdr->serverid, 32); ptr += 4; encode_value(ptr, hdr->event_size, 32); ptr += 4; encode_value(ptr, hdr->next_pos, 32); ptr += 4; encode_value(ptr, hdr->flags, 16); ptr += 2; return ptr; }
/** * Send the column definition packet in a response packet sequence. * * @param router The router * @param slave The slave connection * @param name Name of the column * @param type Column type * @param len Column length * @param seqno Packet sequence number * @return Non-zero on success */ static int blr_slave_send_columndef(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno) { GWBUF *pkt; uint8_t *ptr; if ((pkt = gwbuf_alloc(26 + strlen(name))) == NULL) return 0; ptr = GWBUF_DATA(pkt); encode_value(ptr, 22 + strlen(name), 24); // Add length of data packet ptr += 3; *ptr++ = seqno; // Sequence number in response *ptr++ = 3; // Catalog is always def *ptr++ = 'd'; *ptr++ = 'e'; *ptr++ = 'f'; *ptr++ = 0; // Schema name length *ptr++ = 0; // virtal table name length *ptr++ = 0; // Table name length *ptr++ = strlen(name); // Column name length; while (*name) *ptr++ = *name++; // Copy the column name *ptr++ = 0; // Orginal column name *ptr++ = 0x0c; // Length of next fields always 12 *ptr++ = 0x3f; // Character set *ptr++ = 0; encode_value(ptr, len, 32); // Add length of column ptr += 4; *ptr++ = type; *ptr++ = 0x81; // Two bytes of flags if (type == 0xfd) *ptr++ = 0x1f; else *ptr++ = 0x00; *ptr++= 0; *ptr++= 0; *ptr++= 0; return slave->dcb->func.write(slave->dcb, pkt); }
string t_header::encode(void) const { string s; if (!populated) return s; s = (t_parser::compact_headers && !compact_name.empty() ? compact_name : header_name); s += ": "; s += encode_value(); s += CRLF; return s; }
/** * Send the field count packet in a response packet sequence. * * @param router The router * @param slave The slave connection * @param count Number of columns in the result set * @return Non-zero on success */ static int blr_slave_send_fieldcount(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int count) { GWBUF *pkt; uint8_t *ptr; if ((pkt = gwbuf_alloc(5)) == NULL) return 0; ptr = GWBUF_DATA(pkt); encode_value(ptr, 1, 24); // Add length of data packet ptr += 3; *ptr++ = 0x01; // Sequence number in response *ptr++ = count; // Length of result string return slave->dcb->func.write(slave->dcb, pkt); }
static void cb_params_store_init_item(struct params_st *params, lcb_size_t idx, VALUE key_obj, VALUE value_obj, lcb_uint32_t flags, lcb_cas_t cas, lcb_time_t exptime) { key_obj = unify_key(params->bucket, key_obj, 1); value_obj = encode_value(value_obj, params->cmd.store.flags); if (value_obj == Qundef) { rb_raise(eValueFormatError, "unable to convert value for key '%s'", RSTRING_PTR(key_obj)); } params->cmd.store.items[idx].v.v0.datatype = params->cmd.store.datatype; params->cmd.store.items[idx].v.v0.operation = params->cmd.store.operation; params->cmd.store.items[idx].v.v0.key = RSTRING_PTR(key_obj); params->cmd.store.items[idx].v.v0.nkey = RSTRING_LEN(key_obj); params->cmd.store.items[idx].v.v0.bytes = RSTRING_PTR(value_obj); params->cmd.store.items[idx].v.v0.nbytes = RSTRING_LEN(value_obj); params->cmd.store.items[idx].v.v0.flags = flags; params->cmd.store.items[idx].v.v0.cas = cas; params->cmd.store.items[idx].v.v0.exptime = exptime; params->npayload += RSTRING_LEN(key_obj) + RSTRING_LEN(value_obj) + sizeof(flags) + sizeof(exptime); }
/** * Construct an error response * * @param router The router instance * @param slave The slave server instance * @param msg The error message to send */ static void blr_slave_send_error(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *msg) { GWBUF *pkt; unsigned char *data; int len; if ((pkt = gwbuf_alloc(strlen(msg) + 13)) == NULL) return; data = GWBUF_DATA(pkt); len = strlen(msg) + 1; encode_value(&data[0], len, 24); // Payload length data[3] = 0; // Sequence id // Payload data[4] = 0xff; // Error indicator data[5] = 0; // Error Code data[6] = 0; // Error Code strncpy((char *)&data[7], "#00000", 6); memcpy(&data[13], msg, strlen(msg)); // Error Message slave->dcb->func.write(slave->dcb, pkt); }
// Return the data for all the names[], start .. end etc. // as get_values() is supposed to return them. // // Returns raw values if interpol <= 0.0. // Returns 0 on error. xmlrpc_value *get_sheet_data(xmlrpc_env *env, int key, const stdVector<stdString> names, const epicsTime &start, const epicsTime &end, long count, ReaderFactory::How how, double delta) { try { #ifdef LOGFILE stdString txt; LOG_MSG("get_sheet_data\n"); LOG_MSG("Start : %s\n", epicsTimeTxt(start, txt)); LOG_MSG("End : %s\n", epicsTimeTxt(end, txt)); LOG_MSG("Method: %s\n", ReaderFactory::toString(how, delta)); #endif AutoPtr<Index> index(open_index(env, key)); if (env->fault_occurred) return 0; AutoPtr<SpreadsheetReader> sheet(new SpreadsheetReader(*index, how, delta)); AutoXmlRpcValue results(xmlrpc_build_value(env, "()")); if (env->fault_occurred) return 0; long name_count = names.size(); AutoArrayPtr<AutoXmlRpcValue> meta (new AutoXmlRpcValue[name_count]); AutoArrayPtr<AutoXmlRpcValue> values (new AutoXmlRpcValue[name_count]); AutoArrayPtr<xmlrpc_int32> xml_type (new xmlrpc_int32[name_count]); AutoArrayPtr<xmlrpc_int32> xml_count(new xmlrpc_int32[name_count]); AutoArrayPtr<size_t> ch_vals (new size_t[name_count]); bool ok = sheet->find(names, &start); long i; // Per-channel meta-info. for (i=0; i<name_count; ++i) { #ifdef LOGFILE LOG_MSG("Handling '%s'\n", names[i].c_str()); #endif ch_vals[i] = 0; values[i] = xmlrpc_build_value(env, "()"); if (env->fault_occurred) return 0; if (sheet->found(i)) { // Fix meta/type/count based on first value meta[i] = encode_ctrl_info(env, &sheet->getInfo(i)); dbr_type_to_xml_type(sheet->getType(i), sheet->getCount(i), xml_type[i], xml_count[i]); #if 0 LOG_MSG("Ch %lu: type, count = %d, %d\n", i, (int)xml_type[i], (int)xml_count[i]); #endif } else { // Channel exists, but has no data meta[i] = encode_ctrl_info(env, 0); xml_type[i] = XML_ENUM; xml_count[i] = 1; } } // Collect values long num_vals = 0; while (ok && num_vals < count && sheet->getTime() < end) { for (i=0; i<name_count; ++i) { if (sheet->get(i)) { ++ch_vals[i]; encode_value(env, sheet->getType(i), sheet->getCount(i), sheet->getTime(), sheet->get(i), xml_type[i], xml_count[i], values[i]); } else { // Encode as no value, but one of them ;-) // to avoid confusion w/ viewers that like to see some data in any case. encode_value(env, 0, 1, sheet->getTime(), 0, xml_type[i], xml_count[i], values[i]); } } ++num_vals; ok = sheet->next(); } // Assemble result = { name, meta, type, count, values } for (i=0; i<name_count; ++i) { AutoXmlRpcValue result( xmlrpc_build_value(env, "{s:s,s:V,s:i,s:i,s:V}", "name", names[i].c_str(), "meta", meta[i].get(), "type", xml_type[i], "count", xml_count[i], "values", values[i].get())); // Add to result array xmlrpc_array_append_item(env, results, result); #ifdef LOGFILE LOG_MSG("Ch %lu: %zu values\n", i, ch_vals[i]); #endif } #ifdef LOGFILE LOG_MSG("%ld values total\n", num_vals); #endif return results.release(); } catch (GenericException &e) { #ifdef LOGFILE LOG_MSG("Error:\n%s\n", e.what()); #endif xmlrpc_env_set_fault_formatted(env, ARCH_DAT_DATA_ERROR, "%s", e.what()); } return 0; }
// Return the data for all the names[], start .. end etc. // as get_values() is supposed to return them. // Returns 0 on error. xmlrpc_value *get_channel_data(xmlrpc_env *env, int key, const stdVector<stdString> names, const epicsTime &start, const epicsTime &end, long count, ReaderFactory::How how, double delta) { AutoXmlRpcValue results; try { #ifdef LOGFILE stdString txt; LOG_MSG("get_channel_data\n"); LOG_MSG("Method: %s\n", ReaderFactory::toString(how, delta)); LOG_MSG("Start: %s\n", epicsTimeTxt(start, txt)); LOG_MSG("End : %s\n", epicsTimeTxt(end, txt)); #endif AutoPtr<Index> index(open_index(env, key)); if (env->fault_occurred) return 0; AutoPtr<DataReader> reader(ReaderFactory::create(*index, how, delta)); results = xmlrpc_build_value(env, "()"); if (env->fault_occurred) return 0; long i, name_count = names.size(); for (i=0; i<name_count; ++i) { #ifdef LOGFILE LOG_MSG("Handling '%s'\n", names[i].c_str()); #endif long num_vals = 0; AutoXmlRpcValue values(xmlrpc_build_value(env, "()")); if (env->fault_occurred) return 0; const RawValue::Data *data = reader->find(names[i], &start); AutoXmlRpcValue meta; xmlrpc_int32 xml_type, xml_count; if (data == 0) { // No exception from file error etc., just no data. meta = encode_ctrl_info(env, 0); xml_type = XML_ENUM; xml_count = 1; } else { // Fix meta/type/count based on first value meta = encode_ctrl_info(env, &reader->getInfo()); dbr_type_to_xml_type(reader->getType(), reader->getCount(), xml_type, xml_count); while (num_vals < count && data && RawValue::getTime(data) < end) { encode_value(env, reader->getType(), reader->getCount(), RawValue::getTime(data), data, xml_type, xml_count, values); ++num_vals; data = reader->next(); } } // Assemble result = { name, meta, type, count, values } AutoXmlRpcValue result(xmlrpc_build_value( env, "{s:s,s:V,s:i,s:i,s:V}", "name", names[i].c_str(), "meta", (xmlrpc_value *)meta, "type", xml_type, "count", xml_count, "values", (xmlrpc_value *)values)); // Add to result array xmlrpc_array_append_item(env, results, result); #ifdef LOGFILE LOG_MSG("%ld values\n", num_vals); #endif } // for ( .. name .. ) } catch (GenericException &e) { #ifdef LOGFILE LOG_MSG("Error:\n%s\n", e.what()); #endif xmlrpc_env_set_fault_formatted(env, ARCH_DAT_DATA_ERROR, "%s", e.what()); return 0; } return results.release(); }
static int load_registration_id(PROV_AUTH_INFO* handle) { int result; if (handle->sec_type == PROV_AUTH_TYPE_TPM) { SHA256Context sha_ctx; uint8_t msg_digest[SHA256HashSize]; unsigned char* endorsement_key; size_t ek_len; if (handle->hsm_client_get_endorsement_key(handle->hsm_client_handle, &endorsement_key, &ek_len) != 0) { LogError("Failed getting device reg id"); result = MU_FAILURE; } else { if (SHA256Reset(&sha_ctx) != 0) { LogError("Failed sha256 reset"); result = MU_FAILURE; } else if (SHA256Input(&sha_ctx, endorsement_key, (unsigned int)ek_len) != 0) { LogError("Failed SHA256Input"); result = MU_FAILURE; } else if (SHA256Result(&sha_ctx, msg_digest) != 0) { LogError("Failed SHA256Result"); result = MU_FAILURE; } else { handle->registration_id = encode_value(msg_digest, SHA256HashSize); if (handle->registration_id == NULL) { LogError("Failed allocating registration Id"); result = MU_FAILURE; } else { result = 0; } } free(endorsement_key); } } else { handle->registration_id = handle->hsm_client_get_common_name(handle->hsm_client_handle); if (handle->registration_id == NULL) { LogError("Failed getting common name from certificate"); result = MU_FAILURE; } else { result = 0; } } return result; }
/** * Generate an internal rotate event that we can use to cause the slave to move beyond * a binlog file that is misisng the rotate eent at the end. * * @param router The router instance * @param slave The slave to rotate * @return Non-zero if the rotate took place */ static int blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { char *sptr; int filenum; GWBUF *resp; uint8_t *ptr; int len, binlognamelen; REP_HEADER hdr; uint32_t chksum; if ((sptr = strrchr(slave->binlogfile, '.')) == NULL) return 0; blr_close_binlog(router, slave->file); filenum = atoi(sptr + 1); sprintf(slave->binlogfile, BINLOG_NAMEFMT, router->fileroot, filenum + 1); slave->binlog_pos = 4; if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) return 0; binlognamelen = strlen(slave->binlogfile); if (slave->nocrc) len = 19 + 8 + binlognamelen; else len = 19 + 8 + 4 + binlognamelen; // Build a fake rotate event resp = gwbuf_alloc(len + 5); hdr.payload_len = len + 1; hdr.seqno = slave->seqno++; hdr.ok = 0; hdr.timestamp = 0L; hdr.event_type = ROTATE_EVENT; hdr.serverid = router->masterid; hdr.event_size = len; hdr.next_pos = 0; hdr.flags = 0x20; ptr = blr_build_header(resp, &hdr); encode_value(ptr, slave->binlog_pos, 64); ptr += 8; memcpy(ptr, slave->binlogfile, binlognamelen); ptr += binlognamelen; if (!slave->nocrc) { /* * Now add the CRC to the fake binlog rotate event. * * The algorithm is first to compute the checksum of an empty buffer * and then the checksum of the event portion of the message, ie we do not * include the length, sequence number and ok byte that makes up the first * 5 bytes of the message. We also do not include the 4 byte checksum itself. */ chksum = crc32(0L, NULL, 0); chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); encode_value(ptr, chksum, 32); } slave->dcb->func.write(slave->dcb, resp); return 1; }
/** * We have a registered slave that is behind the current leading edge of the * binlog. We must replay the log entries to bring this node up to speed. * * There may be a large number of records to send to the slave, the process * is triggered by the slave COM_BINLOG_DUMP message and all the events must * be sent without receiving any new event. This measn there is no trigger into * MaxScale other than this initial message. However, if we simply send all the * events we end up with an extremely long write queue on the DCB and risk * running the server out of resources. * * The slave catchup routine will send a burst of replication events per single * call. The paramter "long" control the number of events in the burst. The * short burst is intended to be used when the master receive an event and * needs to put the slave into catchup mode. This prevents the slave taking * too much tiem away from the thread that is processing the master events. * * At the end of the burst a fake EPOLLOUT event is added to the poll event * queue. This ensures that the slave callback for processing DCB write drain * will be called and future catchup requests will be handled on another thread. * * @param router The binlog router * @param slave The slave that is behind * @param large Send a long or short burst of events * @return The number of bytes written */ int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large) { GWBUF *head, *record; REP_HEADER hdr; int written, rval = 1, burst; int rotating; unsigned long burst_size; uint8_t *ptr; if (large) burst = router->long_burst; else burst = router->short_burst; burst_size = router->burst_size; spinlock_acquire(&slave->catch_lock); if (slave->cstate & CS_BUSY) { spinlock_release(&slave->catch_lock); return 0; } slave->cstate |= CS_BUSY; spinlock_release(&slave->catch_lock); if (slave->file == NULL) { rotating = router->rotating; if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) { if (rotating) { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; slave->cstate &= ~CS_BUSY; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); return rval; } LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_slave_catchup failed to open binlog file %s", slave->binlogfile))); slave->cstate &= ~CS_BUSY; slave->state = BLRS_ERRORED; dcb_close(slave->dcb); return 0; } } slave->stats.n_bursts++; while (burst-- && burst_size > 0 && (record = blr_read_binlog(router, slave->file, slave->binlog_pos, &hdr)) != NULL) { head = gwbuf_alloc(5); ptr = GWBUF_DATA(head); encode_value(ptr, hdr.event_size + 1, 24); ptr += 3; *ptr++ = slave->seqno++; *ptr++ = 0; // OK head = gwbuf_append(head, record); if (hdr.event_type == ROTATE_EVENT) { unsigned long beat1 = hkheartbeat; blr_close_binlog(router, slave->file); if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_close_binlog took %d beats", hkheartbeat - beat1))); blr_slave_rotate(slave, GWBUF_DATA(record)); beat1 = hkheartbeat; if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) { if (rotating) { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; slave->cstate &= ~CS_BUSY; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); return rval; } LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_slave_catchup failed to open binlog file %s", slave->binlogfile))); slave->state = BLRS_ERRORED; dcb_close(slave->dcb); break; } if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_open_binlog took %d beats", hkheartbeat - beat1))); } slave->stats.n_bytes += gwbuf_length(head); written = slave->dcb->func.write(slave->dcb, head); if (written && hdr.event_type != ROTATE_EVENT) { slave->binlog_pos = hdr.next_pos; } rval = written; slave->stats.n_events++; burst_size -= hdr.event_size; } if (record == NULL) slave->stats.n_failed_read++; spinlock_acquire(&slave->catch_lock); slave->cstate &= ~CS_BUSY; spinlock_release(&slave->catch_lock); if (record) { slave->stats.n_flows++; spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); } else if (slave->binlog_pos == router->binlog_position && strcmp(slave->binlogfile, router->binlog_name) == 0) { int state_change = 0; spinlock_acquire(&router->binlog_lock); spinlock_acquire(&slave->catch_lock); /* * Now check again since we hold the router->binlog_lock * and slave->catch_lock. */ if (slave->binlog_pos != router->binlog_position || strcmp(slave->binlogfile, router->binlog_name) != 0) { slave->cstate &= ~CS_UPTODATE; slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); spinlock_release(&router->binlog_lock); poll_fake_write_event(slave->dcb); } else { if ((slave->cstate & CS_UPTODATE) == 0) { slave->stats.n_upd++; slave->cstate |= CS_UPTODATE; spinlock_release(&slave->catch_lock); spinlock_release(&router->binlog_lock); state_change = 1; } } if (state_change) { slave->stats.n_caughtup++; if (slave->stats.n_caughtup == 1) { LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "%s: Slave %s is up to date %s, %u.", router->service->name, slave->dcb->remote, slave->binlogfile, slave->binlog_pos))); } else if ((slave->stats.n_caughtup % 50) == 0) { LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "%s: Slave %s is up to date %s, %u.", router->service->name, slave->dcb->remote, slave->binlogfile, slave->binlog_pos))); } } } else { if (slave->binlog_pos >= blr_file_size(slave->file) && router->rotating == 0 && strcmp(router->binlog_name, slave->binlogfile) != 0 && blr_master_connected(router)) { /* We may have reached the end of file of a non-current * binlog file. * * Note if the master is rotating there is a window during * which the rotate event has been written to the old binlog * but the new binlog file has not yet been created. Therefore * we ignore these issues during the rotate processing. */ LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, "Slave reached end of file for binlong file %s at %u " "which is not the file currently being downloaded. " "Master binlog is %s, %lu.", slave->binlogfile, slave->binlog_pos, router->binlog_name, router->binlog_position))); if (blr_slave_fake_rotate(router, slave)) { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); } else { slave->state = BLRS_ERRORED; dcb_close(slave->dcb); } } else { spinlock_acquire(&slave->catch_lock); slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); } } return rval; }
void encode_key(Buffer& buf, const KeyObject& key) { uint32 header = (uint32) (key.db << 8) + key.type; BufferHelper::WriteFixUInt32(buf, header); BufferHelper::WriteVarSlice(buf, key.key); switch (key.type) { case HASH_FIELD: { const HashKeyObject& hk = (const HashKeyObject&) key; BufferHelper::WriteVarSlice(buf, hk.field); break; } case LIST_ELEMENT: { const ListKeyObject& lk = (const ListKeyObject&) key; BufferHelper::WriteFixFloat(buf, lk.score); break; } case SET_ELEMENT: { const SetKeyObject& sk = (const SetKeyObject&) key; encode_value(buf, sk.value); break; } case ZSET_ELEMENT: { const ZSetKeyObject& sk = (const ZSetKeyObject&) key; BufferHelper::WriteFixDouble(buf, sk.score); encode_value(buf, sk.value); break; } case ZSET_ELEMENT_SCORE: { const ZSetScoreKeyObject& zk = (const ZSetScoreKeyObject&) key; encode_value(buf, zk.value); break; } case TABLE_INDEX: { const TableIndexKeyObject& index = (const TableIndexKeyObject&) key; BufferHelper::WriteVarSlice(buf, index.colname); encode_value(buf, index.colvalue); BufferHelper::WriteVarUInt32(buf, index.index.size()); ValueArray::const_iterator it = index.index.begin(); while (it != index.index.end()) { encode_value(buf, *it); it++; } break; } case TABLE_COL: { const TableColKeyObject& col = (const TableColKeyObject&) key; BufferHelper::WriteVarSlice(buf, col.colname); BufferHelper::WriteVarUInt32(buf, col.index.size()); ValueArray::const_iterator it = col.index.begin(); while (it != col.index.end()) { encode_value(buf, *it); it++; } break; } case BITSET_ELEMENT: { const BitSetKeyObject& bk = (const BitSetKeyObject&) key; BufferHelper::WriteVarUInt64(buf, bk.index); break; } case KEY_EXPIRATION_ELEMENT: { const ExpireKeyObject& bk = (const ExpireKeyObject&) key; BufferHelper::WriteVarUInt64(buf, bk.expireat); break; } case LIST_META: case ZSET_META: case SET_META: case TABLE_META: case TABLE_SCHEMA: case BITSET_META: case KEY_EXPIRATION_MAPPING: case SCRIPT: default: { break; } } }
/** * Process a COM_BINLOG_DUMP message from the slave. This is the * final step in the process of registration. The new master, MaxScale * must send a response packet and generate a fake BINLOG_ROTATE event * with the binlog file requested by the slave. And then send a * FORMAT_DESCRIPTION_EVENT that has been saved from the real master. * * Once send MaxScale must continue to send binlog events to the slave. * * @param router The router instance * @param slave The slave server * @param queue The BINLOG_DUMP packet * @return The number of bytes written to the slave */ static int blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue) { GWBUF *resp; uint8_t *ptr; int len, flags, serverid, rval, binlognamelen; REP_HEADER hdr; uint32_t chksum; ptr = GWBUF_DATA(queue); len = extract_field(ptr, 24); binlognamelen = len - 11; ptr += 4; // Skip length and sequence number if (*ptr++ != COM_BINLOG_DUMP) { LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_slave_binlog_dump expected a COM_BINLOG_DUMP but received %d", *(ptr-1)))); return 0; } slave->binlog_pos = extract_field(ptr, 32); ptr += 4; flags = extract_field(ptr, 16); ptr += 2; serverid = extract_field(ptr, 32); ptr += 4; strncpy(slave->binlogfile, (char *)ptr, binlognamelen); slave->binlogfile[binlognamelen] = 0; slave->seqno = 1; if (slave->nocrc) len = 19 + 8 + binlognamelen; else len = 19 + 8 + 4 + binlognamelen; // Build a fake rotate event resp = gwbuf_alloc(len + 5); hdr.payload_len = len + 1; hdr.seqno = slave->seqno++; hdr.ok = 0; hdr.timestamp = 0L; hdr.event_type = ROTATE_EVENT; hdr.serverid = router->masterid; hdr.event_size = len; hdr.next_pos = 0; hdr.flags = 0x20; ptr = blr_build_header(resp, &hdr); encode_value(ptr, slave->binlog_pos, 64); ptr += 8; memcpy(ptr, slave->binlogfile, binlognamelen); ptr += binlognamelen; if (!slave->nocrc) { /* * Now add the CRC to the fake binlog rotate event. * * The algorithm is first to compute the checksum of an empty buffer * and then the checksum of the event portion of the message, ie we do not * include the length, sequence number and ok byte that makes up the first * 5 bytes of the message. We also do not include the 4 byte checksum itself. */ chksum = crc32(0L, NULL, 0); chksum = crc32(chksum, GWBUF_DATA(resp) + 5, hdr.event_size - 4); encode_value(ptr, chksum, 32); } rval = slave->dcb->func.write(slave->dcb, resp); /* Send the FORMAT_DESCRIPTION_EVENT */ if (slave->binlog_pos != 4) blr_slave_send_fde(router, slave); slave->dcb->low_water = router->low_water; slave->dcb->high_water = router->high_water; dcb_add_callback(slave->dcb, DCB_REASON_DRAINED, blr_slave_callback, slave); slave->state = BLRS_DUMPING; LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "%s: New slave %s, server id %d, requested binlog file %s from position %lu", router->service->name, slave->dcb->remote, slave->serverid, slave->binlogfile, slave->binlog_pos))); if (slave->binlog_pos != router->binlog_position || strcmp(slave->binlogfile, router->binlog_name) != 0) { spinlock_acquire(&slave->catch_lock); slave->cstate &= ~CS_UPTODATE; slave->cstate |= CS_EXPECTCB; spinlock_release(&slave->catch_lock); poll_fake_write_event(slave->dcb); } return rval; }