Пример #1
0
static bool SetupStream( Stream *stream, MCB *mcb )
{
	IOCReply1 *rep = (IOCReply1 *)mcb->Control;
	Port server = mcb->MsgHdr.Reply;

	if( (mcb->MsgHdr.ContSize*sizeof(word)) < (sizeof(IOCReply1)-sizeof(word)) ) return false;
		
	stream->Type = rep->Type;
	stream->Flags = Flags_Stream|rep->Flags;

	if ( server == NullPort )
	{
		FreePort( stream->Server );
		stream->Server = rep->Object;
	}
	else 
	{
		if( stream->Server != server ) FreePort(stream->Server);
		stream->Server = server;
		stream->Flags |= Flags_Server;

		/* pipes do not have a server despite returning a port on open */
		if( stream->Type == Type_Pipe ) stream->Flags ^= Flags_Server;

		if( (server & Port_Flags_Remote) != 0 )
			stream->Flags |= Flags_Remote;
	}

	stream->Access = rep->Access;
	stream->Reply = mcb->MsgHdr.Dest;
	if(mcb->MsgHdr.FnRc >= Err_Null ) stream->FnMod = mcb->MsgHdr.FnRc;
	stream->Timeout = stream->Flags&Flags_Fast?mcb->Timeout:IOCTimeout;


	strcpy(stream->Name,&mcb->Data[rep->Pathname]);
	
	if( (stream->Type == Type_Pseudo) && (stream->Flags & Flags_OpenOnGet) )
		ReOpen(stream);	

	return true;
}
Пример #2
0
PUBLIC WORD Refine(Object *object, AccMask mask)
{
	word rc = Err_Null;
	MCB *mcb ;
	IOCReply2 *rep;
	Port reply;

#ifdef SYSDEB
	SysDebug(ioc)("Refine(%O,%A)",object,mask);
#endif

	if( (rc = CheckObject(object,C_Locate)) != Err_Null ) return rc;

	reply = object->Reply;
	
	mcb = NewMsgBuf(0);

	InitMCB(mcb,MsgHdr_Flags_preserve,
		MyTask->IOCPort,reply,FC_GSP+FG_Refine|object->FnMod);

	MarshalCommon(mcb,object,NULL);
	MarshalWord(mcb,mask);

	if( (rc = IOCMsg(mcb,NULL)) < Err_Null ) goto Done;

	rep = (IOCReply2 *)mcb->Control;

	object->Access = rep->Cap;	/* copy new capability in */

	rc = Err_Null;

    Done:

#ifdef SYSDEB
	SysDebug(ioc)("Refine: %E",rc);
	if( mcb->MsgHdr.Reply != NullPort ) SysDebug(error)("Refine: Non-Null Reply port %x",mcb->MsgHdr.Reply);
#endif
	if( mcb->MsgHdr.Reply != NullPort ) FreePort(mcb->MsgHdr.Reply);
	FreeMsgBuf(mcb);

	object->Result2 = rc;
	
	return rc;
}
Пример #3
0
PUBLIC WORD
SetDate(
	Object *	object,
	STRING		name,
	DateSet *	dates )
{
	word rc = Err_Null;
	MCB *mcb ;
	Port reply;

#ifdef SYSDEB
	SysDebug(ioc)("SetDate(%O,%N)",object,name);
#endif

	if( (rc = CheckObject(object,C_Locate)) != Err_Null ) return rc;

	reply = object->Reply;
	
	mcb = NewMsgBuf(0);

	InitMCB(mcb,MsgHdr_Flags_preserve,
		MyTask->IOCPort,reply,FC_GSP+FG_SetDate|object->FnMod);

	MarshalCommon(mcb,object,name);
	MarshalDate(mcb,dates->Creation);
	MarshalDate(mcb,dates->Access);
	MarshalDate(mcb,dates->Modified);

	if( (rc = IOCMsg(mcb,NULL)) < Err_Null ) goto Done;

	rc = Err_Null;

    Done:
#ifdef SYSDEB
	SysDebug(ioc)("SetDate: %E",rc);
	if( mcb->MsgHdr.Reply != NullPort ) SysDebug(error)("SetDate: Non-Null Reply port %x",mcb->MsgHdr.Reply);
#endif
	if( mcb->MsgHdr.Reply != NullPort ) FreePort(mcb->MsgHdr.Reply);
	FreeMsgBuf(mcb);

	object->Result2 = rc;
	
	return rc;
}
Пример #4
0
PUBLIC WORD
Link(
     Object *	object,
     string	name,
     Object *	to )
{
	word rc = Err_Null;
	MCB *mcb ;
	Port reply;
#ifdef SYSDEB
	SysDebug(ioc)("Link(%O,%N,%O)",object,name,to);
#endif

	if( (rc = CheckObject(object,C_Locate)) != Err_Null ) return rc;

	reply = object->Reply;
	
	mcb = NewMsgBuf(0);

	InitMCB(mcb,MsgHdr_Flags_preserve,
		MyTask->IOCPort,reply,FC_GSP+FG_Link|object->FnMod);

	MarshalCommon(mcb,object,name);
	MarshalString(mcb,to->Name);
	MarshalCap(mcb,&to->Access);

	if( (rc = IOCMsg(mcb,NULL)) < Err_Null ) goto Done;

	rc = Err_Null;

    Done:
#ifdef SYSDEB
	SysDebug(ioc)("Link: %E",rc);
	if( mcb->MsgHdr.Reply != NullPort ) SysDebug(error)("Link: Non-Null Reply port %x",mcb->MsgHdr.Reply);
#endif
	if( mcb->MsgHdr.Reply != NullPort ) FreePort(mcb->MsgHdr.Reply);

	FreeMsgBuf(mcb);

	object->Result2 = rc;
	
	return rc;
}
Пример #5
0
PUBLIC WORD
ObjectInfo(
	   Object *	object,
	   STRING	name,
	   byte *	info )
{
	word rc = Err_Null;
	MCB *mcb ;
	Port reply;

#ifdef SYSDEB
	SysDebug(ioc)("ObjectInfo(%O,%N,%P)",object,name,info);
#endif

	if( (rc = CheckObject(object,C_Locate)) != Err_Null ) return rc;
	
	reply = object->Reply;
	
	mcb = NewMsgBuf(0);

	InitMCB(mcb,MsgHdr_Flags_preserve,
		MyTask->IOCPort,reply,FC_GSP+FG_ObjectInfo|object->FnMod);

	MarshalCommon(mcb,object,name);

	if( (rc = IOCMsg(mcb,info)) < Err_Null ) goto Done;

	rc = Err_Null;

    Done:
#ifdef SYSDEB
	SysDebug(ioc)("ObjectInfo: %E infosize %d",rc,mcb->MsgHdr.DataSize);
	if( mcb->MsgHdr.Reply != NullPort ) SysDebug(error)("ObjectInfo: Non-Null Reply port %x",mcb->MsgHdr.Reply);	
#endif
	if( mcb->MsgHdr.Reply != NullPort ) FreePort(mcb->MsgHdr.Reply);

	FreeMsgBuf(mcb);

	object->Result2 = rc;
	
	return rc;
	
}
Пример #6
0
PUBLIC WORD
Protect(
	Object *	object,
	STRING		name,
	Matrix		matrix )
{
	word rc = Err_Null;
	MCB *mcb ;
	Port reply;

#ifdef SYSDEB
	SysDebug(ioc)("Protect(%O,%N,%X)",object,name,matrix);
#endif

	if( (rc = CheckObject(object,C_Locate)) != Err_Null ) return rc;

	reply = object->Reply;
	
	mcb = NewMsgBuf(0);

	InitMCB(mcb,MsgHdr_Flags_preserve,
		MyTask->IOCPort,reply,FC_GSP+FG_Protect|object->FnMod);

	MarshalCommon(mcb,object,name);
	MarshalWord(mcb,matrix);

	if( (rc = IOCMsg(mcb,NULL)) < Err_Null ) goto Done;

	rc = Err_Null;

    Done:
#ifdef SYSDEB
	SysDebug(ioc)("Protect: %E",rc);
	if( mcb->MsgHdr.Reply != NullPort ) SysDebug(error)("Protect: Non-Null Reply port %x",mcb->MsgHdr.Reply);
#endif
	if( mcb->MsgHdr.Reply != NullPort ) FreePort(mcb->MsgHdr.Reply);
	FreeMsgBuf(mcb);

	object->Result2 = rc;
	
	return rc;
}
Пример #7
0
PUBLIC INT Sync (IN STRING PathTo)

