static void
client_handler( void* _client, int  events )
{
    Client  client = _client;

    if (events & SYS_EVENT_READ) {
        int  ret;
        /* read into buffer, one character at a time */
        ret = sys_channel_read( client->channel, client->in_buff + client->in_pos, 1 );
        if (ret != 1) {
            fprintf(stderr, "client %p could not read byte, result = %d, error: %s\n",
                    client, ret, strerror(errno) );
            goto ExitClient;
        }
        if (client->in_buff[client->in_pos] == '\r' ||
            client->in_buff[client->in_pos] == '\n' ) {
            const char*  cmd = client->in_buff;
            client->in_buff[client->in_pos] = 0;

            if (client->in_pos > 0) {
                client_handle_line( client, cmd );
                client->in_pos = 0;
            }
        } else
            client->in_pos += 1;
    }

    if (events & SYS_EVENT_WRITE) {
        int  ret;
        /* write from output buffer, one char at a time */
        ret = sys_channel_write( client->channel, client->out_buff + client->out_pos, 1 );
        if (ret != 1) {
            fprintf(stderr, "client %p could not write byte, result = %d, error: %s\n",
                    client, ret, strerror(errno) );
            goto ExitClient;
        }
        client->out_pos += 1;
        if (client->out_pos == client->out_size) {
            client->out_size = 0;
            client->out_pos  = 0;
            /* we don't need to write */
            sys_channel_on( client->channel, SYS_EVENT_READ, client_handler, client );
        }
    }
    return;

ExitClient:
    printf( "client %p exiting\n", client );
    client_free( client );
}
static void
accept_func( void*  _server, int  events )
{
    SysChannel  server  = _server;
    SysChannel  handler;
    Client      client;

    printf( "connection accepted for server channel, getting handler socket\n" );
    handler = sys_channel_create_tcp_handler( server );
    client  = client_alloc( handler );
    printf( "got one. created client %p\n", client );

    events=events;
    sys_channel_on( handler, SYS_EVENT_READ, client_handler, client );
}
int  main( void )
{
    int  port = DEFAULT_PORT;
    SysChannel  server;

    sys_main_init();
    modem = amodem_create( NULL, NULL );

    server = sys_channel_create_tcp_server( port );
    printf( "GSM simulator listening on local port %d\n", port );

    sys_channel_on( server, SYS_EVENT_READ, accept_func, server );
    sys_main_loop();
    printf( "GSM simulator exiting\n" );
    return 0;
}
static void
client_append( Client  client, const char*  str, int len )
{
    int  avail;

    if (len < 0)
        len = strlen(str);

    avail = sizeof(client->out_buff) - client->out_size;
    if (len > avail)
        len = avail;

    memcpy( client->out_buff + client->out_size, str, len );
    if (client->out_size == 0) {
        sys_channel_on( client->channel, SYS_EVENT_READ | SYS_EVENT_WRITE, client_handler, client );
    }
    client->out_size += len;
}
static void
remote_call_event( void*  opaque, int  events )
{
    RemoteCall  call = opaque;

    S("%s: called for call (%d,%d), events=%02x\n", __FUNCTION__,
       call->from_port, call->to_port, events);

    if (events & SYS_EVENT_READ) {
        /* simply drain the channel */
        char  temp[32];
        int  n = sys_channel_read( call->channel, temp, sizeof(temp) );
        if (n <= 0) {
            /* remote emulator probably quitted */
            //S("%s: emulator %d quitted with %d: %s\n", __FUNCTION__, call->to_port, errno, errno_str);
            remote_call_free( call );
            return;
        }
    }

    if (events & SYS_EVENT_WRITE) {
        int  n;

        if (S_ACTIVE) {
            int  nn;
            S("%s: call (%d,%d) sending %d bytes '", __FUNCTION__,
            call->from_port, call->to_port, call->buff_len - call->buff_pos );
            for (nn = call->buff_pos; nn < call->buff_len; nn++) {
                int  c = call->buff[nn];
                if (c < 32) {
                    if (c == '\n')
                        S("\\n");
                    else if (c == '\t')
                        S("\\t");
                    else if (c == '\r')
                        S("\\r");
                    else
                        S("\\x%02x", c);
                } else
                    S("%c", c);
            }
            S("'\n");
        }

        n = sys_channel_write( call->channel,
                               call->buff + call->buff_pos,
                               call->buff_len - call->buff_pos );
        if (n <= 0) {
            /* remote emulator probably quitted */
            S("%s: emulator %d quitted unexpectedly with error %d: %s\n",
                    __FUNCTION__, call->to_port, errno, errno_str);
            if (call->result_func)
                call->result_func( call->result_opaque, 0 );
            remote_call_free( call );
            return;
        }
        call->buff_pos += n;

        if (call->buff_pos >= call->buff_len) {
            /* cool, we sent everything */
            S("%s: finished sending data to %d\n", __FUNCTION__, call->to_port);
            if (!call->quitting) {
                    call->quitting = 1;
                    sprintf( call->buff, "quit\n" );
                    call->buff_len = strlen(call->buff);
                    call->buff_pos = 0;
            } else {
                call->quitting = 0;
                if (call->result_func)
                    call->result_func( call->result_opaque, 1 );

                sys_channel_on( call->channel, SYS_EVENT_READ, remote_call_event, call );
            }
        }
    }
}
static RemoteCall
remote_call_alloc( RemoteCallType  type, int  to_port, int  from_port )
{
    RemoteCall  rcall    = calloc( sizeof(*rcall), 1 );
    int         from_num = remote_number_from_port(from_port);

    if (rcall != NULL) {
        char  *p, *end;

        rcall->pref      = &rcall->next;
        rcall->type      = type;
        rcall->to_port   = to_port;
        rcall->from_port = from_port;
        rcall->buff      = rcall->buff0;
        rcall->buff_size = sizeof(rcall->buff0);
        rcall->buff_pos  = 0;

        p   = rcall->buff;
        end = p + rcall->buff_size;

        switch (type) {
            case REMOTE_CALL_DIAL:
                p = bufprint(p, end, "gsm call " PHONE_PREFIX "%d\n", from_num );
                break;

            case REMOTE_CALL_BUSY:
                p = bufprint(p, end, "gsm busy " PHONE_PREFIX "%d\n", from_num);
                break;

            case REMOTE_CALL_HOLD:
                p = bufprint(p, end, "gsm hold " PHONE_PREFIX "%d\n", from_num);
                break;

            case REMOTE_CALL_ACCEPT:
                p = bufprint(p, end, "gsm accept " PHONE_PREFIX "%d\n", from_num);
                break;

            case REMOTE_CALL_HANGUP:
                p = bufprint(p, end, "gsm cancel " PHONE_PREFIX "%d\n", from_num );
                break;

            default:
                ;
        }
        if (p >= end) {
            D("%s: buffer too short\n", __FUNCTION__ );
            remote_call_free(rcall);
            return NULL;
        }

        rcall->buff_len = p - rcall->buff;

        rcall->channel = sys_channel_create_tcp_client( "localhost", to_port );
        if (rcall->channel == NULL) {
            D("%s: could not create channel to port %d\n", __FUNCTION__, to_port);
            remote_call_free(rcall);
            return NULL;
        }

        sys_channel_on( rcall->channel, SYS_EVENT_WRITE, remote_call_event, rcall );
    }
    return  rcall;
}