Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  chansrv.c

  Sprache: C
 

/**
 * xrdp: A Remote Desktop Protocol server.
 *
 * Copyright (C) Jay Sorg 2009-2013
 * Copyright (C) Laxmikant Rashinkar 2009-2012
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif

#include "arch.h"
#include "os_calls.h"
#include "string_calls.h"
#include "thread_calls.h"
#include "trans.h"
#include "chansrv.h"
#include "defines.h"
#include "sound.h"
#include "clipboard.h"
#include "devredir.h"
#include "list.h"
#include "file.h"
#include "log.h"
#include "rail.h"
#include "xcommon.h"
#include "chansrv_fuse.h"
#include "chansrv_config.h"
#include "xrdp_sockets.h"
#include "audin.h"
#include "channel_defs.h"

#include "scp.h"
#include "scp_sync.h"

#include "ms-rdpbcgr.h"

static struct trans *g_lis_trans = 0;
static struct trans *g_con_trans = 0;
static struct trans *g_api_lis_trans = 0;
static struct list *g_api_con_trans_list = 0/* list of apps using api functions */
static struct chan_item g_chan_items[32];
static int g_num_chan_items = 0;
static int g_cliprdr_index = -1;
static int g_rdpsnd_index = -1;
static int g_rdpdr_index = -1;
static int g_rail_index = -1;

static tbus g_term_event = 0;
static tintptr g_sigchld_event = 0;
static tbus g_thread_done_event = 0;

struct config_chansrv *g_cfg = NULL;

int g_display_num = -1;
int g_cliprdr_chan_id = -1/* cliprdr */
int g_rdpsnd_chan_id = -1;  /* rdpsnd  */
int g_rdpdr_chan_id = -1;   /* rdpdr   */
int g_rail_chan_id = -1;    /* rail    */

char *g_exec_name;
tbus g_exec_event = 0;
tbus g_exec_mutex;
tbus g_exec_sem;
int g_exec_pid = 0;

#define ARRAYSIZE(x) (sizeof(x)/sizeof(*(x)))
/* max total channel bytes size */
#define MAX_CHANNEL_BYTES (1 * 1024 * 1024 * 1024/* 1 GB */
#define MAX_CHANNEL_FRAG_BYTES 1600

#define CHANSRV_DRDYNVC_STATUS_CLOSED       0
#define CHANSRV_DRDYNVC_STATUS_OPEN_SENT    1
#define CHANSRV_DRDYNVC_STATUS_OPEN         2
#define CHANSRV_DRDYNVC_STATUS_CLOSE_SENT   3

struct chansrv_drdynvc
{
    int chan_id;
    int status; /* see CHANSRV_DRDYNVC_STATUS_* */
    int flags;
    int pad0;
    int (*open_response)(int chan_id, int creation_status);
    int (*close_response)(int chan_id);
    int (*data_first)(int chan_id, char *data, int bytes, int total_bytes);
    int (*data)(int chan_id, char *data, int bytes);
    struct trans *xrdp_api_trans;
};

static struct chansrv_drdynvc g_drdynvcs[256];

/* data in struct trans::callback_data */
struct xrdp_api_data
{
    int chan_flags;
    int chan_id;
};

// Session state passed to xrdpapi
// Whenever a single member of this struct changes, the whole
// block is sent to xrdpapi. This simplifies the interface, but
// complicates xrdpapi somewhat
static struct xrdp_chan_session_state g_session_state;

struct timeout_obj
{
    tui32 mstime;
    void *data;
    void (*callback)(void *data);
    struct timeout_obj *next;
};

static struct timeout_obj *g_timeout_head = 0;
static struct timeout_obj *g_timeout_tail = 0;

/*****************************************************************************/
int
add_timeout(int msoffset, void (*callback)(void *data), void *data)
{
    struct timeout_obj *tobj;
    tui32 now;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "add_timeout:");
    now = g_time3();
    tobj = g_new0(struct timeout_obj, 1);
    tobj->mstime = now + msoffset;
    tobj->callback = callback;
    tobj->data = data;
    if (g_timeout_tail == 0)
    {
        g_timeout_head = tobj;
        g_timeout_tail = tobj;
    }
    else
    {
        g_timeout_tail->next = tobj;
        g_timeout_tail = tobj;
    }
    return 0;
}

/*****************************************************************************/
static int
get_timeout(int *timeout)
{
    struct timeout_obj *tobj;
    tui32 now;
    int ltimeout;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "get_timeout:");
    ltimeout = *timeout;
    if (ltimeout < 1)
    {
        ltimeout = 0;
    }
    tobj = g_timeout_head;
    if (tobj != 0)
    {
        now = g_time3();
        while (tobj != 0)
        {
            LOG_DEVEL(LOG_LEVEL_DEBUG, "  now %u tobj->mstime %u", now, tobj->mstime);
            if (now < tobj->mstime)
            {
                ltimeout = tobj->mstime - now;
            }
            tobj = tobj->next;
        }
    }
    if (ltimeout > 0)
    {
        LOG_DEVEL(LOG_LEVEL_DEBUG, "  ltimeout %d", ltimeout);
        if (*timeout < 1)
        {
            *timeout = ltimeout;
        }
        else
        {
            if (*timeout > ltimeout)
            {
                *timeout = ltimeout;
            }
        }
    }
    return 0;
}

/*****************************************************************************/
static int
check_timeout(void)
{
    struct timeout_obj *tobj;
    struct timeout_obj *last_tobj;
    struct timeout_obj *temp_tobj;
    int count;
    tui32 now;
    UNUSED_VAR(count);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "check_timeout:");
    count = 0;
    tobj = g_timeout_head;
    if (tobj != 0)
    {
        last_tobj = 0;
        while (tobj != 0)
        {
            count++;
            now = g_time3();
            if (now >= tobj->mstime)
            {
                tobj->callback(tobj->data);
                if (last_tobj == 0)
                {
                    g_timeout_head = tobj->next;
                    if (g_timeout_head == 0)
                    {
                        g_timeout_tail = 0;
                    }
                }
                else
                {
                    last_tobj->next = tobj->next;
                    if (g_timeout_tail == tobj)
                    {
                        g_timeout_tail = last_tobj;
                    }
                }
                temp_tobj = tobj;
                tobj = tobj->next;
                g_free(temp_tobj);
            }
            else
            {
                last_tobj = tobj;
                tobj = tobj->next;
            }
        }
    }
    LOG_DEVEL(LOG_LEVEL_DEBUG, "  count %d", count);
    return 0;
}

/*****************************************************************************/
int
g_is_term(void)
{
    return g_is_wait_obj_set(g_term_event);
}

/*****************************************************************************/
/* send data to a static virtual channel
   size can be > MAX_CHANNEL_FRAG_BYTES, in which case, > 1 message
   will be sent*/

