| /* |
| * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| * Not a Contribution. |
| * |
| * Copyright (C) 2007-2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| #include <assert.h> |
| #include <cutils/properties.h> |
| #include <utils/String8.h> |
| #include <android_runtime/Log.h> |
| #include <utils/Log.h> |
| #ifdef __BIONIC__ |
| #include <android/set_abort_message.h> |
| #endif |
| #include <utils/Log.h> |
| |
| |
| #include "jni.h" |
| #include <nativehelper/JNIHelp.h> |
| #include "utils/misc.h" |
| #include "android_runtime/AndroidRuntime.h" |
| |
| #define LOG_BUF_SIZE 1024 |
| #define SEEMP_SOCK_NAME "/dev/socket/seempdw" |
| #define ZYGOTE_PARENT_PID 1 |
| #ifndef __unused |
| #define __unused __attribute__((__unused__)) |
| #endif |
| |
| static int __write_to_log_init(struct iovec *vec, size_t nr); |
| static int (*write_to_log)(struct iovec *vec, size_t nr) = __write_to_log_init; |
| static int logd_fd = -1; |
| |
| /* give up, resources too limited */ |
| static int __write_to_log_null(struct iovec *vec __unused, |
| size_t nr __unused) |
| { |
| return -1; |
| } |
| |
| /* log_init_lock assumed */ |
| static int __write_to_log_initialize() |
| { |
| int i, ret = 0; |
| if (logd_fd >= 0) { |
| i = logd_fd; |
| logd_fd = -1; |
| close(i); |
| } |
| |
| i = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); |
| if (i < 0) { |
| ret = -errno; |
| write_to_log = __write_to_log_null; |
| } else if (fcntl(i, F_SETFL, O_NONBLOCK) < 0) { |
| ret = -errno; |
| close(i); |
| i = -1; |
| write_to_log = __write_to_log_null; |
| } else { |
| struct sockaddr_un un; |
| memset(&un, 0, sizeof(struct sockaddr_un)); |
| un.sun_family = AF_UNIX; |
| strlcpy(un.sun_path, SEEMP_SOCK_NAME, sizeof(un.sun_path)); |
| if (connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un)) < 0) { |
| ret = -errno; |
| close(i); |
| i = -1; |
| } |
| } |
| logd_fd = i; |
| return ret; |
| } |
| |
| static int __write_to_log_socket(struct iovec *vec, size_t nr) |
| { |
| ssize_t ret; |
| if (logd_fd < 0) { |
| return -EBADF; |
| } |
| |
| /* |
| * The write below could be lost, but will never block. |
| * |
| * ENOTCONN occurs if logd dies. |
| * EAGAIN occurs if logd is overloaded. |
| */ |
| ret = writev(logd_fd, vec, nr); |
| if (ret < 0) { |
| ret = -errno; |
| if (ret == -ENOTCONN) { |
| ret = __write_to_log_initialize(); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| ret = writev(logd_fd, vec, nr); |
| if (ret < 0) { |
| ret = -errno; |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int __write_to_log_init(struct iovec *vec, size_t nr) |
| { |
| if (write_to_log == __write_to_log_init) { |
| |
| if (getppid() == ZYGOTE_PARENT_PID) { |
| return 0; |
| } |
| |
| int ret; |
| |
| ret = __write_to_log_initialize(); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| write_to_log = __write_to_log_socket; |
| } |
| return write_to_log(vec, nr); |
| } |
| |
| int __android_seemp_socket_write(int len, const char *msg) |
| { |
| struct iovec vec; |
| vec.iov_base = (void *) msg; |
| vec.iov_len = len; |
| |
| return write_to_log(&vec, 1); |
| } |
| |
| namespace android { |
| |
| /* |
| * In class android.util.Log: |
| * public static native int println_native(int buffer, int priority, String tag, String msg) |
| */ |
| static jint android_util_SeempLog_println_native(JNIEnv* env, jobject clazz, |
| jint api, jstring msgObj) |
| { |
| if (msgObj == NULL) { |
| jniThrowNullPointerException(env, "seemp_println needs a message"); |
| return -1; |
| } |
| |
| int apiId = (int)api; |
| int apiIdLen = sizeof(apiId); |
| int utf8MsgLen = env->GetStringUTFLength(msgObj); |
| int len = apiIdLen + 1 + utf8MsgLen + 1; |
| char *msg = (char*)malloc(len); |
| if ( NULL == msg ) |
| { |
| return -1; |
| } |
| char *params = msg + apiIdLen + 1; // api_id + encoding byte + params |
| |
| *((int*)msg) = apiId; // copy api id |
| // // skip encoding byte |
| env->GetStringUTFRegion(msgObj, 0, env->GetStringLength(msgObj), params); // copy message |
| msg[len - 1] = 0; // copy terminating zero |
| |
| int res = __android_seemp_socket_write(len, msg); // send message |
| |
| free(msg); |
| |
| return res; |
| } |
| |
| /* |
| * JNI registration. |
| */ |
| static JNINativeMethod gMethods[] = { |
| /* name, signature, funcPtr */ |
| { "seemp_println_native", "(ILjava/lang/String;)I", |
| (void*) android_util_SeempLog_println_native }, |
| }; |
| |
| int register_android_util_SeempLog(JNIEnv* env) |
| { |
| jclass clazz = env->FindClass("android/util/SeempLog"); |
| if (clazz == NULL) { |
| return -1; |
| } |
| |
| return AndroidRuntime::registerNativeMethods(env, "android/util/SeempLog", gMethods, |
| NELEM(gMethods)); |
| } |
| |
| }; // namespace android |