BAL and Maple Release 2.2

Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bcm68620_release/release/host_reference/cli/bcmcli_server.c b/bcm68620_release/release/host_reference/cli/bcmcli_server.c
new file mode 100644
index 0000000..160d98d
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/bcmcli_server.c
@@ -0,0 +1,546 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(c) 2016 Broadcom
+   All Rights Reserved
+
+Unless you and Broadcom execute a separate written software license
+agreement governing use of this software, this software is licensed
+to you under the terms of the GNU General Public License version 2
+(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+with the following added to such license:
+
+   As a special exception, the copyright holders of this software give
+   you permission to link this software with independent modules, and
+   to copy and distribute the resulting executable under terms of your
+   choice, provided that you also meet, for each linked independent
+   module, the terms and conditions of the license of that module.
+   An independent module is a module which is not derived from this
+   software.  The special exception does not apply to any modifications
+   of the software.
+
+Not withstanding the above, under no circumstances may you combine
+this software in any way with any other Broadcom software provided
+under a license other than the GPL, without Broadcom's express prior
+written consent.
+
+:>
+ */
+
+
+/*******************************************************************
+ * bcmcli_server.c
+ *
+ * CLI engine - remote shell support
+ *
+ * This module is a back-end of remote shell support.
+ * - multiple servers
+ * - domain and TCP-based connections
+ * - session access level - per server
+ *******************************************************************/
+
+#include <bcmcli_server.h>
+
+typedef struct bcmclis_server bcmclis_server_t;
+
+/* Server connection
+ */
+typedef struct bcmclis_conn
+{
+    struct bcmclis_conn *next;
+    bcmclis_server_t *server;
+    const char *address; /* client address */
+    int sock;             /* transport socket */
+    bdmf_task rx_thread;
+    bcmcli_session *session;
+    uint32_t bytes_sent;
+    uint32_t bytes_received;
+    bdmf_task conn_thread;
+} bcmclis_conn_t;
+
+/* Server control bdmfock
+ */
+struct bcmclis_server
+{
+    bcmclis_server_t *next;
+    bcmclis_conn_t *conn_list;
+    int sock;             /* listening socket */
+    bcmclis_parm_t parms;
+    int id;
+    int nconns;
+    bcmos_fastlock lock;
+    bdmf_task listen_thread;
+};
+
+/* socaddr variants */
+typedef union
+{
+    struct sockaddr sa;
+    struct sockaddr_un domain_sa;
+    struct sockaddr_in tcp_sa;
+} sockaddr_any;
+
+static bcmclis_server_t *bcmclis_servers;
+static int bcmclis_server_id;
+
+static bcmclis_server_t *bcmclis_id_to_server(int hs, bcmclis_server_t **prev)
+{
+    bcmclis_server_t *s=bcmclis_servers;
+    if (prev)
+        *prev = NULL;
+    while(s)
+    {
+        if (s->id == hs)
+            break;
+        if (prev)
+            *prev = s;
+        s = s->next;
+    }
+    return s;
+}
+
+/* Parse address helper */
+static int bcmclis_parse_address(const bcmclis_parm_t *parms, int *protocol, sockaddr_any *sa, int *len)
+{
+    switch(parms->transport)
+    {
+    case BCMCLI_TRANSPORT_DOMAIN_SOCKET:
+    {
+        *protocol = AF_UNIX;
+        sa->domain_sa.sun_family = AF_UNIX;  /* local is declared before socket() ^ */
+        strcpy(sa->domain_sa.sun_path, parms->address);
+        *len = strlen(sa->domain_sa.sun_path) + sizeof(sa->domain_sa.sun_family);
+        break;
+    }
+    case BCMCLI_TRANSPORT_TCP_SOCKET:
+    {
+        *protocol = AF_INET;
+        sa->tcp_sa.sin_family = AF_INET;
+        sa->tcp_sa.sin_port = htons(atoi(parms->address));
+        sa->tcp_sa.sin_addr.s_addr = INADDR_ANY;
+        *len = sizeof(sa->tcp_sa);
+        break;
+    }
+    default:
+        return BCM_ERR_PARM;
+    }
+    return 0;
+}
+
+
+/* disconnect client and clear resources */
+static void bcmclis_disconnect(bcmclis_conn_t *conn)
+{
+    bcmclis_server_t *s=conn->server;
+    bcmclis_conn_t *c=s->conn_list, *prev=NULL;
+
+    bcmos_fastlock_lock(&s->lock);
+    while(c && c!=conn)
+    {
+        prev = c;
+        c = c->next;
+    }
+    BUG_ON(!c);
+    if (prev)
+        prev->next = c->next;
+    else
+        s->conn_list = c->next;
+    --s->nconns;
+    bcmos_fastlock_unlock(&s->lock);
+    bcmcli_session_close(c->session);
+    close(c->sock);
+    bdmf_task_destroy(c->rx_thread);
+    bcmos_free(c);
+}
+
+/*
+ * Session callbacks
+ */
+
+/** Session's output function.
+ * returns the number of bytes written or <0 if error
+ */
+static int bcmclis_cb_sess_write(void *user_priv, const void *buf, uint32_t size)
+{
+    bcmclis_conn_t *c=user_priv;
+    int rc;
+
+    rc = send(c->sock, buf, size, 0);
+    /* disconnect if IO error */
+    if (rc < size)
+        bcmclis_disconnect(c);
+    else
+        c->bytes_sent += rc;
+    return rc;
+}
+
+#define CHAR_EOT 0x04
+
+/** Session's input function.
+ * returns the number of bytes read or <0 if error
+ */
+static char *bcmclis_read_line(bcmclis_conn_t *c, char *buf, uint32_t size)
+{
+    int i;
+    int rc;
+    int len=0;
+
+    for(i=0; i<size-1; i++)
+    {
+        char ch;
+        rc = recv(c->sock, &ch, 1, MSG_WAITALL);
+        if (rc <= 0)
+            break;
+        if (ch == '\r')
+            continue;
+        if (ch == CHAR_EOT)
+            break;
+        buf[len++] = ch;
+        if (ch == '\n')
+            break;
+    }
+    c->bytes_received += i;
+    buf[len] = 0;
+    return (len ? buf : NULL);
+}
+
+/* Receive handler */
+static int bcmclis_rx_thread_handler(void *arg)
+{
+    char buf[512];
+    bcmclis_conn_t *c=arg;
+
+    while(!bcmcli_is_stopped(c->session) &&
+          bcmclis_read_line(c, buf, sizeof(buf)))
+    {
+        bcmcli_parse(c->session, buf);
+    }
+    bcmclis_disconnect(c);
+    return 0;
+}
+
+/* New client connection indication */
+static void bcmclis_connect(bcmclis_server_t *s, char *addr, int sock)
+{
+    bcmclis_conn_t *c;
+    bcmcli_session_parm sess_parm;
+    int rc;
+
+    if (s->parms.max_clients && s->nconns >= s->parms.max_clients)
+    {
+        bcmos_printf("bdmfmons: server %s: refused connection because max number has been reached\n", s->parms.address);
+        close(sock);
+        return;
+    }
+
+    c = bcmos_calloc(sizeof(*c) + strlen(addr) + 1);
+    if (!c)
+        goto cleanup;
+    c->address = (char *)c + sizeof(*c);
+    strcpy((char *)c->address, addr);
+    c->server = s;
+    c->sock = sock;
+
+    /* create new management session */
+    memset(&sess_parm, 0, sizeof(sess_parm));
+    sess_parm.access_right = s->parms.access;
+    sess_parm.write = bcmclis_cb_sess_write;
+    sess_parm.user_priv = c;
+    rc = bcmcli_session_open(&sess_parm, &c->session);
+    if (rc)
+        goto cleanup;
+
+    /* wait for receive in a separate thread */
+    rc = bdmf_task_create("bcmclis_rx",
+                    BDMFSYS_DEFAULT_TASK_PRIORITY,
+                    BDMFSYS_DEFAULT_TASK_STACK,
+                    bcmclis_rx_thread_handler, c,
+                    &c->rx_thread);
+    if (rc)
+        goto cleanup;
+
+    bcmos_fastlock_lock(&s->lock);
+    c->next = s->conn_list;
+    s->conn_list = c;
+    ++s->nconns;
+    bcmos_fastlock_unlock(&s->lock);
+
+    return;
+
+cleanup:
+    close(sock);
+    if (c->session)
+        bcmcli_session_close(c->session);
+    if (c)
+        bcmos_free(c);
+}
+
+/* Receive handler */
+static int bcmclis_listen_thread_handler(void *arg)
+{
+    bcmclis_server_t *s=arg;
+    sockaddr_any addr;
+    socklen_t len;
+    int sock;
+
+    while(1)
+    {
+        char caddr[64];
+        len = sizeof(addr);
+        sock = accept(s->sock, &addr.sa, &len);
+        if (sock < 0)
+        {
+            perror("accept");
+            break;
+        }
+        if (s->parms.transport==BCMCLI_TRANSPORT_DOMAIN_SOCKET)
+            strncpy(caddr, s->parms.address, sizeof(caddr)-1);
+        else
+        {
+            snprintf(caddr, sizeof(caddr)-1, "%s:%d",
+                inet_ntoa(addr.tcp_sa.sin_addr), ntohs(addr.tcp_sa.sin_port));
+        }
+        bcmclis_connect(s, caddr, sock);
+    }
+    return 0;
+}
+
+/*
+ * External API
+ */
+
+/** Create shell server.
+ * Immediately after creation server is ready to accept client connections
+ * \param[in]   parms   Server parameters
+ * \param[out]  hs      Server handle
+ * \return  0 - OK\n
+ *         <0 - error code
+ */
+bcmos_errno bcmclis_server_create(const bcmclis_parm_t *parms, int *hs)
+{
+    bcmclis_server_t *s;
+    int protocol;
+    sockaddr_any sa;
+    int len;
+    int rc;
+
+    if (!parms || !hs || !parms->address)
+        return BCM_ERR_PARM;
+
+    /* parse address */
+    if (bcmclis_parse_address(parms, &protocol, &sa, &len))
+        return BCM_ERR_PARM;
+
+    /* allocate server structure */
+    s = bcmos_calloc(sizeof(bcmclis_server_t)+strlen(parms->address)+1);
+    if (!s)
+        return BCM_ERR_NOMEM;
+    s->parms = *parms;
+    s->parms.address = (char *)s + sizeof(*s);
+    strcpy(s->parms.address, parms->address);
+    s->id = ++bcmclis_server_id;
+    bcmos_fastlock_init(&s->lock);
+
+    /* create socket and start listening */
+    s->sock = socket(protocol, SOCK_STREAM, 0);
+    if ((s->sock < 0) ||
+        (bind(s->sock, &sa.sa, len) < 0) ||
+        (listen(s->sock, 1) < 0))
+    {
+        perror("socket/bind/listen");
+        close(s->sock);
+        bcmos_free(s);
+        return BCM_ERR_PARM;
+    }
+
+    /* wait for connection(s) in a separate thread */
+    rc = bdmf_task_create("bcmclis_listen",
+                    BDMFSYS_DEFAULT_TASK_PRIORITY,
+                    BDMFSYS_DEFAULT_TASK_STACK,
+                    bcmclis_listen_thread_handler, s,
+                    &s->listen_thread);
+    if (rc)
+    {
+        close(s->sock);
+        bcmos_free(s);
+        return rc;
+    }
+
+    /* all good */
+    s->next = bcmclis_servers;
+    bcmclis_servers = s;
+    *hs = s->id;
+
+    return 0;
+}
+
+/** Destroy shell server.
+ * All client connections if any are closed
+ * \param[in]   hs      Server handle
+ * \return  0 - OK\n
+ *         <0 - error code
+ */
+bcmos_errno bcmclis_server_destroy(int hs)
+{
+    bcmclis_server_t *prev;
+    bcmclis_server_t *s = bcmclis_id_to_server(hs, &prev);
+    bcmclis_conn_t *c;
+    if (!s)
+        return BCM_ERR_NOENT;
+
+    bdmf_task_destroy(s->listen_thread);
+    close(s->sock);
+
+    /* disconnect all clients */
+    while((c = s->conn_list))
+        bcmclis_disconnect(c);
+
+    /* destroy server */
+    bcmos_fastlock_lock(&s->lock);
+    if (prev)
+        prev->next = s->next;
+    else
+        bcmclis_servers = s->next;
+    bcmos_fastlock_unlock(&s->lock);
+
+    bcmos_free(s);
+    return 0;
+}
+
+/*
+ * Shell command handlers
+ */
+
+static bcmcli_enum_val transport_type_enum_tabdmfe[] = {
+    { .name="domain_socket", .val=BCMCLI_TRANSPORT_DOMAIN_SOCKET},
+    { .name="tcp_socket", .val=BCMCLI_TRANSPORT_TCP_SOCKET},
+    BCMCLI_ENUM_LAST
+};
+
+static bcmcli_enum_val access_type_enum_tabdmfe[] = {
+    { .name="guest", .val=BCMCLI_ACCESS_GUEST},
+    { .name="admin", .val=BCMCLI_ACCESS_ADMIN},
+    { .name="debug", .val=BCMCLI_ACCESS_DEBUG},
+    BCMCLI_ENUM_LAST
+};
+
+/* Create remote shell server
+    BCMCLI_MAKE_PARM_ENUM("transport", "Transport type", transport_type_enum_tabdmfe, 0),
+    BCMCLI_MAKE_PARM("address", "Bind address", BCMCLI_PARM_STRING, 0),
+    BCMCLI_MAKE_PARM_ENUM("access", "Access level", access_type_enum_tabdmfe, 0),
+    BCMCLI_MAKE_PARM_DEFVAL("max_clients", "Max clients. 0=default", BCMCLI_PARM_NUMBER, 0, 0),
+*/
+static int bcmclis_mon_create(bcmcli_session *session, const bcmcli_cmd_parm parm[],  uint16_t n_parms)
+{
+    bcmclis_transport_type_t transport = (bcmclis_transport_type_t)parm[0].value.number;
+    char *address = (char *)parm[1].value.number;
+    bcmcli_access_right access = (bcmcli_access_right)parm[2].value.number;
+    int max_clients = (int)parm[3].value.number;
+    bcmclis_parm_t parms;
+    int hs;
+    int rc;
+
+    memset(&parms, 0, sizeof(parms));
+    parms.transport = transport;
+    parms.access = access;
+    parms.address = address;
+    parms.max_clients = max_clients;
+    rc = bcmclis_server_create(&parms, &hs);
+    if (rc)
+        bcmcli_session_print(session, "bcmclis_server_create() failed with rc=%d - %s\n",
+                        rc, bcmos_strerror(rc));
+    else
+        bcmcli_session_print(session, "Remote shell server created. Server id %d\n", hs);
+    return rc;
+}
+
+/* Destroy remote shell server
+    BCMCLI_MAKE_PARM("server_id", "Server id", BCMCLI_PARM_NUMBER, 0),
+*/
+static int bcmclis_mon_destroy(bcmcli_session *session, const bcmcli_cmd_parm parm[],  uint16_t n_parms)
+{
+    int hs = (int)parm[0].value.number;
+    int rc;
+    rc = bcmclis_server_destroy(hs);
+    bcmcli_session_print(session, "Remote shell server %d destroyed. rc=%d - %s\n",
+        hs, rc, bcmos_strerror(rc));
+    return rc;
+}
+
+/* Show remote shell servers
+*/
+static int bcmclis_mon_show(bcmcli_session *session, const bcmcli_cmd_parm parm[],  uint16_t n_parms)
+{
+    bcmclis_server_t *s=bcmclis_servers;
+    bcmclis_conn_t *c;
+    while(s)
+    {
+        bcmcli_session_print(session, "Remote server %d at %s\n", s->id, s->parms.address);
+        c = s->conn_list;
+        while(c)
+        {
+            bcmcli_session_print(session, "\t - %s. bytes sent:%d received:%d\n",
+                c->address, c->bytes_sent, c->bytes_received);
+            c = c->next;
+        }
+        s = s->next;
+    }
+    return 0;
+}
+
+/* Create shell_server directory in root_dir
+   Returns the "shell_server" directory handle
+*/
+bcmcli_entry *bcmclis_server_mon_init(bcmcli_entry *root_dir)
+{
+    bcmcli_entry *shell_dir;
+
+    if ((shell_dir=bcmcli_dir_find(NULL, "shell_server"))!=NULL)
+        return NULL;
+
+    shell_dir = bcmcli_dir_add(root_dir, "shell_server",
+                             "Remote Shell",
+                             BCMCLI_ACCESS_GUEST, NULL);
+
+    {
+        static bcmcli_cmd_parm parms[]={
+            BCMCLI_MAKE_PARM_ENUM("transport", "Transport type", transport_type_enum_tabdmfe, 0),
+            BCMCLI_MAKE_PARM("address", "Bind address: domain_socket address or TCP port", BCMCLI_PARM_STRING, 0),
+            BCMCLI_MAKE_PARM_ENUM("access", "Access level", access_type_enum_tabdmfe, 0),
+            BCMCLI_MAKE_PARM_DEFVAL("max_clients", "Max clients. 0=default", BCMCLI_PARM_NUMBER, 0, 0),
+            BCMCLI_PARM_LIST_TERMINATOR
+        };
+        bcmcli_cmd_add(shell_dir, "create", bcmclis_mon_create,
+                      "Create remote shell server",
+                      BCMCLI_ACCESS_ADMIN, NULL, parms);
+    }
+
+    {
+        static bcmcli_cmd_parm parms[]={
+            BCMCLI_MAKE_PARM("server_id", "Server id", BCMCLI_PARM_NUMBER, 0),
+            BCMCLI_PARM_LIST_TERMINATOR
+        };
+        bcmcli_cmd_add(shell_dir, "destroy", bcmclis_mon_destroy,
+                      "Destroy remote shell server",
+                      BCMCLI_ACCESS_ADMIN, NULL, parms);
+    }
+
+    {
+        bcmcli_cmd_add(shell_dir, "show", bcmclis_mon_show,
+                      "Show remote shell servers",
+                      BCMCLI_ACCESS_GUEST, NULL, NULL);
+    }
+
+    return shell_dir;
+}
+
+/* Destroy shell_server directory
+*/
+void bcmclis_server_mon_destroy(void)
+{
+    bcmcli_entry *shell_dir;
+    shell_dir=bcmcli_dir_find(NULL, "shell_server");
+    if (shell_dir)
+        bcmcli_token_destroy(shell_dir);
+}
+