Mark Salyzyn | 7100288 | 2016-03-08 16:18:26 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <errno.h> |
| 18 | #include <fcntl.h> |
| 19 | #include <pthread.h> |
| 20 | #if !defined(__MINGW32__) |
| 21 | #include <pwd.h> |
| 22 | #endif |
| 23 | #include <sched.h> |
| 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
| 26 | #include <sys/types.h> |
| 27 | #include <log/uio.h> |
| 28 | |
| 29 | #include <cutils/list.h> /* template, no library dependency */ |
| 30 | #include <log/log_frontend.h> |
| 31 | #include <private/android_filesystem_config.h> |
| 32 | #include <private/android_logger.h> |
| 33 | #include <system/thread_defs.h> |
| 34 | |
| 35 | #include "config_read.h" |
| 36 | #include "config_write.h" |
| 37 | #include "log_portability.h" |
| 38 | #include "logger.h" |
| 39 | |
| 40 | static const char baseServiceName[] = "android.logd"; |
| 41 | |
| 42 | static int writeToLocalInit(); |
| 43 | static int writeToLocalAvailable(log_id_t logId); |
| 44 | static void writeToLocalReset(); |
| 45 | static int writeToLocalWrite(log_id_t logId, struct timespec *ts, |
| 46 | struct iovec *vec, size_t nr); |
| 47 | |
| 48 | LIBLOG_HIDDEN struct android_log_transport_write localLoggerWrite = { |
| 49 | .node = { &localLoggerWrite.node, &localLoggerWrite.node }, |
| 50 | .context.private = NULL, |
| 51 | .name = "local", |
| 52 | .available = writeToLocalAvailable, |
| 53 | .open = writeToLocalInit, |
| 54 | .close = writeToLocalReset, |
| 55 | .write = writeToLocalWrite, |
| 56 | }; |
| 57 | |
| 58 | static int writeToLocalVersion(struct android_log_logger *logger, |
| 59 | struct android_log_transport_context *transp); |
| 60 | static int writeToLocalRead(struct android_log_logger_list *logger_list, |
| 61 | struct android_log_transport_context *transp, |
| 62 | struct log_msg *log_msg); |
| 63 | static int writeToLocalPoll(struct android_log_logger_list *logger_list, |
| 64 | struct android_log_transport_context *transp); |
| 65 | static void writeToLocalClose(struct android_log_logger_list *logger_list, |
| 66 | struct android_log_transport_context *transp); |
| 67 | static int writeToLocalClear(struct android_log_logger *logger, |
| 68 | struct android_log_transport_context *transp); |
| 69 | static ssize_t writeToLocalGetSize( |
| 70 | struct android_log_logger *logger, |
| 71 | struct android_log_transport_context *transp); |
| 72 | static ssize_t writeToLocalSetSize( |
| 73 | struct android_log_logger *logger, |
| 74 | struct android_log_transport_context *transp __unused, |
| 75 | size_t size); |
| 76 | static ssize_t writeToLocalGetReadbleSize( |
| 77 | struct android_log_logger *logger, |
| 78 | struct android_log_transport_context *transp); |
| 79 | |
| 80 | struct android_log_transport_read localLoggerRead = { |
| 81 | .node = { &localLoggerRead.node, &localLoggerRead.node }, |
| 82 | .name = "local", |
| 83 | .available = writeToLocalAvailable, |
| 84 | .version = writeToLocalVersion, |
| 85 | .read = writeToLocalRead, |
| 86 | .poll = writeToLocalPoll, |
| 87 | .close = writeToLocalClose, |
| 88 | .clear = writeToLocalClear, |
| 89 | .getSize = writeToLocalGetSize, |
| 90 | .setSize = writeToLocalSetSize, |
| 91 | .getReadableSize = writeToLocalGetReadbleSize, |
| 92 | .getPrune = NULL, |
| 93 | .setPrune = NULL, |
| 94 | .getStats = NULL, |
| 95 | }; |
| 96 | |
| 97 | struct LogBufferElement { |
| 98 | struct listnode node; |
| 99 | log_id_t logId; |
| 100 | pid_t tid; |
| 101 | log_time timestamp; |
| 102 | unsigned short len; |
| 103 | char msg[]; |
| 104 | }; |
| 105 | |
| 106 | static const size_t MAX_SIZE_DEFAULT = 32768; |
| 107 | |
| 108 | /* |
| 109 | * Number of log buffers we support with the following assumption: |
| 110 | * . . . |
| 111 | * LOG_ID_SECURITY = 5, // security logs go to the system logs only |
| 112 | * LOG_ID_KERNEL = 6, // place last, third-parties can not use it |
| 113 | * LOG_ID_MAX |
| 114 | * } log_id_t; |
| 115 | * |
| 116 | * Confirm the following should <log/log_id.h> be adjusted in the future. |
| 117 | */ |
| 118 | #define NUMBER_OF_LOG_BUFFERS ((LOG_ID_SECURITY == (LOG_ID_MAX - 2)) ? \ |
| 119 | LOG_ID_SECURITY : \ |
| 120 | LOG_ID_KERNEL) |
| 121 | #define BLOCK_LOG_BUFFERS(id) (((id) == LOG_ID_SECURITY) || \ |
| 122 | ((id) == LOG_ID_KERNEL)) |
| 123 | |
| 124 | static struct LogBuffer { |
| 125 | struct listnode head; |
| 126 | pthread_rwlock_t listLock; |
| 127 | char *serviceName; /* Also indicates ready by having a value */ |
| 128 | /* Order and proximity important for memset */ |
| 129 | size_t number[NUMBER_OF_LOG_BUFFERS]; /* clear memset */ |
| 130 | size_t size[NUMBER_OF_LOG_BUFFERS]; /* clear memset */ |
| 131 | size_t totalSize[NUMBER_OF_LOG_BUFFERS]; /* init memset */ |
| 132 | size_t maxSize[NUMBER_OF_LOG_BUFFERS]; /* init MAX_SIZE_DEFAULT */ |
| 133 | struct listnode *last[NUMBER_OF_LOG_BUFFERS]; /* init &head */ |
| 134 | } logbuf = { |
| 135 | .head = { &logbuf.head, &logbuf.head }, |
| 136 | .listLock = PTHREAD_RWLOCK_INITIALIZER, |
| 137 | }; |
| 138 | |
| 139 | static void LogBufferInit(struct LogBuffer *log) { |
| 140 | size_t i; |
| 141 | |
| 142 | pthread_rwlock_wrlock(&log->listLock); |
| 143 | list_init(&log->head); |
| 144 | memset(log->number, 0, |
| 145 | sizeof(log->number) + sizeof(log->size) + sizeof(log->totalSize)); |
| 146 | for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) { |
| 147 | log->maxSize[i] = MAX_SIZE_DEFAULT; |
| 148 | log->last[i] = &log->head; |
| 149 | } |
| 150 | #ifdef __BIONIC__ |
| 151 | asprintf(&log->serviceName, "%s@%d:%d", baseServiceName, |
| 152 | __android_log_uid(), getpid()); |
| 153 | #else |
| 154 | char buffer[sizeof(baseServiceName) + 1 + 5 + 1 + 5 + 8]; |
| 155 | snprintf(buffer, sizeof(buffer), "%s@%d:%d", baseServiceName, |
| 156 | __android_log_uid(), getpid()); |
| 157 | log->serviceName = strdup(buffer); |
| 158 | #endif |
| 159 | pthread_rwlock_unlock(&log->listLock); |
| 160 | } |
| 161 | |
| 162 | static void LogBufferClear(struct LogBuffer *log) { |
| 163 | size_t i; |
| 164 | struct listnode *node; |
| 165 | |
| 166 | pthread_rwlock_wrlock(&log->listLock); |
| 167 | memset(log->number, 0, sizeof(log->number) + sizeof(log->size)); |
| 168 | for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) { |
| 169 | log->last[i] = &log->head; |
| 170 | } |
| 171 | while ((node = list_head(&log->head)) != &log->head) { |
| 172 | struct LogBufferElement *element; |
| 173 | |
| 174 | element = node_to_item(node, struct LogBufferElement, node); |
| 175 | list_remove(node); |
| 176 | free(element); |
| 177 | } |
| 178 | pthread_rwlock_unlock(&log->listLock); |
| 179 | } |
| 180 | |
| 181 | static inline void LogBufferFree(struct LogBuffer *log) { |
| 182 | pthread_rwlock_wrlock(&log->listLock); |
| 183 | free(log->serviceName); |
| 184 | log->serviceName = NULL; |
| 185 | pthread_rwlock_unlock(&log->listLock); |
| 186 | LogBufferClear(log); |
| 187 | } |
| 188 | |
| 189 | static int LogBufferLog(struct LogBuffer *log, |
| 190 | struct LogBufferElement *element) { |
| 191 | log_id_t logId = element->logId; |
| 192 | |
| 193 | pthread_rwlock_wrlock(&log->listLock); |
| 194 | log->number[logId]++; |
| 195 | log->size[logId] += element->len; |
| 196 | log->totalSize[logId] += element->len; |
| 197 | /* prune entry(s) until enough space is available */ |
| 198 | if (log->last[logId] == &log->head) { |
| 199 | log->last[logId] = list_tail(&log->head); |
| 200 | } |
| 201 | while (log->size[logId] > log->maxSize[logId]) { |
| 202 | struct listnode *node = log->last[logId]; |
| 203 | struct LogBufferElement *e; |
| 204 | struct android_log_logger_list *logger_list; |
| 205 | |
| 206 | e = node_to_item(node, struct LogBufferElement, node); |
| 207 | log->number[logId]--; |
| 208 | log->size[logId] -= e->len; |
| 209 | logger_list_rdlock(); |
| 210 | logger_list_for_each(logger_list) { |
| 211 | struct android_log_transport_context *transp; |
| 212 | |
| 213 | transport_context_for_each(transp, logger_list) { |
| 214 | if ((transp->transport == &localLoggerRead) && |
| 215 | (transp->context.node == node)) { |
| 216 | if (node == &log->head) { |
| 217 | transp->context.node = &log->head; |
| 218 | } else { |
| 219 | transp->context.node = node->next; |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | logger_list_unlock(); |
| 225 | if (node != &log->head) { |
| 226 | log->last[logId] = node->prev; |
| 227 | } |
| 228 | list_remove(node); |
| 229 | free(e); |
| 230 | } |
| 231 | /* add entry to list */ |
| 232 | list_add_head(&log->head, &element->node); |
| 233 | /* ToDo: wake up all readers */ |
| 234 | pthread_rwlock_unlock(&log->listLock); |
| 235 | |
| 236 | return element->len; |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | * return zero if permitted to log directly to logd, |
| 241 | * return 1 if binder server started and |
| 242 | * return negative error number if failed to start binder server. |
| 243 | */ |
| 244 | static int writeToLocalInit() { |
| 245 | pthread_attr_t attr; |
| 246 | struct LogBuffer *log; |
| 247 | |
| 248 | if (writeToLocalAvailable(LOG_ID_MAIN) < 0) { |
| 249 | return -EPERM; |
| 250 | } |
| 251 | |
| 252 | log = &logbuf; |
| 253 | if (!log->serviceName) { |
| 254 | LogBufferInit(log); |
| 255 | } |
| 256 | |
| 257 | if (!log->serviceName) { |
| 258 | LogBufferFree(log); |
| 259 | return -ENOMEM; |
| 260 | } |
| 261 | |
| 262 | return EPERM; /* successful local-only logging */ |
| 263 | } |
| 264 | |
| 265 | static void writeToLocalReset() { |
| 266 | LogBufferFree(&logbuf); |
| 267 | } |
| 268 | |
| 269 | static int writeToLocalAvailable(log_id_t logId) { |
| 270 | #if !defined(__MINGW32__) |
| 271 | uid_t uid; |
| 272 | #endif |
| 273 | |
| 274 | if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) { |
| 275 | return -EINVAL; |
| 276 | } |
| 277 | |
| 278 | /* Android hard coded permitted, system goes to logd */ |
| 279 | #if !defined(__MINGW32__) |
| 280 | if (__android_log_frontend == LOGGER_DEFAULT) { |
| 281 | uid = __android_log_uid(); |
| 282 | if ((uid < AID_APP) && (getpwuid(uid) != NULL)) { |
| 283 | return -EPERM; |
| 284 | } |
| 285 | } |
| 286 | #endif |
| 287 | |
| 288 | /* ToDo: Ask package manager for LOGD permissions */ |
| 289 | /* Assume we do _not_ have permissions to go to LOGD, so must go local */ |
| 290 | return 0; |
| 291 | } |
| 292 | |
| 293 | static int writeToLocalWrite(log_id_t logId, struct timespec *ts, |
| 294 | struct iovec *vec, size_t nr) { |
| 295 | size_t len, i; |
| 296 | struct LogBufferElement *element; |
| 297 | |
| 298 | if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) { |
| 299 | return -EINVAL; |
| 300 | } |
| 301 | |
| 302 | len = 0; |
| 303 | for (i = 0; i < nr; ++i) { |
| 304 | len += vec[i].iov_len; |
| 305 | } |
| 306 | |
| 307 | if (len > LOGGER_ENTRY_MAX_PAYLOAD) { |
| 308 | len = LOGGER_ENTRY_MAX_PAYLOAD; |
| 309 | } |
| 310 | element = (struct LogBufferElement *)calloc(1, |
| 311 | sizeof(struct LogBufferElement) + len + 1); |
| 312 | if (!element) { |
| 313 | return errno ? -errno : -ENOMEM; |
| 314 | } |
| 315 | element->timestamp.tv_sec = ts->tv_sec; |
| 316 | element->timestamp.tv_nsec = ts->tv_nsec; |
| 317 | #ifdef __BIONIC__ |
| 318 | element->tid = gettid(); |
| 319 | #else |
| 320 | element->tid = getpid(); |
| 321 | #endif |
| 322 | element->logId = logId; |
| 323 | element->len = len; |
| 324 | |
| 325 | char *cp = element->msg; |
| 326 | for (i = 0; i < nr; ++i) { |
| 327 | size_t iov_len = vec[i].iov_len; |
| 328 | if (iov_len > len) { |
| 329 | iov_len = len; |
| 330 | } |
| 331 | memcpy(cp, vec[i].iov_base, iov_len); |
| 332 | len -= iov_len; |
| 333 | if (len == 0) { |
| 334 | break; |
| 335 | } |
| 336 | cp += iov_len; |
| 337 | } |
| 338 | |
| 339 | return LogBufferLog(&logbuf, element); |
| 340 | } |
| 341 | |
| 342 | static int writeToLocalVersion( |
| 343 | struct android_log_logger *logger __unused, |
| 344 | struct android_log_transport_context *transp __unused) { |
| 345 | return 3; |
| 346 | } |
| 347 | |
| 348 | /* within reader lock, serviceName already validated */ |
| 349 | static struct listnode *writeToLocalNode( |
| 350 | struct android_log_logger_list *logger_list, |
| 351 | struct android_log_transport_context *transp) { |
| 352 | struct listnode *node; |
| 353 | unsigned logMask; |
| 354 | unsigned int tail; |
| 355 | |
| 356 | node = transp->context.node; |
| 357 | if (node) { |
| 358 | return node; |
| 359 | } |
| 360 | |
| 361 | if (!logger_list->tail) { |
| 362 | return transp->context.node = &logbuf.head; |
| 363 | } |
| 364 | |
| 365 | logMask = transp->logMask; |
| 366 | tail = logger_list->tail; |
| 367 | |
| 368 | for (node = list_head(&logbuf.head); node != &logbuf.head; node = node->next) { |
| 369 | struct LogBufferElement *element; |
| 370 | log_id_t logId; |
| 371 | |
| 372 | element = node_to_item(node, struct LogBufferElement, node); |
| 373 | logId = element->logId; |
| 374 | |
| 375 | if ((logMask & (1 << logId)) && !--tail) { |
| 376 | node = node->next; |
| 377 | break; |
| 378 | } |
| 379 | } |
| 380 | return transp->context.node = node; |
| 381 | } |
| 382 | |
| 383 | static int writeToLocalRead( |
| 384 | struct android_log_logger_list *logger_list, |
| 385 | struct android_log_transport_context *transp, |
| 386 | struct log_msg *log_msg) { |
| 387 | int ret; |
| 388 | struct listnode *node; |
| 389 | unsigned logMask; |
| 390 | |
| 391 | pthread_rwlock_rdlock(&logbuf.listLock); |
| 392 | if (!logbuf.serviceName) { |
| 393 | pthread_rwlock_unlock(&logbuf.listLock); |
| 394 | return (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0; |
| 395 | } |
| 396 | |
| 397 | logMask = transp->logMask; |
| 398 | |
| 399 | node = writeToLocalNode(logger_list, transp); |
| 400 | |
| 401 | ret = 0; |
| 402 | |
| 403 | while (node != list_head(&logbuf.head)) { |
| 404 | struct LogBufferElement *element; |
| 405 | log_id_t logId; |
| 406 | |
| 407 | node = node->prev; |
| 408 | element = node_to_item(node, struct LogBufferElement, node); |
| 409 | logId = element->logId; |
| 410 | |
| 411 | if (logMask & (1 << logId)) { |
| 412 | ret = log_msg->entry_v3.len = element->len; |
| 413 | log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3); |
| 414 | log_msg->entry_v3.pid = getpid(); |
| 415 | log_msg->entry_v3.tid = element->tid; |
| 416 | log_msg->entry_v3.sec = element->timestamp.tv_sec; |
| 417 | log_msg->entry_v3.nsec = element->timestamp.tv_nsec; |
| 418 | log_msg->entry_v3.lid = logId; |
| 419 | memcpy(log_msg->entry_v3.msg, element->msg, ret); |
| 420 | ret += log_msg->entry_v3.hdr_size; |
| 421 | break; |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | transp->context.node = node; |
| 426 | |
| 427 | /* ToDo: if blocking, and no entry, put reader to sleep */ |
| 428 | pthread_rwlock_unlock(&logbuf.listLock); |
| 429 | return ret; |
| 430 | } |
| 431 | |
| 432 | static int writeToLocalPoll( |
| 433 | struct android_log_logger_list *logger_list, |
| 434 | struct android_log_transport_context *transp) { |
| 435 | int ret = (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0; |
| 436 | |
| 437 | pthread_rwlock_rdlock(&logbuf.listLock); |
| 438 | |
| 439 | if (logbuf.serviceName) { |
| 440 | unsigned logMask = transp->logMask; |
| 441 | struct listnode *node = writeToLocalNode(logger_list, transp); |
| 442 | |
| 443 | ret = (node != list_head(&logbuf.head)); |
| 444 | if (ret) { |
| 445 | do { |
| 446 | ret = !!(logMask & (1 << (node_to_item(node->prev, |
| 447 | struct LogBufferElement, |
| 448 | node))->logId)); |
| 449 | } while (!ret && ((node = node->prev) != list_head(&logbuf.head))); |
| 450 | } |
| 451 | |
| 452 | transp->context.node = node; |
| 453 | } |
| 454 | |
| 455 | pthread_rwlock_unlock(&logbuf.listLock); |
| 456 | |
| 457 | return ret; |
| 458 | } |
| 459 | |
| 460 | static void writeToLocalClose( |
| 461 | struct android_log_logger_list *logger_list __unused, |
| 462 | struct android_log_transport_context *transp) { |
| 463 | pthread_rwlock_wrlock(&logbuf.listLock); |
| 464 | transp->context.node = list_head(&logbuf.head); |
| 465 | pthread_rwlock_unlock(&logbuf.listLock); |
| 466 | } |
| 467 | |
| 468 | static int writeToLocalClear( |
| 469 | struct android_log_logger *logger, |
| 470 | struct android_log_transport_context *unused __unused) { |
| 471 | log_id_t logId = logger->logId; |
| 472 | struct listnode *node, *n; |
| 473 | |
| 474 | if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) { |
| 475 | return -EINVAL; |
| 476 | } |
| 477 | |
| 478 | pthread_rwlock_wrlock(&logbuf.listLock); |
| 479 | logbuf.number[logId] = 0; |
| 480 | logbuf.last[logId] = &logbuf.head; |
| 481 | list_for_each_safe(node, n, &logbuf.head) { |
| 482 | struct LogBufferElement *element; |
| 483 | element = node_to_item(node, struct LogBufferElement, node); |
| 484 | |
| 485 | if (logId == element->logId) { |
| 486 | struct android_log_logger_list *logger_list; |
| 487 | |
| 488 | logger_list_rdlock(); |
| 489 | logger_list_for_each(logger_list) { |
| 490 | struct android_log_transport_context *transp; |
| 491 | |
| 492 | transport_context_for_each(transp, logger_list) { |
| 493 | if ((transp->transport == &localLoggerRead) && |
| 494 | (transp->context.node == node)) { |
| 495 | transp->context.node = node->next; |
| 496 | } |
| 497 | } |
| 498 | } |
| 499 | logger_list_unlock(); |
| 500 | list_remove(node); |
| 501 | free(element); |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | pthread_rwlock_unlock(&logbuf.listLock); |
| 506 | |
| 507 | return 0; |
| 508 | } |
| 509 | |
| 510 | static ssize_t writeToLocalGetSize( |
| 511 | struct android_log_logger *logger, |
| 512 | struct android_log_transport_context *transp __unused) { |
| 513 | ssize_t ret = -EINVAL; |
| 514 | log_id_t logId = logger->logId; |
| 515 | |
| 516 | if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) { |
| 517 | pthread_rwlock_rdlock(&logbuf.listLock); |
| 518 | ret = logbuf.maxSize[logId]; |
| 519 | pthread_rwlock_unlock(&logbuf.listLock); |
| 520 | } |
| 521 | |
| 522 | return ret; |
| 523 | } |
| 524 | |
| 525 | static ssize_t writeToLocalSetSize( |
| 526 | struct android_log_logger *logger, |
| 527 | struct android_log_transport_context *transp __unused, |
| 528 | size_t size) { |
| 529 | ssize_t ret = -EINVAL; |
| 530 | |
| 531 | if ((size > LOGGER_ENTRY_MAX_LEN) || (size < (4 * 1024 * 1024))) { |
| 532 | log_id_t logId = logger->logId; |
| 533 | if ((logId < NUMBER_OF_LOG_BUFFERS) || !BLOCK_LOG_BUFFERS(logId)) { |
| 534 | pthread_rwlock_wrlock(&logbuf.listLock); |
| 535 | ret = logbuf.maxSize[logId] = size; |
| 536 | pthread_rwlock_unlock(&logbuf.listLock); |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | return ret; |
| 541 | } |
| 542 | |
| 543 | static ssize_t writeToLocalGetReadbleSize( |
| 544 | struct android_log_logger *logger, |
| 545 | struct android_log_transport_context *transp __unused) { |
| 546 | ssize_t ret = -EINVAL; |
| 547 | log_id_t logId = logger->logId; |
| 548 | |
| 549 | if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) { |
| 550 | pthread_rwlock_rdlock(&logbuf.listLock); |
| 551 | ret = logbuf.serviceName ? (ssize_t)logbuf.size[logId] : -EBADF; |
| 552 | pthread_rwlock_unlock(&logbuf.listLock); |
| 553 | } |
| 554 | |
| 555 | return ret; |
| 556 | } |