dirload_service_set: add support for multiple service directories.
authorDavin McCall <davmac@davmac.org>
Mon, 14 May 2018 09:24:01 +0000 (10:24 +0100)
committerDavin McCall <davmac@davmac.org>
Mon, 14 May 2018 18:00:04 +0000 (19:00 +0100)
src/includes/service.h
src/load-service.cc

index b13e80e4efa177dfde553342b5288aa623ffe29b..318deefd123766dbfb386b45c9baf1b096a0d6f1 100644 (file)
@@ -845,13 +845,55 @@ class service_set
     }
 };
 
+// A service directory entry, tracking the directory as a nul-terminated string, which may either
+// be static or dynamically allocated (via new char[...]).
+class service_dir_entry
+{
+    const char *dir;
+    bool dir_dyn_allocd;  // dynamically allocated?
+
+    public:
+    service_dir_entry(const char *dir_p, bool dir_dyn_allocd_p) :
+        dir(dir_p), dir_dyn_allocd(dir_dyn_allocd_p)
+    { }
+
+    ~service_dir_entry()
+    {
+        if (dir_dyn_allocd) {
+            delete[] dir;
+        }
+    }
+
+    const char *get_dir() const
+    {
+        return dir;
+    }
+};
+
+// A service set which loads services from one of several service directories.
 class dirload_service_set : public service_set
 {
-    const char *service_dir;  // directory containing service descriptions
+    std::vector<service_dir_entry> service_dirs; // directories containing service descriptions
 
     public:
-    dirload_service_set(const char *service_dir_p) : service_set(), service_dir(service_dir_p)
+    dirload_service_set() : service_set()
+    {
+        // nothing to do.
+    }
+
+    // Construct a dirload_service_set which loads services from the specified directory. The
+    // directory specified can be dynamically allocated via "new char[...]" (dyn_allocd == true)
+    // or statically allocated.
+    dirload_service_set(const char *service_dir_p, bool dyn_allocd = false) : service_set()
+    {
+        service_dirs.emplace_back(service_dir_p, false);
+    }
+
+    // Append a directory to the list of service directories, so that it is searched last for
+    // service description files.
+    void add_service_dir(const char *service_dir_p, bool dyn_allocd = true)
     {
+        service_dirs.emplace_back(service_dir_p, dyn_allocd);
     }
 
     service_record *load_service(const char *name) override;
index 8e064b702d1bbc321d0790b81c24e14f51da9c01..5a76a54bbc752844a0deb51c580a51d45d3e6ddb 100644 (file)
@@ -377,12 +377,19 @@ service_record * dirload_service_set::load_service(const char * name)
         return rval;
     }
 
-    // Couldn't find one. Have to load it.    
-    string service_filename = service_dir;
-    if (*(service_filename.rbegin()) != '/') {
-        service_filename += '/';
+    ifstream service_file;
+
+    // Couldn't find one. Have to load it.
+    for (auto &service_dir : service_dirs) {
+        string service_filename = service_dir.get_dir();
+        if (*(service_filename.rbegin()) != '/') {
+            service_filename += '/';
+        }
+        service_filename += name;
+
+        service_file.open(service_filename.c_str(), ios::in);
+        if (service_file) break;
     }
-    service_filename += name;
     
     string command;
     list<pair<unsigned,unsigned>> command_offsets;
@@ -416,16 +423,8 @@ service_record * dirload_service_set::load_service(const char * name)
     gid_t run_as_gid = -1;
 
     string line;
-    ifstream service_file;
     service_file.exceptions(ios::badbit | ios::failbit);
     
-    try {
-        service_file.open(service_filename.c_str(), ios::in);
-    }
-    catch (std::ios_base::failure &exc) {
-        throw service_not_found(name);
-    }
-    
     // Add a dummy service record now to prevent infinite recursion in case of cyclic dependency.
     // We replace this with the real service later (or remove it if we find a configuration error).
     rval = new service_record(this, string(name));