/* returns error */
int
send_channel_data(int chan_id, const char *data, int size)
{
    int sending_bytes;
    int chan_flags;
    int error;
    int total_size;
    struct stream *s;

    if ((chan_id < 0) || (chan_id > 31) ||
            (data == NULL) ||
            (size < 1) || (size > MAX_CHANNEL_BYTES))
    {
        /* bad param */
        return 1;
    }
    total_size = size;
    chan_flags = 1/* first */
    while (size > 0)
    {
        sending_bytes = MIN(MAX_CHANNEL_FRAG_BYTES, size);
        if (sending_bytes >= size)
        {
            chan_flags |= 2/* last */
        }
        s = trans_get_out_s(g_con_trans, 26 + sending_bytes);
        if (s == NULL)
        {
            return 2;
        }
        out_uint32_le(s, 0); /* version */
        out_uint32_le(s, 26 + sending_bytes);
        out_uint32_le(s, 8); /* msg id */
        out_uint32_le(s, 18 + sending_bytes);
        out_uint16_le(s, chan_id);
        out_uint16_le(s, chan_flags);
        out_uint16_le(s, sending_bytes);
        out_uint32_le(s, total_size);
        out_uint8a(s, data, sending_bytes);
        s_mark_end(s);
        size -= sending_bytes;
        data += sending_bytes;
        error = trans_write_copy(g_con_trans);
        if (error != 0)
        {
            return 3;
        }
        chan_flags = 0;
    }
    return 0;
}

/*****************************************************************************/
/* returns error */
int
send_rail_drawing_orders(char *data, int size)
{
    LOG_DEVEL(LOG_LEVEL_DEBUG, "chansrv::send_rail_drawing_orders: size %d", size);

    struct stream *s;
    int error;

    s = trans_get_out_s(g_con_trans, 8192);
    if (s == NULL)
    {
        return 1;
    }
    out_uint32_le(s, 0); /* version */
    out_uint32_le(s, 8 + 8 + size); /* size */
    out_uint32_le(s, 10); /* msg id */
    out_uint32_le(s, 8 + size); /* size */
    out_uint8a(s, data, size);
    s_mark_end(s);
    error = trans_force_write(g_con_trans);
    if (error != 0)
    {
        return 1;
    }
    return 0;
}

/*****************************************************************************/
/**
 * Sends a session API state event to one listener
 */

static int
xrdpapi_send_session_state_event_single(struct trans *t)
{
    struct stream *s = t->out_s;
    init_stream(s, (int)sizeof(g_session_state));
    out_uint8a(s, &g_session_state, sizeof(g_session_state));
    s_mark_end(s);
    return trans_write_copy(t);
}

/*****************************************************************************/
/**
 * Sends a session API state event to all listeners (if any)
 *
 * This should be called after updating g_session_state. The xrdpapi
 * module will generate any appropriate 'windows' events from the message
 */

static void
xrdpapi_send_session_state_event_all(void)
{
    int index;
    struct trans *ltran;
    struct xrdp_api_data *api_data;

    for (index = 0; index < g_api_con_trans_list->count; index++)
    {
        ltran = (struct trans *) list_get_item(g_api_con_trans_list, index);
        if (ltran != NULL)
        {
            api_data = (struct xrdp_api_data *) (ltran->callback_data);
            if (api_data != NULL)
            {
                if (api_data->chan_id == CHAN_ID_XRDP_SESSION_INFO)
                {
                    (void)xrdpapi_send_session_state_event_single(ltran);
                }
            }
        }
    }
}

/*****************************************************************************/
/* returns error */
static int
process_message_channel_setup(struct stream *s)
{
    int num_chans;
    int index;
    int rv;
    struct chan_item *ci;

    g_num_chan_items = 0;
    g_cliprdr_index = -1;
    g_rdpsnd_index = -1;
    g_rdpdr_index = -1;
    g_rail_index = -1;
    g_cliprdr_chan_id = -1;
    g_rdpsnd_chan_id = -1;
    g_rdpdr_chan_id = -1;
    g_rail_chan_id = -1;
    LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_channel_setup:");
    in_uint16_le(s, num_chans);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_channel_setup: num_chans %d",
              num_chans);

    for (index = 0; index < num_chans; index++)
    {
        ci = &(g_chan_items[g_num_chan_items]);
        g_memset(ci->name, 0sizeof(ci->name));
        in_uint8a(s, ci->name, CHANNEL_NAME_LEN + 1);
        in_uint16_le(s, ci->id);
        in_uint16_le(s, ci->flags);
        LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_channel_setup: chan name '%s' "
                  "id %d flags %8.8x", ci->name, ci->id, ci->flags);

        if (g_strcasecmp(ci->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
        {
            g_cliprdr_index = g_num_chan_items;
            g_cliprdr_chan_id = ci->id;
        }
        else if (g_strcasecmp(ci->name, RDPSND_SVC_CHANNEL_NAME) == 0)
        {
            g_rdpsnd_index = g_num_chan_items;
            g_rdpsnd_chan_id = ci->id;
        }
        else if (g_strcasecmp(ci->name, RDPDR_SVC_CHANNEL_NAME) == 0)
        {
            g_rdpdr_index = g_num_chan_items;
            g_rdpdr_chan_id = ci->id;
        }
        /* disabled for now */
        else if (g_strcasecmp(ci->name, RAIL_SVC_CHANNEL_NAME) == 0)
        {
            g_rail_index = g_num_chan_items;
            g_rail_chan_id = ci->id;
        }
        else
        {
            LOG_DEVEL(LOG_LEVEL_DEBUG, "other %s", ci->name);
        }

        g_num_chan_items++;
    }

    rv = 0;

    if (g_cliprdr_index >= 0)
    {
        clipboard_init();
        xfuse_init();
    }

    if (g_rdpsnd_index >= 0)
    {
        sound_init();
    }

    if (g_rdpdr_index >= 0)
    {
        devredir_init();
        xfuse_init();
    }

    if (g_rail_index >= 0)
    {
        rail_init();
    }

    audin_init();

    return rv;
}

