Repository navigation
Improve remote shell loop error handling #851
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -528,41 +528,54 @@ static int remoteServiceAdmin_callback(struct mg_connection *conn) { | |
| if (export != NULL) { | ||
| uint64_t datalength = request_info->content_length; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to official documentation:
If every request body is required to have |
||
| char* data = malloc(datalength + 1); | ||
| mg_read(conn, data, datalength); | ||
| data[datalength] = '\0'; | ||
|
|
||
| char *response = NULL; | ||
| int responceLength = 0; | ||
| int rc = exportRegistration_call(export, data, -1, &metadata, &response, &responceLength); | ||
| if (rc != CELIX_SUCCESS) { | ||
| RSA_LOG_ERROR(rsa, "Error trying to invoke remove service, got error %i\n", rc); | ||
| size_t readLen = 0; | ||
| while (readLen < datalength) { | ||
| int this_read = mg_read(conn, data + readLen, (size_t)(datalength - readLen)); | ||
| if (this_read <= 0) { | ||
| break; | ||
| } | ||
| readLen += (size_t)this_read; | ||
| } | ||
| data[readLen] = '\0'; | ||
|
|
||
| if (rc == CELIX_SUCCESS && response != NULL) { | ||
| mg_write(conn, data_response_headers, strlen(data_response_headers)); | ||
|
|
||
| char *bufLoc = response; | ||
| size_t bytesLeft = strlen(response); | ||
| if (bytesLeft > INT_MAX) { | ||
| //NOTE arcording to civetweb mg_write, there is a limit on mg_write for INT_MAX. | ||
| RSA_LOG_WARNING(rsa, "nr of bytes to send for a remote call is > INT_MAX, this can lead to issues\n"); | ||
| if (readLen < datalength) { | ||
| RSA_LOG_ERROR(rsa, "Error while reading request body, received %zu of %llu bytes", readLen, (unsigned long long)datalength); | ||
| mg_send_http_error(conn, 400, "%s", "Bad request"); | ||
| result = 400; | ||
| } else { | ||
| char *response = NULL; | ||
| int responceLength = 0; | ||
| int rc = exportRegistration_call(export, data, -1, &metadata, &response, &responceLength); | ||
| if (rc != CELIX_SUCCESS) { | ||
| RSA_LOG_ERROR(rsa, "Error trying to invoke remove service, got error %i\n", rc); | ||
| } | ||
| while (bytesLeft > 0) { | ||
| int send = mg_write(conn, bufLoc, strlen(bufLoc)); | ||
| if (send > 0) { | ||
| bytesLeft -= send; | ||
| bufLoc += send; | ||
| } else { | ||
| RSA_LOG_ERROR(rsa, "Error sending response: %s", strerror(errno)); | ||
| break; | ||
|
|
||
| if (rc == CELIX_SUCCESS && response != NULL) { | ||
| mg_write(conn, data_response_headers, strlen(data_response_headers)); | ||
|
|
||
| char *bufLoc = response; | ||
| size_t bytesLeft = strlen(response); | ||
| if (bytesLeft > INT_MAX) { | ||
| //NOTE arcording to civetweb mg_write, there is a limit on mg_write for INT_MAX. | ||
| RSA_LOG_WARNING(rsa, "nr of bytes to send for a remote call is > INT_MAX, this can lead to issues\n"); | ||
| } | ||
| while (bytesLeft > 0) { | ||
| int send = mg_write(conn, bufLoc, strlen(bufLoc)); | ||
| if (send > 0) { | ||
| bytesLeft -= send; | ||
| bufLoc += send; | ||
| } else { | ||
| RSA_LOG_ERROR(rsa, "Error sending response: %s", strerror(errno)); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| free(response); | ||
| } else { | ||
| mg_write(conn, no_content_response_headers, strlen(no_content_response_headers)); | ||
| free(response); | ||
| } else { | ||
| mg_write(conn, no_content_response_headers, strlen(no_content_response_headers)); | ||
| } | ||
| result = 1; | ||
| } | ||
| result = 1; | ||
|
|
||
| free(data); | ||
| exportRegistration_decreaseUsage(export); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -151,10 +151,13 @@ celix_status_t remoteShell_stopConnections(remote_shell_pt instance) { | |
| void *remoteShell_connection_run(void *data) { | ||
| celix_status_t status = CELIX_SUCCESS; | ||
| connection_pt connection = data; | ||
| size_t len; | ||
| ssize_t len; | ||
| int result; | ||
| struct timeval timeout; /* Timeout for select */ | ||
|
|
||
| char buff[COMMAND_BUFF_SIZE]; | ||
| size_t used = 0; | ||
|
|
||
| int fd = fileno(connection->socketStream); | ||
|
|
||
| connection->threadRunning = true; | ||
|
|
@@ -172,27 +175,42 @@ void *remoteShell_connection_run(void *data) { | |
|
|
||
| /* The socket_fd has data available to be read */ | ||
| if (result > 0 && FD_ISSET(fd, &connection->pollset)) { | ||
| char buff[COMMAND_BUFF_SIZE]; | ||
|
|
||
| len = recv(fd, buff, COMMAND_BUFF_SIZE - 1, 0); | ||
| if (len < COMMAND_BUFF_SIZE) { | ||
| celix_status_t commandStatus = CELIX_SUCCESS; | ||
| buff[len] = '\0'; | ||
|
|
||
| commandStatus = remoteShell_connection_execute(connection, buff); | ||
|
|
||
| if (commandStatus == CELIX_SUCCESS) { | ||
| remoteShell_connection_print(connection, RS_PROMPT); | ||
| } else if (commandStatus == CELIX_FILE_IO_EXCEPTION) { | ||
| //exit command | ||
| break; | ||
| } else { //error | ||
| remoteShell_connection_print(connection, RS_ERROR); | ||
| remoteShell_connection_print(connection, RS_PROMPT); | ||
| len = recv(fd, buff + used, COMMAND_BUFF_SIZE - 1 - used, 0); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is still not robust enough. A common error breaking this is |
||
| if (len > 0) { | ||
| used += (size_t)len; | ||
| char *newline = NULL; | ||
| while (status == CELIX_SUCCESS && (newline = memchr(buff, '\n', used)) != NULL) { | ||
| size_t consumed = (size_t)(newline - buff) + 1; | ||
| *newline = '\0'; | ||
|
|
||
| celix_status_t commandStatus = remoteShell_connection_execute(connection, buff); | ||
|
|
||
| if (commandStatus == CELIX_SUCCESS) { | ||
| remoteShell_connection_print(connection, RS_PROMPT); | ||
| } else if (commandStatus == CELIX_FILE_IO_EXCEPTION) { | ||
| //exit command | ||
| status = CELIX_FILE_IO_EXCEPTION; | ||
| break; | ||
| } else { //error | ||
| remoteShell_connection_print(connection, RS_ERROR); | ||
| remoteShell_connection_print(connection, RS_PROMPT); | ||
| } | ||
|
|
||
| memmove(buff, buff + consumed, used - consumed); | ||
| used -= consumed; | ||
| } | ||
|
|
||
| if (status == CELIX_SUCCESS && used >= COMMAND_BUFF_SIZE - 1) { | ||
| //Buffer is full without a newline, so no complete command could be read. | ||
| celix_logHelper_log(*connection->parent->loghelper, CELIX_LOG_LEVEL_ERROR, "REMOTE_SHELL: Received data without a newline, dropping data"); | ||
| used = 0; | ||
| } | ||
| } else if (len == 0) { | ||
| celix_logHelper_log(*connection->parent->loghelper, CELIX_LOG_LEVEL_INFO, "REMOTE_SHELL: Connection closed by peer"); | ||
| break; | ||
| } else { | ||
| celix_logHelper_log(*connection->parent->loghelper, CELIX_LOG_LEVEL_ERROR, "REMOTE_SHELL: Error while retrieving data"); | ||
| celix_logHelper_log(*connection->parent->loghelper, CELIX_LOG_LEVEL_ERROR, "REMOTE_SHELL: recv() failed: %s", strerror(errno)); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
read == 0usually means EOF, rather than an error.