示例#1
0
static void tunnel_release (tunnel_t *t)
{
    channel_close(&t->front);
    channel_close(&t->back);
    tunnel_init(t);
    PREPEND_STRUCT(IDLE_STRUCT_NAME(tunnel_t),t);
}
示例#2
0
static void
remmina_ssh_tunnel_close_all_channels (RemminaSSHTunnel *tunnel)
{
	int i;

	for (i = 0; i < tunnel->num_channels; i++)
	{
		close (tunnel->sockets[i]);
		remmina_ssh_tunnel_buffer_free (tunnel->socketbuffers[i]);
		channel_close (tunnel->channels[i]);
		channel_free (tunnel->channels[i]);
	}

	g_free(tunnel->channels);
	tunnel->channels = NULL;
	g_free(tunnel->sockets);
	tunnel->sockets = NULL;
	g_free(tunnel->socketbuffers);
	tunnel->socketbuffers = NULL;

	tunnel->num_channels = 0;
	tunnel->max_channels = 0;

	if (tunnel->x11_channel)
	{
		channel_close (tunnel->x11_channel);
		channel_free (tunnel->x11_channel);
		tunnel->x11_channel = NULL;
	}
}
static void handle_channel_msg(void * x) {
    Trap trap;
    ChannelNP * c = (ChannelNP *)x;
    int has_msg;

    assert(is_dispatch_thread());
    assert(c->magic == CHANNEL_MAGIC);
    assert(c->ibuf.handling_msg == HandleMsgTriggered);
    assert(c->ibuf.message_count);

    has_msg = ibuf_start_message(&c->ibuf);
    if (has_msg <= 0) {
        if (has_msg < 0 && c->chan.state != ChannelStateDisconnected) {
            trace(LOG_PROTOCOL, "Socket is shutdown by remote peer, channel %#lx %s", c, c->chan.peer_name);
            channel_close(&c->chan);
        }
    }
    else if (set_trap(&trap)) {
        if (c->chan.receive) {
            c->chan.receive(&c->chan);
        }
        else {
            handle_protocol_message(&c->chan);
            assert(c->out_bin_block == NULL);
        }
        clear_trap(&trap);
    }
    else {
        trace(LOG_ALWAYS, "Exception in message handler: %s", errno_to_str(trap.error));
        send_eof_and_close(&c->chan, trap.error);
    }
}
示例#4
0
void channel_init_for_debug(void)
{
	u8 i = 0, j = 0;

	_channel_config();
	
	//初始化血沉值
	for (i=0; i<MAX_CHANNELS; i++)
	{
		channel_open(i);  
		switch (i)
		{
		case 0:
			tubes[i].inplace = 1;
			break;
		case 1:
			tubes[i].inplace = 0;
			break;
		case 2:
			tubes[i].inplace = 0;
			break;
		case 3:
			tubes[i].inplace = 0;
			break;
#ifndef SMALL_MACHINE
	  	case 4:
			tubes[i].inplace = 0;
			break;
		case 5:
			tubes[i].inplace = 0;
			break;
		case 6:
			tubes[i].inplace = 0;
			break;
		case 7:
			tubes[i].inplace = 0;
			break;
		case 8:	
			tubes[i].inplace = 0;
			break;
		case 9:
			tubes[i].inplace = 0;
			break;
#endif
		}
		channel_close();
				
            if (tubes[i].inplace)
            {
                tubes[i].status = CHN_STATUS_WAITING;
			tubes[i].insert_time = rtc_get_sec();
            }
            else
                tubes[i].status = CHN_STATUS_NONE;
            
		tubes[i].remains = MAX_MEASURE_TIMES;
		for	(j=0; j<MAX_MEASURE_TIMES; j++)
			tubes[i].values[j] = 0;
	}
}
示例#5
0
/*
 * Deallocate a client context
 */
