Switch to unified view

a/upmpd/upmpd.cxx b/upmpd/upmpd.cxx
1
/* Copyright (C) 2014 J.F.Dockes
1
/* Copyright (C) 2014 J.F.Dockes
2
 *     This program is free software; you can redistribute it and/or modify
2
 *   This program is free software; you can redistribute it and/or modify
3
 *     it under the terms of the GNU General Public License as published by
3
 *   it under the terms of the GNU General Public License as published by
4
 *     the Free Software Foundation; either version 2 of the License, or
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *     (at your option) any later version.
5
 *   (at your option) any later version.
6
 *
6
 *
7
 *     This program is distributed in the hope that it will be useful,
7
 *   This program is distributed in the hope that it will be useful,
8
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *     GNU General Public License for more details.
10
 *   GNU General Public License for more details.
11
 *
11
 *
12
 *     You should have received a copy of the GNU General Public License
12
 *   You should have received a copy of the GNU General Public License
13
 *     along with this program; if not, write to the
13
 *   along with this program; if not, write to the
14
 *     Free Software Foundation, Inc.,
14
 *   Free Software Foundation, Inc.,
15
 *     59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
16
 */
17
17
18
#include <stdio.h>
18
#include <stdio.h>
19
#include <stdlib.h>
19
#include <stdlib.h>
20
#include <unistd.h>
20
#include <unistd.h>
...
...
49
#include "ohplaylist.hxx"
49
#include "ohplaylist.hxx"
50
50
51
static const string dfltFriendlyName("UpMpd");
51
static const string dfltFriendlyName("UpMpd");
52
string upmpdProtocolInfo;
52
string upmpdProtocolInfo;
53
53
54
static UpnpDevice *dev;
55
56
static void onsig(int)
57
{
58
    LOGDEB("Got sig" << endl);
59
    dev->shouldExit();
60
}
61
62
static const int catchedSigs[] = {SIGINT, SIGQUIT, SIGTERM};
63
64
static void setupsigs()
65
{
66
    struct sigaction action;
67
    action.sa_handler = onsig;
68
    action.sa_flags = 0;
69
    sigemptyset(&action.sa_mask);
70
    for (unsigned int i = 0; i < sizeof(catchedSigs) / sizeof(int); i++)
71
        if (signal(catchedSigs[i], SIG_IGN) != SIG_IGN) {
72
            if (sigaction(catchedSigs[i], &action, 0) < 0) {
73
                perror("Sigaction failed");
74
            }
75
        }
76
}
77
54
// Note: if we ever need this to work without cxx11, there is this:
78
// Note: if we ever need this to work without cxx11, there is this:
55
// http://www.tutok.sk/fastgl/callback.html
79
// http://www.tutok.sk/fastgl/callback.html
56
UpMpd::UpMpd(const string& deviceid, const string& friendlyname,
80
UpMpd::UpMpd(const string& deviceid, const string& friendlyname,
57
           const unordered_map<string, string>& xmlfiles,
81
             const unordered_map<string, string>& xmlfiles,
58
           MPDCli *mpdcli, unsigned int opts, const string& cachefn)
82
             MPDCli *mpdcli, unsigned int opts, const string& cachefn)
59
  : UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_mpds(0),
83
    : UpnpDevice(deviceid, xmlfiles), m_mpdcli(mpdcli), m_mpds(0),
60
    m_options(opts),
84
      m_options(opts),
61
    m_mcachefn(cachefn)
85
      m_mcachefn(cachefn)