/*****************************************************************************/
/* returns error */
static int
process_message_channel_data(struct stream *s)
{
    int chan_id;
    int chan_flags;
    int rv;
    int length;
    int total_length;
    int index;
    int found;
    struct stream *ls;
    struct trans *ltran;
    struct xrdp_api_data *api_data;

    in_uint16_le(s, chan_id);
    in_uint16_le(s, chan_flags);
    in_uint16_le(s, length);
    in_uint32_le(s, total_length);
    LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_channel_data: chan_id %d "
              "chan_flags %d", chan_id, chan_flags);
    rv = 0;

    if (rv == 0)
    {
        if (chan_id == g_cliprdr_chan_id)
        {
            rv = clipboard_data_in(s, chan_id, chan_flags, length, total_length);
        }
        else if (chan_id == g_rdpsnd_chan_id)
        {
            rv = sound_data_in(s, chan_id, chan_flags, length, total_length);
        }
        else if (chan_id == g_rdpdr_chan_id)
        {
            rv = devredir_data_in(s, chan_id, chan_flags, length, total_length);
        }
        else if (chan_id == g_rail_chan_id)
        {
            rv = rail_data_in(s, chan_id, chan_flags, length, total_length);
        }
        else
        {
            found = 0;
            for (index = 0; index < g_api_con_trans_list->count; index++)
            {
                ltran = (struct trans *) list_get_item(g_api_con_trans_list, index);
                if (ltran != NULL)
                {
                    api_data = (struct xrdp_api_data *) (ltran->callback_data);
                    if (api_data != NULL)
                    {
                        if (api_data->chan_id == chan_id)
                        {
                            found = 1;
                            ls = ltran->out_s;
                            if (chan_flags & 1/* first */
                            {
                                init_stream(ls, total_length);
                            }
                            out_uint8a(ls, s->p, length);
                            if (chan_flags & 2/* last */
                            {
                                s_mark_end(ls);
                                rv = trans_write_copy(ltran);
                            }
                            break;
                        }
                    }
                }
            }
            if (found == 0)
            {
                LOG_DEVEL(LOG_LEVEL_INFO, "process_message_channel_data: not found channel %d", chan_id);
            }
        }

    }
    return rv;
}

/*****************************************************************************/
/* returns error */
/* open response from client */
static int
process_message_drdynvc_open_response(struct stream *s)
{
    struct chansrv_drdynvc *drdynvc;
    int chan_id;
    int creation_status;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_drdynvc_open_response:");
    if (!s_check_rem(s, 8))
    {
        return 1;
    }
    in_uint32_le(s, chan_id);
    in_uint32_le(s, creation_status);
    if ((chan_id < 0) || (chan_id > 255))
    {
        return 1;
    }
    drdynvc = g_drdynvcs + chan_id;
    if (drdynvc->status != CHANSRV_DRDYNVC_STATUS_OPEN_SENT)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "process_message_drdynvc_open_response: status not right");
        return 1;
    }
    if (creation_status == 0)
    {
        drdynvc->status = CHANSRV_DRDYNVC_STATUS_OPEN;
    }
    else
    {
        drdynvc->status = CHANSRV_DRDYNVC_STATUS_CLOSED;
    }
    if (drdynvc->open_response != NULL)
    {
        if (drdynvc->open_response(chan_id, creation_status) != 0)
        {
            return 1;
        }
    }
    return 0;
}

/*****************************************************************************/
/* returns error */
/* close response from client */
static int
process_message_drdynvc_close_response(struct stream *s)
{
    struct chansrv_drdynvc *drdynvc;
    int chan_id;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_drdynvc_close_response:");
    if (!s_check_rem(s, 4))
    {
        return 1;
    }
    in_uint32_le(s, chan_id);
    if ((chan_id < 0) || (chan_id > 255))
    {
        return 1;
    }
    drdynvc = g_drdynvcs + chan_id;
    if (drdynvc->status != CHANSRV_DRDYNVC_STATUS_CLOSE_SENT)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "process_message_drdynvc_close_response: status not right");
        return 0;
    }
    drdynvc->status = CHANSRV_DRDYNVC_STATUS_CLOSED;
    if (drdynvc->close_response != NULL)
    {
        if (drdynvc->close_response(chan_id) != 0)
        {
            return 1;
        }
    }
    return 0;
}

/*****************************************************************************/
/* returns error */
/* data from client */
static int
process_message_drdynvc_data_first(struct stream *s)
{
    struct chansrv_drdynvc *drdynvc;
    int chan_id;
    int bytes;
    int total_bytes;
    char *data;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_drdynvc_data_first:");
    if (!s_check_rem(s, 12))
    {
        return 1;
    }
    in_uint32_le(s, chan_id);
    in_uint32_le(s, bytes);
    in_uint32_le(s, total_bytes);
    if (!s_check_rem(s, bytes))
    {
        return 1;
    }
    in_uint8p(s, data, bytes);
    if ((chan_id < 0) || (chan_id > 255))
    {
        return 1;
    }
    drdynvc = g_drdynvcs + chan_id;
    if (drdynvc->data_first != NULL)
    {
        if (drdynvc->data_first(chan_id, data, bytes, total_bytes) != 0)
        {
            return 1;
        }
    }
    return 0;
}

/*****************************************************************************/
/* returns error */
/* data from client */
static int
process_message_drdynvc_data(struct stream *s)
{
    struct chansrv_drdynvc *drdynvc;
    int chan_id;
    int bytes;
    char *data;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_drdynvc_data:");
    if (!s_check_rem(s, 8))
    {
        return 1;
    }
    in_uint32_le(s, chan_id);
    in_uint32_le(s, bytes);
    if (!s_check_rem(s, bytes))
    {
        return 1;
    }
    in_uint8p(s, data, bytes);
    drdynvc = g_drdynvcs + chan_id;
    if (drdynvc->data != NULL)
    {
        if (drdynvc->data(chan_id, data, bytes) != 0)
        {
            return 1;
        }
    }
    return 0;
}

/*****************************************************************************/
/* open call from chansrv */
int
chansrv_drdynvc_open(const char *name, int flags,
                     struct chansrv_drdynvc_procs *procs, int *chan_id)
{
    struct stream *s;
    int name_bytes;
    int lchan_id;
    int error;

    lchan_id = 1;
    while (g_drdynvcs[lchan_id].status != CHANSRV_DRDYNVC_STATUS_CLOSED)
    {
        lchan_id++;
        if (lchan_id > 255)
        {
            return 1;
        }
    }
    s = trans_get_out_s(g_con_trans, 8192);
    if (s == NULL)
    {
        return 1;
    }
    name_bytes = g_strlen(name);
    out_uint32_le(s, 0); /* version */
    out_uint32_le(s, 8 + 8 + 4 + name_bytes + 4 + 4);
    out_uint32_le(s, 12); /* msg id */
    out_uint32_le(s, 8 + 4 + name_bytes + 4 + 4);
    out_uint32_le(s, name_bytes);
    out_uint8a(s, name, name_bytes);
    out_uint32_le(s, flags);
    out_uint32_le(s, lchan_id);
    s_mark_end(s);
    error = trans_write_copy(g_con_trans);
    if (error == 0)
    {
        if (chan_id != NULL)
        {
            *chan_id = lchan_id;
            g_drdynvcs[lchan_id].open_response = procs->open_response;
            g_drdynvcs[lchan_id].close_response = procs->close_response;
            g_drdynvcs[lchan_id].data_first = procs->data_first;
            g_drdynvcs[lchan_id].data = procs->data;
            g_drdynvcs[lchan_id].status = CHANSRV_DRDYNVC_STATUS_OPEN_SENT;

        }
    }
    return error;
}

/*****************************************************************************/
/* close call from chansrv */
int
chansrv_drdynvc_close(int chan_id)
{
    struct stream *s;
    int error;

    s = trans_get_out_s(g_con_trans, 8192);
    if (s == NULL)
    {
        return 1;
    }
    out_uint32_le(s, 0); /* version */
    out_uint32_le(s, 20);
    out_uint32_le(s, 14); /* msg id */
    out_uint32_le(s, 12);
    out_uint32_le(s, chan_id);
    s_mark_end(s);
    error = trans_write_copy(g_con_trans);
    g_drdynvcs[chan_id].status = CHANSRV_DRDYNVC_STATUS_CLOSE_SENT;
    return error;
}

