Merge changes Ic446c026,I86568a5b am: 51b7cb006f am: 41dbec9791 am: fea35fc7e4
am: d0ea8a98cd

Change-Id: I4f8fe713f768748e521ad4d6c18edf0e4b382591
diff --git a/init/Android.mk b/init/Android.mk
index bfdc664..e9d2f3b 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -144,6 +144,7 @@
 LOCAL_SRC_FILES := \
     devices_test.cpp \
     init_parser_test.cpp \
+    init_test.cpp \
     property_service_test.cpp \
     util_test.cpp \
 
@@ -155,7 +156,7 @@
 LOCAL_STATIC_LIBRARIES := libinit
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror
+LOCAL_CPPFLAGS := -Wall -Wextra -Werror -std=gnu++1z
 include $(BUILD_NATIVE_TEST)
 
 
diff --git a/init/action.cpp b/init/action.cpp
index c128968..8d49e2a 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -363,7 +363,7 @@
     }
 }
 
-bool ActionParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                 int line, std::string* err) {
     std::vector<std::string> triggers(args.begin() + 1, args.end());
     if (triggers.size() < 1) {
@@ -380,13 +380,12 @@
     return true;
 }
 
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args, int line,
-                                    std::string* err) {
-    return action_ ? action_->AddCommand(args, line, err) : false;
+bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    return action_ ? action_->AddCommand(std::move(args), line, err) : false;
 }
 
 void ActionParser::EndSection() {
     if (action_ && action_->NumCommands() > 0) {
-        ActionManager::GetInstance().AddAction(std::move(action_));
+        action_manager_->AddAction(std::move(action_));
     }
 }
