Switch to unified view

a/libupnpp/device/device.cxx b/libupnpp/device/device.cxx
...
...
31
using namespace UPnPP;
31
using namespace UPnPP;
32
32
33
namespace UPnPProvider {
33
namespace UPnPProvider {
34
34
35
unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
35
unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
36
PTMutexInit o_devices_lock;
36
37
37
static bool vectorstoargslists(const vector<string>& names, 
38
static bool vectorstoargslists(const vector<string>& names, 
38
                               const vector<string>& values,
39
                               const vector<string>& values,
39
                               vector<string>& qvalues,
40
                               vector<string>& qvalues,
40
                               vector<const char *>& cnames,
41
                               vector<const char *>& cnames,
...
...
60
61
61
static const int expiretime = 3600;
62
static const int expiretime = 3600;
62
63
63
UpnpDevice::UpnpDevice(const string& deviceId, 
64
UpnpDevice::UpnpDevice(const string& deviceId, 
64
                       const unordered_map<string, string>& xmlfiles)
65
                       const unordered_map<string, string>& xmlfiles)
65
    : m_deviceId(deviceId), m_needExit(false)
66
    : m_deviceId(deviceId), m_needExit(false), m_evloopcond(PTHREAD_COND_INITIALIZER)
66
{
67
{
67
    //LOGDEB("UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl);
68
    //LOGDEB("UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl);
68
69
69
    m_lib = LibUPnP::getLibUPnP(true);
70
    m_lib = LibUPnP::getLibUPnP(true);
70
    if (!m_lib) {
71
    if (!m_lib) {
...
...
76
               m_lib->errAsString("main", m_lib->getInitError()) << endl);
77
               m_lib->errAsString("main", m_lib->getInitError()) << endl);
77
        m_lib = 0;
78
        m_lib = 0;
78
        return;
79
        return;
79
    }
80
    }
80
81
82
    {
83
        PTMutexLocker lock(o_devices_lock);
81
    if (o_devices.empty()) {
84
        if (o_devices.empty()) {
82
        // First call: init callbacks
85
            // First call: init callbacks
83
        m_lib->registerHandler(UPNP_CONTROL_ACTION_REQUEST, sCallBack, this);
86
            m_lib->registerHandler(UPNP_CONTROL_ACTION_REQUEST, sCallBack, this);
84
  m_lib->registerHandler(UPNP_CONTROL_GET_VAR_REQUEST, sCallBack, this);
87
            m_lib->registerHandler(UPNP_CONTROL_GET_VAR_REQUEST, sCallBack, this);
85
  m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
88
            m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
89
        }
90
        o_devices[m_deviceId] = this;
86
    }
91
    }
87
92
88
    VirtualDir* theVD = VirtualDir::getVirtualDir();
93
    VirtualDir* theVD = VirtualDir::getVirtualDir();
89
    if (theVD == 0) {
94
    if (theVD == 0) {
90
        LOGFAT("UpnpDevice::UpnpDevice: can't get VirtualDir" << endl);
95
        LOGFAT("UpnpDevice::UpnpDevice: can't get VirtualDir" << endl);
...
...
112
    }
117
    }
113
118
114
    if ((ret = UpnpSendAdvertisement(m_dvh, expiretime)) != 0) {
119
    if ((ret = UpnpSendAdvertisement(m_dvh, expiretime)) != 0) {
115
        LOGERR("UpnpDevice: UpnpSendAdvertisement error: " << ret << endl);
120
        LOGERR("UpnpDevice: UpnpSendAdvertisement error: " << ret << endl);
116
    }
121
    }
117
118
    o_devices[m_deviceId] = this;
119
}
122
}
120
123
121
UpnpDevice::~UpnpDevice()
124
UpnpDevice::~UpnpDevice()
122
{
125
{
123
    UpnpUnRegisterRootDevice(m_dvh);
126
    UpnpUnRegisterRootDevice(m_dvh);
124
}
125
127
126
static PTMutexInit cblock;
128
    PTMutexLocker lock(o_devices_lock);
129
    unordered_map<std::string, UpnpDevice *>::iterator it = o_devices.find(m_deviceId);
130
    if (it != o_devices.end())
131
        o_devices.erase(it);
132
}
127
133
128
// Main libupnp callback: use the device id and call the right device
134
// Main libupnp callback: use the device id and call the right device
129
int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
135
int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
130
{
136
{
131
    //LOGDEB("UpnpDevice::sCallBack" << endl);
137
    //LOGDEB("UpnpDevice::sCallBack" << endl);
132
    PTMutexLocker lock(cblock);
133
138
134
    string deviceid;
139
    string deviceid;
135
    switch (et) {
140
    switch (et) {
136
    case UPNP_CONTROL_ACTION_REQUEST:
141
    case UPNP_CONTROL_ACTION_REQUEST:
137
        deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
142
        deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
...
...
149
        LOGERR("UpnpDevice::sCallBack: unknown event " << et << endl);
154
        LOGERR("UpnpDevice::sCallBack: unknown event " << et << endl);
150
        return UPNP_E_INVALID_PARAM;
155
        return UPNP_E_INVALID_PARAM;
151
    }
156
    }
152
    // LOGDEB("UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl);
157
    // LOGDEB("UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl);
153
158
154
    unordered_map<std::string, UpnpDevice *>::iterator it =
159
    unordered_map<std::string, UpnpDevice *>::iterator it;
160
    {
161
        PTMutexLocker lock(o_devices_lock);
162
155
        o_devices.find(deviceid);
163
        it = o_devices.find(deviceid);
156
164
157
    if (it == o_devices.end()) {
165
        if (it == o_devices.end()) {
158
        LOGERR("UpnpDevice::sCallBack: Device not found: [" << 
166
            LOGERR("UpnpDevice::sCallBack: Device not found: [" << 
159
               deviceid << "]" << endl);
167
                   deviceid << "]" << endl);
160
        return UPNP_E_INVALID_PARAM;
168
            return UPNP_E_INVALID_PARAM;
161
    }
169
        }
170
    }
171
162
    // LOGDEB("UpnpDevice::sCallBack: device found: [" << it->second 
172
    // LOGDEB("UpnpDevice::sCallBack: device found: [" << it->second 
163
    // << "]" << endl);
173
    // << "]" << endl);
164
    return (it->second)->callBack(et, evp);
174
    return (it->second)->callBack(et, evp);
175
}
176
177
unordered_map<string, UpnpService*>::const_iterator UpnpDevice::findService(const string& serviceid)
178
{
179
    PTMutexLocker lock(m_lock);
180
    auto servit = m_servicemap.find(serviceid);
181
    if (servit == m_servicemap.end()) {
182
        LOGERR("UpnpDevice: Bad serviceID: " << serviceid << endl);
183
    }
184
    return servit;
165
}
185
}
166
186
167
int UpnpDevice::callBack(Upnp_EventType et, void* evp)
187
int UpnpDevice::callBack(Upnp_EventType et, void* evp)
168
{
188
{
169
    switch (et) {
189
    switch (et) {
170
    case UPNP_CONTROL_ACTION_REQUEST:
190
    case UPNP_CONTROL_ACTION_REQUEST:
171
    {
191
    {
172
        struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
192
        struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
193
173
        DOMString pdoc = ixmlPrintDocument(act->ActionRequest);
194
        DOMString pdoc = ixmlPrintDocument(act->ActionRequest);
174
        LOGDEB("UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
195
        LOGDEB("UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
175
               ". Params: " << pdoc << endl);
196
               ". Params: " << pdoc << endl);
176
        ixmlFreeDOMString(pdoc);
197
        ixmlFreeDOMString(pdoc);
177
198
178
        unordered_map<string, UpnpService*>::const_iterator servit = 
199
        auto servit = findService(act->ServiceID);
179
            m_servicemap.find(act->ServiceID);
180
        if (servit == m_servicemap.end()) {
200
        if (servit == m_servicemap.end()) {
181
            LOGERR("Bad serviceID" << endl);
182
            return UPNP_E_INVALID_PARAM;
201
            return UPNP_E_INVALID_PARAM;
183
        }
202
        }
184
        const string& servicetype = servit->second->getServiceType();
185
203
186
        unordered_map<string, soapfun>::iterator callit = 
187
            m_calls.find(string(act->ActionName) + string(act->ServiceID));
188
        if (callit == m_calls.end()) {
189
            LOGINF("No such action: " << act->ActionName << endl);
190
            return UPNP_E_INVALID_PARAM;
191
        }
192
193
        SoapArgs sc;
194
        if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
195
            LOGERR("Error decoding Action call arguments" << endl);
196
            return UPNP_E_INVALID_PARAM;
197
        }
198
        SoapData dt;
204
        SoapData dt;
205
        {
206
            PTMutexLocker lock(m_lock);
207
            const string& servicetype = servit->second->getServiceType();
208
            auto callit = m_calls.find(string(act->ActionName) + string(act->ServiceID));
209
            if (callit == m_calls.end()) {
210
                LOGINF("UpnpDevice: No such action: " << act->ActionName << endl);
211
                return UPNP_E_INVALID_PARAM;
212
            }
213
214
            SoapArgs sc;
215
            if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
216
                LOGERR("Error decoding Action call arguments" << endl);
217
                return UPNP_E_INVALID_PARAM;
218
            }
199
        dt.name = act->ActionName;
219
            dt.name = act->ActionName;
200
        dt.serviceType = servicetype;
220
            dt.serviceType = servicetype;
201
221
202
        // Call the action routine
222
            // Call the action routine
203
        int ret = callit->second(sc, dt);
223
            int ret = callit->second(sc, dt);
204
        if (ret != UPNP_E_SUCCESS) {
224
            if (ret != UPNP_E_SUCCESS) {
205
            LOGERR("Action failed: " << sc.name << endl);
225
                LOGERR("UpnpDevice: Action failed: " << sc.name << endl);
206
            return ret;
226
                return ret;
227
            }
207
        }
228
        }
208
229
209
        // Encode result data
230
        // Encode result data
210
        act->ActionResult = buildSoapBody(dt);
231
        act->ActionResult = buildSoapBody(dt);
211
232
212
        //{DOMString pdoc = ixmlPrintDocument(act->ActionResult);
233
        //{DOMString pdoc = ixmlPrintDocument(act->ActionResult);
213
        //LOGDEB("Response data: " << pdoc << endl);
234
        //LOGDEB("Response data: " << pdoc << endl);
214
        //ixmlFreeDOMString(pdoc);
235
        //ixmlFreeDOMString(pdoc);
215
        //}
236
        //}
216
237
217
        return ret;
238
        return UPNP_E_SUCCESS;
218
    }
239
    }
219
    break;
240
    break;
220
241
221
    case UPNP_CONTROL_GET_VAR_REQUEST:
242
    case UPNP_CONTROL_GET_VAR_REQUEST:
222
        // Note that the "Control: query for variable" action is
243
        // Note that the "Control: query for variable" action is
...
...
232
    {
253
    {
233
        struct Upnp_Subscription_Request *act = 
254
        struct Upnp_Subscription_Request *act = 
234
            (struct  Upnp_Subscription_Request*)evp;
255
            (struct  Upnp_Subscription_Request*)evp;
235
        LOGDEB("UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl);
256
        LOGDEB("UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl);
236
257
237
        unordered_map<string, UpnpService*>::const_iterator servit = 
258
        auto servit = findService(act->ServiceId);
238
            m_servicemap.find(act->ServiceId);
239
        if (servit == m_servicemap.end()) {
259
        if (servit == m_servicemap.end()) {
240
            LOGERR("Bad serviceID" << endl);
241
            return UPNP_E_INVALID_PARAM;
260
            return UPNP_E_INVALID_PARAM;
242
        }
261
        }
243
262
244
        vector<string> names, values, qvalues;
263
        vector<string> names, values, qvalues;
264
        {
265
            PTMutexLocker lock(m_lock);
245
        if (!servit->second->getEventData(true, names, values)) {
266
            if (!servit->second->getEventData(true, names, values)) {
246
            break;
267
                break;
247
        }
268
            }
269
        }
270
248
        vector<const char *> cnames, cvalues;
271
        vector<const char *> cnames, cvalues;
249
        vectorstoargslists(names, values, qvalues, cnames, cvalues);
272
        vectorstoargslists(names, values, qvalues, cnames, cvalues);
250
        int ret = 
273
        int ret = 
251
            UpnpAcceptSubscription(m_dvh, act->UDN, act->ServiceId,
274
            UpnpAcceptSubscription(m_dvh, act->UDN, act->ServiceId,
252
                                   &cnames[0], &cvalues[0],
275
                                   &cnames[0], &cvalues[0],
...
...
268
    return UPNP_E_INVALID_PARAM;
291
    return UPNP_E_INVALID_PARAM;
269
}
292
}
270
293
271
void UpnpDevice::addService(UpnpService *serv, const std::string& serviceId)
294
void UpnpDevice::addService(UpnpService *serv, const std::string& serviceId)
272
{
295
{
296
    PTMutexLocker lock(m_lock);
273
    m_servicemap[serviceId] = serv;
297
    m_servicemap[serviceId] = serv;
274
    m_serviceids.push_back(serviceId);
298
    m_serviceids.push_back(serviceId);
275
}
299
}
276
300
277
void UpnpDevice::addActionMapping(const UpnpService* serv,
301
void UpnpDevice::addActionMapping(const UpnpService* serv,
278
                                  const std::string& actName,
302
                                  const std::string& actName,
279
                                  soapfun fun)
303
                                  soapfun fun)
280
{
304
{
305
    PTMutexLocker lock(m_lock);
281
    // LOGDEB("UpnpDevice::addActionMapping:" << actName << endl);
306
    // LOGDEB("UpnpDevice::addActionMapping:" << actName << endl);
282
    m_calls[actName + serv->getServiceId()] = fun;
307
    m_calls[actName + serv->getServiceId()] = fun;
283
}
308
}
284
309
285
void UpnpDevice::notifyEvent(const string& serviceId,
310
void UpnpDevice::notifyEvent(const string& serviceId,
...
...
299
                         int(cnames.size()));
324
                         int(cnames.size()));
300
    if (ret != UPNP_E_SUCCESS) {
325
    if (ret != UPNP_E_SUCCESS) {
301
        LOGERR("UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl);
326
        LOGERR("UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl);
302
    }
327
    }
303
}
328
}
304
305
static pthread_cond_t evloopcond = PTHREAD_COND_INITIALIZER;
306
329
307
int timespec_diffms(const struct timespec& old, const struct timespec& recent)
330
int timespec_diffms(const struct timespec& old, const struct timespec& recent)
308
{
331
{
309
    return (recent.tv_sec - old.tv_sec) * 1000 + 
332
    return (recent.tv_sec - old.tv_sec) * 1000 + 
310
        (recent.tv_nsec - old.tv_nsec) / (1000 * 1000);
333
        (recent.tv_nsec - old.tv_nsec) / (1000 * 1000);
...
...
324
}
347
}
325
#endif // ! CLOCK_REALTIME
348
#endif // ! CLOCK_REALTIME
326
349
327
// Loop on services, and poll each for changed data. Generate event
350
// Loop on services, and poll each for changed data. Generate event
328
// only if changed data exists. Every now and then, we generate an
351
// only if changed data exists. Every now and then, we generate an
329
// artificial event with all the current state.
352
// artificial event with all the current state. This is normally run by the main thread.
330
void UpnpDevice::eventloop()
353
void UpnpDevice::eventloop()
331
{
354
{
332
    int count = 0;
355
    int count = 0;
333
    const int loopwait_ms = 1000; // Polling the services every 1 S
356
    const int loopwait_ms = 1000; // Polling the services every 1 S
334
    const int nloopstofull = 10;  // Full state every 10 S
357
    const int nloopstofull = 10;  // Full state every 10 S
...
...
341
        timespec_addnanos(&wkuptime, loopwait_ms * 1000 * 1000);
364
        timespec_addnanos(&wkuptime, loopwait_ms * 1000 * 1000);
342
365
343
        //LOGDEB("eventloop: now " << time(0) << " wkup at "<< 
366
        //LOGDEB("eventloop: now " << time(0) << " wkup at "<< 
344
        //    wkuptime.tv_sec << " S " << wkuptime.tv_nsec << " ns" << endl);
367
        //    wkuptime.tv_sec << " S " << wkuptime.tv_nsec << " ns" << endl);
345
368
346
        PTMutexLocker lock(cblock);
369
        PTMutexLocker lock(m_evlooplock);
347
        int err = pthread_cond_timedwait(&evloopcond, lock.getMutex(), 
370
        int err = pthread_cond_timedwait(&m_evloopcond, lock.getMutex(), 
348
                                         &wkuptime);
371
                                         &wkuptime);
349
        if (m_needExit) {
372
        if (m_needExit) {
350
            break;
373
            break;
351
        } else if (err && err != ETIMEDOUT) {
374
        } else if (err && err != ETIMEDOUT) {
352
            LOGINF("UpnpDevice:eventloop: wait errno " << errno << endl);
375
            LOGINF("UpnpDevice:eventloop: wait errno " << errno << endl);
...
...
380
403
381
        count++;
404
        count++;
382
        bool all = count && ((count % nloopstofull) == 0);
405
        bool all = count && ((count % nloopstofull) == 0);
383
        //LOGDEB("UpnpDevice::eventloop count "<<count<<" all "<<all<<endl);
406
        //LOGDEB("UpnpDevice::eventloop count "<<count<<" all "<<all<<endl);
384
407
385
        for (vector<string>::const_iterator it = 
408
        // We can't lock m_lock around the loop because we don't want
409
        // to hold id when calling notifyEvent() (which calls
410
        // libupnp). This means that we should have a separate lock
411
        // for the services arrays. This would only be useful during
412
        // startup, while we add services, but the event loop is the
413
        // last call the main program will make after adding the
414
        // services, so locking does not seem necessary
386
                 m_serviceids.begin(); it != m_serviceids.end(); it++) {
415
        for (auto it = m_serviceids.begin(); it != m_serviceids.end(); it++) {
387
            vector<string> names, values;
416
            vector<string> names, values;
417
            {
418
                PTMutexLocker lock(m_lock);
388
            UpnpService* serv = m_servicemap[*it];
419
                UpnpService* serv = m_servicemap[*it];
389
            if (!serv->getEventData(all, names, values) || names.empty()) {
420
                if (!serv->getEventData(all, names, values) || names.empty()) {
390
                continue;
421
                    continue;
422
                }
391
            }
423
            }
392
            notifyEvent(*it, names, values);
424
            notifyEvent(*it, names, values);
393
        }
425
        }
394
    }
426
    }
395
}
427
}
396
428
429
// Can't take the loop lock here. We're called from the service and
430
// hold the device lock. The locks would be taken in opposite order, 
431
// causing a potential deadlock:
432
//  - device action takes device lock
433
//  - loop wakes up, takes loop lock
434
//  - blocks on device lock before calling getevent
435
//  - device calls loopwakeup which blocks on loop lock
436
// -> deadlock
397
void UpnpDevice::loopWakeup()
437
void UpnpDevice::loopWakeup()
398
{
438
{
399
    pthread_cond_broadcast(&evloopcond);
439
    pthread_cond_broadcast(&m_evloopcond);
400
}
440
}
401
441
402
void UpnpDevice::shouldExit()
442
void UpnpDevice::shouldExit()
403
{
443
{
404
    m_needExit = true;
444
    m_needExit = true;
405
    pthread_cond_broadcast(&evloopcond);
445
    pthread_cond_broadcast(&m_evloopcond);
406
}
446
}
407
447
408
}// End namespace UPnPProvider
448
}// End namespace UPnPProvider