6a5d858dce
Functions are added to xrdpapi to allows the connection status to be determimed. These functions are modelled on the Windows API functions, but are not compatible with them. In particular, the error handling is different. A way for an application to receive events is also provided. At present, only connect/disconnected events are implemented.
2100 lines
56 KiB
C
2100 lines
56 KiB
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
|
|
|
|
#ifdef XRDP_IBUS
|
|
#include "input.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_get_elapsed_ms();
|
|
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_get_elapsed_ms();
|
|
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_get_elapsed_ms();
|
|
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, 0, sizeof(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;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* tell xrdp we can do Unicode input */
|
|
static int
|
|
chansrv_advertise_unicode_input(int status)
|
|
{
|
|
struct stream *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 + 4);
|
|
out_uint32_le(s, 20); /* msg id */
|
|
out_uint32_le(s, 8 + 4);
|
|
out_uint32_le(s, status);
|
|
s_mark_end(s);
|
|
return trans_write_copy(g_con_trans);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* 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;
|
|
}
|
|
|
|
#ifdef XRDP_IBUS
|
|
/*****************************************************************************/
|
|
static int
|
|
process_message_unicode_data(struct stream *s)
|
|
{
|
|
int rv = 0;
|
|
int key_down;
|
|
char32_t unicode;
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_unicode_keypress:");
|
|
if (!s_check_rem(s, 8))
|
|
{
|
|
rv = 1;
|
|
}
|
|
else
|
|
{
|
|
in_uint32_le(s, key_down);
|
|
in_uint32_le(s, unicode);
|
|
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "process_message_unicode_keypress: received unicode %i", unicode);
|
|
|
|
if (key_down)
|
|
{
|
|
xrdp_input_send_unicode(unicode);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static int
|
|
process_message_unicode_setup(struct stream *s)
|
|
{
|
|
int rv = xrdp_input_unicode_init();
|
|
if (rv == 0)
|
|
{
|
|
// Tell xrdp we can support Unicode input
|
|
rv = chansrv_advertise_unicode_input(0);
|
|
}
|
|
else
|
|
{
|
|
// Tell xrdp there's a problem starting the framework
|
|
chansrv_advertise_unicode_input(2);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static int
|
|
process_message_unicode_shutdown(struct stream *s)
|
|
{
|
|
return xrdp_input_unicode_destroy();
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
/* 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;
|
|
case 21: /* unicode setup */
|
|
#ifdef XRDP_IBUS
|
|
rv = process_message_unicode_setup(s);
|
|
#else
|
|
// We don't support this.
|
|
rv = chansrv_advertise_unicode_input(1);
|
|
#endif
|
|
break;
|
|
case 23: /* unicode key event */
|
|
#ifdef XRDP_IBUS
|
|
rv = process_message_unicode_data(s);
|
|
#endif
|
|
break;
|
|
case 25: /* unicode shut down */
|
|
#ifdef XRDP_IBUS
|
|
rv = process_message_unicode_shutdown(s);
|
|
#endif
|
|
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 */
|
|
static 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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static 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, 0, sizeof(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
|
|
*
|
|
******************************************************************************/
|
|
static 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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static 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
|
|
*
|
|
******************************************************************************/
|
|
static 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, 8192, 8192);
|
|
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 * 4, 8192 * 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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static 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, 0, sizeof(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, 3, 0, 0, -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, 1, 0, 0, -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;
|
|
}
|