2019년 7월 14일 일요일

property 변경시 어떤 동작 시키기 ( on property in init.rc )


android 에서 property 값 변화시마다 임의의 동작 수행 방밥


init.rc에 on property 사용

아래와 같은 코드가 있음

on property:persist.service.adb.enable=1
    start adbd

설명 : persist.service.adb.enable=1 값이 write 되면 adbd가 실행됨


좀 더 자세한 설명


https://android.googlesource.com/platform/system/core/+/refs/tags/android-9.0.0_r45/init/

Triggers

Triggers are strings which can be used to match certain kinds of events and used to cause an action to occur.
Triggers are subdivided into event triggers and property triggers.
Event triggers are strings triggered by the ‘trigger’ command or by the QueueEventTrigger() function within the init executable. These take the form of a simple string such as ‘boot’ or ‘late-init’.
Property triggers are strings triggered when a named property changes value to a given new value or when a named property changes value to any new value. These take the form of ‘property:=’ and ‘property:=*’ respectively. Property triggers are additionally evaluated and triggered accordingly during the initial boot phase of init.
An Action can have multiple property triggers but may only have one event trigger.
For example: on boot && property:a=b defines an action that is only executed when the ‘boot’ event trigger happens and the property a equals b.
on property:a=b && property:c=d defines an action that is executed at three times:
  1. During initial boot if property a=b and property c=d.
  2. Any time that property a transitions to value b, while property c already equals d.
  3. Any time that property c transitions to value d, while property a already equals b.

만약 값이 변하게 될때마다 처리를 하고 싶을때는 어떻게 할까?
on property:persist.service.adb.enable=*

property 서비스의 전반적인 흐름은 다음과 같습니다.
아래함수에서 init 서비스에서 property 서비스가 만들어지고 소켓 polling을 하고 있다가 커맨드가 도착하면 동작하는 방식입니다.
840void start_property_service() {
841    selinux_callback cb;
842    cb.func_audit = SelinuxAuditCallback;
843    selinux_set_callback(SELINUX_CB_AUDIT, cb);
844
845    property_set("ro.property_service.version", "2");
846
847    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
848                                   false, 0666, 0, 0, nullptr);
849    if (property_set_fd == -1) {
850        PLOG(FATAL) << "start_property_service socket creation failed";
851    }
852
853    listen(property_set_fd, 8);
854
855    register_epoll_handler(property_set_fd, handle_property_set_fd);
856}


130static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
131    size_t valuelen = value.size();
132
133    if (!IsLegalPropertyName(name)) {
134        *error = "Illegal property name";
135        return PROP_ERROR_INVALID_NAME;
136    }
137
138    if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
139        *error = "Property value too long";
140        return PROP_ERROR_INVALID_VALUE;
141    }
142
143    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
144        *error = "Value is not a UTF8 encoded string";
145        return PROP_ERROR_INVALID_VALUE;
146    }
147
148    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
149    if (pi != nullptr) {
150        // ro.* properties are actually "write-once".
151        if (StartsWith(name, "ro.")) {
152            *error = "Read-only property was already set";
153            return PROP_ERROR_READ_ONLY_PROPERTY;
154        }
155
156        __system_property_update(pi, value.c_str(), valuelen);
157    } else {
158        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
159        if (rc < 0) {
160            *error = "__system_property_add failed";
161            return PROP_ERROR_SET_FAILED;
162        }
163    }
164
165    // Don't write properties to disk until after we have read all default
166    // properties to prevent them from being overwritten by default values.
167    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
168        WritePersistentProperty(name, value);
169    }
170    property_changed(name, value);
171    return PROP_SUCCESS;
172}



165void property_changed(const std::string& name, const std::string& value) {
166    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
167    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
168    // if there are other pending events to process or if init is waiting on an exec service or
169    // waiting on a property.
170    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
171    // commands to be executed.
172    if (name == "sys.powerctl") {
173        // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
174        // because it modifies the contents of the action queue, which can cause the action queue
175        // to get into a bad state if this function is called from a command being executed by the
176        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
177        // command is run in the main init loop.
178        // TODO: once property service is removed from init, this will never happen from a builtin,
179        // but rather from a callback from the property service socket, in which case this hack can
180        // go away.
181        shutdown_command = value;
182        do_shutdown = true;
183    }
184
185    if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
186


최종단에서는 property 값이 변할때 trigger 할것이 있는지 검사하게 되는 CheckPropertyTriggers 함수를 이용하게됩니다.

관련소스
trigger_value != "*" 조건이 false가 되므로 found 가 true가 됩니다.

155bool Action::CheckPropertyTriggers(const std::string& name,
156                                   const std::string& value) const {
157    if (property_triggers_.empty()) {
158        return true;
159    }
160
161    bool found = name.empty();
162    for (const auto& [trigger_name, trigger_value] : property_triggers_) {
163        if (trigger_name == name) {
164            if (trigger_value != "*" && trigger_value != value) {
165                return false;
166            } else {
167                found = true;
168            }
169        } else {
170            std::string prop_val = android::base::GetProperty(trigger_name, "");
171            if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
172                return false;
173            }
174        }
175    }
176    return found;
177}