62
{
86
{
63
  // Note: the order is significant here as it will be used when
87
    // Note: the order is significant here as it will be used when
64
  // calling the getStatus() methods, and we want AVTransport to
88
    // calling the getStatus() methods, and we want AVTransport to
65
  // update the mpd status for OHInfo
89
    // update the mpd status for OHInfo
66
  UpMpdRenderCtl *rdctl = new UpMpdRenderCtl(this);
90
    UpMpdRenderCtl *rdctl = new UpMpdRenderCtl(this);
67
  m_services.push_back(rdctl);
91
    m_services.push_back(rdctl);
68
  m_services.push_back(new UpMpdAVTransport(this));
92
    m_services.push_back(new UpMpdAVTransport(this));
69
  m_services.push_back(new UpMpdConMan(this));
93
    m_services.push_back(new UpMpdConMan(this));
70
  if (m_options & upmpdDoOH) {
94
    if (m_options & upmpdDoOH) {
71
      m_services.push_back(new OHProduct(this, friendlyname));
95
        m_services.push_back(new OHProduct(this, friendlyname));
72
      m_services.push_back(new OHInfo(this));
96
        m_services.push_back(new OHInfo(this));
73
      m_services.push_back(new OHTime(this));
97
        m_services.push_back(new OHTime(this));
74
      m_services.push_back(new OHVolume(this, rdctl));
98
        m_services.push_back(new OHVolume(this, rdctl));
75
      m_services.push_back(new OHPlaylist(this, rdctl));
99
        m_services.push_back(new OHPlaylist(this, rdctl));
76
  }
100
    }
77
}
101
}
78
102
79
UpMpd::~UpMpd()
103
UpMpd::~UpMpd()
80
{
104
{
81
  for (vector<UpnpService*>::iterator it = m_services.begin();
105
    for (vector<UpnpService*>::iterator it = m_services.begin();
82
       it != m_services.end(); it++) {
106
         it != m_services.end(); it++) {
83
      delete(*it);
107
        delete(*it);
84
  }
108
    }
85
}
109
}
86
110
87
const MpdStatus& UpMpd::getMpdStatus()
111
const MpdStatus& UpMpd::getMpdStatus()
88
{
112
{
89
    m_mpds = &m_mpdcli->getStatus();
113
    m_mpds = &m_mpdcli->getStatus();
...
...
94
// Main program
118
// Main program
95
119
96
#include "conftree.hxx"
120
#include "conftree.hxx"
97
121
98
static const string ohDesc(
122
static const string ohDesc(
99
  "<service>"
123
    "<service>"
100
  "  <serviceType>urn:av-openhome-org:service:Product:1</serviceType>"
124
    "  <serviceType>urn:av-openhome-org:service:Product:1</serviceType>"
101
  "  <serviceId>urn:av-openhome-org:serviceId:Product</serviceId>"
125
    "  <serviceId>urn:av-openhome-org:serviceId:Product</serviceId>"
102
  "  <SCPDURL>/OHProduct.xml</SCPDURL>"
126
    "  <SCPDURL>/OHProduct.xml</SCPDURL>"
103
  "  <controlURL>/ctl/OHProduct</controlURL>"
127
    "  <controlURL>/ctl/OHProduct</controlURL>"
104
  "  <eventSubURL>/evt/OHProduct</eventSubURL>"
128
    "  <eventSubURL>/evt/OHProduct</eventSubURL>"
105
  "</service>"
129
    "</service>"
106
  "<service>"
130
    "<service>"
107
  "  <serviceType>urn:av-openhome-org:service:Info:1</serviceType>"
131
    "  <serviceType>urn:av-openhome-org:service:Info:1</serviceType>"
108
  "  <serviceId>urn:av-openhome-org:serviceId:Info</serviceId>"
132
    "  <serviceId>urn:av-openhome-org:serviceId:Info</serviceId>"
109
  "  <SCPDURL>/OHInfo.xml</SCPDURL>"
133
    "  <SCPDURL>/OHInfo.xml</SCPDURL>"
110
  "  <controlURL>/ctl/OHInfo</controlURL>"
134
    "  <controlURL>/ctl/OHInfo</controlURL>"
111
  "  <eventSubURL>/evt/OHInfo</eventSubURL>"
135
    "  <eventSubURL>/evt/OHInfo</eventSubURL>"
112
  "</service>"
136
    "</service>"
113
  "<service>"
137
    "<service>"
114
  "  <serviceType>urn:av-openhome-org:service:Time:1</serviceType>"
138
    "  <serviceType>urn:av-openhome-org:service:Time:1</serviceType>"
115
  "  <serviceId>urn:av-openhome-org:serviceId:Time</serviceId>"
139
    "  <serviceId>urn:av-openhome-org:serviceId:Time</serviceId>"
116
  "  <SCPDURL>/OHTime.xml</SCPDURL>"
140
    "  <SCPDURL>/OHTime.xml</SCPDURL>"
117
  "  <controlURL>/ctl/OHTime</controlURL>"
141
    "  <controlURL>/ctl/OHTime</controlURL>"
118
  "  <eventSubURL>/evt/OHTime</eventSubURL>"
142
    "  <eventSubURL>/evt/OHTime</eventSubURL>"
119
  "</service>"
143
    "</service>"
120
  "<service>"
144
    "<service>"
121
  "  <serviceType>urn:av-openhome-org:service:Volume:1</serviceType>"
145
    "  <serviceType>urn:av-openhome-org:service:Volume:1</serviceType>"
122
  "  <serviceId>urn:av-openhome-org:serviceId:Volume</serviceId>"
146
    "  <serviceId>urn:av-openhome-org:serviceId:Volume</serviceId>"
123
  "  <SCPDURL>/OHVolume.xml</SCPDURL>"
147
    "  <SCPDURL>/OHVolume.xml</SCPDURL>"
124
  "  <controlURL>/ctl/OHVolume</controlURL>"
148
    "  <controlURL>/ctl/OHVolume</controlURL>"
125
  "  <eventSubURL>/evt/OHVolume</eventSubURL>"
149
    "  <eventSubURL>/evt/OHVolume</eventSubURL>"
126
  "</service>"
150
    "</service>"
127
  "<service>"
151
    "<service>"
128
  "  <serviceType>urn:av-openhome-org:service:Playlist:1</serviceType>"
152
    "  <serviceType>urn:av-openhome-org:service:Playlist:1</serviceType>"
129
  "  <serviceId>urn:av-openhome-org:serviceId:Playlist</serviceId>"
153
    "  <serviceId>urn:av-openhome-org:serviceId:Playlist</serviceId>"
130
  "  <SCPDURL>/OHPlaylist.xml</SCPDURL>"
154
    "  <SCPDURL>/OHPlaylist.xml</SCPDURL>"
131
  "  <controlURL>/ctl/OHPlaylist</controlURL>"
155
    "  <controlURL>/ctl/OHPlaylist</controlURL>"
132
  "  <eventSubURL>/evt/OHPlaylist</eventSubURL>"
156
    "  <eventSubURL>/evt/OHPlaylist</eventSubURL>"
133
  "</service>"
157
    "</service>"
134
  );
158
    );
135
159
136
static char *thisprog;
160
static char *thisprog;
137
161
138
static int op_flags;
162
static int op_flags;
139
#define OPT_MOINS 0x1
163
#define OPT_MOINS 0x1
140
#define OPT_h   0x2
164
#define OPT_h     0x2
141
#define OPT_p   0x4
165
#define OPT_p     0x4
142
#define OPT_d   0x8
166
#define OPT_d     0x8
143
#define OPT_D     0x10
167
#define OPT_D     0x10
144
#define OPT_c     0x20
168
#define OPT_c     0x20
145
#define OPT_l     0x40
169
#define OPT_l     0x40
146
#define OPT_f     0x80
170
#define OPT_f     0x80
147
#define OPT_q     0x100
171
#define OPT_q     0x100
148
#define OPT_i     0x200
172
#define OPT_i     0x200
149
#define OPT_P     0x400
173
#define OPT_P     0x400
150
#define OPT_O     0x800
174
#define OPT_O     0x800
151
175
152
static const char usage[] = 
176
static const char usage[] = 
153
"-c configfile \t configuration file to use\n"
177
    "-c configfile \t configuration file to use\n"
154
"""""""""""""""""""""""""""""-h host    \t specify host MPD is running on\n"""""""""""""""""""""""""""""
178
    "-h host    \t specify host MPD is running on\n"
155
"-p port     \t specify MPD port\n"
179
    "-p port     \t specify MPD port\n"
156
"-d logfilename\t debug messages to\n"
180
    "-d logfilename\t debug messages to\n"
157
"-l loglevel\t  log level (0-6)\n"
181
    "-l loglevel\t  log level (0-6)\n"
158
"-D    \t run as a daemon\n"
182
    "-D    \t run as a daemon\n"
159
"-f friendlyname\t define device displayed name\n"
183
    "-f friendlyname\t define device displayed name\n"
160
"-q 0|1\t if set, we own the mpd queue, else avoid clearing it whenever we feel like it\n"
184
    "-q 0|1\t if set, we own the mpd queue, else avoid clearing it whenever we feel like it\n"
161
"-i iface    \t specify network interface name to be used for UPnP\n"
185
    "-i iface    \t specify network interface name to be used for UPnP\n"
162
"-P upport    \t specify port number to be used for UPnP\n"
186
    "-P upport    \t specify port number to be used for UPnP\n"
163
"-O 0|1\t decide if we run and export the OpenHome services\n"
187
    "-O 0|1\t decide if we run and export the OpenHome services\n"
164
"\n"
188
    "\n"
165
          ;
189
    ;
166
static void
190
static void
167
Usage(void)
191
Usage(void)
168
{
192
{
169
  fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
193
    fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
170
  exit(1);
194
    exit(1);
171
}
195
}
172
196
173
static string myDeviceUUID;
197
static string myDeviceUUID;
174
198
175
static string datadir(DATADIR "/");
199
static string datadir(DATADIR "/");
176
static string configdir(CONFIGDIR "/");
200
static string configdir(CONFIGDIR "/");
177
201
178
// Our XML description data. !Keep description.xml first!
202
// Our XML description data. !Keep description.xml first!
179
static vector<const char *> xmlfilenames = 
203
static vector<const char *> xmlfilenames = 
180
{
204
{
181
  /* keep first */ "description.xml", /* keep first */
205
    /* keep first */ "description.xml", /* keep first */
182
  "RenderingControl.xml", "AVTransport.xml", "ConnectionManager.xml",
206
    "RenderingControl.xml", "AVTransport.xml", "ConnectionManager.xml",
183
};
207
};
184
static vector<const char *> ohxmlfilenames = 
208
static vector<const char *> ohxmlfilenames = 
185
{
209
{
186
  "OHProduct.xml", "OHInfo.xml", "OHTime.xml", "OHVolume.xml", 
210
    "OHProduct.xml", "OHInfo.xml", "OHTime.xml", "OHVolume.xml", 
187
  "OHPlaylist.xml",
211
    "OHPlaylist.xml",
188
};
212
};
189
213
190
214
191
int main(int argc, char *argv[])
215
int main(int argc, char *argv[])
192
{
216
{
193
  string mpdhost("localhost");
217
    string mpdhost("localhost");
194
  int mpdport = 6600;
218
    int mpdport = 6600;
195
  string mpdpassword;
219
    string mpdpassword;
196
  string logfilename;
220
    string logfilename;
197
  int loglevel(upnppdebug::Logger::LLINF);
221
    int loglevel(upnppdebug::Logger::LLINF);
198
  string configfile;
222
    string configfile;
199
  string friendlyname(dfltFriendlyName);
223
    string friendlyname(dfltFriendlyName);
200
  bool ownqueue = true;
224
    bool ownqueue = true;
201
  bool openhome = true;
225
    bool openhome = true;
202
  bool ohmetapersist = true;
226
    bool ohmetapersist = true;
203
  string upmpdcliuser("upmpdcli");
227
    string upmpdcliuser("upmpdcli");
204
  string pidfilename("/var/run/upmpdcli.pid");
228
    string pidfilename("/var/run/upmpdcli.pid");
205
  string iface;
229
    string iface;
206
  unsigned short upport = 0;
230
    unsigned short upport = 0;
207
  string upnpip;
231
    string upnpip;
208
232
209
  const char *cp;
233
    const char *cp;
210
  if ((cp = getenv("UPMPD_HOST")))
234
    if ((cp = getenv("UPMPD_HOST")))
211
      mpdhost = cp;
235
        mpdhost = cp;
212
  if ((cp = getenv("UPMPD_PORT")))
236
    if ((cp = getenv("UPMPD_PORT")))
213
      mpdport = atoi(cp);
237
        mpdport = atoi(cp);
214
  if ((cp = getenv("UPMPD_FRIENDLYNAME")))
238
    if ((cp = getenv("UPMPD_FRIENDLYNAME")))
215
      friendlyname = atoi(cp);
239
        friendlyname = atoi(cp);
216
  if ((cp = getenv("UPMPD_CONFIG")))
240
    if ((cp = getenv("UPMPD_CONFIG")))
217
      configfile = cp;
241
        configfile = cp;
218
  if ((cp = getenv("UPMPD_UPNPIFACE")))
242
    if ((cp = getenv("UPMPD_UPNPIFACE")))
219
      iface = cp;
243
        iface = cp;
220
  if ((cp = getenv("UPMPD_UPNPPORT")))
244
    if ((cp = getenv("UPMPD_UPNPPORT")))
221
      upport = atoi(cp);
245
        upport = atoi(cp);
222
246
223
  thisprog = argv[0];
247
    thisprog = argv[0];
224
  argc--; argv++;
248
    argc--; argv++;
225
  while (argc > 0 && **argv == '-') {
249
    while (argc > 0 && **argv == '-') {
226
      (*argv)++;
250
        (*argv)++;
227
      if (!(**argv))
251
        if (!(**argv))
228
          Usage();
252
            Usage();
229
      while (**argv)
253
        while (**argv)
230
          switch (*(*argv)++) {
254
            switch (*(*argv)++) {
231
          case 'c':    op_flags |= OPT_c; if (argc < 2)  Usage();
255
            case 'c':   op_flags |= OPT_c; if (argc < 2)  Usage();
232
              configfile = *(++argv); argc--; goto b1;
256
                configfile = *(++argv); argc--; goto b1;
233
          case 'D':    op_flags |= OPT_D; break;
257
            case 'D':   op_flags |= OPT_D; break;
234
          case 'd':    op_flags |= OPT_d; if (argc < 2)  Usage();
258
            case 'd':   op_flags |= OPT_d; if (argc < 2)  Usage();
235
              logfilename = *(++argv); argc--; goto b1;
259
                logfilename = *(++argv); argc--; goto b1;
236
          case 'f':    op_flags |= OPT_f; if (argc < 2)  Usage();
260
            case 'f':   op_flags |= OPT_f; if (argc < 2)  Usage();
237
              friendlyname = *(++argv); argc--; goto b1;
261
                friendlyname = *(++argv); argc--; goto b1;
238
          case 'h':    op_flags |= OPT_h; if (argc < 2)  Usage();
262
            case 'h':   op_flags |= OPT_h; if (argc < 2)  Usage();
239
              mpdhost = *(++argv); argc--; goto b1;
263
                mpdhost = *(++argv); argc--; goto b1;
240
          case 'i':    op_flags |= OPT_i; if (argc < 2)  Usage();
264
            case 'i':   op_flags |= OPT_i; if (argc < 2)  Usage();
241
              iface = *(++argv); argc--; goto b1;
265
                iface = *(++argv); argc--; goto b1;
242
          case 'l':    op_flags |= OPT_l; if (argc < 2)  Usage();
266
            case 'l':   op_flags |= OPT_l; if (argc < 2)  Usage();
243
              loglevel = atoi(*(++argv)); argc--; goto b1;
267
                loglevel = atoi(*(++argv)); argc--; goto b1;
244
          case 'O': {
268
            case 'O': {
245
              op_flags |= OPT_O; 
269
                op_flags |= OPT_O; 
246
              if (argc < 2)  Usage();
270
                if (argc < 2)  Usage();
247
              const char *cp =  *(++argv);
271
                const char *cp =  *(++argv);
248
              if (*cp == '1' || *cp == 't' || *cp == 'T' || *cp == 'y' || 
272
                if (*cp == '1' || *cp == 't' || *cp == 'T' || *cp == 'y' || 
249
                  *cp == 'Y')
273
                    *cp == 'Y')
250
                  openhome = true;
274
                    openhome = true;
251
              argc--; goto b1;
275
                argc--; goto b1;
252
          }
276
            }
253
          case 'P':    op_flags |= OPT_P; if (argc < 2)  Usage();
277
            case 'P':   op_flags |= OPT_P; if (argc < 2)  Usage();
254
              upport = atoi(*(++argv)); argc--; goto b1;
278
                upport = atoi(*(++argv)); argc--; goto b1;
255
          case 'p':    op_flags |= OPT_p; if (argc < 2)  Usage();
279
            case 'p':   op_flags |= OPT_p; if (argc < 2)  Usage();
256
              mpdport = atoi(*(++argv)); argc--; goto b1;
280
                mpdport = atoi(*(++argv)); argc--; goto b1;
257
          case 'q':    op_flags |= OPT_q; if (argc < 2)  Usage();
281
            case 'q':   op_flags |= OPT_q; if (argc < 2)  Usage();
258
              ownqueue = atoi(*(++argv)) != 0; argc--; goto b1;
282
                ownqueue = atoi(*(++argv)) != 0; argc--; goto b1;
259
          default: Usage();   break;
283
            default: Usage();   break;
260
          }
284
            }
261
  b1: argc--; argv++;
285
    b1: argc--; argv++;
262
  }
286
    }
263
287
264
  if (argc != 0)
288
    if (argc != 0)
265
      Usage();
289
        Usage();
266
290
267
  if (!configfile.empty()) {
291
    if (!configfile.empty()) {
268
      ConfSimple config(configfile.c_str(), 1, true);
292
        ConfSimple config(configfile.c_str(), 1, true);
269
      if (!config.ok()) {
293
        if (!config.ok()) {
270
          cerr << "Could not open config: " << configfile << endl;
294
            cerr << "Could not open config: " << configfile << endl;
271
          return 1;
295
            return 1;
272
      }
296
        }
273
      string value;
297
        string value;
274
      if (!(op_flags & OPT_d))
298
        if (!(op_flags & OPT_d))
275
          config.get("logfilename", logfilename);
299
            config.get("logfilename", logfilename);
276
      if (!(op_flags & OPT_f))
300
        if (!(op_flags & OPT_f))
277
          config.get("friendlyname", friendlyname);
301
            config.get("friendlyname", friendlyname);
278
      if (!(op_flags & OPT_l) && config.get("loglevel", value))
302
        if (!(op_flags & OPT_l) && config.get("loglevel", value))
279
          loglevel = atoi(value.c_str());
303
            loglevel = atoi(value.c_str());
280
      if (!(op_flags & OPT_h))
304
        if (!(op_flags & OPT_h))
281
          config.get("mpdhost", mpdhost);
305
            config.get("mpdhost", mpdhost);
282
      if (!(op_flags & OPT_p) && config.get("mpdport", value)) {
306
        if (!(op_flags & OPT_p) && config.get("mpdport", value)) {
283
          mpdport = atoi(value.c_str());
307
            mpdport = atoi(value.c_str());
284
      }
308
        }
285
      config.get("mpdpassword", mpdpassword);
309
        config.get("mpdpassword", mpdpassword);
286
      if (!(op_flags & OPT_q) && config.get("ownqueue", value)) {
310
        if (!(op_flags & OPT_q) && config.get("ownqueue", value)) {
287
          ownqueue = atoi(value.c_str()) != 0;
311
            ownqueue = atoi(value.c_str()) != 0;
288
      }
312
        }
289
      if (config.get("openhome", value)) {
313
        if (config.get("openhome", value)) {
290
          openhome = atoi(value.c_str()) != 0;
314
            openhome = atoi(value.c_str()) != 0;
291
      }
315
        }
292
      if (config.get("ohmetapersist", value)) {
316
        if (config.get("ohmetapersist", value)) {
293
          ohmetapersist = atoi(value.c_str()) != 0;
317
            ohmetapersist = atoi(value.c_str()) != 0;
294
      }
318
        }
295
      if (!(op_flags & OPT_i)) {
319
        if (!(op_flags & OPT_i)) {
296
          config.get("upnpiface", iface);
320
            config.get("upnpiface", iface);
297
          if (iface.empty()) {
321
            if (iface.empty()) {
298
              config.get("upnpip", upnpip);
322
                config.get("upnpip", upnpip);
299
          }
323
            }
300
      }
324
        }
301
      if (!(op_flags & OPT_P) && config.get("upnpport", value)) {
325
        if (!(op_flags & OPT_P) && config.get("upnpport", value)) {
302
          upport = atoi(value.c_str());
326
            upport = atoi(value.c_str());
303
      }
327
        }
304
  }
328
    }
305
329
306
  if (upnppdebug::Logger::getTheLog(logfilename) == 0) {
330
    if (upnppdebug::Logger::getTheLog(logfilename) == 0) {
307
      cerr << "Can't initialize log" << endl;
331
        cerr << "Can't initialize log" << endl;
308
      return 1;
332
        return 1;
309
  }
333
    }
310
  upnppdebug::Logger::getTheLog("")->setLogLevel(upnppdebug::Logger::LogLevel(loglevel));
334
    upnppdebug::Logger::getTheLog("")->setLogLevel(upnppdebug::Logger::LogLevel(loglevel));
311
335
312
    Pidfile pidfile(pidfilename);
336
    Pidfile pidfile(pidfilename);
313
337
314
  string cachedir;
338
    string cachedir;
315
339
316
  // If started by root, do the pidfile + change uid thing
340
    // If started by root, do the pidfile + change uid thing
317
  uid_t runas(0);
341
    uid_t runas(0);
318
  if (geteuid() == 0) {
342
    if (geteuid() == 0) {
319
      struct passwd *pass = getpwnam(upmpdcliuser.c_str());
343
        struct passwd *pass = getpwnam(upmpdcliuser.c_str());
320
      if (pass == 0) {
344
        if (pass == 0) {
321
          LOGFAT("upmpdcli won't run as root and user " << upmpdcliuser << 
345
            LOGFAT("upmpdcli won't run as root and user " << upmpdcliuser << 
322
                 " does not exist " << endl);
346
                   " does not exist " << endl);
323
          return 1;
347
            return 1;
324
      }
348
        }
325
      runas = pass->pw_uid;
349
        runas = pass->pw_uid;
326
350
327
      pid_t pid;
351
        pid_t pid;
328
      if ((pid = pidfile.open()) != 0) {
352
        if ((pid = pidfile.open()) != 0) {
329
          LOGFAT("Can't open pidfile: " << pidfile.getreason() << 
353
            LOGFAT("Can't open pidfile: " << pidfile.getreason() << 
330
                 ". Return (other pid?): " << pid << endl);
354
                   ". Return (other pid?): " << pid << endl);
331
          return 1;
355
            return 1;
332
      }
356
        }
333
      if (pidfile.write_pid() != 0) {
357
        if (pidfile.write_pid() != 0) {
334
          LOGFAT("Can't write pidfile: " << pidfile.getreason() << endl);
358
            LOGFAT("Can't write pidfile: " << pidfile.getreason() << endl);
335
          return 1;
359
            return 1;
336
      }
360
        }
337
      cachedir = "/var/cache/upmpdcli";
361
        cachedir = "/var/cache/upmpdcli";
338
  } else {
362
    } else {
339
      cachedir = path_cat(path_tildexpand("~") , "/.cache/upmpdcli");
363
        cachedir = path_cat(path_tildexpand("~") , "/.cache/upmpdcli");
340
  }
364
    }
341
365
342
  string mcfn;
366
    string mcfn;
343
  if (ohmetapersist) {
367
    if (ohmetapersist) {
344
      mcfn = path_cat(cachedir, "/metacache");
368
        mcfn = path_cat(cachedir, "/metacache");
345
      if (!path_makepath(cachedir, 0755)) {
369
        if (!path_makepath(cachedir, 0755)) {
346
          LOGERR("makepath("<< cachedir << ") : errno : " << errno << endl);
370
            LOGERR("makepath("<< cachedir << ") : errno : " << errno << endl);
347
      } else {
371
        } else {
348
          int fd;
372
            int fd;
349
          if ((fd = open(mcfn.c_str(), O_CREAT|O_RDWR, 0644)) < 0) {
373
            if ((fd = open(mcfn.c_str(), O_CREAT|O_RDWR, 0644)) < 0) {
350
              LOGERR("creat("<< mcfn << ") : errno : " << errno << endl);
374
                LOGERR("creat("<< mcfn << ") : errno : " << errno << endl);
351
          } else {
375
            } else {
352
              close(fd);
376
                close(fd);
353
              if (geteuid() == 0 && chown(mcfn.c_str(), runas, -1) != 0) {
377
                if (geteuid() == 0 && chown(mcfn.c_str(), runas, -1) != 0) {
354
                  LOGERR("chown("<< mcfn << ") : errno : " << errno << endl);
378
                    LOGERR("chown("<< mcfn << ") : errno : " << errno << endl);
355
              }
379
                }
356
          }
380
            }
357
      }
381
        }
358
  }
382
    }
359
  
383
    
360
  if ((op_flags & OPT_D)) {
384
    if ((op_flags & OPT_D)) {
361
      if (daemon(1, 0)) {
385
        if (daemon(1, 0)) {
362
          LOGFAT("Daemon failed: errno " << errno << endl);
386
            LOGFAT("Daemon failed: errno " << errno << endl);
363
          return 1;
387
            return 1;
364
      }
388
        }
365
  }
389
    }
366
390
367
  if (geteuid() == 0) {
391
    if (geteuid() == 0) {
368
      // Need to rewrite pid, it may have changed with the daemon call
392
        // Need to rewrite pid, it may have changed with the daemon call
369
      pidfile.write_pid();
393
        pidfile.write_pid();
370
      if (!logfilename.empty() && logfilename.compare("stderr")) {
394
        if (!logfilename.empty() && logfilename.compare("stderr")) {
371
          if (chown(logfilename.c_str(), runas, -1) < 0) {
395
            if (chown(logfilename.c_str(), runas, -1) < 0) {
372
              LOGERR("chown("<<logfilename<<") : errno : " << errno << endl);
396
                LOGERR("chown("<<logfilename<<") : errno : " << errno << endl);
373
          }
397
            }
374
      }
398
        }
375
      if (setuid(runas) < 0) {
399
        if (setuid(runas) < 0) {
376
          LOGFAT("Can't set my uid to " << runas << " current: " << geteuid()
400
            LOGFAT("Can't set my uid to " << runas << " current: " << geteuid()
377
                 << endl);
401
                   << endl);
378
          return 1;
402
            return 1;
379
      }
403
        }
380
  }
404
    }
381
405
382
  // Initialize MPD client object. Retry until it works or power fail.
406
    // Initialize MPD client object. Retry until it works or power fail.
383
  MPDCli *mpdclip = 0;
407
    MPDCli *mpdclip = 0;
384
  int mpdretrysecs = 2;
408
    int mpdretrysecs = 2;
385
  for (;;) {
386
      mpdclip = new MPDCli(mpdhost, mpdport, mpdpassword);
387
      if (mpdclip == 0) {
388
          LOGFAT("Can't allocate MPD client object" << endl);
389
          return 1;
390
      }
391
      if (!mpdclip->ok()) {
392
          LOGERR("MPD connection failed" << endl);
393
          delete mpdclip;
394
          mpdclip = 0;
395
          sleep(mpdretrysecs);
396
          mpdretrysecs = MIN(2*mpdretrysecs, 120);
397
      } else {
398
          break;
399
      }
400
  }
401
402
  // Initialize libupnpp, and check health
403
  LibUPnP *mylib = 0;
404
  string hwaddr;
405
  int libretrysecs = 10;
406
    for (;;) {
409
    for (;;) {
410
        mpdclip = new MPDCli(mpdhost, mpdport, mpdpassword);
411
        if (mpdclip == 0) {
412
            LOGFAT("Can't allocate MPD client object" << endl);
413
            return 1;
414
        }
415
        if (!mpdclip->ok()) {
416
            LOGERR("MPD connection failed" << endl);
417
            delete mpdclip;
418
            mpdclip = 0;
419
            sleep(mpdretrysecs);
420
            mpdretrysecs = MIN(2*mpdretrysecs, 120);
421
        } else {
422
            break;
423
        }
424
    }
425
426
    // Initialize libupnpp, and check health
427
    LibUPnP *mylib = 0;
428
    string hwaddr;
429
    int libretrysecs = 10;
430
    for (;;) {
407
      // Libupnp init fails if we're started at boot and the network
431
        // Libupnp init fails if we're started at boot and the network
408
      // is not ready yet. So retry this forever
432
        // is not ready yet. So retry this forever
409
      mylib = LibUPnP::getLibUPnP(true, &hwaddr, iface, upnpip, upport);
433
        mylib = LibUPnP::getLibUPnP(true, &hwaddr, iface, upnpip, upport);
410
      if (mylib) {
434
        if (mylib) {
411
          break;
435
            break;
412
      }
436
        }
413
      sleep(libretrysecs);
437
        sleep(libretrysecs);
414
      libretrysecs = MIN(2*libretrysecs, 120);
438
        libretrysecs = MIN(2*libretrysecs, 120);
415
  }
439
    }
416
440
417
  if (!mylib->ok()) {
441
    if (!mylib->ok()) {
418
      LOGFAT("Lib init failed: " <<
442
        LOGFAT("Lib init failed: " <<
419
             mylib->errAsString("main", mylib->getInitError()) << endl);
443
               mylib->errAsString("main", mylib->getInitError()) << endl);
420
      return 1;
444
        return 1;
421
  }
445
    }
422
446
423
  //string upnplogfilename("/tmp/upmpdcli_libupnp.log");
447
    //string upnplogfilename("/tmp/upmpdcli_libupnp.log");
424
  //mylib->setLogFileName(upnplogfilename, LibUPnP::LogLevelDebug);
448
    //mylib->setLogFileName(upnplogfilename, LibUPnP::LogLevelDebug);
425
449
426
  // Create unique ID
450
    // Create unique ID
427
  string UUID = LibUPnP::makeDevUUID(friendlyname, hwaddr);
451
    string UUID = LibUPnP::makeDevUUID(friendlyname, hwaddr);
428
452
429
  // Read our XML data to make it available from the virtual directory
453
    // Read our XML data to make it available from the virtual directory
430
  if (openhome) {
454
    if (openhome) {
431
      xmlfilenames.insert(xmlfilenames.end(), ohxmlfilenames.begin(),
455
        xmlfilenames.insert(xmlfilenames.end(), ohxmlfilenames.begin(),
432
                          ohxmlfilenames.end());
456
                            ohxmlfilenames.end());
433
  }
457
    }
434
458
435
  {
459
    {
436
      string protofile = path_cat(datadir, "protocolinfo.txt");
460
        string protofile = path_cat(datadir, "protocolinfo.txt");
437
      if (!read_protocolinfo(protofile, upmpdProtocolInfo)) {
461
        if (!read_protocolinfo(protofile, upmpdProtocolInfo)) {
438
          LOGFAT("Failed reading protocol info from " << protofile << endl);
462
            LOGFAT("Failed reading protocol info from " << protofile << endl);
439
          return 1;
463
            return 1;
440
      }
464
        }
441
  }
465
    }
442
          
466
            
443
  string reason;
467
    string reason;
444
  unordered_map<string, string> xmlfiles;
468
    unordered_map<string, string> xmlfiles;
445
  for (unsigned int i = 0; i < xmlfilenames.size(); i++) {
469
    for (unsigned int i = 0; i < xmlfilenames.size(); i++) {
446
      string filename = path_cat(datadir, xmlfilenames[i]);
470
        string filename = path_cat(datadir, xmlfilenames[i]);
447
      string data;
471
        string data;
448
      if (!file_to_string(filename, data, &reason)) {
472
        if (!file_to_string(filename, data, &reason)) {
449
          LOGFAT("Failed reading " << filename << " : " << reason << endl);
473
            LOGFAT("Failed reading " << filename << " : " << reason << endl);
450
          return 1;
474
            return 1;
451
      }
475
        }
452
      if (i == 0) {
476
        if (i == 0) {
453
          // Special for description: set UUID and friendlyname
477
            // Special for description: set UUID and friendlyname
454
          data = regsub1("@UUID@", data, UUID);
478
            data = regsub1("@UUID@", data, UUID);
455
          data = regsub1("@FRIENDLYNAME@", data, friendlyname);
479
            data = regsub1("@FRIENDLYNAME@", data, friendlyname);
456
          if (openhome) 
480
            if (openhome) 
457
              data = regsub1("@OPENHOME@", data, ohDesc);
481
                data = regsub1("@OPENHOME@", data, ohDesc);
458
      }
482
        }
459
      xmlfiles[xmlfilenames[i]] = data;
483
        xmlfiles[xmlfilenames[i]] = data;
460
  }
484
    }
461
  unsigned int options = UpMpd::upmpdNone;
485
    unsigned int options = UpMpd::upmpdNone;
462
  if (ownqueue)
486
    if (ownqueue)
463
      options |= UpMpd::upmpdOwnQueue;
487
        options |= UpMpd::upmpdOwnQueue;
464
  if (openhome)
488
    if (openhome)
465
      options |= UpMpd::upmpdDoOH;
489
        options |= UpMpd::upmpdDoOH;
466
  if (ohmetapersist)
490
    if (ohmetapersist)
467
      options |= UpMpd::upmpdOhMetaPersist;
491
        options |= UpMpd::upmpdOhMetaPersist;
468
492
469
  // Initialize the UPnP device object.
493
    // Initialize the UPnP device object.
470
  UpMpd device(string("uuid:") + UUID, friendlyname, 
494
    UpMpd device(string("uuid:") + UUID, friendlyname, 
471
               xmlfiles, mpdclip, options, mcfn);
495
                 xmlfiles, mpdclip, options, mcfn);
496
    dev = &device;
472
497
473
  // And forever generate state change events.
498
    // And forever generate state change events.
474
  LOGDEB("Entering event loop" << endl);
499
    LOGDEB("Entering event loop" << endl);
500
    setupsigs();
475
  device.eventloop();
501
    device.eventloop();
502
    LOGDEB("Event loop returned" << endl);
476
503
477
  return 0;
504
    return 0;
478
}
505
}
479
480
/* Local Variables: */
481
/* mode: c++ */
482
/* c-basic-offset: 4 */
483
/* tab-width: 4 */
484
/* indent-tabs-mode: t */
485
/* End: */