1 /*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6 /*
7 * Copyright (C) 2018-2022 Intel Corporation.
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 #include <errno.h>
23 #include <stdio.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "crash_dump.h"
30 #include "packet.h"
31 #include "log_sys.h"
32 #include "protocol.h"
33 #include "version.h"
34
35 /**
36 * Usercrash works as C/S model: usercrash_c works as usercrash client to
37 * collect crash logs and information once crash event occurs. For each time,
38 * usercrash_c receives 3 params from core_dump and sends connect request event
39 * to usercrash_s, then it receives file fd from server to fill crash info into
40 * the file. After this work is done, it will notify server that dump work is
41 * completed.
42 */
43
44 /**
45 * @sockfd: the socket fd.
46 * set_timeout is used to set timeout for the sockfd, in case client is blocked
47 * when client cannot receive the data from server, or send data to server
48 */
set_timeout(int sockfd)49 static int set_timeout(int sockfd)
50 {
51 struct timeval timeout = {50, 0};
52
53 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
54 sizeof(timeout)) != 0) {
55 LOGE("failed to set receive timeout\n");
56 return -1;
57 }
58 if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
59 sizeof(timeout)) != 0) {
60 LOGE("failed to set send timeout\n");
61 return -1;
62 }
63 return 0;
64 }
65
66 /**
67 * @pid: crash process pid.
68 * @usercrashd_socket: client socket fd pointer, get the value from
69 * usercrashd_connect function.
70 * @output_fd: file fd, receives from server to store dump file.
71 * @name: crash process name
72 * usercrashd_connect is used to connect to server, and get the crash file fd
73 * from server
74 */
usercrashd_connect(int pid,int * usercrashd_socket,int * output_fd,const char * name)75 static int usercrashd_connect(int pid, int *usercrashd_socket,
76 int *output_fd, const char *name)
77 {
78 int sockfd;
79 int tmp_output_fd;
80 ssize_t rc;
81 int flags;
82 struct crash_packet packet = {0};
83
84 if (name == NULL) {
85 LOGE("crash process name is NULL\n");
86 return -1;
87 }
88 sockfd = socket_local_client(SOCKET_NAME, strlen(SOCKET_NAME),
89 SOCK_SEQPACKET | SOCK_CLOEXEC);
90 if (sockfd == -1) {
91 LOGE("failed to connect to usercrashd, error (%s)\n",
92 strerror(errno));
93 return -1;
94 }
95 packet.packet_type = kDumpRequest;
96 packet.pid = pid;
97 strncpy(packet.name, name, COMM_NAME_LEN);
98 packet.name[COMM_NAME_LEN - 1] = '\0';
99 if (set_timeout(sockfd)) {
100 close(sockfd);
101 return -1;
102 }
103 if (write(sockfd, &packet, sizeof(packet)) !=
104 sizeof(packet)) {
105 LOGE("failed to write DumpRequest packet, error (%s)\n",
106 strerror(errno));
107 close(sockfd);
108 return -1;
109 }
110
111 rc = recv_fd(sockfd, &packet, sizeof(packet),
112 &tmp_output_fd);
113 if (rc == -1) {
114 LOGE("failed to read response to DumpRequest packet, ");
115 LOGE("error (%s)\n", strerror(errno));
116 close(sockfd);
117 return -1;
118 } else if (rc != sizeof(packet)) {
119 LOGE("received DumpRequest response packet of incorrect ");
120 LOGE("length (expected %lu, got %ld)\n", sizeof(packet), rc);
121 goto fail;
122 }
123 if (packet.packet_type != kPerformDump) {
124 LOGE("unexpected dump response:%d\n", packet.packet_type);
125 goto fail;
126 }
127
128 /**
129 * Make the fd O_APPEND so that our output is guaranteed to be at the
130 * end of a file. (This also makes selinux rules consistent, because
131 * selinux distinguishes between writing to a regular fd, and writing
132 * to an fd with O_APPEND)
133 */
134 flags = fcntl(tmp_output_fd, F_GETFL);
135 if (fcntl(tmp_output_fd, F_SETFL, flags | O_APPEND) != 0) {
136 LOGE("failed to set output fd flags, error (%s)\n",
137 strerror(errno));
138 goto fail;
139 }
140
141 *usercrashd_socket = sockfd;
142 *output_fd = tmp_output_fd;
143
144 return 0;
145 fail:
146 close(sockfd);
147 close(tmp_output_fd);
148 return -1;
149
150 }
151
152 /**
153 * @usercrashd_socket: client socket fd, used to communicate with server.
154 * usercrashd_notify_completion is used to tell the server it has done the
155 * dump, the server will pop another crash from the queue and execute the dump
156 * process
157 */
usercrashd_notify_completion(int usercrashd_socket)158 static int usercrashd_notify_completion(int usercrashd_socket)
159 {
160 struct crash_packet packet = {0};
161
162 packet.packet_type = kCompletedDump;
163 if (set_timeout(usercrashd_socket)) {
164 close(usercrashd_socket);
165 return -1;
166 }
167 if (write(usercrashd_socket, &packet,
168 sizeof(packet)) != sizeof(packet)) {
169 return -1;
170 }
171 return 0;
172 }
173
print_usage(void)174 static void print_usage(void)
175 {
176 printf("usercrash - tool to dump crash info for the crashes in the ");
177 printf("userspace on sos. It's the client role of usercrash.\n");
178 printf("[Usage]\n");
179 printf("\t--coredump, usercrash_c <pid> <comm> <sig> ");
180 printf("(root role to run)\n");
181 printf("[Option]\n");
182 printf("\t-h: print this usage message\n");
183 printf("\t-v: print usercrash_c version\n");
184 }
185
main(int argc,char * argv[])186 int main(int argc, char *argv[])
187 {
188 int pid;
189 int sig;
190 int out_fd;
191 int sock;
192 int ret;
193
194 if (argc > 1) {
195 if (strcmp(argv[1], "-v") == 0) {
196 printf("version is %d.%d-%s, build by %s@%s\n",
197 UC_MAJOR_VERSION, UC_MINOR_VERSION,
198 UC_BUILD_VERSION, UC_BUILD_USER,
199 UC_BUILD_TIME);
200 return 0;
201 }
202 if (strcmp(argv[1], "-h") == 0) {
203 print_usage();
204 return 0;
205 }
206 } else
207 print_usage();
208
209 if (getuid() != 0) {
210 LOGE("failed to execute usercrash_c, root is required\n");
211 exit(EXIT_FAILURE);
212 }
213
214 if (argc == 4) {
215 /* it's from coredump */
216 pid = (int)strtol(argv[1], NULL, 10);
217 sig = (int)strtol(argv[3], NULL, 10);
218 ret = usercrashd_connect(pid, &sock, &out_fd, argv[2]);
219 if (ret) {
220 LOGE("usercrashd_connect failed, error (%s)\n",
221 strerror(errno));
222 exit(EXIT_FAILURE);
223 }
224 crash_dump(pid, sig, out_fd);
225 close(out_fd);
226 if (usercrashd_notify_completion(sock)) {
227 LOGE("failed to notify usercrashd of completion");
228 close(sock);
229 exit(EXIT_FAILURE);
230 }
231 close(sock);
232 } else {
233 print_usage();
234 return 0;
235 }
236
237 return 0;
238 }
239