/*****************************************************************************/
int
chansrv_drdynvc_data_first(int chan_id, const char *data, int data_bytes,
                           int total_data_bytes)
{
    struct stream *s;
    int error;

    //LOG_DEVEL(LOG_LEVEL_DEBUG, "chansrv_drdynvc_data_first: data_bytes %d total_data_bytes %d",
    //          data_bytes, total_data_bytes);
    s = trans_get_out_s(g_con_trans, 8192);
    if (s == NULL)
    {
        return 1;
    }
    out_uint32_le(s, 0); /* version */
    out_uint32_le(s, 28 + data_bytes);
    out_uint32_le(s, 16); /* msg id */
    out_uint32_le(s, 20 + data_bytes);
    out_uint32_le(s, chan_id);
    out_uint32_le(s, data_bytes);
    out_uint32_le(s, total_data_bytes);
    out_uint8a(s, data, data_bytes);
    s_mark_end(s);
    error = trans_write_copy(g_con_trans);
    return error;
}

/*****************************************************************************/
int
chansrv_drdynvc_data(int chan_id, const char *data, int data_bytes)
{
    struct stream *s;
    int error;

    // LOG_DEVEL(LOG_LEVEL_DEBUG, "chansrv_drdynvc_data: data_bytes %d", data_bytes);
    s = trans_get_out_s(g_con_trans, 8192);
    if (s == NULL)
    {
        return 1;
    }
    out_uint32_le(s, 0); /* version */
    out_uint32_le(s, 24 + data_bytes);
    out_uint32_le(s, 18); /* msg id */
    out_uint32_le(s, 16 + data_bytes);
    out_uint32_le(s, chan_id);
    out_uint32_le(s, data_bytes);
    out_uint8a(s, data, data_bytes);
    s_mark_end(s);
    error = trans_write_copy(g_con_trans);
    return error;
}

/*****************************************************************************/
int
chansrv_drdynvc_send_data(int chan_id, const char *data, int data_bytes)
{
    int this_send_bytes;

    // LOG_DEVEL(LOG_LEVEL_DEBUG, "chansrv_drdynvc_send_data: data_bytes %d", data_bytes);
    if (data_bytes > 1590)
    {
        if (chansrv_drdynvc_data_first(chan_id, data, 1590, data_bytes) != 0)
        {
            return 1;
        }
        data_bytes -= 1590;
        data += 1590;
    }
    while (data_bytes > 0)
    {
        this_send_bytes = MIN(1590, data_bytes);
        if (chansrv_drdynvc_data(chan_id, data, this_send_bytes) != 0)
        {
            return 1;
        }
        data_bytes -= this_send_bytes;
        data += this_send_bytes;
    }
    return 0;
}

/*****************************************************************************/
/* returns error */
static int
process_message(void)
{
    struct stream *s = (struct stream *)NULL;
    int size = 0;
    int id = 0;
    int rv = 0;
    char *next_msg = (char *)NULL;

    if (g_con_trans == 0)
    {
        return 1;
    }

    s = trans_get_in_s(g_con_trans);

    if (s == 0)
    {
        return 1;
    }

    rv = 0;

    while (s_check_rem(s, 8))
    {
        next_msg = s->p;
        in_uint32_le(s, id);
        in_uint32_le(s, size);
        next_msg += size;

        switch (id)
        {
            case 3/* channel setup */
                rv = process_message_channel_setup(s);
                break;
            case 5/* channel data */
                rv = process_message_channel_data(s);
                break;
            case 13/* drdynvc open response */
                rv = process_message_drdynvc_open_response(s);
                break;
            case 15/* drdynvc close response */
                rv = process_message_drdynvc_close_response(s);
                break;
            case 17/* drdynvc data first */
                rv = process_message_drdynvc_data_first(s);
                break;
            case 19/* drdynvc data */
                rv = process_message_drdynvc_data(s);
                break;
            default:
                LOG_DEVEL(LOG_LEVEL_ERROR, "process_message: unknown msg %d", id);
                break;
        }

        if (rv != 0)
        {
            g_writeln("process_message: error rv %d id %d", rv, id);
            rv = 0;
        }

        s->p = next_msg;
    }

    return rv;
}

/*****************************************************************************/
/* returns error */
int
my_trans_data_in(struct trans *trans)
{
    struct stream *s = (struct stream *)NULL;
    int size = 0;
    int error = 0;

    if (trans == 0)
    {
        return 0;
    }

    if (trans != g_con_trans)
    {
        return 1;
    }

    LOG_DEVEL(LOG_LEVEL_DEBUG, "my_trans_data_in:");
    s = trans_get_in_s(trans);
    in_uint8s(s, 4); /* id */
    in_uint32_le(s, size);
    error = trans_force_read(trans, size - 8);

    if (error == 0)
    {
        /* here, the entire message block is read in, process it */
        error = process_message();
        if (error == 0)
        {
        }
        else
        {
            g_writeln("my_trans_data_in: process_message failed");
        }
    }
    else
    {
        g_writeln("my_trans_data_in: trans_force_read failed");
    }

    return error;
}

/*****************************************************************************/
struct trans *
get_api_trans_from_chan_id(int chan_id)
{
    return g_drdynvcs[chan_id].xrdp_api_trans;
}

/*****************************************************************************/
static int
my_api_open_response(int chan_id, int creation_status)
{
    struct trans *trans;
    struct stream *s;

    //g_writeln("my_api_open_response: chan_id %d creation_status %d",
    //          chan_id, creation_status);
    trans = get_api_trans_from_chan_id(chan_id);
    if (trans == NULL)
    {
        return 1;
    }
    s = trans_get_out_s(trans, 8192);
    if (s == NULL)
    {
        return 1;
    }
    out_uint32_pe(s, creation_status);
    s_mark_end(s);
    if (trans_write_copy(trans) != 0)
    {
        return 1;
    }
    return 0;
}

/*****************************************************************************/
static int
my_api_close_response(int chan_id)
{
    //g_writeln("my_api_close_response:");
    return 0;
}

/*****************************************************************************/
static int
my_api_data_first(int chan_id, char *data, int bytes, int total_bytes)
{
    struct trans *trans;
    struct stream *s;

    //g_writeln("my_api_data_first: bytes %d total_bytes %d", bytes, total_bytes);
    trans = get_api_trans_from_chan_id(chan_id);
    if (trans == NULL)
    {
        return 1;
    }
    s = trans_get_out_s(trans, bytes);
    if (s == NULL)
    {
        return 1;
    }
    out_uint8a(s, data, bytes);
    s_mark_end(s);
    if (trans_write_copy(trans) != 0)
    {
        return 1;
    }
    return 0;
}

