mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-06-25 17:31:33 +02:00
Compare commits
4 Commits
modem-v0.1
...
docs/docs_
Author | SHA1 | Date | |
---|---|---|---|
a3e8421de8 | |||
3bf0511938 | |||
319fce018e | |||
d047ff569c |
77
.github/workflows/publish-docs.yml
vendored
Normal file
77
.github/workflows/publish-docs.yml
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
name: Docs and Publish New
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
env:
|
||||
DOCS_DEPLOY_URL_BASE: "${{ secrets.DOCS_PREVIEW_SERVER_URL }}"
|
||||
#DOCS_DEPLOY_SERVER: "ci.espressif.cn:42348"
|
||||
DOCS_DEPLOY_SERVER: "ci.espressif.cn:42348"
|
||||
DOCS_DEPLOY_SERVER_USER: "${{ secrets.DOCS_SERVER_USER }}"
|
||||
DOCS_DEPLOY_PRIVATEKEY : "${{ secrets.DOCS_DEPLOY_KEY }}"
|
||||
DOCS_DEPLOY_KEY: "${{ secrets.DOCS_DEPLOY_KEY }}"
|
||||
DOCS_DEPLOY_PATH : "${{ secrets.DOCS_PATH }}"
|
||||
|
||||
jobs:
|
||||
docs_build:
|
||||
name: Docs-Build-And-Upload
|
||||
runs-on: ubuntu-latest
|
||||
# Skip running on forks since it won't have access to secrets
|
||||
#if: github.repository == 'espressif/esp-protocols'
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
- name: Generate and delploying docs
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install doxygen clang python3-pip
|
||||
python -m pip install breathe recommonmark esp-docs
|
||||
cd $GITHUB_WORKSPACE/components/esp_websocket_client/docs
|
||||
./generate_docs
|
||||
source $GITHUB_WORKSPACE/docs/utils.sh
|
||||
add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
|
||||
export GIT_VER=$(git describe --always)
|
||||
ls -l $GITHUB_WORKSPACE/components/esp_websocket_client/docs
|
||||
export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/components/esp_websocket_client/docs
|
||||
deploy-docs
|
||||
|
||||
#build_docs:
|
||||
# stage: build
|
||||
# image: $ESP_DOCS_ENV_IMAGE
|
||||
# tags:
|
||||
# - build_docs
|
||||
# artifacts:
|
||||
# when: always
|
||||
# paths:
|
||||
# - docs/_build/*/*/*.txt
|
||||
# - docs/_build/*/*/html/*
|
||||
# expire_in: 4 days
|
||||
# # No cleaning when the artifacts
|
||||
# after_script: []
|
||||
# script:
|
||||
# - cd docs
|
||||
# - pip install -r requirements.txt
|
||||
# - build-docs -l en -t esp32
|
||||
#
|
||||
#.deploy_docs_template:
|
||||
# stage: deploy
|
||||
# image: $ESP_DOCS_ENV_IMAGE
|
||||
# tags:
|
||||
# - deploy_docs
|
||||
# needs:
|
||||
# - build_docs
|
||||
# only:
|
||||
# changes:
|
||||
# - "docs/**/*"
|
||||
# script:
|
||||
# - source ${CI_PROJECT_DIR}/docs/utils.sh
|
||||
# - add_doc_server_ssh_keys ${{ secrets.DOCS_DEPLOY_PRIVATEKEY }} ${{ secrets.DOCS_DEPLOY_SERVER }} ${{ secrets.DOCS_DEPLOY_SERVER_USER }}
|
||||
# - export GIT_VER=$(git describe --always)
|
||||
# - pip install -r ${CI_PROJECT_DIR}/docs/requirements.txt
|
||||
# - deploy-docs
|
||||
#
|
@ -107,6 +107,7 @@ typedef enum {
|
||||
struct esp_websocket_client {
|
||||
esp_event_loop_handle_t event_handle;
|
||||
TaskHandle_t task_handle;
|
||||
esp_websocket_error_codes_t error_handle;
|
||||
esp_transport_list_handle_t transport_list;
|
||||
esp_transport_handle_t transport;
|
||||
websocket_config_storage_t *config;
|
||||
@ -196,6 +197,16 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
|
||||
event_data.payload_len = client->payload_len;
|
||||
event_data.payload_offset = client->payload_offset;
|
||||
|
||||
if (client->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
event_data.error_handle.esp_tls_last_esp_err = esp_tls_get_and_clear_last_error(esp_transport_get_error_handle(client->transport),
|
||||
&client->error_handle.esp_tls_stack_err,
|
||||
&client->error_handle.esp_tls_cert_verify_flags);
|
||||
event_data.error_handle.esp_transport_sock_errno = esp_transport_get_errno(client->transport);
|
||||
}
|
||||
event_data.error_handle.error_type = client->error_handle.error_type;
|
||||
event_data.error_handle.esp_ws_handshake_status_code = client->error_handle.esp_ws_handshake_status_code;
|
||||
|
||||
|
||||
if ((err = esp_event_post_to(client->event_handle,
|
||||
WEBSOCKET_EVENTS, event,
|
||||
&event_data,
|
||||
@ -206,7 +217,7 @@ static esp_err_t esp_websocket_client_dispatch_event(esp_websocket_client_handle
|
||||
return esp_event_loop_run(client->event_handle, 0);
|
||||
}
|
||||
|
||||
static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client)
|
||||
static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_handle_t client, esp_websocket_error_type_t error_type)
|
||||
{
|
||||
ESP_WS_CLIENT_STATE_CHECK(TAG, client, return ESP_FAIL);
|
||||
esp_transport_close(client->transport);
|
||||
@ -215,6 +226,8 @@ static esp_err_t esp_websocket_client_abort_connection(esp_websocket_client_hand
|
||||
client->reconnect_tick_ms = _tick_get_ms();
|
||||
ESP_LOGI(TAG, "Reconnect after %d ms", client->wait_timeout_ms);
|
||||
}
|
||||
|
||||
client->error_handle.error_type = error_type;
|
||||
client->state = WEBSOCKET_STATE_WAIT_TIMEOUT;
|
||||
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DISCONNECTED, NULL, 0);
|
||||
return ESP_OK;
|
||||
@ -725,6 +738,19 @@ esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, con
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_websocket_client_set_headers(esp_websocket_client_handle_t client, const char *headers)
|
||||
{
|
||||
if (client == NULL || client->state != WEBSOCKET_STATE_CONNECTED || headers == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
xSemaphoreTakeRecursive(client->lock, portMAX_DELAY);
|
||||
esp_err_t ret = esp_transport_ws_set_headers(client->transport, headers);
|
||||
xSemaphoreGiveRecursive(client->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
|
||||
{
|
||||
int rlen;
|
||||
@ -739,14 +765,10 @@ static esp_err_t esp_websocket_client_recv(esp_websocket_client_handle_t client)
|
||||
esp_websocket_free_buf(client, false);
|
||||
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
|
||||
if (error_handle) {
|
||||
ESP_LOGE(TAG, "esp_transport_read() failed with %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
rlen, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
|
||||
error_handle->esp_tls_flags, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_read() failed with %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
esp_websocket_client_error(client, "esp_transport_read() failed with %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
rlen, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
|
||||
error_handle->esp_tls_flags, errno);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "esp_transport_read() failed with %d, errno=%d", rlen, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_read() failed with %d, errno=%d", rlen, errno);
|
||||
}
|
||||
return ESP_FAIL;
|
||||
@ -817,32 +839,32 @@ static void esp_websocket_client_task(void *pv)
|
||||
client->run = false;
|
||||
break;
|
||||
}
|
||||
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_BEFORE_CONNECT, NULL, 0);
|
||||
int result = esp_transport_connect(client->transport,
|
||||
client->config->host,
|
||||
client->config->port,
|
||||
client->config->network_timeout_ms);
|
||||
if (result < 0) {
|
||||
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
|
||||
client->error_handle.esp_ws_handshake_status_code = esp_transport_ws_get_upgrade_request_status(client->transport);
|
||||
if (error_handle) {
|
||||
ESP_LOGE(TAG, "esp_transport_connect() failed with %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
result, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
|
||||
error_handle->esp_tls_flags, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_connect() failed with %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
esp_websocket_client_error(client, "esp_transport_connect() failed with %d,\
|
||||
transport_error=%s, tls_error_code=%i, tls_flags=%i, esp_ws_handshake_status_code=%d, errno=%d",
|
||||
result, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
|
||||
error_handle->esp_tls_flags, errno);
|
||||
error_handle->esp_tls_flags, client->error_handle.esp_ws_handshake_status_code, errno);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "esp_transport_connect() failed with %d, errno=%d", result, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_connect() failed with %d, errno=%d", result, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_connect() failed with %d, esp_ws_handshake_status_code=%d, errno=%d",
|
||||
result, client->error_handle.esp_ws_handshake_status_code, errno);
|
||||
}
|
||||
esp_websocket_client_abort_connection(client);
|
||||
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port);
|
||||
|
||||
client->state = WEBSOCKET_STATE_CONNECTED;
|
||||
client->wait_for_pong_resp = false;
|
||||
client->error_handle.error_type = WEBSOCKET_ERROR_TYPE_NONE;
|
||||
esp_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CONNECTED, NULL, 0);
|
||||
|
||||
break;
|
||||
case WEBSOCKET_STATE_CONNECTED:
|
||||
if ((CLOSE_FRAME_SENT_BIT & xEventGroupGetBits(client->status_bits)) == 0) { // only send and check for PING
|
||||
@ -860,14 +882,14 @@ static void esp_websocket_client_task(void *pv)
|
||||
|
||||
if ( _tick_get_ms() - client->pingpong_tick_ms > client->config->pingpong_timeout_sec * 1000 ) {
|
||||
if (client->wait_for_pong_resp) {
|
||||
ESP_LOGE(TAG, "Error, no PONG received for more than %d seconds after PING", client->config->pingpong_timeout_sec);
|
||||
esp_websocket_client_error(client, "Error, no PONG received for more than %d seconds after PING", client->config->pingpong_timeout_sec);
|
||||
esp_websocket_client_abort_connection(client);
|
||||
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_PONG_TIMEOUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (read_select == 0) {
|
||||
ESP_LOGV(TAG, "Read poll timeout: skipping esp_transport_read()...");
|
||||
break;
|
||||
@ -876,7 +898,7 @@ static void esp_websocket_client_task(void *pv)
|
||||
|
||||
if (esp_websocket_client_recv(client) == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Error receive data");
|
||||
esp_websocket_client_abort_connection(client);
|
||||
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -910,17 +932,13 @@ static void esp_websocket_client_task(void *pv)
|
||||
if (read_select < 0) {
|
||||
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
|
||||
if (error_handle) {
|
||||
ESP_LOGE(TAG, "esp_transport_poll_read() returned %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
read_select, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
|
||||
error_handle->esp_tls_flags, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_poll_read() returned %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
esp_websocket_client_error(client, "esp_transport_poll_read() returned %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
read_select, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
|
||||
error_handle->esp_tls_flags, errno);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "esp_transport_poll_read() returned %d, errno=%d", read_select, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_poll_read() returned %d, errno=%d", read_select, errno);
|
||||
}
|
||||
esp_websocket_client_abort_connection(client);
|
||||
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
|
||||
}
|
||||
} else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) {
|
||||
// waiting for reconnecting...
|
||||
@ -1120,17 +1138,13 @@ int esp_websocket_client_send_with_opcode(esp_websocket_client_handle_t client,
|
||||
esp_websocket_free_buf(client, true);
|
||||
esp_tls_error_handle_t error_handle = esp_transport_get_error_handle(client->transport);
|
||||
if (error_handle) {
|
||||
ESP_LOGE(TAG, "esp_transport_write() returned %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
ret, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
|
||||
error_handle->esp_tls_flags, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_write() returned %d, last_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
esp_websocket_client_error(client, "esp_transport_write() returned %d, transport_error=%s, tls_error_code=%i, tls_flags=%i, errno=%d",
|
||||
ret, esp_err_to_name(error_handle->last_error), error_handle->esp_tls_error_code,
|
||||
error_handle->esp_tls_flags, errno);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "esp_transport_write() returned %d, errno=%d", ret, errno);
|
||||
esp_websocket_client_error(client, "esp_transport_write() returned %d, errno=%d", ret, errno);
|
||||
}
|
||||
esp_websocket_client_abort_connection(client);
|
||||
esp_websocket_client_abort_connection(client, WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT);
|
||||
goto unlock_and_return;
|
||||
}
|
||||
current_opcode = 0;
|
||||
|
@ -37,6 +37,13 @@ static const char *TAG = "websocket";
|
||||
static TimerHandle_t shutdown_signal_timer;
|
||||
static SemaphoreHandle_t shutdown_sema;
|
||||
|
||||
static void log_error_if_nonzero(const char *message, int error_code)
|
||||
{
|
||||
if (error_code != 0) {
|
||||
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdown_signaler(TimerHandle_t xTimer)
|
||||
{
|
||||
ESP_LOGI(TAG, "No data received for %d seconds, signaling shutdown", NO_DATA_TIMEOUT_SEC);
|
||||
@ -71,6 +78,12 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
|
||||
break;
|
||||
case WEBSOCKET_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
|
||||
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
|
||||
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
|
||||
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
|
||||
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
|
||||
}
|
||||
break;
|
||||
case WEBSOCKET_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "WEBSOCKET_EVENT_DATA");
|
||||
@ -99,6 +112,12 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i
|
||||
break;
|
||||
case WEBSOCKET_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
|
||||
log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code);
|
||||
if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err);
|
||||
log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err);
|
||||
log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -35,9 +35,35 @@ typedef enum {
|
||||
WEBSOCKET_EVENT_DISCONNECTED, /*!< The connection has been disconnected */
|
||||
WEBSOCKET_EVENT_DATA, /*!< When receiving data from the server, possibly multiple portions of the packet */
|
||||
WEBSOCKET_EVENT_CLOSED, /*!< The connection has been closed cleanly */
|
||||
WEBSOCKET_EVENT_BEFORE_CONNECT, /*!< The event occurs before connecting */
|
||||
WEBSOCKET_EVENT_MAX
|
||||
} esp_websocket_event_id_t;
|
||||
|
||||
/**
|
||||
* @brief Websocket connection error codes propagated via ERROR event
|
||||
*/
|
||||
typedef enum {
|
||||
WEBSOCKET_ERROR_TYPE_NONE = 0,
|
||||
WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT,
|
||||
WEBSOCKET_ERROR_TYPE_PONG_TIMEOUT,
|
||||
WEBSOCKET_ERROR_TYPE_HANDSHAKE
|
||||
} esp_websocket_error_type_t;
|
||||
|
||||
/**
|
||||
* @brief Websocket error code structure to be passed as a contextual information into ERROR event
|
||||
*/
|
||||
typedef struct {
|
||||
/* compatible portion of the struct corresponding to struct esp_tls_last_error */
|
||||
esp_err_t esp_tls_last_esp_err; /*!< last esp_err code reported from esp-tls component */
|
||||
int esp_tls_stack_err; /*!< tls specific error code reported from underlying tls stack */
|
||||
int esp_tls_cert_verify_flags; /*!< tls flags reported from underlying tls stack during certificate verification */
|
||||
/* esp-websocket specific structure extension */
|
||||
esp_websocket_error_type_t error_type;
|
||||
int esp_ws_handshake_status_code; /*!< http status code of the websocket upgrade handshake */
|
||||
/* tcp_transport extension */
|
||||
int esp_transport_sock_errno; /*!< errno from the underlying socket */
|
||||
} esp_websocket_error_codes_t;
|
||||
|
||||
/**
|
||||
* @brief Websocket event data
|
||||
*/
|
||||
@ -50,6 +76,7 @@ typedef struct {
|
||||
void *user_context; /*!< user_data context, from esp_websocket_client_config_t user_data */
|
||||
int payload_len; /*!< Total payload length, payloads exceeding buffer will be posted through multiple events */
|
||||
int payload_offset; /*!< Actual offset for the data associated with this event */
|
||||
esp_websocket_error_codes_t error_handle; /*!< esp-websocket error handle including esp-tls errors as well as internal websocket errors */
|
||||
} esp_websocket_event_data_t;
|
||||
|
||||
/**
|
||||
@ -127,6 +154,17 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie
|
||||
*/
|
||||
esp_err_t esp_websocket_client_set_uri(esp_websocket_client_handle_t client, const char *uri);
|
||||
|
||||
/**
|
||||
* @brief Set additional websocket headers for the client, when performing this behavior, the headers will replace the old ones
|
||||
* @pre Must stop the WebSocket client before set headers if the client has been connected
|
||||
*
|
||||
* @param[in] client The client
|
||||
* @param headers additional header strings each terminated with \r\n
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t esp_websocket_client_set_headers(esp_websocket_client_handle_t client, const char *headers);
|
||||
|
||||
/**
|
||||
* @brief Open the WebSocket connection
|
||||
*
|
||||
|
18
docs/utils.sh
Normal file
18
docs/utils.sh
Normal file
@ -0,0 +1,18 @@
|
||||
# Bash helper functions for adding SSH keys
|
||||
|
||||
function add_ssh_keys() {
|
||||
local key_string="${1}"
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
echo -n "${key_string}" >~/.ssh/id_rsa_base64
|
||||
base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 >~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
}
|
||||
|
||||
function add_doc_server_ssh_keys() {
|
||||
local key_string="${1}"
|
||||
local server_url="${2}"
|
||||
local server_user="${3}"
|
||||
add_ssh_keys "${key_string}"
|
||||
echo -e "Host ${server_url}\n\tStrictHostKeyChecking no\n\tUser ${server_user}\n" >>~/.ssh/config
|
||||
}
|
Reference in New Issue
Block a user