summaryrefslogtreecommitdiff
path: root/pjlib-util
diff options
context:
space:
mode:
authorRiza Sulistyo <riza@teluu.com>2013-04-05 03:02:19 +0000
committerRiza Sulistyo <riza@teluu.com>2013-04-05 03:02:19 +0000
commit3c8d3b370e33627a0a08a3bcfceaf9709e4a3991 (patch)
tree7e65b6d873106dd32e1809f9c59ee64bb5329a6c /pjlib-util
parentcebde64ea044c362999974b662dc3e58e0577778 (diff)
Re #1643: Code restructure+add callback to support symbian gui app
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4461 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib-util')
-rw-r--r--pjlib-util/include/pjlib-util.h3
-rw-r--r--pjlib-util/include/pjlib-util/cli_telnet.h33
-rw-r--r--pjlib-util/include/pjlib-util/config.h2
-rw-r--r--pjlib-util/src/pjlib-util/cli_telnet.c259
4 files changed, 230 insertions, 67 deletions
diff --git a/pjlib-util/include/pjlib-util.h b/pjlib-util/include/pjlib-util.h
index ee3b2768..b4e34656 100644
--- a/pjlib-util/include/pjlib-util.h
+++ b/pjlib-util/include/pjlib-util.h
@@ -63,4 +63,7 @@
/* HTTP */
#include <pjlib-util/http_client.h>
+/** CLI Telnet **/
+#include <pjlib-util/cli_telnet.h>
+
#endif /* __PJLIB_UTIL_H__ */
diff --git a/pjlib-util/include/pjlib-util/cli_telnet.h b/pjlib-util/include/pjlib-util/cli_telnet.h
index bf8dafbc..f640de04 100644
--- a/pjlib-util/include/pjlib-util/cli_telnet.h
+++ b/pjlib-util/include/pjlib-util/cli_telnet.h
@@ -34,6 +34,31 @@ PJ_BEGIN_DECL
*
*/
+ /**
+ * This structure contains the information about the telnet.
+ * Application will get updated information each time the telnet is started/
+ * restarted.
+ */
+typedef struct pj_cli_telnet_info
+{
+ /**
+ * The telnet's ip address.
+ */
+ pj_str_t ip_address;
+
+ /**
+ * The telnet's port number.
+ */
+ pj_uint16_t port;
+} pj_cli_telnet_info;
+
+/**
+ * This specifies the callback called when telnet is started
+ *
+ * @param telnet_info The telnet runtime information.
+ *
+ */
+typedef void (*pj_cli_telnet_on_started)(pj_cli_telnet_info *telnet_info);
/**
* This structure contains various options to instantiate the telnet daemon.
@@ -85,8 +110,14 @@ typedef struct pj_cli_telnet_cfg
*/
pj_str_t prompt_str;
-} pj_cli_telnet_cfg;
+ /**
+ * Specify the pj_cli_telnet_on_started callback.
+ *
+ * Default: empty
+ */
+ pj_cli_telnet_on_started on_started;
+} pj_cli_telnet_cfg;
/**
* Initialize pj_cli_telnet_cfg with its default values.
diff --git a/pjlib-util/include/pjlib-util/config.h b/pjlib-util/include/pjlib-util/config.h
index 19821c0b..99da78df 100644
--- a/pjlib-util/include/pjlib-util/config.h
+++ b/pjlib-util/include/pjlib-util/config.h
@@ -291,7 +291,7 @@
* Default: 120
*/
#ifndef PJ_CLI_MAX_CMDBUF
-# define PJ_CLI_MAX_CMDBUF 120
+# define PJ_CLI_MAX_CMDBUF 512
#endif
/**
diff --git a/pjlib-util/src/pjlib-util/cli_telnet.c b/pjlib-util/src/pjlib-util/cli_telnet.c
index c6a8cf16..c3c19dfc 100644
--- a/pjlib-util/src/pjlib-util/cli_telnet.c
+++ b/pjlib-util/src/pjlib-util/cli_telnet.c
@@ -29,6 +29,15 @@
#include <pj/except.h>
#include <pjlib-util/errno.h>
#include <pjlib-util/scanner.h>
+#include <pj/addr_resolv.h>
+#include <pj/compat/socket.h>
+
+#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
+ (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0)
+
+#define EADDRINUSE WSAEADDRINUSE
+
+#endif
#define CLI_TELNET_BUF_SIZE 256
@@ -44,6 +53,14 @@
#endif
#define MAX_CLI_TELNET_OPTIONS 256
+/** Maximum retry on Telnet Restart **/
+#define MAX_RETRY_ON_TELNET_RESTART 100
+/** Minimum number of millisecond to wait before retrying to re-bind on
+ * telnet restart **/
+#define MIN_WAIT_ON_TELNET_RESTART 20
+/** Maximum number of millisecod to wait before retrying to re-bind on
+ * telnet restart **/
+#define MAX_WAIT_ON_TELNET_RESTART 1000
/**
* This specify the state for the telnet option negotiation.
@@ -261,7 +278,7 @@ typedef struct cli_telnet_sess
unsigned buf_len;
} cli_telnet_sess;
-struct cli_telnet_fe
+typedef struct cli_telnet_fe
{
pj_cli_front_end base;
pj_pool_t *pool;
@@ -272,8 +289,8 @@ struct cli_telnet_fe
pj_activesock_t *asock;
pj_thread_t *worker_thread;
pj_bool_t is_quitting;
- pj_mutex_t *mutex;
-};
+ pj_mutex_t *mutex;
+} cli_telnet_fe;
/* Forward Declaration */
static pj_status_t telnet_sess_send2(cli_telnet_sess *sess,
@@ -282,6 +299,9 @@ static pj_status_t telnet_sess_send2(cli_telnet_sess *sess,
static pj_status_t telnet_sess_send(cli_telnet_sess *sess,
const pj_str_t *str);
+static pj_status_t telnet_start(cli_telnet_fe *fe);
+static pj_status_t telnet_restart(cli_telnet_fe *tfe);
+
/**
* Return the number of characters between the current cursor position
* to the end of line.
@@ -645,7 +665,7 @@ static void send_prompt_str(cli_telnet_sess *sess)
{
pj_str_t send_data;
char data_str[128];
- struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+ cli_telnet_fe *fe = (cli_telnet_fe *)sess->base.fe;
send_data.ptr = &data_str[0];
send_data.slen = 0;
@@ -669,7 +689,7 @@ static void send_err_arg(cli_telnet_sess *sess,
char data_str[256];
unsigned len;
unsigned i;
- struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+ cli_telnet_fe *fe = (cli_telnet_fe *)sess->base.fe;
send_data.ptr = &data_str[0];
send_data.slen = 0;
@@ -743,7 +763,7 @@ static void send_ambi_arg(cli_telnet_sess *sess,
unsigned len;
pj_str_t send_data;
char data[1028];
- struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+ cli_telnet_fe *fe = (cli_telnet_fe *)sess->base.fe;
const pj_cli_hint_info *hint = info->hint;
out_parse_state parse_state = OP_NORMAL;
pj_ssize_t max_length = 0;
@@ -1318,7 +1338,7 @@ static pj_status_t telnet_sess_send2(cli_telnet_sess *sess,
static void telnet_sess_destroy(pj_cli_sess *sess)
{
cli_telnet_sess *tsess = (cli_telnet_sess *)sess;
- pj_mutex_t *mutex = ((struct cli_telnet_fe *)sess->fe)->mutex;
+ pj_mutex_t *mutex = ((cli_telnet_fe *)sess->fe)->mutex;
pj_mutex_lock(mutex);
pj_list_erase(sess);
@@ -1334,7 +1354,7 @@ static void telnet_sess_destroy(pj_cli_sess *sess)
static void telnet_fe_write_log(pj_cli_front_end *fe, int level,
const char *data, int len)
{
- struct cli_telnet_fe * tfe = (struct cli_telnet_fe *)fe;
+ cli_telnet_fe *tfe = (cli_telnet_fe *)fe;
pj_cli_sess *sess;
pj_mutex_lock(tfe->mutex);
@@ -1357,7 +1377,7 @@ static void telnet_fe_write_log(pj_cli_front_end *fe, int level,
static void telnet_fe_destroy(pj_cli_front_end *fe)
{
- struct cli_telnet_fe *tfe = (struct cli_telnet_fe *)fe;
+ cli_telnet_fe *tfe = (cli_telnet_fe *)fe;
pj_cli_sess *sess;
tfe->is_quitting = PJ_TRUE;
@@ -1391,7 +1411,7 @@ static void telnet_fe_destroy(pj_cli_front_end *fe)
static int poll_worker_thread(void *p)
{
- struct cli_telnet_fe *fe = (struct cli_telnet_fe *)p;
+ cli_telnet_fe *fe = (cli_telnet_fe *)p;
while (!fe->is_quitting) {
pj_time_val delay = {0, 50};
@@ -1408,9 +1428,10 @@ static pj_bool_t telnet_sess_on_data_sent(pj_activesock_t *asock,
cli_telnet_sess *sess = (cli_telnet_sess *)
pj_activesock_get_user_data(asock);
- PJ_UNUSED_ARG(op_key);
+ PJ_UNUSED_ARG(op_key);
if (sent <= 0) {
+ TRACE_((THIS_FILE, "Error On data send"));
pj_cli_sess_end_session(&sess->base);
return PJ_FALSE;
}
@@ -1441,20 +1462,20 @@ static pj_bool_t telnet_sess_on_data_read(pj_activesock_t *asock,
{
cli_telnet_sess *sess = (cli_telnet_sess *)
pj_activesock_get_user_data(asock);
- struct cli_telnet_fe *tfe = (struct cli_telnet_fe *)sess->base.fe;
+ cli_telnet_fe *tfe = (cli_telnet_fe *)sess->base.fe;
unsigned char *cdata = (unsigned char*)data;
pj_status_t is_valid = PJ_TRUE;
PJ_UNUSED_ARG(size);
PJ_UNUSED_ARG(remainder);
- if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- pj_cli_sess_end_session(&sess->base);
+ if (tfe->is_quitting)
return PJ_FALSE;
- }
- if (tfe->is_quitting)
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ TRACE_((THIS_FILE, "Error on data read %d", status));
return PJ_FALSE;
+ }
pj_mutex_lock(sess->smutex);
@@ -1551,13 +1572,14 @@ static pj_bool_t telnet_sess_on_data_read(pj_activesock_t *asock,
static pj_bool_t telnet_fe_on_accept(pj_activesock_t *asock,
pj_sock_t newsock,
const pj_sockaddr_t *src_addr,
- int src_addr_len)
+ int src_addr_len,
+ pj_status_t status)
{
- struct cli_telnet_fe *fe = (struct cli_telnet_fe *)
- pj_activesock_get_user_data(asock);
+ cli_telnet_fe *fe = (cli_telnet_fe *) pj_activesock_get_user_data(asock);
+
pj_status_t sstatus;
pj_pool_t *pool;
- cli_telnet_sess *sess;
+ cli_telnet_sess *sess = NULL;
pj_activesock_cb asock_cb;
PJ_UNUSED_ARG(src_addr);
@@ -1566,6 +1588,14 @@ static pj_bool_t telnet_fe_on_accept(pj_activesock_t *asock,
if (fe->is_quitting)
return PJ_FALSE;
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ TRACE_((THIS_FILE, "Error on data accept %d", status));
+ if (status == PJ_ESOCKETSTOP)
+ telnet_restart(fe);
+
+ return PJ_FALSE;
+ }
+
/* An incoming connection is accepted, create a new session */
pool = pj_pool_create(fe->pool->factory, "telnet_sess",
PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC,
@@ -1657,19 +1687,16 @@ PJ_DEF(pj_status_t) pj_cli_telnet_create(pj_cli_t *cli,
pj_cli_telnet_cfg *param,
pj_cli_front_end **p_fe)
{
- struct cli_telnet_fe *fe;
+ cli_telnet_fe *fe;
pj_pool_t *pool;
- pj_sock_t sock = PJ_INVALID_SOCKET;
- pj_activesock_cb asock_cb;
- pj_sockaddr_in addr;
- pj_status_t sstatus;
+ pj_status_t status;
PJ_ASSERT_RETURN(cli, PJ_EINVAL);
pool = pj_pool_create(pj_cli_get_param(cli)->pf, "telnet_fe",
PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC,
NULL);
- fe = PJ_POOL_ZALLOC_T(pool, struct cli_telnet_fe);
+ fe = PJ_POOL_ZALLOC_T(pool, cli_telnet_fe);
if (!fe)
return PJ_ENOMEM;
@@ -1687,42 +1714,86 @@ PJ_DEF(pj_status_t) pj_cli_telnet_create(pj_cli_t *cli,
fe->base.op->on_destroy = &telnet_fe_destroy;
fe->pool = pool;
- if (!fe->cfg.ioqueue) {
+ if (!fe->cfg.ioqueue) {
/* Create own ioqueue if application doesn't supply one */
- sstatus = pj_ioqueue_create(pool, 8, &fe->cfg.ioqueue);
- if (sstatus != PJ_SUCCESS)
+ status = pj_ioqueue_create(pool, 8, &fe->cfg.ioqueue);
+ if (status != PJ_SUCCESS)
goto on_exit;
fe->own_ioqueue = PJ_TRUE;
}
- sstatus = pj_mutex_create_recursive(pool, "mutex_telnet_fe", &fe->mutex);
- if (sstatus != PJ_SUCCESS)
+ status = pj_mutex_create_recursive(pool, "mutex_telnet_fe", &fe->mutex);
+ if (status != PJ_SUCCESS)
goto on_exit;
/* Start telnet daemon */
- sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
- &sock);
- if (sstatus != PJ_SUCCESS)
+ telnet_start(fe);
+
+ pj_cli_register_front_end(cli, &fe->base);
+
+ if (p_fe)
+ *p_fe = &fe->base;
+
+ return PJ_SUCCESS;
+
+on_exit:
+ if (fe->own_ioqueue)
+ pj_ioqueue_destroy(fe->cfg.ioqueue);
+
+ if (fe->mutex)
+ pj_mutex_destroy(fe->mutex);
+
+ pj_pool_release(pool);
+ return status;
+}
+
+static pj_status_t telnet_start(cli_telnet_fe *fe)
+{
+ pj_sock_t sock = PJ_INVALID_SOCKET;
+ pj_activesock_cb asock_cb;
+ pj_sockaddr_in addr;
+ pj_status_t status;
+ int val;
+ int restart_retry;
+ unsigned msec;
+
+ /* Start telnet daemon */
+ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
+
+ if (status != PJ_SUCCESS)
goto on_exit;
pj_sockaddr_in_init(&addr, NULL, fe->cfg.port);
- sstatus = pj_sock_bind(sock, &addr, sizeof(addr));
- if (sstatus == PJ_SUCCESS) {
- pj_sockaddr_in addr;
- int addr_len = sizeof(addr);
+ val = 1;
+ status = pj_sock_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ &val, sizeof(val));
+
+ if (status != PJ_SUCCESS)
+ goto on_exit;
+
+ /* The loop is silly, but what else can we do? */
+ for (msec=MIN_WAIT_ON_TELNET_RESTART, restart_retry=0;
+ restart_retry < MAX_RETRY_ON_TELNET_RESTART;
+ ++restart_retry, msec=(msec<MAX_WAIT_ON_TELNET_RESTART?
+ msec*2 : MAX_WAIT_ON_TELNET_RESTART))
+ {
+ status = pj_sock_bind(sock, &addr, sizeof(addr));
+ if (status != PJ_STATUS_FROM_OS(EADDRINUSE))
+ break;
+ PJ_LOG(4,(THIS_FILE, "Address is still in use, retrying.."));
+ pj_thread_sleep(msec);
+ }
+
+ if (status == PJ_SUCCESS) {
+ int addr_len = sizeof(addr);
- sstatus = pj_sock_getsockname(sock, &addr, &addr_len);
- if (sstatus != PJ_SUCCESS)
+ status = pj_sock_getsockname(sock, &addr, &addr_len);
+ if (status != PJ_SUCCESS)
goto on_exit;
- fe->cfg.port = pj_sockaddr_in_get_port(&addr);
- if (param)
- param->port = fe->cfg.port;
-
- PJ_LOG(3, (THIS_FILE, "CLI telnet daemon listening at port %d",
- fe->cfg.port));
-
+ fe->cfg.port = pj_sockaddr_in_get_port(&addr);
+
if (fe->cfg.prompt_str.slen == 0) {
pj_str_t prompt_sign = {"> ", 2};
char *prompt_data = pj_pool_alloc(fe->pool,
@@ -1737,35 +1808,51 @@ PJ_DEF(pj_status_t) pj_cli_telnet_create(pj_cli_t *cli,
goto on_exit;
}
- sstatus = pj_sock_listen(sock, 4);
- if (sstatus != PJ_SUCCESS)
+ status = pj_sock_listen(sock, 4);
+ if (status != PJ_SUCCESS)
goto on_exit;
pj_bzero(&asock_cb, sizeof(asock_cb));
- asock_cb.on_accept_complete = &telnet_fe_on_accept;
- sstatus = pj_activesock_create(pool, sock, pj_SOCK_STREAM(),
- NULL, fe->cfg.ioqueue,
- &asock_cb, fe, &fe->asock);
- if (sstatus != PJ_SUCCESS)
+ asock_cb.on_accept_complete2 = &telnet_fe_on_accept;
+ status = pj_activesock_create(fe->pool, sock, pj_SOCK_STREAM(),
+ NULL, fe->cfg.ioqueue,
+ &asock_cb, fe, &fe->asock);
+ if (status != PJ_SUCCESS)
goto on_exit;
- sstatus = pj_activesock_start_accept(fe->asock, pool);
- if (sstatus != PJ_SUCCESS)
+ status = pj_activesock_start_accept(fe->asock, fe->pool);
+ if (status != PJ_SUCCESS)
goto on_exit;
if (fe->own_ioqueue) {
/* Create our own worker thread */
- sstatus = pj_thread_create(pool, "worker_telnet_fe",
- &poll_worker_thread, fe, 0, 0,
- &fe->worker_thread);
- if (sstatus != PJ_SUCCESS)
+ status = pj_thread_create(fe->pool, "worker_telnet_fe",
+ &poll_worker_thread, fe, 0, 0,
+ &fe->worker_thread);
+ if (status != PJ_SUCCESS)
goto on_exit;
}
- pj_cli_register_front_end(cli, &fe->base);
+ /** Fill telnet information and call pj_cli_telnet_on_started callback */
+ if (fe->cfg.on_started) {
+ char ip_addr[32];
+ pj_cli_telnet_info telnet_info;
+ pj_sockaddr hostip;
- if (p_fe)
- *p_fe = &fe->base;
+ telnet_info.ip_address.ptr = ip_addr;
+ telnet_info.ip_address.slen = 0;
+
+ status = pj_gethostip(pj_AF_INET(), &hostip);
+ if (status != PJ_SUCCESS)
+ goto on_exit;
+
+ pj_strcpy2(&telnet_info.ip_address,
+ pj_inet_ntoa(hostip.ipv4.sin_addr));
+
+ telnet_info.port = fe->cfg.port;
+
+ (*fe->cfg.on_started)(&telnet_info);
+ }
return PJ_SUCCESS;
@@ -1781,6 +1868,48 @@ on_exit:
if (fe->mutex)
pj_mutex_destroy(fe->mutex);
- pj_pool_release(pool);
- return sstatus;
+ pj_pool_release(fe->pool);
+ return status;
+}
+
+static pj_status_t telnet_restart(cli_telnet_fe *fe)
+{
+ pj_status_t status;
+ pj_cli_sess *sess;
+
+ fe->is_quitting = PJ_TRUE;
+ if (fe->worker_thread) {
+ pj_thread_join(fe->worker_thread);
+ }
+
+ pj_mutex_lock(fe->mutex);
+
+ /* Destroy all the sessions */
+ sess = fe->sess_head.next;
+ while (sess != &fe->sess_head) {
+ (*sess->op->destroy)(sess);
+ sess = fe->sess_head.next;
+ }
+
+ pj_mutex_unlock(fe->mutex);
+
+ /** Close existing activesock **/
+ status = pj_activesock_close(fe->asock);
+ if (status != PJ_SUCCESS)
+ goto on_exit;
+
+ if (fe->worker_thread) {
+ pj_thread_destroy(fe->worker_thread);
+ fe->worker_thread = NULL;
+ }
+
+ fe->is_quitting = PJ_FALSE;
+
+ /** Start Telnet **/
+ status = telnet_start(fe);
+ if (status == PJ_SUCCESS)
+ TRACE_((THIS_FILE, "Telnet Restarted"));
+
+on_exit:
+ return status;
}