예제)
이미 찾아보니 * 로 사용하는 소스들이 존재 합니다.
722# system server cannot write to /proc/sys files,
723# and chown/chmod does not work for /proc/sys/ entries.
724# So proxy writes through init.
725on property:sys.sysctl.extra_free_kbytes=*
726    write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
727
728# "tcp_default_init_rwnd" Is too long!
729on property:sys.sysctl.tcp_def_init_rwnd=*
730    write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd}




추가로 write시 ${sys.sysctl.extra_free_kbytes} 이부분이 있는데 이건 뭘까 검색해 봤습니다.
설명상은 <contents>에 의해 확장될수 있다는 의미로 보입니다.

534`write <path> <content>`
535> Open the file at _path_ and write a string to it with write(2).
536  If the file does not exist, it will be created. If it does exist,
537  it will be truncated. Properties are expanded within _content_.
538

그리고 이것은 ${property.name} 이런 형태로 표현할 수 있다는것이고요.

15System properties can be expanded using the syntax
16`${property.name}`. This also works in contexts where concatenation is
17required, such as `import /init.recovery.${ro.hardware}.rc`.


1082        {"write",                   {2,     2,    {true,   do_write}}},

724static Result<Success> do_write(const BuiltinArguments& args) {
725    if (auto result = WriteFile(args[1], args[2]); !result) {
726        return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
727    }
728
729    return Success();
730}


204Result<Success> WriteFile(const std::string& path, const std::string& content) {
205    android::base::unique_fd fd(TEMP_FAILURE_RETRY(
206        OpenFile(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
207    if (fd == -1) {
208        return ErrnoError() << "open() failed";
209    }
210    if (!android::base::WriteStringToFd(content, fd)) {
211        return ErrnoError() << "Unable to write file contents";
212    }
213    return Success();
214}

81bool WriteStringToFd(const std::string& content, int fd) {
82  const char* p = content.data();
83  size_t left = content.size();
84  while (left > 0) {
85    ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left));
86    if (n == -1) {
87      return false;
88    }
89    p += n;
90    left -= n;
91  }
92  return true;
93}

따라가 봤지만 ${} 확장하는 코드는 보이지 않습니다. 그렇다면 do_write 하기전에 이미 확장된것은 아닌지 검토해 보았습니다.

36Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
37                                   const std::vector<std::string>& args,
38                                   const std::string& context) {
39    auto builtin_arguments = BuiltinArguments(context);
40
41    builtin_arguments.args.resize(args.size());
42    builtin_arguments.args[0] = args[0];
43    for (std::size_t i = 1; i < args.size(); ++i) {
44        if (!expand_props(args[i], &builtin_arguments.args[i])) {
45            return Error() << "cannot expand '" << args[i] << "'";
46        }
47    }
48
49    return function(builtin_arguments);
50}

실제 확장은 expand_props 여기에서 이루어지며 인자를 넘기기 전에 이루어 집니다.

296bool expand_props(const std::string& src, std::string* dst) {
297    const char* src_ptr = src.c_str();
298
299    if (!dst) {
300        return false;
301    }
302
303    /* - variables can either be $x.y or ${x.y}, in case they are only part
304     *   of the string.
305     * - will accept $$ as a literal $.
306     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
307     *   bad things will happen
308     * - ${x.y:-default} will return default value if property empty.
309     */
310    while (*src_ptr) {
311        const char* c;
312
313        c = strchr(src_ptr, '$');
314        if (!c) {
315            dst->append(src_ptr);
316            return true;
317        }
318
319        dst->append(src_ptr, c);
320        c++;
321
322        if (*c == '$') {
323            dst->push_back(*(c++));
324            src_ptr = c;
325            continue;
326        } else if (*c == '\0') {
327            return true;
328        }
329
330        std::string prop_name;
331        std::string def_val;
332        if (*c == '{') {
333            c++;
334            const char* end = strchr(c, '}');
335            if (!end) {
336                // failed to find closing brace, abort.
337                LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
338                return false;
339            }
340            prop_name = std::string(c, end);
341            c = end + 1;
342            size_t def = prop_name.find(":-");
343            if (def < prop_name.size()) {
344                def_val = prop_name.substr(def + 2);
345                prop_name = prop_name.substr(0, def);
346            }
347        } else {
348            prop_name = c;
349            LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
350            c += prop_name.size();
351        }
352
353        if (prop_name.empty()) {
354            LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
355            return false;
356        }
357
358        std::string prop_val = android::base::GetProperty(prop_name, "");
359        if (prop_val.empty()) {
360            if (def_val.empty()) {
361                LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
362                return false;
363            }
364            prop_val = def_val;
365        }
366
367        dst->append(prop_val);
368        src_ptr = c;
369    }
370
371    return true;
372}

댓글 없음:

댓글 쓰기