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 <fcntl.h>
19 #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 
unquote(const char * & cp,const char * & delim)28 static 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 
__android_logcat_parse(const char * command,std::vector<std::string> & args,std::vector<std::string> & envs)44 static 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 
android_logcat_popen(android_logcat_context * ctx,const char * command)69 FILE* 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 
102     int duped = dup(fd);
103     FILE* retval = fdopen(duped, "reb");
104     if (!retval) {
105         close(duped);
106         android_logcat_destroy(ctx);
107     }
108     return retval;
109 }
110 
android_logcat_pclose(android_logcat_context * ctx,FILE * output)111 int 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 
android_logcat_system(const char * command)125 int 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 }
154