VOID portfwd_destroy_client(PortForwardClientContext *pcctx)
{
	// Remove the client from the list
	if (pcctx->prev)
		pcctx->prev->next = pcctx->next;
	else
		clients = pcctx->next;

	if (pcctx->next)
		pcctx->next->prev = pcctx->prev;

	// Close the socket/channel
	if (pcctx->clientFd)
		closesocket(pcctx->clientFd);
	if (pcctx->notify)
	{
		scheduler_remove_waitable(pcctx->notify);

		CloseHandle(pcctx->notify);
	}
	if (pcctx->channel)
		channel_close(pcctx->channel, pcctx->remote, NULL, 0, NULL);

	// Deallocate the context
	free(pcctx);
}
示例#6
0
文件: scp.c 项目: CUEBoxer/OpenStudio
int ssh_scp_close(ssh_scp scp){
  char buffer[128];
  int err;
  if(scp->channel != NULL){
    if(channel_send_eof(scp->channel) == SSH_ERROR){
      scp->state=SSH_SCP_ERROR;
      return SSH_ERROR;
    }
    /* avoid situations where data are buffered and
     * not yet stored on disk. This can happen if the close is sent
     * before we got the EOF back
     */
    while(!channel_is_eof(scp->channel)){
      err=channel_read(scp->channel,buffer,sizeof(buffer),0);
      if(err==SSH_ERROR)
        break;
    }
    if(channel_close(scp->channel) == SSH_ERROR){
      scp->state=SSH_SCP_ERROR;
      return SSH_ERROR;
    }
    channel_free(scp->channel);
    scp->channel=NULL;
  }
  scp->state=SSH_SCP_NEW;
  return SSH_OK;
}
示例#7
0
文件: gatmux.c 项目: Conjuror/ofono
gboolean g_at_mux_shutdown(GAtMux *mux)
{
	int i;

	if (mux->shutdown == TRUE)
		return FALSE;

	if (mux->channel == NULL)
		return FALSE;

	if (mux->read_watch > 0)
		g_source_remove(mux->read_watch);

	for (i = 0; i < MAX_CHANNELS; i++) {
		if (mux->dlcs[i] == NULL)
			continue;

		channel_close((GIOChannel *) mux->dlcs[i], NULL);
	}

	if (mux->driver->shutdown)
		mux->driver->shutdown(mux);

	mux->shutdown = TRUE;

	return TRUE;
}
示例#8
0
/*
 * Deallocates and cleans up the attributes of a socket context
 */
VOID free_socket_context(SocketContext *ctx)
{
	dprintf( "[TCP] free_socket_context. ctx=0x%08X", ctx );

	// Close the socket and notification handle
	if (ctx->fd){
		closesocket(ctx->fd);
		ctx->fd = 0;
	}
	
	if (ctx->channel) {
		channel_close(ctx->channel, ctx->remote, NULL, 0, NULL);
		ctx->channel = NULL;
	}

	if (ctx->notify)
	{
		dprintf( "[TCP] free_socket_context. remove_waitable ctx=0x%08X notify=0x%08X", ctx, ctx->notify);
		// The scheduler calls CloseHandle on our WSACreateEvent() for us
		scheduler_remove_waitable(ctx->notify);
		ctx->notify = NULL;
	}

	// Free the context
	free(ctx);
}
示例#9
0
文件: rpc.c 项目: Hooman3/minix
/*===========================================================================*
 *				rpc_close				     *
 *===========================================================================*/
void rpc_close(void)
{
/* Close the HGFS RPC backdoor channel.
 */

  channel_close(&rpc_chan);
}
示例#10
0
/*!
 * @brief Deallocates and cleans up the attributes of a tcp server socket context.
 * @param ctx Pointer to the context to free.
 */
VOID free_tcp_server_context(TcpServerContext * ctx)
{
    do
    {
        if (!ctx)
        {
            break;
        }

        dprintf("[TCP-SERVER] free_tcp_server_context. ctx=0x%08X", ctx);

        if (ctx->fd)
        {
            closesocket(ctx->fd);
            ctx->fd = 0;
        }

        if (ctx->channel)
        {
            channel_close(ctx->channel, ctx->remote, NULL, 0, NULL);
            ctx->channel = NULL;
        }

        if (ctx->notify)
        {
            scheduler_signal_waitable(ctx->notify, Stop);
            ctx->notify = NULL;
        }

        free(ctx);

    } while (0);
}
示例#11
0
/**
 * @brief Close and free a channel.
 *
 * @param channel       The channel to free.
 *
 * @warning Any data unread on this channel will be lost.
 */
