cli.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. #pragma GCC diagnostic error "-Wall"
  2. #include "cli.h"
  3. #include <string.h>
  4. #include "FreeRTOS.h"
  5. #include "task.h"
  6. #include "FreeRTOS_CLI.h"
  7. #include "CLI_Commands.h"
  8. #include "settings_api.h"
  9. #include "parameters.h"
  10. #include "control_symbol.h"
  11. #include "log.h"
  12. #include "web_params_api.h"
  13. #include "tinystdio.h"
  14. #include "telnet_server.h"
  15. #define ISO_nl 0x0a
  16. #define ISO_cr 0x0d
  17. #define ISO_etx 0x03
  18. #define ISO_del 0x7f
  19. #define REPEAT_SENSOR_INFO_TIME configTICK_RATE_HZ*5*1
  20. /**
  21. * @brief Общая структура настроек
  22. */
  23. extern SETTINGS_t sSettings;
  24. static const char * const pcEndOfCommandOutputString = "\r\n[Нажмите клавишу ENTER для повторного выполнения предыдущей команды]\r\n>";
  25. const char pcWarningMessage[] = "Количество соединенений превышено. Данное соединение будет закрыто\r\n";
  26. const unsigned pcWarningMessageLen = array_len(pcWarningMessage);
  27. extern int8_t cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
  28. cli_state_t cli_states[MAX_SESSIONS];
  29. void SensorInfoTimerCallback(TimerHandle_t pxTimer) {
  30. portBASE_TYPE xReturned = pdTRUE;
  31. uint8_t num_timer = 0;
  32. for (uint8_t i = 0; i < array_len(cli_states); i ++) {
  33. if(pxTimer == cli_states[i].RepeatSensorInfoTimer){
  34. num_timer = i;
  35. break;
  36. }
  37. }
  38. do {
  39. /* Ensure there is not a string lingering in
  40. the output buffer. */
  41. cOutputBuffer[ 0 ] = 0x00;
  42. xReturned = FreeRTOS_CLIProcessCommand(&cli_states[num_timer], (int8_t *)"sensor info", cOutputBuffer, configCOMMAND_INT_MAX_OUTPUT_SIZE);
  43. cli_states[num_timer].send(cli_states[num_timer].num_connect, (char *)cOutputBuffer, strlen( ( const char * ) cOutputBuffer ));
  44. } while( xReturned != pdFALSE );
  45. xTimerStart(cli_states[num_timer].RepeatSensorInfoTimer, 0);
  46. }
  47. static char *get_user_name(user_level_t user_id)
  48. {
  49. char *username;
  50. switch (user_id) {
  51. case 0:
  52. username = "Администратор";
  53. break;
  54. case 1:
  55. username = "Пользователь";
  56. break;
  57. default:
  58. username = "Неизвестен";
  59. break;
  60. }
  61. return username;
  62. }
  63. static void FreeRTOS_ChangePWDProcess( int8_t * pcWriteBuffer, cli_state_t *s)
  64. {
  65. static char password[MAX_WEB_PASSWD_LEN]; // trading reentrancy for memory
  66. const int8_t * const pcNewPSWHeader = ( int8_t * ) "\r\nВведите повторно новый пароль:";
  67. memset(pcWriteBuffer, 0, configCOMMAND_INT_MAX_OUTPUT_SIZE);
  68. uint32_t len = strlen(s->buf);
  69. if (len >= MAX_WEB_PASSWD_LEN) {
  70. strcpy((char *)pcWriteBuffer, "\r\nНовый пароль слишком длинный!\r\n>");
  71. s->input_state = CLI_CMD;
  72. return;
  73. }
  74. if(s->input_state == CLI_CHANGE_PWD){
  75. memset(password, 0, MAX_WEB_PASSWD_LEN);
  76. if(!control_string_en_digit(s->buf, len)){
  77. strcpy( ( char * ) pcWriteBuffer, "\r\nОшибка при вводе нового пароля\r\n>" );
  78. s->input_state = CLI_CMD;
  79. } else {
  80. strncpy(password, s->buf, len);
  81. strncpy( ( char * ) pcWriteBuffer, ( const char * ) pcNewPSWHeader, strlen( ( char * ) pcNewPSWHeader ) );
  82. s->input_state = CLI_CHANGE_PWD_ACK;
  83. }
  84. } else {
  85. if (strncmp(password, s->buf, MAX_WEB_PASSWD_LEN) == 0) {
  86. memcpy(sSettings.sAuth[s->id_change_pwd].password, password, 11);
  87. cli_save_config(s);
  88. log_event_data(LOG_PSW_CHANGE, get_user_name(s->user_id));
  89. strcpy( ( char * ) pcWriteBuffer, "\r\nПароль успешно изменен\r\n>" );
  90. } else {
  91. strcpy( ( char * ) pcWriteBuffer, "\r\nОшибка при вводе нового пароля\r\n>" );
  92. }
  93. s->input_state = CLI_CMD;
  94. }
  95. }
  96. static void cli_input(cli_state_t *s)
  97. {
  98. portBASE_TYPE xReturned;
  99. switch(s->input_state){
  100. case CLI_AUTH:
  101. case CLI_AUTH_PASSW:
  102. if(FreeRTOS_CLIAuthProcess(cOutputBuffer, s)){
  103. s->send(s->num_connect, (char *)cOutputBuffer, strlen( ( const char * ) cOutputBuffer ));
  104. memset( s->buf, 0x00, cmdMAX_INPUT_SIZE );
  105. }
  106. else{
  107. s->state = STATE_CLOSE;
  108. }
  109. break;
  110. case CLI_CMD:
  111. /* The input string has been terminated. Was the
  112. input a quit command? */
  113. if( strcmp( "quit", ( const char * ) s->buf ) == 0 )
  114. {
  115. s->state = STATE_CLOSE;
  116. }
  117. else
  118. {
  119. /* The input string was not a quit command.
  120. Pass the string to the command interpreter. */
  121. /* See if the command is empty, indicating that the last command is
  122. to be executed again. */
  123. if( s->bufptr == 0 )
  124. {
  125. if(s->flag_telnet_ip_option ){
  126. s->flag_telnet_ip_option = false;
  127. return;
  128. }
  129. else{
  130. strcpy( s->buf, s->prev_cmd );
  131. }
  132. }
  133. /* Transmit a line separator, just to make the
  134. output easier to read. */
  135. s->send(s->num_connect, "\r\n", strlen( "\r\n" ));
  136. do
  137. {
  138. /* Ensure there is not a string lingering in
  139. the output buffer. */
  140. cOutputBuffer[ 0 ] = 0x00;
  141. // FIXME telnetState should go into the command processor
  142. //input_state = s->input_state;
  143. xReturned = FreeRTOS_CLIProcessCommand(s, (int8_t *)s->buf, (int8_t *)cOutputBuffer, configCOMMAND_INT_MAX_OUTPUT_SIZE );
  144. //s->input_state = input_state;
  145. s->send(s->num_connect, (char *)cOutputBuffer, strlen( ( const char * ) cOutputBuffer ));
  146. } while( xReturned != pdFALSE );
  147. if( strcmp( "sensor info", ( const char * ) s->buf ) == 0 ){
  148. strcpy( s->prev_cmd, s->buf );
  149. memset( s->buf, 0x00, cmdMAX_INPUT_SIZE );
  150. xTimerStart(s->RepeatSensorInfoTimer, 0);
  151. } else {
  152. /* All the strings generated by the input
  153. command have been sent. Clear the input
  154. string ready to receive the next command.
  155. Remember the command that was just processed
  156. first in case it is to be processed again. */
  157. strcpy( s->prev_cmd, s->buf );
  158. memset( s->buf, 0x00, cmdMAX_INPUT_SIZE );
  159. if(s->input_state != CLI_CHANGE_PWD)
  160. s->send(s->num_connect, (char *)pcEndOfCommandOutputString, strlen( ( const char * ) pcEndOfCommandOutputString ));
  161. }
  162. }
  163. break;
  164. case CLI_CHANGE_PWD:
  165. case CLI_CHANGE_PWD_ACK:
  166. FreeRTOS_ChangePWDProcess(cOutputBuffer, s);
  167. s->send(s->num_connect, (char *)cOutputBuffer, strlen( ( const char * ) cOutputBuffer ));
  168. memset( s->buf, 0x00, cmdMAX_INPUT_SIZE );
  169. break;
  170. }
  171. }
  172. void cli_getchar(cli_state_t *s, char incoming_char, bool echo_enabled)
  173. {
  174. s->buf[s->bufptr] = incoming_char;
  175. if(echo_enabled && s->input_state != CLI_AUTH_PASSW){
  176. if (s->buf[s->bufptr] != ISO_etx && s->buf[s->bufptr] != ISO_del) {
  177. s->send( s->num_connect, &s->buf[s->bufptr], 1);
  178. }
  179. }
  180. else if(s->input_state == CLI_AUTH_PASSW){
  181. s->send( s->num_connect, " ", 1);
  182. }
  183. if(s->buf[s->bufptr] == ISO_nl || s->buf[s->bufptr] == 0) {
  184. s->bufptr = 0;
  185. return;
  186. }
  187. if(s->buf[s->bufptr] == ISO_cr || s->bufptr == sizeof(s->buf) - 1) {
  188. if(s->bufptr > 0) {
  189. s->buf[s->bufptr] = 0;
  190. }
  191. cli_input(s);
  192. s->bufptr = 0;
  193. } else if ( s->buf[s->bufptr] == '\b' || s->buf[s->bufptr] == ISO_del) {
  194. /* Backspace was pressed. Erase the last character in the string - if any. */
  195. s->buf[s->bufptr] = '\0';
  196. if (s->bufptr > 0) {
  197. s->bufptr--;
  198. s->buf[s->bufptr] = '\0';
  199. const char backspace[] = "\b \b";
  200. s->send(s->num_connect, backspace, sizeof(backspace));
  201. }
  202. } else if (s->buf[s->bufptr] == ISO_etx){
  203. xTimerStop(s->RepeatSensorInfoTimer, 0);
  204. s->flag_telnet_ip_option = true;
  205. if(s->input_state != CLI_CHANGE_PWD && s->input_state != CLI_CHANGE_PWD_ACK)
  206. s->send( s->num_connect, pcEndOfCommandOutputString, strlen( ( const char * ) pcEndOfCommandOutputString ));
  207. } else {
  208. ++s->bufptr;
  209. }
  210. }
  211. void cli_init(void)
  212. {
  213. for (unsigned i = 0; i < array_len(cli_states); i++) {
  214. memset(cli_states[i].buf, 0, cmdMAX_INPUT_SIZE);
  215. memset(cli_states[i].prev_cmd, 0, cmdMAX_INPUT_SIZE);
  216. cli_states[i].optlen = 0;
  217. cli_states[i].num_connect = 0;
  218. cli_states[i].user_id = USER;
  219. cli_states[i].flag_telnet_ip_option = false;
  220. cli_states[i].RepeatSensorInfoTimer = xTimerCreate("SensorInfoTmr", REPEAT_SENSOR_INFO_TIME, pdFALSE, ( void * ) i, SensorInfoTimerCallback);
  221. }
  222. vRegisterCLICommands();
  223. }
  224. cli_state_t *alloc_state(void)
  225. {
  226. for (unsigned i = 0; i < array_len(cli_states); ++i) {
  227. if (cli_states[i].state == STATE_UNUSED) {
  228. cli_states[i].state = STATE_CLOSE;
  229. cli_states[i].prev_cmd[0] = 0; // don't leak the previous user's history
  230. cli_states[i].input_state = CLI_AUTH; // pre-authenticated state
  231. cli_states[i].user_id = USER; // lowest permission level
  232. return &cli_states[i];
  233. }
  234. }
  235. return 0;
  236. }
  237. void free_state(cli_state_t *state)
  238. {
  239. xTimerStop(state->RepeatSensorInfoTimer, 0);
  240. state->state = STATE_UNUSED;
  241. }
  242. void cli_hello(cli_state_t *cli_state)
  243. {
  244. cli_state->state = STATE_NORMAL;
  245. const char hello[] = "hello\r\n>";
  246. cli_state->send(cli_state->num_connect, hello, sizeof(hello));
  247. }
  248. void cli_save_config(cli_state_t *cli_state)
  249. {
  250. SETTINGS_Save();
  251. log_event_data(LOG_SETTING_SAVE, get_user_name(cli_state->user_id));
  252. }
  253. user_level_t cli_auth_user(const char *user, const char *password, log_type_t log_entry_type)
  254. {
  255. user_level_t rv = MAX_USER_LEVELS;
  256. char WebPassword[MAX_WEB_PASSWD_LEN];
  257. char WebLogin[MAX_WEB_LOGIN_LEN];
  258. uint8_t valueLen, user_id;
  259. for (user_id = 0; user_id < MAX_WEB_USERS; user_id++) {
  260. GetUserLogin(user_id, WebLogin, &valueLen);
  261. GetUserPassword(user_id, WebPassword, &valueLen);
  262. /* Check login and password */
  263. if ((strncmp(WebLogin, user, MAX_WEB_LOGIN_LEN) == 0) &&
  264. (strncmp(WebPassword, password, MAX_WEB_PASSWD_LEN) == 0)) {
  265. /* Login and pass are valid */
  266. rv = user_id;
  267. log_event_data(log_entry_type, get_user_name(user_id));
  268. break;
  269. }
  270. }
  271. return rv;
  272. }