/*****************************************************************************/
static int
my_api_data(int chan_id, char *data, int bytes)
{
    struct trans *trans;
    struct stream *s;

    //g_writeln("my_api_data: bytes %d", bytes);
    trans = get_api_trans_from_chan_id(chan_id);
    if (trans == NULL)
    {
        return 1;
    }
    s = trans_get_out_s(trans, bytes);
    if (s == NULL)
    {
        return 1;
    }
    out_uint8a(s, data, bytes);
    s_mark_end(s);
    if (trans_write_copy(trans) != 0)
    {
        return 1;
    }
    return 0;
}

/*
 * Handles an incoming channel connect request on an xrdpapi channel
 *
 ******************************************************************************/

static int
handle_xrdpapi_connection_request(struct trans *trans)
{
    struct xrdp_api_data *ad = (struct xrdp_api_data *)(trans->callback_data);
    struct stream *s = trans_get_in_s(trans);
    int rv = 1// Assume failure
    struct xrdp_chan_connect connect_data;

    in_uint8a(s, &connect_data, sizeof(connect_data));
    connect_data.name[MAX_DVC_NAME_LEN] = '\0';
    //LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: chan_name %s chan_flags 0x%8.8x", connect_data.name, connect_data.flags);
    if (connect_data.version != XRDPAPI_CONNECT_PDU_VERSION)
    {
        return 1;
    }
    ad->chan_flags = connect_data.flags;

    if (connect_data.flags == 0 || connect_data.private_chan != 0)
    {
        /* SVC (or private) */
        if (connect_data.private_chan >= CHAN_ID_XRDP_BASE &&
                connect_data.private_chan < CHAN_ID_XRDP_MAX)
        {
            /* Valid private channel */
            ad->chan_id = connect_data.private_chan;
            ad->chan_flags = 0;
            rv = 0;
        }
        else if (connect_data.private_chan == 0)
        {
            /* Check SVC names */
            int index;
            for (index = 0; index < g_num_chan_items; index++)
            {
                if (g_strcasecmp(g_chan_items[index].name,
                                 connect_data.name) == 0)
                {
                    ad->chan_id = g_chan_items[index].id;
                    rv = 0;
                    break;
                }
            }
        }

        /* Send the status back to the caller */
        struct stream *out_s = trans_get_out_s(trans, 8192);
        if (out_s == NULL)
        {
            return 1;
        }
        out_uint32_pe(out_s, rv);
        s_mark_end(out_s);
        if (trans_write_copy(trans) != 0)
        {
            return 1;
        }

        /* Channel-specific processing */
        if (rv == 0 && connect_data.private_chan != 0)
        {
            switch (connect_data.private_chan)
            {
                case CHAN_ID_XRDP_SESSION_INFO:
                {
                    rv = xrdpapi_send_session_state_event_single(trans);
                    break;
                }
                default:
                    break;
            }
        }
    }
    else
    {
        /* DVS */
        struct chansrv_drdynvc_procs procs;
        g_memset(&procs, 0sizeof(procs));
        procs.open_response = my_api_open_response;
        procs.close_response = my_api_close_response;
        procs.data_first = my_api_data_first;
        procs.data = my_api_data;
        rv = chansrv_drdynvc_open(connect_data.name, ad->chan_flags,
                                  &procs, &(ad->chan_id));
        //LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: chansrv_drdynvc_open rv %d "
        //          "chan_id %d", rv, ad->chan_id);
        g_drdynvcs[ad->chan_id].xrdp_api_trans = trans;
    }

    return rv;
}

/*
 * called when VirtualChannelOpen() is invoked in xrdpapi.c, and later,
 * when the channel is written to
 *
 ******************************************************************************/

int
my_api_trans_data_in(struct trans *trans)
{
    struct stream *s;
    struct xrdp_api_data *ad;
    int rv;
    int bytes;

    //LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: extra_flags %d", trans->extra_flags);
    rv = 0;
    ad = (struct xrdp_api_data *) (trans->callback_data);
    s = trans_get_in_s(trans);
    if (trans->extra_flags == 0)
    {
        trans->header_size = XRDPAPI_CONNECT_PDU_LEN; // Need more data
        trans->extra_flags = 1;
    }
    else if (trans->extra_flags == 1)
    {
        // We've got a complete connection request on the channel */
        handle_xrdpapi_connection_request(trans);
        init_stream(s, 0);
        trans->extra_flags = 2// Mark the connection phase as complete
        trans->header_size = 0;
    }
    else
    {
        bytes = g_sck_recv(trans->sck, s->data, s->size, 0);
        if (bytes < 1)
        {
            //LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: disconnect");
            return 1;
        }
        if (ad->chan_flags == 0)
        {
            if (ad->chan_id == CHAN_ID_XRDP_SESSION_INFO)
            {
                // Ignore data sent to this channel
                rv = 0;
            }
            else
            {
                /* SVC */
                rv = send_channel_data(ad->chan_id, s->data, bytes);
            }
        }
        else
        {
            /* DVS */
            //LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_data_in: s->size %d bytes %d", s->size, bytes);
            rv = chansrv_drdynvc_send_data(ad->chan_id, s->data, bytes);
        }
        init_stream(s, 0);
    }
    return rv;
}

/*****************************************************************************/
int
my_trans_conn_in(struct trans *trans, struct trans *new_trans)
{
    if (trans == 0)
    {
        return 1;
    }

    if (trans != g_lis_trans)
    {
        return 1;
    }

    if (g_con_trans != 0/* if already set, error */
    {
        return 1;
    }

    if (new_trans == 0)
    {
        return 1;
    }

    LOG_DEVEL(LOG_LEVEL_DEBUG, "my_trans_conn_in:");
    g_con_trans = new_trans;
    g_con_trans->trans_data_in = my_trans_data_in;
    g_con_trans->header_size = 8;

    /* Tell any xrdpapi listeners the session is connected. The
     * previous state is guaranteed to be 'disconnected' at this point */

    g_session_state.is_connected = 1;
    xrdpapi_send_session_state_event_all();

    /* stop listening */
    trans_delete(g_lis_trans);
    g_lis_trans = 0;
    return 0;
}

/*
 * called when WTSVirtualChannelOpenEx is invoked in xrdpapi.c
 *
 ******************************************************************************/

int
my_api_trans_conn_in(struct trans *trans, struct trans *new_trans)
{
    struct xrdp_api_data *ad;

    //LOG_DEVEL(LOG_LEVEL_DEBUG, "my_api_trans_conn_in:");
    if ((trans == NULL) || (trans != g_api_lis_trans) || (new_trans == NULL))
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "my_api_trans_conn_in: error");
        return 1;
    }
    new_trans->trans_data_in = my_api_trans_data_in;
    new_trans->header_size = 8;
    new_trans->no_stream_init_on_data_in = 1;
    ad = g_new0(struct xrdp_api_data, 1);
    if (ad == NULL)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "my_api_trans_conn_in: error");
        return 1;
    }
    new_trans->callback_data = ad;
    list_add_item(g_api_con_trans_list, (intptr_t) new_trans);
    return 0;
}