void channel_free(CHANNEL *channel) {
  SSH_SESSION *session = channel->session;
  enter_function();

  if (channel == NULL) {
    leave_function();
    return;
  }

  if (session->alive && channel->open) {
    channel_close(channel);
  }

  /* handle the "my channel is first on session list" case */
  if (session->channels == channel) {
    session->channels = channel->next;
  }

  /* handle the "my channel is the only on session list" case */
  if (channel->next == channel) {
    session->channels = NULL;
  } else {
    channel->prev->next = channel->next;
    channel->next->prev = channel->prev;
  }

  buffer_free(channel->stdout_buffer);
  buffer_free(channel->stderr_buffer);

  /* debug trick to catch use after frees */
  memset(channel, 'X', sizeof(CHANNEL));
  SAFE_FREE(channel);

  leave_function();
}
示例#12
0
文件: xfer.c 项目: hdm/framework2
/*
 * File upload open completion handler for when a channel for a given file has
 * been opened
 */
DWORD file_upload_open_complete(Remote *remote, Channel *channel, 
		LPVOID context, DWORD result)
{
	FileUploadContext *ctx = (FileUploadContext *)context;
	DWORD res = ERROR_SUCCESS;
	BOOL textPrinted = TRUE;

	do
	{
		// If the result was not successful, no sense in continuing
		if ((!channel) || 
		    (result != ERROR_SUCCESS))
		{
			console_write_output(
					"\n"
					INBOUND_PREFIX " FS: file_upload_open failed, result %lu.\n",
					result);
			res = result;
			break;
		}

		// Try to open the local source file
		if (!(ctx->fd = fopen(ctx->source, "rb")))
		{
			console_write_output(
					"\n"
					"Error: Local file '%s' could not be opened for reading.\n",
					ctx->source);
			res = ERROR_FILE_NOT_FOUND;
			break;
		}

		textPrinted = FALSE;

		res = file_upload_write_complete(remote, channel, context,
				result, 1);

	} while (0);

	// If the result was not successful, clean up the context here
	if (res != ERROR_SUCCESS)
	{
		// Close the channel if it's valid
		if (channel)
			channel_close(channel, remote, NULL, 0, NULL);

		// Deallocate the passed in context
		if (ctx->fd)
			fclose(ctx->fd);

		free(ctx);
	}

	if (textPrinted)
		console_write_prompt();

	return res;
}
示例#13
0
/*
 * Callback for when data is available on the standard output handle of
 * a process channel that is interactive mode
 */
DWORD process_channel_interact_notify(Remote *remote, Channel *channel)
{

	ProcessChannelContext *ctx = (ProcessChannelContext *)channel->ops.stream.native.context;
	DWORD bytesRead, bytesAvail = 0;
	CHAR buffer[16384];
	DWORD result = ERROR_SUCCESS;

#ifdef _WIN32
	if( PeekNamedPipe( ctx->pStdout, NULL, 0, NULL, &bytesAvail, NULL ) )
	{
		if( bytesAvail )
		{
			if( ReadFile( ctx->pStdout, buffer, sizeof(buffer) - 1, &bytesRead, NULL ) )
			{
				return channel_write( channel, remote, NULL, 0, buffer, bytesRead, NULL );
			}
		}
		else
		{
			// sf: if no data is available on the pipe we sleep to avoid running a tight loop
			// in this thread, as anonymous pipes won't block for data to arrive.
			Sleep( 100 );
		}
	}
#else
	bytesRead = read ( ctx->pStdout, buffer, sizeof(buffer) - 1);

	if ( bytesRead > 0 ) 

	{
		dprintf("bytesRead: %d, errno: %d", bytesRead, errno);

		result = channel_write ( channel, remote, NULL, 0, buffer, bytesRead, NULL );
	} 

	if(bytesRead == -1) {
		if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
			errno = ERROR_SUCCESS;
		}
	}

	if(bytesRead == 0) {
		errno = ECONNRESET;
	}

	if(bytesRead <= 0) result = errno;

