Switch to side-by-side view

--- a/src/main.cxx
+++ b/src/main.cxx
@@ -43,9 +43,9 @@
 #include "upmpd.hxx"
 #include "mediaserver/mediaserver.hxx"
 #include "mediaserver/contentdirectory.hxx"
-#include "httpfs.hxx"
 #include "upmpdutils.hxx"
 #include "pathut.h"
+#include "readfile.h"
 
 using namespace std;
 using namespace UPnPP;
@@ -81,8 +81,8 @@
     "-P upport    \t specify port number to be used for UPnP\n"
     "-O 0|1\t decide if we run and export the OpenHome services\n"
     "-v      \tprint version info\n"
-    "-m <0|1|2|3> media server mode "
-    "(default, forked|only renderer|only media|combined)\n"
+    "-m <0|1|2|3|4> media server mode "
+    "(default, forked|only renderer|only media|combined/embedded|combined/multidev)\n"
     "\n"
     ;
 
@@ -95,18 +95,20 @@
 // control points are confused by embedded devices.
 // 
 // - -m 0, default, Forked: this is for the main process, which will
-//   implement a Media Renderer device, and, if needed, fork/exec the
-//   Media Server (with option -m 2)
+//    implement a Media Renderer device, and, if needed, fork/exec the
+//    Media Server (with option -m 2)
 // - -m 1, RdrOnly: for the main instance: be a Renderer, do not start the
-//   Media Server even if the configuration indicates it is needed
-//   (this is not used in normal situations, just edit the config
-//   instead!)
+//    Media Server even if the configuration indicates it is needed
+//    (this is not used in normal situations, just edit the config
+//    instead!)
 // - -m 2, MSOnly Media Server only, this is for the process forked/execed
-//   by a main Renderer process, or a standalone Media Server.
+//    by a main Renderer process, or a standalone Media Server.
 // - -m 3, Combined: for the main process: implement the Media Server
-//   as an embedded device. This works just fine with, for example,
-//   upplay, but confuses most of the other Control Points.
-enum MSMode {Forked, RdrOnly, MSOnly, Combined};
+//    as an embedded device. This works just fine with, for example,
+//    upplay, but confuses most of the other Control Points.
+// - -m 4, Combined: for the main process: implement the Media Server
+//    as a separate root device. Only works with a modified libupnp.
+enum MSMode {Forked, RdrOnly, MSOnly, CombinedEmbedded, CombinedMultiDev};
 
 static void
 versionInfo(FILE *fp)
@@ -150,8 +152,16 @@
     }
 };
 
+// Ad-hoc struct to hold the input identification parameters for the device(s)
+struct UDevIds {
+    std::string fname;
+    std::string uuid;
+    std::string fnameMS;
+    std::string uuidMS;
+};
+
 // Static for cleanup in sig handler.
-static UpnpDevice *dev;
+static vector<UpnpDevice *> devs;
 
 string g_datadir(DATADIR "/");
 string g_cachedir("/var/cache/upmpdcli");
@@ -165,7 +175,9 @@
 static void onsig(int)
 {
     LOGDEB("Got sig" << endl);
-    dev->shouldExit();
+    for (auto& dev : devs) {
+        dev->shouldExit();
+    }
 }
 
 static const int catchedSigs[] = {SIGINT, SIGQUIT, SIGTERM};
@@ -247,8 +259,9 @@
     unsigned short upport = 0;
     string upnpip;
     int msm = 0;
-    bool inprocessms = false;
-    bool msonly = false;
+    bool inprocessms{false};
+    bool msonly{false};
+    bool msroot{false};
     
     const char *cp;
     if ((cp = getenv("UPMPD_HOST")))
@@ -308,7 +321,7 @@
     b1: argc--; argv++;
     }
 
