blob: 519eaa8a723fb90d8de2646ca874e189e91645c4 [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_session.c
33 *
34 * CLI engine - session management
35 *
36 *******************************************************************/
37
38#include <bcmos_system.h>
39#include <bcmolt_utils.h>
40
41#define BCMCLI_INTERNAL
42#include <bcmcli_session.h>
43
44static bcmos_fastlock session_lock;
45static bcmcli_session *session_list;
46static int session_module_initialized;
47
48/*
49 * Internal functions
50 */
51
52static void _bcmcli_session_update_prompt(bcmcli_session *session)
53{
54 if (!session)
55 return;
56
57 if (session->parms.get_prompt)
58 {
59 session->parms.get_prompt(session, session->prompt_buf, BCMCLI_MAX_PROMPT_LEN);
60 session->prompt_buf[BCMCLI_MAX_PROMPT_LEN - 1] = '\0';
61 }
62 else
63 {
64 session->prompt_buf[0] = '\0';
65 }
66}
67
68static char *_bcmcli_session_gets(bcmcli_session *session, char *buffer, uint32_t size)
69{
70 const char *line = NULL;
71 _bcmcli_session_update_prompt(session);
72#ifdef CONFIG_LIBEDIT
73 if (session && session->el && session->history)
74 {
75 int line_len;
76 line = (el_gets(session->el, &line_len));
77 if (!line)
78 return NULL;
79 if (line_len > size)
80 {
81 bcmos_printf("%s: buffer is too short %u - got %d. Truncated\n",
82 __FUNCTION__, size, line_len);
83 }
84 strncpy(buffer, line, size);
85 if (*line && *line != '\n' && *line != '#')
86 history(session->history, &session->histevent, H_ENTER, line);
87 }
88 else
89#endif
90#ifdef CONFIG_LINENOISE
91 if (session && session->ln_session)
92 {
93 char *ln_line = linenoise(session->ln_session, session->prompt_buf, buffer, size);
94 if (ln_line)
95 {
96 if (strlen(ln_line))
97 {
98 linenoiseHistoryAdd(session->ln_session, ln_line); /* Add to the history. */
99 }
100 else
101 {
102 strncpy(buffer, "\n", size-1);
103 }
104 buffer[size-1] = 0;
105 line = buffer;
106 }
107 }
108 else
109#endif
110 {
111 bcmcli_session_print(session, "%s", session->prompt_buf);
112 if (session && session->parms.gets)
113 line = session->parms.gets(session, buffer, size);
114 else
115 line = fgets(buffer, size, stdin);
116 }
117 return line ? buffer : NULL;
118}
119
120#ifdef CONFIG_LIBEDIT
121
122static char *_bcmcli_editline_prompt(EditLine *e)
123{
124 bcmcli_session *session = NULL;
125 el_get(e, EL_CLIENTDATA, &session);
126 BUG_ON(session == NULL || session->magic != BCMCLI_SESSION_MAGIC);
127 _bcmcli_session_update_prompt(session);
128 return session->prompt_buf;
129}
130
131static int _bcmcli_editline_cfn(EditLine *el, char *c)
132{
133 bcmcli_session *session = NULL;
134 char insert_buf[80];
135 int c1;
136 bcmos_errno rc;
137
138 el_get(el, EL_CLIENTDATA, &session);
139 BUG_ON(session == NULL || session->magic != BCMCLI_SESSION_MAGIC);
140 c1 = session->parms.get_char(session);
141
142 /* ToDo: handle \t parameter extension */
143 while (c1 > 0 && c1 == '\t')
144 {
145
146 const LineInfo *li = el_line(el);
147 char *line = bcmos_alloc(li->cursor - li->buffer + 1);
148 if (!line)
149 continue;
150 memcpy(line, li->buffer, li->cursor - li->buffer);
151 line[li->cursor - li->buffer] = 0;
152 rc = bcmcli_extend(session, line, insert_buf, sizeof(insert_buf));
153 bcmos_free(line);
154 if (rc)
155 {
156 c1 = session->parms.get_char(session);
157 continue;
158 }
159 el_insertstr(el, insert_buf);
160 printf("\r");
161 el_set(el, EL_REFRESH, NULL);
162 c1 = session->parms.get_char(session);
163 }
164 if (c1 < 0)
165 return -1;
166 *c = c1;
167 return 1;
168}
169#endif
170
171/* linenoise line editing library: completion support */
172#ifdef CONFIG_LINENOISE
173
174static int _bcmcli_linenoise_read_char(long fd_in, char *c)
175{
176 bcmcli_session *session = (bcmcli_session *)fd_in;
177 int c1;
178 c1 = session->parms.get_char(session);
179 if (c1 < 0)
180 {
181 return -1;
182 }
183 *c = c1;
184 return 1;
185}
186
187static int _bcmcli_linenoise_write(long fd_out, const char *buf, size_t len)
188{
189 bcmcli_session *session = (bcmcli_session *)fd_out;
190 /* Use a shortcut for len==1 - which is char-by-char input.
191 bcmos_printf("%*s", buf, 1) misbehaves on vxw platform,
192 possibly because it is too slow.
193 */
194 if (len == 1 && !session->parms.write)
195 {
196 bcmos_putchar(buf[0]);
197 return 1;
198 }
199 return bcmcli_session_write(session, buf, len);
200}
201
202static int _bcmcli_linenoise_tab(linenoiseSession *ln_session, const char *buf, int pos)
203{
204 bcmcli_session *session = NULL;
205 char *line;
206 char insert_buf[80]="";
207 bcmos_errno rc;
208 int len;
209
210 session = linenoiseSessionData(ln_session);
211 BUG_ON(session == NULL || session->magic != BCMCLI_SESSION_MAGIC);
212 line = bcmos_alloc(strlen(buf)+1);
213 if (!line)
214 return 0;
215 strcpy(line, buf);
216 rc = bcmcli_extend(session, line, insert_buf, sizeof(insert_buf));
217 bcmos_free(line);
218 if (rc || !strlen(insert_buf))
219 return 0;
220
221 len = strlen(buf);
222 line = bcmos_alloc(strlen(buf)+strlen(insert_buf)+1);
223 if (!line)
224 return 0;
225 if (pos >=0 && pos < len)
226 {
227 strncpy(line, buf, pos);
228 line[pos] = 0;
229 strcat(line, insert_buf);
230 strcat(line, &buf[pos]);
231 pos += strlen(insert_buf);
232 }
233 else
234 {
235 strcpy(line, buf);
236 strcat(line, insert_buf);
237 pos = strlen(line);
238 }
239 linenoiseSetBuffer(ln_session, line, pos);
240 bcmos_free(line);
241 return 1;
242}
243
244#endif
245
246/* Default getc function */
247static int _bcmcli_session_get_char(bcmcli_session *session)
248{
249 return bcmos_getchar();
250}
251
252/** Initialize session management module
253 * \return
254 * 0 =OK\n
255 * <0 =error code
256 */
257static void bcmcli_session_module_init(void)
258{
259 bcmos_fastlock_init(&session_lock, 0);
260 session_module_initialized = 1;
261}
262
263/** Open management session */
264int bcmcli_session_open_user(const bcmcli_session_parm *parm, bcmcli_session **p_session)
265{
266 bcmcli_session *session;
267 bcmcli_session **p_last_next;
268 const char *name;
269 char *name_clone;
270 long flags;
271 int size;
272
273 if (!p_session || !parm)
274 return BCM_ERR_PARM;
275#ifndef CONFIG_EDITLINE
276 if (parm->line_edit_mode == BCMCLI_LINE_EDIT_ENABLE)
277 {
278 bcmos_trace(BCMOS_TRACE_LEVEL_ERROR, "Line editing feature is not compiled in. define CONFIG_EDITLINE\n");
279 return BCM_ERR_NOT_SUPPORTED;
280 }
281#endif
282 if (!session_module_initialized)
283 bcmcli_session_module_init();
284 name = parm->name;
285 if (!name)
286 name = "*unnamed*";
287 size = sizeof(bcmcli_session) + strlen(name) + 1 + parm->extra_size;
288 session=bcmos_calloc(size);
289 if (!session)
290 return BCM_ERR_NOMEM;
291 session->parms = *parm;
292 name_clone = (char *)session + sizeof(bcmcli_session) + parm->extra_size;
293 strcpy(name_clone, name);
294 session->parms.name = name_clone;
295 if (!session->parms.get_char)
296 session->parms.get_char = _bcmcli_session_get_char;
297
298#ifdef CONFIG_LIBEDIT
299 if (!parm->gets && (parm->line_edit_mode == BCMCLI_LINE_EDIT_ENABLE ||
300 parm->line_edit_mode == BCMCLI_LINE_EDIT_DEFAULT))
301 {
302 /* Initialize editline library */
303 session->el = el_init(session->parms.name, stdin, stdout, stderr);
304 session->history = history_init();
305 if (session->el && session->history)
306 {
307 el_set(session->el, EL_EDITOR, "emacs");
308 el_set(session->el, EL_PROMPT, &_bcmcli_editline_prompt);
309 el_set(session->el, EL_TERMINAL, "xterm");
310 el_set(session->el, EL_GETCFN, _bcmcli_editline_cfn);
311 el_set(session->el, EL_CLIENTDATA, session);
312 history(session->history, &session->histevent, H_SETSIZE, 800);
313 el_set(session->el, EL_HIST, history, session->history);
314 }
315 else
316 {
317 bcmos_trace(BCMOS_TRACE_LEVEL_ERROR, "Can't initialize editline library\n");
318 bcmos_free(session);
319 return BCM_ERR_INTERNAL;
320 }
321 }
322#endif
323
324#ifdef CONFIG_LINENOISE
325 /* Set the completion callback. This will be called every time the
326 * user uses the <tab> key. */
327 if (!parm->gets && (parm->line_edit_mode == BCMCLI_LINE_EDIT_ENABLE ||
328 parm->line_edit_mode == BCMCLI_LINE_EDIT_DEFAULT))
329 {
330 linenoiseSessionIO io={.fd_in=(long)session, .fd_out=(long)session,
331 .read_char=_bcmcli_linenoise_read_char,
332 .write=_bcmcli_linenoise_write
333 };
334 if (linenoiseSessionOpen(&io, session, &session->ln_session))
335 {
336 bcmos_trace(BCMOS_TRACE_LEVEL_ERROR, "Can't create linenoise session\n");
337 bcmos_free(session);
338 return BCM_ERR_INTERNAL;
339 }
340 linenoiseSetCompletionCallback(session->ln_session, _bcmcli_linenoise_tab);
341 }
342#endif
343
344 session->magic = BCMCLI_SESSION_MAGIC;
345
346 flags = bcmos_fastlock_lock(&session_lock);
347 p_last_next = &session_list;
348 while(*p_last_next)
349 p_last_next = &((*p_last_next)->next);
350 *p_last_next = session;
351 bcmos_fastlock_unlock(&session_lock, flags);
352
353 *p_session = session;
354
355 return 0;
356}
357
358static int bcmcli_session_string_write(bcmcli_session *session, const char *buf, uint32_t size)
359{
360 bcmolt_string *str = bcmcli_session_user_priv(session);
361 return bcmolt_string_copy(str, buf, size);
362}
363
364bcmos_errno bcmcli_session_open_string(bcmcli_session **session, bcmolt_string *str)
365{
366 bcmcli_session_parm sp = { .user_priv = str, .write = bcmcli_session_string_write };
367
368 return bcmcli_session_open_user(&sp, session);
369}
370
371/** Close management session.
372 * \param[in] session Session handle
373 */
374void bcmcli_session_close(bcmcli_session *session)
375{
376 long flags;
377
378 BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
379 flags = bcmos_fastlock_lock(&session_lock);
380 if (session==session_list)
381 session_list = session->next;
382 else
383 {
384 bcmcli_session *prev = session_list;
385 while (prev && prev->next != session)
386 prev = prev->next;
387 if (!prev)
388 {
389 bcmos_fastlock_unlock(&session_lock, flags);
390 bcmos_trace(BCMOS_TRACE_LEVEL_ERROR, "%s: can't find session\n", __FUNCTION__);
391 return;
392 }
393 prev->next = session->next;
394 }
395 bcmos_fastlock_unlock(&session_lock, flags);
396
397#ifdef CONFIG_LIBEDIT
398 if (session->history)
399 history_end(session->history);
400 if (session->el)
401 el_end(session->el);
402#endif
403#ifdef CONFIG_LINENOISE
404 if (session->ln_session)
405 linenoiseSessionClose(session->ln_session);
406#endif
407 session->magic = BCMCLI_SESSION_MAGIC_DEL;
408 bcmos_free(session);
409
410}
411
412/** Configure RAW input mode
413 *
414 * \param[in] session Session handle
415 * \param[in] is_raw TRUE=enable raw mode, FALSE=disable raw mode
416 * \return
417 * =0 - OK \n
418 * BCM_ERR_NOT_SUPPORTED - raw mode is not supported\n
419 */
420bcmos_errno bcmcli_session_raw_mode_set(bcmcli_session *session, bcmos_bool is_raw)
421{
422#ifdef CONFIG_LINENOISE
423 int rc;
424 if (session->parms.gets)
425 return BCM_ERR_NOT_SUPPORTED;
426 rc = linenoiseSetRaw(session->ln_session, is_raw);
427 return (rc == 0) ? BCM_ERR_OK : BCM_ERR_NOT_SUPPORTED;
428#else
429 return BCM_ERR_NOT_SUPPORTED;
430#endif
431}
432
433/** Default write callback function
434 * write to stdout
435 */
436static int _bcmcli_session_write(bcmcli_session *session, const char *buf, uint32_t size)
437{
438 return bcmos_printf("%.*s", size, buf);
439}
440
441
442/** Write function.
443 * Write buffer to the current session.
444 * \param[in] session Session handle. NULL=use stdout
445 * \param[in] buffer output buffer
446 * \param[in] size number of bytes to be written
447 * \return
448 * >=0 - number of bytes written\n
449 * <0 - output error
450 */
451int bcmcli_session_write(bcmcli_session *session, const char *buf, uint32_t size)
452{
453 int (*write_cb)(bcmcli_session *session, const char *buf, uint32_t size);
454 if (session && session->parms.write)
455 {
456 BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
457 write_cb = session->parms.write;
458 }
459 else
460 write_cb = _bcmcli_session_write;
461 return write_cb(session, buf, size);
462}
463
464
465/** Read line
466 * \param[in] session Session handle. NULL=use default
467 * \param[in,out] buf input buffer
468 * \param[in] size buf size
469 * \return
470 * buf if successful
471 * NULL if EOF or error
472 */
473char *bcmcli_session_gets(bcmcli_session *session, char *buf, uint32_t size)
474{
475 return _bcmcli_session_gets(session, buf, size);
476}
477
478
479/** Print function.
480 * Prints in the context of current session.
481 * \param[in] session Session handle. NULL=use stdout
482 * \param[in] format print format - as in printf
483 * \param[in] ap parameters list. Undefined after the call
484 */
485void bcmcli_session_vprint(bcmcli_session *session, const char *format, va_list ap)
486{
487 if (session && session->parms.write)
488 {
489 BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
490 vsnprintf(session->outbuf, sizeof(session->outbuf), format, ap);
491 bcmcli_session_write(session, session->outbuf, strlen(session->outbuf));
492 }
493 else
494 bcmos_vprintf(format, ap);
495}
496
497
498/** Print function.
499 * Prints in the context of current session.
500 * \param[in] session Session handle. NULL=use stdout
501 * \param[in] format print format - as in printf
502 */
503void bcmcli_session_print(bcmcli_session *session, const char *format, ...)
504{
505 va_list ap;
506 va_start(ap, format);
507 bcmcli_session_vprint(session, format, ap);
508 va_end(ap);
509}
510
511/** Get user_priv provoded in session partameters when it was registered
512 * \param[in] session Session handle. NULL=use stdin
513 * \return usr_priv value
514 */
515void *bcmcli_session_user_priv(bcmcli_session *session)
516{
517 if (!session)
518 return NULL;
519 BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
520 return session->parms.user_priv;
521}
522
523
524/** Get extra data associated with the session
525 * \param[in] session Session handle. NULL=default session
526 * \return extra_data pointer or NULL if there is no extra data
527 */
528void *bcmcli_session_data(bcmcli_session *session)
529{
530 if (!session)
531 return NULL;
532 BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
533 if (session->parms.extra_size <= 0)
534 return NULL;
535 return (char *)session + sizeof(*session);
536}
537
538
539/** Get session namedata
540 * \param[in] session Session handle. NULL=default session
541 * \return session name
542 */
543const char *bcmcli_session_name(bcmcli_session *session)
544{
545 if (!session)
546 return NULL;
547 BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
548 return session->parms.name;
549}
550
551
552/** Get session access righte
553 * \param[in] session Session handle. NULL=default debug session
554 * \return session access right
555 */
556bcmcli_access_right bcmcli_session_access_right(bcmcli_session *session)
557{
558 if (!session)
559 return BCMCLI_ACCESS_DEBUG;
560 BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
561 return session->parms.access_right;
562}
563
564/** Print buffer in hexadecimal format
565 * \param[in] session Session handle. NULL=use stdout
566 * \param[in] buffer Buffer address
567 * \param[in] offset Start offset in the buffer
568 * \param[in] count Number of bytes to dump
569 * \param[in] indent Optional indentation string
570 */
571void bcmcli_session_hexdump(bcmcli_session *session, const void *buffer, uint32_t offset, uint32_t count, const char *indent)
572{
573 bcmos_hexdump((bcmos_msg_print_cb)bcmcli_session_print, session, buffer, offset, count, indent);
574}
575
576/*
577 * Exports
578 */
579EXPORT_SYMBOL(bcmcli_session_open);
580EXPORT_SYMBOL(bcmcli_session_close);
581EXPORT_SYMBOL(bcmcli_session_write);
582EXPORT_SYMBOL(bcmcli_session_vprint);
583EXPORT_SYMBOL(bcmcli_session_print);
584EXPORT_SYMBOL(bcmcli_session_access_right);
585EXPORT_SYMBOL(bcmcli_session_data);
586EXPORT_SYMBOL(bcmcli_session_name);
587EXPORT_SYMBOL(bcmcli_session_hexdump);