#endif
	if( GetLastError() != ERROR_SUCCESS )
	{
		dprintf("Closing down socket: errno: %d\n", errno);
		process_channel_close( channel, NULL, ctx );
		channel_close( channel, remote, NULL, 0, NULL );
	}

	return result;
}
示例#14
0
void * forward(void * ptr)
{
    int err, s, i = 0;
    msg_t tmp;
    msg_t vtmp[NB_MSG];

    while((err = channel_recv(chan_s,&tmp)) != 0)
    {
        if(err == -1)
        {
            perror("FWD - error channel_recv");
            channel_close(chan_s);
            channel_close(chan_r);
            break;
        }

        if(i < NB_MSG)
        {
            memcpy(&vtmp[i],&tmp,sizeof(msg_t));
            i++;
            continue;
        }
        else
        {
            do{
                s = channel_vsend(chan_r,vtmp,NB_MSG);

                if(s > -1)
                    n += s;

            }while(s == 0);

            i = 0;
        }

        if(n >= MAX_FWD_MSG)
        {
            channel_close(chan_s);
            channel_close(chan_r);
            break;
        }
    }

    pthread_exit(NULL);
}
示例#15
0
static void channel_check_all_for_debug(void)
{
    u8 i;
    
	for (i=0; i<MAX_CHANNELS; i++)
	{
	    if  (tubes[i].status != CHN_STATUS_NONE)
            continue;
        
		channel_open(i);
		switch (i)
		{
		case 0:
			tubes[i].inplace = 1;
			break;
		case 1:
			tubes[i].inplace = 0;
			break;
		case 2:
			tubes[i].inplace = 0;
			break;
		case 3:
			tubes[i].inplace = 0;
			break;
#ifndef SMALL_MACHINE
	  	case 4:
			tubes[i].inplace = 0;
			break;
		case 5:
			tubes[i].inplace = 0;
			break;
		case 6:
			tubes[i].inplace = 0;
			break;
		case 7:
			tubes[i].inplace = 0;
			break;
		case 8:
			tubes[i].inplace = 0;
			break;
		case 9:
			tubes[i].inplace = 0;
			break;
#endif
		}			
		channel_close();
        
        if (tubes[i].inplace)
        {
            tubes[i].status = CHN_STATUS_WAITING;
			tubes[i].insert_time = rtc_get_sec();
        }
		else
            tubes[i].status = CHN_STATUS_NONE;
	}
}
示例#16
0
void * receive(void * ptr)
{
    int err;
    msg_t tmp;
    channel_set chset;

    chset.chan = chan_r;
    chset.events = CHANNEL_EVENT_READ|CHANNEL_EVENT_CLOSE;

    while(1)
    {
        chset.revents = CHANNEL_EVENT_NOEVT;
        err = channel_select(&chset,1,-1);

        if(err == -1)
        {
            perror("recv - channel_select()");
            channel_close(chan_s);
            channel_close(chan_r);
            break;
        }
        else if(err == 0)
            continue;

        if(CHAN_READ_EVT(chset.revents))
        {
            err = channel_recv(chan_r,&tmp);
            if(err <= 0)
            {
                break;
            }
            /*else if(err > 0)
              printf("%s",tmp.content);*/
        }
        else if(CHAN_CLOSE_EVT(chset.revents))
        {
            channel_close(chan_r);
            break;
        }
    }

    pthread_exit(NULL);
}
示例#17
0
void
remmina_nx_session_free (RemminaNXSession *nx)
{
    pthread_t thread;

    if (nx->proxy_watch_source)
    {
        g_source_remove (nx->proxy_watch_source);
        nx->proxy_watch_source = 0;
    }
    if (nx->proxy_pid)
    {
        kill (nx->proxy_pid, SIGTERM);
        g_spawn_close_pid (nx->proxy_pid);
        nx->proxy_pid = 0;
    }
    thread = nx->thread;
    if (thread)
    {
        nx->running = FALSE;
        pthread_cancel (thread);
        pthread_join (thread, NULL);
        nx->thread = 0;
    }
    if (nx->channel)
    {
        channel_close (nx->channel);
        channel_free (nx->channel);
    }
    if (nx->server_sock >= 0)
    {
        close (nx->server_sock);
        nx->server_sock = -1;
    }

    g_free (nx->server);
    g_free (nx->error);
    g_hash_table_destroy (nx->session_parameters);
    g_string_free (nx->response, TRUE);
    g_free (nx->version);
    g_free (nx->session_id);
    g_free (nx->proxy_cookie);

    if (nx->session_list)
    {
        g_object_unref (nx->session_list);
        nx->session_list = NULL;
    }
    if (nx->session)
    {
        ssh_free (nx->session);
        nx->session = NULL;
    }
    g_free (nx);
}
示例#18
0
文件: sshclient.cpp 项目: aopui/gnash
void 
SSHClient::closeChannel(ssh_channel channel)
{
//    GNASH_REPORT_FUNCTION;

    if (channel) {
	channel_close(channel);
//	free(channel);
	_channel = 0;
    }
}
示例#19
0
文件: channel.c 项目: SLieng/nvm
/// Teardown the module
void channel_teardown(void)
{
  if (!channels) {
    return;
  }

  Channel *channel;

  map_foreach_value(channels, channel, {
    channel_close(channel->id, kChannelPartAll, NULL);
  });
示例#20
0
/*
 * Close a channel and free all its resources.
 */
    void
channel_free(channel_T *channel)
{
    channel_close(channel);
    if (channel->ch_next != NULL)
	channel->ch_next->ch_prev = channel->ch_prev;
    if (channel->ch_prev == NULL)
	first_channel = channel->ch_next;
    else
	channel->ch_prev->ch_next = channel->ch_next;
    vim_free(channel);
}
示例#21
0
static void connect_done(void * args, int error, Channel * c2) {
    ConnectInfo * info = (ConnectInfo *)args;
    Channel * c1 = info->c1;

    if (!is_channel_closed(c1)) {
        assert(c1->state == ChannelStateRedirectReceived);
        if (error) {
            fprintf(stderr, "cannot connect to peer: %s\n", dest_url);
            channel_close(c1);
        }
        else {
            proxy_create(c1, c2);
        }
    }
    else if (!error) {
        channel_close(c2);
    }
    channel_unlock(c1);
    peer_server_free(info->ps);
    loc_free(info);
}
示例#22
0
static void
remmina_ssh_tunnel_remove_channel (RemminaSSHTunnel *tunnel, gint n)
{
	channel_close (tunnel->channels[n]);
	channel_free (tunnel->channels[n]);
	close (tunnel->sockets[n]);
	remmina_ssh_tunnel_buffer_free (tunnel->socketbuffers[n]);
	tunnel->num_channels--;
	tunnel->channels[n] = tunnel->channels[tunnel->num_channels];
	tunnel->channels[tunnel->num_channels] = NULL;
	tunnel->sockets[n] = tunnel->sockets[tunnel->num_channels];
	tunnel->socketbuffers[n] = tunnel->socketbuffers[tunnel->num_channels];
}
示例#23
0
文件: main_lua.c 项目: eswartz/emul
static int lua_channel_close(lua_State *L)
{
    struct channel_extra *ce = NULL;

    assert(L == luastate);
    if(lua_gettop(L) != 1 || (ce = lua2channel(L, 1)) == NULL) {
        luaL_error(L, "wrong number or type of arguments");
    }
    trace(LOG_LUA, "lua_channel_close %p", ce->c);
    if(ce->c == NULL) luaL_error(L, "disconnected channel");
    channel_close(ce->c);
    return 0;
}
示例#24
0
void * sendm(void * ptr)
{
    channel_set chset;
    int err, nb = 0;
    msg_t tmp;

    tmp.pid = getpid();
    sprintf(tmp.content," %ld @ %d !\n",pthread_self(),tmp.pid);

    chset.chan = chan_s;
    chset.events = CHANNEL_EVENT_WRITE|CHANNEL_EVENT_CLOSE;
    chset.revents = CHANNEL_EVENT_NOEVT;

    while(nb < MAX_MSG)
    {
        chset.revents = CHANNEL_EVENT_NOEVT;

        err = channel_select(&chset,1,CHANNEL_TIME_WAIT);

        if(err == -1)
        {
            perror("channel_select()");
        }
        else if(err == 0)
            continue;

        if(CHAN_WRITE_EVT(chset.revents))
        {
            err = channel_send(chan_s,&tmp);

            if(err == -1)
            {
                if(errno == EWOULDBLOCK)
                    continue;

                perror("sendm channel_send()");
                channel_close(chan_s);
                break;
            }
            nb++;
        }

        if(CHAN_CLOSE_EVT(chset.revents))
            break;
    }

    pthread_exit(NULL);
}
示例#25
0
文件: rpc.c 项目: Hooman3/minix
/*===========================================================================*
 *				rpc_open				     *
 *===========================================================================*/
int rpc_open(void)
{
/* Open a HGFS RPC backdoor channel to the VMware host, and make sure that it
 * is working. Return OK upon success, or a negative error code otherwise; in
 * particular, return EAGAIN if shared folders are disabled.
 */
  int r;

  if ((r = channel_open(&rpc_chan, CH_OUT)) != OK)
	return r;

  r = rpc_test();

  if (r != OK)
	channel_close(&rpc_chan);

  return r;
}
示例#26
0
文件: tcp.c 项目: lizard007/msf3
/*
 * Deallocates and cleans up the attributes of a socket context
 */
VOID free_socket_context(SocketContext *ctx)
{
	// Close the socket and notification handle
	if (ctx->fd)
		closesocket(ctx->fd);
	if (ctx->notify)
	{
		scheduler_remove_waitable(ctx->notify);

		WSACloseEvent(ctx->notify);
	}

	if (ctx->channel)
		channel_close(ctx->channel, ctx->remote, NULL, 0, NULL);

	// Free the context
	free(ctx);
}
示例#27
0
static void connect_dest(void * x) {
    Channel * c1 = (Channel *)x;
    PeerServer * ps;
    ConnectInfo * info;

    ps = channel_peer_from_url(dest_url);
    if (ps == NULL) {
        trace(LOG_ALWAYS, "cannot parse peer url: %s", dest_url);
        channel_close(c1);
        return;
    }
    channel_lock(c1);
    c1->state = ChannelStateRedirectReceived;
    info = (ConnectInfo *)loc_alloc_zero(sizeof(ConnectInfo));
    info->ps = ps;
    info->c1 = c1;
    channel_connect(ps, connect_done, info);
}
示例#28
0
/*
 * Callback for when data is available on the standard output handle of
 * a process channel that is interactive mode
 */
DWORD process_channel_interact_notify(Remote *remote, LPVOID entryContext, LPVOID threadContext)
{
	Channel *channel = (Channel*)entryContext;
	ProcessChannelContext *ctx = (ProcessChannelContext *)threadContext;
	DWORD bytesRead, bytesAvail = 0;
	CHAR buffer[16384];
	DWORD result = ERROR_SUCCESS;

	if (!channel_exists(channel) || ctx == NULL)
	{
		return result;
	}
	if( PeekNamedPipe( ctx->pStdout, NULL, 0, NULL, &bytesAvail, NULL ) )
	{
		if( bytesAvail )
		{
			if( ReadFile( ctx->pStdout, buffer, sizeof(buffer) - 1, &bytesRead, NULL ) )
			{
				return channel_write( channel, remote, NULL, 0, buffer, bytesRead, NULL );
			}
			result = GetLastError();
		}
		else
		{
			// sf: if no data is available on the pipe we sleep to avoid running a tight loop
			// in this thread, as anonymous pipes won't block for data to arrive.
			Sleep( 100 );
		}
	}
	else
	{
		result = GetLastError();
	}

	if( result != ERROR_SUCCESS )
	{
		dprintf("Closing down socket: result: %d\n", result);
		process_channel_close( channel, NULL, ctx );
		channel_close( channel, remote, NULL, 0, NULL );
	}

	return result;
}
示例#29
0
/*
 * Notification handler for when a client connection has data
 */
DWORD portfwd_local_client_notify(Remote *remote,
		PortForwardClientContext *pcctx)
{
	UCHAR buf[8192];
	LONG bytesRead;

	// Reset the notification event
	ResetEvent(pcctx->notify);

	// Read data from the client connection
	if (((bytesRead = recv(pcctx->clientFd, buf, sizeof(buf), 0)) 
			== SOCKET_ERROR) || 
	    (bytesRead == 0))
		channel_close(pcctx->channel, pcctx->remote, NULL, 0, NULL);
		//portfwd_destroy_client(pcctx);
	else if (pcctx->channel)
		channel_write(pcctx->channel, pcctx->remote, NULL, 0, buf, bytesRead, 0);
	
	return ERROR_SUCCESS;
}
示例#30
0
static void proxy_disconnected(Channel * c) {
    Proxy * proxy = c->client_data;

    assert(c == proxy->c);
    assert(proxy->state == ProxyStateConnecting || proxy->state == ProxyStateConnected);
    proxy->state = ProxyStateDisconnected;
    if (proxy[proxy->other].state == ProxyStateDisconnected) {
        trace(LOG_PROXY, "Proxy disconnected");
        if (proxy->other == -1) proxy--;
        assert(proxy[0].c->spg == proxy[1].c->spg);
        suspend_group_free(proxy[0].c->spg);
        proxy[0].c->spg = proxy[1].c->spg = NULL;
        proxy[0].c->client_data = proxy[1].c->client_data = NULL;
        protocol_release(proxy[0].proto);
        protocol_release(proxy[1].proto);
        loc_free(proxy);
    }
    else {
        channel_close(proxy[proxy->other].c);
    }
}