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