Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6dabc81a authored by Mark Salyzyn's avatar Mark Salyzyn
Browse files

liblogcat: add android_logcat_popen and android_logcat_system

Supply a wrapper to the logcat API that provides some analogous
functionality to popen and system libc calls with some bits of
KISS shell-like parsing for environment, quotes and error
redirection handling.

Test: gTest logcat-unit-tests
Bug: 35326290
Change-Id: I9494ce71267ad2b2bec7fcccfc7d4beddae9aea6
parent 1d6928b7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)

LOCAL_MODULE := liblogcat
LOCAL_SRC_FILES := logcat.cpp
LOCAL_SRC_FILES := logcat.cpp logcat_system.cpp
LOCAL_SHARED_LIBRARIES := $(logcatLibs)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDES_DIR := $(LOCAL_PATH)/include
+18 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#ifndef _LIBS_LOGCAT_H /* header boilerplate */
#define _LIBS_LOGCAT_H

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif
@@ -95,6 +97,22 @@ int android_logcat_run_command_thread_running(android_logcat_context ctx);
 */
int android_logcat_destroy(android_logcat_context* ctx);

/* derived helpers */

/*
 * In-process thread that acts like somewhat like libc-like system and popen
 * respectively.  Can not handle shell scripting, only pure calls to the
 * logcat operations. The android_logcat_system is a wrapper for the
 * create_android_logcat, android_logcat_run_command and android_logcat_destroy
 * API above.  The android_logcat_popen is a wrapper for the
 * android_logcat_run_command_thread API above.  The android_logcat_pclose is
 * a wrapper for a reasonable wait until output has subsided for command
 * completion, fclose on the FILE pointer and the android_logcat_destroy API.
 */
int android_logcat_system(const char* command);
FILE* android_logcat_popen(android_logcat_context* ctx, const char* command);
int android_logcat_pclose(android_logcat_context* ctx, FILE* output);

#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */

#ifdef __cplusplus
+148 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 <ctype.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <string>
#include <vector>

#include <log/logcat.h>

static std::string unquote(const char*& cp, const char*& delim) {
    if ((*cp == '\'') || (*cp == '"')) {
        // KISS: Simple quotes. Do not handle the case
        //       of concatenation like "blah"foo'bar'
        char quote = *cp++;
        delim = strchr(cp, quote);
        if (!delim) delim = cp + strlen(cp);
        std::string str(cp, delim);
        if (*delim) ++delim;
        return str;
    }
    delim = strpbrk(cp, " \t\f\r\n");
    if (!delim) delim = cp + strlen(cp);
    return std::string(cp, delim);
}

static bool __android_logcat_parse(const char* command,
                                   std::vector<std::string>& args,
                                   std::vector<std::string>& envs) {
    for (const char *delim, *cp = command; cp && *cp; cp = delim) {
        while (isspace(*cp)) ++cp;
        if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
            const char* env = cp;
            while (isalnum(*cp) || (*cp == '_')) ++cp;
            if (cp && (*cp == '=')) {
                std::string str(env, ++cp);
                str += unquote(cp, delim);
                envs.push_back(str);
                continue;
            }
            cp = env;
        }
        args.push_back(unquote(cp, delim));
        if ((args.size() == 1) && (args[0] != "logcat") &&
            (args[0] != "/system/bin/logcat")) {
            return false;
        }
    }
    return args.size() != 0;
}

FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
    *ctx = NULL;

    std::vector<std::string> args;
    std::vector<std::string> envs;
    if (!__android_logcat_parse(command, args, envs)) return NULL;

    std::vector<const char*> argv;
    for (auto& str : args) {
        argv.push_back(str.c_str());
    }
    argv.push_back(NULL);

    std::vector<const char*> envp;
    for (auto& str : envs) {
        envp.push_back(str.c_str());
    }
    envp.push_back(NULL);

    *ctx = create_android_logcat();
    if (!*ctx) return NULL;

    int fd = android_logcat_run_command_thread(
        *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
    argv.clear();
    args.clear();
    envp.clear();
    envs.clear();
    if (fd < 0) {
        android_logcat_destroy(ctx);
        return NULL;
    }

    FILE* retval = fdopen(fd, "reb");
    if (!retval) android_logcat_destroy(ctx);
    return retval;
}

int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
    if (*ctx) {
        static const useconds_t wait_sample = 20000;
        // Wait two seconds maximum
        for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
             android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
            usleep(wait_sample);
        }
    }

    if (output) fclose(output);
    return android_logcat_destroy(ctx);
}

int android_logcat_system(const char* command) {
    std::vector<std::string> args;
    std::vector<std::string> envs;
    if (!__android_logcat_parse(command, args, envs)) return -1;

    std::vector<const char*> argv;
    for (auto& str : args) {
        argv.push_back(str.c_str());
    }
    argv.push_back(NULL);

    std::vector<const char*> envp;
    for (auto& str : envs) {
        envp.push_back(str.c_str());
    }
    envp.push_back(NULL);

    android_logcat_context ctx = create_android_logcat();
    if (!ctx) return -1;
    /* Command return value */
    int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
                                            (char* const*)&argv[0],
                                            (char* const*)&envp[0]);
    /* destroy return value */
    int ret = android_logcat_destroy(&ctx);
    /* Paranoia merging any discrepancies between the two return values */
    if (!ret) ret = retval;
    return ret;
}
+3 −1
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ include $(BUILD_NATIVE_TEST)

test_src_files := \
    logcat_test.cpp \
    liblogcat_test.cpp \

# Build tests for the device (with .so). Run with:
#   adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
@@ -55,6 +56,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := $(test_module_prefix)unit-tests
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_SHARED_LIBRARIES := liblog libbase liblogcat
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include
LOCAL_SRC_FILES := $(test_src_files)
include $(BUILD_NATIVE_TEST)
+25 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 <log/logcat.h>

#define logcat_define(context) android_logcat_context context
#define logcat_popen(context, command) android_logcat_popen(&context, command)
#define logcat_pclose(context, fp) android_logcat_pclose(&context, fp)
#define logcat_system(command) android_logcat_system(command)
#define logcat liblogcat

#include "logcat_test.cpp"
Loading