blob: 6dfd110c778fad5104a02559d64fb9c6710b33bf [file] [log] [blame]
Mark Salyzyn6dabc812017-02-10 13:09:07 -08001/*
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 <ctype.h>
Josh Gao03d055d2017-10-18 15:42:00 -070018#include <fcntl.h>
Mark Salyzyn6dabc812017-02-10 13:09:07 -080019#include <stdio.h>
20#include <string.h>
21#include <unistd.h>
22
23#include <string>
24#include <vector>
25
26#include <log/logcat.h>
27
28static std::string unquote(const char*& cp, const char*& delim) {
29 if ((*cp == '\'') || (*cp == '"')) {
30 // KISS: Simple quotes. Do not handle the case
31 // of concatenation like "blah"foo'bar'
32 char quote = *cp++;
33 delim = strchr(cp, quote);
34 if (!delim) delim = cp + strlen(cp);
35 std::string str(cp, delim);
36 if (*delim) ++delim;
37 return str;
38 }
39 delim = strpbrk(cp, " \t\f\r\n");
40 if (!delim) delim = cp + strlen(cp);
41 return std::string(cp, delim);
42}
43
44static bool __android_logcat_parse(const char* command,
45 std::vector<std::string>& args,
46 std::vector<std::string>& envs) {
47 for (const char *delim, *cp = command; cp && *cp; cp = delim) {
48 while (isspace(*cp)) ++cp;
49 if ((args.size() == 0) && (*cp != '=') && !isdigit(*cp)) {
50 const char* env = cp;
51 while (isalnum(*cp) || (*cp == '_')) ++cp;
52 if (cp && (*cp == '=')) {
53 std::string str(env, ++cp);
54 str += unquote(cp, delim);
55 envs.push_back(str);
56 continue;
57 }
58 cp = env;
59 }
60 args.push_back(unquote(cp, delim));
61 if ((args.size() == 1) && (args[0] != "logcat") &&
62 (args[0] != "/system/bin/logcat")) {
63 return false;
64 }
65 }
66 return args.size() != 0;
67}
68
69FILE* android_logcat_popen(android_logcat_context* ctx, const char* command) {
70 *ctx = NULL;
71
72 std::vector<std::string> args;
73 std::vector<std::string> envs;
74 if (!__android_logcat_parse(command, args, envs)) return NULL;
75
76 std::vector<const char*> argv;
77 for (auto& str : args) {
78 argv.push_back(str.c_str());
79 }
80 argv.push_back(NULL);
81
82 std::vector<const char*> envp;
83 for (auto& str : envs) {
84 envp.push_back(str.c_str());
85 }
86 envp.push_back(NULL);
87
88 *ctx = create_android_logcat();
89 if (!*ctx) return NULL;
90
91 int fd = android_logcat_run_command_thread(
92 *ctx, argv.size() - 1, (char* const*)&argv[0], (char* const*)&envp[0]);
93 argv.clear();
94 args.clear();
95 envp.clear();
96 envs.clear();
97 if (fd < 0) {
98 android_logcat_destroy(ctx);
99 return NULL;
100 }
101
Josh Gao03d055d2017-10-18 15:42:00 -0700102 int duped = dup(fd);
103 FILE* retval = fdopen(duped, "reb");
104 if (!retval) {
105 close(duped);
106 android_logcat_destroy(ctx);
107 }
Mark Salyzyn6dabc812017-02-10 13:09:07 -0800108 return retval;
109}
110
111int android_logcat_pclose(android_logcat_context* ctx, FILE* output) {
112 if (*ctx) {
113 static const useconds_t wait_sample = 20000;
114 // Wait two seconds maximum
115 for (size_t retry = ((2 * 1000000) + wait_sample - 1) / wait_sample;
116 android_logcat_run_command_thread_running(*ctx) && retry; --retry) {
117 usleep(wait_sample);
118 }
119 }
120
121 if (output) fclose(output);
122 return android_logcat_destroy(ctx);
123}
124
125int android_logcat_system(const char* command) {
126 std::vector<std::string> args;
127 std::vector<std::string> envs;
128 if (!__android_logcat_parse(command, args, envs)) return -1;
129
130 std::vector<const char*> argv;
131 for (auto& str : args) {
132 argv.push_back(str.c_str());
133 }
134 argv.push_back(NULL);
135
136 std::vector<const char*> envp;
137 for (auto& str : envs) {
138 envp.push_back(str.c_str());
139 }
140 envp.push_back(NULL);
141
142 android_logcat_context ctx = create_android_logcat();
143 if (!ctx) return -1;
144 /* Command return value */
145 int retval = android_logcat_run_command(ctx, -1, -1, argv.size() - 1,
146 (char* const*)&argv[0],
147 (char* const*)&envp[0]);
148 /* destroy return value */
149 int ret = android_logcat_destroy(&ctx);
150 /* Paranoia merging any discrepancies between the two return values */
151 if (!ret) ret = retval;
152 return ret;
153}