/** * \brief This function destroys layer stored at verse server */ void vs_layer_destroy(struct VSNode *node, struct VSLayer *layer) { struct VSLayer *child_layer; struct VSLayerValue *item; struct VBucket *vbucket; /* Free values in all items */ vbucket = (struct VBucket*)layer->values.lb.first; while(vbucket != NULL) { item = (struct VSLayerValue*)vbucket->data; free(item->value); vbucket = vbucket->next; } /* Destroy hashed array with items */ v_hash_array_destroy(&layer->values); /* Set references to parent layer in all child layers to NULL */ child_layer = layer->child_layers.first; while(child_layer != NULL) { child_layer->parent = NULL; child_layer = child_layer->next; } layer->child_layers.first = NULL; layer->child_layers.last = NULL; /* If this layer has parent layer, then remove this layer from * parent linked list of child layers */ if(layer->parent != NULL) { v_list_rem_item(&layer->parent->child_layers, layer); } /* Free list of followers and subscribers */ v_list_free(&layer->layer_folls); v_list_free(&layer->layer_subs); v_print_log(VRS_PRINT_DEBUG_MSG, "Layer: %d destroyed\n", layer->id); /* Destroy this layer itself */ v_hash_array_remove_item(&node->layers, layer); free(layer); }
/** * \brief This function handle changing link between nodes */ int vs_handle_link_change(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *node_link) { struct VSUser *user = (struct VSUser*)vsession->user; struct VSNode *old_parent_node, *parent_node, *child_node; struct VSLink *link; struct VSNodeSubscriber *node_subscriber; struct VSEntityFollower *node_follower; uint32 parent_node_id = UINT32(node_link->data[0]); uint32 child_node_id = UINT32(node_link->data[UINT32_SIZE]); int i; /* Try to find child node */ if((child_node = vs_node_find(vs_ctx, child_node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node (id: %d) not found\n", __FUNCTION__, __LINE__, child_node_id); return 0; } /* Child node has to be created */ if(! (child_node->state == ENTITY_CREATED || child_node->state == ENTITY_CREATING)) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node id: %d is not in NODE_CREATED state: %d\n", __FUNCTION__, __LINE__, child_node->id, child_node->state); return 0; } /* Is user owner of child node or can user write to child node? */ if(vs_node_can_write(vsession, child_node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d user: %s can't write to child node: %d (owner: %s)\n", __FUNCTION__, __LINE__, user->username, child_node->id, child_node->owner->username); return 0; } /* Old link */ link = child_node->parent_link; /* Old parent node */ old_parent_node = link->parent; /* Is user owner of old parent node or can user write to old parent node? */ if(vs_node_can_write(vsession, old_parent_node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d user: %s can't write to old parent node: %d (owner: %s)\n", __FUNCTION__, __LINE__, user->username, old_parent_node->id, old_parent_node->owner->username); return 0; } /* Try to find new parent node */ if((parent_node = vs_node_find(vs_ctx, parent_node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node (id: %d) not found\n", __FUNCTION__, __LINE__, parent_node_id); return 0; } /* Parent node has to be created */ if(! (parent_node->state == ENTITY_CREATED || parent_node->state == ENTITY_CREATING)) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node (id: %d) is not in NODE_CREATED state: %d\n", __FUNCTION__, __LINE__, parent_node->id, parent_node->state); return 0; } /* Test if client doesn't want to recreate existing link */ if( parent_node == old_parent_node) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d link between nodes (parent_id: %d) (child_id: %d) already exists\n", __FUNCTION__, __LINE__, parent_node->id, child_node->id); return 0; } /* Is user owner of parent node or can user write to parent node? */ if(vs_node_can_write(vsession, parent_node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d user: %s can't write to parent node: %d (owner: %s)\n", __FUNCTION__, __LINE__, user->username, parent_node->id, parent_node->owner->username); return 0; } /* Test, if new link could be created */ if(vs_link_test_nodes(parent_node, child_node) != 1) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s():%d node: %d can't be child of node: %d\n", __FUNCTION__, __LINE__, child_node->id, parent_node->id); return 0; } /* Remove link from old parent node */ v_list_rem_item(&old_parent_node->children_links, link); /* Add link to new parent node */ v_list_add_tail(&parent_node->children_links, link); link->parent = parent_node; /* Update child node internal properties according new parent node */ vs_link_update_child(parent_node, child_node); /* Update version in child node, parent node and old parent node */ vs_node_inc_version(parent_node); vs_node_inc_version(child_node); vs_node_inc_version(old_parent_node); /* Subscribers of old and new parent node will receive information about * changing link between nodes. Prevent double sending command Node_Link, * when clients are subscribed to both nodes. */ /* Set temporary value in all sessions */ for(i=0; i<vs_ctx->max_sessions; i++) { vs_ctx->vsessions[i]->tmp = 0; } /* Send Node_Link command to subscribers of old parent node and set * session temporary value */ node_subscriber = old_parent_node->node_subs.first; while(node_subscriber != NULL) { if(vs_node_can_read(node_subscriber->session, old_parent_node) == 1) { node_subscriber->session->tmp = 1; vs_link_change_send(node_subscriber, link); } node_subscriber = node_subscriber->next; } /* When client is subscribed to the new parent node and aware of child * node, then send to the client only node_link */ node_follower = child_node->node_folls.first; while(node_follower != NULL) { if(node_follower->node_sub->session->tmp != 1) { vs_link_change_send(node_follower->node_sub, link); node_follower->node_sub->session->tmp = 1; } node_follower = node_follower->next; } /* Send Node_Create command to subscribers of new parent node, when * subscribers were not subscribed to child node */ node_subscriber = parent_node->node_subs.first; while(node_subscriber != NULL) { if(node_subscriber->session->tmp != 1) { if(vs_node_can_read(node_subscriber->session, parent_node) == 1) { vs_node_send_create(node_subscriber, child_node, NULL); } } node_subscriber = node_subscriber->next; } return 1; }
/* * \brief This function pop command from queue with specific priority. * * Returned command is allocated on the heap and have to be free from calling * function. When *count is not NULL, then this function will write number of * commands with same ID that could be compressed on this address. When *len * is not NULL, then this function will write length of compressed commands * at this address. When *len value is not 0, then this value represents maximal * size of commands that could be removed from this priority queue. * * \param[in] *out_queue The pointer at list of priority queues * \param[in] prio The value of priority * \param[out] *count The pointer at count of commands with same id that * could be compressed. * \param[out] *share The size of address that could be shared * \param[out] *len The length of compressed commands. * * \return This function returns pointer at command. This command is allocated * at the heap. */ struct Generic_Cmd *v_out_queue_pop(struct VOutQueue *out_queue, uint8 prio, uint16 *count, int8 *share, uint16 *len) { struct VPrioOutQueue *prio_queue = out_queue->queues[prio]; struct VOutQueueCommand *queue_cmd; struct Generic_Cmd *cmd=NULL; int i, can_pop_cmd = 1; /* Lock mutex */ pthread_mutex_lock(&out_queue->lock); /*printf("\tcount: %u, share: %u, len: %u\n", *count, *share, *len);*/ queue_cmd = prio_queue->cmds.first; if(queue_cmd != NULL ) { cmd = (struct Generic_Cmd *)queue_cmd->vbucket->data; assert(cmd != NULL); /* Return value of count and length of compressed commands */ if(queue_cmd->counter != NULL) { if(count != NULL) { *count = *queue_cmd->counter; } if(share != NULL) { *share = *queue_cmd->share; } if(len != NULL) { /* When *len value is not 0, then this value represents maximal * size of buffer that could be filled with this priority queue. This * function should return value, that is smaller or equal than this * maximum. */ if(*len != 0) { if(*len < *queue_cmd->len) { if (count != NULL) { *count = v_cmd_count(cmd, *len, *queue_cmd->share); /* FIXME: compute length and count correctly, when command is added to the queue */ *count = ((*count) > (*queue_cmd->counter)) ? *queue_cmd->counter : *count; /* Is enough space in buffer to unpack this command? */ if(*count == 0) { can_pop_cmd = 0; } *len = v_cmds_len(cmd, *count, *queue_cmd->share, 0); } else { can_pop_cmd = 0; } } else { *len = *queue_cmd->len; } } else { *len = *queue_cmd->len; } } } else { if(count != NULL) { *count = 0; } if(share != NULL) { *share = 0; } if(len != NULL) { if(v_cmd_size(cmd) > *len) { can_pop_cmd = 0; } *len = 0; } } /* Is it possible to pop command from the queue */ if(can_pop_cmd == 1) { /* Remove command from hashed linked list */ v_hash_array_remove_item(&out_queue->cmds[cmd->id]->cmds, (void*)cmd); /* Remove command from priority queue */ v_list_rem_item(&prio_queue->cmds, queue_cmd); /* Update total count and size of commands */ out_queue->count--; out_queue->size -= out_queue->cmds[cmd->id]->item_size; /* Update count and size of commands in subqueue with priority prio */ out_queue->queues[prio]->count--; out_queue->queues[prio]->size -= out_queue->cmds[cmd->id]->item_size; /* If needed, then update counter of commands with the same id in the queue */ if(queue_cmd->counter != NULL) { *queue_cmd->counter -= 1; /* Free values, when it's last command in the queue */ if(*queue_cmd->counter == 0) { free(queue_cmd->counter); queue_cmd->counter = NULL; free(queue_cmd->share); queue_cmd->share = NULL; free(queue_cmd->len); queue_cmd->len = NULL; } } /* When this priority queue is empty now, then update summary of * real priorities and it is possibly necessary to change maximal * or minimal priority queue*/ if(out_queue->queues[prio]->count == 0) { if(prio>=VRS_DEFAULT_PRIORITY) { out_queue->r_prio_sum_high -= out_queue->queues[prio]->r_prio; } else { out_queue->r_prio_sum_low -= out_queue->queues[prio]->r_prio; } if(out_queue->max_prio == prio && out_queue->min_prio != prio) { for(i=prio; i>=out_queue->min_prio; i--) { if(out_queue->queues[i]->count != 0) { out_queue->max_prio = i; break; } } } else if(out_queue->min_prio == prio && out_queue->max_prio != prio) { for(i=prio; i<=out_queue->max_prio; i++) { if(out_queue->queues[i]->count != 0) { out_queue->min_prio = i; break; } } } else if(out_queue->min_prio == prio && out_queue->max_prio == prio) { out_queue->min_prio = out_queue->max_prio = VRS_DEFAULT_PRIORITY; } } /* Free queue command */ free(queue_cmd); } else { cmd = NULL; } } pthread_mutex_unlock(&out_queue->lock); return cmd; }
/** * \brief This function adds command to the head or tail of the queue */ static int _v_out_queue_push(struct VOutQueue *out_queue, uint8 flag, uint8 prio, struct Generic_Cmd *cmd) { struct VOutQueueCommand *queue_cmd; struct VBucket *vbucket; int ret = 1; assert(cmd!=NULL); /* Try to find command with the same address, when duplicities are not * allowed in command queue */ if(out_queue->cmds[cmd->id]->flag & REMOVE_HASH_DUPS) { vbucket = v_hash_array_find_item(&out_queue->cmds[cmd->id]->cmds, (void*)cmd); if(vbucket != NULL) { /* Bucket has to include not NULL pointer */ assert(vbucket->ptr!=NULL); queue_cmd = (struct VOutQueueCommand*)vbucket->ptr; /* Remove old command from the queue if the priority is different * and add command to the end of new priority queue */ if(queue_cmd->prio != prio) { /* Remove old obsolete data */ v_hash_array_remove_item(&out_queue->cmds[cmd->id]->cmds, vbucket->data); /* Remove old command */ v_list_rem_item(&out_queue->queues[queue_cmd->prio]->cmds, queue_cmd); /* Update size and count in old priority queue */ out_queue->queues[queue_cmd->prio]->count--; out_queue->queues[queue_cmd->prio]->size -= out_queue->cmds[cmd->id]->item_size; /* If needed, then update counter of commands with the same id in the queue */ if(queue_cmd->counter != NULL) { *queue_cmd->counter -= 1; if(*queue_cmd->counter == 0) { free(queue_cmd->counter); queue_cmd->counter = NULL; free(queue_cmd->share); queue_cmd->share = NULL; free(queue_cmd->len); queue_cmd->len = NULL; } } /* When this priority queue is empty now, then update summary of real priorities */ if(out_queue->queues[queue_cmd->prio]->count == 0) { if(prio>=VRS_DEFAULT_PRIORITY) out_queue->r_prio_sum_high -= out_queue->queues[queue_cmd->prio]->r_prio; else out_queue->r_prio_sum_low -= out_queue->queues[queue_cmd->prio]->r_prio; } /* Update new priority command */ queue_cmd->id = cmd->id; queue_cmd->prio = prio; if(out_queue->cmds[cmd->id]->flag & NOT_SHARE_ADDR) { _v_out_queue_command_add(out_queue->queues[prio], flag, 0, queue_cmd, cmd); } else { _v_out_queue_command_add(out_queue->queues[prio], flag, 1, queue_cmd, cmd); } /* Add data of the command to the hashed linked list */ queue_cmd->vbucket = v_hash_array_add_item(&out_queue->cmds[cmd->id]->cmds, cmd, out_queue->cmds[cmd->id]->item_size); queue_cmd->vbucket->ptr = (void*)queue_cmd; assert(queue_cmd->vbucket->data != NULL); /* When this priority queue was empty, then update summary of real priorities */ if(out_queue->queues[prio]->count == 0) { if(prio>=VRS_DEFAULT_PRIORITY) out_queue->r_prio_sum_high += out_queue->queues[prio]->r_prio; else out_queue->r_prio_sum_low += out_queue->queues[prio]->r_prio; } /* Update count and size in new priority queue */ out_queue->queues[prio]->count++; out_queue->queues[prio]->size += out_queue->cmds[cmd->id]->item_size; /* If necessary update maximal or minimal priority */ if(prio > out_queue->max_prio) { out_queue->max_prio = prio; } else if(prio < out_queue->min_prio) { out_queue->min_prio = prio; } } else { /* Debug print */ #if 0 v_print_log(VRS_PRINT_DEBUG_MSG, "Replacing obsolete command with new\n"); v_cmd_print(VRS_PRINT_DEBUG_MSG, (struct Generic_Cmd*)vbucket->data); v_cmd_print(VRS_PRINT_DEBUG_MSG, cmd); #endif /* Destroy original command */ v_cmd_destroy((struct Generic_Cmd**)&vbucket->data); /* Replace data of current queue command with new data */ vbucket->data = (void*)cmd; } } else { /* Add new command in queue */ queue_cmd = _v_out_queue_command_create(out_queue, flag, prio, cmd); if(queue_cmd == NULL) { ret = 0; } } } else { /* Add new command in queue */ queue_cmd = _v_out_queue_command_create(out_queue, flag, prio, cmd); if(queue_cmd == NULL) { ret = 0; } } return ret; }
/** * \brief This function removes packet with id from history of sent packets. * It removes all its commands from the command history too. * * \param[in] *C The verse context. * \param[in] id The ID of packet, the will be removed from the history. * * \return This function returns 1, then packet with id was found in the history * and it returns 0 otherwise. */ int v_packet_history_rem_packet(struct vContext *C, uint32 id) { struct VS_CTX *vs_ctx = CTX_server_ctx(C); struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C); struct VPacket_History *history = &dgram_conn->packet_history; struct VPacket *r_packet = CTX_r_packet(C); struct VSent_Packet *sent_packet; int ret = 0, is_fake_cmd_received = 0; sent_packet = v_packet_history_find_packet(history, id); if(sent_packet != NULL) { struct VSent_Command *sent_cmd; v_print_log(VRS_PRINT_DEBUG_MSG, "Removing packet: %d from history\n", sent_packet->id); /* Go through the whole list of sent commands and free them from the * hashed linked list */ sent_cmd = sent_packet->cmds.first; while(sent_cmd != NULL) { /* Remove own command from hashed linked list if it wasn't already * removed, when command was obsoleted by some newer packet */ if(sent_cmd->vbucket!=NULL) { struct Generic_Cmd *cmd = (struct Generic_Cmd*)sent_cmd->vbucket->data; /* Bucket has to include some data */ assert(sent_cmd->vbucket->data!=NULL); /* Decrease total size of commands that were sent and wasn't acknowladged yet*/ dgram_conn->sent_size -= v_cmd_size(cmd); /* Put fake command for create/destroy commands at verse server */ if(vs_ctx != NULL) { struct VSession *vsession = CTX_current_session(C); struct Generic_Cmd *fake_cmd = NULL; switch(cmd->id) { case CMD_NODE_CREATE: fake_cmd = v_fake_node_create_ack_create(UINT32(cmd->data[UINT16_SIZE + UINT32_SIZE])); break; case CMD_NODE_DESTROY: fake_cmd = v_fake_node_destroy_ack_create(UINT32(cmd->data[0])); break; case CMD_TAGGROUP_CREATE: fake_cmd = v_fake_taggroup_create_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE])); break; case CMD_TAGGROUP_DESTROY: fake_cmd = v_fake_taggroup_destroy_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE])); break; case CMD_TAG_CREATE: fake_cmd = v_tag_create_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE]), UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE])); break; case CMD_TAG_DESTROY: fake_cmd = v_tag_destroy_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE]), UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE])); break; case CMD_LAYER_CREATE: fake_cmd = v_fake_layer_create_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE])); break; case CMD_LAYER_DESTROY: fake_cmd = v_fake_layer_destroy_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE])); break; default: break; } if(fake_cmd != NULL) { is_fake_cmd_received = 1; /* Push command to the queue of incomming commands */ v_in_queue_push(vsession->in_queue, fake_cmd); /* Print content of fake command */ v_fake_cmd_print(VRS_PRINT_DEBUG_MSG, fake_cmd); } } /* Remove command from the history of sent commands */ ret = v_hash_array_remove_item(&history->cmd_hist[sent_cmd->id]->cmds, sent_cmd->vbucket->data); if(ret == 1) { /* Destroy command */ v_cmd_destroy(&cmd); } else { v_print_log(VRS_PRINT_ERROR, "Unable to remove command id: %d\n", sent_cmd->id); ret = 0; } } sent_cmd = sent_cmd->next; } /* Free linked list of sent commands */ v_list_free(&sent_packet->cmds); /* Remove packet itself from the linked list of sent packet */ v_list_rem_item(&history->packets, sent_packet); free(sent_packet); ret = 1; } else { /* When acknowledged payload packet is not found in history, then it * is OK, because it is probably keep alive packet without any node * commands */ v_print_log(VRS_PRINT_DEBUG_MSG, "Packet with id: %d, not found in history\n", id); } /* When pure ack packet caused adding some fake commands to the queue, then * poke server data thread */ if( (vs_ctx != NULL) && (is_fake_cmd_received == 1) && (r_packet->data == NULL)) { sem_post(vs_ctx->data.sem); } return ret; }