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 
51 const int SOCKET_TYPE = SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC;
52 const struct sockaddr_un SOCKET_ADDR = {AF_UNIX, SOCKET_FILE_DIR INPUT_SOCKET_NAME};
53 
54 static bool musl_log_enable = false;
55 
56 #ifdef OHOS_ENABLE_PARAMETER
57 static const char *param_name = "musl.log.enable";
58 static const char *g_logLevelParam = "musl.log.level";
59 #endif
60 static int g_logLevel = LOG_ERROR;
61 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
62 static volatile int g_socketFd = INVALID_SOCKET;
63 
64 extern int __close(int fd);
65 
66 // only generate a new socketFd
GenerateHilogSocketFdnull67 static 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 
84 HILOG_LOCAL_API
CASHilogGlobalSocketFd(int socketFd)85 int 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 
99 HILOG_LOCAL_API
CheckHilogValidnull100 bool 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 */
116 HILOG_LOCAL_API
RefreshHiLogSocketFdnull117 void 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 
InitHilogSocketFdnull133 void 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 
SendMessage(HilogMsg *header, const char *tag, uint16_t tagLen, const char *fmt, uint16_t fmtLen)145 static 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 
190 HILOG_LOCAL_API
HiLogAdapterPrintArgs( const LogType type, const LogLevel level, const unsigned int domain, const char *tag, const char *fmt, va_list ap)191 int 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 
212 HILOG_LOCAL_API
HiLogAdapterPrint(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, ...)213 int 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 
227 HILOG_LOCAL_API
HiLogAdapterVaList(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, va_list ap)228 int 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 
is_musl_log_enablenull236 bool is_musl_log_enable()
237 {
238     if (getpid() == 1) {
239         return false;
240     }
241     return musl_log_enable;
242 }
243 
HiLogAdapterIsLoggable(unsigned int domain, const char *tag, LogLevel level)244 bool 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
get_bool_sysparam(CachedHandle cachedhandle)258 bool 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 
resetLogLevelnull269 void 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 
musl_log_resetnull297 void 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