/*****************************************************************************/
static int
setup_listen(void)
{
    char port[XRDP_SOCKETS_MAXPATH];
    int error = 0;

    if (g_lis_trans != 0)
    {
        trans_delete(g_lis_trans);
    }

    g_lis_trans = trans_create(TRANS_MODE_UNIX, 81928192);
    g_lis_trans->is_term = g_is_term;
    g_snprintf(port, sizeof(port), XRDP_CHANSRV_STR, g_getuid(), g_display_num);

    g_lis_trans->trans_conn_in = my_trans_conn_in;
    error = trans_listen(g_lis_trans, port);

    if (error != 0)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "setup_listen: trans_listen failed for port %s",
                  port);
        return 1;
    }

    return 0;
}

/*****************************************************************************/
static int
setup_api_listen(void)
{
    char port[XRDP_SOCKETS_MAXPATH];
    int error = 0;

    g_api_lis_trans = trans_create(TRANS_MODE_UNIX, 8192 * 48192 * 4);
    g_api_lis_trans->is_term = g_is_term;
    g_snprintf(port, sizeof(port), CHANSRV_API_STR, g_getuid(), g_display_num);
    g_api_lis_trans->trans_conn_in = my_api_trans_conn_in;
    error = trans_listen(g_api_lis_trans, port);

    if (error != 0)
    {
        LOG_DEVEL(LOG_LEVEL_ERROR, "setup_api_listen: trans_listen failed for port %s",
                  port);
        return 1;
    }

    return 0;
}

/*****************************************************************************/
static int
api_con_trans_list_get_wait_objs_rw(intptr_t *robjs, int *rcount,
                                    intptr_t *wobjs, int *wcount,
                                    int *timeout)
{
    int api_con_index;
    struct trans *ltran;

    for (api_con_index = g_api_con_trans_list->count - 1;
            api_con_index >= 0;
            api_con_index--)
    {
        ltran = (struct trans *)
                list_get_item(g_api_con_trans_list, api_con_index);
        if (ltran != NULL)
        {
            trans_get_wait_objs_rw(ltran, robjs, rcount, wobjs, wcount,
                                   timeout);
        }
    }
    return 0;
}

/*****************************************************************************/
static int
api_con_trans_list_check_wait_objs(void)
{
    int api_con_index;
    int drdynvc_index;
    struct trans *ltran;
    struct xrdp_api_data *ad;

    for (api_con_index = g_api_con_trans_list->count - 1;
            api_con_index >= 0;
            api_con_index--)
    {
        ltran = (struct trans *)
                list_get_item(g_api_con_trans_list, api_con_index);
        if (ltran != NULL)
        {
            if (trans_check_wait_objs(ltran) != 0)
            {
                /* disconnect */
                list_remove_item(g_api_con_trans_list, api_con_index);
                ad = (struct xrdp_api_data *) (ltran->callback_data);
                if (ad->chan_flags != 0)
                {
                    chansrv_drdynvc_close(ad->chan_id);
                }
                for (drdynvc_index = 0;
                        drdynvc_index < (int) ARRAYSIZE(g_drdynvcs);
                        drdynvc_index++)
                {
                    if (g_drdynvcs[drdynvc_index].xrdp_api_trans == ltran)
                    {
                        g_drdynvcs[drdynvc_index].xrdp_api_trans = NULL;
                    }
                }
                g_free(ad);
                trans_delete(ltran);
            }
        }
    }
    return 0;
}

/*****************************************************************************/
static int
api_con_trans_list_remove_all(void)
{
    int api_con_index;
    struct trans *ltran;

    for (api_con_index = g_api_con_trans_list->count - 1;
            api_con_index >= 0;
            api_con_index--)
    {
        ltran = (struct trans *)
                list_get_item(g_api_con_trans_list, api_con_index);
        if (ltran != NULL)
        {
            list_remove_item(g_api_con_trans_list, api_con_index);
            g_free(ltran->callback_data);
            trans_delete(ltran);
        }
    }
    return 0;
}

/*****************************************************************************/
THREAD_RV THREAD_CC
channel_thread_loop(void *in_val)
{
    tbus objs[32];
    tbus wobjs[32];
    int num_objs;
    int num_wobjs;
    int timeout;
    int error;
    THREAD_RV rv;

    LOG_DEVEL(LOG_LEVEL_INFO, "channel_thread_loop: thread start");
    rv = 0;
    g_api_con_trans_list = list_create();
    setup_api_listen();
    error = setup_listen();

    if (error == 0)
    {
        timeout = -1;
        num_objs = 0;
        num_wobjs = 0;
        objs[num_objs] = g_term_event;
        num_objs++;
        trans_get_wait_objs(g_lis_trans, objs, &num_objs);
        trans_get_wait_objs(g_api_lis_trans, objs, &num_objs);

        //g_writeln("timeout %d", timeout);
        while (g_obj_wait(objs, num_objs, wobjs, num_wobjs, timeout) == 0)
        {
            check_timeout();
            if (g_is_wait_obj_set(g_term_event))
            {
                LOG_DEVEL(LOG_LEVEL_INFO, "channel_thread_loop: g_term_event set");
                clipboard_deinit();
                sound_deinit();
                devredir_deinit();
                rail_deinit();
                break;
            }

            if (g_lis_trans != 0)
            {
                if (trans_check_wait_objs(g_lis_trans) != 0)
                {
                    LOG_DEVEL(LOG_LEVEL_INFO, "channel_thread_loop: "
                              "trans_check_wait_objs error");
                }
            }

            if (g_con_trans != 0)
            {
                if (trans_check_wait_objs(g_con_trans) != 0)
                {
                    LOG_DEVEL(LOG_LEVEL_INFO, "channel_thread_loop: "
                              "trans_check_wait_objs error resetting");

                    clipboard_deinit();
                    sound_deinit();
                    devredir_deinit();
                    rail_deinit();
                    /* delete g_con_trans */
                    trans_delete(g_con_trans);
                    g_con_trans = 0;
                    /* Tell any xrdpapi listeners the session is
                     * disconnected.  The previous state is guaranteed
                     * to be 'connected' at this point */

                    g_session_state.is_connected = 0;
                    (void)xrdpapi_send_session_state_event_all();
                    /* create new listener */
                    error = setup_listen();

                    if (error != 0)
                    {
                        break;
                    }
                }
            }

            if (g_api_lis_trans != 0)
            {
                if (trans_check_wait_objs(g_api_lis_trans) != 0)
                {
                    LOG_DEVEL(LOG_LEVEL_ERROR, "channel_thread_loop: trans_check_wait_objs failed");
                }
            }
            /* check the wait_objs in g_api_con_trans_list */
            api_con_trans_list_check_wait_objs();
            xcommon_check_wait_objs();
            sound_check_wait_objs();
            devredir_check_wait_objs();
            xfuse_check_wait_objs();
            timeout = -1;
            num_objs = 0;
            num_wobjs = 0;
            objs[num_objs] = g_term_event;
            num_objs++;
            trans_get_wait_objs_rw(g_lis_trans, objs, &num_objs,
                                   wobjs, &num_wobjs, &timeout);
            trans_get_wait_objs_rw(g_con_trans, objs, &num_objs,
                                   wobjs, &num_wobjs, &timeout);
            trans_get_wait_objs_rw(g_api_lis_trans, objs, &num_objs,
                                   wobjs, &num_wobjs, &timeout);
            /* get the wait_objs from in g_api_con_trans_list */
            api_con_trans_list_get_wait_objs_rw(objs, &num_objs,
                                                wobjs, &num_wobjs,
                                                &timeout);
            xcommon_get_wait_objs(objs, &num_objs, &timeout);
            sound_get_wait_objs(objs, &num_objs, &timeout);
            devredir_get_wait_objs(objs, &num_objs, &timeout);
            xfuse_get_wait_objs(objs, &num_objs, &timeout);
            get_timeout(&timeout);
        } /* end while (g_obj_wait(objs, num_objs, 0, 0, timeout) == 0) */
    }

    trans_delete(g_lis_trans);
    g_lis_trans = 0;
    trans_delete(g_con_trans);
    g_con_trans = 0;
    /* Tell any xrdpapi listeners the session is disconnected */
    if (g_session_state.is_connected)
    {
        g_session_state.is_connected = 0;
        (void)xrdpapi_send_session_state_event_all();
    }
    trans_delete(g_api_lis_trans);
    g_api_lis_trans = 0;
    api_con_trans_list_remove_all();
    list_delete(g_api_con_trans_list);
    LOG_DEVEL(LOG_LEVEL_INFO, "channel_thread_loop: thread stop");
    g_set_wait_obj(g_thread_done_event);
    return rv;
}