/******************************************************************************
**
**  PURPOSE:
**    Locates the file server with <PathTo> and sends a "private" message to the
**    server to signal a file system synchronisation request.
**
**  PARAMETERS:
**
**    In:
**      <PathTo>  Path to the file server
**
**  RETURN:
**    SyncOK      No error occured
**
**  EXAMPLE:
**
**    In:
**      <PathTo> = "/helios/bin/fs"
** 
**    Return:
**      SyncOK
**
*** endspec *******************************************************************/

{
  MCB   Mcb;
  word  E;
  Port  Reply;
  word  ControlV [IOCMsgMax];
  byte  DataV [IOCDataMax];
 
/******************************************************************************* 
**
**  -Get a port for the reply message
**  -Basic initialisation of the MessageControlBlock
**
*******************************************************************************/
 	
 Reply = NewPort ();			

 InitMCB (&Mcb, MsgHdr_Flags_preserve, MyTask->IOCPort, Reply, FC_GSP | SS_HardDisk | FG_Private | FO_ForceSync);

/******************************************************************************* 
**
**  -Preparing control and data vector
**
*******************************************************************************/

  Mcb.Control = ControlV;	
  Mcb.Data    = DataV; 	   
  MarshalCommon (&Mcb, Null (Object), PathTo);

/******************************************************************************* 
**
**  -Send the message to the server
**  -Expect the server's reply (infinite wait)
**  -Release the port
**  -Normal termination
**
*******************************************************************************/
					
 E = PutMsg (&Mcb);

 InitMCB (&Mcb, MsgHdr_Flags_preserve, Reply, NullPort, 0);
 Mcb.Timeout = MaxInt;
 GetMsg (&Mcb);

 FreePort (Reply);
 
 return (SyncOK);
}
Пример #8
0
PUBLIC Stream *
Open(
     Object *	object,
     string	name,
     word	mode )
{
	word rc = Err_Null;
	Stream *stream = NULL;
	MCB *mcb;
	IOCReply1 *rep;
	word stlen;
	Port reply;

#ifdef SYSDEB
	SysDebug(ioc)("Open(%O,%N,%x)",object,name,mode);
#endif

	if( CheckObject(object,C_Locate) != Err_Null ) return Null(Stream);

	reply = NewPort();

	mcb = NewMsgBuf(0);
	rep = (IOCReply1 *)mcb->Control;

	InitMCB(mcb,MsgHdr_Flags_preserve,
		MyTask->IOCPort,reply,FC_GSP|FG_Open|object->FnMod);

	MarshalCommon(mcb,object,name);

	MarshalWord(mcb,mode);

	if( (rc = IOCMsg(mcb,NULL)) < Err_Null ) goto Done;

	stlen = sizeof(Stream) + (word)strlen(mcb->Data+rep->Pathname) + SafetyMargin;

	stream = (Stream *)Malloc(stlen);

	if( stream == NULL ) 
	{
		rc = EC_Error|SS_SysLib|EG_NoMemory|EO_Stream;
		goto Done;
	}
	else memclr( (void *)stream, (int)stlen );

	if( SetupStream( stream, mcb ) )
	{
		stream->Flags |= mode&Flags_SaveMode;
		InitSemaphore( &stream->Mutex, 1 );
		stream->Pos = 0;
	}

	AddStream( stream );	

	rc = Err_Null;
	
	if( mode & Flags_Append ) Seek(stream, S_End, 0);
    Done:
#ifdef SYSDEB
	SysDebug(ioc)("Open: %E stream: %S",rc,stream);
#endif
	FreeMsgBuf(mcb);

	if( rc < Err_Null ) FreePort(reply);

	object->Result2 = rc;
	return stream;
}
Пример #9
0
PUBLIC Object *
Create(
       Object *	object,
       string	name,
       word	type,
       word	size,
       byte *	data )
{
	word rc = Err_Null;
	Object *obj = Null(Object);
	MCB *mcb;
	IOCReply1 *rep;
	word oblen;
	Port reply;

#ifdef SYSDEB
	SysDebug(ioc)("Create(%O,%N,%T,%d,%P)",object,name,type,size,data);
#endif

	if ( CheckObject(object,C_Locate) != Err_Null )
	  {
	    return NULL;
	  }

	reply = NewPort();

	mcb = NewMsgBuf(0);
	rep = (IOCReply1 *)mcb->Control;
	
	InitMCB(mcb,MsgHdr_Flags_preserve,
		MyTask->IOCPort,reply,FC_GSP|FG_Create|object->FnMod);

	MarshalCommon(mcb,object,name);

	MarshalWord(mcb,type);
	MarshalWord(mcb,size);
	MarshalOffset(mcb);
	MarshalData(mcb,size,data);

	mcb->Timeout = object->Timeout;
	
	/* IOdebug( "Create: sending message" ); */
	
	if ( (rc = IOCMsg(mcb, NULL)) < Err_Null )
	  {
	    /* IOdebug( "Create: message send failed" ); */
	    
	    goto Done;
	  }
	
	/* IOdebug( "Create: message sent" ); */
	
	oblen = sizeof(Object) + (word)strlen(mcb->Data+rep->Pathname) + SafetyMargin;

	obj = (Object *)Malloc(oblen);
	
	if ( obj == NULL )
	  {
	    rc = EC_Error|SS_SysLib|EG_NoMemory|EO_Object;
		
	    goto Done;
	  }	
	else memclr( (void *)obj, (int)oblen );

	obj->Type    = rep->Type;
	obj->Flags   = rep->Flags;
	obj->Access  = rep->Access;
	obj->Reply   = reply;
	obj->FnMod   = rc & SS_Mask;
	obj->Timeout = IOCTimeout;
	
	strcpy(obj->Name,mcb->Data+rep->Pathname);

	AddObject( obj );

	rc = Err_Null;

    Done:
#ifdef SYSDEB
	SysDebug(ioc)("Create: %E object: %O",rc,obj);
	if( mcb->MsgHdr.Reply != NullPort ) SysDebug(error)("Create: Non-Null Reply port %x",mcb->MsgHdr.Reply);
#endif
	if( mcb->MsgHdr.Reply != NullPort ) FreePort(mcb->MsgHdr.Reply);

	FreeMsgBuf(mcb);

	if( rc < Err_Null ) FreePort(reply);

	object->Result2 = rc;
	
	return obj;	
}
Пример #10
0
PUBLIC Object *
Locate(
       Object *	object,
       STRING	name )
{
	word rc = Err_Null;
	Object *obj = Null(Object);
	MCB *mcb;
	IOCReply1 *rep;
	word oblen;
	Port reply;
	word fnmod = 0;


#ifdef SYSDEB
	SysDebug(ioc)("Locate(%O,%N)",object,name);
#endif
	/* Locate can be called with a null object pointer */
	
	if( object != NULL ) 
	{	
		if( CheckObject(object,C_Locate) != Err_Null )
		  {
		    return NULL;
		  }
		
		fnmod = object->FnMod;
	}

	reply = NewPort();

	mcb = NewMsgBuf(0);
	rep = (IOCReply1 *)mcb->Control;

	InitMCB(mcb,MsgHdr_Flags_preserve,
		MyTask->IOCPort,reply,FC_GSP|FG_Locate|fnmod);

	MarshalCommon(mcb,object,name);

	if( (rc = IOCMsg(mcb,NULL)) < Err_Null )
	  {
	    goto Done;
	  }

	oblen = sizeof(Object) + (word)strlen(mcb->Data+rep->Pathname) + SafetyMargin;

	obj = (Object *)Malloc(oblen);
	
	if( obj == NULL )
	{
		rc = EC_Error|SS_SysLib|EG_NoMemory|EO_Object;

		goto Done;
	}
	else memclr( (void *)obj, (int)oblen );

	obj->Type    = rep->Type;
	obj->Flags   = rep->Flags;
	obj->Access  = rep->Access;
	obj->Reply   = reply;
	obj->FnMod   = rc & SS_Mask;
	obj->Timeout = IOCTimeout;
	
	strcpy(obj->Name,mcb->Data+rep->Pathname);

	AddObject( obj );

	rc = Err_Null;

    Done:
#ifdef SYSDEB
	SysDebug(ioc)("Locate: %E object: %O",rc,obj);
	if( mcb->MsgHdr.Reply != NullPort ) SysDebug(error)("Locate: Non-Null Reply port %x",mcb->MsgHdr.Reply);
#endif
	if( mcb->MsgHdr.Reply != NullPort ) FreePort(mcb->MsgHdr.Reply);

	FreeMsgBuf(mcb);

	if( object != Null(Object) ) object->Result2 = rc;

	if( rc < Err_Null ) FreePort(reply);

	return obj;
}
Пример #11
0
int 
main ( int argc, char *argv[] )
{
    char	*tname;
    char	wmname[100];
    MCB		m;
    word	e;
    word	Control_V[IOCMsgMax];
    byte	Data_V[IOCDataMax];
    Port	reply;
  
    					/* Check args for plausibility	*/
    if ( argc == 1 )
    {
    	strncpy ( wmname, Heliosno ( stdin )->Name, 99 );
    	wmname[99] = '\0';
    	* ( strrchr ( wmname, c_dirchar ) ) = '\0';
    }
    else
    {
    	strncpy ( wmname, argv[1], 99 );
    	wmname[99] = '\0';
    	if ( argc > 2 )
 	fprintf (stderr, "%s : Further arguments are ignored !\n",
 		 argv[0] );
    }
    
/*    printf ( "%s: window server is \"%s\".\n", argv[0], wmname );	*/
        
 /*-----------------  Prepare MCB for marshalling  ---------------------*/
 

    reply = NewPort ();			/* Basic initialisation of the	*/
					/* MesssageControlBlock		*/
    InitMCB ( &m, MsgHdr_Flags_preserve, MyTask->IOCPort, reply,
 	   FC_GSP + FG_Terminate);
 	   				/* Preparing control and data	*/
    m.Control = Control_V;		/* vector			*/
    m.Data    = Data_V; 	   
    MarshalCommon ( &m, Null ( Object ), wmname );          
    MarshalString ( &m, tname );

/*    printf ( "%s sending request.\n", argv[0] );			*/
/*    fflush ( stdout );						*/
    
    e = PutMsg ( &m );			/* Send message to the server	*/
    if ( e != Err_Null )
    {
 	fprintf (stderr, "%s : Can't send message to server %s :%x\n",
 		 argv[0], wmname, e);
 	return 1;
    }
 					/* Wait for reply		*/
 					/* from the window server...	*/
    InitMCB ( &m, MsgHdr_Flags_preserve, reply, NullPort, 0 );
    m.Timeout = MaxInt;

/*    printf ( "%s waiting for reply.\n", argv[0] );			*/
/*    fflush ( stdout );						*/
    
    e = GetMsg ( &m );
    FreePort ( reply );
 
    if ( m.MsgHdr.FnRc == FC_GSP + SS_Window + FG_Terminate )
	return 0;

    else
    {
 	fprintf (stderr,"%s: Failed to terminate %s - %08x\n",
 		 argv[0], wmname, m.MsgHdr.FnRc ); 
	return 1;
    }
}
Пример #12
0
static void tcpThread(void *context)
{
	Socket *sock = (Socket*) context;
	TCPSocket *tcpsock = (TCPSocket*) context;
	Semaphore *sems[4] = {&tcpsock->semConnected, &tcpsock->semStop, &tcpsock->semAck, &tcpsock->semAckOut};
	Semaphore *semsOut[3] = {&tcpsock->semStop, &tcpsock->semAckOut, &tcpsock->semSendFetch};

	detachMe();
	
	uint16_t srcport, dstport;
	if (sock->domain == AF_INET)
	{
		const struct sockaddr_in *inaddr = (const struct sockaddr_in*) &tcpsock->peername;
		struct sockaddr_in *inname = (struct sockaddr_in*) &tcpsock->sockname;
		srcport = inname->sin_port;
		dstport = inaddr->sin_port;
	}
	else
	{
		const struct sockaddr_in6 *inaddr = (const struct sockaddr_in6*) &tcpsock->peername;
		struct sockaddr_in6 *inname = (struct sockaddr_in6*) &tcpsock->sockname;
		inname->sin6_family = AF_INET6;
		srcport = inname->sin6_port;
		dstport = inaddr->sin6_port;
	};

	int connectDone = 0;
	if (tcpsock->state == TCP_ESTABLISHED)
	{
		sems[0] = NULL;
		connectDone = 1;
	};
	int wantExit = 0;
	while (1)
	{
		if (wantExit) break;
		
		uint64_t deadline = getNanotime() + sock->options[GSO_SNDTIMEO];
		if (sock->options[GSO_SNDTIMEO] == 0)
		{
			deadline = 0;
		};
		
		int sendOK = 0;
		int retransCount = 16;
		while (((getNanotime() < deadline) || (deadline == 0)) && (retransCount--))
		{
			tcpsock->currentOut->segment->ackno = htonl(tcpsock->nextAckNo);
			ChecksumOutbound(tcpsock->currentOut);
			
			int status = sendPacketEx(&tcpsock->sockname, &tcpsock->peername,
							tcpsock->currentOut->segment, tcpsock->currentOut->size,
							IPPROTO_TCP, sock->options, sock->ifname);

			if (status != 0)
			{
				tcpsock->sockErr = -status;

				if (tcpsock->state == TCP_CONNECTING)
				{
					semSignal(&tcpsock->semConnected);
				};
			
				tcpsock->state = TCP_TERMINATED;
				kfree(tcpsock->currentOut);
				wantExit = 1;
				break;
			};
			
			uint8_t bitmap = 0;
			if (semPoll(4, sems, &bitmap, 0, TCP_RETRANS_TIMEOUT) == 0)
			{
				continue;
			};
			
			if (bitmap & (1 << 1))
			{
				kfree(tcpsock->currentOut);
				tcpsock->state = TCP_TERMINATED;
				wantExit = 1;
				break;
			};
			
			if (bitmap & (1 << 2))
			{
				semWait(&tcpsock->semAck);
				sendOK = 1;
				break;
			};
			
			if (bitmap & (1 << 3))
			{
				semWaitGen(&tcpsock->semAckOut, -1, 0, 0);
			};
		};
		
		if (wantExit) break;
		
		int wasFin = tcpsock->currentOut->segment->flags & TCP_FIN;
		kfree(tcpsock->currentOut);
		
		if (!sendOK)
		{
			tcpsock->sockErr = ETIMEDOUT;
			
			if (tcpsock->state == TCP_CONNECTING)
			{
				semSignal(&tcpsock->semConnected);
			};
			
			tcpsock->state = TCP_TERMINATED;
			return;
		};
		
		if (!connectDone)
		{
			while ((getNanotime() < deadline) || (deadline == 0))
			{
				uint8_t bitmap = 0;
				semPoll(3, sems, &bitmap, 0, sock->options[GSO_SNDTIMEO]);
			
				if (bitmap & (1 << 1))
				{
					tcpsock->state = TCP_TERMINATED;
					return;
				};
				
				if (bitmap & (1 << 0))
				{
					connectDone = 1;
					break;
				};
			};

			if (!connectDone)
			{
				tcpsock->sockErr = ETIMEDOUT;
				semSignal(&tcpsock->semConnected);
				tcpsock->state = TCP_TERMINATED;
				return;
			};

			sems[0] = NULL;
			tcpsock->state = TCP_ESTABLISHED;
		};
		
		if (wasFin) break;
		
		while (1)
		{
			uint8_t bitmap = 0;
			semPoll(3, semsOut, &bitmap, 0, 0);
		
			if (bitmap & (1 << 2))
			{
				if (bitmap & (1 << 1))
				{
					semWaitGen(&tcpsock->semAckOut, -1, 0, 0);
				};
				
				int count = semWaitGen(&tcpsock->semSendFetch, 512, 0, 0);
				
				TCPOutbound *ob = CreateOutbound(&tcpsock->sockname, &tcpsock->peername, (size_t)count);
				ob->segment->srcport = srcport;
				ob->segment->dstport = dstport;
				ob->segment->seqno = htonl(tcpsock->nextSeqNo);
				// ackno filled in at the start of the loop iteration
				ob->segment->dataOffsetNS = 0x50;
				ob->segment->flags = TCP_PSH | TCP_ACK;
				// a count of zero means end of data, so send FIN.
				if (count == 0)
				{
					ob->segment->flags = TCP_FIN | TCP_ACK;
					tcpsock->nextSeqNo++;
				};
				ob->segment->winsz = htons(TCP_BUFFER_SIZE);
				uint8_t *put = (uint8_t*) &ob->segment[1];
				uint32_t size = (uint32_t)count;
				while (count--)
				{
					*put++ = tcpsock->bufSend[tcpsock->idxSendFetch];
					tcpsock->idxSendFetch = (tcpsock->idxSendFetch+1) % TCP_BUFFER_SIZE;
				};

				tcpsock->nextSeqNo += size;
				tcpsock->expectedAck = tcpsock->nextSeqNo;
				tcpsock->currentOut = ob;
				semSignal2(&tcpsock->semSendPut, (int)size);
				break;		// continues the outer loop
			};
			
			if (bitmap & (1 << 1))
			{
				semWaitGen(&tcpsock->semAckOut, -1, 0, 0);
			
				TCPOutbound *ob = CreateOutbound(&tcpsock->sockname, &tcpsock->peername, 0);
				TCPSegment *ack = ob->segment;
				
				ack->srcport = srcport;
				ack->dstport = dstport;
				ack->seqno = htonl(tcpsock->nextSeqNo);
				ack->ackno = htonl(tcpsock->nextAckNo);
				ack->dataOffsetNS = 0x50;
				ack->flags = TCP_ACK;
				ack->winsz = htons(TCP_BUFFER_SIZE);
				ChecksumOutbound(ob);
				
				sendPacketEx(&tcpsock->sockname, &tcpsock->peername,
						ob->segment, ob->size,
						IPPROTO_TCP, sock->options, sock->ifname);
				
				kfree(ob);
			};

			if (bitmap & (1 << 0))
			{
				tcpsock->state = TCP_TERMINATED;
				wantExit = 1;
				break;
			};
		};
	};

	// wait up to 4 minutes, acknowledging any packets if necessary (during a clean exit)
	uint64_t deadline = getNanotime() + 4UL * 60UL * 1000000000UL;
	uint64_t currentTime;
	Semaphore *semsClosing[2] = {&tcpsock->semStop, &tcpsock->semAckOut};
	while ((currentTime = getNanotime()) < deadline)
	{
		uint8_t bitmap = 0;
		semPoll(2, semsClosing, &bitmap, 0, deadline - currentTime);

		if (bitmap & (1 << 0))
		{
			wantExit = 1;
			semsClosing[0] = NULL;
		};
		
		if (bitmap & (1 << 1))
		{
			if (!wantExit)
			{
				semWaitGen(&tcpsock->semAckOut, -1, 0, 0);
	
				TCPOutbound *ob = CreateOutbound(&tcpsock->sockname, &tcpsock->peername, 0);
				TCPSegment *ack = ob->segment;
		
				ack->srcport = srcport;
				ack->dstport = dstport;
				ack->seqno = htonl(tcpsock->nextSeqNo);
				ack->ackno = htonl(tcpsock->nextAckNo);
				ack->dataOffsetNS = 0x50;
				ack->flags = TCP_FIN | TCP_ACK;
				ack->winsz = htons(TCP_BUFFER_SIZE);
				ChecksumOutbound(ob);
		
				sendPacketEx(&tcpsock->sockname, &tcpsock->peername,
						ob->segment, ob->size,
						IPPROTO_TCP, sock->options, sock->ifname);
		
				kfree(ob);
			};
		};
	};

	// wait for the socket to actually be closed by the application (in case it wasn't already)
	while (semWaitGen(&tcpsock->semSendFetch, 512, 0, 0) != 0);
	
	// free the port and socket
	FreePort(srcport);
	FreeSocket(sock);
};