-    if (argc != 0 || msm < 0 || msm > 3) {
+    if (argc != 0 || msm < 0 || msm > 4) {
         Usage();
     }
     MSMode arg_msmode = MSMode(msm);
@@ -442,9 +455,15 @@
         inprocessms = true;
         msonly = true;
         break;
-    case Combined:
+    case CombinedEmbedded:
         inprocessms = true;
         msonly = false;
+        msroot = false;
+        break;
+    case CombinedMultiDev:
+        inprocessms = true;
+        msonly = false;
+        msroot = true;
         break;
     case RdrOnly:
     case Forked:
@@ -690,15 +709,8 @@
         pidfilename = pidfilename + "-ms";
     }
 
-    // Initialize the data we serve through HTTP (device and service
-    // descriptions, icons, presentation page, etc.)
-    unordered_map<string, VDirContent> files;
-    if (!initHttpFs(files, g_datadir, ids, enableAV, enableOH,
-                    !senderpath.empty(), inprocessms, msonly,
-                    iconpath, presentationhtml)) {
-        exit(1);
-    }
-
+    opts.iconpath = iconpath;
+    opts.presentationhtml = presentationhtml;
     if (ownqueue)
         opts.options |= UpMpd::upmpdOwnQueue;
     if (enableOH)
@@ -734,48 +746,49 @@
     if (!enableAV)
         opts.options |= UpMpd::upmpdNoAV;
 
+    setupsigs();
+
     UpMpd *mediarenderer{nullptr};
     if (!msonly) {
         mediarenderer = new UpMpd(string("uuid:") + ids.uuid, ids.fname,
-                                  ohProductDesc, files, mpdclip, opts);
-    }
-
-    MediaServer *mediaserver = nullptr;
-    unordered_map<string, VDirContent> emptyfiles =
-        unordered_map<string, VDirContent>();
-    unordered_map<string, VDirContent> *msfiles = &emptyfiles;
+                                  ohProductDesc, mpdclip, opts);
+        devs.push_back(mediarenderer);
+    }
+
+    MediaServer *mediaserver{nullptr};
     
     if (inprocessms) {
-        if (msonly) {
-            msfiles = &files;
-        }
-	// Create the Media Server embedded device object. There needs
-	// be no reference to the root object because there can be
-	// only one (libupnp restriction)
-	mediaserver = new MediaServer(string("uuid:") + ids.uuidMS, ids.fnameMS,
-                                      enableMediaServer, *msfiles);
+	// Create the Media Server device. If msonly is set, both
+	// branches do the same thing and create a root device
+	// (mediarenderer is null).
+        // The multidev modified libupnp is needed for using 2 root devices
+        mediaserver = new MediaServer(msroot?nullptr:mediarenderer, string("uuid:") +
+                                      ids.uuidMS,ids.fnameMS, enableMediaServer);
+        devs.push_back(mediaserver);
+        LOGDEB("Media server event loop" << endl);
     } else if (enableMediaServer) {
         startMsOnlyProcess();
     }
-    
-    // And forever generate state change events.
-    LOGDEB("Entering event loop" << endl);
-    setupsigs();
-    if (msonly) {
-        assert(nullptr != mediaserver);
-        dev = mediaserver;
-        LOGDEB("Media server event loop" << endl);
-        if (enableMediaServer) {
-            mediaserver->eventloop();
-        } else {
-            pause();
-        }
-    } else {
+    if (!msonly) {
         LOGDEB("Renderer event loop" << endl);
-        assert(nullptr != mediarenderer);
-        dev = mediarenderer;
-        mediarenderer->eventloop();
-    }
+        mediarenderer->startloop();
+    }
+    if (inprocessms && enableMediaServer) {
+        mediaserver->startloop();
+    }
+    pause();
     LOGDEB("Event loop returned" << endl);
     return 0;
 }
+
+// Read file from datadir
+bool readLibFile(const std::string& name, std::string& contents)
+{
+    string path = path_cat(g_datadir, name);
+    string reason;
+    if (!file_to_string(path, contents, &reason)) {
+        LOGERR("readLibFile: error reading " << name << " : " << reason << endl);
+        return false;
+    }
+    return true;
+}