1/*
2 * Copyright (C) 2022 Huawei Technologies Co., Ltd.
3 * Licensed under the Mulan PSL v2.
4 * You can use this software according to the terms and conditions of the Mulan PSL v2.
5 * You may obtain a copy of Mulan PSL v2 at:
6 *     http://license.coscl.org.cn/MulanPSL2
7 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9 * PURPOSE.
10 * See the Mulan PSL v2 for more details.
11 */
12#include "tlogcat.h"
13
14#include <string.h>
15#include <unistd.h>
16#include <fcntl.h>
17#include <time.h>
18#include <errno.h>
19#include <ctype.h>
20#include <dirent.h>
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <sys/ioctl.h> /* for ioctl */
24#include <sys/select.h>
25#include <securec.h>
26
27#include "tee_log.h"
28#include "tarzip.h"
29#include "proc_tag.h"
30#include "sys_log_api.h"
31
32#ifdef LOG_TAG
33#undef LOG_TAG
34#endif
35#define LOG_TAG "tlogcat"
36
37#define LOG_FILE_TA_DEMO            "LOG@A32B3D00CB5711E39C1A0800200C9A66-0"
38#define LOG_FILE_TA_COMPRESS_DEMO   "LOG@A32B3D00CB5711E39C1A0800200C9A66-0.tar.gz"
39#define LOG_FILE_TEE_DEMO           "teeOS_log-0"
40#define LOG_FILE_TEE_COMPRESS_DEMO  "teeos-log-0.tar.gz"
41
42#define TLOGCAT_FILE_MODE           0750
43#define UUID_MAX_STR_LEN            40U
44/* for log item */
45#define LOG_ITEM_MAGIC              0x5A5A
46#define LOG_ITEM_LEN_ALIGN          64
47#define LOG_READ_STATUS_ERROR       0x000FFFF
48
49#ifndef FILE_NAME_MAX_BUF
50#define FILE_NAME_MAX_BUF           256
51#endif
52
53#define LOG_BUFFER_LEN              0x2000 /* 8K */
54#define LOG_FILES_MAX               128U
55#define FILE_VALID                  0x5a5aa5a5
56#define LOG_FILE_LIMIT              (500 * 1024) /* log file size limit:500k */
57#define MAX_TEE_VERSION_LEN         256U
58
59#ifndef TEE_LOG_SUBFOLDER
60#define TEE_LOG_SUBFOLDER "tee"
61#endif
62
63char g_teePath[FILE_NAME_MAX_BUF] = {0};
64char g_teeTempPath[FILE_NAME_MAX_BUF] = {0};
65gid_t g_teePathGroup = 0;
66#ifdef CONFIG_TEE_PRIVATE_LOGFILE
67struct LogFile {
68    FILE *file;
69    struct TeeUuid uuid;
70    long fileLen;
71    uint32_t fileIndex; /* 0,1,2,3 */
72    int32_t valid;      /* FILE_VALID */
73    char logName[FILE_NAME_MAX_BUF];
74};
75static struct LogFile *g_files = NULL;
76#endif
77static char *g_compressFile = NULL;
78static struct TeeUuid g_compressUuid;
79char *g_logBuffer = NULL;
80
81/* for ioctl */
82#define TEELOGGERIO                   0xBE
83#define GET_VERSION_BASE              5
84#define SET_READERPOS_CUR_BASE        6
85#define SET_TLOGCAT_STAT_BASE         7
86#define GET_TLOGCAT_STAT_BASE         8
87
88#define TEELOGGER_GET_VERSION       _IOR(TEELOGGERIO, GET_VERSION_BASE, char[MAX_TEE_VERSION_LEN])
89/* set the log reader pos to current pos */
90#define TEELOGGER_SET_READERPOS_CUR _IO(TEELOGGERIO, SET_READERPOS_CUR_BASE)
91#define TEELOGGER_SET_TLOGCAT_STAT  _IO(TEELOGGERIO, SET_TLOGCAT_STAT_BASE)
92#define TEELOGGER_GET_TLOGCAT_STAT  _IO(TEELOGGERIO, GET_TLOGCAT_STAT_BASE)
93
94static int32_t g_devFd = -1;
95static char g_teeVersion[MAX_TEE_VERSION_LEN];
96static int32_t g_readposCur = 0;
97
98#ifdef CONFIG_TEE_PRIVATE_LOGFILE
99static int32_t GetLogPathBasePos(const char *temp, char **pos)
100{
101    if (access(TEE_LOG_PATH_BASE, F_OK) != 0) {
102        tloge("log dir is not exist\n");
103        return -1;
104    }
105
106    if (strncmp(temp, TEE_LOG_PATH_BASE, strlen(TEE_LOG_PATH_BASE)) == 0) {
107        *pos += strlen(TEE_LOG_PATH_BASE);
108    }
109    return 0;
110}
111
112/*
113 * path:file path name.
114 * P: /data/vendor/log/hisi_logs/tee/
115 * before P version: /data/hisi_logs/running_trace/teeos_logs/LOG@A32B3D00CB5711E39C1A0800200C9A66-0
116 */
117static int32_t LogFilesMkdirR(const char *path)
118{
119    int32_t ret;
120    bool check = false;
121    char *temp = strdup(path);
122    char *pos = temp;
123
124    if (temp == NULL) {
125        return -1;
126    }
127
128    ret = GetLogPathBasePos(temp, &pos);
129    if (ret != 0) {
130        free(temp);
131        return ret;
132    }
133
134    for (; *pos != '\0'; ++pos) {
135        if (*pos == '/') {
136            *pos = '\0';
137
138            ret = mkdir(temp, S_IRWXU | S_IRWXG);
139            check = (ret < 0 && errno == EEXIST);
140            if (check) {
141                /* file is exist */
142                *pos = '/';
143                continue;
144            } else if (ret != 0) {
145                tloge("mkdir %s fail, errno %d\n", temp, errno);
146                free(temp);
147                return -1;
148            }
149            ret = chmod(temp, TLOGCAT_FILE_MODE);
150            if (ret < 0) {
151                tloge("chmod %s err %d\n", temp, ret);
152            }
153            tlogv("for %s\n", temp);
154            *pos = '/';
155        }
156    }
157
158    free(temp);
159    return 0;
160}
161
162static void GetUuidStr(const struct TeeUuid *uuid, char *name, uint32_t nameLen)
163{
164    int32_t ret = snprintf_s(name, nameLen, nameLen - 1, "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X",
165                             uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, uuid->clockSeqAndNode[0],
166                             uuid->clockSeqAndNode[1], uuid->clockSeqAndNode[2], uuid->clockSeqAndNode[3],
167                             uuid->clockSeqAndNode[4], uuid->clockSeqAndNode[5], uuid->clockSeqAndNode[6],
168                             uuid->clockSeqAndNode[7]);
169    if (ret <= 0) {
170        tloge("convert uuid to string failed\n");
171    }
172
173    return;
174}
175
176static struct LogFile *LogFilesAdd(const struct TeeUuid *uuid, const char *logName,
177    FILE *file, long fileLen, uint32_t index)
178{
179    uint32_t i;
180    errno_t rc;
181
182    for (i = 0; i < LOG_FILES_MAX; i++) {
183        /* filter valid file */
184        if (g_files[i].file != NULL || g_files[i].valid == FILE_VALID) {
185            continue;
186        }
187
188        rc = memcpy_s(g_files[i].logName, sizeof(g_files[i].logName), logName, strlen(logName) + 1);
189        if (rc != EOK) {
190            tloge("memcpy log name failed\n");
191            goto CLOSE_F;
192        }
193
194        rc = memcpy_s(&g_files[i].uuid, sizeof(g_files[i].uuid), uuid, sizeof(struct TeeUuid));
195        if (rc != EOK) {
196            tloge("memcpy uuid failed\n");
197            goto CLOSE_F;
198        }
199
200        g_files[i].file = file;
201        g_files[i].fileLen = fileLen;
202        g_files[i].fileIndex = index;
203        g_files[i].valid = FILE_VALID;
204
205        return &g_files[i];
206    }
207
208CLOSE_F:
209    (void)fclose(file);
210    return NULL;
211}
212
213static void CloseFileFd(int32_t *fd)
214{
215    if (*fd < 0) {
216        return;
217    }
218
219    close(*fd);
220    *fd = -1;
221}
222
223static FILE *LogFilesOpen(const char *logName, long *fileLen)
224{
225    int32_t ret;
226    FILE *file = NULL;
227    bool isNewFile = false;
228
229    int32_t fd1 = open(logName, O_WRONLY);
230    if (fd1 < 0) {
231        /* file is not exist */
232        file = fopen(logName, "a+");
233        isNewFile = true;
234    } else {
235        /* file is exist */
236        file = fdopen(fd1, "w");
237    }
238
239    if (file == NULL) {
240        tloge("open error:logName %s, errno %d\n", logName, errno);
241        CloseFileFd(&fd1);
242        return NULL;
243    }
244
245    int32_t fd2 = fileno(file);
246    if (fd2 < 0) {
247        tloge("fileno is error\n");
248        (void)fclose(file);
249        return NULL;
250    }
251    ret = fchmod(fd2, S_IRUSR | S_IWUSR | S_IRGRP);
252    if (ret < 0) {
253        tloge("chmod error\n");
254    }
255
256    (void)fseek(file, 0L, SEEK_END); /* seek to file ending */
257    if (fileLen != NULL) {
258        *fileLen = ftell(file); /* get file len */
259    }
260
261    /* write tee version info */
262    if (isNewFile) {
263        size_t ret1 = fwrite(g_teeVersion, 1, strlen(g_teeVersion), file);
264        if (ret1 == 0) {
265            tloge("write tee verion to %s failed %zu\n", logName, ret1);
266        }
267    }
268
269    return file;
270}
271#endif
272
273static bool IsTaUuid(const struct TeeUuid *uuid)
274{
275    if (uuid == NULL) {
276        return false;
277    }
278
279    uint32_t i;
280    uint8_t *p = (uint8_t *)uuid;
281    for (i = 0; i < sizeof(*uuid); i++) {
282        if (p[i] != 0) {
283            return true;
284        }
285    }
286
287    return false;
288}
289
290#ifdef CONFIG_TEE_PRIVATE_LOGFILE
291struct FileNameAttr {
292    const char *uuidAscii;
293    bool isTa;
294    uint32_t index;
295};
296
297static void SetFileNameAttr(struct FileNameAttr *nameAttr, const char *uuidAscii,
298    bool isTa, uint32_t index)
299{
300    nameAttr->uuidAscii = uuidAscii;
301    nameAttr->isTa = isTa;
302    nameAttr->index = index;
303}
304
305static int32_t LogAssembleFilename(char *logName, size_t logNameLen,
306    const char *logPath, const struct FileNameAttr *nameAttr)
307{
308    if (nameAttr->isTa) {
309        return snprintf_s(logName, logNameLen, strlen(LOG_FILE_TA_DEMO) + strlen(logPath), "%s%s%s-%u",
310            logPath, "LOG@", nameAttr->uuidAscii, nameAttr->index);
311    } else {
312        return snprintf_s(logName, logNameLen, strlen(LOG_FILE_TEE_DEMO) + strlen(logPath), "%s%s-%u",
313            logPath, "teeOS_log", nameAttr->index);
314    }
315}
316
317static int32_t LogAssembleCompressFilename(char *logName, size_t logNameLen,
318    const char *logPath, const struct FileNameAttr *nameAttr)
319{
320    if (nameAttr->isTa) {
321        return snprintf_s(logName, logNameLen, strlen(LOG_FILE_TA_COMPRESS_DEMO) + strlen(logPath),
322            "%s%s%s-%u.tar.gz", logPath, "LOG@", nameAttr->uuidAscii, nameAttr->index);
323    } else {
324        return snprintf_s(logName, logNameLen, strlen(LOG_FILE_TEE_COMPRESS_DEMO) + strlen(logPath),
325            "%s%s-%u.tar.gz", logPath, "teeos-log", nameAttr->index);
326    }
327}
328
329static void TriggerCompress(void)
330{
331    char *filesToCompress[LOG_FILE_INDEX_MAX] = {0};
332    uint32_t i;
333    int32_t ret;
334    char uuidAscii[UUID_MAX_STR_LEN] = {0};
335    struct FileNameAttr nameAttr = {0};
336    bool isTa = IsTaUuid(&g_compressUuid);
337
338    GetUuidStr(&g_compressUuid, uuidAscii, sizeof(uuidAscii));
339
340    for (i = 0; i < LOG_FILE_INDEX_MAX; i++) {
341        filesToCompress[i] = malloc(FILE_NAME_MAX_BUF);
342        if (filesToCompress[i] == NULL) {
343            tloge("malloc file for compress failed\n");
344            goto FREE_RES;
345        }
346
347        SetFileNameAttr(&nameAttr, uuidAscii, isTa, i);
348        ret = LogAssembleFilename(filesToCompress[i], FILE_NAME_MAX_BUF, g_teeTempPath, &nameAttr);
349        if (ret < 0) {
350            tloge("snprintf file to compress error %d %s, %s, %u\n", ret, g_teeTempPath, uuidAscii, i);
351            continue;
352        }
353    }
354
355    TarZipFiles(LOG_FILE_INDEX_MAX, (const char**)filesToCompress, g_compressFile, g_teePathGroup);
356
357FREE_RES:
358    /* remove compressed logs */
359    for (i = 0; i < LOG_FILE_INDEX_MAX; i++) {
360        if (filesToCompress[i] == NULL) {
361            continue;
362        }
363
364        ret = unlink(filesToCompress[i]);
365        if (ret < 0) {
366            tloge("unlink file %s failed, ret %d\n", filesToCompress[i], ret);
367        }
368        free(filesToCompress[i]);
369    }
370
371    ret = rmdir(g_teeTempPath);
372    if (ret < 0) {
373        tloge("rmdir failed %s, ret %d, errno %d\n", g_teeTempPath, ret, errno);
374    }
375}
376#endif
377
378char g_logNameCompress[FILE_NAME_MAX_BUF] = {0};
379char g_uuidAscii[UUID_MAX_STR_LEN] = {0};
380char g_logName[FILE_NAME_MAX_BUF] = {0};
381
382#ifdef CONFIG_TEE_PRIVATE_LOGFILE
383static char *GetCompressFile(const struct TeeUuid *uuid)
384{
385    uint32_t i;
386    struct FileNameAttr nameAttr = {0};
387    bool isTa = IsTaUuid(uuid);
388
389    /*
390     * Find a suitable compressed file name,
391     * %s-0.tar.gz, %s-1.tar.gz %s-2.tar.gz %s-3.tar.gz then to zero, recycle using
392     */
393    for (i = 0; i < LOG_FILE_INDEX_MAX; i++) {
394        SetFileNameAttr(&nameAttr, g_uuidAscii, isTa, i);
395        int32_t rc = LogAssembleCompressFilename(g_logNameCompress, sizeof(g_logNameCompress), g_teePath, &nameAttr);
396        if (rc < 0) {
397            tloge("snprintf log name compresserror error %d %s %s %u\n", rc, g_teePath, g_uuidAscii, i);
398            continue;
399        }
400
401        if (access(g_logNameCompress, F_OK) != 0) {
402            break;
403        }
404    }
405
406    if (i >= LOG_FILE_INDEX_MAX) {
407        return NULL;
408    }
409
410    return g_logNameCompress;
411}
412
413static void ArrangeCompressFile(const struct TeeUuid *uuid)
414{
415    uint32_t i;
416    int32_t ret;
417    struct FileNameAttr nameAttr = {0};
418    bool isTa = IsTaUuid(uuid);
419
420    /* delete first file, and other files's name number rename forward. */
421    SetFileNameAttr(&nameAttr, g_uuidAscii, isTa, 0);
422    ret = LogAssembleCompressFilename(g_logName, sizeof(g_logName), g_teePath, &nameAttr);
423    if (ret < 0) {
424        tloge("arrange snprintf error %d %s %s %d\n", ret, g_teePath, g_uuidAscii, 0);
425        return;
426    }
427    ret = unlink(g_logName);
428    if (ret < 0) {
429        tloge("unlink failed %s, %d\n", g_logName, ret);
430    }
431
432    /*
433     * Updates the names of files except the first file.
434     * Use the last file name as the compress file name.
435     */
436    for (i = 1; i < LOG_FILE_INDEX_MAX; i++) {
437        SetFileNameAttr(&nameAttr, g_uuidAscii, isTa, i);
438        ret = LogAssembleCompressFilename(g_logNameCompress, sizeof(g_logNameCompress), g_teePath, &nameAttr);
439        if (ret < 0) {
440            tloge("snprintf log name compress error %d %s %s %u\n", ret, g_teePath, g_uuidAscii, i);
441            continue;
442        }
443
444        ret = rename(g_logNameCompress, g_logName);
445        if (ret < 0) {
446            tloge("rename error %s, %s, %d, errno %d\n", g_logNameCompress, g_logName, ret, errno);
447        }
448
449        ret = memcpy_s(g_logName, sizeof(g_logName), g_logNameCompress, sizeof(g_logNameCompress));
450        if (ret != EOK) {
451            tloge("memcpy_s error %d\n", ret);
452            return;
453        }
454    }
455}
456
457static void UnlinkTmpFile(const char *name)
458{
459    struct stat st = {0};
460
461    if (lstat(name, &st) < 0) {
462        tloge("lstat %s failed, errno is %d\n", name, errno);
463        return;
464    }
465
466    if (S_ISDIR(st.st_mode)) {
467        return;
468    }
469
470    if (unlink(name) < 0) {
471        tloge("unlink %s failed, errno is %d\n", name, errno);
472    }
473}
474
475static void LogTmpDirClear(const char *tmpPath)
476{
477    int32_t ret;
478    char filePathName[FILE_NAME_MAX_BUF] = {0};
479
480    DIR *dir = opendir(tmpPath);
481    if (dir == NULL) {
482        tloge("open dir %s failed, errno:%d\n", tmpPath, errno);
483        return;
484    }
485
486    struct dirent *de = readdir(dir);
487
488    while (de != NULL) {
489        if (strncmp(de->d_name, "..", sizeof("..")) == 0 || strncmp(de->d_name, ".", sizeof(".")) == 0) {
490            de = readdir(dir);
491            continue;
492        }
493        ret = snprintf_s(filePathName, sizeof(filePathName), sizeof(filePathName) - 1,
494            "%s/%s", tmpPath, de->d_name);
495        if (ret == -1) {
496            tloge("get fiel path name failed %d\n", ret);
497            de = readdir(dir);
498            continue;
499        }
500        UnlinkTmpFile(filePathName);
501        de = readdir(dir);
502    }
503
504    (void)closedir(dir);
505    ret = rmdir(tmpPath);
506    if (ret < 0) {
507        tloge("clear %s failed, err:%d, errno:%d\n", tmpPath, ret, errno);
508    }
509}
510
511static int32_t MkdirTmpPath(const char *tmpPath)
512{
513    int32_t ret;
514
515    /* create a temp path, and move these files to this path for compressing */
516    ret = rmdir(tmpPath);
517
518    bool check = (ret < 0 && errno != ENOENT);
519    if (check) {
520        LogTmpDirClear(tmpPath);
521    }
522
523    ret = mkdir(tmpPath, TLOGCAT_FILE_MODE);
524    if (ret < 0) {
525        tloge("mkdir %s failed, errno:%d\n", tmpPath, errno);
526        return -1;
527    }
528    return 0;
529}
530
531static void MoveFileToTmpPath(bool isTa, uint32_t index)
532{
533    int32_t ret;
534    struct FileNameAttr nameAttr = {0};
535
536    SetFileNameAttr(&nameAttr, g_uuidAscii, isTa, index);
537    ret = LogAssembleFilename(g_logName, sizeof(g_logName), g_teePath, &nameAttr);
538    if (ret < 0) {
539        tloge("snprintf log name error %d %s %s %u\n", ret, g_teePath, g_uuidAscii, index);
540        return;
541    }
542
543    ret = LogAssembleFilename(g_logNameCompress, sizeof(g_logNameCompress), g_teeTempPath, &nameAttr);
544    if (ret < 0) {
545        tloge("snprintf log name compress error %d %s %s %u\n", ret, g_teeTempPath, g_uuidAscii, index);
546        return;
547    }
548
549    ret = rename(g_logName, g_logNameCompress);
550    bool check = (ret < 0 && errno != ENOENT);
551    /* File is exist, but rename is failed */
552    if (check) {
553        tloge("rename %s failed, err: %d, errno:%d\n", g_logName, ret, errno);
554        ret = unlink(g_logName);
555        if (ret < 0) {
556            tloge("unlink failed %s %d\n", g_logName, ret);
557        }
558    }
559}
560
561static void LogFilesCompress(const struct TeeUuid *uuid)
562{
563    int32_t i;
564    int32_t rc;
565    bool isTa = IsTaUuid(uuid);
566
567    rc = MkdirTmpPath(g_teeTempPath);
568    if (rc != 0) {
569        return;
570    }
571
572    GetUuidStr(uuid, g_uuidAscii, sizeof(g_uuidAscii));
573
574    for (i = LOG_FILE_INDEX_MAX - 1; i >= 0; i--) {
575        MoveFileToTmpPath(isTa, (uint32_t)i);
576    }
577
578    /*
579     * Find a suitable compressed file name,
580     * %s-0.tar.gz, %s-1.tar.gz %s-2.tar.gz %s-3.tar.gz then to zero, recycle using.
581     */
582    if (GetCompressFile(uuid) == NULL) {
583        /*
584         * Delete first file, and other files's name number rename forward.
585         * Use the last file name as the compress file name.
586         */
587        ArrangeCompressFile(uuid);
588    }
589
590    g_compressFile = g_logNameCompress;
591    rc = memcpy_s(&g_compressUuid, sizeof(g_compressUuid), uuid, sizeof(struct TeeUuid));
592    if (rc != EOK) {
593        tloge("memcpy_s error %d\n", rc);
594        return;
595    }
596
597    TriggerCompress();
598}
599
600static void LogFileFull(uint32_t fileNum)
601{
602    char logName[FILE_NAME_MAX_BUF] = {0};
603    char logName2[FILE_NAME_MAX_BUF] = {0};
604    char uuidAscii[UUID_MAX_STR_LEN] = {0};
605    int32_t rc;
606    struct FileNameAttr nameAttr = {0};
607
608    if (g_files[fileNum].fileIndex >= LOG_FILE_INDEX_MAX) {
609        tloge("the file index is overflow %u\n", g_files[fileNum].fileIndex);
610        return;
611    }
612
613    bool isTa = IsTaUuid(&g_files[fileNum].uuid);
614
615    GetUuidStr(&g_files[fileNum].uuid, uuidAscii, sizeof(uuidAscii));
616
617    SetFileNameAttr(&nameAttr, uuidAscii, isTa, 0);
618    rc = LogAssembleFilename(logName, sizeof(logName), g_teePath, &nameAttr);
619    if (rc < 0) {
620        tloge("snprintf log name error %d %s %s %d\n", rc, g_teePath, uuidAscii, 0);
621        return;
622    }
623
624    SetFileNameAttr(&nameAttr, uuidAscii, isTa, g_files[fileNum].fileIndex + 1);
625    rc = LogAssembleFilename(logName2, sizeof(logName2), g_teePath, &nameAttr);
626    if (rc < 0) {
627        tloge("snprintf log name2 error %d %s %s %u\n", rc, g_teePath, uuidAscii, g_files[fileNum].fileIndex + 1);
628        return;
629    }
630
631    rc = rename(logName, logName2);
632    if (rc < 0) {
633        tloge("file full and rename error %s, %s, %d, errno %d\n", logName, logName2, rc, errno);
634    }
635
636    return;
637}
638
639static int32_t LogFilesChecklimit(uint32_t fileNum)
640{
641    if (g_files[fileNum].fileLen >= LOG_FILE_LIMIT) {
642        (void)fclose(g_files[fileNum].file);
643
644        if (g_files[fileNum].fileIndex >= (LOG_FILE_INDEX_MAX - 1)) {
645            /* four files are all full, need to compress files. */
646            LogFilesCompress(&g_files[fileNum].uuid);
647        } else {
648            /* this file is full */
649            LogFileFull(fileNum);
650        }
651
652        errno_t rc = memset_s(&g_files[fileNum], sizeof(g_files[fileNum]), 0, sizeof(struct LogFile));
653        if (rc != EOK) {
654            tloge("memset failed %d\n", rc);
655        }
656
657        return -1;
658    }
659
660    return 0;
661}
662
663static struct LogFile *GetUsableFile(const struct TeeUuid *uuid)
664{
665    uint32_t i;
666
667    for (i = 0; i < LOG_FILES_MAX; i++) {
668        if (memcmp(&g_files[i].uuid, uuid, sizeof(struct TeeUuid)) != 0) {
669            continue;
670        }
671
672        if (g_files[i].valid != FILE_VALID) {
673            continue;
674        }
675
676        if (g_files[i].file == NULL) {
677            tloge("unexpected error in index %u, file is null\n", i);
678            (void)memset_s(&g_files[i], sizeof(g_files[i]), 0, sizeof(g_files[i]));
679            continue;
680        }
681
682        /* check file len is limit */
683        if (LogFilesChecklimit(i) != 0) {
684            continue;
685        }
686
687        tlogd("get log file %s\n", g_files[i].logName);
688        return &g_files[i];
689    }
690
691    return NULL;
692}
693
694static void GetFileIndex(const char *uuidAscii, bool isTa, uint32_t *fileIndex)
695{
696    char logName[FILE_NAME_MAX_BUF] = {0};
697    int32_t i;
698    struct FileNameAttr nameAttr = {0};
699
700    /* get the number of file */
701    for (i = LOG_FILE_INDEX_MAX - 1; i >= 0; i--) {
702        *fileIndex = (uint32_t)i;
703
704        SetFileNameAttr(&nameAttr, uuidAscii, isTa, (uint32_t)i);
705        int32_t ret = LogAssembleFilename(logName, sizeof(logName), g_teePath, &nameAttr);
706        if (ret < 0) {
707            tloge("snprintf log name error %d %s %s %d\n", ret, g_teePath, uuidAscii, i);
708            continue;
709        }
710
711        if (access(logName, F_OK) == 0) {
712            break;
713        }
714    }
715}
716
717static struct LogFile *LogFilesGet(const struct TeeUuid *uuid, bool isTa)
718{
719    uint32_t fileIndex;
720    errno_t rc;
721    char logName[FILE_NAME_MAX_BUF] = {0};
722    char uuidAscii[UUID_MAX_STR_LEN] = {0};
723    long fileLen;
724    FILE *file = NULL;
725    struct FileNameAttr nameAttr = {0};
726
727    if (uuid == NULL) {
728        return NULL;
729    }
730
731    struct LogFile *logFile = GetUsableFile(uuid);
732    if (logFile != NULL) {
733        return logFile;
734    }
735
736    /* base on uuid data, new a file */
737    if (LogFilesMkdirR(g_teePath) != 0) {
738        tloge("mkdir log path is failed\n");
739        return NULL;
740    }
741    GetUuidStr(uuid, uuidAscii, sizeof(uuidAscii));
742
743    /* get the number of file */
744    GetFileIndex(uuidAscii, isTa, &fileIndex);
745
746    /* each time write the "-0" suffix name file */
747    SetFileNameAttr(&nameAttr, uuidAscii, isTa, 0);
748    rc = LogAssembleFilename(logName, sizeof(logName), g_teePath, &nameAttr);
749    if (rc < 0) {
750        tloge("snprintf log name error %d %s %s\n", rc, g_teePath, uuidAscii);
751        return NULL;
752    }
753
754    file = LogFilesOpen(logName, &fileLen);
755    if (file == NULL) {
756        return NULL;
757    }
758
759    return LogFilesAdd(uuid, logName, file, fileLen, fileIndex);
760}
761
762static void LogFilesClose(void)
763{
764    uint32_t i;
765
766    if (g_files == NULL) {
767        return;
768    }
769
770    /*
771     * Check whether the file size exceeds the value of LOG_FILE_LIMIT. If yes, create another file.
772     * If the four files are all full, compress the files and delete the original files.
773     */
774    for (i = 0; i < LOG_FILES_MAX; i++) {
775        if (g_files[i].file == NULL) {
776            continue;
777        }
778
779        tlogd("close file %s, fileLen %ld\n", g_files[i].logName, g_files[i].fileLen);
780        (void)fflush(g_files[i].file);
781        (void)fclose(g_files[i].file);
782
783        if (g_files[i].fileLen >= LOG_FILE_LIMIT) {
784            if (g_files[i].fileIndex >= (LOG_FILE_INDEX_MAX - 1)) {
785                /* four files are all full, need to compress files. */
786                LogFilesCompress(&g_files[i].uuid);
787            } else {
788                /* this file is full */
789                LogFileFull(i);
790            }
791        }
792
793        (void)memset_s(&g_files[i], sizeof(g_files[i]), 0, sizeof(g_files[i]));
794    }
795}
796#endif
797
798static void HelpShow(void)
799{
800    printf("this is help, you should input:\n");
801    printf("    -v:  print Tee version\n");
802    printf("    -t:  only print the new log\n");
803}
804
805static struct LogItem *LogItemGetNext(const char *logBuffer, size_t scopeLen)
806{
807    if (logBuffer == NULL) {
808        return NULL;
809    }
810
811    struct LogItem *logItemNext = (struct LogItem *)logBuffer;
812
813    size_t itemMaxSize = ((scopeLen > LOG_ITEM_MAX_LEN) ? LOG_ITEM_MAX_LEN : scopeLen);
814
815    bool isValidItem = ((logItemNext->magic == LOG_ITEM_MAGIC) &&
816        (logItemNext->logBufferLen > 0) && (logItemNext->logRealLen > 0) &&
817        (logItemNext->logBufferLen % LOG_ITEM_LEN_ALIGN == 0) &&
818        (logItemNext->logRealLen <= logItemNext->logBufferLen) &&
819        ((logItemNext->logBufferLen - logItemNext->logRealLen) < LOG_ITEM_LEN_ALIGN) &&
820        (logItemNext->logBufferLen + sizeof(struct LogItem) <= itemMaxSize));
821    if (isValidItem) {
822        return logItemNext;
823    }
824
825    tlogd("logItemNext info: magic %x, logBufferLen %x, logRealLen %x\n",
826          logItemNext->magic, logItemNext->logBufferLen, logItemNext->logRealLen);
827    return NULL;
828}
829
830static void LogSetReadposCur(void)
831{
832    int32_t ret;
833    if (g_devFd < 0) {
834        tloge("open log device error\n");
835        return;
836    }
837    ret = ioctl(g_devFd, TEELOGGER_SET_READERPOS_CUR, 0);
838    if (ret != 0) {
839        tloge("set readpos cur failed %d\n", ret);
840    }
841
842    g_readposCur = 1;
843    return;
844}
845
846static int32_t LogSetTlogcatF(void)
847{
848    int32_t ret;
849
850    if (g_devFd < 0) {
851        tloge("open log device error\n");
852        return -1;
853    }
854
855    ret = ioctl(g_devFd, TEELOGGER_SET_TLOGCAT_STAT, 0);
856    if (ret != 0) {
857        tloge("set tlogcat status failed %d\n", ret);
858    }
859
860    return ret;
861}
862
863static int32_t LogGetTlogcatF(void)
864{
865    int32_t ret;
866
867    if (g_devFd < 0) {
868        tloge("open log device error\n");
869        return -1;
870    }
871
872    ret = ioctl(g_devFd, TEELOGGER_GET_TLOGCAT_STAT, 0);
873    if (ret != 0) {
874        tloge("get tlogcat status failed %d\n", ret);
875    }
876
877    return ret;
878}
879
880#ifdef CONFIG_TEE_PRIVATE_LOGFILE
881static void WritePrivateLogFile(const struct LogItem *logItem, bool isTa)
882{
883    size_t writeNum;
884
885    struct LogFile *logFile = LogFilesGet((struct TeeUuid *)logItem->uuid, isTa);
886    if ((logFile == NULL) || (logFile->file == NULL)) {
887        tloge("can not save log, file is null\n");
888        return;
889    }
890
891    writeNum = fwrite(logItem->logBuffer, 1, (size_t)logItem->logRealLen, logFile->file);
892    if (writeNum != (size_t)logItem->logRealLen) {
893        tloge("save file failed %zu, %u\n", writeNum, logItem->logRealLen);
894        (void)fclose(logFile->file);
895        (void)memset_s(logFile, sizeof(struct LogFile), 0, sizeof(struct LogFile));
896    }
897
898    logFile->fileLen += (long)writeNum;
899}
900#endif
901
902static void WriteLogFile(const struct LogItem *logItem)
903{
904    bool isTa = IsTaUuid((struct TeeUuid *)logItem->uuid);
905
906    LogWriteSysLog(logItem, isTa);
907#ifdef CONFIG_TEE_PRIVATE_LOGFILE
908    WritePrivateLogFile(logItem, isTa);
909#endif
910}
911
912static void OutputLog(struct LogItem *logItem, bool writeFile)
913{
914    if (writeFile) {
915        /* write log file */
916        WriteLogFile(logItem);
917        return;
918    }
919
920    /* ouput log info to display interface */
921    if (logItem->logRealLen < logItem->logBufferLen) {
922        logItem->logBuffer[logItem->logRealLen] = 0;
923    } else {
924        logItem->logBuffer[logItem->logRealLen - 1] = 0;
925    }
926    printf("%s", (char *)logItem->logBuffer);
927}
928
929static void ReadLogBuffer(bool writeFile, const char *logBuffer, size_t readLen)
930{
931    size_t logItemTotalLen = 0;
932
933    /* Cyclically processes all log records. */
934    struct LogItem *logItem = LogItemGetNext(logBuffer, readLen);
935
936    while (logItem != NULL) {
937        tlogd("get log length %u\n", logItem->logBufferLen);
938
939        OutputLog(logItem, writeFile);
940
941        /* check log item have been finished */
942        logItemTotalLen += logItem->logBufferLen + sizeof(struct LogItem);
943        if (logItemTotalLen >= readLen) {
944            tlogd("totallen %zd, readLen %zd\n", logItemTotalLen, readLen);
945            break;
946        }
947
948        logItem = LogItemGetNext((char *)(logItem->logBuffer + logItem->logBufferLen),
949            readLen - logItemTotalLen);
950    }
951}
952
953#define SLEEP_NAO_SECONDS 300000000
954static void ProcReadStatusError(void)
955{
956    /* sleep 300 ms then retry, tv_nsec' unit is nanosecond */
957    struct timespec requst = {0};
958
959    requst.tv_nsec = SLEEP_NAO_SECONDS;
960    (void)nanosleep(&requst, NULL);
961}
962
963static int32_t ProcReadLog(bool writeFile, const fd_set *readset)
964{
965    ssize_t ret;
966    size_t readLen;
967
968    if (FD_ISSET(g_devFd, readset)) {
969    READ_RETRY:
970        ret = read(g_devFd, g_logBuffer, LOG_BUFFER_LEN);
971        if (ret == 0) {
972            tlogd("tlogcat read no data:ret=%zd\n", ret);
973            return -1;
974        } else if (ret < 0) {
975            tloge("tlogcat read failed:ret=%zd\n", ret);
976            return -1;
977        } else if (ret > LOG_BUFFER_LEN) {
978            tloge("invalid read length = %zd\n", ret);
979            return -1;
980        } else {
981            tlogd("read length ret = %zd\n", ret);
982        }
983
984        readLen = (size_t)ret;
985
986        /* if the log crc check is error , maybe the log memory need update, wait a while and try again */
987        if (ret == LOG_READ_STATUS_ERROR) {
988            ProcReadStatusError();
989            goto READ_RETRY;
990        }
991
992        /* Cyclically processes all log records. */
993        ReadLogBuffer(writeFile, g_logBuffer, readLen);
994        goto READ_RETRY; /* read next buffer from log mem */
995    } else {
996        tloge("no have read signal\n");
997    }
998
999    return 0;
1000}
1001
1002static void LogPrintTeeVersion(void);
1003
1004static void Func(bool writeFile)
1005{
1006    int32_t result;
1007    int32_t ret;
1008    fd_set readset;
1009
1010    if (!writeFile) {
1011        LogPrintTeeVersion();
1012    }
1013
1014    while (1) {
1015        /*
1016         * When current logs read finished, the code will return here, close this file,
1017         * and waiting a new start reading.
1018         */
1019#ifdef CONFIG_TEE_PRIVATE_LOGFILE
1020        LogFilesClose();
1021#endif
1022        /* Wait for the log memory read signal. */
1023        do {
1024            FD_ZERO(&readset);
1025            FD_SET(g_devFd, &readset);
1026            tlogd("while select\n");
1027            result = select((g_devFd + 1), &readset, NULL, NULL, NULL);
1028        } while (result == -1 && errno == EINTR);
1029
1030        if (result < 0) {
1031            continue;
1032        }
1033
1034        ret = ProcReadLog(writeFile, &readset);
1035        if (ret != 0) {
1036            continue;
1037        }
1038#ifdef CONFIG_TEE_PRIVATE_LOGFILE
1039        /* close file */
1040        LogFilesClose();
1041#endif
1042        FreeTagNode();
1043    }
1044
1045    return;
1046}
1047
1048static void GetTeePathGroup(void)
1049{
1050#ifdef AID_SYSTEM
1051    g_teePathGroup = AID_SYSTEM;
1052#else
1053    struct stat pathStat = {0};
1054
1055    if (stat(TEE_LOG_PATH_BASE, &pathStat) != 0) {
1056        tloge("get base path stat failed\n");
1057        return;
1058    }
1059    g_teePathGroup = pathStat.st_gid;
1060#endif
1061}
1062
1063#define MAX_TEE_LOG_SUBFOLDER_LEN      30U
1064#define TEE_COMPRESS_SUBFOLDER         "_tmp"
1065static int32_t GetTeeLogPath(void)
1066{
1067    int32_t ret;
1068
1069    if (strnlen(TEE_LOG_PATH_BASE, FILE_NAME_MAX_BUF) >= FILE_NAME_MAX_BUF ||
1070        strnlen(TEE_LOG_SUBFOLDER, MAX_TEE_LOG_SUBFOLDER_LEN) >= MAX_TEE_LOG_SUBFOLDER_LEN) {
1071        tloge("invalid tee path params cfg, please check make scripts\n");
1072        return -1;
1073    }
1074
1075    GetTeePathGroup();
1076
1077    ret = snprintf_s(g_teePath, sizeof(g_teePath), sizeof(g_teePath) - 1,
1078        "%s/%s/", TEE_LOG_PATH_BASE, TEE_LOG_SUBFOLDER);
1079    if (ret < 0) {
1080        tloge("get tee log path failed\n");
1081        return -1;
1082    }
1083
1084    ret = snprintf_s(g_teeTempPath, sizeof(g_teeTempPath), sizeof(g_teeTempPath) - 1,
1085        "%s/%s/", g_teePath, TEE_COMPRESS_SUBFOLDER);
1086    if (ret < 0) {
1087        tloge("get tee temp log path failed\n");
1088        return -1;
1089    }
1090    return 0;
1091}
1092
1093static int32_t Prepare(void)
1094{
1095    int32_t ret = GetTeeLogPath();
1096    if (ret != 0) {
1097        return ret;
1098    }
1099
1100    g_logBuffer = malloc(LOG_BUFFER_LEN);
1101    if (g_logBuffer == NULL) {
1102        tloge("malloc log buffer failed\n");
1103        return -1;
1104    }
1105
1106#ifdef CONFIG_TEE_PRIVATE_LOGFILE
1107    g_files = malloc(sizeof(struct LogFile) * LOG_FILES_MAX);
1108    if (g_files == NULL) {
1109        tloge("malloc files failed\n");
1110        return -1;
1111    }
1112
1113    (void)memset_s(g_files, (sizeof(struct LogFile) * LOG_FILES_MAX), 0, (sizeof(struct LogFile) * LOG_FILES_MAX));
1114#endif
1115
1116    g_devFd = open("/dev/teelog", O_RDONLY);
1117    if (g_devFd < 0) {
1118        tloge("open log device error\n");
1119        return -1;
1120    }
1121
1122    tlogd("open dev success g_devFd=%d\n", g_devFd);
1123
1124    /* get tee version info */
1125    ret = ioctl(g_devFd, TEELOGGER_GET_VERSION, g_teeVersion);
1126    if (ret != 0) {
1127        tloge("get tee verison failed %d\n", ret);
1128    }
1129
1130    OpenTeeLog();
1131    return 0;
1132}
1133
1134static void Destruct(void)
1135{
1136    if (g_logBuffer != NULL) {
1137        free(g_logBuffer);
1138        g_logBuffer = NULL;
1139    }
1140
1141#ifdef CONFIG_TEE_PRIVATE_LOGFILE
1142    if (g_files != NULL) {
1143        free(g_files);
1144        g_files = NULL;
1145    }
1146#endif
1147
1148    if (g_devFd >= 0) {
1149        close(g_devFd);
1150        g_devFd = -1;
1151    }
1152
1153    CloseTeeLog();
1154}
1155
1156static void LogPrintTeeVersion(void)
1157{
1158    g_teeVersion[sizeof(g_teeVersion) - 1] = '\0';
1159    printf("%s\n", g_teeVersion);
1160}
1161
1162#define SET_TLOGCAT_F 1
1163static int32_t LogCmdF(void)
1164{
1165    printf("HAVE option: -f\n");
1166
1167    if (LogGetTlogcatF() == SET_TLOGCAT_F) {
1168        tlogd("tlogcat is running\n");
1169        printf("tlogcat -f is running, only one instance is allowed at the same time\n");
1170        return 0;
1171    } else {
1172        tlogd("tlogcat running prop has not been set, first time running tlogat -f\n");
1173    }
1174
1175    if (LogSetTlogcatF() != 0) {
1176        tloge("set tlogcat running prop to 1 failed\n");
1177        return -1;
1178    } else {
1179        tlogi("set tlogcat running prop to 1 succ\n");
1180    }
1181
1182    Func(true);
1183    return 0;
1184}
1185
1186static bool g_defaultOp = true;
1187static int32_t SwitchSelect(int32_t ch)
1188{
1189    switch (ch) {
1190        case 'v':
1191            LogPrintTeeVersion();
1192            g_defaultOp = false;
1193            break;
1194        case 't':
1195            LogSetReadposCur();
1196            break;
1197        case 'f':
1198            if (LogCmdF() != 0) {
1199                return -1;
1200            }
1201            break;
1202        case 'h':
1203            printf("HAVE option: -h\n");
1204            HelpShow();
1205            break;
1206        default:
1207            printf("Unknown option: %c\n", (char)optopt);
1208            HelpShow();
1209            break;
1210    }
1211    return 0;
1212}
1213
1214int32_t main(int32_t argc, char *argv[])
1215{
1216    printf("tlogcat start ++\n");
1217    int32_t ch;
1218    g_defaultOp = true;
1219
1220    if (Prepare() < 0) {
1221        goto FREE_RES;
1222    }
1223
1224    while ((ch = getopt(argc, argv, "f::ms:ghvt")) != -1) {
1225        g_defaultOp = false;
1226        if (optind > 0 && optind < argc) {
1227            tlogd("optind: %d, argc:%d, argv[%d]:%s\n", optind, argc, optind, argv[optind]);
1228        }
1229        if (SwitchSelect(ch) != 0) {
1230            tloge("option failed\n");
1231        }
1232
1233        printf("----------------------------\n");
1234        if (optind > 0 && optind < argc) {
1235            tlogd("optind=%d, argv[%d]=%s\n", optind, optind, argv[optind]);
1236        }
1237    }
1238
1239    if (g_defaultOp || g_readposCur != 0) {
1240        Func(false);
1241    }
1242
1243    printf("tlogcat end --\n");
1244
1245FREE_RES:
1246    Destruct();
1247    return 0;
1248}
1249