mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-01 11:55:25 +00:00
This patch is to fix error logs in client sending to server via socket. Since gdb inherits the socket descriptor of client which is used as its stderr, in this case, the error logs from gdb will mess up server, so closing the inheritance when the socket is created. Tracked-On: #3200 Signed-off-by: xiaojin2 <xiaojing.liu@intel.com> Reviewed-by: Liu, Xinwu <xinwu.liu@intel.com> Acked-by: Chen, Gang <gang.c.chen@intel.com> Acked-by: Anthony Xu <anthony.xu@intel.com>
239 lines
6.1 KiB
C
239 lines
6.1 KiB
C
/*
|
|
* Copyright (C) <2018> Intel Corporation
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2018 Intel Corporation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "crash_dump.h"
|
|
#include "packet.h"
|
|
#include "log_sys.h"
|
|
#include "protocol.h"
|
|
#include "version.h"
|
|
|
|
/**
|
|
* Usercrash works as C/S model: usercrash_c works as usercrash client to
|
|
* collect crash logs and information once crash event occurs. For each time,
|
|
* usercrash_c receives 3 params from core_dump and sends connect request event
|
|
* to usercrash_s, then it receives file fd from server to fill crash info into
|
|
* the file. After this work is done, it will notify server that dump work is
|
|
* completed.
|
|
*/
|
|
|
|
/**
|
|
* @sockfd: the socket fd.
|
|
* set_timeout is used to set timeout for the sockfd, in case client is blocked
|
|
* when client cannot receive the data from server, or send data to server
|
|
*/
|
|
static int set_timeout(int sockfd)
|
|
{
|
|
struct timeval timeout = {50, 0};
|
|
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
|
|
sizeof(timeout)) != 0) {
|
|
LOGE("failed to set receive timeout\n");
|
|
return -1;
|
|
}
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
|
|
sizeof(timeout)) != 0) {
|
|
LOGE("failed to set send timeout\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @pid: crash process pid.
|
|
* @usercrashd_socket: client socket fd pointer, get the value from
|
|
* usercrashd_connect function.
|
|
* @output_fd: file fd, receives from server to store dump file.
|
|
* @name: crash process name
|
|
* usercrashd_connect is used to connect to server, and get the crash file fd
|
|
* from server
|
|
*/
|
|
static int usercrashd_connect(int pid, int *usercrashd_socket,
|
|
int *output_fd, const char *name)
|
|
{
|
|
int sockfd;
|
|
int tmp_output_fd;
|
|
ssize_t rc;
|
|
int flags;
|
|
struct crash_packet packet = {0};
|
|
|
|
if (name == NULL) {
|
|
LOGE("crash process name is NULL\n");
|
|
return -1;
|
|
}
|
|
sockfd = socket_local_client(SOCKET_NAME, strlen(SOCKET_NAME),
|
|
SOCK_SEQPACKET | SOCK_CLOEXEC);
|
|
if (sockfd == -1) {
|
|
LOGE("failed to connect to usercrashd, error (%s)\n",
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
packet.packet_type = kDumpRequest;
|
|
packet.pid = pid;
|
|
strncpy(packet.name, name, COMM_NAME_LEN);
|
|
packet.name[COMM_NAME_LEN - 1] = '\0';
|
|
if (set_timeout(sockfd)) {
|
|
close(sockfd);
|
|
return -1;
|
|
}
|
|
if (write(sockfd, &packet, sizeof(packet)) !=
|
|
sizeof(packet)) {
|
|
LOGE("failed to write DumpRequest packet, error (%s)\n",
|
|
strerror(errno));
|
|
close(sockfd);
|
|
return -1;
|
|
}
|
|
|
|
rc = recv_fd(sockfd, &packet, sizeof(packet),
|
|
&tmp_output_fd);
|
|
if (rc == -1) {
|
|
LOGE("failed to read response to DumpRequest packet, ");
|
|
LOGE("error (%s)\n", strerror(errno));
|
|
close(sockfd);
|
|
return -1;
|
|
} else if (rc != sizeof(packet)) {
|
|
LOGE("received DumpRequest response packet of incorrect ");
|
|
LOGE("length (expected %lu, got %ld)\n", sizeof(packet), rc);
|
|
goto fail;
|
|
}
|
|
if (packet.packet_type != kPerformDump) {
|
|
LOGE("unexpected dump response:%d\n", packet.packet_type);
|
|
goto fail;
|
|
}
|
|
|
|
/**
|
|
* Make the fd O_APPEND so that our output is guaranteed to be at the
|
|
* end of a file. (This also makes selinux rules consistent, because
|
|
* selinux distinguishes between writing to a regular fd, and writing
|
|
* to an fd with O_APPEND)
|
|
*/
|
|
flags = fcntl(tmp_output_fd, F_GETFL);
|
|
if (fcntl(tmp_output_fd, F_SETFL, flags | O_APPEND) != 0) {
|
|
LOGE("failed to set output fd flags, error (%s)\n",
|
|
strerror(errno));
|
|
goto fail;
|
|
}
|
|
|
|
*usercrashd_socket = sockfd;
|
|
*output_fd = tmp_output_fd;
|
|
|
|
return 0;
|
|
fail:
|
|
close(sockfd);
|
|
close(tmp_output_fd);
|
|
return -1;
|
|
|
|
}
|
|
|
|
/**
|
|
* @usercrashd_socket: client socket fd, used to communicate with server.
|
|
* usercrashd_notify_completion is used to tell the server it has done the
|
|
* dump, the server will pop another crash from the queue and execute the dump
|
|
* process
|
|
*/
|
|
static int usercrashd_notify_completion(int usercrashd_socket)
|
|
{
|
|
struct crash_packet packet = {0};
|
|
|
|
packet.packet_type = kCompletedDump;
|
|
if (set_timeout(usercrashd_socket)) {
|
|
close(usercrashd_socket);
|
|
return -1;
|
|
}
|
|
if (write(usercrashd_socket, &packet,
|
|
sizeof(packet)) != sizeof(packet)) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void print_usage(void)
|
|
{
|
|
printf("usercrash - tool to dump crash info for the crashes in the ");
|
|
printf("userspace on sos. It's the client role of usercrash.\n");
|
|
printf("[Usage]\n");
|
|
printf("\t--coredump, usercrash_c <pid> <comm> <sig> ");
|
|
printf("(root role to run)\n");
|
|
printf("[Option]\n");
|
|
printf("\t-h: print this usage message\n");
|
|
printf("\t-v: print usercrash_c version\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int pid;
|
|
int sig;
|
|
int out_fd;
|
|
int sock;
|
|
int ret;
|
|
|
|
if (argc > 1) {
|
|
if (strcmp(argv[1], "-v") == 0) {
|
|
printf("version is %d.%d-%s, build by %s@%s\n",
|
|
UC_MAJOR_VERSION, UC_MINOR_VERSION,
|
|
UC_BUILD_VERSION, UC_BUILD_USER,
|
|
UC_BUILD_TIME);
|
|
return 0;
|
|
}
|
|
if (strcmp(argv[1], "-h") == 0) {
|
|
print_usage();
|
|
return 0;
|
|
}
|
|
} else
|
|
print_usage();
|
|
|
|
if (getuid() != 0) {
|
|
LOGE("failed to execute usercrash_c, root is required\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (argc == 4) {
|
|
/* it's from coredump */
|
|
pid = (int)strtol(argv[1], NULL, 10);
|
|
sig = (int)strtol(argv[3], NULL, 10);
|
|
ret = usercrashd_connect(pid, &sock, &out_fd, argv[2]);
|
|
if (ret) {
|
|
LOGE("usercrashd_connect failed, error (%s)\n",
|
|
strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
crash_dump(pid, sig, out_fd);
|
|
close(out_fd);
|
|
if (usercrashd_notify_completion(sock)) {
|
|
LOGE("failed to notify usercrashd of completion");
|
|
close(sock);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
close(sock);
|
|
} else {
|
|
print_usage();
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|