Switch to unified view

a/libupnpp/device.cxx b/libupnpp/device.cxx
...
...
18
using namespace std;
18
using namespace std;
19
19
20
#include "upnpplib.hxx"
20
#include "upnpplib.hxx"
21
#include "vdir.hxx"
21
#include "vdir.hxx"
22
#include "device.hxx"
22
#include "device.hxx"
23
#include "log.hxx"
23
24
24
unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
25
unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
26
25
static string xmlquote(const string& in)
27
static string xmlquote(const string& in)
26
{
28
{
27
    string out;
29
    string out;
28
    for (unsigned int i = 0; i < in.size(); i++) {
30
    for (unsigned int i = 0; i < in.size(); i++) {
29
        switch(in[i]) {
31
        switch(in[i]) {
...
...
36
        }
38
        }
37
    }
39
    }
38
    return out;
40
    return out;
39
}
41
}
40
42
41
42
static bool vectorstoargslists(const vector<string>& names, 
43
static bool vectorstoargslists(const vector<string>& names, 
43
                               const vector<string>& values,
44
                               const vector<string>& values,
44
                               vector<string>& qvalues,
45
                               vector<string>& qvalues,
45
                               vector<const char *>& cnames,
46
                               vector<const char *>& cnames,
46
                               vector<const char *>& cvalues)
47
                               vector<const char *>& cvalues)
