1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#define _GNU_SOURCE
17
18#include <hilog_adapter.h>
19
20#include <errno.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <string.h>
24#include <sys/socket.h>
25#include <sys/time.h>
26#include <sys/uio.h>
27#include <sys/un.h>
28#include <time.h>
29#include <unistd.h>
30#include <pthread.h>
31#include <stdlib.h>
32#include <fcntl.h>
33#include <atomic.h>
34
35#include "hilog_common.h"
36#ifdef OHOS_ENABLE_PARAMETER
37#include "sys_param.h"
38#endif
39#include "vsnprintf_s_p.h"
40
41#define LOG_LEN 3
42#define ERROR_FD 2
43#ifdef OHOS_ENABLE_PARAMETER
44#define SYSPARAM_LENGTH 32
45#endif
46
47#define INVALID_SOCKET (-1)
48#define INVALID_RESULT (-1)
49#define CAS_FAIL (-1)
50
51const int SOCKET_TYPE = SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC;
52const struct sockaddr_un SOCKET_ADDR = {AF_UNIX, SOCKET_FILE_DIR INPUT_SOCKET_NAME};
53
54static bool musl_log_enable = false;
55
56#ifdef OHOS_ENABLE_PARAMETER
57static const char *param_name = "musl.log.enable";
58static const char *g_logLevelParam = "musl.log.level";
59#endif
60static int g_logLevel = LOG_ERROR;
61static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
62static volatile int g_socketFd = INVALID_SOCKET;
63
64extern int __close(int fd);
65
66// only generate a new socketFd
67static int GenerateHilogSocketFd()
68{
69    int socketFd = TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCKET_TYPE, 0));
70    if (socketFd == INVALID_SOCKET) {
71        dprintf(ERROR_FD, "HiLogAdapter_init: Can't create socket! Errno: %d\n", errno);
72        return INVALID_SOCKET;
73    }
74    long int result =
75        TEMP_FAILURE_RETRY(connect(socketFd, (const struct sockaddr *)(&SOCKET_ADDR), sizeof(SOCKET_ADDR)));
76    if (result == INVALID_RESULT) {
77        dprintf(ERROR_FD, "HiLogAdapter_init: Can't connect to server. Errno: %d\n", errno);
78        __close(socketFd);
79        return INVALID_SOCKET;
80    }
81    return socketFd;
82}
83
84HILOG_LOCAL_API
85int CASHilogGlobalSocketFd(int socketFd)
86{
87    if (socketFd == INVALID_SOCKET) {
88        return INVALID_RESULT;
89    }
90    // we should use CAS to avoid multi-thread problem
91    if (a_cas(&g_socketFd, INVALID_SOCKET, socketFd) != INVALID_SOCKET) {
92        // failure CAS: other threads execute to this branch to close extra fd
93        return CAS_FAIL;
94    }
95    // success CAS: only one thread can execute to this branch
96    return socketFd;
97}
98
99HILOG_LOCAL_API
100bool CheckHilogValid()
101{
102    int socketFd = INVALID_SOCKET;
103    // read fd by using atomic operation
104#ifdef a_ll
105    socketFd = a_ll(&g_socketFd);
106#else
107    socketFd = g_socketFd;
108#endif
109    return socketFd != INVALID_SOCKET;
110}
111
112/**
113* This interface only for static-link style testcase, this symbol should not be exposed in dynamic libraries
114* Dangerous operation, please do not use in normal business
115*/
116HILOG_LOCAL_API
117void RefreshHiLogSocketFd()
118{
119    int socketFd = INVALID_SOCKET;
120    // read fd by using atomic operation
121#ifdef a_ll
122    socketFd = a_ll(&g_socketFd);
123#else
124    socketFd = g_socketFd;
125#endif
126    if (socketFd == INVALID_SOCKET) {
127        return;
128    }
129    a_store(&g_socketFd, INVALID_SOCKET);
130    __close(socketFd);
131}
132
133void InitHilogSocketFd()
134{
135    int socketFd = GenerateHilogSocketFd();
136    if (socketFd == INVALID_SOCKET) {
137        return;
138    }
139    int result = CASHilogGlobalSocketFd(socketFd);
140    if (result == CAS_FAIL) {
141        __close(socketFd);
142    }
143}
144
145static int SendMessage(HilogMsg *header, const char *tag, uint16_t tagLen, const char *fmt, uint16_t fmtLen)
146{
147    bool releaseSocket = false;
148    int socketFd = INVALID_SOCKET;
149    // read fd by using atomic operation
150#ifdef a_ll
151    socketFd = a_ll(&g_socketFd);
152#else
153    socketFd = g_socketFd;
154#endif
155    if (socketFd == INVALID_SOCKET) {
156        socketFd = GenerateHilogSocketFd();
157        if (socketFd == INVALID_SOCKET) {
158            return INVALID_RESULT;
159        }
160        int result = CASHilogGlobalSocketFd(socketFd);
161        if (result == CAS_FAIL) {
162            releaseSocket = true;
163        }
164    }
165
166    struct timespec ts = {0};
167    (void)clock_gettime(CLOCK_REALTIME, &ts);
168    struct timespec ts_mono = {0};
169    (void)clock_gettime(CLOCK_MONOTONIC, &ts_mono);
170    header->tv_sec = (uint32_t)(ts.tv_sec);
171    header->tv_nsec = (uint32_t)(ts.tv_nsec);
172    header->mono_sec = (uint32_t)(ts_mono.tv_sec);
173    header->len = sizeof(HilogMsg) + tagLen + fmtLen;
174    header->tag_len = tagLen;
175
176    struct iovec vec[LOG_LEN] = {0};
177    vec[0].iov_base = header;                   // 0 : index of hos log header
178    vec[0].iov_len = sizeof(HilogMsg);          // 0 : index of hos log header
179    vec[1].iov_base = (void *)((char *)(tag));  // 1 : index of log tag
180    vec[1].iov_len = tagLen;                    // 1 : index of log tag
181    vec[2].iov_base = (void *)((char *)(fmt));  // 2 : index of log content
182    vec[2].iov_len = fmtLen;                    // 2 : index of log content
183    int ret = TEMP_FAILURE_RETRY(writev(socketFd, vec, LOG_LEN));
184    if (releaseSocket) {
185        __close(socketFd);
186    }
187    return ret;
188}
189
190HILOG_LOCAL_API
191int HiLogAdapterPrintArgs(
192    const LogType type, const LogLevel level, const unsigned int domain, const char *tag, const char *fmt, va_list ap)
193{
194    char buf[MAX_LOG_LEN] = {0};
195
196    vsnprintfp_s(buf, MAX_LOG_LEN, MAX_LOG_LEN - 1, true, fmt, ap);
197
198    size_t tagLen = strnlen(tag, MAX_TAG_LEN - 1);
199    size_t logLen = strnlen(buf, MAX_LOG_LEN - 1);
200    HilogMsg header = {0};
201    header.type = type;
202    header.level = level;
203#ifndef __RECV_MSG_WITH_UCRED_
204    header.pid = getpid();
205#endif
206    header.tid = (uint32_t)(gettid());
207    header.domain = domain;
208
209    return SendMessage(&header, tag, tagLen + 1, buf, logLen + 1);
210}
211
212HILOG_LOCAL_API
213int HiLogAdapterPrint(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, ...)
214{
215    if (!HiLogAdapterIsLoggable(domain, tag, level)) {
216        return -1;
217    }
218
219    int ret;
220    va_list ap;
221    va_start(ap, fmt);
222    ret = HiLogAdapterPrintArgs(type, level, domain, tag, fmt, ap);
223    va_end(ap);
224    return ret;
225}
226
227HILOG_LOCAL_API
228int HiLogAdapterVaList(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, va_list ap)
229{
230    if (!HiLogAdapterIsLoggable(domain, tag, level)) {
231        return -1;
232    }
233    return HiLogAdapterPrintArgs(type, level, domain, tag, fmt, ap);
234}
235
236bool is_musl_log_enable()
237{
238    if (getpid() == 1) {
239        return false;
240    }
241    return musl_log_enable;
242}
243
244bool HiLogAdapterIsLoggable(unsigned int domain, const char *tag, LogLevel level)
245{
246    if (tag == NULL || level < g_logLevel || level <= LOG_LEVEL_MIN || level >= LOG_LEVEL_MAX) {
247        return false;
248    }
249
250    if (!is_musl_log_enable()) {
251        return false;
252    }
253
254    return true;
255}
256
257#ifdef OHOS_ENABLE_PARAMETER
258bool get_bool_sysparam(CachedHandle cachedhandle)
259{
260    const char *param_value = CachedParameterGet(cachedhandle);
261    if (param_value != NULL) {
262        if (strcmp(param_value, "true") == 0) {
263            return true;
264        }
265    }
266    return false;
267}
268
269void resetLogLevel()
270{
271    static CachedHandle muslLogLevelHandle = NULL;
272    if (muslLogLevelHandle == NULL) {
273        muslLogLevelHandle = CachedParameterCreate(g_logLevelParam, "ERROR");
274    }
275    const char *value = CachedParameterGet(muslLogLevelHandle);
276    if (value != NULL) {
277        if (!strcmp(value, "DEBUG")) {
278            g_logLevel = LOG_DEBUG;
279        } else if (!strcmp(value, "INFO")) {
280            g_logLevel = LOG_INFO;
281        } else if (!strcmp(value, "WARN")) {
282            g_logLevel = LOG_WARN;
283        } else if (!strcmp(value, "ERROR")) {
284            g_logLevel = LOG_ERROR;
285        } else if (!strcmp(value, "FATAL")) {
286            g_logLevel = LOG_FATAL;
287        } else {
288            g_logLevel = LOG_ERROR;
289        }
290    } else {
291        g_logLevel = LOG_ERROR;
292    }
293}
294
295#endif
296
297void musl_log_reset()
298{
299#if (defined(OHOS_ENABLE_PARAMETER))
300    static CachedHandle musl_log_Handle = NULL;
301    if (musl_log_Handle == NULL) {
302        musl_log_Handle = CachedParameterCreate(param_name, "false");
303    }
304    musl_log_enable = get_bool_sysparam(musl_log_Handle);
305    resetLogLevel();
306#elif (defined(ENABLE_MUSL_LOG))
307    musl_log_enable = true;
308#endif
309}
310