1 /*
2 * Copyright 2015, Intel Corporation
3 * Copyright (C) 2015 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * Written by William Roberts <william.c.roberts@intel.com>
18 *
19 */
20
21 #define LOG_TAG "packagelistparser"
22
23 #include <errno.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/limits.h>
29
30 #include <log/log.h>
31 #include <packagelistparser/packagelistparser.h>
32
33 #define CLOGE(fmt, ...) \
34 do {\
35 IF_ALOGE() {\
36 ALOGE(fmt, ##__VA_ARGS__);\
37 }\
38 } while(0)
39
get_gid_cnt(const char * gids)40 static size_t get_gid_cnt(const char *gids)
41 {
42 size_t cnt;
43
44 if (*gids == '\0') {
45 return 0;
46 }
47
48 if (!strcmp(gids, "none")) {
49 return 0;
50 }
51
52 for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
53 ;
54
55 return cnt;
56 }
57
parse_gids(char * gids,gid_t * gid_list,size_t * cnt)58 static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
59 {
60 gid_t gid;
61 char* token;
62 char *endptr;
63 size_t cmp = 0;
64
65 while ((token = strsep(&gids, ",\r\n"))) {
66
67 if (cmp > *cnt) {
68 return false;
69 }
70
71 gid = strtoul(token, &endptr, 10);
72 if (*endptr != '\0') {
73 return false;
74 }
75
76 /*
77 * if unsigned long is greater than size of gid_t,
78 * prevent a truncation based roll-over
79 */
80 if (gid > GID_MAX) {
81 CLOGE("A gid in field \"gid list\" greater than GID_MAX");
82 return false;
83 }
84
85 gid_list[cmp++] = gid;
86 }
87 return true;
88 }
89
packagelist_parse(pfn_on_package callback,void * userdata)90 extern bool packagelist_parse(pfn_on_package callback, void *userdata)
91 {
92
93 FILE *fp;
94 char *cur;
95 char *next;
96 char *endptr;
97 unsigned long tmp;
98 ssize_t bytesread;
99
100 bool rc = false;
101 char *buf = NULL;
102 size_t buflen = 0;
103 unsigned long lineno = 1;
104 const char *errmsg = NULL;
105 struct pkg_info *pkg_info = NULL;
106
107 fp = fopen(PACKAGES_LIST_FILE, "re");
108 if (!fp) {
109 CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
110 strerror(errno));
111 return false;
112 }
113
114 while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
115
116 pkg_info = calloc(1, sizeof(*pkg_info));
117 if (!pkg_info) {
118 goto err;
119 }
120
121 next = buf;
122
123 cur = strsep(&next, " \t\r\n");
124 if (!cur) {
125 errmsg = "Could not get next token for \"package name\"";
126 goto err;
127 }
128
129 pkg_info->name = strdup(cur);
130 if (!pkg_info->name) {
131 goto err;
132 }
133
134 cur = strsep(&next, " \t\r\n");
135 if (!cur) {
136 errmsg = "Could not get next token for field \"uid\"";
137 goto err;
138 }
139
140 tmp = strtoul(cur, &endptr, 10);
141 if (*endptr != '\0') {
142 errmsg = "Could not convert field \"uid\" to integer value";
143 goto err;
144 }
145
146 /*
147 * if unsigned long is greater than size of uid_t,
148 * prevent a truncation based roll-over
149 */
150 if (tmp > UID_MAX) {
151 errmsg = "Field \"uid\" greater than UID_MAX";
152 goto err;
153 }
154
155 pkg_info->uid = (uid_t) tmp;
156
157 cur = strsep(&next, " \t\r\n");
158 if (!cur) {
159 errmsg = "Could not get next token for field \"debuggable\"";
160 goto err;
161 }
162
163 tmp = strtoul(cur, &endptr, 10);
164 if (*endptr != '\0') {
165 errmsg = "Could not convert field \"debuggable\" to integer value";
166 goto err;
167 }
168
169 /* should be a valid boolean of 1 or 0 */
170 if (!(tmp == 0 || tmp == 1)) {
171 errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
172 goto err;
173 }
174
175 pkg_info->debuggable = (bool) tmp;
176
177 cur = strsep(&next, " \t\r\n");
178 if (!cur) {
179 errmsg = "Could not get next token for field \"data dir\"";
180 goto err;
181 }
182
183 pkg_info->data_dir = strdup(cur);
184 if (!pkg_info->data_dir) {
185 goto err;
186 }
187
188 cur = strsep(&next, " \t\r\n");
189 if (!cur) {
190 errmsg = "Could not get next token for field \"seinfo\"";
191 goto err;
192 }
193
194 pkg_info->seinfo = strdup(cur);
195 if (!pkg_info->seinfo) {
196 goto err;
197 }
198
199 cur = strsep(&next, " \t\r\n");
200 if (!cur) {
201 errmsg = "Could not get next token for field \"gid(s)\"";
202 goto err;
203 }
204
205 /*
206 * Parse the gid list, could be in the form of none, single gid or list:
207 * none
208 * gid
209 * gid, gid ...
210 */
211 pkg_info->gids.cnt = get_gid_cnt(cur);
212 if (pkg_info->gids.cnt > 0) {
213
214 pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
215 if (!pkg_info->gids.gids) {
216 goto err;
217 }
218
219 rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
220 if (!rc) {
221 errmsg = "Could not parse field \"gid list\"";
222 goto err;
223 }
224 }
225
226 cur = strsep(&next, " \t\r\n");
227 if (cur) {
228 tmp = strtoul(cur, &endptr, 10);
229 if (*endptr != '\0') {
230 errmsg = "Could not convert field \"profileable_from_shell\" to integer value";
231 goto err;
232 }
233
234 /* should be a valid boolean of 1 or 0 */
235 if (!(tmp == 0 || tmp == 1)) {
236 errmsg = "Field \"profileable_from_shell\" is not 0 or 1 boolean value";
237 goto err;
238 }
239
240 pkg_info->profileable_from_shell = (bool)tmp;
241 }
242 cur = strsep(&next, " \t\r\n");
243 if (cur) {
244 tmp = strtoul(cur, &endptr, 10);
245 if (*endptr != '\0') {
246 errmsg = "Could not convert field \"versionCode\" to integer value";
247 goto err;
248 }
249 pkg_info->version_code = tmp;
250 }
251
252 rc = callback(pkg_info, userdata);
253 if (rc == false) {
254 /*
255 * We do not log this as this can be intentional from
256 * callback to abort processing. We go to out to not
257 * free the pkg_info
258 */
259 rc = true;
260 goto out;
261 }
262 lineno++;
263 }
264
265 rc = true;
266
267 out:
268 free(buf);
269 fclose(fp);
270 return rc;
271
272 err:
273 if (errmsg) {
274 CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
275 PACKAGES_LIST_FILE, lineno, errmsg);
276 }
277 rc = false;
278 packagelist_free(pkg_info);
279 goto out;
280 }
281
packagelist_free(pkg_info * info)282 void packagelist_free(pkg_info *info)
283 {
284 if (info) {
285 free(info->name);
286 free(info->data_dir);
287 free(info->seinfo);
288 free(info->gids.gids);
289 free(info);
290 }
291 }
292