static int handle_var_request(struct upnp_device *priv,
			      struct Upnp_State_Var_Request *var_event) {
	struct service *srv = find_service(priv->upnp_device_descriptor,
					   var_event->ServiceID);
	if (srv == NULL) {
		var_event->ErrCode = UPNP_SOAP_E_INVALID_ARGS;
		return -1;
	}

	ithread_mutex_lock(srv->service_mutex);

	char *result = NULL;
	const int var_count =
		VariableContainer_get_num_vars(srv->variable_container);
	for (int i = 0; i < var_count; ++i) {
		const char *name;
		const char *value =
			VariableContainer_get(srv->variable_container, i, &name);
		if (value && strcmp(var_event->StateVarName, name) == 0) {
			result = strdup(value);
			break;
		}
	}

	ithread_mutex_unlock(srv->service_mutex);

	var_event->CurrentVal = result;
	var_event->ErrCode = (result == NULL)
		? UPNP_SOAP_E_INVALID_VAR
		: UPNP_E_SUCCESS;
	Log_info("upnp", "Variable request %s -> %s (%s)",
		 var_event->StateVarName, result, var_event->ServiceID);
	return 0;
}
void upnp_append_variable(struct action_event *event,
                          int varnum, const char *paramname)
{
	const char *value;
	struct service *service = event->service;

	assert(event != NULL);
	assert(paramname != NULL);

	ithread_mutex_lock(service->service_mutex);

	value = VariableContainer_get(service->variable_container, varnum, NULL);
	assert(value != NULL);   // triggers on invalid variable.
	upnp_add_response(event, paramname, value);

	ithread_mutex_unlock(service->service_mutex);
}
static const char *get_var(transport_variable_t varnum) {
	return VariableContainer_get(state_variables_, varnum, NULL, NULL);
}
static int handle_subscription_request(struct upnp_device *priv,
                                       struct Upnp_Subscription_Request
                                              *sr_event)
{
	struct service *srv;
	int rc;

	assert(priv != NULL);

	Log_info("upnp", "Subscription request for %s (%s)",
		 sr_event->ServiceId, sr_event->UDN);

	srv = find_service(priv->upnp_device_descriptor, sr_event->ServiceId);
	if (srv == NULL) {
		Log_error("upnp", "%s: Unknown service '%s'", __FUNCTION__,
			  sr_event->ServiceId);
		return -1;
	}

	int result = -1;
	ithread_mutex_lock(&(priv->device_mutex));

	// There is really only one variable evented: LastChange
	const char *eventvar_names[] = {
		"LastChange",
		NULL
	};
	const char *eventvar_values[] = {
		NULL, NULL
	};

	// Build the current state of the variables as one gigantic initial
	// LastChange update.
	ithread_mutex_lock(srv->service_mutex);
	const int var_count =
		VariableContainer_get_num_vars(srv->variable_container);
	// TODO(hzeller): maybe use srv->last_change directly ?
	upnp_last_change_builder_t *builder = UPnPLastChangeBuilder_new(srv->event_xml_ns);
	for (int i = 0; i < var_count; ++i) {
		const char *name;
		const char *value =
			VariableContainer_get(srv->variable_container, i, &name);
		// Send over all variables except "LastChange" itself. Also all
		// A_ARG_TYPE variables are not evented.
		if (value && strcmp("LastChange", name) != 0
		    && strncmp("A_ARG_TYPE_", name, strlen("A_ARG_TYPE_")) != 0) {
			UPnPLastChangeBuilder_add(builder, name, value);
		}
	}
	ithread_mutex_unlock(srv->service_mutex);
	char *xml_value = UPnPLastChangeBuilder_to_xml(builder);
	Log_info("upnp", "Initial variable sync: %s", xml_value);
	eventvar_values[0] = xmlescape(xml_value, 0);
	free(xml_value);
	UPnPLastChangeBuilder_delete(builder);

	rc = UpnpAcceptSubscription(priv->device_handle,
				    sr_event->UDN, sr_event->ServiceId,
				    eventvar_names, eventvar_values, 1,
				    sr_event->Sid);
	if (rc == UPNP_E_SUCCESS) {
		result = 0;
	} else {
		Log_error("upnp", "Accept Subscription Error: %s (%d)",
			  UpnpGetErrorMessage(rc), rc);
	}

	ithread_mutex_unlock(&(priv->device_mutex));

	free((char*)eventvar_values[0]);

	return result;
}