diff --git a/init/action.h b/init/action.h
index 25e5a3e..c528a7c 100644
--- a/init/action.h
+++ b/init/action.h
@@ -88,9 +88,12 @@
 };
 
 class ActionManager {
-public:
+  public:
     static ActionManager& GetInstance();
 
+    // Exposed for testing
+    ActionManager();
+
     void AddAction(std::unique_ptr<Action> action);
     void QueueEventTrigger(const std::string& trigger);
     void QueuePropertyTrigger(const std::string& name, const std::string& value);
@@ -100,9 +103,7 @@
     bool HasMoreCommands() const;
     void DumpState() const;
 
-private:
-    ActionManager();
-
+  private:
     ActionManager(ActionManager const&) = delete;
     void operator=(ActionManager const&) = delete;
 
@@ -114,16 +115,15 @@
 
 class ActionParser : public SectionParser {
   public:
-    ActionParser() : action_(nullptr) {
-    }
-    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+    ActionParser(ActionManager* action_manager)
+        : action_manager_(action_manager), action_(nullptr) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
     void EndSection() override;
-    void EndFile(const std::string&) override {
-    }
 
   private:
+    ActionManager* action_manager_;
     std::unique_ptr<Action> action_;
 };
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index f687b6c..04cf4dc 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -390,7 +390,7 @@
 
     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
-    if (false) parser.DumpState();
+    if (false) DumpState();
 }
 
 /* mount_fstab
@@ -855,7 +855,7 @@
     return e4crypt_do_init_user0();
 }
 
-BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map builtin_functions = {
diff --git a/init/builtins.h b/init/builtins.h
index 53f4a71..e1f0567 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -17,19 +17,20 @@
 #ifndef _INIT_BUILTINS_H
 #define _INIT_BUILTINS_H
 
+#include <functional>
 #include <map>
 #include <string>
 #include <vector>
 
 #include "keyword_map.h"
 
-using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
 class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
-public:
-    BuiltinFunctionMap() {
-    }
-private:
-    Map& map() const override;
+  public:
+    BuiltinFunctionMap() {}
+
+  private:
+    const Map& map() const override;
 };
 
 #endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index f66b2ba..99275e5 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -20,7 +20,7 @@
 
 #include "util.h"
 
-bool ImportParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                 int line, std::string* err) {
     if (args.size() != 2) {
         *err = "single argument needed for import\n";
@@ -35,16 +35,18 @@
     }
 
     LOG(INFO) << "Added '" << conf_file << "' to import list";
-    imports_.emplace_back(std::move(conf_file));
+    if (filename_.empty()) filename_ = filename;
+    imports_.emplace_back(std::move(conf_file), line);
     return true;
 }
 
-void ImportParser::EndFile(const std::string& filename) {
+void ImportParser::EndFile() {
     auto current_imports = std::move(imports_);
     imports_.clear();
-    for (const auto& s : current_imports) {
-        if (!Parser::GetInstance().ParseConfig(s)) {
-            PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
+    for (const auto& [import, line_num] : current_imports) {
+        if (!parser_->ParseConfig(import)) {
+            PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
+                        << "'";
         }
     }
 }
diff --git a/init/import_parser.h b/init/import_parser.h
index e15d555..45cbfad 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -24,20 +24,17 @@
 
 class ImportParser : public SectionParser {
   public:
-    ImportParser()  {
-    }
-    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+    ImportParser(Parser* parser) : parser_(parser) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args, int line,
-                          std::string* err) override {
-        return true;
-    }
-    void EndSection() override {
-    }
-    void EndFile(const std::string& filename) override;
+    void EndFile() override;
 
   private:
-    std::vector<std::string> imports_;
+    Parser* parser_;
+    // Store filename for later error reporting.
+    std::string filename_;
+    // Vector of imports and their line numbers for later error reporting.
+    std::vector<std::pair<std::string, int>> imports_;
 };
 
 #endif
diff --git a/init/init.cpp b/init/init.cpp
index a857377..09373de 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -96,6 +96,11 @@
 static std::string wait_prop_name;
 static std::string wait_prop_value;
 
+void DumpState() {
+    ServiceManager::GetInstance().DumpState();
+    ActionManager::GetInstance().DumpState();
+}
+
 void register_epoll_handler(int fd, void (*fn)()) {
     epoll_event ev;
     ev.events = EPOLLIN;
@@ -1429,10 +1434,13 @@
     const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
 
+    ActionManager& am = ActionManager::GetInstance();
+    ServiceManager& sm = ServiceManager::GetInstance();
     Parser& parser = Parser::GetInstance();
-    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
-    parser.AddSectionParser("on", std::make_unique<ActionParser>());
-    parser.AddSectionParser("import", std::make_unique<ImportParser>());
+
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
     std::string bootscript = GetProperty("ro.boot.init_rc", "");
     if (bootscript.empty()) {
         parser.ParseConfig("/init.rc");
@@ -1450,9 +1458,7 @@
 
     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
-    if (false) parser.DumpState();
-
-    ActionManager& am = ActionManager::GetInstance();
+    if (false) DumpState();
 
     am.QueueEventTrigger("early-init");
 
@@ -1487,10 +1493,10 @@
         // By default, sleep until something happens.
         int epoll_timeout_ms = -1;
 
-        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
             am.ExecuteOneCommand();
         }
-        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
             restart_processes();
 
             // If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index 1da3350..6add75f 100644
--- a/init/init.h
+++ b/init/init.h
@@ -34,4 +34,6 @@
 
 bool start_waiting_for_property(const char *name, const char *value);
 
+void DumpState();
+
 #endif  /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index b425497..5c7af79 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -17,14 +17,12 @@
 #include "init_parser.h"
 
 #include <dirent.h>
-#include <fcntl.h>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "action.h"
 #include "parser.h"
-#include "service.h"
+#include "util.h"
 
 Parser::Parser() {
 }
@@ -71,13 +69,14 @@
                 }
                 section_parser = section_parsers_[args[0]].get();
                 std::string ret_err;
-                if (!section_parser->ParseSection(args, state.filename, state.line, &ret_err)) {
+                if (!section_parser->ParseSection(std::move(args), state.filename, state.line,
+                                                  &ret_err)) {
                     parse_error(&state, "%s\n", ret_err.c_str());
                     section_parser = nullptr;
                 }
             } else if (section_parser) {
                 std::string ret_err;
-                if (!section_parser->ParseLineSection(args, state.line, &ret_err)) {
+                if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
                     parse_error(&state, "%s\n", ret_err.c_str());
                 }
             }
@@ -100,8 +99,8 @@
 
     data.push_back('\n'); // TODO: fix parse_config.
     ParseData(path, data);
-    for (const auto& sp : section_parsers_) {
-        sp.second->EndFile(path);
+    for (const auto& [section_name, section_parser] : section_parsers_) {
+        section_parser->EndFile();
     }
 
     LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
@@ -141,8 +140,3 @@
     }
     return ParseConfigFile(path);
 }
-
-void Parser::DumpState() const {
-    ServiceManager::GetInstance().DumpState();
-    ActionManager::GetInstance().DumpState();
-}
diff --git a/init/init_parser.h b/init/init_parser.h
index 64f0cac..fe70d7d 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -22,25 +22,50 @@
 #include <string>
 #include <vector>
 
+//  SectionParser is an interface that can parse a given 'section' in init.
+//
+//  You can implement up to 4 functions below, with ParseSection() being mandatory.
+//  The first two function return bool with false indicating a failure and has a std::string* err
+//  parameter into which an error string can be written.  It will be reported along with the
+//  filename and line number of where the error occurred.
+//
+//  1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+//                       int line, std::string* err)
+//    This function is called when a section is first encountered.
+//
+//  2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+//    This function is called on each subsequent line until the next section is encountered.
+//
+//  3) bool EndSection()
+//    This function is called either when a new section is found or at the end of the file.
+//    It indicates that parsing of the current section is complete and any relevant objects should
+//    be committed.
+//
+//  4) bool EndFile()
+//    This function is called at the end of the file.
+//    It indicates that the parsing has completed and any relevant objects should be committed.
+
 class SectionParser {
-public:
-    virtual ~SectionParser() {
-    }
-    virtual bool ParseSection(const std::vector<std::string>& args, const std::string& filename,
+  public:
+    virtual ~SectionParser() {}
+    virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line, std::string* err) = 0;
-    virtual bool ParseLineSection(const std::vector<std::string>& args, int line,
-                                  std::string* err) = 0;
-    virtual void EndSection() = 0;
-    virtual void EndFile(const std::string& filename) = 0;
+    virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
+    virtual void EndSection(){};
+    virtual void EndFile(){};
 };
 
 class Parser {
-public:
+  public:
     static Parser& GetInstance();
-    void DumpState() const;
+
+    // Exposed for testing
+    Parser();
+
     bool ParseConfig(const std::string& path);
     void AddSectionParser(const std::string& name,
                           std::unique_ptr<SectionParser> parser);
+
     void set_is_system_etc_init_loaded(bool loaded) {
         is_system_etc_init_loaded_ = loaded;
     }
@@ -54,9 +79,7 @@
     bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
     bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
 
-private:
-    Parser();
-
+  private:
     void ParseData(const std::string& filename, const std::string& data);
     bool ParseConfigFile(const std::string& path);
     bool ParseConfigDir(const std::string& path);
diff --git a/init/init_test.cpp b/init/init_test.cpp
new file mode 100644
index 0000000..3da14b5
--- /dev/null
+++ b/init/init_test.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "action.h"
+#include "builtins.h"
+#include "import_parser.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+#include "util.h"
+
+class TestFunctionMap : public KeywordMap<BuiltinFunction> {
+  public:
+    // Helper for argument-less functions
+    using BuiltinFunctionNoArgs = std::function<void(void)>;
+    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+        Add(name, 0, 0, [function](const std::vector<std::string>&) {
+            function();
+            return 0;
+        });
+    }
+
+    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+             const BuiltinFunction function) {
+        builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
+    }
+
+  private:
+    Map builtin_functions_ = {};
+
+    const Map& map() const override { return builtin_functions_; }
+};
+
+using ActionManagerCommand = std::function<void(ActionManager&)>;
+
+void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+              const std::vector<ActionManagerCommand>& commands) {
+    ActionManager am;
+
+    Action::set_function_map(&test_function_map);
+
+    Parser parser;
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+    ASSERT_TRUE(parser.ParseConfig(init_script_file));
+
+    for (const auto& command : commands) {
+        command(am);
+    }
+
+    while (am.HasMoreCommands()) {
+        am.ExecuteOneCommand();
+    }
+}
+
+void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+                  const std::vector<ActionManagerCommand>& commands) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+    TestInit(tf.path, test_function_map, commands);
+}
+
+TEST(init, SimpleEventTrigger) {
+    bool expect_true = false;
+    std::string init_script =
+        R"init(
+on boot
+pass_test
+)init";
+
+    TestFunctionMap test_function_map;
+    test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInitText(init_script, test_function_map, commands);
+
+    EXPECT_TRUE(expect_true);
+}
+
+TEST(init, EventTriggerOrder) {
+    std::string init_script =
+        R"init(
+on boot
+execute_first
+
+on boot && property:ro.hardware=*
+execute_second
+
+on boot
+execute_third
+
+)init";
+
+    int num_executed = 0;
+    TestFunctionMap test_function_map;
+    test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
+    test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
+    test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInitText(init_script, test_function_map, commands);
+}
+
+TEST(init, EventTriggerOrderMultipleFiles) {
+    // 6 total files, which should have their triggers executed in the following order:
+    // 1: start - original script parsed
+    // 2: first_import - immediately imported by first_script
+    // 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import
+    // 4: a_import - file imported by dir_a
+    // 5: dir_b - file named 'b.rc' in dir
+    // 6: last_import - imported after dir is imported
+
+    TemporaryFile first_import;
+    ASSERT_TRUE(first_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", first_import.fd));
+
+    TemporaryFile dir_a_import;
+    ASSERT_TRUE(dir_a_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 4", dir_a_import.fd));
+
+    TemporaryFile last_import;
+    ASSERT_TRUE(last_import.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 6", last_import.fd));
+
+    TemporaryDir dir;
+    // clang-format off
+    std::string dir_a_script = "import " + std::string(dir_a_import.path) + "\n"
+                               "on boot\n"
+                               "execute 3";
+    // clang-format on
+    // write_file() ensures the right mode is set
+    ASSERT_TRUE(write_file(std::string(dir.path) + "/a.rc", dir_a_script));
+
+    ASSERT_TRUE(write_file(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
+
+    // clang-format off
+    std::string start_script = "import " + std::string(first_import.path) + "\n"
+                               "import " + std::string(dir.path) + "\n"
+                               "import " + std::string(last_import.path) + "\n"
+                               "on boot\n"
+                               "execute 1";
+    // clang-format on
+    TemporaryFile start;
+    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+    int num_executed = 0;
+    auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+        EXPECT_EQ(2U, args.size());
+        EXPECT_EQ(++num_executed, std::stoi(args[1]));
+        return 0;
+    };
+
+    TestFunctionMap test_function_map;
+    test_function_map.Add("execute", 1, 1, execute_command);
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+
+    TestInit(start.path, test_function_map, commands);
+
+    EXPECT_EQ(6, num_executed);
+}
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 693d82a..2b91260 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -24,9 +24,9 @@
 
 template <typename Function>
 class KeywordMap {
-public:
+  public:
     using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
-    using Map = const std::map<std::string, FunctionInfo>;
+    using Map = std::map<std::string, FunctionInfo>;
 
     virtual ~KeywordMap() {
     }
@@ -68,10 +68,10 @@
         return std::get<Function>(function_info);
     }
 
-private:
-//Map of keyword ->
-//(minimum number of arguments, maximum number of arguments, function pointer)
-    virtual Map& map() const = 0;
+  private:
+    // Map of keyword ->
+    // (minimum number of arguments, maximum number of arguments, function pointer)
+    virtual const Map& map() const = 0;
 };
 
 #endif
diff --git a/init/service.cpp b/init/service.cpp
index c0745e3..ab404a1 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -534,14 +534,14 @@
 }
 
 class Service::OptionParserMap : public KeywordMap<OptionParser> {
-public:
-    OptionParserMap() {
-    }
-private:
-    Map& map() const override;
+  public:
+    OptionParserMap() {}
+
+  private:
+    const Map& map() const override;
 };
 
-Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
+const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
     static const Map option_parsers = {
@@ -877,11 +877,6 @@
 }
 
 void ServiceManager::AddService(std::unique_ptr<Service> service) {
-    Service* old_service = FindServiceByName(service->name());
-    if (old_service) {
-        LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
-        return;
-    }
     services_.emplace_back(std::move(service));
 }
 
@@ -1095,7 +1090,7 @@
     }
 }
 
-bool ServiceParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
+bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                  int line, std::string* err) {
     if (args.size() < 3) {
         *err = "services must have a name and a program";
@@ -1108,19 +1103,24 @@
         return false;
     }
 
+    Service* old_service = service_manager_->FindServiceByName(name);
+    if (old_service) {
+        *err = "ignored duplicate definition of service '" + name + "'";
+        return false;
+    }
+
     std::vector<std::string> str_args(args.begin() + 2, args.end());
     service_ = std::make_unique<Service>(name, str_args);
     return true;
 }
 
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, int line,
-                                     std::string* err) {
-    return service_ ? service_->ParseLine(args, err) : false;
+bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+    return service_ ? service_->ParseLine(std::move(args), err) : false;
 }
 
 void ServiceParser::EndSection() {
     if (service_) {
-        ServiceManager::GetInstance().AddService(std::move(service_));
+        service_manager_->AddService(std::move(service_));
     }
 }
 
diff --git a/init/service.h b/init/service.h
index 6fe0258..634fe4e 100644
--- a/init/service.h
+++ b/init/service.h
@@ -213,16 +213,17 @@
 
 class ServiceParser : public SectionParser {
   public:
-    ServiceParser() : service_(nullptr) {}
-    bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
+    ServiceParser(ServiceManager* service_manager)
+        : service_manager_(service_manager), service_(nullptr) {}
+    bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
                       std::string* err) override;
-    bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
+    bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
     void EndSection() override;
-    void EndFile(const std::string&) override {}
 
   private:
     bool IsValidName(const std::string& name) const;
 
+    ServiceManager* service_manager_;
     std::unique_ptr<Service> service_;
 };