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);
+}
+