Switch to side-by-side view

--- a/upmpd/upmpd.cxx
+++ b/upmpd/upmpd.cxx
@@ -1,18 +1,18 @@
 /* Copyright (C) 2014 J.F.Dockes
- *	 This program is free software; you can redistribute it and/or modify
- *	 it under the terms of the GNU General Public License as published by
- *	 the Free Software Foundation; either version 2 of the License, or
- *	 (at your option) any later version.
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
  *
- *	 This program is distributed in the hope that it will be useful,
- *	 but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	 GNU General Public License for more details.
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
  *
- *	 You should have received a copy of the GNU General Public License
- *	 along with this program; if not, write to the
- *	 Free Software Foundation, Inc.,
- *	 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
 #include <stdio.h>
@@ -51,37 +51,61 @@
 static const string dfltFriendlyName("UpMpd");
 string upmpdProtocolInfo;
 
+static UpnpDevice *dev;
+
+static void onsig(int)
+{
+    LOGDEB("Got sig" << endl);
+    dev->shouldExit();
+}
+
+static const int catchedSigs[] = {SIGINT, SIGQUIT, SIGTERM};
+
+static void setupsigs()
+{
+    struct sigaction action;
+    action.sa_handler = onsig;
+    action.sa_flags = 0;
+    sigemptyset(&action.sa_mask);
+    for (unsigned int i = 0; i < sizeof(catchedSigs) / sizeof(int); i++)
+        if (signal(catchedSigs[i], SIG_IGN) != SIG_IGN) {
+            if (sigaction(catchedSigs[i], &action, 0) < 0) {
+                perror("Sigaction failed");
+            }
+        }
+}
+
 // Note: if we ever need this to work without cxx11, there is this:
 // http://www.tutok.sk/fastgl/callback.html
 UpMpd::UpMpd(const string& deviceid, const string& friendlyname,
-			 const unordered_map<string, string>& xmlfiles,
-			 MPDCli *mpdcli, unsigned int opts, const string& cachefn)
-	: UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_mpds(0),
-	  m_options(opts),
-	  m_mcachefn(cachefn)
-{
-	// Note: the order is significant here as it will be used when
-	// calling the getStatus() methods, and we want AVTransport to
-	// update the mpd status for OHInfo
-	UpMpdRenderCtl *rdctl = new UpMpdRenderCtl(this);
-	m_services.push_back(rdctl);
-	m_services.push_back(new UpMpdAVTransport(this));
-	m_services.push_back(new UpMpdConMan(this));
-	if (m_options & upmpdDoOH) {
-		m_services.push_back(new OHProduct(this, friendlyname));
-		m_services.push_back(new OHInfo(this));
-		m_services.push_back(new OHTime(this));
-		m_services.push_back(new OHVolume(this, rdctl));
-		m_services.push_back(new OHPlaylist(this, rdctl));
-	}
+             const unordered_map<string, string>& xmlfiles,
+             MPDCli *mpdcli, unsigned int opts, const string& cachefn)
+    : UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_mpds(0),
+      m_options(opts),
+      m_mcachefn(cachefn)
+{
+    // Note: the order is significant here as it will be used when
+    // calling the getStatus() methods, and we want AVTransport to
+    // update the mpd status for OHInfo
+    UpMpdRenderCtl *rdctl = new UpMpdRenderCtl(this);
+    m_services.push_back(rdctl);
+    m_services.push_back(new UpMpdAVTransport(this));
+    m_services.push_back(new UpMpdConMan(this));
+    if (m_options & upmpdDoOH) {
+        m_services.push_back(new OHProduct(this, friendlyname));
+        m_services.push_back(new OHInfo(this));
+        m_services.push_back(new OHTime(this));
+        m_services.push_back(new OHVolume(this, rdctl));
+        m_services.push_back(new OHPlaylist(this, rdctl));
+    }
 }
 
 UpMpd::~UpMpd()
 {
-	for (vector<UpnpService*>::iterator it = m_services.begin();
-		 it != m_services.end(); it++) {
-		delete(*it);
-	}
+    for (vector<UpnpService*>::iterator it = m_services.begin();
+         it != m_services.end(); it++) {
+        delete(*it);
+    }
 }
 
 const MpdStatus& UpMpd::getMpdStatus()
@@ -96,50 +120,50 @@
 #include "conftree.hxx"
 
 static const string ohDesc(
-	"<service>"
-	"  <serviceType>urn:av-openhome-org:service:Product:1</serviceType>"
-	"  <serviceId>urn:av-openhome-org:serviceId:Product</serviceId>"
-	"  <SCPDURL>/OHProduct.xml</SCPDURL>"
-	"  <controlURL>/ctl/OHProduct</controlURL>"
-	"  <eventSubURL>/evt/OHProduct</eventSubURL>"
-	"</service>"
-	"<service>"
-	"  <serviceType>urn:av-openhome-org:service:Info:1</serviceType>"
-	"  <serviceId>urn:av-openhome-org:serviceId:Info</serviceId>"
-	"  <SCPDURL>/OHInfo.xml</SCPDURL>"
-	"  <controlURL>/ctl/OHInfo</controlURL>"
-	"  <eventSubURL>/evt/OHInfo</eventSubURL>"
-	"</service>"
-	"<service>"
-	"  <serviceType>urn:av-openhome-org:service:Time:1</serviceType>"
-	"  <serviceId>urn:av-openhome-org:serviceId:Time</serviceId>"
-	"  <SCPDURL>/OHTime.xml</SCPDURL>"
-	"  <controlURL>/ctl/OHTime</controlURL>"
-	"  <eventSubURL>/evt/OHTime</eventSubURL>"
-	"</service>"
-	"<service>"
-	"  <serviceType>urn:av-openhome-org:service:Volume:1</serviceType>"
-	"  <serviceId>urn:av-openhome-org:serviceId:Volume</serviceId>"
-	"  <SCPDURL>/OHVolume.xml</SCPDURL>"
-	"  <controlURL>/ctl/OHVolume</controlURL>"
-	"  <eventSubURL>/evt/OHVolume</eventSubURL>"
-	"</service>"
-	"<service>"
-	"  <serviceType>urn:av-openhome-org:service:Playlist:1</serviceType>"
-	"  <serviceId>urn:av-openhome-org:serviceId:Playlist</serviceId>"
-	"  <SCPDURL>/OHPlaylist.xml</SCPDURL>"
-	"  <controlURL>/ctl/OHPlaylist</controlURL>"
-	"  <eventSubURL>/evt/OHPlaylist</eventSubURL>"
-	"</service>"
-	);
+    "<service>"
+    "  <serviceType>urn:av-openhome-org:service:Product:1</serviceType>"
+    "  <serviceId>urn:av-openhome-org:serviceId:Product</serviceId>"
+    "  <SCPDURL>/OHProduct.xml</SCPDURL>"
+    "  <controlURL>/ctl/OHProduct</controlURL>"
+    "  <eventSubURL>/evt/OHProduct</eventSubURL>"
+    "</service>"
+    "<service>"
+    "  <serviceType>urn:av-openhome-org:service:Info:1</serviceType>"
+    "  <serviceId>urn:av-openhome-org:serviceId:Info</serviceId>"
+    "  <SCPDURL>/OHInfo.xml</SCPDURL>"
+    "  <controlURL>/ctl/OHInfo</controlURL>"
+    "  <eventSubURL>/evt/OHInfo</eventSubURL>"
+    "</service>"
+    "<service>"
+    "  <serviceType>urn:av-openhome-org:service:Time:1</serviceType>"
+    "  <serviceId>urn:av-openhome-org:serviceId:Time</serviceId>"
+    "  <SCPDURL>/OHTime.xml</SCPDURL>"
+    "  <controlURL>/ctl/OHTime</controlURL>"
+    "  <eventSubURL>/evt/OHTime</eventSubURL>"
+    "</service>"
+    "<service>"
+    "  <serviceType>urn:av-openhome-org:service:Volume:1</serviceType>"
+    "  <serviceId>urn:av-openhome-org:serviceId:Volume</serviceId>"
+    "  <SCPDURL>/OHVolume.xml</SCPDURL>"
+    "  <controlURL>/ctl/OHVolume</controlURL>"
+    "  <eventSubURL>/evt/OHVolume</eventSubURL>"
+    "</service>"
+    "<service>"
+    "  <serviceType>urn:av-openhome-org:service:Playlist:1</serviceType>"
+    "  <serviceId>urn:av-openhome-org:serviceId:Playlist</serviceId>"
+    "  <SCPDURL>/OHPlaylist.xml</SCPDURL>"
+    "  <controlURL>/ctl/OHPlaylist</controlURL>"
+    "  <eventSubURL>/evt/OHPlaylist</eventSubURL>"
+    "</service>"
+    );
 
 static char *thisprog;
 
 static int op_flags;
 #define OPT_MOINS 0x1
-#define OPT_h	  0x2
-#define OPT_p	  0x4
-#define OPT_d	  0x8
+#define OPT_h     0x2
+#define OPT_p     0x4
+#define OPT_d     0x8
 #define OPT_D     0x10
 #define OPT_c     0x20
 #define OPT_l     0x40
@@ -150,24 +174,24 @@
 #define OPT_O     0x800
 
 static const char usage[] = 
-"-c configfile \t configuration file to use\n"
-"""""""""""""""""""""""""""""-h host    \t specify host MPD is running on\n"""""""""""""""""""""""""""""
-"-p port     \t specify MPD port\n"
-"-d logfilename\t debug messages to\n"
-"-l loglevel\t  log level (0-6)\n"
-"-D    \t run as a daemon\n"
-"-f friendlyname\t define device displayed name\n"
-"-q 0|1\t if set, we own the mpd queue, else avoid clearing it whenever we feel like it\n"
-"-i iface    \t specify network interface name to be used for UPnP\n"
-"-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"
-"\n"
-			;
+    "-c configfile \t configuration file to use\n"
+    "-h host    \t specify host MPD is running on\n"
+    "-p port     \t specify MPD port\n"
+    "-d logfilename\t debug messages to\n"
+    "-l loglevel\t  log level (0-6)\n"
+    "-D    \t run as a daemon\n"
+    "-f friendlyname\t define device displayed name\n"
+    "-q 0|1\t if set, we own the mpd queue, else avoid clearing it whenever we feel like it\n"
+    "-i iface    \t specify network interface name to be used for UPnP\n"
+    "-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"
+    "\n"
+    ;
 static void
 Usage(void)
 {
-	fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
-	exit(1);
+    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
+    exit(1);
 }
 
 static string myDeviceUUID;
@@ -178,308 +202,304 @@
 // Our XML description data. !Keep description.xml first!
 static vector<const char *> xmlfilenames = 
 {
-	/* keep first */ "description.xml", /* keep first */
-	"RenderingControl.xml", "AVTransport.xml", "ConnectionManager.xml",
+    /* keep first */ "description.xml", /* keep first */
+    "RenderingControl.xml", "AVTransport.xml", "ConnectionManager.xml",
 };
 static vector<const char *> ohxmlfilenames = 
 {
-	"OHProduct.xml", "OHInfo.xml", "OHTime.xml", "OHVolume.xml", 
-	"OHPlaylist.xml",
+    "OHProduct.xml", "OHInfo.xml", "OHTime.xml", "OHVolume.xml", 
+    "OHPlaylist.xml",
 };
 
 
 int main(int argc, char *argv[])
 {
-	string mpdhost("localhost");
-	int mpdport = 6600;
-	string mpdpassword;
-	string logfilename;
-	int loglevel(upnppdebug::Logger::LLINF);
-	string configfile;
-	string friendlyname(dfltFriendlyName);
-	bool ownqueue = true;
-	bool openhome = true;
-	bool ohmetapersist = true;
-	string upmpdcliuser("upmpdcli");
-	string pidfilename("/var/run/upmpdcli.pid");
-	string iface;
-	unsigned short upport = 0;
-	string upnpip;
-
-	const char *cp;
-	if ((cp = getenv("UPMPD_HOST")))
-		mpdhost = cp;
-	if ((cp = getenv("UPMPD_PORT")))
-		mpdport = atoi(cp);
-	if ((cp = getenv("UPMPD_FRIENDLYNAME")))
-		friendlyname = atoi(cp);
-	if ((cp = getenv("UPMPD_CONFIG")))
-		configfile = cp;
-	if ((cp = getenv("UPMPD_UPNPIFACE")))
-		iface = cp;
-	if ((cp = getenv("UPMPD_UPNPPORT")))
-		upport = atoi(cp);
-
-	thisprog = argv[0];
-	argc--; argv++;
-	while (argc > 0 && **argv == '-') {
-		(*argv)++;
-		if (!(**argv))
-			Usage();
-		while (**argv)
-			switch (*(*argv)++) {
-			case 'c':	op_flags |= OPT_c; if (argc < 2)  Usage();
-				configfile = *(++argv); argc--; goto b1;
-			case 'D':	op_flags |= OPT_D; break;
-			case 'd':	op_flags |= OPT_d; if (argc < 2)  Usage();
-				logfilename = *(++argv); argc--; goto b1;
-			case 'f':	op_flags |= OPT_f; if (argc < 2)  Usage();
-				friendlyname = *(++argv); argc--; goto b1;
-			case 'h':	op_flags |= OPT_h; if (argc < 2)  Usage();
-				mpdhost = *(++argv); argc--; goto b1;
-			case 'i':	op_flags |= OPT_i; if (argc < 2)  Usage();
-				iface = *(++argv); argc--; goto b1;
-			case 'l':	op_flags |= OPT_l; if (argc < 2)  Usage();
-				loglevel = atoi(*(++argv)); argc--; goto b1;
-			case 'O': {
-				op_flags |= OPT_O; 
-				if (argc < 2)  Usage();
-				const char *cp =  *(++argv);
-				if (*cp == '1' || *cp == 't' || *cp == 'T' || *cp == 'y' || 
-					*cp == 'Y')
-					openhome = true;
-				argc--; goto b1;
-			}
-			case 'P':	op_flags |= OPT_P; if (argc < 2)  Usage();
-				upport = atoi(*(++argv)); argc--; goto b1;
-			case 'p':	op_flags |= OPT_p; if (argc < 2)  Usage();
-				mpdport = atoi(*(++argv)); argc--; goto b1;
-			case 'q':	op_flags |= OPT_q; if (argc < 2)  Usage();
-				ownqueue = atoi(*(++argv)) != 0; argc--; goto b1;
-			default: Usage();	break;
-			}
-	b1: argc--; argv++;
-	}
-
-	if (argc != 0)
-		Usage();
-
-	if (!configfile.empty()) {
-		ConfSimple config(configfile.c_str(), 1, true);
-		if (!config.ok()) {
-			cerr << "Could not open config: " << configfile << endl;
-			return 1;
-		}
-		string value;
-		if (!(op_flags & OPT_d))
-			config.get("logfilename", logfilename);
-		if (!(op_flags & OPT_f))
-			config.get("friendlyname", friendlyname);
-		if (!(op_flags & OPT_l) && config.get("loglevel", value))
-			loglevel = atoi(value.c_str());
-		if (!(op_flags & OPT_h))
-			config.get("mpdhost", mpdhost);
-		if (!(op_flags & OPT_p) && config.get("mpdport", value)) {
-			mpdport = atoi(value.c_str());
-		}
-		config.get("mpdpassword", mpdpassword);
-		if (!(op_flags & OPT_q) && config.get("ownqueue", value)) {
-			ownqueue = atoi(value.c_str()) != 0;
-		}
-		if (config.get("openhome", value)) {
-			openhome = atoi(value.c_str()) != 0;
-		}
-		if (config.get("ohmetapersist", value)) {
-			ohmetapersist = atoi(value.c_str()) != 0;
-		}
-		if (!(op_flags & OPT_i)) {
-			config.get("upnpiface", iface);
-			if (iface.empty()) {
-				config.get("upnpip", upnpip);
-			}
-		}
-		if (!(op_flags & OPT_P) && config.get("upnpport", value)) {
-			upport = atoi(value.c_str());
-		}
-	}
-
-	if (upnppdebug::Logger::getTheLog(logfilename) == 0) {
-		cerr << "Can't initialize log" << endl;
-		return 1;
-	}
-	upnppdebug::Logger::getTheLog("")->setLogLevel(upnppdebug::Logger::LogLevel(loglevel));
+    string mpdhost("localhost");
+    int mpdport = 6600;
+    string mpdpassword;
+    string logfilename;
+    int loglevel(upnppdebug::Logger::LLINF);
+    string configfile;
+    string friendlyname(dfltFriendlyName);
+    bool ownqueue = true;
+    bool openhome = true;
+    bool ohmetapersist = true;
+    string upmpdcliuser("upmpdcli");
+    string pidfilename("/var/run/upmpdcli.pid");
+    string iface;
+    unsigned short upport = 0;
+    string upnpip;
+
+    const char *cp;
+    if ((cp = getenv("UPMPD_HOST")))
+        mpdhost = cp;
+    if ((cp = getenv("UPMPD_PORT")))
+        mpdport = atoi(cp);
+    if ((cp = getenv("UPMPD_FRIENDLYNAME")))
+        friendlyname = atoi(cp);
+    if ((cp = getenv("UPMPD_CONFIG")))
+        configfile = cp;
+    if ((cp = getenv("UPMPD_UPNPIFACE")))
+        iface = cp;
+    if ((cp = getenv("UPMPD_UPNPPORT")))
+        upport = atoi(cp);
+
+    thisprog = argv[0];
+    argc--; argv++;
+    while (argc > 0 && **argv == '-') {
+        (*argv)++;
+        if (!(**argv))
+            Usage();
+        while (**argv)
+            switch (*(*argv)++) {
+            case 'c':   op_flags |= OPT_c; if (argc < 2)  Usage();
+                configfile = *(++argv); argc--; goto b1;
+            case 'D':   op_flags |= OPT_D; break;
+            case 'd':   op_flags |= OPT_d; if (argc < 2)  Usage();
+                logfilename = *(++argv); argc--; goto b1;
+            case 'f':   op_flags |= OPT_f; if (argc < 2)  Usage();
+                friendlyname = *(++argv); argc--; goto b1;
+            case 'h':   op_flags |= OPT_h; if (argc < 2)  Usage();
+                mpdhost = *(++argv); argc--; goto b1;
+            case 'i':   op_flags |= OPT_i; if (argc < 2)  Usage();
+                iface = *(++argv); argc--; goto b1;
+            case 'l':   op_flags |= OPT_l; if (argc < 2)  Usage();
+                loglevel = atoi(*(++argv)); argc--; goto b1;
+            case 'O': {
+                op_flags |= OPT_O; 
+                if (argc < 2)  Usage();
+                const char *cp =  *(++argv);
+                if (*cp == '1' || *cp == 't' || *cp == 'T' || *cp == 'y' || 
+                    *cp == 'Y')
+                    openhome = true;
+                argc--; goto b1;
+            }
+            case 'P':   op_flags |= OPT_P; if (argc < 2)  Usage();
+                upport = atoi(*(++argv)); argc--; goto b1;
+            case 'p':   op_flags |= OPT_p; if (argc < 2)  Usage();
+                mpdport = atoi(*(++argv)); argc--; goto b1;
+            case 'q':   op_flags |= OPT_q; if (argc < 2)  Usage();
+                ownqueue = atoi(*(++argv)) != 0; argc--; goto b1;
+            default: Usage();   break;
+            }
+    b1: argc--; argv++;
+    }
+
+    if (argc != 0)
+        Usage();
+
+    if (!configfile.empty()) {
+        ConfSimple config(configfile.c_str(), 1, true);
+        if (!config.ok()) {
+            cerr << "Could not open config: " << configfile << endl;
+            return 1;
+        }
+        string value;
+        if (!(op_flags & OPT_d))
+            config.get("logfilename", logfilename);
+        if (!(op_flags & OPT_f))
+            config.get("friendlyname", friendlyname);
+        if (!(op_flags & OPT_l) && config.get("loglevel", value))
+            loglevel = atoi(value.c_str());
+        if (!(op_flags & OPT_h))
+            config.get("mpdhost", mpdhost);
+        if (!(op_flags & OPT_p) && config.get("mpdport", value)) {
+            mpdport = atoi(value.c_str());
+        }
+        config.get("mpdpassword", mpdpassword);
+        if (!(op_flags & OPT_q) && config.get("ownqueue", value)) {
+            ownqueue = atoi(value.c_str()) != 0;
+        }
+        if (config.get("openhome", value)) {
+            openhome = atoi(value.c_str()) != 0;
+        }
+        if (config.get("ohmetapersist", value)) {
+            ohmetapersist = atoi(value.c_str()) != 0;
+        }
+        if (!(op_flags & OPT_i)) {
+            config.get("upnpiface", iface);
+            if (iface.empty()) {
+                config.get("upnpip", upnpip);
+            }
+        }
+        if (!(op_flags & OPT_P) && config.get("upnpport", value)) {
+            upport = atoi(value.c_str());
+        }
+    }
+
+    if (upnppdebug::Logger::getTheLog(logfilename) == 0) {
+        cerr << "Can't initialize log" << endl;
+        return 1;
+    }
+    upnppdebug::Logger::getTheLog("")->setLogLevel(upnppdebug::Logger::LogLevel(loglevel));
 
     Pidfile pidfile(pidfilename);
 
-	string cachedir;
-
-	// If started by root, do the pidfile + change uid thing
-	uid_t runas(0);
-	if (geteuid() == 0) {
-		struct passwd *pass = getpwnam(upmpdcliuser.c_str());
-		if (pass == 0) {
-			LOGFAT("upmpdcli won't run as root and user " << upmpdcliuser << 
-				   " does not exist " << endl);
-			return 1;
-		}
-		runas = pass->pw_uid;
-
-		pid_t pid;
-		if ((pid = pidfile.open()) != 0) {
-			LOGFAT("Can't open pidfile: " << pidfile.getreason() << 
-				   ". Return (other pid?): " << pid << endl);
-			return 1;
-		}
-		if (pidfile.write_pid() != 0) {
-			LOGFAT("Can't write pidfile: " << pidfile.getreason() << endl);
-			return 1;
-		}
-		cachedir = "/var/cache/upmpdcli";
-	} else {
-		cachedir = path_cat(path_tildexpand("~") , "/.cache/upmpdcli");
-	}
-
-	string mcfn;
-	if (ohmetapersist) {
-		mcfn = path_cat(cachedir, "/metacache");
-		if (!path_makepath(cachedir, 0755)) {
-			LOGERR("makepath("<< cachedir << ") : errno : " << errno << endl);
-		} else {
-			int fd;
-			if ((fd = open(mcfn.c_str(), O_CREAT|O_RDWR, 0644)) < 0) {
-				LOGERR("creat("<< mcfn << ") : errno : " << errno << endl);
-			} else {
-				close(fd);
-				if (geteuid() == 0 && chown(mcfn.c_str(), runas, -1) != 0) {
-					LOGERR("chown("<< mcfn << ") : errno : " << errno << endl);
-				}
-			}
-		}
-	}
-	
-	if ((op_flags & OPT_D)) {
-		if (daemon(1, 0)) {
-			LOGFAT("Daemon failed: errno " << errno << endl);
-			return 1;
-		}
-	}
-
-	if (geteuid() == 0) {
-		// Need to rewrite pid, it may have changed with the daemon call
-		pidfile.write_pid();
-		if (!logfilename.empty() && logfilename.compare("stderr")) {
-			if (chown(logfilename.c_str(), runas, -1) < 0) {
-				LOGERR("chown("<<logfilename<<") : errno : " << errno << endl);
-			}
-		}
-		if (setuid(runas) < 0) {
-			LOGFAT("Can't set my uid to " << runas << " current: " << geteuid()
-				   << endl);
-			return 1;
-		}
-	}
-
-	// Initialize MPD client object. Retry until it works or power fail.
-	MPDCli *mpdclip = 0;
-	int mpdretrysecs = 2;
-	for (;;) {
-		mpdclip = new MPDCli(mpdhost, mpdport, mpdpassword);
-		if (mpdclip == 0) {
-			LOGFAT("Can't allocate MPD client object" << endl);
-			return 1;
-		}
-		if (!mpdclip->ok()) {
-			LOGERR("MPD connection failed" << endl);
-			delete mpdclip;
-			mpdclip = 0;
-			sleep(mpdretrysecs);
-			mpdretrysecs = MIN(2*mpdretrysecs, 120);
-		} else {
-			break;
-		}
-	}
-
-	// Initialize libupnpp, and check health
-	LibUPnP *mylib = 0;
-	string hwaddr;
-	int libretrysecs = 10;
+    string cachedir;
+
+    // If started by root, do the pidfile + change uid thing
+    uid_t runas(0);
+    if (geteuid() == 0) {
+        struct passwd *pass = getpwnam(upmpdcliuser.c_str());
+        if (pass == 0) {
+            LOGFAT("upmpdcli won't run as root and user " << upmpdcliuser << 
+                   " does not exist " << endl);
+            return 1;
+        }
+        runas = pass->pw_uid;
+
+        pid_t pid;
+        if ((pid = pidfile.open()) != 0) {
+            LOGFAT("Can't open pidfile: " << pidfile.getreason() << 
+                   ". Return (other pid?): " << pid << endl);
+            return 1;
+        }
+        if (pidfile.write_pid() != 0) {
+            LOGFAT("Can't write pidfile: " << pidfile.getreason() << endl);
+            return 1;
+        }
+        cachedir = "/var/cache/upmpdcli";
+    } else {
+        cachedir = path_cat(path_tildexpand("~") , "/.cache/upmpdcli");
+    }
+
+    string mcfn;
+    if (ohmetapersist) {
+        mcfn = path_cat(cachedir, "/metacache");
+        if (!path_makepath(cachedir, 0755)) {
+            LOGERR("makepath("<< cachedir << ") : errno : " << errno << endl);
+        } else {
+            int fd;
+            if ((fd = open(mcfn.c_str(), O_CREAT|O_RDWR, 0644)) < 0) {
+                LOGERR("creat("<< mcfn << ") : errno : " << errno << endl);
+            } else {
+                close(fd);
+                if (geteuid() == 0 && chown(mcfn.c_str(), runas, -1) != 0) {
+                    LOGERR("chown("<< mcfn << ") : errno : " << errno << endl);
+                }
+            }
+        }
+    }
+    
+    if ((op_flags & OPT_D)) {
+        if (daemon(1, 0)) {
+            LOGFAT("Daemon failed: errno " << errno << endl);
+            return 1;
+        }
+    }
+
+    if (geteuid() == 0) {
+        // Need to rewrite pid, it may have changed with the daemon call
+        pidfile.write_pid();
+        if (!logfilename.empty() && logfilename.compare("stderr")) {
+            if (chown(logfilename.c_str(), runas, -1) < 0) {
+                LOGERR("chown("<<logfilename<<") : errno : " << errno << endl);
+            }
+        }
+        if (setuid(runas) < 0) {
+            LOGFAT("Can't set my uid to " << runas << " current: " << geteuid()
+                   << endl);
+            return 1;
+        }
+    }
+
+    // Initialize MPD client object. Retry until it works or power fail.
+    MPDCli *mpdclip = 0;
+    int mpdretrysecs = 2;
     for (;;) {
-		// Libupnp init fails if we're started at boot and the network
-		// is not ready yet. So retry this forever
-		mylib = LibUPnP::getLibUPnP(true, &hwaddr, iface, upnpip, upport);
-		if (mylib) {
-			break;
-		}
-		sleep(libretrysecs);
-		libretrysecs = MIN(2*libretrysecs, 120);
-	}
-
-	if (!mylib->ok()) {
-		LOGFAT("Lib init failed: " <<
-			   mylib->errAsString("main", mylib->getInitError()) << endl);
-		return 1;
-	}
-
-	//string upnplogfilename("/tmp/upmpdcli_libupnp.log");
-	//mylib->setLogFileName(upnplogfilename, LibUPnP::LogLevelDebug);
-
-	// Create unique ID
-	string UUID = LibUPnP::makeDevUUID(friendlyname, hwaddr);
-
-	// Read our XML data to make it available from the virtual directory
-	if (openhome) {
-		xmlfilenames.insert(xmlfilenames.end(), ohxmlfilenames.begin(),
-							ohxmlfilenames.end());
-	}
-
-	{
-		string protofile = path_cat(datadir, "protocolinfo.txt");
-		if (!read_protocolinfo(protofile, upmpdProtocolInfo)) {
-			LOGFAT("Failed reading protocol info from " << protofile << endl);
-			return 1;
-		}
-	}
-			
-	string reason;
-	unordered_map<string, string> xmlfiles;
-	for (unsigned int i = 0; i < xmlfilenames.size(); i++) {
-		string filename = path_cat(datadir, xmlfilenames[i]);
-		string data;
-		if (!file_to_string(filename, data, &reason)) {
-			LOGFAT("Failed reading " << filename << " : " << reason << endl);
-			return 1;
-		}
-		if (i == 0) {
-			// Special for description: set UUID and friendlyname
-			data = regsub1("@UUID@", data, UUID);
-			data = regsub1("@FRIENDLYNAME@", data, friendlyname);
-			if (openhome) 
-				data = regsub1("@OPENHOME@", data, ohDesc);
-		}
-		xmlfiles[xmlfilenames[i]] = data;
-	}
-	unsigned int options = UpMpd::upmpdNone;
-	if (ownqueue)
-		options |= UpMpd::upmpdOwnQueue;
-	if (openhome)
-		options |= UpMpd::upmpdDoOH;
-	if (ohmetapersist)
-		options |= UpMpd::upmpdOhMetaPersist;
-
-	// Initialize the UPnP device object.
-	UpMpd device(string("uuid:") + UUID, friendlyname, 
-				 xmlfiles, mpdclip, options, mcfn);
-
-	// And forever generate state change events.
-	LOGDEB("Entering event loop" << endl);
-	device.eventloop();
-
-	return 0;
-}
-
-/* Local Variables: */
-/* mode: c++ */
-/* c-basic-offset: 4 */
-/* tab-width: 4 */
-/* indent-tabs-mode: t */
-/* End: */
+        mpdclip = new MPDCli(mpdhost, mpdport, mpdpassword);
+        if (mpdclip == 0) {
+            LOGFAT("Can't allocate MPD client object" << endl);
+            return 1;
+        }
+        if (!mpdclip->ok()) {
+            LOGERR("MPD connection failed" << endl);
+            delete mpdclip;
+            mpdclip = 0;
+            sleep(mpdretrysecs);
+            mpdretrysecs = MIN(2*mpdretrysecs, 120);
+        } else {
+            break;
+        }
+    }
+
+    // Initialize libupnpp, and check health
+    LibUPnP *mylib = 0;
+    string hwaddr;
+    int libretrysecs = 10;
+    for (;;) {
+        // Libupnp init fails if we're started at boot and the network
+        // is not ready yet. So retry this forever
+        mylib = LibUPnP::getLibUPnP(true, &hwaddr, iface, upnpip, upport);
+        if (mylib) {
+            break;
+        }
+        sleep(libretrysecs);
+        libretrysecs = MIN(2*libretrysecs, 120);
+    }
+
+    if (!mylib->ok()) {
+        LOGFAT("Lib init failed: " <<
+               mylib->errAsString("main", mylib->getInitError()) << endl);
+        return 1;
+    }
+
+    //string upnplogfilename("/tmp/upmpdcli_libupnp.log");
+    //mylib->setLogFileName(upnplogfilename, LibUPnP::LogLevelDebug);
+
+    // Create unique ID
+    string UUID = LibUPnP::makeDevUUID(friendlyname, hwaddr);
+
+    // Read our XML data to make it available from the virtual directory
+    if (openhome) {
+        xmlfilenames.insert(xmlfilenames.end(), ohxmlfilenames.begin(),
+                            ohxmlfilenames.end());
+    }
+
+    {
+        string protofile = path_cat(datadir, "protocolinfo.txt");
+        if (!read_protocolinfo(protofile, upmpdProtocolInfo)) {
+            LOGFAT("Failed reading protocol info from " << protofile << endl);
+            return 1;
+        }
+    }
+            
+    string reason;
+    unordered_map<string, string> xmlfiles;
+    for (unsigned int i = 0; i < xmlfilenames.size(); i++) {
+        string filename = path_cat(datadir, xmlfilenames[i]);
+        string data;
+        if (!file_to_string(filename, data, &reason)) {
+            LOGFAT("Failed reading " << filename << " : " << reason << endl);
+            return 1;
+        }
+        if (i == 0) {
+            // Special for description: set UUID and friendlyname
+            data = regsub1("@UUID@", data, UUID);
+            data = regsub1("@FRIENDLYNAME@", data, friendlyname);
+            if (openhome) 
+                data = regsub1("@OPENHOME@", data, ohDesc);
+        }
+        xmlfiles[xmlfilenames[i]] = data;
+    }
+    unsigned int options = UpMpd::upmpdNone;
+    if (ownqueue)
+        options |= UpMpd::upmpdOwnQueue;
+    if (openhome)
+        options |= UpMpd::upmpdDoOH;
+    if (ohmetapersist)
+        options |= UpMpd::upmpdOhMetaPersist;
+
+    // Initialize the UPnP device object.
+    UpMpd device(string("uuid:") + UUID, friendlyname, 
+                 xmlfiles, mpdclip, options, mcfn);
+    dev = &device;
+
+    // And forever generate state change events.
+    LOGDEB("Entering event loop" << endl);
+    setupsigs();
+    device.eventloop();
+    LOGDEB("Event loop returned" << endl);
+
+    return 0;
+}