/*=r=************************************************************************/ static void m__file_segment_sent (const TRANS_STATUS *info) { /*------------------------------------------------------------*/ d_msg__ (":::File_Segment_Sent trans %s; offset=%lu, length=%lu.\n", cfdp_trans_as_string (info->trans), info->fd_offset, info->fd_length); }
/*=r=************************************************************************/ static void m__new_node (NAK *nak_ptr, u_int_4 begin, u_int_4 end, NODE **neww) /* WHAT IT DOES: Attempts to create a new node. If successful, the node * is loaded with the given begin & end values, and a pointer * to that node is returned (via 'neww'). If unsuccessful, 'neww' is null. * NOTE: 'nak_ptr' is an input parameter that is only relevant when dynamic * allocation is disabled (in that case, 'creation' is different). */ { NODE *node_ptr; /*------------------------------------------------------------*/ /* If configured to do so, output a debug message */ if (cfdp_is_message_class_enabled (CFDP_MSG_DEBUG_NAK)) d_msg__ ("Gap added (%lu-%lu).\n", begin, end); /* Initialize the node-pointer to a value indicating failure */ *neww = NULL; /* Attempt to allocate memory for a new node */ *neww = nak_mem__malloc (nak_ptr); if (*neww == NULL) /* Oops. Out of memory. */ return; /* Load the new node with the specified 'begin' and 'end' values */ node_ptr = *neww; node_ptr->begin = begin; node_ptr->end = end; node_ptr->next = NULL; }
/*=r=*************************************************************************/ static void m__move_to_state_s2 (MACHINE *m) { /*------------------------------------------------------------*/ /* Move to state S2 (Cancelling) */ m->publik.state = S2; if (cfdp_is_message_class_enabled (CFDP_MSG_STATE_CHANGE)) d_msg__ ("<R2> (State S2) 'Transaction Cancelled'\n"); }
/*=r=************************************************************************/ void nak__data_received (NAK *nak_ptr, u_int_4 begin, u_int_4 end) /* ALGORITHM: The given data-range (begin-end) must fit one of the four * cases below. Determine which case applies, and perform the * corresponding action(s). * ASSUMPTION: The scope always starts at zero. */ { /*------------------------------------------------------------*/ /* Validate the given inputs */ if ((begin < 0) || (end < 0) || (begin > end)) { e_msg__ ("cfdp_engine: Nak module ignored receipt of Filedata " "with invalid range (%lu-%lu)\n", begin, end); return; } else { /* If configured to do so, output a debug message */ if (cfdp_is_message_class_enabled (CFDP_MSG_DEBUG_NAK)) d_msg__ ("Valid_filedata_received: %lu-%lu.\n", begin, end); } /*-------------------------------------------------------------- * Case 1 -- the given range is fully within the current scope. * Action: the scope doesn't change; just remove the given range * from the current gap-list. * Example: scope=0-1000, range=100-200. *--------------------------------------------------------------*/ if (end <= nak_ptr->end_of_scope) m__remove_range_from_list (nak_ptr, begin, end); /*------------------------------------------------------------------------ * Case 2 -- the given range is both inside and outside the current scope. * Action: The scope has to be extended, and the portion of the given * range that is within the current scope has to be removed from the * current gap-list. * Example: scope=0-1000, range=900-1100. New scope is 0-1100, * and the range 900-1000 is removed from gap-list. *------------------------------------------------------------------------*/ else if ((begin < nak_ptr->end_of_scope) && (end > nak_ptr->end_of_scope)) { m__remove_range_from_list (nak_ptr, begin, nak_ptr->end_of_scope); nak_ptr->end_of_scope = end; } /*----------------------------------------------------------------- * Case 3 -- the given range begins at the current scope boundary * (i.e. there is no gap between this new data and previously-received * data). * Action: Just extend the current scope. * Example: scope=0-1000, range=1000-1100. New scope is 0-1100. *-----------------------------------------------------------------*/ else if (begin == nak_ptr->end_of_scope) nak_ptr->end_of_scope = end; /*---------------------------------------------------------------------- * Case 4 -- the given range is completely outside the current scope; * i.e. there is a gap between the end of any previously-received * data and the start of this new data. * Action: Add the new gap to the list, and extend the current scope. * Example: scope=0-1000, range=1100-1200. New scope is 0-1200, and * a gap from 1000-1100 is added to the gap-list. *----------------------------------------------------------------------*/ else if (begin > nak_ptr->end_of_scope) { m__add_gap_to_list (nak_ptr, nak_ptr->end_of_scope, begin); nak_ptr->end_of_scope = end; } else /* If we got here, then there is a bug in the above code */ ASSERT__BUG_DETECTED; }
/*=r=************************************************************************/ static void m__remove_range_from_node (NAK *nak_ptr, NODE *p, u_int_4 begin, u_int_4 end) /* WHAT IT DOES: It removes the given range ('begin' through 'end') from * the given node (presumed to be within the given nak-list). * NOTE: Any range is acceptable (i.e. if the range does not overlap the * node's range, then no action is taken). * NOTE: If it is necessary to delete the node, then the given nak-list * will be modified. */ { NODE *new_node_ptr; /*------------------------------------------------------------*/ /* If configured to do so, output a debug message */ if (cfdp_is_message_class_enabled (CFDP_MSG_DEBUG_NAK)) d_msg__ ("Removing range (%lu-%lu) from node (%lu-%lu).\n", begin, end, p->begin, p->end); /* Example of case 1: node contains 100-200, begin=100, end=200 */ if ((begin <= p->begin) && (end >= p->end)) /* The range overlaps the entire node; remove node from list */ m__remove_node (nak_ptr, p); /* Example of case 2: node contains 100-200, begin=300, end=400 */ else if ((end <= p->begin) || (begin >= p->end)) /* The range does not overlap the node at all; no action needed */ ; /* Example of case 3: node contains 100-200, begin=50, end=150 */ else if ((begin <= p->begin) && (end > p->begin)) /* The range overlaps a front portion of the node; shorten the node */ p->begin = end; /* Example of case 4: node contains 100-200, begin=150, end=250 */ else if ((begin < p->end) && (end >=p->end)) /* The range overlaps a rear portion of the node; shorten the node */ p->end = begin; /* Example of case 5: node contains 100-200, begin=125, end=175. * Single gap of 100-200 is replaced with two gaps: 100-125 and 175-200. */ else if ((begin > p->begin) && (end < p->end)) /* The range overlaps an interior portion of the node, but does not * overlap the beginning or end. Break the single node into 2 nodes. */ { /* Create a new node and put the remaining "rear" portion into it */ m__new_node (nak_ptr, end, p->end, &new_node_ptr); if (new_node_ptr == NULL) /* Oops. Out of memory. Leave nak as-is. * There is some loss of efficiency (due to unnecessary retrans * by the Sender), but the transaction will be able to complete * successfully (because no data gap has been 'forgotten'). */ return; /* Modify original node to leave only the remaining "front" portion */ p->end = begin; /* Adjust 'next' pointers so as to insert the new node after the * current node. */ new_node_ptr->next = p->next; p->next = new_node_ptr; } else /* This code should never be reached * (unless the other "if" cases above don't cover all possibilities). * If this code is reached, report the bug. * If the assertion routine allows the program to continue, * abort the routine. */ { ASSERT__BUG_DETECTED; return; } }
/*=r=************************************************************************/ boolean cfdp_give_pdu (PDU pdu) /* NOTE: The CFDP protocol requires a transaction's Inactivity-timer * to be started (or restarted) each time a PDU is received. * That action is taken here rather than in 25 different places in * all the state table modules. */ { HDR hdr; MACHINE *m; ROLE my_role; _PDU_TYPE_ pdu_type; boolean successful; /*------------------------------------------------------------*/ REPORT_VERSION_ONCE; /*------------------*/ /* Validate the pdu */ /*------------------*/ if (!pdu__is_this_pdu_acceptable (&pdu)) return (0); pdu_type = pdu__what_type (&pdu); if (pdu_type == _DONT_KNOW_) /* PDUs such as 'Keepalive' are simply ignored */ { d_msg__ ("cfdp_engine: ignored incoming PDU of unknown type.\n"); return (1); } hdr = pdu__hdr_struct_from_pdu (&pdu); my_role = pdu__receiver_role_from_pdu_hdr (hdr); /* If we are the sender of the file, there are some pdu-types that our * partner should never send (for example, EOF). If we are the receiver * of the file, there are other pdu-types that our partner should never * send (for example, Ack-EOF). */ if (!m__is_pdu_sane (pdu_type, my_role)) return (0); /* Perhaps put out some debug messages */ if (cfdp_is_message_class_enabled (CFDP_MSG_DEBUG_PDU)) { pdu__display_raw_pdu (pdu); pdu__display_disassembled_pdu (pdu); } /* Validation was successful; assume the response will also be successful * unless/until proven otherwise. */ successful = YES; /*----------------------------------------------------------------*/ /* If an existing state machine is assigned to this transaction, */ /* pass the PDU to that state machine. */ /*----------------------------------------------------------------*/ if ((m = machine_list__get_this_trans (hdr.trans)) != NULL) { /* Conceptually, re-starting the inactivity-timer belongs in each * of the state tables, but it's done here for practical purposes * (it would be duplicated in 20+ places otherwise). */ if (!m->publik.frozen && !m->publik.suspended) m->inactivity_timer = timer__start (mib__inactivity_timeout(NULL)); m__give_pdu_to_trans (m, &pdu); } /*----------------------------------------------------------------*/ /* If the incoming PDU is a MD/FD/EOF for a Receiver, then create */ /* a new state machine and pass the PDU to that state machine. */ /*----------------------------------------------------------------*/ else if (((pdu_type == _MD_) || (pdu_type == _FD_) || (pdu_type == _EOF_)) && ((my_role == R_1) || (my_role == R_2))) { if ((m = m__start_new_receiver_machine (hdr)) != NULL) m__give_pdu_to_trans (m, &pdu); else successful = NO; } /*--------------------------------------------------------------------*/ /* If the incoming PDU is a Finished PDU for a Class 2 Sender, then */ /* create a new state machine and pass the PDU to that state machine. */ /*--------------------------------------------------------------------*/ else if ((pdu_type == _FIN_) && (my_role == S_2)) { if ((m = m__restart_old_sender_machine (hdr)) != NULL) m__give_pdu_to_trans (m, &pdu); else successful = NO; } /*----------------------------*/ /* Any other PDUs are ignored */ /*----------------------------*/ else d_msg__ ("cfdp_engine: ignored an incoming PDU (trans %s).\n\n", cfdp_trans_as_string (hdr.trans)); return (successful); }
/*=r=************************************************************************/ boolean cfdp_give_request (const char *request_string) { CFDP_FILE *fp; MACHINE *m; REQUEST request; boolean successful = YES; /*------------------------------------------------------------*/ REPORT_VERSION_ONCE; /*---------------------------------*/ /* Validate the given User Request */ /*---------------------------------*/ if (request_string == NULL) { e_msg__ ("cfdp_engine: cfdp_give_request: given a null-pointer.\n"); successful = NO; } else if (!utils__request_from_string (request_string, &request)) /* The string does not represent a valid request */ { e_msg__ ("cfdp_engine: request is invalid (%s).\n", request_string); successful = NO; } else if ((request.type == REQ_PUT) && (cfdp_are_these_ids_equal (request.info.put.dest_id, mib__get_my_id()))) /* This Put Request is invalid */ { e_msg__ ("cfdp_engine: Won't allow you to send a file to yourself!\n"); successful = NO; } /* If the User Request is an "empty" one, just ignore it */ else if (request.type == REQ_NONE) /* No action needed */ d_msg__ ("cfdp_engine: ignoring an empty User Request.\n"); /*--------------------------------------------------------- * If the User Request is a Put Request, then create a new * state machine, and pass the Put Request to that machine *---------------------------------------------------------*/ else if (request.type == REQ_PUT) { /* Optimization: If the source-file doesn't exist, catch it now */ fp = fopen_callback (request.info.put.source_file_name, "rb"); if (fp == NULL) /* The source-file doesn't exist; no point in trying to transfer it */ { e_msg__ ("cfdp_engine: can't transfer non-existent file (%s).\n", request.info.put.source_file_name); successful = NO; } else { /* It does exist; close it and let the state machine re-open it */ fclose_callback (fp); /* Start a new state machine */ if ((m = m__start_new_sender_machine (request)) != NULL) /* Fire the state table event associated with a Put Request */ m__state_table (m, RECEIVED_PUT_REQUEST, NULL, &request); else successful = NO; } } /*------------------------------------------------------------------ * If the User Request applies to *all* transactions, then pass it * to all active state machines *------------------------------------------------------------------*/ else if (request.type == REQ_REPORT) /* Report all active transactions */ m__give_request_to_all_trans (request.type); else if (request.type == REQ_FREEZE) /* Freeze all active transactions, and any future ones too */ { m__give_request_to_all_trans (request.type); misc__freeze_all_partners (); } else if (request.type == REQ_THAW) /* Thaw all active transactions, and any future ones too */ { m__give_request_to_all_trans (request.type); misc__thaw_all_partners (); } else if (request.type == REQ_ABANDON_ALL_TRANSACTIONS) m__give_request_to_all_trans (REQ_ABANDON); else if (request.type == REQ_CANCEL_ALL_TRANSACTIONS) m__give_request_to_all_trans (REQ_CANCEL); else if (request.type == REQ_SUSPEND_ALL_TRANSACTIONS) m__give_request_to_all_trans (REQ_SUSPEND); else if (request.type == REQ_RESUME_ALL_TRANSACTIONS) m__give_request_to_all_trans (REQ_RESUME); /*----------------------------------------------------------------- * If the User Request applies to one transaction, then attempt to * find an active state machine assigned to that transaction, and * pass the Request to that state machine. *-----------------------------------------------------------------*/ else if ((request.type == REQ_ABANDON) || (request.type == REQ_CANCEL) || (request.type == REQ_SUSPEND) || (request.type == REQ_RESUME)) { if ((m = machine_list__get_this_trans (request.info.trans)) != NULL) /* Success (a machine is assigned to this transaction) */ m__give_request_to_one_trans (m, request.type); else /* No state machine assigned */ { e_msg__ ("cfdp_engine: ignoring User-Request that references " "unknown transaction (%s).\n", cfdp_trans_as_string (request.info.trans)); successful = NO; } } else { w_msg__ ("cfdp_engine: ignoring unrecognized User-Request (%s).\n", request_string); successful = NO; } return (successful); }