tools: acrn-crashlog: implementation for the client of usercrash

This patch is the implementation patch for the client of usercrash.

The usercrash_c works as the client of usercrash to collect crash logs
and information once crash event occurs in the userspace. For each
time, usercrash_c receives 3 params from core_dump and sends connect
request event to the server, then it receives file fd from server to
save crash info into the file. After this work is done, it will notify
server that the dump work is completed.

Signed-off-by: xiaojin2 <xiaojing.liu@intel.com>
Reviewed-by: Zhang Yanmin <yanmin.zhang@intel.com>
Reviewed-by: Liu Chuansheng <chuansheng.liu@intel.com>
Reviewed-by: Zhao Yakui <yakui.zhao@intel.com>
Reviewed-by: Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com>
Acked-by: Eddie Dong <Eddie.dong@intel.com>
This commit is contained in:
xiaojin2 2018-05-14 13:47:34 +08:00 committed by lijinxia
parent afe1a64948
commit 6627fdf772

View File

@ -3,6 +3,34 @@
* 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"
/**
* 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,
@ -12,11 +40,188 @@
* completed.
*/
int main(void)
/**
* @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)
{
//TO BE DONE
//This empty function is to satisfy the dependency of Makefile.
//This is the entry of usercrash_c, the implementation will be
//filled by following patches.
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
*/
bool usercrashd_connect(int pid, int *usercrashd_socket,
int *output_fd, char *name)
{
int sockfd;
int tmp_output_fd;
ssize_t rc;
int flags;
struct crash_packet packet = {0};
sockfd = socket_local_client(SOCKET_NAME, SOCK_SEQPACKET);
if (sockfd == -1) {
LOGE("failed to connect to usercrashd, error (%s)\n",
strerror(errno));
return false;
}
packet.packet_type = kDumpRequest;
packet.pid = pid;
strncpy(packet.name, name, COMM_NAME_LEN - 1);
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 false;
}
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 false;
} 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 true;
fail:
close(sockfd);
close(tmp_output_fd);
return false;
}
/**
* @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
*/
bool 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 false;
}
return true;
}
static void print_usage(void)
{
printf("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("usercrash_c version is 1.0\n");
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 = atoi(argv[1]);
sig = atoi(argv[3]);
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;
}