blob: 160d98d4c0b4db590fdc19ae607bde289cd1a297 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30
31/*******************************************************************
32 * bcmcli_server.c
33 *
34 * CLI engine - remote shell support
35 *
36 * This module is a back-end of remote shell support.
37 * - multiple servers
38 * - domain and TCP-based connections
39 * - session access level - per server
40 *******************************************************************/
41
42#include <bcmcli_server.h>
43
44typedef struct bcmclis_server bcmclis_server_t;
45
46/* Server connection
47 */
48typedef struct bcmclis_conn
49{
50 struct bcmclis_conn *next;
51 bcmclis_server_t *server;
52 const char *address; /* client address */
53 int sock; /* transport socket */
54 bdmf_task rx_thread;
55 bcmcli_session *session;
56 uint32_t bytes_sent;
57 uint32_t bytes_received;
58 bdmf_task conn_thread;
59} bcmclis_conn_t;
60
61/* Server control bdmfock
62 */
63struct bcmclis_server
64{
65 bcmclis_server_t *next;
66 bcmclis_conn_t *conn_list;
67 int sock; /* listening socket */
68 bcmclis_parm_t parms;
69 int id;
70 int nconns;
71 bcmos_fastlock lock;
72 bdmf_task listen_thread;
73};
74
75/* socaddr variants */
76typedef union
77{
78 struct sockaddr sa;
79 struct sockaddr_un domain_sa;
80 struct sockaddr_in tcp_sa;
81} sockaddr_any;
82
83static bcmclis_server_t *bcmclis_servers;
84static int bcmclis_server_id;
85
86static bcmclis_server_t *bcmclis_id_to_server(int hs, bcmclis_server_t **prev)
87{
88 bcmclis_server_t *s=bcmclis_servers;
89 if (prev)
90 *prev = NULL;
91 while(s)
92 {
93 if (s->id == hs)
94 break;
95 if (prev)
96 *prev = s;
97 s = s->next;
98 }
99 return s;
100}
101
102/* Parse address helper */
103static int bcmclis_parse_address(const bcmclis_parm_t *parms, int *protocol, sockaddr_any *sa, int *len)
104{
105 switch(parms->transport)
106 {
107 case BCMCLI_TRANSPORT_DOMAIN_SOCKET:
108 {
109 *protocol = AF_UNIX;
110 sa->domain_sa.sun_family = AF_UNIX; /* local is declared before socket() ^ */
111 strcpy(sa->domain_sa.sun_path, parms->address);
112 *len = strlen(sa->domain_sa.sun_path) + sizeof(sa->domain_sa.sun_family);
113 break;
114 }
115 case BCMCLI_TRANSPORT_TCP_SOCKET:
116 {
117 *protocol = AF_INET;
118 sa->tcp_sa.sin_family = AF_INET;
119 sa->tcp_sa.sin_port = htons(atoi(parms->address));
120 sa->tcp_sa.sin_addr.s_addr = INADDR_ANY;
121 *len = sizeof(sa->tcp_sa);
122 break;
123 }
124 default:
125 return BCM_ERR_PARM;
126 }
127 return 0;
128}
129
130
131/* disconnect client and clear resources */
132static void bcmclis_disconnect(bcmclis_conn_t *conn)
133{
134 bcmclis_server_t *s=conn->server;
135 bcmclis_conn_t *c=s->conn_list, *prev=NULL;
136
137 bcmos_fastlock_lock(&s->lock);
138 while(c && c!=conn)
139 {
140 prev = c;
141 c = c->next;
142 }
143 BUG_ON(!c);
144 if (prev)
145 prev->next = c->next;
146 else
147 s->conn_list = c->next;
148 --s->nconns;
149 bcmos_fastlock_unlock(&s->lock);
150 bcmcli_session_close(c->session);
151 close(c->sock);
152 bdmf_task_destroy(c->rx_thread);
153 bcmos_free(c);
154}
155
156/*
157 * Session callbacks
158 */
159
160/** Session's output function.
161 * returns the number of bytes written or <0 if error
162 */
163static int bcmclis_cb_sess_write(void *user_priv, const void *buf, uint32_t size)
164{
165 bcmclis_conn_t *c=user_priv;
166 int rc;
167
168 rc = send(c->sock, buf, size, 0);
169 /* disconnect if IO error */
170 if (rc < size)
171 bcmclis_disconnect(c);
172 else
173 c->bytes_sent += rc;
174 return rc;
175}
176
177#define CHAR_EOT 0x04
178
179/** Session's input function.
180 * returns the number of bytes read or <0 if error
181 */
182static char *bcmclis_read_line(bcmclis_conn_t *c, char *buf, uint32_t size)
183{
184 int i;
185 int rc;
186 int len=0;
187
188 for(i=0; i<size-1; i++)
189 {
190 char ch;
191 rc = recv(c->sock, &ch, 1, MSG_WAITALL);
192 if (rc <= 0)
193 break;
194 if (ch == '\r')
195 continue;
196 if (ch == CHAR_EOT)
197 break;
198 buf[len++] = ch;
199 if (ch == '\n')
200 break;
201 }
202 c->bytes_received += i;
203 buf[len] = 0;
204 return (len ? buf : NULL);
205}
206
207/* Receive handler */
208static int bcmclis_rx_thread_handler(void *arg)
209{
210 char buf[512];
211 bcmclis_conn_t *c=arg;
212
213 while(!bcmcli_is_stopped(c->session) &&
214 bcmclis_read_line(c, buf, sizeof(buf)))
215 {
216 bcmcli_parse(c->session, buf);
217 }
218 bcmclis_disconnect(c);
219 return 0;
220}
221
222/* New client connection indication */
223static void bcmclis_connect(bcmclis_server_t *s, char *addr, int sock)
224{
225 bcmclis_conn_t *c;
226 bcmcli_session_parm sess_parm;
227 int rc;
228
229 if (s->parms.max_clients && s->nconns >= s->parms.max_clients)
230 {
231 bcmos_printf("bdmfmons: server %s: refused connection because max number has been reached\n", s->parms.address);
232 close(sock);
233 return;
234 }
235
236 c = bcmos_calloc(sizeof(*c) + strlen(addr) + 1);
237 if (!c)
238 goto cleanup;
239 c->address = (char *)c + sizeof(*c);
240 strcpy((char *)c->address, addr);
241 c->server = s;
242 c->sock = sock;
243
244 /* create new management session */
245 memset(&sess_parm, 0, sizeof(sess_parm));
246 sess_parm.access_right = s->parms.access;
247 sess_parm.write = bcmclis_cb_sess_write;
248 sess_parm.user_priv = c;
249 rc = bcmcli_session_open(&sess_parm, &c->session);
250 if (rc)
251 goto cleanup;
252
253 /* wait for receive in a separate thread */
254 rc = bdmf_task_create("bcmclis_rx",
255 BDMFSYS_DEFAULT_TASK_PRIORITY,
256 BDMFSYS_DEFAULT_TASK_STACK,
257 bcmclis_rx_thread_handler, c,
258 &c->rx_thread);
259 if (rc)
260 goto cleanup;
261
262 bcmos_fastlock_lock(&s->lock);
263 c->next = s->conn_list;
264 s->conn_list = c;
265 ++s->nconns;
266 bcmos_fastlock_unlock(&s->lock);
267
268 return;
269
270cleanup:
271 close(sock);
272 if (c->session)
273 bcmcli_session_close(c->session);
274 if (c)
275 bcmos_free(c);
276}
277
278/* Receive handler */
279static int bcmclis_listen_thread_handler(void *arg)
280{
281 bcmclis_server_t *s=arg;
282 sockaddr_any addr;
283 socklen_t len;
284 int sock;
285
286 while(1)
287 {
288 char caddr[64];
289 len = sizeof(addr);
290 sock = accept(s->sock, &addr.sa, &len);
291 if (sock < 0)
292 {
293 perror("accept");
294 break;
295 }
296 if (s->parms.transport==BCMCLI_TRANSPORT_DOMAIN_SOCKET)
297 strncpy(caddr, s->parms.address, sizeof(caddr)-1);
298 else
299 {
300 snprintf(caddr, sizeof(caddr)-1, "%s:%d",
301 inet_ntoa(addr.tcp_sa.sin_addr), ntohs(addr.tcp_sa.sin_port));
302 }
303 bcmclis_connect(s, caddr, sock);
304 }
305 return 0;
306}
307
308/*
309 * External API
310 */
311
312/** Create shell server.
313 * Immediately after creation server is ready to accept client connections
314 * \param[in] parms Server parameters
315 * \param[out] hs Server handle
316 * \return 0 - OK\n
317 * <0 - error code
318 */
319bcmos_errno bcmclis_server_create(const bcmclis_parm_t *parms, int *hs)
320{
321 bcmclis_server_t *s;
322 int protocol;
323 sockaddr_any sa;
324 int len;
325 int rc;
326
327 if (!parms || !hs || !parms->address)
328 return BCM_ERR_PARM;
329
330 /* parse address */
331 if (bcmclis_parse_address(parms, &protocol, &sa, &len))
332 return BCM_ERR_PARM;
333
334 /* allocate server structure */
335 s = bcmos_calloc(sizeof(bcmclis_server_t)+strlen(parms->address)+1);
336 if (!s)
337 return BCM_ERR_NOMEM;
338 s->parms = *parms;
339 s->parms.address = (char *)s + sizeof(*s);
340 strcpy(s->parms.address, parms->address);
341 s->id = ++bcmclis_server_id;
342 bcmos_fastlock_init(&s->lock);
343
344 /* create socket and start listening */
345 s->sock = socket(protocol, SOCK_STREAM, 0);
346 if ((s->sock < 0) ||
347 (bind(s->sock, &sa.sa, len) < 0) ||
348 (listen(s->sock, 1) < 0))
349 {
350 perror("socket/bind/listen");
351 close(s->sock);
352 bcmos_free(s);
353 return BCM_ERR_PARM;
354 }
355
356 /* wait for connection(s) in a separate thread */
357 rc = bdmf_task_create("bcmclis_listen",
358 BDMFSYS_DEFAULT_TASK_PRIORITY,
359 BDMFSYS_DEFAULT_TASK_STACK,
360 bcmclis_listen_thread_handler, s,
361 &s->listen_thread);
362 if (rc)
363 {
364 close(s->sock);
365 bcmos_free(s);
366 return rc;
367 }
368
369 /* all good */
370 s->next = bcmclis_servers;
371 bcmclis_servers = s;
372 *hs = s->id;
373
374 return 0;
375}
376
377/** Destroy shell server.
378 * All client connections if any are closed
379 * \param[in] hs Server handle
380 * \return 0 - OK\n
381 * <0 - error code
382 */
383bcmos_errno bcmclis_server_destroy(int hs)
384{
385 bcmclis_server_t *prev;
386 bcmclis_server_t *s = bcmclis_id_to_server(hs, &prev);
387 bcmclis_conn_t *c;
388 if (!s)
389 return BCM_ERR_NOENT;
390
391 bdmf_task_destroy(s->listen_thread);
392 close(s->sock);
393
394 /* disconnect all clients */
395 while((c = s->conn_list))
396 bcmclis_disconnect(c);
397
398 /* destroy server */
399 bcmos_fastlock_lock(&s->lock);
400 if (prev)
401 prev->next = s->next;
402 else
403 bcmclis_servers = s->next;
404 bcmos_fastlock_unlock(&s->lock);
405
406 bcmos_free(s);
407 return 0;
408}
409
410/*
411 * Shell command handlers
412 */
413
414static bcmcli_enum_val transport_type_enum_tabdmfe[] = {
415 { .name="domain_socket", .val=BCMCLI_TRANSPORT_DOMAIN_SOCKET},
416 { .name="tcp_socket", .val=BCMCLI_TRANSPORT_TCP_SOCKET},
417 BCMCLI_ENUM_LAST
418};
419
420static bcmcli_enum_val access_type_enum_tabdmfe[] = {
421 { .name="guest", .val=BCMCLI_ACCESS_GUEST},
422 { .name="admin", .val=BCMCLI_ACCESS_ADMIN},
423 { .name="debug", .val=BCMCLI_ACCESS_DEBUG},
424 BCMCLI_ENUM_LAST
425};
426
427/* Create remote shell server
428 BCMCLI_MAKE_PARM_ENUM("transport", "Transport type", transport_type_enum_tabdmfe, 0),
429 BCMCLI_MAKE_PARM("address", "Bind address", BCMCLI_PARM_STRING, 0),
430 BCMCLI_MAKE_PARM_ENUM("access", "Access level", access_type_enum_tabdmfe, 0),
431 BCMCLI_MAKE_PARM_DEFVAL("max_clients", "Max clients. 0=default", BCMCLI_PARM_NUMBER, 0, 0),
432*/
433static int bcmclis_mon_create(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
434{
435 bcmclis_transport_type_t transport = (bcmclis_transport_type_t)parm[0].value.number;
436 char *address = (char *)parm[1].value.number;
437 bcmcli_access_right access = (bcmcli_access_right)parm[2].value.number;
438 int max_clients = (int)parm[3].value.number;
439 bcmclis_parm_t parms;
440 int hs;
441 int rc;
442
443 memset(&parms, 0, sizeof(parms));
444 parms.transport = transport;
445 parms.access = access;
446 parms.address = address;
447 parms.max_clients = max_clients;
448 rc = bcmclis_server_create(&parms, &hs);
449 if (rc)
450 bcmcli_session_print(session, "bcmclis_server_create() failed with rc=%d - %s\n",
451 rc, bcmos_strerror(rc));
452 else
453 bcmcli_session_print(session, "Remote shell server created. Server id %d\n", hs);
454 return rc;
455}
456
457/* Destroy remote shell server
458 BCMCLI_MAKE_PARM("server_id", "Server id", BCMCLI_PARM_NUMBER, 0),
459*/
460static int bcmclis_mon_destroy(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
461{
462 int hs = (int)parm[0].value.number;
463 int rc;
464 rc = bcmclis_server_destroy(hs);
465 bcmcli_session_print(session, "Remote shell server %d destroyed. rc=%d - %s\n",
466 hs, rc, bcmos_strerror(rc));
467 return rc;
468}
469
470/* Show remote shell servers
471*/
472static int bcmclis_mon_show(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
473{
474 bcmclis_server_t *s=bcmclis_servers;
475 bcmclis_conn_t *c;
476 while(s)
477 {
478 bcmcli_session_print(session, "Remote server %d at %s\n", s->id, s->parms.address);
479 c = s->conn_list;
480 while(c)
481 {
482 bcmcli_session_print(session, "\t - %s. bytes sent:%d received:%d\n",
483 c->address, c->bytes_sent, c->bytes_received);
484 c = c->next;
485 }
486 s = s->next;
487 }
488 return 0;
489}
490
491/* Create shell_server directory in root_dir
492 Returns the "shell_server" directory handle
493*/
494bcmcli_entry *bcmclis_server_mon_init(bcmcli_entry *root_dir)
495{
496 bcmcli_entry *shell_dir;
497
498 if ((shell_dir=bcmcli_dir_find(NULL, "shell_server"))!=NULL)
499 return NULL;
500
501 shell_dir = bcmcli_dir_add(root_dir, "shell_server",
502 "Remote Shell",
503 BCMCLI_ACCESS_GUEST, NULL);
504
505 {
506 static bcmcli_cmd_parm parms[]={
507 BCMCLI_MAKE_PARM_ENUM("transport", "Transport type", transport_type_enum_tabdmfe, 0),
508 BCMCLI_MAKE_PARM("address", "Bind address: domain_socket address or TCP port", BCMCLI_PARM_STRING, 0),
509 BCMCLI_MAKE_PARM_ENUM("access", "Access level", access_type_enum_tabdmfe, 0),
510 BCMCLI_MAKE_PARM_DEFVAL("max_clients", "Max clients. 0=default", BCMCLI_PARM_NUMBER, 0, 0),
511 BCMCLI_PARM_LIST_TERMINATOR
512 };
513 bcmcli_cmd_add(shell_dir, "create", bcmclis_mon_create,
514 "Create remote shell server",
515 BCMCLI_ACCESS_ADMIN, NULL, parms);
516 }
517
518 {
519 static bcmcli_cmd_parm parms[]={
520 BCMCLI_MAKE_PARM("server_id", "Server id", BCMCLI_PARM_NUMBER, 0),
521 BCMCLI_PARM_LIST_TERMINATOR
522 };
523 bcmcli_cmd_add(shell_dir, "destroy", bcmclis_mon_destroy,
524 "Destroy remote shell server",
525 BCMCLI_ACCESS_ADMIN, NULL, parms);
526 }
527
528 {
529 bcmcli_cmd_add(shell_dir, "show", bcmclis_mon_show,
530 "Show remote shell servers",
531 BCMCLI_ACCESS_GUEST, NULL, NULL);
532 }
533
534 return shell_dir;
535}
536
537/* Destroy shell_server directory
538*/
539void bcmclis_server_mon_destroy(void)
540{
541 bcmcli_entry *shell_dir;
542 shell_dir=bcmcli_dir_find(NULL, "shell_server");
543 if (shell_dir)
544 bcmcli_token_destroy(shell_dir);
545}
546