Loading init/builtins.cpp +7 −52 Original line number Diff line number Diff line Loading @@ -62,14 +62,8 @@ extern "C" int init_module(void *, unsigned long, const char *); static int insmod(const char *filename, char *options) { char filename_val[PROP_VALUE_MAX]; if (expand_props(filename_val, filename, sizeof(filename_val)) == -1) { ERROR("insmod: cannot expand '%s'\n", filename); return -EINVAL; } std::string module; if (!read_file(filename_val, &module)) { if (!read_file(filename, &module)) { return -1; } Loading Loading @@ -468,17 +462,11 @@ int do_mount_all(int nargs, char **args) int child_ret = -1; int status; struct fstab *fstab; char fstabfile[PROP_VALUE_MAX]; if (nargs != 2) { return -1; } if (expand_props(fstabfile, args[1], sizeof(fstabfile)) == -1) { ERROR("mount_all: cannot expand '%s' \n", args[1]); return -EINVAL; } const char* fstabfile = args[1]; /* * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and * do the call in the child to provide protection to the main init Loading Loading @@ -572,15 +560,7 @@ int do_setprop(int nargs, char **args) { const char *name = args[1]; const char *value = args[2]; char prop_val[PROP_VALUE_MAX]; int ret; ret = expand_props(prop_val, value, sizeof(prop_val)); if (ret) { ERROR("cannot expand '%s' while assigning to '%s'\n", value, name); return -EINVAL; } property_set(name, prop_val); property_set(name, value); return 0; } Loading Loading @@ -626,19 +606,12 @@ int do_restart(int nargs, char **args) int do_powerctl(int nargs, char **args) { char command[PROP_VALUE_MAX]; int res; const char* command = args[1]; int len = 0; int cmd = 0; const char *reboot_target; void (*callback_on_ro_remount)(const struct mntent*) = NULL; res = expand_props(command, args[1], sizeof(command)); if (res) { ERROR("powerctl: cannot expand '%s'\n", args[1]); return -EINVAL; } if (strncmp(command, "shutdown", 8) == 0) { cmd = ANDROID_RB_POWEROFF; len = 8; Loading Loading @@ -666,13 +639,7 @@ int do_powerctl(int nargs, char **args) int do_trigger(int nargs, char **args) { char prop_val[PROP_VALUE_MAX]; int res = expand_props(prop_val, args[1], sizeof(prop_val)); if (res) { ERROR("trigger: cannot expand '%s'\n", args[1]); return -EINVAL; } action_for_each_trigger(prop_val, action_add_queue_tail); action_for_each_trigger(args[1], action_add_queue_tail); return 0; } Loading Loading @@ -727,13 +694,7 @@ int do_write(int nargs, char **args) { const char *path = args[1]; const char *value = args[2]; char expanded_value[256]; if (expand_props(expanded_value, value, sizeof(expanded_value))) { ERROR("cannot expand '%s' while writing to '%s'\n", value, path); return -EINVAL; } return write_file(path, expanded_value); return write_file(path, value); } int do_copy(int nargs, char **args) Loading Loading @@ -856,18 +817,12 @@ int do_restorecon_recursive(int nargs, char **args) { } int do_loglevel(int nargs, char **args) { int log_level; char log_level_str[PROP_VALUE_MAX] = ""; if (nargs != 2) { ERROR("loglevel: missing argument\n"); return -EINVAL; } if (expand_props(log_level_str, args[1], sizeof(log_level_str))) { ERROR("loglevel: cannot expand '%s'\n", args[1]); return -EINVAL; } log_level = atoi(log_level_str); int log_level = atoi(args[1]); if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) { ERROR("loglevel: invalid log level'%d'\n", log_level); return -EINVAL; Loading init/init.cpp +35 −20 Original line number Diff line number Diff line Loading @@ -570,25 +570,24 @@ static int is_last_command(struct action *act, struct command *cmd) } void build_triggers_string(char *name_str, int length, struct action *cur_action) { std::string build_triggers_string(struct action *cur_action) { std::string result; struct listnode *node; struct trigger *cur_trigger; list_for_each(node, &cur_action->triggers) { cur_trigger = node_to_item(node, struct trigger, nlist); if (node != cur_action->triggers.next) { strlcat(name_str, " " , length); result.push_back(' '); } strlcat(name_str, cur_trigger->name , length); result += cur_trigger->name; } return result; } void execute_one_command() { Timer t; char cmd_str[256] = ""; char name_str[256] = ""; if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) { cur_action = action_remove_queue_head(); cur_command = NULL; Loading @@ -596,9 +595,8 @@ void execute_one_command() { return; } build_triggers_string(name_str, sizeof(name_str), cur_action); INFO("processing action %p (%s)\n", cur_action, name_str); std::string trigger_name = build_triggers_string(cur_action); INFO("processing action %p (%s)\n", cur_action, trigger_name.c_str()); cur_command = get_first_command(cur_action); } else { cur_command = get_next_command(cur_action, cur_command); Loading @@ -607,23 +605,40 @@ void execute_one_command() { if (!cur_command) { return; } int result = cur_command->func(cur_command->nargs, cur_command->args); int result = 0; std::vector<std::string> arg_strs(cur_command->nargs); arg_strs[0] = cur_command->args[0]; for (int i = 1; i < cur_command->nargs; ++i) { if (expand_props(cur_command->args[i], &arg_strs[i]) == -1) { ERROR("%s: cannot expand '%s'\n", cur_command->args[0], cur_command->args[i]); result = -EINVAL; break; } } if (result == 0) { std::vector<char*> args; for (auto& s : arg_strs) { args.push_back(&s[0]); } result = cur_command->func(args.size(), &args[0]); } if (klog_get_level() >= KLOG_INFO_LEVEL) { for (int i = 0; i < cur_command->nargs; i++) { strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str)); if (i < cur_command->nargs - 1) { strlcat(cmd_str, " ", sizeof(cmd_str)); std::string cmd_str; for (int i = 0; i < cur_command->nargs; ++i) { if (i > 0) { cmd_str.push_back(' '); } cmd_str += cur_command->args[i]; } char source[256]; std::string trigger_name = build_triggers_string(cur_action); std::string source; if (cur_command->filename) { snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line); } else { *source = '\0'; source = android::base::StringPrintf(" (%s:%d)", cur_command->filename, cur_command->line); } INFO("Command '%s' action=%s%s returned %d took %.2fs\n", cmd_str, cur_action ? name_str : "", source, result, t.duration()); cmd_str.c_str(), trigger_name.c_str(), source.c_str(), result, t.duration()); } } Loading init/init.h +1 −1 Original line number Diff line number Diff line Loading @@ -138,7 +138,7 @@ extern bool waiting_for_exec; extern struct selabel_handle *sehandle; extern struct selabel_handle *sehandle_prop; void build_triggers_string(char *name_str, int length, struct action *cur_action); std::string build_triggers_string(struct action *cur_action); void handle_control_message(const char *msg, const char *arg); Loading init/init_parser.cpp +30 −71 Original line number Diff line number Diff line Loading @@ -95,9 +95,8 @@ void dump_parser_state() { list_for_each(node, &action_list) { action* act = node_to_item(node, struct action, alist); INFO("on "); char name_str[256] = ""; build_triggers_string(name_str, sizeof(name_str), act); INFO("%s", name_str); std::string trigger_name = build_triggers_string(act); INFO("%s", trigger_name.c_str()); INFO("\n"); struct listnode* node2; Loading Loading @@ -216,27 +215,12 @@ static int lookup_keyword(const char *s) static void parse_line_no_op(struct parse_state*, int, char**) { } static int push_chars(char **dst, int *len, const char *chars, int cnt) { if (cnt > *len) return -1; memcpy(*dst, chars, cnt); *dst += cnt; *len -= cnt; return 0; } int expand_props(char *dst, const char *src, int dst_size) { char *dst_ptr = dst; int expand_props(const char *src, std::string *dst) { const char *src_ptr = src; int ret = 0; int left = dst_size - 1; if (!src || !dst || dst_size == 0) if (!src || !dst) { return -1; } /* - variables can either be $x.y or ${x.y}, in case they are only part * of the string. Loading @@ -244,102 +228,75 @@ int expand_props(char *dst, const char *src, int dst_size) * - no nested property expansion, i.e. ${foo.${bar}} is not supported, * bad things will happen */ while (*src_ptr && left > 0) { char *c; char prop[PROP_NAME_MAX + 1]; int prop_len = 0; while (*src_ptr) { const char *c; c = strchr(src_ptr, '$'); if (!c) { while (left-- > 0 && *src_ptr) *(dst_ptr++) = *(src_ptr++); dst->append(src_ptr); break; } memset(prop, 0, sizeof(prop)); ret = push_chars(&dst_ptr, &left, src_ptr, c - src_ptr); if (ret < 0) goto err_nospace; dst->append(src_ptr, c); c++; if (*c == '$') { *(dst_ptr++) = *(c++); dst->push_back(*(c++)); src_ptr = c; left--; continue; } else if (*c == '\0') { break; } std::string prop_name; if (*c == '{') { c++; while (*c && *c != '}' && prop_len < PROP_NAME_MAX) prop[prop_len++] = *(c++); if (*c != '}') { /* failed to find closing brace, abort. */ if (prop_len == PROP_NAME_MAX) ERROR("prop name too long during expansion of '%s'\n", src); else if (*c == '\0') ERROR("unexpected end of string in '%s', looking for }\n", src); const char* end = strchr(c, '}'); if (!end) { // failed to find closing brace, abort. ERROR("unexpected end of string in '%s', looking for }\n", src); goto err; } prop[prop_len] = '\0'; c++; } else if (*c) { while (*c && prop_len < PROP_NAME_MAX) prop[prop_len++] = *(c++); if (prop_len == PROP_NAME_MAX && *c != '\0') { ERROR("prop name too long in '%s'\n", src); goto err; } prop[prop_len] = '\0'; prop_name = std::string(c, end); c = end + 1; } else { prop_name = c; ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n", prop); c); c += prop_name.size(); } if (prop_len == 0) { if (prop_name.empty()) { ERROR("invalid zero-length prop name in '%s'\n", src); goto err; } std::string prop_val = property_get(prop); std::string prop_val = property_get(prop_name.c_str()); if (prop_val.empty()) { ERROR("property '%s' doesn't exist while expanding '%s'\n", prop, src); prop_name.c_str(), src); goto err; } ret = push_chars(&dst_ptr, &left, prop_val.c_str(), prop_val.size()); if (ret < 0) goto err_nospace; dst->append(prop_val); src_ptr = c; continue; } *dst_ptr = '\0'; return 0; err_nospace: ERROR("destination buffer overflow while expanding '%s'\n", src); err: return -1; } static void parse_import(struct parse_state *state, int nargs, char **args) { struct listnode *import_list = (listnode*) state->priv; char conf_file[PATH_MAX]; int ret; if (nargs != 2) { ERROR("single argument needed for import\n"); return; } ret = expand_props(conf_file, args[1], sizeof(conf_file)); std::string conf_file; int ret = expand_props(args[1], &conf_file); if (ret) { ERROR("error while handling import on line '%d' in '%s'\n", state->line, state->filename); Loading @@ -347,7 +304,9 @@ static void parse_import(struct parse_state *state, int nargs, char **args) } struct import* import = (struct import*) calloc(1, sizeof(struct import)); import->filename = strdup(conf_file); import->filename = strdup(conf_file.c_str()); struct listnode *import_list = (listnode*) state->priv; list_add_tail(import_list, &import->list); INFO("Added '%s' to import list\n", import->filename); } Loading init/init_parser.h +3 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #ifndef _INIT_INIT_PARSER_H_ #define _INIT_INIT_PARSER_H_ #include <string> #define INIT_PARSER_MAXARGS 64 struct action; Loading @@ -32,7 +34,7 @@ void queue_all_property_triggers(); void queue_builtin_action(int (*func)(int nargs, char **args), const char *name); bool init_parse_config_file(const char* path); int expand_props(char *dst, const char *src, int len); int expand_props(const char *src, std::string *dst); service* make_exec_oneshot_service(int argc, char** argv); Loading Loading
init/builtins.cpp +7 −52 Original line number Diff line number Diff line Loading @@ -62,14 +62,8 @@ extern "C" int init_module(void *, unsigned long, const char *); static int insmod(const char *filename, char *options) { char filename_val[PROP_VALUE_MAX]; if (expand_props(filename_val, filename, sizeof(filename_val)) == -1) { ERROR("insmod: cannot expand '%s'\n", filename); return -EINVAL; } std::string module; if (!read_file(filename_val, &module)) { if (!read_file(filename, &module)) { return -1; } Loading Loading @@ -468,17 +462,11 @@ int do_mount_all(int nargs, char **args) int child_ret = -1; int status; struct fstab *fstab; char fstabfile[PROP_VALUE_MAX]; if (nargs != 2) { return -1; } if (expand_props(fstabfile, args[1], sizeof(fstabfile)) == -1) { ERROR("mount_all: cannot expand '%s' \n", args[1]); return -EINVAL; } const char* fstabfile = args[1]; /* * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and * do the call in the child to provide protection to the main init Loading Loading @@ -572,15 +560,7 @@ int do_setprop(int nargs, char **args) { const char *name = args[1]; const char *value = args[2]; char prop_val[PROP_VALUE_MAX]; int ret; ret = expand_props(prop_val, value, sizeof(prop_val)); if (ret) { ERROR("cannot expand '%s' while assigning to '%s'\n", value, name); return -EINVAL; } property_set(name, prop_val); property_set(name, value); return 0; } Loading Loading @@ -626,19 +606,12 @@ int do_restart(int nargs, char **args) int do_powerctl(int nargs, char **args) { char command[PROP_VALUE_MAX]; int res; const char* command = args[1]; int len = 0; int cmd = 0; const char *reboot_target; void (*callback_on_ro_remount)(const struct mntent*) = NULL; res = expand_props(command, args[1], sizeof(command)); if (res) { ERROR("powerctl: cannot expand '%s'\n", args[1]); return -EINVAL; } if (strncmp(command, "shutdown", 8) == 0) { cmd = ANDROID_RB_POWEROFF; len = 8; Loading Loading @@ -666,13 +639,7 @@ int do_powerctl(int nargs, char **args) int do_trigger(int nargs, char **args) { char prop_val[PROP_VALUE_MAX]; int res = expand_props(prop_val, args[1], sizeof(prop_val)); if (res) { ERROR("trigger: cannot expand '%s'\n", args[1]); return -EINVAL; } action_for_each_trigger(prop_val, action_add_queue_tail); action_for_each_trigger(args[1], action_add_queue_tail); return 0; } Loading Loading @@ -727,13 +694,7 @@ int do_write(int nargs, char **args) { const char *path = args[1]; const char *value = args[2]; char expanded_value[256]; if (expand_props(expanded_value, value, sizeof(expanded_value))) { ERROR("cannot expand '%s' while writing to '%s'\n", value, path); return -EINVAL; } return write_file(path, expanded_value); return write_file(path, value); } int do_copy(int nargs, char **args) Loading Loading @@ -856,18 +817,12 @@ int do_restorecon_recursive(int nargs, char **args) { } int do_loglevel(int nargs, char **args) { int log_level; char log_level_str[PROP_VALUE_MAX] = ""; if (nargs != 2) { ERROR("loglevel: missing argument\n"); return -EINVAL; } if (expand_props(log_level_str, args[1], sizeof(log_level_str))) { ERROR("loglevel: cannot expand '%s'\n", args[1]); return -EINVAL; } log_level = atoi(log_level_str); int log_level = atoi(args[1]); if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) { ERROR("loglevel: invalid log level'%d'\n", log_level); return -EINVAL; Loading
init/init.cpp +35 −20 Original line number Diff line number Diff line Loading @@ -570,25 +570,24 @@ static int is_last_command(struct action *act, struct command *cmd) } void build_triggers_string(char *name_str, int length, struct action *cur_action) { std::string build_triggers_string(struct action *cur_action) { std::string result; struct listnode *node; struct trigger *cur_trigger; list_for_each(node, &cur_action->triggers) { cur_trigger = node_to_item(node, struct trigger, nlist); if (node != cur_action->triggers.next) { strlcat(name_str, " " , length); result.push_back(' '); } strlcat(name_str, cur_trigger->name , length); result += cur_trigger->name; } return result; } void execute_one_command() { Timer t; char cmd_str[256] = ""; char name_str[256] = ""; if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) { cur_action = action_remove_queue_head(); cur_command = NULL; Loading @@ -596,9 +595,8 @@ void execute_one_command() { return; } build_triggers_string(name_str, sizeof(name_str), cur_action); INFO("processing action %p (%s)\n", cur_action, name_str); std::string trigger_name = build_triggers_string(cur_action); INFO("processing action %p (%s)\n", cur_action, trigger_name.c_str()); cur_command = get_first_command(cur_action); } else { cur_command = get_next_command(cur_action, cur_command); Loading @@ -607,23 +605,40 @@ void execute_one_command() { if (!cur_command) { return; } int result = cur_command->func(cur_command->nargs, cur_command->args); int result = 0; std::vector<std::string> arg_strs(cur_command->nargs); arg_strs[0] = cur_command->args[0]; for (int i = 1; i < cur_command->nargs; ++i) { if (expand_props(cur_command->args[i], &arg_strs[i]) == -1) { ERROR("%s: cannot expand '%s'\n", cur_command->args[0], cur_command->args[i]); result = -EINVAL; break; } } if (result == 0) { std::vector<char*> args; for (auto& s : arg_strs) { args.push_back(&s[0]); } result = cur_command->func(args.size(), &args[0]); } if (klog_get_level() >= KLOG_INFO_LEVEL) { for (int i = 0; i < cur_command->nargs; i++) { strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str)); if (i < cur_command->nargs - 1) { strlcat(cmd_str, " ", sizeof(cmd_str)); std::string cmd_str; for (int i = 0; i < cur_command->nargs; ++i) { if (i > 0) { cmd_str.push_back(' '); } cmd_str += cur_command->args[i]; } char source[256]; std::string trigger_name = build_triggers_string(cur_action); std::string source; if (cur_command->filename) { snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line); } else { *source = '\0'; source = android::base::StringPrintf(" (%s:%d)", cur_command->filename, cur_command->line); } INFO("Command '%s' action=%s%s returned %d took %.2fs\n", cmd_str, cur_action ? name_str : "", source, result, t.duration()); cmd_str.c_str(), trigger_name.c_str(), source.c_str(), result, t.duration()); } } Loading
init/init.h +1 −1 Original line number Diff line number Diff line Loading @@ -138,7 +138,7 @@ extern bool waiting_for_exec; extern struct selabel_handle *sehandle; extern struct selabel_handle *sehandle_prop; void build_triggers_string(char *name_str, int length, struct action *cur_action); std::string build_triggers_string(struct action *cur_action); void handle_control_message(const char *msg, const char *arg); Loading
init/init_parser.cpp +30 −71 Original line number Diff line number Diff line Loading @@ -95,9 +95,8 @@ void dump_parser_state() { list_for_each(node, &action_list) { action* act = node_to_item(node, struct action, alist); INFO("on "); char name_str[256] = ""; build_triggers_string(name_str, sizeof(name_str), act); INFO("%s", name_str); std::string trigger_name = build_triggers_string(act); INFO("%s", trigger_name.c_str()); INFO("\n"); struct listnode* node2; Loading Loading @@ -216,27 +215,12 @@ static int lookup_keyword(const char *s) static void parse_line_no_op(struct parse_state*, int, char**) { } static int push_chars(char **dst, int *len, const char *chars, int cnt) { if (cnt > *len) return -1; memcpy(*dst, chars, cnt); *dst += cnt; *len -= cnt; return 0; } int expand_props(char *dst, const char *src, int dst_size) { char *dst_ptr = dst; int expand_props(const char *src, std::string *dst) { const char *src_ptr = src; int ret = 0; int left = dst_size - 1; if (!src || !dst || dst_size == 0) if (!src || !dst) { return -1; } /* - variables can either be $x.y or ${x.y}, in case they are only part * of the string. Loading @@ -244,102 +228,75 @@ int expand_props(char *dst, const char *src, int dst_size) * - no nested property expansion, i.e. ${foo.${bar}} is not supported, * bad things will happen */ while (*src_ptr && left > 0) { char *c; char prop[PROP_NAME_MAX + 1]; int prop_len = 0; while (*src_ptr) { const char *c; c = strchr(src_ptr, '$'); if (!c) { while (left-- > 0 && *src_ptr) *(dst_ptr++) = *(src_ptr++); dst->append(src_ptr); break; } memset(prop, 0, sizeof(prop)); ret = push_chars(&dst_ptr, &left, src_ptr, c - src_ptr); if (ret < 0) goto err_nospace; dst->append(src_ptr, c); c++; if (*c == '$') { *(dst_ptr++) = *(c++); dst->push_back(*(c++)); src_ptr = c; left--; continue; } else if (*c == '\0') { break; } std::string prop_name; if (*c == '{') { c++; while (*c && *c != '}' && prop_len < PROP_NAME_MAX) prop[prop_len++] = *(c++); if (*c != '}') { /* failed to find closing brace, abort. */ if (prop_len == PROP_NAME_MAX) ERROR("prop name too long during expansion of '%s'\n", src); else if (*c == '\0') ERROR("unexpected end of string in '%s', looking for }\n", src); const char* end = strchr(c, '}'); if (!end) { // failed to find closing brace, abort. ERROR("unexpected end of string in '%s', looking for }\n", src); goto err; } prop[prop_len] = '\0'; c++; } else if (*c) { while (*c && prop_len < PROP_NAME_MAX) prop[prop_len++] = *(c++); if (prop_len == PROP_NAME_MAX && *c != '\0') { ERROR("prop name too long in '%s'\n", src); goto err; } prop[prop_len] = '\0'; prop_name = std::string(c, end); c = end + 1; } else { prop_name = c; ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n", prop); c); c += prop_name.size(); } if (prop_len == 0) { if (prop_name.empty()) { ERROR("invalid zero-length prop name in '%s'\n", src); goto err; } std::string prop_val = property_get(prop); std::string prop_val = property_get(prop_name.c_str()); if (prop_val.empty()) { ERROR("property '%s' doesn't exist while expanding '%s'\n", prop, src); prop_name.c_str(), src); goto err; } ret = push_chars(&dst_ptr, &left, prop_val.c_str(), prop_val.size()); if (ret < 0) goto err_nospace; dst->append(prop_val); src_ptr = c; continue; } *dst_ptr = '\0'; return 0; err_nospace: ERROR("destination buffer overflow while expanding '%s'\n", src); err: return -1; } static void parse_import(struct parse_state *state, int nargs, char **args) { struct listnode *import_list = (listnode*) state->priv; char conf_file[PATH_MAX]; int ret; if (nargs != 2) { ERROR("single argument needed for import\n"); return; } ret = expand_props(conf_file, args[1], sizeof(conf_file)); std::string conf_file; int ret = expand_props(args[1], &conf_file); if (ret) { ERROR("error while handling import on line '%d' in '%s'\n", state->line, state->filename); Loading @@ -347,7 +304,9 @@ static void parse_import(struct parse_state *state, int nargs, char **args) } struct import* import = (struct import*) calloc(1, sizeof(struct import)); import->filename = strdup(conf_file); import->filename = strdup(conf_file.c_str()); struct listnode *import_list = (listnode*) state->priv; list_add_tail(import_list, &import->list); INFO("Added '%s' to import list\n", import->filename); } Loading
init/init_parser.h +3 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #ifndef _INIT_INIT_PARSER_H_ #define _INIT_INIT_PARSER_H_ #include <string> #define INIT_PARSER_MAXARGS 64 struct action; Loading @@ -32,7 +34,7 @@ void queue_all_property_triggers(); void queue_builtin_action(int (*func)(int nargs, char **args), const char *name); bool init_parse_config_file(const char* path); int expand_props(char *dst, const char *src, int len); int expand_props(const char *src, std::string *dst); service* make_exec_oneshot_service(int argc, char** argv); Loading