/* * $Source$ * $Header$ */ #ifndef lint static char *rcsid_gdb_trans2_c = "$Header$"; #endif /************************************************************************ * * gdb_trans2.c * * GDB - Data Transport Services Routines (Part 2) * * Author: Noah Mendelsohn * Copyright: 1986 MIT Project Athena * For copying and distribution information, please see * the file . * * * These routines implement layer 6 of the Client Library * Specification of the GDB system, as well as the facilities * outlined in the GDB Protocol Specification. Part 2 of 2. * * Some of the routines specified are actually implemented as * macros defined in gdb.h. * ************************************************************************/ #include #include #include #include #include "gdb.h" #include #include #include extern int errno; /* Unix error slot */ /* * The following values are returned by g_con_progress */ #define NOPROGRESS 0 /* nothing happened on this */ /* connection--must be 0*/ #define PROGRESS 1 /* connection has progressed */ #define COMPLETE 2 /* an operation has */ /* completed on this con */ /************************************************************************/ /* /* queue_operation(queue_operation) /* /* Add an operation to the queue for a given connection, and /* then allows all connections to progress. Returns the last /* known status of the operation. /* /************************************************************************/ int queue_operation(con, direction, op) CONNECTION con; int direction; OPERATION op; { register HALF_CONNECTION hcon = (direction==CON_INPUT)?(&(con->in)): (&(con->out)); GDB_CHECK_CON(con, "queue_operation") /* * Write message to debugging log */ if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "op queued: con=0x%x dir=%s op=0x%x Q was %s empty\n", con, (direction == CON_INPUT)?"INPUT":"OUTPUT", op, (hcon->op_q_first == (OPERATION)hcon)?"":"not"); /* * Make sure connection is up */ if (con->status != CON_UP) { op->status = OP_CANCELLED; if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "\nop NOT queued\n"); return OP_CANCELLED; } /* * Put the new operation at the end of the queue */ op->prev = hcon->op_q_last; op->next = (OPERATION)hcon; hcon->op_q_last->next = op; hcon->op_q_last = op; /* * Mark it as queued */ op->status = OP_QUEUED; op->halfcon = hcon; /* * Force progress on this connection */ (void) g_con_progress(con - gdb_cons); /* * Con_select with notime is used here as a kind of fudge for * doing a fastprogress with a select built in before it. */ (void) con_select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &gdb_notime);/* XXX */ /* * Return the last known status of the operation */ return op->status; } /************************************************************************/ /* /* requeue_operation /* /* This routine may be called from an init or continuation routine /* to cause the current operation to be requeued on a new connection. /* The init routine field ofthe operation should be properly set to /* indicate the routine to receive control when the operation actually /* runs on the new connection. The caller of this routine is /* responsible for returning the status OP_REQUEUED to its caller. /* /* This routine returns the status of the newly queued operation. /* Note, however, that even if this operation returns the status /* CANCELLED, the operation itself may not continue to execute /* on the old connection and it should return the status OP_REQUEUED, /* NOT OP_CANCELLED (at least in this implementation.) /* /************************************************************************/ int requeue_operation(con, direction, op) CONNECTION con; int direction; OPERATION op; { /* * Make sure the connection supplied is a legal one */ GDB_CHECK_CON(con, "requeue_operation") /* * Write message to debugging log */ if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "op requeued: new con=0x%x dir=%s op=0x%x\n", con, (direction == CON_INPUT)?"INPUT":"OUTPUT", op); /* * Dequeue the operation from its old half connection */ (void) g_op_newhead(op->halfcon); /* * Now queue it on the new one */ return queue_operation(con, direction, op); } /************************************************************************/ /* /* g_preempt_me /* /* Sticks a new operation in ahead of the current one and runs it /* on the current connection. May be called only from an init or /* continuation routine. The old operation must have completely /* prepared the descriptor for the new operation, i.e. it should /* be in the same state as it would be for a call to queue_operation. /* g_preempt_me makes it possible for operations to be built by /* composition of other smaller operations, since newop runs, in /* a sense, as a subroutine of oldop. opdop must (1) reset its /* initialization routine to be a routine to be called when newop /* completes or cancels and (2) return the status OP_PREEMPTED to /* its caller. /* /************************************************************************/ int g_preempt_me(oldop, newop) OPERATION oldop; OPERATION newop; { register OPERATION old=oldop, new=newop; register HALF_CONNECTION hc = old->halfcon; /* * Write message to debugging log */ if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "op preempted: halfcon=0x%x oldop=0x%x newop=0x%x\n", hc,oldop,newop); /* * link in the new operation */ old->prev = new; hc->op_q_first = new; new->prev = (OPERATION)hc; new->next = old; /* * Set the status of the new operation */ new->status = OP_QUEUED; new->halfcon = hc; /* * Change the status of the old operation (one could argue that * this should be done in gdb_hcon_progress after the return code * is detected.) */ old->status = OP_QUEUED; return OP_QUEUED; } /************************************************************************/ /* /* gdb_progress /* /* This routine should be called whenever it is suspected that /* progress can be made on any connection. This routine will /* cause all connections to proceed as far as they can without /* blocking, and will make a best effort to avoid long blocks. /* This routine MAY retain control for long periods when sustained /* progress is possible, but it will not knowingly hang. /* /* Returns: number of connections on which OPERATIONS have /* COMPLETED (not just progressed). /* /************************************************************************/ int gdb_progress() { register int i; /* index to available */ /* connections */ register int return_value = 0; /* the value we return */ int rc; /* short term storage for */ /* a return code */ int progress_made; /* true when some con */ /* made progress during */ /* latest pass through list */ int complete_map[GDB_MAX_CONNECTIONS]; /* indicates whether a */ /* transmission operation */ /* is newly complete on */ /* corresponding connection */ /* 1 if yes else 0 */ int maxcon = gdb_mcons; /* gdb_mcons may change */ /* out from under us if */ /* connections break. This */ /* is the initial value. */ /* * Zero out the completion map for all connections. */ for (i=0; iin.flags&HCON_UNUSED))&& ((con->in.stream_buffer_remaining > 0) || FD_ISSET(infd, &last_crfds))) { rc = gdb_hcon_progress(CON_INPUT, &con->in); if (!gdb_conok) { g_stop_with_errno(con); rc = COMPLETE; } if (rc == COMPLETE) retval++; } if ((!(con->out.flags&HCON_UNUSED))&& (FD_ISSET(outfd, &last_cwfds))) { rc = gdb_hcon_progress(CON_OUTPUT, &con->out); if (!gdb_conok) { g_stop_with_errno(con); rc = COMPLETE; } if (rc == COMPLETE) retval++; } } /* * We've gone as far as we can, now find out how many connections * have had operations complete. */ return retval; } /************************************************************************/ /* /* g_con_progress /* /* Make as much progress as possible on the specified connection. /* Returns NOPROGRESS if no bytes moved on either half connection, /* PROGRESS, if some moved and no operations completed, or COMPLETE if /* any of the operations completed. Note that each connection /* consists of two half connections, and we must make each of them /* progress as far as possible. /* /* The nest here starts getting so deep that it's hard to pass state /* around efficiently. We use a single global variable, gdb_conok, /* to indicate whether the connection we're working on now has died. /* The move data routines set this to FALSE whenever there is a /* fatal error on a connection. We check it, and do a proper /* sever on the connection if it seems to be in trouble. /* /************************************************************************/ int g_con_progress(con_id) int con_id; /* index of this connection */ /* in the connection desc. */ /* arrays*/ { register CONNECTION con= (&gdb_cons[con_id]); /* pointer to the connection */ /* data structure */ register int progress = NOPROGRESS; register int live = TRUE; /* true when we've seen */ /* enough to make sure we */ /* want to go around again*/ int rc; /* * Check status of connection-if it's not running, then just return. */ if (con->status != CON_UP) return NOPROGRESS; /* * Repeatedly make progress on each half connection until both * are idle. Important to keep trying as one may become active * while the other is progressing. */ gdb_conok = TRUE; /* this gets set to FALSE */ /* for fatal I/O errors */ /* there may be a timing */ /* window here in use of */ /* HCON_BUSY. Also: it is */ /* essential that errno */ /* remain valid after conok */ /* goes bad */ while (live) { live = FALSE; /* until proven otherwise */ /* * make progress on the input connection note that following * logic depends on NOPROGRESS being 0 */ if (rc = gdb_hcon_progress(CON_INPUT, &con->in)) { live = TRUE; progress = max(rc, progress); } /* * See if connection has died */ if (!gdb_conok) { g_stop_with_errno(con); return COMPLETE; /* dying connection always */ /* implies that the */ /* operation at the head */ /* of the queue completed */ } /* * make progress on the output connection */ if (rc = gdb_hcon_progress(CON_OUTPUT, &con->out)) { live = TRUE; progress = max(rc, progress); } /* * See if connection has died */ if (!gdb_conok) { g_stop_with_errno(con); return COMPLETE; } } return progress; } /************************************************************************/ /* /* gdb_hcon_progress /* /* Allows a specified half-connection to progress as much as possible, /* and returns true iff at least one operation is newly completed. /* /************************************************************************/ int gdb_hcon_progress(direction, hc) int direction; /* CON_INPUT or CON_OUTPUT */ struct half_con_data *hc; /* pointer to control struct */ /* for this half connection */ { HALF_CONNECTION half_con = hc; /* half connection pointer */ /* fast copy in register */ register OPERATION op; /* current operation on this */ /* half connection */ int progress = NOPROGRESS; /* can indicate any progress */ /* on the half con or */ /* whether any operations */ /* completed */ int done; /* true when no more progress*/ /* can be made */ int fcn_result; /* result of latest init or */ /* continue function */ /* * Write message to debugging log */ if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "hcon_progress: halfcon=0x%x dir=%s ", half_con, (direction==CON_INPUT)?"INPUT":"OUTPUT"); /*----------------------------------------------------------*/ /* /* See if we are being re-entered and are already working /* on this half_con. If so, return right now. /* /*----------------------------------------------------------*/ if (half_con->flags & HCON_BUSY) { /* * Write message to debugging log */ if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "BUSY, returning\n"); return NOPROGRESS; } /*----------------------------------------------------------*/ /* /* See if there is an operation on this half connection. /* If not, return. /* /*----------------------------------------------------------*/ op = half_con->op_q_first; /* pick up first operation */ /* in queue */ if (op == (OPERATION)half_con) { /* see if end of circular */ /* list */ /* * Write message to debugging log */ if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "Q EMPTY, returning\n"); return NOPROGRESS; /* nothing to do on */ /* this half session */ } /*----------------------------------------------------------*/ /* /* Loop until all operations are complete, or until no further /* progress can be made on this one. /* /* Loop invariants: /* /* 1) Op contains the operation at the head of the q, or /* else is == half_con, indicating no more operationos /* to be processed. /* /* 2) The operation at the head of the queue is either running /* or continuing. As soon as one completes, it is dequeued. /* /* Progress is declared whenever an operation newly /* returns OP_COMPLETE, i.e. whenever there has been /* an operation which went from running to complete. /* /* Done is declared whenever an operation returns anything /* other than complete, indicating that it cannot progress /* further at this time. Loop ends. /* /* While we're here, mark us busy so we won't try the /* same half_con on reentry. /* /*----------------------------------------------------------*/ done = FALSE; /* this one may be able to */ /* progress */ half_con->flags |= HCON_BUSY; /* don't try this hcon */ /* while we already doing */ /* it. Could happen if */ /* we queue new ops */ half_con->flags &= ~HCON_PROGRESS; /* gdb_move_data will */ /* indicate progress here*/ if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "LOOPING\n"); /*----------------------------------------------------------*/ /* /* Loop through the operations queued on this half con /* trying to make progress on them, in order. /* /*----------------------------------------------------------*/ while (!done && op != (OPERATION)half_con) { if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "\top=0x%x status%d...", op, OP_STATUS(op)); switch (op->status) { /* * Operation is at head of queue for first time and has * never been started. Try to start it up. */ case OP_QUEUED: /* * Call the initialization routine for this operation */ fcn_result = (*op->fcn.init)(op,half_con,op->arg); if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "init result=%d\n", fcn_result); switch (fcn_result) { case OP_COMPLETE: case OP_CANCELLED: op->status = fcn_result; op = g_op_newhead(half_con); progress = COMPLETE; break; case OP_PREEMPTED: op->status = OP_QUEUED; /* fall thru */ case OP_REQUEUED: /* important: don't set status on re-queued */ /* op as it may already have completed in */ /* its second life ! */ op = half_con->op_q_first; progress = max(progress, PROGRESS); break; default: op->status = fcn_result; done = TRUE; /* could not get done */ } break; /* * Operation is at head of queue and has already * started trying to run. The only reason we could be in this * state is that the last time we tried to do the requested input * or output, all the data could not be moved synchronously. * We therefore try to move some more, and if it all goes now, * we call the continuation routine. */ case OP_RUNNING: /* * Try to move some more data. If it won't all * go now, we're done with this half connection. * * If this is a special listening connection which * has an operation queued trying to do a listen, * then do the listen. Otherwise do an ordinary * data move. */ if (half_con->flags & HCON_PENDING_LISTEN) { if (gdb_listen(half_con)==FALSE) { if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "NO LISTEN\n"); done = TRUE; break; } } else if (gdb_move_data(direction, half_con)==FALSE) { done = TRUE; if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "NO DATA\n"); break; } /* * The pending data transmission has now completed. * Call the continuation routine for this operation */ fcn_result = (*op->fcn.cont)(op,half_con,op->arg); if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "cont result=%d\n", fcn_result); switch (fcn_result) { case OP_COMPLETE: case OP_CANCELLED: op->status = fcn_result; op = g_op_newhead(half_con); progress = COMPLETE; break; case OP_PREEMPTED: op->status = OP_QUEUED; /* fall thru */ case OP_REQUEUED: /* important: don't set status on re-queued */ /* op as it may already have completed in */ /* its second life ! */ op = half_con->op_q_first; progress = max(progress, PROGRESS); break; default: op->status = fcn_result; done = TRUE; /* could not get done */ } break; /* * Following cases are all unexpected, at least for the * moment. (See explanation of loop invariants for this while * loop. Give up if they turn up. */ case OP_COMPLETE: GDB_GIVEUP("gdb_hcon_progress: found OP_COMPLETE on q") case OP_CANCELLED: GDB_GIVEUP("gdb_hcon_progress: found OP_CANCELLED on q") case OP_CANCELLING: GDB_GIVEUP("gdb_hcon_progress: OP_CANCELLING") default: GDB_GIVEUP("gdb_hcon_progress: Operation is queued, but is not runnable") } } if (progress == NOPROGRESS && (half_con->flags & HCON_PROGRESS)) progress = PROGRESS; half_con->flags &= ~HCON_BUSY; if (gdb_Debug & GDB_LOG) fprintf(gdb_log, "hcon_progress: returns %d\n",progress); return progress; /* NOPROGRESS, PROGRESS */ /* or COMPLETE */ } /************************************************************************/ /* /* g_op_newhead /* /* Dequeues the operation at the head of the queue for the /* given half connection and returns the pointer to the /* new head of the queue. If the queue is null, then a pointer /* to the half_con itself is returned. (The lists are /* linked circularly.) /* /************************************************************************/ OPERATION g_op_newhead(hcp) HALF_CONNECTION hcp; { register OPERATION newhead, oldhead; /* * Get old and new heads of chain */ oldhead = hcp->op_q_first; newhead = oldhead->next; /* * Make sure nobody chained a bad one on us */ if (newhead == NULL) { if (gdb_Debug & GDB_LOG) { fprintf(gdb_log,"\t\tg_op_newhead: found null link, oldhead = 0x%x newhead=0x%x halfcon=0x%x\n\t\t\t hc->first=0x%x hc->last=0x%x\n", oldhead, newhead, hcp, hcp->op_q_first, hcp->op_q_last); } GDB_GIVEUP("g_op_newhead: found NULL chain link") } /* * Remove oldhead from chain, fixing up chain pointers */ newhead->prev = oldhead->prev; hcp->op_q_first = newhead; /* * Clean up pointers in the newly dequeued operation. This is * just for cleanliness and ease of debugging. */ oldhead->next = oldhead->prev = NULL; oldhead->halfcon = NULL; return newhead; } /************************************************************************/ /* /* gdb_move_data /* /* This routine attempts to make further progress on the pending /* level transmission operation pending on this half connection. /* (Presumes that such an operation is pending.) Returns TRUE /* if all the requested data has been moved, else FALSE. /* /* We assume here that all fd's are set to non-blocking I/O, so /* we can safely try reading and writing until they return 0 bytes. /* /************************************************************************/ #define FIX_BUFFER_POINTERS(hc, count) if (count>0) {hc->next_byte += count; \ hc->remaining -= count;} int gdb_move_data(direction, hc) int direction; /* CON_INPUT or CON_OUTPUT */ struct half_con_data *hc; /* pointer to control struct */ /* for this half connection */ { register HALF_CONNECTION half_con = hc; /* half connection pointer */ /* fast copy in register */ register fd_set *fdbits; /* the mask we should adjust */ /* for this direction */ /* * For safety, in case we're called when nothing is pending. */ if (half_con->remaining == 0) return TRUE; /* * Move the data into the user's buffer. In the case of input * data may come first from the stream buffer, then from the socket * itself. */ if (direction == CON_INPUT) { gdb_transfer_from_buffer(half_con); /* * If remaining is greater than 0, then we emptied * the stream buffer and still weren't done. Try * to read it from the pipe and re-fill the stream * buffer. */ if (half_con->remaining) { gdb_read_data_and_buffer(half_con); } } else { gdb_write_data(half_con); } /* * The file descriptor masks used for doing selects must be activated * when and only when there is a pending operation trying to use * the connection. Update the masks for this half connection. */ fdbits = (direction == CON_INPUT)? &gdb_crfds : &gdb_cwfds; if (half_con->remaining >0 && gdb_conok) FD_SET(half_con->fd, fdbits); else FD_CLR(half_con->fd, fdbits); return (half_con->remaining == 0); } /************************************************************************/ /* /* gdb_transfer_from_buffer /* /* Given an inbound half connection, satisfy as much as possible /* of desired data from the stream buffer. /* /************************************************************************/ int gdb_transfer_from_buffer(hc) register HALF_CONNECTION hc; { register int count; /* amount to move */ /* * Figure out how much, if any, we'll be able to do here */ count = min(hc->remaining, hc->stream_buffer_remaining); if (count <= 0) return; /* could not satisfy */ /* any from buffered data*/ /* * Copy the data, update both stream and data buffer pointers */ memcpy(hc->next_byte, hc->stream_buffer_next, count); hc->stream_buffer_next += count; hc->stream_buffer_remaining -= count; FIX_BUFFER_POINTERS(hc, count) } /************************************************************************/ /* /* gdb_write_data /* /* This routine implements gdb_move_data for an outbound half /* connection. /* /************************************************************************/ int gdb_write_data(hc) struct half_con_data *hc; /* pointer to control struct */ /* for this half connection */ { register HALF_CONNECTION half_con = hc; /* half connection pointer */ /* fast copy in register */ register int count; /* number of bytes read */ /* or written in latest */ /* attempt */ fd_set *fdbits; /* the mask we should adjust */ /* for this direction */ fd_set tst_bits; /* these are used for */ /* the select we do prior */ /* to reading which tells */ /* us whether 0 byte read */ /* means empty or closed */ int selected; /* TRUE iff select says */ /* we should be able to */ /* progress */ /* * Loop writing to the socket until it claims that no more * progress can be made. Note that some versions of Unix report * socket failure by select = 1, write count = 0. To avoid * extra selects, we try the write first, and only do the select/write * sequence if write seems not to be progressing. */ FD_ZERO(&tst_bits); while(half_con->remaining>0) { count = write(half_con->fd, half_con->next_byte, (int)min(half_con->remaining, GDB_MAX_SOCK_WRITE)); if (count == 0) { FD_SET(half_con->fd,&tst_bits); selected = select(gdb_mfd, (fd_set *)NULL, &tst_bits, (fd_set *)NULL, &gdb_notime); if (selected == (-1)) { gdb_conok = FALSE; break; } if (selected == 0) { count =0; break; } count = write(half_con->fd, half_con->next_byte, (int)min(half_con->remaining, GDB_MAX_SOCK_WRITE)); if (count==0) { if (selected == 1) gdb_conok = FALSE; break; /* no more data available now*/ } } /* * Count is != 0 */ if (count<0) { count = 0; if (errno != EWOULDBLOCK) { gdb_conok = FALSE; /* tell callers */ /* that con has */ /* died */ } break; } half_con->flags |= HCON_PROGRESS; FIX_BUFFER_POINTERS(half_con, count) } /* * The file descriptor masks used for doing selects must be activated * when and only when there is a pending operation trying to use * the connection. Update the masks for this half connection. */ fdbits = &gdb_cwfds; if (half_con->remaining >0 && gdb_conok) FD_SET(half_con->fd, fdbits); else FD_CLR(half_con->fd, fdbits); return; } /************************************************************************/ /* /* /* gdb_read_data_and_buffer /* /* This routine is called only when the half_connection stream /* buffer is known to be empty and the "next-byte" buffer /* has more to be filled in. We try in one read to finish /* off the user's request and at the same time fill the stream /* buffer for later. /* /* We assume here that all fd's are set to non-blocking I/O, so /* we can safely try reading and writing until they return 0 bytes. /* /************************************************************************/ int gdb_read_data_and_buffer(hc) struct half_con_data *hc; /* pointer to control struct */ /* for this half connection */ { register HALF_CONNECTION half_con = hc; /* half connection pointer */ /* fast copy in register */ register int count; /* number of bytes read */ /* or written in latest */ /* attempt */ fd_set *fdbits; /* the mask we should adjust */ /* for this direction */ struct iovec iov[2]; /* we use this to hold */ /* pointers to (1) the */ /* actual user data buffer */ /* and (2) the pipe length */ /* pre-read buffer */ int fix_amount; /* amount to adjust */ /* half_con->remaining*/ /*----------------------------------------------------------*/ /* /* Mark the stream buffer as empty, in case we don't /* get around to filling it. /* /*----------------------------------------------------------*/ half_con -> stream_buffer_next = half_con -> stream_buffer; half_con -> stream_buffer_remaining = 0; /*----------------------------------------------------------*/ /* /* Loop trying to read data from the socket. We scatter /* first into the user's buffer directly, then into /* the stream buffer (which helps us save system /* calls next time around.) We stop either when: /* socket reports error/no progress or user's buffer is /* full. /* /*----------------------------------------------------------*/ /* * Loop until either (1) the connection reported that it could * not progress any further or (2) the full count has been * satisfied. Some versions of Unix observe the rule that * a closed connection, especially when reading, is indicated * by returning a count of 0 on read when select claims that progress * can be made. We used to handle this case. Bill Sommerfeld * has introduced a performance change which leaves that checking * out in the latest version. To add it back, then ONLY in * the case where read returned 0, do a select followed by another * read (the order is important). If we ever run on a system that * works in this way, we may hang at close time. */ while(half_con->remaining>0) { /* * First we try a read, and if it works, we believe it */ iov[0].iov_base = half_con -> next_byte; iov[0].iov_len = half_con -> remaining; iov[1].iov_base = half_con -> stream_buffer; iov[1].iov_len = half_con -> stream_buffer_length; count = readv(half_con->fd, iov, 2); if (count<0) { count = 0; if (errno != EWOULDBLOCK) gdb_conok = FALSE; /* tell callers that */ /* con has died */ break; } if (count == 0) {/* We hit EOF */ gdb_conok = FALSE; break; } /* * Count is >0, we moved some data. Note, setting of * stream_buffer_remaining can only be non-zero on last * time through the loop, because that will be when * half_con->remaining goes to zero. */ half_con->flags |= HCON_PROGRESS; half_con->stream_buffer_remaining=max(0, count-iov[0].iov_len); fix_amount = min(count,half_con->remaining); FIX_BUFFER_POINTERS(half_con, fix_amount); } /* * The file descriptor masks used for doing selects must be activated * when and only when there is a pending operation trying to use * the connection. Update the masks for this half connection. */ fdbits = &gdb_crfds; if (half_con->remaining >0) FD_SET(half_con->fd, fdbits); else FD_CLR(half_con->fd, fdbits); return ; } /************************************************************************/ /* /* gdb_receive_data (gdb_receive_data) /* /* This routine is called by an init or continuation routine to /* request that a specified amount of data be read, without /* blocking, on the supplied connection. This routine returns /* OP_COMPLETE if the entire read completed synchronously, /* or OP_RUNNING if the read remains ongoing or is cancelling /* due to error on the socket. /* /************************************************************************/ int gdb_receive_data(half_con, ptr, len) HALF_CONNECTION half_con; /* read on this connection*/ char *ptr; /* put first byte here */ int len; /* number of bytes to read */ { /* * Fill in the initial state of the attempted receive */ half_con->remaining = len; half_con->next_byte = ptr; /* * Now see if we can make some progress on this read, possibly * even completing it synchronously. Return appropriate * result to our caller. Note: errors are reflected as OP_RUNNING * with global variable gdb_cnok set to FALSE. */ if(gdb_move_data(CON_INPUT, half_con)) return OP_COMPLETE; else return OP_RUNNING; } /************************************************************************/ /* /* gdb_send_data (gdb_send_data) /* /* This routine is called by an init or continuation routine to /* request that a specified amount of data be written, without /* blocking, on the supplied connection. This routine returns /* OP_COMPLETE if the entire write completed synchronously, /* or OP_RUNNING if the output remains ongoing or there was an error. /* /************************************************************************/ int gdb_send_data(half_con, ptr, len) HALF_CONNECTION half_con; /* write on this connection*/ char *ptr; /* put first byte here */ int len; /* number of bytes to read */ { /* * Fill in the initial state of the attempted receive */ half_con->remaining = len; half_con->next_byte = ptr; /* * Now see if we can make some progress on this read, possibly * even completing it synchronously. Return appropriate * result to our caller. */ if(gdb_move_data(CON_OUTPUT, half_con)) return OP_COMPLETE; else return OP_RUNNING; } /************************************************************************/ /* /* gdb_start_a_listen /* /* This routine is called by an init or continuation routine to /* request that a connection be done. This routine returns /* OP_COMPLETE if the accept completed synchronously, /* or OP_RUNNING if the output remains ongoing or there was an error. /* /************************************************************************/ int gdb_start_a_listen(half_con, otherside, lenp, fdp) HALF_CONNECTION half_con; /* write on this connection*/ char *otherside; /* put first byte here */ int *lenp; /* number of bytes to read */ int *fdp; { /* * Fill in the initial state of the attempted accept */ half_con->accepted_len = lenp; half_con->next_byte = otherside; half_con->accepted_fdp = fdp; /* * Now see if we can make some progress on this read, possibly * even completing it synchronously. Return appropriate * result to our caller. */ if(gdb_listen(half_con)) return OP_COMPLETE; else return OP_RUNNING; } /************************************************************************/ /* /* gdb_listen (gdb_listen) /* /* This routine is called from gdb_start_a_listen or hcon_progress to attempt /* to continue making progress in accepting a connection on a /* listening connection. /* /************************************************************************/ int gdb_listen(hc) struct half_con_data *hc; /* pointer to control struct */ /* for this half connection */ { register HALF_CONNECTION half_con = hc; /* half connection pointer */ /* fast copy in register */ GDB_INIT_CHECK half_con->flags &= ~HCON_PENDING_LISTEN;/* in case we succeed */ /* * The first implementatin of this used to do a select to make sure * that the accept would not block. Bill Sommerfeld has changed this * to non-blocking I/O, so the following code is commented out. */ #ifdef notdef FD_ZERO(&tst_bits); FD_SET(half_con->fd,&tst_bits); selected = select(gdb_mfd,&tst_bits, (fd_set *)NULL, (fd_set *)NULL, &gdb_notime); /* * If selected==(-1), then we know there's something * wrong with the socket */ if (selected == (-1)) { gdb_conok = FALSE; return FALSE; } /* * if selected==0, then we know accept won't do anything, so * don't try. */ if (selected == 0) { half_con->flags |= HCON_PENDING_LISTEN; FD_SET(half_con->fd, &gdb_crfds); /* we'll be looking for */ /* this whenever we select*/ return FALSE; } /* * Selected is >0. The accept SHOULD not hang. */ #endif /* * Here is Bill's non-blocking implementation */ *(half_con->accepted_fdp) = accept(half_con->fd, (struct sockaddr *)half_con->next_byte, half_con->accepted_len); /* * See whether the accept succeeded */ if (*(half_con->accepted_fdp) < 0) { if (errno != EWOULDBLOCK) { gdb_conok = FALSE; /* error will be returned */ /* in shut-down listening con*/ } half_con->flags |= HCON_PENDING_LISTEN; FD_SET(half_con->fd, &gdb_crfds); return FALSE; } FD_CLR(half_con->fd, &gdb_crfds); /* don't select on this */ return TRUE; }