blob: e037eff9599e967fd7c88e53854ffe7623ef6b28 [file] [log] [blame]
developere5e687d2023-08-08 16:05:33 +08001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
4 *
5 * Author: Alvin Kuo <Alvin.Kuo@mediatek.com>
6 */
7
8#include <limits.h>
9#include <stdlib.h>
10#include <stdint.h>
11#include <string.h>
12#include <unistd.h>
13#include <stdio.h>
14#include <errno.h>
15#include <fcntl.h>
16#include <time.h>
17#include <poll.h>
18
19#include <sys/types.h>
20#include <sys/stat.h>
21
22#include "dump.h"
23
24static int time_to_str(time_t *time_sec, char *time_str, unsigned int time_str_size)
25{
26 struct tm *ptm;
27 int ret;
28
29 ptm = gmtime(time_sec);
30 if (!ptm)
31 return -1;
32
33 ret = strftime(time_str, time_str_size, "%Y%m%d%H%M%S", ptm);
34 if (!ret)
35 return -2;
36
37 return 0;
38}
39
40static int save_dump_data(char *dump_root_dir,
41 struct dump_data_header *dd_hdr,
42 char *dd)
43{
44 size_t dump_file_size = dd_hdr->info.size + sizeof(struct dump_info);
45 char dump_time_str[32];
46 struct stat st = { 0 };
47 char *dump_file = NULL;
48 char *dump_dir = NULL;
developerf6c5ced2023-10-03 16:23:42 +080049 size_t path_len;
50 int ret;
developere5e687d2023-08-08 16:05:33 +080051 int fd;
52
53 ret = time_to_str((time_t *)&dd_hdr->info.dump_time_sec,
54 dump_time_str, sizeof(dump_time_str));
55 if (ret < 0) {
56 fprintf(stderr,
57 DUMP_LOG_FMT("time_to_str(%lu) fail(%d)\n"),
58 dd_hdr->info.dump_time_sec, ret);
developerf6c5ced2023-10-03 16:23:42 +080059 return ret;
developere5e687d2023-08-08 16:05:33 +080060 }
61
developerf6c5ced2023-10-03 16:23:42 +080062 /* create the dump directory */
63 path_len = strlen(dump_root_dir) + 1 + strlen(dump_time_str) + 1;
64 dump_dir = malloc(path_len);
65 if (!dump_dir)
66 return -ENOMEM;
67
68 ret = snprintf(dump_dir, path_len, "%s/%s", dump_root_dir, dump_time_str);
69 if (ret < 0)
70 goto free_dump_dir;
71
72 ret = mkdir(dump_dir, 0775);
73 if (ret && errno != EEXIST) {
74 fprintf(stderr,
75 DUMP_LOG_FMT("mkdir(%s) fail(%s)\n"),
76 dump_dir, strerror(errno));
77 goto free_dump_dir;
developere5e687d2023-08-08 16:05:33 +080078 }
developere5e687d2023-08-08 16:05:33 +080079
developerf6c5ced2023-10-03 16:23:42 +080080 /* TODO: only keep latest three dump directories */
81
82 /* create the dump file */
83 path_len = strlen(dump_dir) + 1 + strlen(dd_hdr->info.name) + 1;
84 dump_file = malloc(path_len);
developere5e687d2023-08-08 16:05:33 +080085 if (!dump_file) {
86 ret = -ENOMEM;
87 goto free_dump_dir;
88 }
developere5e687d2023-08-08 16:05:33 +080089
developerf6c5ced2023-10-03 16:23:42 +080090 ret = snprintf(dump_file, path_len, "%s/%s", dump_dir, dd_hdr->info.name);
91 if (ret < 0)
92 goto free_dump_file;
developere5e687d2023-08-08 16:05:33 +080093
developer8762a192023-09-04 09:16:22 +080094 fd = open(dump_file, O_WRONLY | O_CREAT, 0664);
developere5e687d2023-08-08 16:05:33 +080095 if (fd < 0) {
96 fprintf(stderr,
97 DUMP_LOG_FMT("open(%s) fail(%s)\n"),
98 dump_file, strerror(errno));
developerf6c5ced2023-10-03 16:23:42 +080099 ret = fd;
developere5e687d2023-08-08 16:05:33 +0800100 goto free_dump_file;
101 }
102
developerf6c5ced2023-10-03 16:23:42 +0800103 /* write the dump information at the begining of dump file */
104 ret = lseek(fd, 0, SEEK_SET);
105 if (ret < 0) {
106 fprintf(stderr,
107 DUMP_LOG_FMT("lseek fail(%s)\n"),
108 strerror(errno));
109 goto close_dump_file;
110 }
111
developere5e687d2023-08-08 16:05:33 +0800112 write(fd, &dd_hdr->info, sizeof(struct dump_info));
113
developerf6c5ced2023-10-03 16:23:42 +0800114 /* write the dump data start from dump information plus data offset */
115 ret = lseek(fd, dd_hdr->data_offset, SEEK_CUR);
116 if (ret < 0) {
117 fprintf(stderr,
118 DUMP_LOG_FMT("lseek fail(%s)\n"),
119 strerror(errno));
120 goto close_dump_file;
121 }
developere5e687d2023-08-08 16:05:33 +0800122
developerf6c5ced2023-10-03 16:23:42 +0800123 write(fd, dd, dd_hdr->data_len);
developere5e687d2023-08-08 16:05:33 +0800124
125 if (dd_hdr->last_frag) {
developerf6c5ced2023-10-03 16:23:42 +0800126 ret = stat(dump_file, &st);
127 if (ret < 0) {
128 fprintf(stderr,
129 DUMP_LOG_FMT("stat(%s) fail(%s)\n"),
130 dump_file, strerror(errno));
131 goto close_dump_file;
132 }
133
developere5e687d2023-08-08 16:05:33 +0800134 if ((size_t)st.st_size != dump_file_size) {
135 fprintf(stderr,
136 DUMP_LOG_FMT("file(%s) size %zu != %zu\n"),
137 dump_file, st.st_size, dump_file_size);
developerf6c5ced2023-10-03 16:23:42 +0800138 ret = -EINVAL;
139 goto close_dump_file;
developere5e687d2023-08-08 16:05:33 +0800140 }
141 }
142
developerf6c5ced2023-10-03 16:23:42 +0800143 ret = 0;
144
145close_dump_file:
146 close(fd);
147
developere5e687d2023-08-08 16:05:33 +0800148free_dump_file:
149 free(dump_file);
developere5e687d2023-08-08 16:05:33 +0800150
151free_dump_dir:
152 free(dump_dir);
developere5e687d2023-08-08 16:05:33 +0800153
developere5e687d2023-08-08 16:05:33 +0800154 return ret;
155}
156
157static int read_retry(int fd, void *buf, int len)
158{
159 int out_len = 0;
160 int ret;
161
162 while (len > 0) {
163 ret = read(fd, buf, len);
164 if (ret < 0) {
165 if (errno == EINTR || errno == EAGAIN)
166 continue;
167
developerf6c5ced2023-10-03 16:23:42 +0800168 return ret;
developere5e687d2023-08-08 16:05:33 +0800169 }
170
171 if (!ret)
developerf6c5ced2023-10-03 16:23:42 +0800172 break;
developere5e687d2023-08-08 16:05:33 +0800173
174 out_len += ret;
175 len -= ret;
176 buf += ret;
177 }
178
179 return out_len;
180}
181
182static int mkdir_p(char *path, mode_t mode)
183{
developerf6c5ced2023-10-03 16:23:42 +0800184 size_t path_len;
185 char *cpy_path;
186 char *cur_path;
187 char *tmp_path;
developere5e687d2023-08-08 16:05:33 +0800188 char *dir;
developerf6c5ced2023-10-03 16:23:42 +0800189 int ret;
developere5e687d2023-08-08 16:05:33 +0800190
developerf6c5ced2023-10-03 16:23:42 +0800191 path_len = strlen(path) + 1;
192 if (path_len == 0)
193 return -EINVAL;
developere5e687d2023-08-08 16:05:33 +0800194
developerf6c5ced2023-10-03 16:23:42 +0800195 cpy_path = malloc(path_len);
196 if (!cpy_path)
197 return -ENOMEM;
198 strncpy(cpy_path, path, path_len);
199
200 cur_path = calloc(1, path_len);
developere5e687d2023-08-08 16:05:33 +0800201 if (!cur_path) {
202 ret = -ENOMEM;
203 goto free_cpy_path;
204 }
developerf6c5ced2023-10-03 16:23:42 +0800205
206 tmp_path = malloc(path_len);
207 if (!tmp_path) {
208 ret = -ENOMEM;
209 goto free_cur_path;
210 }
developere5e687d2023-08-08 16:05:33 +0800211
212 for (dir = strtok(cpy_path, "/");
213 dir != NULL;
214 dir = strtok(NULL, "/")) {
215 /* keep current path */
developerf6c5ced2023-10-03 16:23:42 +0800216 strncpy(tmp_path, cur_path, path_len);
developere5e687d2023-08-08 16:05:33 +0800217
218 /* append directory in current path */
developerf6c5ced2023-10-03 16:23:42 +0800219 ret = snprintf(cur_path, path_len, "%s/%s", tmp_path, dir);
220 if (ret < 0) {
221 fprintf(stderr,
222 DUMP_LOG_FMT("append dir(%s) in cur_path(%s) fail(%d)\n"),
223 dir, cur_path, ret);
224 goto free_tmp_path;
225 }
developere5e687d2023-08-08 16:05:33 +0800226
developerf6c5ced2023-10-03 16:23:42 +0800227 ret = mkdir(cur_path, mode);
228 if (ret && errno != EEXIST) {
229 fprintf(stderr,
230 DUMP_LOG_FMT("mkdir(%s) fail(%s)\n"),
231 cur_path, strerror(errno));
232 goto free_tmp_path;
developere5e687d2023-08-08 16:05:33 +0800233 }
234 }
235
developerf6c5ced2023-10-03 16:23:42 +0800236 ret = 0;
237
238free_tmp_path:
239 free(tmp_path);
240
developere5e687d2023-08-08 16:05:33 +0800241free_cur_path:
242 free(cur_path);
developere5e687d2023-08-08 16:05:33 +0800243
244free_cpy_path:
245 free(cpy_path);
developere5e687d2023-08-08 16:05:33 +0800246
developere5e687d2023-08-08 16:05:33 +0800247 return ret;
248}
249
250int tops_save_dump_data(char *dump_root_dir)
251{
252 struct stat st = { 0 };
253 int ret = 0;
254 int fd;
255
developerf6c5ced2023-10-03 16:23:42 +0800256 if (!dump_root_dir)
257 return -EINVAL;
developere5e687d2023-08-08 16:05:33 +0800258
259 /* reserve 256 bytes for saving name of dump directory and dump file */
260 if (strlen(dump_root_dir) > (PATH_MAX - 256)) {
261 fprintf(stderr,
262 DUMP_LOG_FMT("dump_root_dir(%s) length %zu > %u\n"),
263 dump_root_dir, strlen(dump_root_dir), PATH_MAX - 256);
developerf6c5ced2023-10-03 16:23:42 +0800264 return -EINVAL;
developere5e687d2023-08-08 16:05:33 +0800265 }
266
developerf6c5ced2023-10-03 16:23:42 +0800267 ret = mkdir_p(dump_root_dir, 0775);
268 if (ret < 0) {
269 fprintf(stderr,
270 DUMP_LOG_FMT("mkdir_p(%s) fail(%d)\n"),
271 dump_root_dir, ret);
272 return ret;
developere5e687d2023-08-08 16:05:33 +0800273 }
274
275 fd = open(DUMP_DATA_PATH, O_RDONLY);
276 if (fd < 0) {
277 fprintf(stderr,
278 DUMP_LOG_FMT("open(%s) fail(%s)\n"),
279 DUMP_DATA_PATH, strerror(errno));
developerf6c5ced2023-10-03 16:23:42 +0800280 return fd;
developere5e687d2023-08-08 16:05:33 +0800281 }
282
283 while (1) {
284 char dd[RELAY_DUMP_SUBBUF_SIZE - sizeof(struct dump_data_header)];
285 struct dump_data_header dd_hdr;
286 struct pollfd pfd = {
287 .fd = fd,
288 .events = POLLIN | POLLHUP | POLLERR,
289 };
290
developerf6c5ced2023-10-03 16:23:42 +0800291 ret = poll(&pfd, 1, -1);
292 if (ret < 0) {
293 fprintf(stderr,
294 DUMP_LOG_FMT("poll fail(%s)\n"),
295 strerror(errno));
296 break;
297 }
developere5e687d2023-08-08 16:05:33 +0800298
299 ret = read_retry(fd, &dd_hdr, sizeof(struct dump_data_header));
300 if (ret < 0) {
301 fprintf(stderr,
302 DUMP_LOG_FMT("read dd_hdr fail(%d)\n"), ret);
developere5e687d2023-08-08 16:05:33 +0800303 break;
304 }
305
306 if (!ret)
307 continue;
308
309 if (dd_hdr.data_len == 0) {
310 fprintf(stderr,
311 DUMP_LOG_FMT("read empty data\n"));
312 continue;
313 }
314
315 if (dd_hdr.data_len > sizeof(dd)) {
316 fprintf(stderr,
317 DUMP_LOG_FMT("data length %u > %lu\n"),
318 dd_hdr.data_len, sizeof(dd));
developerf6c5ced2023-10-03 16:23:42 +0800319 ret = -ENOMEM;
developere5e687d2023-08-08 16:05:33 +0800320 break;
321 }
322
323 ret = read_retry(fd, dd, dd_hdr.data_len);
324 if (ret < 0) {
325 fprintf(stderr,
326 DUMP_LOG_FMT("read dd fail(%d)\n"), ret);
developere5e687d2023-08-08 16:05:33 +0800327 break;
328 }
329
330 if ((uint32_t)ret != dd_hdr.data_len) {
331 fprintf(stderr,
332 DUMP_LOG_FMT("read dd length %u != %u\n"),
333 (uint32_t)ret, dd_hdr.data_len);
developerf6c5ced2023-10-03 16:23:42 +0800334 ret = -EAGAIN;
developere5e687d2023-08-08 16:05:33 +0800335 break;
336 }
337
338 ret = save_dump_data(dump_root_dir, &dd_hdr, dd);
339 if (ret) {
340 fprintf(stderr,
341 DUMP_LOG_FMT("save_dump_data(%s) fail(%d)\n"),
342 dump_root_dir, ret);
343 break;
344 }
345 }
346
347 close(fd);
348
developere5e687d2023-08-08 16:05:33 +0800349 return ret;
350}