47
{
48
{
48
    if (names.size() != values.size()) {
49
    if (names.size() != values.size()) {
49
        cerr << "vectorstoargslists: bad sizes" << endl;
50
        LOGERR("vectorstoargslists: bad sizes" << endl);
50
        return false;
51
        return false;
51
    }
52
    }
52
53
53
    cnames.reserve(names.size());
54
    cnames.reserve(names.size());
54
    for (unsigned int i = 0; i < names.size(); i++) {
55
    for (unsigned int i = 0; i < names.size(); i++) {
...
...
66
67
67
UpnpDevice::UpnpDevice(const string& deviceId, 
68
UpnpDevice::UpnpDevice(const string& deviceId, 
68
                       const unordered_map<string, string>& xmlfiles)
69
                       const unordered_map<string, string>& xmlfiles)
69
    : m_deviceId(deviceId)
70
    : m_deviceId(deviceId)
70
{
71
{
71
    cerr << "UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl;
72
    //LOGDEB("UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl);
72
73
73
    m_lib = LibUPnP::getLibUPnP(true);
74
    m_lib = LibUPnP::getLibUPnP(true);
74
    if (!m_lib) {
75
    if (!m_lib) {
75
        cerr << " Can't get LibUPnP" << endl;
76
        LOGFAT(" Can't get LibUPnP" << endl);
76
        return;
77
        return;
77
    }
78
    }
78
    if (!m_lib->ok()) {
79
    if (!m_lib->ok()) {
79
        cerr << "Lib init failed: " <<
80
        LOGFAT("Lib init failed: " <<
80
            m_lib->errAsString("main", m_lib->getInitError()) << endl;
81
               m_lib->errAsString("main", m_lib->getInitError()) << endl);
81
        m_lib = 0;
82
        m_lib = 0;
82
        return;
83
        return;
83
    }
84
    }
84
85
85
    if (o_devices.empty()) {
86
    if (o_devices.empty()) {
...
...
89
    m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
90
    m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
90
    }
91
    }
91
92
92
    VirtualDir* theVD = VirtualDir::getVirtualDir();
93
    VirtualDir* theVD = VirtualDir::getVirtualDir();
93
    if (theVD == 0) {
94
    if (theVD == 0) {
94
        cerr << "UpnpDevice::UpnpDevice: can't get VirtualDir" << endl;
95
        LOGFAT("UpnpDevice::UpnpDevice: can't get VirtualDir" << endl);
95
        return;
96
        return;
96
    }
97
    }
97
98
98
    unordered_map<string,string>::const_iterator it = 
99
    unordered_map<string,string>::const_iterator it = 
99
        xmlfiles.find("description.xml");
100
        xmlfiles.find("description.xml");
100
    if (it == xmlfiles.end()) {
101
    if (it == xmlfiles.end()) {
101
        cerr << "UpnpDevice::UpnpDevice: no description.xml found in xmlfiles"
102
        LOGFAT("UpnpDevice::UpnpDevice: no description.xml found in xmlfiles"
102
             << endl;
103
               << endl);
103
        return;
104
        return;
104
    } 
105
    } 
105
106
106
    const string& description = it->second;
107
    const string& description = it->second;
107
108
...
...
113
    m_lib->setupWebServer(description);
114
    m_lib->setupWebServer(description);
114
115
115
    o_devices[m_deviceId] = this;
116
    o_devices[m_deviceId] = this;
116
}
117
}
117
118
119
static PTMutexInit cblock;
120
118
// Main libupnp callback: use the device id and call the right device
121
// Main libupnp callback: use the device id and call the right device
119
int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
122
int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
120
{
123
{
121
    //cerr << "UpnpDevice::sCallBack" << endl;
124
    //LOGDEB("UpnpDevice::sCallBack" << endl);
125
    PTMutexLocker lock(cblock);
122
126
123
    string deviceid;
127
    string deviceid;
124
    switch (et) {
128
    switch (et) {
125
    case UPNP_CONTROL_ACTION_REQUEST:
129
    case UPNP_CONTROL_ACTION_REQUEST:
126
        deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
130
        deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
...
...
133
    case UPNP_EVENT_SUBSCRIPTION_REQUEST:
137
    case UPNP_EVENT_SUBSCRIPTION_REQUEST:
134
        deviceid = ((struct  Upnp_Subscription_Request*)evp)->UDN;
138
        deviceid = ((struct  Upnp_Subscription_Request*)evp)->UDN;
135
    break;
139
    break;
136
140
137
    default:
141
    default:
138
        cerr << "UpnpDevice::sCallBack: unknown event " << et << endl;
142
        LOGERR("UpnpDevice::sCallBack: unknown event " << et << endl);
139
        return UPNP_E_INVALID_PARAM;
143
        return UPNP_E_INVALID_PARAM;
140
    }
144
    }
141
    // cerr << "UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl;
145
    // LOGDEB("UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl);
142
146
143
    unordered_map<std::string, UpnpDevice *> *devmap = 
147
    unordered_map<std::string, UpnpDevice *> *devmap = 
144
        (unordered_map<std::string, UpnpDevice *> *) tok;
148
        (unordered_map<std::string, UpnpDevice *> *) tok;
145
    unordered_map<std::string, UpnpDevice *>::iterator it =
149
    unordered_map<std::string, UpnpDevice *>::iterator it =
146
        o_devices.find(deviceid);
150
        o_devices.find(deviceid);
147
151
148
    if (it == o_devices.end()) {
152
    if (it == o_devices.end()) {
149
        cerr << "UpnpDevice::sCallBack: Device not found: [" << 
153
        LOGERR("UpnpDevice::sCallBack: Device not found: [" << 
150
            deviceid << "]" << endl;
154
               deviceid << "]" << endl);
151
        return UPNP_E_INVALID_PARAM;
155
        return UPNP_E_INVALID_PARAM;
152
    }
156
    }
153
    // cerr << "UpnpDevice::sCallBack: device found: [" << it->second 
157
    // LOGDEB("UpnpDevice::sCallBack: device found: [" << it->second 
154
    // << "]" << endl;
158
    // << "]" << endl);
155
    return (it->second)->callBack(et, evp);
159
    return (it->second)->callBack(et, evp);
156
}
160
}
157
161
158
static PTMutexInit cblock;
159
int UpnpDevice::callBack(Upnp_EventType et, void* evp)
162
int UpnpDevice::callBack(Upnp_EventType et, void* evp)
160
{
163
{
161
    PTMutexLocker lock(cblock);
162
    cerr << "UpnpDevice::callBack: evt type:" << 
163
        LibUPnP::evTypeAsString(et).c_str() << endl;
164
165
    switch (et) {
164
    switch (et) {
166
    case UPNP_CONTROL_ACTION_REQUEST:
165
    case UPNP_CONTROL_ACTION_REQUEST:
167
    {
166
    {
168
        struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
167
        struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
169
        cerr << "UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
168
        LOGDEB("UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
170
            " Params: " << ixmlPrintDocument(act->ActionRequest) << endl;
169
               ". Params: " << ixmlPrintDocument(act->ActionRequest) << endl);
171
170
172
        unordered_map<string, string>::const_iterator servit = 
171
        unordered_map<string, string>::const_iterator servit = 
173
            m_serviceTypes.find(act->ServiceID);
172
            m_serviceTypes.find(act->ServiceID);
174
        if (servit == m_serviceTypes.end()) {
173
        if (servit == m_serviceTypes.end()) {
175
            cerr << "Bad serviceID" << endl;
174
            LOGERR("Bad serviceID" << endl);
176
            return UPNP_E_INVALID_PARAM;
175
            return UPNP_E_INVALID_PARAM;
177
        }
176
        }
178
        const string& servicetype = servit->second;
177
        const string& servicetype = servit->second;
179
178
180
        unordered_map<string, soapfun>::iterator callit = 
179
        unordered_map<string, soapfun>::iterator callit = 
181
            m_calls.find(act->ActionName);
180
            m_calls.find(act->ActionName);
182
        if (callit == m_calls.end()) {
181
        if (callit == m_calls.end()) {
183
            cerr << "No such action: " << act->ActionName << endl;
182
            LOGINF("No such action: " << act->ActionName << endl);
184
            return UPNP_E_INVALID_PARAM;
183
            return UPNP_E_INVALID_PARAM;
185
        }
184
        }
186
185
187
        SoapArgs sc;
186
        SoapArgs sc;
188
        if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
187
        if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
189
            cerr << "Error decoding Action call arguments" << endl;
188
            LOGERR("Error decoding Action call arguments" << endl);
190
            return UPNP_E_INVALID_PARAM;
189
            return UPNP_E_INVALID_PARAM;
191
        }
190
        }
192
        SoapData dt;
191
        SoapData dt;
193
        dt.name = act->ActionName;
192
        dt.name = act->ActionName;
194
        dt.serviceType = servicetype;
193
        dt.serviceType = servicetype;
195
194
196
        // Call the action routine
195
        // Call the action routine
197
        int ret = callit->second(sc, dt);
196
        int ret = callit->second(sc, dt);
198
        if (ret != UPNP_E_SUCCESS) {
197
        if (ret != UPNP_E_SUCCESS) {
199
            cerr << "Action failed: " << sc.name << endl;
198
            LOGERR("Action failed: " << sc.name << endl);
200
            return ret;
199
            return ret;
201
        }
200
        }
202
201
203
        // Encode result data
202
        // Encode result data
204
        act->ActionResult = buildSoapBody(dt);
203
        act->ActionResult = buildSoapBody(dt);
205
        cerr << "Response data: " << 
204
        //LOGDEB("Response data: " << 
206
            ixmlPrintDocument(act->ActionResult) << endl;
205
        //   ixmlPrintDocument(act->ActionResult) << endl);
207
206
208
        return ret;
207
        return ret;
209
210
    }
208
    }
211
    break;
209
    break;
212
210
213
    case UPNP_CONTROL_GET_VAR_REQUEST:
211
    case UPNP_CONTROL_GET_VAR_REQUEST:
214
        // Note that the "Control: query for variable" action is
212
        // Note that the "Control: query for variable" action is
215
        // deprecated (upnp arch v1), and we should never get these.
213
        // deprecated (upnp arch v1), and we should never get these.
216
    {
214
    {
217
        struct Upnp_State_Var_Request *act = 
215
        struct Upnp_State_Var_Request *act = 
218
            (struct Upnp_State_Var_Request *)evp;
216
            (struct Upnp_State_Var_Request *)evp;
219
        cerr << "UPNP_CONTROL_GET_VAR__REQUEST?: " << act->StateVarName << endl;
217
        LOGDEB("UPNP_CONTROL_GET_VAR__REQUEST?: " << act->StateVarName << endl);
220
    }
218
    }
221
    break;
219
    break;
222
220
223
    case UPNP_EVENT_SUBSCRIPTION_REQUEST:
221
    case UPNP_EVENT_SUBSCRIPTION_REQUEST:
224
    {
222
    {
225
        struct Upnp_Subscription_Request *act = 
223
        struct Upnp_Subscription_Request *act = 
226
            (struct  Upnp_Subscription_Request*)evp;
224
            (struct  Upnp_Subscription_Request*)evp;
227
        cerr << "UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl;
225
        LOGDEB("UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl);
228
226
229
        vector<string> names, values, qvalues;
227
        vector<string> names, values, qvalues;
230
        if (!getEventData(true, act->ServiceId, names, values)) {
228
        if (!getEventData(true, act->ServiceId, names, values)) {
231
            break;
229
            break;
232
        }
230
        }
...
...
235
        int ret = 
233
        int ret = 
236
            UpnpAcceptSubscription(m_lib->getdvh(), act->UDN, act->ServiceId,
234
            UpnpAcceptSubscription(m_lib->getdvh(), act->UDN, act->ServiceId,
237
                                   &cnames[0], &cvalues[0],
235
                                   &cnames[0], &cvalues[0],
238
                                   int(cnames.size()), act->Sid);
236
                                   int(cnames.size()), act->Sid);
239
        if (ret != UPNP_E_SUCCESS) {
237
        if (ret != UPNP_E_SUCCESS) {
240
            cerr << "UpnpDevice::callBack: UpnpAcceptSubscription failed: " 
238
            LOGERR("UpnpDevice::callBack: UpnpAcceptSubscription failed: " 
241
                 << ret << endl;
239
                   << ret << endl);
242
        }
240
        }
243
241
244
        return ret;
242
        return ret;
245
    }
243
    }
246
    break;
244
    break;
247
245
248
    default:
246
    default:
249
        cerr << "UpnpDevice::callBack: unknown op" << endl;
247
        LOGINF("UpnpDevice::callBack: unknown libupnp event type: " <<
248
               LibUPnP::evTypeAsString(et).c_str() << endl);
250
        return UPNP_E_INVALID_PARAM;
249
        return UPNP_E_INVALID_PARAM;
251
    }
250
    }
252
    return UPNP_E_INVALID_PARAM;
251
    return UPNP_E_INVALID_PARAM;
253
}
252
}
254
253
255
void UpnpDevice::addServiceType(const std::string& serviceId, 
254
void UpnpDevice::addServiceType(const std::string& serviceId, 
256
                                const std::string& serviceType)
255
                                const std::string& serviceType)
257
{
256
{
258
    cerr << "UpnpDevice::addServiceType: [" << 
257
    //LOGDEB("UpnpDevice::addServiceType: [" << 
259
        serviceId << "] -> [" << serviceType << endl;
258
    //    serviceId << "] -> [" << serviceType << endl);
260
    m_serviceTypes[serviceId] = serviceType;
259
    m_serviceTypes[serviceId] = serviceType;
261
}
260
}
262
261
263
void UpnpDevice::addActionMapping(const std::string& actName, soapfun fun)
262
void UpnpDevice::addActionMapping(const std::string& actName, soapfun fun)
264
{
263
{
265
    // cerr << "UpnpDevice::addActionMapping:" << actName << endl;
264
    // LOGDEB("UpnpDevice::addActionMapping:" << actName << endl);
266
    m_calls[actName] = fun;
265
    m_calls[actName] = fun;
267
}
266
}
268
267
269
void UpnpDevice::notifyEvent(const string& serviceId,
268
void UpnpDevice::notifyEvent(const string& serviceId,
270
                             const vector<string>& names, 
269
                             const vector<string>& names, 
271
                             const vector<string>& values)
270
                             const vector<string>& values)
272
{
271
{
273
    cerr << "UpnpDevice::notifyEvent" << endl;
272
    LOGDEB("UpnpDevice::notifyEvent: " 
274
273
           << (names.empty()?"Empty names??":names[0]) << endl);
274
    if (names.empty())
275
        return;
275
    vector<const char *> cnames, cvalues;
276
    vector<const char *> cnames, cvalues;
276
    vector<string> qvalues;
277
    vector<string> qvalues;
277
    vectorstoargslists(names, values, qvalues, cnames, cvalues);
278
    vectorstoargslists(names, values, qvalues, cnames, cvalues);
278
279
279
    int ret = UpnpNotify(m_lib->getdvh(), m_deviceId.c_str(), 
280
    int ret = UpnpNotify(m_lib->getdvh(), m_deviceId.c_str(), 
280
                         serviceId.c_str(), &cnames[0], &cvalues[0],
281
                         serviceId.c_str(), &cnames[0], &cvalues[0],
281
                         int(cnames.size()));
282
                         int(cnames.size()));
282
    if (ret != UPNP_E_SUCCESS) {
283
    if (ret != UPNP_E_SUCCESS) {
283
        cerr << "UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl;
284
        LOGERR("UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl);
285
    }
286
}
287
288
static pthread_cond_t evloopcond = PTHREAD_COND_INITIALIZER;
289
290
static void timespec_addnanos(struct timespec *ts, int nanos)
291
{
292
    ts->tv_nsec += nanos;
293
    if (ts->tv_nsec > 1000 * 1000 * 1000) {
294
        int secs = ts->tv_nsec / (1000 * 1000 * 1000);
295
        ts->tv_sec += secs;
296
        ts->tv_nsec -= secs * (1000 * 1000 * 1000);
284
    }
297
    } 
285
}
298
}
299
int timespec_diffms(const struct timespec& old, const struct timespec& recent)
300
{
301
    return (recent.tv_sec - old.tv_sec) * 1000 + 
302
        (recent.tv_nsec - old.tv_nsec) / (1000 * 1000);
303
}
286
304
305
// Loop on services, and poll each for changed data. Generate event
306
// only if changed data exists. Every 3 S we generate an artificial
307
// event with all the current state.
287
void UpnpDevice::eventloop()
308
void UpnpDevice::eventloop()
288
{
309
{
289
    struct timespec duration;
290
    duration.tv_sec = 0;
291
    duration.tv_nsec = 500 * 1000 * 1000; // 1/2 S
292
    while (nanosleep(&duration, 0) == 0) {
293
        static int count = 0;
310
    int count = 0;
294
        //cerr << "UpnpDevice::eventloop: " << count++ << endl;
311
    const int loopwait_ms = 500; // Polling mpd every 1/2 S
312
    const int nloopstofull = 10; // Full state every 5 S
313
    struct timespec wkuptime, earlytime;
314
    bool didearly = false;
315
316
    for (;;) {
317
        clock_gettime(CLOCK_REALTIME, &wkuptime);
318
        timespec_addnanos(&wkuptime, loopwait_ms * 1000 * 1000);
319
320
        //LOGDEB("eventloop: now " << time(0) << " wkup at "<< 
321
        //    wkuptime.tv_sec << " S " << wkuptime.tv_nsec << " ns" << endl);
322
295
        PTMutexLocker lock(cblock);
323
        PTMutexLocker lock(cblock);
324
        int err = pthread_cond_timedwait(&evloopcond, lock.getMutex(), 
325
                                         &wkuptime);
326
        if (err && err != ETIMEDOUT) {
327
            LOGINF("UpnpDevice:eventloop: wait errno " << errno << endl);
328
            break;
329
        } else if (err == 0) {
330
            // Early wakeup. Only does something if it did not already
331
            // happen recently
332
            if (didearly) {
333
                int millis = timespec_diffms(earlytime, wkuptime);
334
                if (millis < loopwait_ms) {
335
                    // Do nothing. didearly stays true
336
                    // LOGDEB("eventloop: early, previous too close "<<endl);
337
                    continue;
338
                } else {
339
                    // had an early wakeup previously, but it was a
340
                    // long time ago. Update state and wakeup
341
                    // LOGDEB("eventloop: early, previous is old "<<endl);
342
                    earlytime = wkuptime;
343
                }
344
            } else {
345
                // Early wakeup, previous one was normal. Remember.
346
                // LOGDEB("eventloop: early, no previous" << endl);
347
                didearly = true;
348
                earlytime = wkuptime;
349
            }
350
        } else {
351
            // Normal wakeup
352
            // LOGDEB("eventloop: normal wakeup" << endl);
353
            didearly = false;
354
        }
355
356
        count++;
357
        bool all = count && ((count % nloopstofull) == 0);
358
        //LOGDEB("UpnpDevice::eventloop count "<<count<<" all "<<all<<endl);
359
296
        for (unordered_map<string, string>::const_iterator it = 
360
        for (unordered_map<string, string>::const_iterator it = 
297
                 m_serviceTypes.begin();
298
             it != m_serviceTypes.end(); it++) {
361
                 m_serviceTypes.begin(); it != m_serviceTypes.end(); it++) {
299
            vector<string> names, values;
362
            vector<string> names, values;
300
            if (!getEventData(false, it->first, names, values) || 
363
            if (!getEventData(all, it->first, names, values) || names.empty()) {
301
                names.empty()) {
302
                continue;
364
                continue;
303
            }
365
            }
304
            notifyEvent(it->first, names, values);
366
            notifyEvent(it->first, names, values);
305
        }
367
        }
306
    }
368
    }
307
}
369
}
370
371
void UpnpDevice::loopWakeup()
372
{
373
    pthread_cond_broadcast(&evloopcond);
374
}
375