blob: 6315a7acf98654578e1ff485aa7bed4b6fbd62a9 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
2 * Copyright (C) 2008 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 <stdarg.h>
18#include <stdlib.h>
19#include <stdio.h>
20#include <string.h>
21#include <fcntl.h>
22#include <ctype.h>
23#include <errno.h>
Colin Cross504bc512010-04-13 19:35:09 -070024#include <time.h>
Nick Kralevichae76f6d2013-07-11 15:38:26 -070025#include <ftw.h>
William Roberts3792e6c2016-04-06 19:18:50 -070026#include <pwd.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080027
Stephen Smalleye46f9d52012-01-13 08:48:47 -050028#include <selinux/label.h>
Stephen Smalleydbd37f22014-01-28 10:34:09 -050029#include <selinux/android.h>
Stephen Smalleye46f9d52012-01-13 08:48:47 -050030
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080031#include <sys/stat.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/un.h>
35
Elliott Hughes4f713192015-12-04 22:00:26 -080036#include <android-base/file.h>
Elliott Hughesf86b5a62016-06-24 15:12:21 -070037#include <android-base/logging.h>
Elliott Hughes4f713192015-12-04 22:00:26 -080038#include <android-base/strings.h>
Dan Albertc007bc32015-03-16 10:08:46 -070039
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080040/* for ANDROID_SOCKET_* */
41#include <cutils/sockets.h>
Elliott Hughes4f713192015-12-04 22:00:26 -080042#include <android-base/stringprintf.h>
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080043
Stephen Smalleye46f9d52012-01-13 08:48:47 -050044#include "init.h"
Colin Crossed8a7d82010-04-19 17:05:34 -070045#include "log.h"
Tom Cherryb7349902015-08-26 11:43:36 -070046#include "property_service.h"
Colin Crossf83d0b92010-04-21 12:04:20 -070047#include "util.h"
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080048
Nick Kralevichd2104df2015-06-18 17:46:54 -070049static unsigned int do_decode_uid(const char *s)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080050{
51 unsigned int v;
52
53 if (!s || *s == '\0')
Nick Kralevichd2104df2015-06-18 17:46:54 -070054 return UINT_MAX;
William Roberts3792e6c2016-04-06 19:18:50 -070055
56 if (isalpha(s[0])) {
57 struct passwd* pwd = getpwnam(s);
58 if (!pwd)
59 return UINT_MAX;
60 return pwd->pw_uid;
61 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080062
63 errno = 0;
64 v = (unsigned int) strtoul(s, 0, 0);
65 if (errno)
Nick Kralevichd2104df2015-06-18 17:46:54 -070066 return UINT_MAX;
67 return v;
68}
69
70/*
71 * decode_uid - decodes and returns the given string, which can be either the
72 * numeric or name representation, into the integer uid or gid. Returns
73 * UINT_MAX on error.
74 */
75unsigned int decode_uid(const char *s) {
76 unsigned int v = do_decode_uid(s);
77 if (v == UINT_MAX) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -070078 LOG(ERROR) << "decode_uid: Unable to find UID for '" << s << "'; returning UINT_MAX";
Nick Kralevichd2104df2015-06-18 17:46:54 -070079 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080080 return v;
81}
82
83/*
84 * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
85 * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
86 * daemon. We communicate the file descriptor's value via the environment
87 * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
88 */
Stephen Smalley8348d272013-05-13 12:37:04 -040089int create_socket(const char *name, int type, mode_t perm, uid_t uid,
90 gid_t gid, const char *socketcon)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080091{
92 struct sockaddr_un addr;
Nick Kralevich9bcfd642016-02-24 15:50:52 -080093 int fd, ret, savederrno;
Stephen Smalley8348d272013-05-13 12:37:04 -040094 char *filecon;
95
Nick Kralevich83ccb1c2015-11-23 16:26:42 -080096 if (socketcon) {
97 if (setsockcreatecon(socketcon) == -1) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -070098 PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
Nick Kralevich83ccb1c2015-11-23 16:26:42 -080099 return -1;
100 }
101 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800102
103 fd = socket(PF_UNIX, type, 0);
104 if (fd < 0) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700105 PLOG(ERROR) << "Failed to open socket '" << name << "'";
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800106 return -1;
107 }
108
Stephen Smalley8348d272013-05-13 12:37:04 -0400109 if (socketcon)
110 setsockcreatecon(NULL);
111
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800112 memset(&addr, 0 , sizeof(addr));
113 addr.sun_family = AF_UNIX;
114 snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
115 name);
116
117 ret = unlink(addr.sun_path);
118 if (ret != 0 && errno != ENOENT) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700119 PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800120 goto out_close;
121 }
122
Stephen Smalley8348d272013-05-13 12:37:04 -0400123 filecon = NULL;
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500124 if (sehandle) {
Stephen Smalley8348d272013-05-13 12:37:04 -0400125 ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500126 if (ret == 0)
Stephen Smalley8348d272013-05-13 12:37:04 -0400127 setfscreatecon(filecon);
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500128 }
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500129
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800130 ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
Nick Kralevich9bcfd642016-02-24 15:50:52 -0800131 savederrno = errno;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800132
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500133 setfscreatecon(NULL);
Stephen Smalley8348d272013-05-13 12:37:04 -0400134 freecon(filecon);
Stephen Smalleye46f9d52012-01-13 08:48:47 -0500135
Nick Kralevich9bcfd642016-02-24 15:50:52 -0800136 if (ret) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700137 errno = savederrno;
138 PLOG(ERROR) << "Failed to bind socket '" << name << "'";
Nick Kralevich9bcfd642016-02-24 15:50:52 -0800139 goto out_unlink;
140 }
141
142 ret = lchown(addr.sun_path, uid, gid);
143 if (ret) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700144 PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
Nick Kralevich9bcfd642016-02-24 15:50:52 -0800145 goto out_unlink;
146 }
147 ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW);
148 if (ret) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700149 PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
Nick Kralevich9bcfd642016-02-24 15:50:52 -0800150 goto out_unlink;
151 }
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800152
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700153 LOG(INFO) << "Created socket '" << addr.sun_path << "'"
154 << ", mode " << std::oct << perm << std::dec
155 << ", user " << uid
156 << ", group " << gid;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800157
158 return fd;
159
160out_unlink:
161 unlink(addr.sun_path);
162out_close:
163 close(fd);
164 return -1;
165}
166
Elliott Hughesf682b472015-02-06 12:19:48 -0800167bool read_file(const char* path, std::string* content) {
168 content->clear();
169
170 int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC));
171 if (fd == -1) {
172 return false;
173 }
174
175 // For security reasons, disallow world-writable
176 // or group-writable files.
Nick Kralevich38f368c2012-01-18 10:39:01 -0800177 struct stat sb;
Elliott Hughesf682b472015-02-06 12:19:48 -0800178 if (fstat(fd, &sb) == -1) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700179 PLOG(ERROR) << "fstat failed for '" << path << "'";
Elliott Hughesf682b472015-02-06 12:19:48 -0800180 return false;
Nick Kralevich38f368c2012-01-18 10:39:01 -0800181 }
182 if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700183 PLOG(ERROR) << "skipping insecure file '" << path << "'";
Elliott Hughesf682b472015-02-06 12:19:48 -0800184 return false;
Nick Kralevich38f368c2012-01-18 10:39:01 -0800185 }
186
Dan Albertc007bc32015-03-16 10:08:46 -0700187 bool okay = android::base::ReadFdToString(fd, content);
Elliott Hughes9fc83432015-05-15 19:16:40 -0700188 close(fd);
Elliott Hughesf682b472015-02-06 12:19:48 -0800189 return okay;
190}
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800191
Elliott Hughesf682b472015-02-06 12:19:48 -0800192int write_file(const char* path, const char* content) {
193 int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
194 if (fd == -1) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700195 PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
Nick Kralevicheedbe812015-04-25 14:10:03 -0700196 return -1;
Elliott Hughesf682b472015-02-06 12:19:48 -0800197 }
Nick Kralevicheedbe812015-04-25 14:10:03 -0700198 int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
199 if (result == -1) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700200 PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
Nick Kralevicheedbe812015-04-25 14:10:03 -0700201 }
Elliott Hughes9fc83432015-05-15 19:16:40 -0700202 close(fd);
Elliott Hughesf682b472015-02-06 12:19:48 -0800203 return result;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800204}
205
Elliott Hughesda40c002015-03-27 23:20:44 -0700206time_t gettime() {
207 timespec now;
208 clock_gettime(CLOCK_MONOTONIC, &now);
209 return now.tv_sec;
210}
Colin Cross504bc512010-04-13 19:35:09 -0700211
Elliott Hughesda40c002015-03-27 23:20:44 -0700212uint64_t gettime_ns() {
213 timespec now;
214 clock_gettime(CLOCK_MONOTONIC, &now);
215 return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
Colin Cross504bc512010-04-13 19:35:09 -0700216}
Colin Crossb0ab94b2010-04-08 16:16:20 -0700217
218int mkdir_recursive(const char *pathname, mode_t mode)
219{
220 char buf[128];
221 const char *slash;
222 const char *p = pathname;
223 int width;
224 int ret;
225 struct stat info;
226
227 while ((slash = strchr(p, '/')) != NULL) {
228 width = slash - pathname;
229 p = slash + 1;
230 if (width < 0)
231 break;
232 if (width == 0)
233 continue;
234 if ((unsigned int)width > sizeof(buf) - 1) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700235 LOG(ERROR) << "path too long for mkdir_recursive";
Colin Crossb0ab94b2010-04-08 16:16:20 -0700236 return -1;
237 }
238 memcpy(buf, pathname, width);
239 buf[width] = 0;
240 if (stat(buf, &info) != 0) {
Stephen Smalleye096e362012-06-11 13:37:39 -0400241 ret = make_dir(buf, mode);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700242 if (ret && errno != EEXIST)
243 return ret;
244 }
245 }
Stephen Smalleye096e362012-06-11 13:37:39 -0400246 ret = make_dir(pathname, mode);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700247 if (ret && errno != EEXIST)
248 return ret;
249 return 0;
250}
251
Johan Redestig93ca79b2012-04-18 16:41:19 +0200252/*
253 * replaces any unacceptable characters with '_', the
254 * length of the resulting string is equal to the input string
255 */
Colin Crossb0ab94b2010-04-08 16:16:20 -0700256void sanitize(char *s)
257{
Johan Redestig93ca79b2012-04-18 16:41:19 +0200258 const char* accept =
259 "abcdefghijklmnopqrstuvwxyz"
260 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
261 "0123456789"
262 "_-.";
263
Colin Crossb0ab94b2010-04-08 16:16:20 -0700264 if (!s)
265 return;
Johan Redestig93ca79b2012-04-18 16:41:19 +0200266
Christopher R. Palmer07f3fee2014-09-22 14:35:54 -0400267 while (*s) {
Johan Redestig93ca79b2012-04-18 16:41:19 +0200268 s += strspn(s, accept);
Christopher R. Palmer07f3fee2014-09-22 14:35:54 -0400269 if (*s) *s++ = '_';
Johan Redestig93ca79b2012-04-18 16:41:19 +0200270 }
Colin Crossb0ab94b2010-04-08 16:16:20 -0700271}
Johan Redestig93ca79b2012-04-18 16:41:19 +0200272
Chris Fries79f33842013-09-05 13:19:21 -0500273void make_link_init(const char *oldpath, const char *newpath)
Colin Crossb0ab94b2010-04-08 16:16:20 -0700274{
275 int ret;
276 char buf[256];
Dan Austina27bbd22016-03-24 11:28:46 -0700277 const char *slash;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700278 int width;
279
280 slash = strrchr(newpath, '/');
281 if (!slash)
282 return;
283 width = slash - newpath;
284 if (width <= 0 || width > (int)sizeof(buf) - 1)
285 return;
286 memcpy(buf, newpath, width);
287 buf[width] = 0;
288 ret = mkdir_recursive(buf, 0755);
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700289 if (ret) PLOG(ERROR) << "Failed to create directory " << buf;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700290
291 ret = symlink(oldpath, newpath);
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700292 if (ret && errno != EEXIST) PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700293}
294
295void remove_link(const char *oldpath, const char *newpath)
296{
297 char path[256];
298 ssize_t ret;
299 ret = readlink(newpath, path, sizeof(path) - 1);
300 if (ret <= 0)
301 return;
302 path[ret] = 0;
303 if (!strcmp(path, oldpath))
304 unlink(newpath);
305}
Colin Crosscd0f1732010-04-19 17:10:24 -0700306
307int wait_for_file(const char *filename, int timeout)
308{
309 struct stat info;
Thierry Strudel91cf41c2015-05-22 15:56:14 -0700310 uint64_t timeout_time_ns = gettime_ns() + timeout * UINT64_C(1000000000);
Colin Crosscd0f1732010-04-19 17:10:24 -0700311 int ret = -1;
312
Thierry Strudel91cf41c2015-05-22 15:56:14 -0700313 while (gettime_ns() < timeout_time_ns && ((ret = stat(filename, &info)) < 0))
Colin Crosscd0f1732010-04-19 17:10:24 -0700314 usleep(10000);
315
316 return ret;
317}
Colin Crossf83d0b92010-04-21 12:04:20 -0700318
319void open_devnull_stdio(void)
320{
Nick Kraleviche34577c2015-04-25 16:24:53 -0700321 int fd = open("/sys/fs/selinux/null", O_RDWR);
322 if (fd == -1) {
Nick Kralevich3d9e2732016-03-03 10:40:12 -0800323 /* Fail silently.
324 * stdout/stderr isn't available, and because
325 * klog_init() is called after open_devnull_stdio(), we can't
326 * log to dmesg. Reordering klog_init() to be called before
327 * open_devnull_stdio() isn't an option either, as then klog_fd
328 * will be assigned 0 or 1, which will end up getting clobbered
329 * by the code below. There's nowhere good to log.
330 */
331
332 exit(1);
Colin Crossf83d0b92010-04-21 12:04:20 -0700333 }
334
Nick Kraleviche34577c2015-04-25 16:24:53 -0700335 dup2(fd, 0);
336 dup2(fd, 1);
337 dup2(fd, 2);
338 if (fd > 2) {
339 close(fd);
340 }
Colin Crossf83d0b92010-04-21 12:04:20 -0700341}
342
Elliott Hughese5ce30f2015-05-06 19:19:24 -0700343void import_kernel_cmdline(bool in_qemu,
344 std::function<void(const std::string&, const std::string&, bool)> fn) {
345 std::string cmdline;
346 android::base::ReadFileToString("/proc/cmdline", &cmdline);
Vladimir Chtchetkine2b995432011-09-28 09:55:31 -0700347
Elliott Hughese5ce30f2015-05-06 19:19:24 -0700348 for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
349 std::vector<std::string> pieces = android::base::Split(entry, "=");
350 if (pieces.size() == 2) {
351 fn(pieces[0], pieces[1], in_qemu);
352 }
Vladimir Chtchetkine2b995432011-09-28 09:55:31 -0700353 }
354}
Stephen Smalleye096e362012-06-11 13:37:39 -0400355
356int make_dir(const char *path, mode_t mode)
357{
358 int rc;
359
Stephen Smalleye096e362012-06-11 13:37:39 -0400360 char *secontext = NULL;
361
362 if (sehandle) {
363 selabel_lookup(sehandle, &secontext, path, mode);
364 setfscreatecon(secontext);
365 }
Stephen Smalleye096e362012-06-11 13:37:39 -0400366
367 rc = mkdir(path, mode);
368
Stephen Smalleye096e362012-06-11 13:37:39 -0400369 if (secontext) {
370 int save_errno = errno;
371 freecon(secontext);
372 setfscreatecon(NULL);
373 errno = save_errno;
374 }
Kenny Rootb5982bf2012-10-16 23:07:05 -0700375
Stephen Smalleye096e362012-06-11 13:37:39 -0400376 return rc;
377}
378
Stephen Smalleydbd37f22014-01-28 10:34:09 -0500379int restorecon(const char* pathname)
Stephen Smalleye096e362012-06-11 13:37:39 -0400380{
Stephen Smalley27a93652014-02-07 09:14:13 -0500381 return selinux_android_restorecon(pathname, 0);
Nick Kralevichae76f6d2013-07-11 15:38:26 -0700382}
383
384int restorecon_recursive(const char* pathname)
385{
Stephen Smalley27a93652014-02-07 09:14:13 -0500386 return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE);
Nick Kralevichae76f6d2013-07-11 15:38:26 -0700387}
Andres Moralesdb5f5d42015-05-08 08:30:33 -0700388
389/*
390 * Writes hex_len hex characters (1/2 byte) to hex from bytes.
391 */
392std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
393 std::string hex("0x");
394 for (size_t i = 0; i < bytes_len; i++)
395 android::base::StringAppendF(&hex, "%02x", bytes[i]);
396 return hex;
397}
Lee Campbellf13b1b32015-07-24 16:57:14 -0700398
399/*
400 * Returns true is pathname is a directory
401 */
402bool is_dir(const char* pathname) {
403 struct stat info;
404 if (stat(pathname, &info) == -1) {
405 return false;
406 }
407 return S_ISDIR(info.st_mode);
408}
Tom Cherryb7349902015-08-26 11:43:36 -0700409
410bool expand_props(const std::string& src, std::string* dst) {
411 const char* src_ptr = src.c_str();
412
413 if (!dst) {
414 return false;
415 }
416
417 /* - variables can either be $x.y or ${x.y}, in case they are only part
418 * of the string.
419 * - will accept $$ as a literal $.
420 * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
421 * bad things will happen
Mark Salyzyn4b561622016-06-07 08:49:01 -0700422 * - ${x.y:-default} will return default value if property empty.
Tom Cherryb7349902015-08-26 11:43:36 -0700423 */
424 while (*src_ptr) {
425 const char* c;
426
427 c = strchr(src_ptr, '$');
428 if (!c) {
429 dst->append(src_ptr);
430 return true;
431 }
432
433 dst->append(src_ptr, c);
434 c++;
435
436 if (*c == '$') {
437 dst->push_back(*(c++));
438 src_ptr = c;
439 continue;
440 } else if (*c == '\0') {
441 return true;
442 }
443
444 std::string prop_name;
Mark Salyzyn4b561622016-06-07 08:49:01 -0700445 std::string def_val;
Tom Cherryb7349902015-08-26 11:43:36 -0700446 if (*c == '{') {
447 c++;
448 const char* end = strchr(c, '}');
449 if (!end) {
450 // failed to find closing brace, abort.
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700451 LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
Tom Cherryb7349902015-08-26 11:43:36 -0700452 return false;
453 }
454 prop_name = std::string(c, end);
455 c = end + 1;
Mark Salyzyn4b561622016-06-07 08:49:01 -0700456 size_t def = prop_name.find(":-");
457 if (def < prop_name.size()) {
458 def_val = prop_name.substr(def + 2);
459 prop_name = prop_name.substr(0, def);
460 }
Tom Cherryb7349902015-08-26 11:43:36 -0700461 } else {
462 prop_name = c;
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700463 LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
Tom Cherryb7349902015-08-26 11:43:36 -0700464 c += prop_name.size();
465 }
466
467 if (prop_name.empty()) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700468 LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
Tom Cherryb7349902015-08-26 11:43:36 -0700469 return false;
470 }
471
472 std::string prop_val = property_get(prop_name.c_str());
473 if (prop_val.empty()) {
Mark Salyzyn4b561622016-06-07 08:49:01 -0700474 if (def_val.empty()) {
Elliott Hughesf86b5a62016-06-24 15:12:21 -0700475 LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
Mark Salyzyn4b561622016-06-07 08:49:01 -0700476 return false;
477 }
478 prop_val = def_val;
Tom Cherryb7349902015-08-26 11:43:36 -0700479 }
480
481 dst->append(prop_val);
482 src_ptr = c;
483 }
484
485 return true;
486}