/** * write data to the socket * */ static network_socket_retval_t network_socket_write_writev(network_socket *con, int send_chunks) { /* send the whole queue */ GList *chunk; struct iovec *iov; gint chunk_id; gint chunk_count; gssize len; int os_errno; gint max_chunk_count; if (send_chunks == 0) return NETWORK_SOCKET_SUCCESS; chunk_count = send_chunks > 0 ? send_chunks : (gint)con->send_queue->chunks->length; if (chunk_count == 0) return NETWORK_SOCKET_SUCCESS; max_chunk_count = sysconf(_SC_IOV_MAX); if (max_chunk_count < 0) { /* option is unknown */ #if defined(UIO_MAXIOV) max_chunk_count = UIO_MAXIOV; /* as defined in POSIX */ #elif defined(IOV_MAX) max_chunk_count = IOV_MAX; /* on older Linux'es */ #else g_assert_not_reached(); /* make sure we provide a work-around in case sysconf() fails on us */ #endif } chunk_count = chunk_count > max_chunk_count ? max_chunk_count : chunk_count; g_assert_cmpint(chunk_count, >, 0); /* make sure it is never negative */ iov = g_new0(struct iovec, chunk_count); for (chunk = con->send_queue->chunks->head, chunk_id = 0; chunk && chunk_id < chunk_count; chunk_id++, chunk = chunk->next) { GString *s = chunk->data; if (chunk_id == 0) { g_assert(con->send_queue->offset < s->len); iov[chunk_id].iov_base = s->str + con->send_queue->offset; iov[chunk_id].iov_len = s->len - con->send_queue->offset; } else { iov[chunk_id].iov_base = s->str; iov[chunk_id].iov_len = s->len; } } g_debug("%s: network socket:%p, send (src:%s, dst:%s) fd:%d", G_STRLOC, con, con->src->name->str, con->dst->name->str, con->fd); len = writev(con->fd, iov, chunk_count); os_errno = errno; g_free(iov); if (-1 == len) { switch (os_errno) { case E_NET_WOULDBLOCK: case EAGAIN: return NETWORK_SOCKET_WAIT_FOR_EVENT; case EPIPE: case E_NET_CONNRESET: case E_NET_CONNABORTED: /** remote side closed the connection */ return NETWORK_SOCKET_ERROR; default: g_message("%s.%d: writev(%s, ...) failed: %s", __FILE__, __LINE__, con->dst->name->str, g_strerror(errno)); return NETWORK_SOCKET_ERROR; } } else if (len == 0) { return NETWORK_SOCKET_ERROR; } con->send_queue->offset += len; con->send_queue->len -= len; /* check all the chunks which we have sent out */ for (chunk = con->send_queue->chunks->head; chunk; ) { GString *s = chunk->data; if (con->send_queue->offset >= s->len) { con->send_queue->offset -= s->len; #ifdef NETWORK_DEBUG_TRACE_IO /* to trace the data we sent to the socket, enable this */ g_debug_hexdump(G_STRLOC, S(s)); #endif g_string_free(s, TRUE); g_queue_delete_link(con->send_queue->chunks, chunk); chunk = con->send_queue->chunks->head; } else { return NETWORK_SOCKET_WAIT_FOR_EVENT; } } return NETWORK_SOCKET_SUCCESS; }
/** * decode a packet into proto_field according to field->fielddef->type * * @param field field definition * @returns 0 on success, -1 on error */ int network_mysqld_proto_get_myisam_field(network_packet *packet, network_mysqld_myisam_field *field) { guint64 length; guint8 i8; guint16 i16; guint32 i32; guint64 i64; double d; network_mysqld_column *column = field->column; int err = 0; switch ((guchar)column->type) { case MYSQL_TYPE_DOUBLE: /* a DOUBLE is stored in IEEE notation by just copying all 8 bytes */ err = err || (column->max_length != 8); /* has to be 8 bytes */ err = err || (packet->offset + 8 > packet->data->len); if (!err) { memcpy(&d, packet->data->str + packet->offset, 8); err = err || network_mysqld_proto_skip(packet, 8); } if (!err) field->data.f = d; break; case MYSQL_TYPE_TIMESTAMP: /* int4store */ case MYSQL_TYPE_LONG: err = err || network_mysqld_proto_get_int32(packet, &i32); if (!err) field->data.i = i32; break; case MYSQL_TYPE_DATETIME: /* int8store */ case MYSQL_TYPE_LONGLONG: err = err || network_mysqld_proto_get_int64(packet, &i64); if (!err) field->data.i = i64; break; case MYSQL_TYPE_INT24: case MYSQL_TYPE_DATE: /* int3store, a newdate, old-data is 4 byte */ err = err || network_mysqld_proto_get_int24(packet, &i32); if (!err) field->data.i = i32; break; case MYSQL_TYPE_SHORT: err = err || network_mysqld_proto_get_int16(packet, &i16); if (!err) field->data.i = i16; break; case MYSQL_TYPE_TINY: err = err || network_mysqld_proto_get_int8(packet, &i8); if (!err) field->data.i = i8; break; case MYSQL_TYPE_ENUM: switch (column->max_length) { case 1: err = err || network_mysqld_proto_get_int8(packet, &i8); if (!err) field->data.i = i8; break; case 2: err = err || network_mysqld_proto_get_int16(packet, &i16); if (!err) field->data.i = i16; break; default: g_error("%s: enum-length = %lu", G_STRLOC, column->max_length); break; } break; case MYSQL_TYPE_BLOB: switch (column->max_length) { case 1: err = err || network_mysqld_proto_get_int8(packet, &i8); if (!err) length = i8; break; case 2: err = err || network_mysqld_proto_get_int16(packet, &i16); if (!err) length = i16; break; case 3: err = err || network_mysqld_proto_get_int24(packet, &i32); if (!err) length = i32; break; case 4: err = err || network_mysqld_proto_get_int32(packet, &i32); if (!err) length = i32; break; default: /* unknown blob-length */ g_debug_hexdump(G_STRLOC, S(packet->data)); g_error("%s: blob-length = %lu", G_STRLOC, column->max_length); break; } err = err || network_mysqld_proto_get_string_len(packet, &field->data.s, length); break; case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: if (column->max_length < 256) { err = err || network_mysqld_proto_get_int8(packet, &i8); err = err || network_mysqld_proto_get_string_len(packet, &field->data.s, i8); } else { err = err || network_mysqld_proto_get_int16(packet, &i16); err = err || network_mysqld_proto_get_string_len(packet, &field->data.s, i16); } break; case MYSQL_TYPE_NEWDECIMAL: { /* the decimal is binary encoded */ guchar digits_per_bytes[] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 4 }; /* how many bytes are needed to store x decimal digits */ guint i_digits = column->max_length - column->decimals; guint f_digits = column->decimals; guint decimal_full_blocks = i_digits / 9; /* 9 decimal digits in 4 bytes */ guint decimal_last_block_digits = i_digits % 9; /* how many digits are left ? */ guint scale_full_blocks = f_digits / 9; /* 9 decimal digits in 4 bytes */ guint scale_last_block_digits = f_digits % 9; /* how many digits are left ? */ guint size = 0; size += decimal_full_blocks * digits_per_bytes[9] + digits_per_bytes[decimal_last_block_digits]; size += scale_full_blocks * digits_per_bytes[9] + digits_per_bytes[scale_last_block_digits]; #if 0 g_debug_hexdump(G_STRLOC " (NEWDECIMAL)", packet->data->str, packet->data->len); #endif #if 0 g_critical("%s: don't know how to decode NEWDECIMAL(%lu, %u) at offset %u (%d)", G_STRLOC, column->max_length, column->decimals, packet->offset, size ); #endif err = err || network_mysqld_proto_skip(packet, size); break; } default: g_debug_hexdump(G_STRLOC, packet->data->str, packet->data->len); g_error("%s: unknown field-type to fetch: %d", G_STRLOC, column->type); break; } return err ? -1 : 0; }