/*****************************************************************************/
static void
term_signal_handler(int sig)
{
    g_set_wait_obj(g_term_event);
}

/*****************************************************************************/
static void
nil_signal_handler(int sig)
{
}

/*****************************************************************************/
static void
set_sigchld_event(int sig)
{
    g_set_wait_obj(g_sigchld_event);
}

/*****************************************************************************/
static void
child_signal_handler(void)
{
    int pid;

    LOG_DEVEL(LOG_LEVEL_INFO, "child_signal_handler:");
    while ((pid = g_waitchild(NULL)) > 0)
    {
        LOG_DEVEL(LOG_LEVEL_INFO, "child_signal_handler: child pid %d", pid);
        if (pid == g_exec_pid)
        {
            LOG_DEVEL(LOG_LEVEL_INFO, "child_signal_handler: found pid %d", pid);
            //shutdownx();
        }
    }
}

/*****************************************************************************/
void
segfault_signal_handler(int sig)
{
    LOG_DEVEL(LOG_LEVEL_ERROR, "segfault_signal_handler: entered.......");
    xfuse_deinit();
    exit(0);
}

/*****************************************************************************/
static void
x_server_fatal_handler(void)
{
    LOG_DEVEL(LOG_LEVEL_INFO, "xserver_fatal_handler: entered.......");
    /* At this point the X server has gone away. Dont make any X calls. */
    xfuse_deinit();
    exit(0);
}

/*****************************************************************************/
int
main_cleanup(void)
{
    if (g_term_event != 0)
    {
        g_delete_wait_obj(g_term_event);
    }
    if (g_sigchld_event != 0)
    {
        g_delete_wait_obj(g_sigchld_event);
    }
    if (g_thread_done_event != 0)
    {
        g_delete_wait_obj(g_thread_done_event);
    }
    if (g_exec_event != 0)
    {
        g_delete_wait_obj(g_exec_event);
        tc_mutex_delete(g_exec_mutex);
        tc_sem_delete(g_exec_sem);
    }
    log_end();
    config_free(g_cfg);
    g_deinit(); /* os_calls */
    return 0;
}

/*****************************************************************************/
static int
get_log_path(char *path, int bytes)
{
    char *log_path;
    int rv;

    rv = 1;
    if (g_cfg->log_file_path != NULL && g_cfg->log_file_path[0] != '\0')
    {
        char uidstr[64];
        char username[64];
        const struct info_string_tag map[] =
        {
            {'u', uidstr},
            {'U', username},
            INFO_STRING_END_OF_LIST
        };

        int uid = g_getuid();
        g_snprintf(uidstr, sizeof(uidstr), "%d", uid);
        if (g_getlogin(username, sizeof(username)) != 0)
        {
            /* Fall back to UID */
            g_strncpy(username, uidstr, sizeof(username) - 1);
        }

        (void)g_format_info_string(path, bytes, g_cfg->log_file_path, map);
        if (g_directory_exist(path) || (g_mkdir(path) == 0))
        {
            rv = 0;
        }
    }
    else if ((log_path = g_getenv("CHANSRV_LOG_PATH")) != 0)
    {
        g_snprintf(path, bytes, "%s", log_path);
        if (g_directory_exist(path) || (g_mkdir(path) == 0))
        {
            rv = 0;
        }
    }
    else if ((log_path = g_getenv("XDG_DATA_HOME")) != 0)
    {
        g_snprintf(path, bytes, "%s%s", log_path, "/xrdp");
        if (g_directory_exist(path) || (g_mkdir(path) == 0))
        {
            rv = 0;
        }
    }

    // Always fall back to the home directory
    if (rv != 0)
    {
        log_path = g_getenv("HOME");
        if (log_path != 0)
        {
            g_snprintf(path, bytes, "%s%s", log_path, "/.local");
            if (g_directory_exist(path) || (g_mkdir(path) == 0))
            {
                g_snprintf(path, bytes, "%s%s", log_path, "/.local/share");
                if (g_directory_exist(path) || (g_mkdir(path) == 0))
                {
                    g_snprintf(path, bytes, "%s%s", log_path, "/.local/share/xrdp");
                    if (g_directory_exist(path) || (g_mkdir(path) == 0))
                    {
                        rv = 0;
                    }
                }
            }
        }
    }
    return rv;
}

/*****************************************************************************/
static int
run_exec(void)
{
    int pid;

    LOG_DEVEL(LOG_LEVEL_DEBUG, "run_exec:");
    pid = g_fork();

    if (pid == 0)
    {
        trans_delete(g_con_trans);
        g_close_wait_obj(g_term_event);
        g_close_wait_obj(g_sigchld_event);
        g_close_wait_obj(g_thread_done_event);
        g_close_wait_obj(g_exec_event);
        tc_mutex_delete(g_exec_mutex);
        tc_sem_delete(g_exec_sem);
        g_execlp3(g_exec_name, g_exec_name, 0);
        g_exit(0);
    }

    g_exec_pid = pid;
    tc_sem_inc(g_exec_sem);

    return 0;
}

/*****************************************************************************/
/**
 * Make sure XRDP_SOCKET_PATH exists
 *
 * We can't do anything without XRDP_SOCKET_PATH existing.
 *
 * Normally this is done by sesman before chansrv starts. If we're running
 * standalone however (i.e. with x11vnc) this won't be done. We don't have the
 * privilege to create the directory, so we have to ask sesman to do it
 * for us.
 */

