/* * 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 #include #include #include #include #include #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 */ 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 "); 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 = 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; }