1 /*
2 * Copyright (C) 2019 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 #define LOG_TAG "packagelistparser"
18
19 #include <packagelistparser/packagelistparser.h>
20
21 #include <errno.h>
22 #include <inttypes.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/limits.h>
27
28 #include <memory>
29
30 #include <log/log.h>
31
parse_gids(const char * path,size_t line_number,const char * gids,pkg_info * info)32 static bool parse_gids(const char* path, size_t line_number, const char* gids, pkg_info* info) {
33 // Nothing to do?
34 if (!gids || !strcmp(gids, "none")) return true;
35
36 // How much space do we need?
37 info->gids.cnt = 1;
38 for (const char* p = gids; *p; ++p) {
39 if (*p == ',') ++info->gids.cnt;
40 }
41
42 // Allocate the space.
43 info->gids.gids = new gid_t[info->gids.cnt];
44 if (!info->gids.gids) return false;
45
46 // And parse the individual gids.
47 size_t i = 0;
48 while (true) {
49 char* end;
50 unsigned long gid = strtoul(gids, &end, 10);
51 if (gid > GID_MAX) {
52 ALOGE("%s:%zu: gid %lu > GID_MAX", path, line_number, gid);
53 return false;
54 }
55
56 if (i >= info->gids.cnt) return false;
57 info->gids.gids[i++] = gid;
58
59 if (*end == '\0') return true;
60 if (*end != ',') return false;
61 gids = end + 1;
62 }
63 return true;
64 }
65
parse_line(const char * path,size_t line_number,const char * line,pkg_info * info)66 static bool parse_line(const char* path, size_t line_number, const char* line, pkg_info* info) {
67 int debuggable;
68 char* gid_list;
69 int profileable_from_shell = 0;
70 int fields =
71 sscanf(line, "%ms %u %d %ms %ms %ms %d %ld", &info->name, &info->uid,
72 &debuggable, &info->data_dir, &info->seinfo, &gid_list,
73 &profileable_from_shell, &info->version_code);
74
75 // Handle the more complicated gids field and free the temporary string.
76 bool gids_okay = parse_gids(path, line_number, gid_list, info);
77 free(gid_list);
78 if (!gids_okay) return false;
79
80 // Did we see enough fields to be getting on with?
81 // The final fields are optional (and not usually present).
82 if (fields < 6) {
83 ALOGE("%s:%zu: too few fields in line", path, line_number);
84 return false;
85 }
86
87 // Convert integers to bools.
88 info->debuggable = debuggable;
89 info->profileable_from_shell = profileable_from_shell;
90
91 return true;
92 }
93
packagelist_parse_file(const char * path,bool (* callback)(pkg_info *,void *),void * user_data)94 bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info*, void*), void* user_data) {
95 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path, "re"), &fclose);
96 if (!fp) {
97 ALOGE("couldn't open '%s': %s", path, strerror(errno));
98 return false;
99 }
100
101 size_t line_number = 0;
102 char* line = nullptr;
103 size_t allocated_length = 0;
104 while (getline(&line, &allocated_length, fp.get()) > 0) {
105 ++line_number;
106 std::unique_ptr<pkg_info, decltype(&packagelist_free)> info(
107 static_cast<pkg_info*>(calloc(1, sizeof(pkg_info))), &packagelist_free);
108 if (!info) {
109 ALOGE("%s:%zu: couldn't allocate pkg_info", path, line_number);
110 return false;
111 }
112
113 if (!parse_line(path, line_number, line, info.get())) return false;
114
115 if (!callback(info.release(), user_data)) break;
116 }
117 free(line);
118 return true;
119 }
120
packagelist_parse(bool (* callback)(pkg_info *,void *),void * user_data)121 bool packagelist_parse(bool (*callback)(pkg_info*, void*), void* user_data) {
122 return packagelist_parse_file("/data/system/packages.list", callback, user_data);
123 }
124
packagelist_free(pkg_info * info)125 void packagelist_free(pkg_info* info) {
126 if (!info) return;
127
128 free(info->name);
129 free(info->data_dir);
130 free(info->seinfo);
131 delete[] info->gids.gids;
132 free(info);
133 }
134