static int
chansrv_create_xrdp_socket_path(void)
{
    char xrdp_socket_path[XRDP_SOCKETS_MAXPATH];
    int rv = 1;

    /* Use our UID to qualify XRDP_SOCKET_PATH */
    g_snprintf(xrdp_socket_path, sizeof(xrdp_socket_path),
               XRDP_SOCKET_PATH, g_getuid());

    if (g_directory_exist(xrdp_socket_path))
    {
        rv = 0;
    }
    else
    {
        LOG(LOG_LEVEL_INFO, "%s doesn't exist - asking sesman to create it",
            xrdp_socket_path);

        struct trans *t = NULL;

        if (!(t = scp_connect(g_cfg->listen_port, "xrdp-chansrv", g_is_term)))
        {
            LOG(LOG_LEVEL_ERROR, "Can't connect to sesman");
        }
        else if (scp_sync_uds_login_request(t) == 0 &&
                 scp_sync_create_sockdir_request(t) == 0)
        {
            rv = 0;
            (void)scp_send_close_connection_request(t);
        }
        trans_delete(t);
    }

    return rv;
}

/*****************************************************************************/
int
main(int argc, char **argv)
{
    tbus waiters[4];
    int pid = 0;
    char text[256];
    const char *config_path;
    char log_path[256];
    const char *display_text;
    char log_file[256];
    enum logReturns error;
    struct log_config *logconfig;
    g_init("xrdp-chansrv"); /* os_calls */
    g_memset(g_drdynvcs, 0sizeof(g_drdynvcs));

    display_text = g_getenv("DISPLAY");
    if (display_text == NULL)
    {
        g_writeln("DISPLAY is not set");
        main_cleanup();
        return 1;
    }
    g_display_num = g_get_display_num_from_display(display_text);
    if (g_display_num < 0)
    {
        g_writeln("Unable to get display from DISPLAY='%s'", display_text);
        main_cleanup();
        return 1;
    }

    /*
     * The user is unable at present to override the sysadmin-provided
     * sesman.ini location */

    config_path = XRDP_CFG_PATH "/sesman.ini";
    if ((g_cfg = config_read(0, config_path)) == NULL)
    {
        main_cleanup();
        return 1;
    }
    config_dump(g_cfg);

    if (get_log_path(log_path, sizeof(log_path)) != 0)
    {
        g_writeln("error reading CHANSRV_LOG_PATH and HOME environment variable");
        main_cleanup();
        return 1;
    }

    pid = g_getpid();

    /* starting logging subsystem */
    g_snprintf(log_file, 255"%s/xrdp-chansrv.%d.log", log_path, g_display_num);
    g_writeln("chansrv::main: using log file [%s]", log_file);
    if (g_file_exist(log_file))
    {
        g_file_delete(log_file);
    }

    logconfig = log_config_init_from_config(config_path, "xrdp-chansrv""Chansrv");
    if (logconfig->log_file != NULL)
    {
        g_free(logconfig->log_file);
    }
    logconfig->log_file = log_file;
    error = log_start_from_param(logconfig);
    logconfig->log_file = NULL;
    log_config_free(logconfig);
    logconfig = NULL;

    if (error != LOG_STARTUP_OK)
    {
        switch (error)
        {
            case LOG_ERROR_MALLOC:
                g_writeln("error on malloc. cannot start logging. quitting.");
                break;
            case LOG_ERROR_FILE_OPEN:
                g_writeln("error opening log file [%s]. quitting.",
                          getLogFile(text, 255));
                break;
            default:
                g_writeln("log_start error");
                break;
        }

        main_cleanup();
        return 1;
    }

    LOG_DEVEL(LOG_LEVEL_INFO, "main: app started pid %d(0x%8.8x)", pid, pid);

    if (chansrv_create_xrdp_socket_path() != 0)
    {
        main_cleanup();
        return 1;
    }

    /*  set up signal objects  */
    g_snprintf(text, sizeof(text), "xrdp_chansrv_%8.8x_main_term", pid);
    g_term_event = g_create_wait_obj(text);
    g_snprintf(text, sizeof(text), "xrdp_chansrv_%8.8x_sigchld", pid);
    g_sigchld_event = g_create_wait_obj(text);

    /*  set up signal handlers */
    g_signal_terminate(term_signal_handler); /* SIGTERM */
    g_signal_user_interrupt(term_signal_handler); /* SIGINT */
    g_signal_pipe(nil_signal_handler); /* SIGPIPE */
    g_signal_child_stop(set_sigchld_event); /* SIGCHLD */
    g_signal_segfault(segfault_signal_handler);

    /* Cater for the X server exiting unexpectedly */
    xcommon_set_x_server_fatal_handler(x_server_fatal_handler);

    LOG_DEVEL(LOG_LEVEL_INFO, "main: DISPLAY env var set to %s", display_text);
    LOG_DEVEL(LOG_LEVEL_INFO, "main: using DISPLAY %d", g_display_num);

    /* Set up RAIL sync objects */
    g_snprintf(text, sizeof(text), "xrdp_chansrv_%8.8x_exec", pid);
    g_exec_event = g_create_wait_obj(text);
    g_exec_mutex = tc_mutex_create();
    g_exec_sem = tc_sem_create(0);

    /* Set up the channel thread */
    g_snprintf(text, sizeof(text), "xrdp_chansrv_%8.8x_thread_done", pid);
    g_thread_done_event = g_create_wait_obj(text);
    tc_thread_create(channel_thread_loop, 0);

    while (g_term_event > 0 && !g_is_wait_obj_set(g_term_event))
    {
        waiters[0] = g_term_event;
        waiters[1] = g_exec_event;
        waiters[2] = g_sigchld_event;

        if (g_obj_wait(waiters, 300, -1) != 0)
        {
            LOG_DEVEL(LOG_LEVEL_ERROR, "main: error, g_obj_wait failed");
            break;
        }

        if (g_is_wait_obj_set(g_term_event))
        {
            break;
        }

        if (g_is_wait_obj_set(g_sigchld_event))
        {
            g_reset_wait_obj(g_sigchld_event);
            child_signal_handler();
        }

        if (g_is_wait_obj_set(g_exec_event))
        {
            g_reset_wait_obj(g_exec_event);
            run_exec();
        }
    }

    while (g_thread_done_event > 0 && !g_is_wait_obj_set(g_thread_done_event))
    {
        /* wait for thread to exit */
        if (g_obj_wait(&g_thread_done_event, 100, -1) != 0)
        {
            LOG_DEVEL(LOG_LEVEL_ERROR, "main: error, g_obj_wait failed");
            break;
        }
    }

    /* cleanup */
    main_cleanup();
    LOG_DEVEL(LOG_LEVEL_INFO, "main: app exiting pid %d(0x%8.8x)", pid, pid);
    return 0;
}

Messung V0.5 in Prozent
C=92 H=91 G=91

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.24Bemerkung:  (vorverarbeitet am  2026-07-10) ¤

*Bot Zugriff






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik