murphyif: allow multiple streams behind one resource-set id
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / murphyif.c
1 /*
2  * module-murphy-ivi -- PulseAudio module for providing audio routing support
3  * Copyright (c) 2012, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU Lesser General Public License,
7  * version 2.1, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
17  * MA 02110-1301 USA.
18  *
19  */
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <alloca.h>
24 #include <errno.h>
25
26 #include <pulse/utf8.h>
27 #include <pulse/timeval.h>
28 #include <pulsecore/pulsecore-config.h>
29 #include <pulsecore/module.h>
30 #include <pulsecore/llist.h>
31 #include <pulsecore/idxset.h>
32 #include <pulsecore/hashmap.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/sink-input.h>
35 #include <pulsecore/source-output.h>
36
37 #ifdef WITH_MURPHYIF
38 #define WITH_DOMCTL
39 #define WITH_RESOURCES
40 #endif
41
42 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
43 #include <murphy/common/macros.h>
44 #include <murphy/common/mainloop.h>
45 #include <murphy/pulse/pulse-glue.h>
46 #endif
47
48 #ifdef WITH_RESOURCES
49 #include <murphy/resource/protocol.h>
50 #include <murphy/common/transport.h>
51 #include <murphy/resource/protocol.h>
52 #include <murphy/resource/data-types.h>
53 #endif
54
55 #include "murphyif.h"
56 #include "node.h"
57 #include "stream-state.h"
58 #include "utils.h"
59
60 #ifdef WITH_RESOURCES
61 #define INVALID_ID      (~(uint32_t)0)
62 #define INVALID_INDEX   (~(uint32_t)0)
63 #define INVALID_SEQNO   (~(uint32_t)0)
64 #define INVALID_REQUEST (~(uint16_t)0)
65
66 #define DISCONNECTED    -1
67 #define CONNECTED        0
68 #define CONNECTING       1
69
70 #define RESCOL_NAMES    "rsetid,autorel,state,grant,pid,policy"
71 #define RESCOL_RSETID   0
72 #define RESCOL_AUTOREL  1
73 #define RESCOL_STATE    2
74 #define RESCOL_GRANT    3
75 #define RESCOL_PID      4
76 #define RESCOL_POLICY   5
77
78 #define RSET_RELEASE    1
79 #define RSET_ACQUIRE    2
80
81 #define PUSH_VALUE(msg, tag, typ, val) \
82     mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
83
84 #define PUSH_ATTRS(msg, rif, proplist)                  \
85     resource_push_attributes(msg, rif, proplist)
86
87 typedef struct resource_attribute  resource_attribute;
88 typedef struct resource_request    resource_request;
89
90 struct resource_attribute {
91     PA_LLIST_FIELDS(resource_attribute);
92     const char *prop;
93     mrp_attr_t  def;
94 };
95
96 struct resource_request {
97     PA_LLIST_FIELDS(resource_request);
98     uint32_t nodidx;
99     uint16_t reqid;
100     uint32_t seqno;
101 };
102
103 #endif
104
105 typedef struct {
106     const char           *addr;
107 #ifdef WITH_DOMCTL
108     mrp_domctl_t         *ctl;
109     int                   ntable;
110     mrp_domctl_table_t   *tables;
111     int                   nwatch;
112     mrp_domctl_watch_t   *watches;
113     pa_murphyif_watch_cb  watchcb;
114 #endif
115 } domctl_interface;
116
117 typedef struct {
118     const char *name;
119     int         tblidx;
120 } audio_resource_t;
121
122 typedef struct {
123     const char       *addr;
124     audio_resource_t  inpres;
125     audio_resource_t  outres;
126 #ifdef WITH_RESOURCES
127     mrp_transport_t *transp;
128     mrp_sockaddr_t   saddr;
129     socklen_t        alen;
130     const char      *atype;
131     pa_bool_t        connected;
132     struct {
133         pa_time_event *evt;
134         pa_usec_t      period;
135     }                connect;
136     struct {
137         uint32_t request;
138         uint32_t reply;
139     }                seqno;
140     struct {
141         pa_hashmap *rsetid;
142         pa_hashmap *pid;
143     }                nodes;
144     PA_LLIST_HEAD(resource_attribute, attrs);
145     PA_LLIST_HEAD(resource_request, reqs);
146 #endif
147 } resource_interface;
148
149
150 struct pa_murphyif {
151 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
152     mrp_mainloop_t *ml;
153 #endif
154     domctl_interface domctl;
155     resource_interface resource;
156 };
157
158 #ifdef WITH_RESOURCES
159 typedef struct {
160     const char *id;
161     pa_bool_t   autorel;
162     int         state;
163     pa_bool_t   grant;
164     const char *policy;
165 } rset_data;
166
167 typedef struct {
168     char       *pid;
169     mir_node   *node;
170     rset_data  *rset;
171 } pid_hash;
172
173 typedef struct {
174     size_t      nnode;
175     mir_node  **nodes;
176     rset_data  *rset;
177 } rset_hash;
178
179 #endif
180
181
182 #ifdef WITH_DOMCTL
183 static void domctl_connect_notify(mrp_domctl_t *,int,int,const char *,void *);
184 static void domctl_watch_notify(mrp_domctl_t *,mrp_domctl_data_t *,int,void *);
185 static void domctl_dump_data(mrp_domctl_data_t *);
186 #endif
187
188 #ifdef WITH_RESOURCES
189 static void       resource_attribute_destroy(resource_interface *,
190                                              resource_attribute *);
191 static int        resource_transport_connect(resource_interface *);
192 static void       resource_xport_closed_evt(mrp_transport_t *, int, void *);
193
194 static mrp_msg_t *resource_create_request(uint32_t, mrp_resproto_request_t);
195 static pa_bool_t  resource_send_message(resource_interface *, mrp_msg_t *,
196                                         uint32_t, uint16_t, uint32_t);
197 static pa_bool_t  resource_set_create_node(struct userdata *, mir_node *,
198                                            pa_nodeset_resdef *, pa_bool_t);
199 static pa_bool_t  resource_set_create_all(struct userdata *);
200 static pa_bool_t  resource_set_destroy_node(struct userdata *, uint32_t);
201 static pa_bool_t  resource_set_destroy_all(struct userdata *);
202 static void       resource_set_notification(struct userdata *, const char *,
203                                             int, mrp_domctl_value_t **);
204
205 static pa_bool_t  resource_push_attributes(mrp_msg_t *, resource_interface *,
206                                            pa_proplist *);
207
208 static void       resource_recv_msg(mrp_transport_t *, mrp_msg_t *, void *);
209 static void       resource_recvfrom_msg(mrp_transport_t *, mrp_msg_t *,
210                                         mrp_sockaddr_t *, socklen_t, void *);
211 static void       resource_set_create_response(struct userdata *, mir_node *,
212                                                mrp_msg_t *, void **);
213 static void       resource_set_create_response_abort(struct userdata *,
214                                                      mrp_msg_t *, void **);
215
216 static pa_bool_t  resource_fetch_seqno(mrp_msg_t *, void **, uint32_t *);
217 static pa_bool_t  resource_fetch_request(mrp_msg_t *, void **, uint16_t *);
218 static pa_bool_t  resource_fetch_status(mrp_msg_t *, void **, int *);
219 static pa_bool_t  resource_fetch_rset_id(mrp_msg_t *, void **, uint32_t*);
220 static pa_bool_t  resource_fetch_rset_state(mrp_msg_t *, void **,
221                                             mrp_resproto_state_t *);
222 static pa_bool_t  resource_fetch_rset_mask(mrp_msg_t *, void **,
223                                            mrp_resproto_state_t *);
224
225 static pa_bool_t  resource_transport_create(struct userdata *, pa_murphyif *);
226 static void       resource_transport_destroy(pa_murphyif *);
227
228 static void connect_attempt(pa_mainloop_api *, pa_time_event *,
229                              const struct timeval *, void *);
230 static void schedule_connect(struct userdata *, resource_interface *);
231 static void cancel_schedule(struct userdata *, resource_interface *);
232
233 static int  node_put_rset(struct userdata *, mir_node *, rset_data *);
234 static void node_enforce_resource_policy(struct userdata *, mir_node *,
235                                          rset_data *);
236 static rset_data *rset_data_dup(rset_data *);
237 static void rset_data_copy(rset_data *, rset_data *);
238 static void rset_data_update(rset_data *, rset_data *);
239 static void rset_data_free(rset_data *);
240
241 static void        pid_hashmap_free(void *, void *);
242 static int         pid_hashmap_put(struct userdata *, const char *,
243                                    mir_node *, rset_data *);
244 static mir_node   *pid_hashmap_get_node(struct userdata *, const char *);
245 static rset_data  *pid_hashmap_get_rset(struct userdata *, const char *);
246 static mir_node   *pid_hashmap_remove_node(struct userdata *, const char *);
247 static rset_data  *pid_hashmap_remove_rset(struct userdata *, const char *);
248
249 static void       rset_hashmap_free(void *, void *);
250 static rset_hash *rset_hashmap_put(struct userdata *, const char *, mir_node *);
251 static rset_hash *rset_hashmap_get(struct userdata *u, const char *rsetid);
252 static int        rset_hashmap_remove(struct userdata *,const char *,mir_node*);
253
254 #endif
255
256 static pa_proplist *get_node_proplist(struct userdata *, mir_node *);
257 static const char *get_node_pid(struct userdata *, mir_node *);
258
259
260 pa_murphyif *pa_murphyif_init(struct userdata *u,
261                               const char *ctl_addr,
262                               const char *res_addr)
263 {
264     pa_murphyif *murphyif;
265     domctl_interface *dif;
266     resource_interface *rif;
267 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
268     mrp_mainloop_t *ml;
269
270     if (!(ml = mrp_mainloop_pulse_get(u->core->mainloop))) {
271         pa_log_error("Failed to set up murphy mainloop.");
272         return NULL;
273     }
274 #endif
275 #ifdef WITH_RESOURCES
276 #endif
277
278     murphyif = pa_xnew0(pa_murphyif, 1);
279     dif = &murphyif->domctl;
280     rif = &murphyif->resource;
281
282 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
283     murphyif->ml = ml;
284 #endif
285
286     dif->addr = pa_xstrdup(ctl_addr ? ctl_addr:MRP_DEFAULT_DOMCTL_ADDRESS);
287 #ifdef WITH_DOMCTL
288 #endif
289
290     rif->addr = pa_xstrdup(res_addr ? res_addr:RESPROTO_DEFAULT_ADDRESS);
291 #ifdef WITH_RESOURCES
292     rif->alen = mrp_transport_resolve(NULL, rif->addr, &rif->saddr,
293                                       sizeof(rif->saddr), &rif->atype);
294     if (rif->alen <= 0) {
295         pa_log("can't resolve resource transport address '%s'", rif->addr);
296     }
297     else {
298         rif->inpres.tblidx = -1;
299         rif->outres.tblidx = -1;
300         rif->connect.period = 1 * PA_USEC_PER_SEC;
301
302         if (!resource_transport_create(u, murphyif)) {
303             pa_log("failed to create resource transport");
304             schedule_connect(u, rif);
305         }
306         else {
307             if (resource_transport_connect(rif) == DISCONNECTED)
308                 schedule_connect(u, rif);
309         }
310     }    
311
312     rif->seqno.request = 1;
313     rif->nodes.rsetid = pa_hashmap_new(pa_idxset_string_hash_func,
314                                        pa_idxset_string_compare_func);
315     rif->nodes.pid = pa_hashmap_new(pa_idxset_string_hash_func,
316                                     pa_idxset_string_compare_func);
317     PA_LLIST_HEAD_INIT(resource_attribute, rif->attrs);
318     PA_LLIST_HEAD_INIT(resource_request, rif->reqs);
319 #endif
320
321     return murphyif;
322 }
323
324
325 void pa_murphyif_done(struct userdata *u)
326 {
327     pa_murphyif *murphyif;
328     domctl_interface *dif;
329     resource_interface *rif;
330 #ifdef WITH_RESOURCES
331     resource_attribute *attr, *a;
332     resource_request *req, *r;
333 #endif
334
335     if (u && (murphyif = u->murphyif)) {
336 #ifdef WITH_DOMCTL
337         mrp_domctl_table_t *t;
338         mrp_domctl_watch_t *w;
339         int i;
340
341         dif = &murphyif->domctl;
342
343         mrp_domctl_destroy(dif->ctl);
344
345         if (dif->ntable > 0 && dif->tables) {
346             for (i = 0;  i < dif->ntable;  i++) {
347                 t = dif->tables + i;
348                 pa_xfree((void *)t->table);
349                 pa_xfree((void *)t->mql_columns);
350                 pa_xfree((void *)t->mql_index);
351             }
352             pa_xfree(dif->tables);
353         }
354
355         if (dif->nwatch > 0 && dif->watches) {
356             for (i = 0;  i < dif->nwatch;  i++) {
357                 w = dif->watches + i;
358                 pa_xfree((void *)w->table);
359                 pa_xfree((void *)w->mql_columns);
360                 pa_xfree((void *)w->mql_where);
361             }
362             pa_xfree(dif->watches);
363         }
364
365         pa_xfree((void *)dif->addr);
366 #endif
367
368 #ifdef WITH_RESOURCES
369         rif = &murphyif->resource;
370
371         resource_transport_destroy(murphyif);
372
373         pa_xfree((void *)rif->atype);
374         pa_hashmap_free(rif->nodes.rsetid, rset_hashmap_free, NULL);
375         pa_hashmap_free(rif->nodes.pid, pid_hashmap_free, NULL);
376
377         PA_LLIST_FOREACH_SAFE(attr, a, rif->attrs)
378             resource_attribute_destroy(rif, attr);
379
380         PA_LLIST_FOREACH_SAFE(req, r, rif->reqs)
381             pa_xfree(req);
382
383         pa_xfree((void *)rif->addr);
384         pa_xfree((void *)rif->inpres.name);
385         pa_xfree((void *)rif->outres.name);
386 #endif
387         mrp_mainloop_destroy(murphyif->ml);
388
389         pa_xfree(murphyif);
390     }
391 }
392
393
394 void pa_murphyif_add_table(struct userdata *u,
395                            const char *table,
396                            const char *columns,
397                            const char *index)
398 {
399     pa_murphyif *murphyif;
400     domctl_interface *dif;
401     mrp_domctl_table_t *t;
402     size_t size;
403     size_t idx;
404     
405     pa_assert(u);
406     pa_assert(table);
407     pa_assert(columns);
408     pa_assert_se((murphyif = u->murphyif));
409
410     dif = &murphyif->domctl;
411
412     idx = dif->ntable++;
413     size = sizeof(mrp_domctl_table_t) * dif->ntable;
414     t = (dif->tables = pa_xrealloc(dif->tables, size)) + idx;
415
416     t->table = pa_xstrdup(table);
417     t->mql_columns = pa_xstrdup(columns);
418     t->mql_index = index ? pa_xstrdup(index) : NULL;
419 }
420
421 int pa_murphyif_add_watch(struct userdata *u,
422                           const char *table,
423                           const char *columns,
424                           const char *where,
425                           int max_rows)
426 {
427     pa_murphyif *murphyif;
428     domctl_interface *dif;
429     mrp_domctl_watch_t *w;
430     size_t size;
431     size_t idx;
432     
433     pa_assert(u);
434     pa_assert(table);
435     pa_assert(columns);
436     pa_assert(max_rows > 0 && max_rows < MQI_QUERY_RESULT_MAX);
437     pa_assert_se((murphyif = u->murphyif));
438
439     dif = &murphyif->domctl;
440
441     idx = dif->nwatch++;
442     size = sizeof(mrp_domctl_watch_t) * dif->nwatch;
443     w = (dif->watches = pa_xrealloc(dif->watches, size)) + idx;
444
445     w->table = pa_xstrdup(table);
446     w->mql_columns = pa_xstrdup(columns);
447     w->mql_where = where ? pa_xstrdup(where) : NULL;
448     w->max_rows = max_rows;
449
450     return idx;
451 }
452
453 void pa_murphyif_setup_domainctl(struct userdata *u, pa_murphyif_watch_cb wcb)
454 {
455     static const char *name = "pulse";
456
457     pa_murphyif *murphyif;
458     domctl_interface *dif;
459
460     pa_assert(u);
461     pa_assert(wcb);
462     pa_assert_se((murphyif = u->murphyif));
463
464     dif = &murphyif->domctl;
465
466 #ifdef WITH_DOMCTL
467     if (dif->ntable || dif->nwatch) {
468         dif->ctl = mrp_domctl_create(name, murphyif->ml,
469                                      dif->tables, dif->ntable,
470                                      dif->watches, dif->nwatch,
471                                      domctl_connect_notify,
472                                      domctl_watch_notify, u);
473         if (!dif->ctl) {
474             pa_log("failed to create '%s' domain controller", name);
475             return;
476         }
477
478         if (!mrp_domctl_connect(dif->ctl, dif->addr, 0)) {
479             pa_log("failed to conect to murphyd");
480             return;
481         }
482
483         dif->watchcb = wcb;
484         pa_log_info("'%s' domain controller sucessfully created", name);
485     }
486 #endif
487 }
488
489 void  pa_murphyif_add_audio_resource(struct userdata *u,
490                                      mir_direction dir,
491                                      const char *name)
492 {
493 #ifdef WITH_DOMCTL
494     static const char *columns = RESCOL_NAMES;
495     static int maxrow = MQI_QUERY_RESULT_MAX - 1;
496 #endif
497     pa_murphyif *murphyif;
498     resource_interface *rif;
499     audio_resource_t *res;
500     char table[1024];
501
502     pa_assert(u);
503     pa_assert(dir == mir_input || dir == mir_output);
504     pa_assert(name);
505
506     pa_assert_se((murphyif = u->murphyif));
507     rif = &murphyif->resource;
508     res = NULL;
509
510     if (dir == mir_input) {
511         if (rif->inpres.name)
512             pa_log("attempt to register playback resource multiple time");
513         else
514             res = &rif->inpres;
515     }
516     else {
517         if (rif->outres.name)
518             pa_log("attempt to register recording resource multiple time");
519         else
520             res = &rif->outres;
521     }
522
523     if (res) {
524         res->name = pa_xstrdup(name);
525 #ifdef WITH_DOMCTL
526         snprintf(table, sizeof(table), "%s_users", name);
527         res->tblidx = pa_murphyif_add_watch(u, table, columns, NULL, maxrow);
528 #endif
529     }
530 }
531
532 void pa_murphyif_add_audio_attribute(struct userdata *u,
533                                      const char *propnam,
534                                      const char *attrnam,
535                                      mqi_data_type_t type,
536                                      ... ) /* default value */
537 {
538 #ifdef WITH_RESOURCES
539     pa_murphyif *murphyif;
540     resource_interface *rif;
541     resource_attribute *attr;
542     mrp_attr_value_t *val;
543     va_list ap;
544
545     pa_assert(u);
546     pa_assert(propnam);
547     pa_assert(attrnam);
548     pa_assert(type == mqi_string  || type == mqi_integer ||
549               type == mqi_unsignd || type == mqi_floating);
550
551     pa_assert_se((murphyif = u->murphyif));
552     rif = &murphyif->resource;
553
554     attr = pa_xnew0(resource_attribute, 1);
555     val  = &attr->def.value;
556
557     attr->prop = pa_xstrdup(propnam);
558     attr->def.name = pa_xstrdup(attrnam);
559     attr->def.type = type;
560
561     va_start(ap, type);
562
563     switch (type){
564     case mqi_string:   val->string    = pa_xstrdup(va_arg(ap, char *));  break;
565     case mqi_integer:  val->integer   = va_arg(ap, int32_t);             break;
566     case mqi_unsignd:  val->unsignd   = va_arg(ap, uint32_t);            break;
567     case mqi_floating: val->floating  = va_arg(ap, double);              break;
568     default:           attr->def.type = mqi_error;                       break;
569     }
570
571     va_end(ap);
572
573      if (attr->def.type == mqi_error)
574          resource_attribute_destroy(rif, attr);
575      else
576          PA_LLIST_PREPEND(resource_attribute, rif->attrs, attr);
577 #endif
578 }
579
580 void pa_murphyif_create_resource_set(struct userdata *u,
581                                      mir_node *node,
582                                      pa_nodeset_resdef *resdef)
583 {
584     pa_core *core;
585     pa_murphyif *murphyif;
586     resource_interface *rif;
587     int state;
588
589     pa_assert(u);
590     pa_assert(node);
591     pa_assert((!node->loop && node->implement == mir_stream) ||
592               ( node->loop && node->implement == mir_device)   );
593     pa_assert(node->direction == mir_input || node->direction == mir_output);
594     pa_assert(node->zone);
595     pa_assert(!node->rsetid);
596
597     pa_assert_se((core = u->core));
598
599     pa_assert_se((murphyif = u->murphyif));
600     rif = &murphyif->resource;
601
602     state = resource_transport_connect(rif);
603
604     switch (state) {
605
606     case CONNECTING:
607         resource_set_create_all(u);
608         break;
609
610     case CONNECTED:
611         node->localrset = resource_set_create_node(u, node, resdef, TRUE);
612         break;
613
614     case DISCONNECTED:
615         break;
616     }
617 }
618
619 void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
620 {
621     pa_murphyif *murphyif;
622     uint32_t rsetid;
623     char *e;
624
625     pa_assert(u);
626     pa_assert(node);
627     pa_assert_se((murphyif = u->murphyif));
628
629     if (node->localrset && node->rsetid) {
630
631         pa_murphyif_delete_node(u, node);
632
633         rsetid = strtoul(node->rsetid, &e, 10);
634
635         if (e == node->rsetid || *e) {
636             pa_log("can't destroy resource set: invalid rsetid '%s'",
637                    node->rsetid);
638         }
639         else {
640             if (resource_set_destroy_node(u, rsetid))
641                 pa_log_debug("resource set %u destruction request", rsetid);
642             else {
643                 pa_log("falied to destroy resourse set %u for node '%s'",
644                        rsetid, node->amname);
645             }
646
647             pa_xfree(node->rsetid);
648
649             node->localrset = FALSE;
650             node->rsetid = NULL;
651         }
652     }
653 }
654
655 int pa_murphyif_add_node(struct userdata *u, mir_node *node)
656 {
657 #ifdef WITH_RESOURCES
658     pa_murphyif *murphyif;
659     resource_interface *rif;
660     const char *pid;
661     rset_data *rset;
662     rset_hash *rh;
663     char buf[64];
664
665     pa_assert(u);
666     pa_assert(node);
667
668     pa_assert_se((murphyif = u->murphyif));
669
670     rif = &murphyif->resource;
671
672     if (!node->rsetid) {
673         pa_log("can't register resource set for node '%s'.: missing rsetid",
674                node->amname);
675     }
676     else if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
677         if (!(pid = get_node_pid(u,node)))
678             pa_log("can't obtain PID for node '%s'", node->amname);
679         else {
680             if (pid_hashmap_put(u, pid, node, NULL) == 0)
681                 return 0;
682
683             if ((rset = pid_hashmap_remove_rset(u, pid))) {
684                 pa_log_debug("found resource-set %s for node '%s'",
685                              rset->id, node->amname);
686
687                 if (node_put_rset(u, node, rset) == 0) {
688                     node_enforce_resource_policy(u, node, rset);
689                     rset_data_free(rset);
690                     return 0;
691                 }
692
693                 pa_log("can't register resource set for node '%s': "
694                        "failed to set rsetid", node->amname);
695
696                 rset_data_free(rset);
697             }
698             else {
699                 pa_log("can't register resource set for node '%s': "
700                        "conflicting pid", node->amname);
701             }
702         }
703     }
704     else {
705         if ((rh = rset_hashmap_put(u, node->rsetid, node))) {
706             rset = rh->rset;
707
708             pa_log_debug("enforce policies on node '%s' rsetid:%s autorel:%s "
709                          "state:%s grant:%s policy:%s", node->amname,
710                          rset->id, rset->autorel ? "yes":"no",
711                          rset->state == RSET_ACQUIRE ? "acquire":"release",
712                          rset->grant ? "yes":"no", rset->policy);
713
714             node_enforce_resource_policy(u, node, rset);
715             return 0;
716         }
717     }
718
719     return -1;
720 #else
721     return 0;
722 #endif
723 }
724
725 void pa_murphyif_delete_node(struct userdata *u, mir_node *node)
726 {
727 #ifdef WITH_RESOURCES
728     pa_murphyif *murphyif;
729     resource_interface *rif;
730     const char *pid;
731     mir_node *deleted;
732
733     pa_assert(u);
734     pa_assert(node);
735
736     pa_assert_se((murphyif = u->murphyif));
737
738     rif = &murphyif->resource;
739
740     if (node->rsetid) {
741         if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
742             if ((pid = get_node_pid(u, node))) {
743                 if (node == pid_hashmap_get_node(u, pid))
744                     pid_hashmap_remove_node(u, pid);
745                 else {
746                     pa_log("pid %s seems to have multiple resource sets. "
747                            "Refuse to delete node %u (%s) from hashmap",
748                            pid, node->index, node->amname);
749                 }
750             }
751         }
752         else {
753             if (rset_hashmap_remove(u, node->rsetid, node) < 0) {
754                 pa_log("failed to remove node '%s' from rset hash", node->amname);
755             }
756         }
757     }
758 #endif
759 }
760
761
762 #ifdef WITH_DOMCTL
763 static void domctl_connect_notify(mrp_domctl_t *dc, int connected, int errcode,
764                                   const char *errmsg, void *user_data)
765 {
766     MRP_UNUSED(dc);
767     MRP_UNUSED(user_data);
768
769     if (connected)
770         pa_log_info("Successfully registered to Murphy.");
771     else {
772         pa_log_error("Domain control Connection to Murphy failed (%d: %s).",
773                      errcode, errmsg);
774     }
775 }
776
777 static void domctl_watch_notify(mrp_domctl_t *dc, mrp_domctl_data_t *tables,
778                                 int ntable, void *user_data)
779 {
780     struct userdata *u = (struct userdata *)user_data;
781     pa_murphyif *murphyif;
782     domctl_interface *dif;
783     resource_interface *rif;
784     mrp_domctl_data_t *t;
785     mrp_domctl_watch_t *w;
786     int i;
787
788     MRP_UNUSED(dc);
789
790     pa_assert(tables);
791     pa_assert(ntable > 0);
792     pa_assert(u);
793     pa_assert_se((murphyif = u->murphyif));
794
795     dif = &murphyif->domctl;
796     rif = &murphyif->resource;
797
798     pa_log_info("Received change notification for %d tables.", ntable);
799
800     for (i = 0; i < ntable; i++) {
801         t = tables + i;
802
803         domctl_dump_data(t);
804
805         pa_assert(t->id >= 0);
806         pa_assert(t->id < dif->nwatch);
807
808         w = dif->watches + t->id;
809
810 #ifdef WITH_RESOURCES
811         if (t->id == rif->inpres.tblidx || t->id == rif->outres.tblidx) {
812             resource_set_notification(u, w->table, t->nrow, t->rows);
813             continue;
814         }
815 #endif
816
817         dif->watchcb(u, w->table, t->nrow, t->rows);
818     }
819 }
820
821 static void domctl_dump_data(mrp_domctl_data_t *table)
822 {
823     mrp_domctl_value_t *row;
824     int                 i, j;
825     char                buf[1024], *p;
826     const char         *t;
827     int                 n, l;
828
829     pa_log_debug("Table #%d: %d rows x %d columns", table->id,
830            table->nrow, table->ncolumn);
831
832     for (i = 0; i < table->nrow; i++) {
833         row = table->rows[i];
834         p   = buf;
835         n   = sizeof(buf);
836
837         for (j = 0, t = ""; j < table->ncolumn; j++, t = ", ") {
838             switch (row[j].type) {
839             case MRP_DOMCTL_STRING:
840                 l  = snprintf(p, n, "%s'%s'", t, row[j].str);
841                 p += l;
842                 n -= l;
843                 break;
844             case MRP_DOMCTL_INTEGER:
845                 l  = snprintf(p, n, "%s%d", t, row[j].s32);
846                 p += l;
847                 n -= l;
848                 break;
849             case MRP_DOMCTL_UNSIGNED:
850                 l  = snprintf(p, n, "%s%u", t, row[j].u32);
851                 p += l;
852                 n -= l;
853                 break;
854             case MRP_DOMCTL_DOUBLE:
855                 l  = snprintf(p, n, "%s%f", t, row[j].dbl);
856                 p += l;
857                 n -= l;
858                 break;
859             default:
860                 l  = snprintf(p, n, "%s<invalid column 0x%x>",
861                               t, row[j].type);
862                 p += l;
863                 n -= l;
864             }
865         }
866
867         pa_log_debug("row #%d: { %s }", i, buf);
868     }
869 }
870 #endif
871
872 #ifdef WITH_RESOURCES
873 static void resource_attribute_destroy(resource_interface *rif,
874                                        resource_attribute *attr)
875 {
876     if (attr) {
877        if (rif)
878            PA_LLIST_REMOVE(resource_attribute, rif->attrs, attr);
879
880        pa_xfree((void *)attr->prop);
881        pa_xfree((void *)attr->def.name);
882
883        if (attr->def.type == mqi_string)
884            pa_xfree((void *)attr->def.value.string);
885
886        pa_xfree(attr);
887     }
888 }
889
890 static int resource_transport_connect(resource_interface *rif)
891 {
892     int status;
893
894     pa_assert(rif);
895
896     if (rif->connected)
897         status = CONNECTED;
898     else {
899         if (!mrp_transport_connect(rif->transp, &rif->saddr, rif->alen))
900             status = DISCONNECTED;
901         else {
902             pa_log_info("resource transport connected to '%s'", rif->addr);
903             rif->connected = TRUE;
904             status = CONNECTING;
905         }
906     }
907
908     return status;
909 }
910
911 static void resource_xport_closed_evt(mrp_transport_t *transp, int error,
912                                       void *void_u)
913 {
914     struct userdata *u = (struct userdata *)void_u;
915     pa_murphyif *murphyif;
916     resource_interface *rif;
917
918     MRP_UNUSED(transp);
919
920     pa_assert(u);
921     pa_assert_se((murphyif = u->murphyif));
922
923     rif = &murphyif->resource;
924
925     if (!error)
926         pa_log("Resource transport connection closed by peer");
927     else {
928         pa_log("Resource transport connection closed with error %d (%s)",
929                error, strerror(error));
930     }
931
932     resource_transport_destroy(murphyif);
933     resource_set_destroy_all(u);
934     schedule_connect(u, rif);
935 }
936
937 static mrp_msg_t *resource_create_request(uint32_t seqno,
938                                           mrp_resproto_request_t req)
939 {
940     uint16_t   type  = req;
941     mrp_msg_t *msg;
942
943     msg = mrp_msg_create(RESPROTO_SEQUENCE_NO , MRP_MSG_FIELD_UINT32, seqno,
944                          RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16, type ,
945                          RESPROTO_MESSAGE_END                               );
946
947     if (!msg)
948         pa_log("can't to create new resource message");
949  
950     return msg;
951 }
952
953 static pa_bool_t resource_send_message(resource_interface *rif,
954                                        mrp_msg_t          *msg,
955                                        uint32_t            nodidx,
956                                        uint16_t            reqid,
957                                        uint32_t            seqno)
958 {
959     resource_request *req;
960     pa_bool_t success = TRUE;
961
962     if (!mrp_transport_send(rif->transp, msg)) {
963         pa_log("failed to send resource message");
964         success = FALSE;
965     }
966     else {
967         req = pa_xnew0(resource_request, 1);
968         req->nodidx = nodidx;
969         req->reqid  = reqid;
970         req->seqno  = seqno;
971
972         PA_LLIST_PREPEND(resource_request, rif->reqs, req);
973     }
974
975     mrp_msg_unref(msg);
976
977     return success;
978 }
979
980 static pa_bool_t resource_set_create_node(struct userdata *u,
981                                           mir_node *node,
982                                           pa_nodeset_resdef *resdef,
983                                           pa_bool_t acquire)
984 {
985     pa_core *core;
986     pa_murphyif *murphyif;
987     resource_interface *rif;
988     resource_request *req;
989     mrp_msg_t *msg;
990     uint16_t reqid;
991     uint32_t seqno;
992     uint32_t rset_flags;
993     const char *role;
994     pa_nodeset_map *map;
995     const char *class;
996     pa_loopnode *loop;
997     pa_sink_input *sinp;
998     pa_source_output *sout;
999     audio_resource_t *res;
1000     const char *resnam;
1001     mir_node_type type = 0;
1002     uint32_t audio_flags = 0;
1003     uint32_t priority;
1004     pa_proplist *proplist = NULL;
1005     pa_bool_t success = TRUE;
1006
1007     pa_assert(u);
1008     pa_assert(node);
1009     pa_assert(node->index != PA_IDXSET_INVALID);
1010     pa_assert((!node->loop && node->implement == mir_stream) ||
1011               ( node->loop && node->implement == mir_device)   );
1012     pa_assert(node->direction == mir_input || node->direction == mir_output);
1013     pa_assert(node->zone);
1014     pa_assert(!node->rsetid);
1015
1016     pa_assert_se((core = u->core));
1017
1018     if ((loop = node->loop)) {
1019         if (node->direction == mir_input) {
1020             sout = pa_idxset_get_by_index(core->source_outputs,
1021                                           loop->source_output_index);
1022             if (sout)
1023                 proplist = sout->proplist;
1024         }
1025         else {
1026             sinp = pa_idxset_get_by_index(core->sink_inputs,
1027                                           loop->sink_input_index);
1028             if (sinp)
1029                 proplist = sinp->proplist;
1030         }
1031         if (proplist && (role = pa_proplist_gets(proplist, PA_PROP_MEDIA_ROLE))) {
1032             if ((map = pa_nodeset_get_map_by_role(u, role)))
1033                 type = map->type;
1034         }
1035     }
1036     else {
1037         if (node->direction == mir_output) {
1038             if ((sout = pa_idxset_get_by_index(core->source_outputs, node->paidx)))
1039                 proplist = sout->proplist;
1040         }
1041         else {
1042             if ((sinp = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
1043                 proplist = sinp->proplist;
1044         }
1045         type = node->type;
1046     }
1047
1048     pa_assert_se((class = pa_nodeset_get_class(u, type)));
1049     pa_assert_se((murphyif = u->murphyif));
1050     rif = &murphyif->resource;
1051
1052     reqid = RESPROTO_CREATE_RESOURCE_SET;
1053     seqno = rif->seqno.request++;
1054     res   = (node->direction == mir_input) ? &rif->inpres : &rif->outres;
1055
1056     pa_assert_se((resnam = res->name));
1057
1058     rset_flags = RESPROTO_RSETFLAG_NOEVENTS;
1059     rset_flags |= (acquire ? RESPROTO_RSETFLAG_AUTOACQUIRE : 0);
1060     rset_flags |= (resdef ? resdef->flags.rset : 0);
1061
1062     audio_flags = (resdef ? resdef->flags.audio : 0);
1063
1064     priority = (resdef ? resdef->priority : 0);
1065
1066     msg = resource_create_request(seqno, reqid);
1067
1068     if (PUSH_VALUE(msg,   RESOURCE_FLAGS   , UINT32, rset_flags)  &&
1069         PUSH_VALUE(msg,   RESOURCE_PRIORITY, UINT32, priority)    &&
1070         PUSH_VALUE(msg,   CLASS_NAME       , STRING, class)       &&
1071         PUSH_VALUE(msg,   ZONE_NAME        , STRING, node->zone)  &&
1072         PUSH_VALUE(msg,   RESOURCE_NAME    , STRING, resnam)      &&
1073         PUSH_VALUE(msg,   RESOURCE_FLAGS   , UINT32, audio_flags) &&
1074         PUSH_VALUE(msg,   ATTRIBUTE_NAME   , STRING, "policy")    &&
1075         PUSH_VALUE(msg,   ATTRIBUTE_VALUE  , STRING, "strict")    &&
1076         PUSH_ATTRS(msg,   rif, proplist)                          &&
1077         PUSH_VALUE(msg,   SECTION_END      , UINT8 , 0)            )
1078     {
1079         success = resource_send_message(rif, msg, node->index, reqid, seqno);
1080     }
1081     else {
1082         success = FALSE;
1083         mrp_msg_unref(msg);
1084     }
1085
1086     if (success)
1087         pa_log_debug("requested resource set for '%s'", node->amname);
1088     else
1089         pa_log_debug("failed to create resource set for '%s'", node->amname);
1090
1091     return success;
1092 }
1093
1094 static pa_bool_t resource_set_create_all(struct userdata *u)
1095 {
1096     uint32_t idx;
1097     mir_node *node;
1098     pa_bool_t success;
1099
1100     pa_assert(u);
1101
1102     success = TRUE;
1103
1104     idx = PA_IDXSET_INVALID;
1105
1106     while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
1107         if ((node->implement == mir_stream && !node->loop) ||
1108             (node->implement == mir_device &&  node->loop)   )
1109         {
1110             if (!node->rsetid) {
1111                 node->localrset = resource_set_create_node(u, node, NULL, FALSE);
1112                 success &= node->localrset;
1113             }
1114         }
1115     }
1116
1117     return success;
1118 }
1119
1120 static pa_bool_t resource_set_destroy_node(struct userdata *u, uint32_t rsetid)
1121 {
1122     pa_murphyif *murphyif;
1123     resource_interface *rif;
1124     mrp_msg_t *msg;
1125     uint16_t reqid;
1126     uint32_t seqno;
1127     uint32_t nodidx;
1128     pa_bool_t success;
1129
1130     pa_assert(u);
1131
1132     pa_assert_se((murphyif = u->murphyif));
1133     rif = &murphyif->resource;
1134
1135     reqid = RESPROTO_DESTROY_RESOURCE_SET;
1136     seqno = rif->seqno.request++;
1137     nodidx = PA_IDXSET_INVALID;
1138     msg = resource_create_request(seqno, reqid);
1139
1140     if (PUSH_VALUE(msg, RESOURCE_SET_ID, UINT32, rsetid))
1141         success = resource_send_message(rif, msg, nodidx, reqid, seqno);
1142     else {
1143         success = FALSE;
1144         mrp_msg_unref(msg);
1145     }
1146
1147     return success;
1148 }
1149
1150 static pa_bool_t resource_set_destroy_all(struct userdata *u)
1151 {
1152     pa_murphyif *murphyif;
1153     resource_interface *rif;
1154     uint32_t idx;
1155     mir_node *node;
1156     uint32_t rsetid;
1157     char *e;
1158     pa_bool_t success;
1159
1160     pa_assert(u);
1161     pa_assert_se((murphyif = u->murphyif));
1162
1163     rif = &murphyif->resource;
1164
1165     success = TRUE;
1166
1167     idx = PA_IDXSET_INVALID;
1168
1169     while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
1170         if (node->implement == mir_stream && node->localrset) {
1171             pa_log_debug("destroying resource set for '%s'", node->amname);
1172
1173             if (rif->connected && node->rsetid) {
1174                 rsetid = strtoul(node->rsetid, &e, 10);
1175
1176                 if (e == node->rsetid || *e)
1177                     success = FALSE;
1178                 else
1179                     success &= resource_set_destroy_node(u, rsetid);
1180             }
1181
1182             pa_xfree(node->rsetid);
1183
1184             node->localrset = FALSE;
1185             node->rsetid = NULL;
1186         }
1187     }
1188
1189     return success;
1190 }
1191
1192 static void resource_set_notification(struct userdata *u,
1193                                       const char *table,
1194                                       int nrow,
1195                                       mrp_domctl_value_t **values)
1196 {
1197     pa_murphyif *murphyif;
1198     resource_interface *rif;
1199     int r;
1200     mrp_domctl_value_t *row;
1201     mrp_domctl_value_t *crsetid;
1202     mrp_domctl_value_t *cautorel;
1203     mrp_domctl_value_t *cstate;
1204     mrp_domctl_value_t *cgrant;
1205     mrp_domctl_value_t *cpid;
1206     mrp_domctl_value_t *cpolicy;
1207     char rsetid[32];
1208     const char *pid;
1209     mir_node *node, **nodes;
1210     rset_hash *rh;
1211     rset_data rset, *rs;
1212     size_t i, size;
1213
1214     pa_assert(u);
1215     pa_assert(table);
1216
1217     pa_assert_se((murphyif = u->murphyif));
1218     rif = &murphyif->resource;
1219
1220     for (r = 0;  r < nrow;  r++) {
1221         row = values[r];
1222         crsetid  =  row + RESCOL_RSETID;
1223         cautorel =  row + RESCOL_AUTOREL;
1224         cstate   =  row + RESCOL_STATE;
1225         cgrant   =  row + RESCOL_GRANT;
1226         cpid     =  row + RESCOL_PID;
1227         cpolicy  =  row + RESCOL_POLICY;
1228
1229         if (crsetid->type  != MRP_DOMCTL_UNSIGNED ||
1230             cautorel->type != MRP_DOMCTL_INTEGER  ||
1231             cstate->type   != MRP_DOMCTL_INTEGER  ||
1232             cgrant->type   != MRP_DOMCTL_INTEGER  ||
1233             cpid->type     != MRP_DOMCTL_STRING   ||
1234             cpolicy->type  != MRP_DOMCTL_STRING    )
1235         {
1236             pa_log("invalid field type in '%s' (%d|%d|%d|%d|%d|%d)", table,
1237                    crsetid->type, cautorel->type, cstate->type,
1238                    cgrant->type, cpid->type, cpolicy->type);
1239             continue;
1240         }
1241
1242         snprintf(rsetid, sizeof(rsetid), "%d", crsetid->s32);
1243         pid = cpid->str;
1244
1245         rset.id      = rsetid;
1246         rset.autorel = cautorel->s32;
1247         rset.state   = cstate->s32;
1248         rset.grant   = cgrant->s32;
1249         rset.policy  = cpolicy->str;
1250
1251
1252         if (rset.autorel != 0 && rset.autorel != 1) {
1253             pa_log_debug("invalid autorel %d in table '%s'",
1254                          rset.autorel, table);
1255             continue;
1256         }
1257         if (rset.state != RSET_RELEASE && rset.state != RSET_ACQUIRE) {
1258             pa_log_debug("invalid state %d in table '%s'", rset.state, table);
1259             continue;
1260         }
1261         if (rset.grant != 0 && rset.grant != 1) {
1262             pa_log_debug("invalid grant %d in table '%s'", rset.grant, table);
1263             continue;
1264         }
1265
1266         if (!(rh = rset_hashmap_get(u, rset.id))) {
1267             if (!pid) {
1268                 pa_log_debug("can't find node for resource set %s "
1269                              "(pid in resource set unknown)", rset.id);
1270                 continue;
1271             }
1272
1273             if ((node = pid_hashmap_remove_node(u, pid))) {
1274                 pa_log_debug("found node %s for resource-set '%s'",
1275                              node->amname, rset.id);
1276
1277                 if (node_put_rset(u, node, &rset) < 0) {
1278                     pa_log("can't register resource set for node '%s': "
1279                            "failed to set rsetid", node->amname);
1280                     continue;
1281                 }
1282             }
1283             else {
1284                 if (pid_hashmap_put(u, pid, NULL, rset_data_dup(&rset)) < 0) {
1285                     if (!(rs = pid_hashmap_get_rset(u, pid)))
1286                         pa_log("failed to add resource set to pid hash");
1287                     else {
1288                         if (!pa_streq(rs->id, rset.id)) {
1289                             pa_log("process %s appears to have multiple resour"
1290                                    "ce sets (%s and %s)", pid, rs->id,rset.id);
1291                         }
1292                         pa_log_debug("update resource-set %s data in "
1293                                      "pid hash (pid %s)", rs->id, pid);
1294                         rset_data_copy(rs, &rset);
1295                     }
1296                 }
1297                 else {
1298                     pa_log_debug("can't find node for resource set %s. "
1299                                  "Beleive the stream will appear later on",
1300                                  rset.id);
1301                 }
1302
1303                 continue;
1304             }
1305         }
1306
1307         rset_data_update(rh->rset, &rset);
1308
1309         /* we need to make a copy of this as node_enforce_resource_policy()
1310            will delete/modify it */
1311         size = sizeof(mir_node *) * (rh->nnode + 1);
1312         nodes = alloca(size);
1313         memcpy(nodes, rh->nodes, size);
1314
1315         for (i = 0;  (node = nodes[i]);  i++) {
1316             pa_log_debug("resource notification for node '%s' autorel:%s "
1317                          "state:%s grant:%s pid:%s policy:%s", node->amname,
1318                          rset.autorel ? "yes":"no",
1319                          rset.state == RSET_ACQUIRE ? "acquire":"release",
1320                          rset.grant ? "yes":"no", pid, rset.policy);
1321
1322             node_enforce_resource_policy(u, node, &rset);
1323         }
1324     }
1325 }
1326
1327
1328 static pa_bool_t resource_push_attributes(mrp_msg_t *msg,
1329                                           resource_interface *rif,
1330                                           pa_proplist *proplist)
1331 {
1332     resource_attribute *attr;
1333     union {
1334         const void *ptr;
1335         const char *str;
1336         int32_t    *i32;
1337         uint32_t   *u32;
1338         double     *dbl;
1339     } v;
1340     size_t size;
1341     int sts;
1342
1343     pa_assert(msg);
1344     pa_assert(rif);
1345
1346     PA_LLIST_FOREACH(attr, rif->attrs) {
1347         if (!PUSH_VALUE(msg, ATTRIBUTE_NAME, STRING, attr->def.name))
1348             return FALSE;
1349
1350         if (proplist)
1351             sts = pa_proplist_get(proplist, attr->prop, &v.ptr, &size);
1352         else
1353             sts = -1;
1354
1355         switch (attr->def.type) {
1356         case mqi_string:
1357             if (sts < 0)
1358                 v.str = attr->def.value.string;
1359             else if (v.str[size-1] != '\0' || strlen(v.str) != (size-1) ||
1360                      !pa_utf8_valid(v.str))
1361                 return FALSE;
1362             if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, STRING, v.str))
1363                 return FALSE;
1364             break;
1365
1366         case mqi_integer:
1367             if (sts < 0)
1368                 v.i32 = &attr->def.value.integer;
1369             else if (size != sizeof(*v.i32))
1370                 return FALSE;
1371             if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.i32))
1372                 return FALSE;
1373             break;
1374             
1375         case mqi_unsignd:
1376             if (sts < 0)
1377                 v.u32 = &attr->def.value.unsignd;
1378             else if (size != sizeof(*v.u32))
1379                 return FALSE;
1380             if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.u32))
1381                 return FALSE;
1382             break;
1383             
1384         case mqi_floating:
1385             if (sts < 0)
1386                 v.dbl = &attr->def.value.floating;
1387             else if (size != sizeof(*v.dbl))
1388                 return FALSE;
1389             if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.dbl))
1390                 return FALSE;
1391             break;
1392
1393         default: /* we should never get here */
1394             return FALSE;
1395         }
1396     }
1397
1398     return TRUE;
1399 }
1400
1401
1402
1403 static void resource_recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *void_u)
1404 {
1405     return resource_recvfrom_msg(t, msg, NULL, 0, void_u);
1406 }
1407
1408 static void resource_recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
1409                                   mrp_sockaddr_t *addr, socklen_t addrlen,
1410                                   void *void_u)
1411 {
1412     struct userdata *u = (struct userdata *)void_u;
1413     pa_core *core;
1414     pa_murphyif *murphyif;
1415     resource_interface *rif;
1416     void     *curs = NULL;
1417     uint32_t  seqno;
1418     uint16_t  reqid;
1419     uint32_t  nodidx;
1420     resource_request *req, *n;
1421     mir_node *node;
1422
1423     MRP_UNUSED(transp);
1424     MRP_UNUSED(addr);
1425     MRP_UNUSED(addrlen);
1426
1427     pa_assert(u);
1428     pa_assert_se((core = u->core));
1429     pa_assert_se((murphyif = u->murphyif));
1430
1431     rif = &murphyif->resource;
1432
1433     if (!resource_fetch_seqno   (msg, &curs, &seqno) ||
1434         !resource_fetch_request (msg, &curs, &reqid)   )
1435     {
1436         pa_log("ignoring malformed message");
1437         return;
1438     }
1439
1440     PA_LLIST_FOREACH_SAFE(req, n, rif->reqs) {
1441         if (req->seqno <= seqno) {
1442             nodidx = req->nodidx;
1443             
1444             if (req->reqid == reqid) {
1445                 PA_LLIST_REMOVE(resource_request, rif->reqs, req);
1446                 pa_xfree(req);
1447             }
1448             
1449             if (!(node = mir_node_find_by_index(u, nodidx))) {
1450                 if (reqid != RESPROTO_DESTROY_RESOURCE_SET) {
1451                     pa_log("got response (reqid:%u seqno:%u) but can't "
1452                            "find the corresponding node", reqid, seqno);
1453                     resource_set_create_response_abort(u, msg, &curs);
1454                 }
1455             }
1456             else {
1457                 if (req->seqno < seqno) {
1458                     pa_log("unanswered request %d", req->seqno);
1459                 }
1460                 else {
1461                     pa_log_debug("got response (reqid:%u seqno:%u "
1462                                  "node:'%s')", reqid, seqno,
1463                                  node ? node->amname : "<unknown>");
1464                     
1465                     switch (reqid) {
1466                     case RESPROTO_CREATE_RESOURCE_SET:
1467                         resource_set_create_response(u, node, msg, &curs);
1468                         break;
1469                     case RESPROTO_DESTROY_RESOURCE_SET:
1470                         break;
1471                     default:
1472                         pa_log("ignoring unsupported resource request "
1473                                "type %u", reqid);
1474                         break;
1475                     }
1476                 }
1477             }
1478         } /* PA_LLIST_FOREACH_SAFE */
1479     }
1480 }
1481
1482
1483 static void resource_set_create_response(struct userdata *u, mir_node *node,
1484                                          mrp_msg_t *msg, void **pcursor)
1485 {
1486     int status;
1487     uint32_t rsetid;
1488     char buf[4096];
1489
1490     pa_assert(u);
1491     pa_assert(node);
1492     pa_assert(msg);
1493     pa_assert(pcursor);
1494
1495     if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1496         !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1497     {
1498         pa_log("ignoring malformed response to resource set creation");
1499         return;
1500     }
1501
1502     if (status) {
1503         pa_log("creation of resource set failed. error code %u", status);
1504         return;
1505     }
1506
1507     node->rsetid = pa_sprintf_malloc("%d", rsetid);
1508     
1509     if (pa_murphyif_add_node(u, node) == 0) {
1510         pa_log_debug("resource set was successfully created");
1511         mir_node_print(node, buf, sizeof(buf));
1512         pa_log_debug("modified node:\n%s", buf);
1513     }
1514     else {
1515         pa_log("failed to create resource set: "
1516                    "conflicting resource set id");
1517     }
1518 }
1519
1520 static void resource_set_create_response_abort(struct userdata *u,
1521                                                mrp_msg_t *msg, void **pcursor)
1522 {
1523     int status;
1524     uint32_t rsetid;
1525
1526     pa_assert(u);
1527     pa_assert(msg);
1528     pa_assert(pcursor);
1529
1530     if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1531         !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1532     {
1533         pa_log("ignoring malformed response to resource set creation");
1534         return;
1535     }
1536
1537     if (status) {
1538         pa_log("creation of resource set failed. error code %u", status);
1539         return;
1540     }
1541
1542     if (resource_set_destroy_node(u, rsetid))
1543         pa_log_debug("destroying resource set %u", rsetid);
1544     else
1545         pa_log("attempt to destroy resource set %u failed", rsetid);
1546 }
1547
1548
1549 static pa_bool_t resource_fetch_seqno(mrp_msg_t *msg,
1550                                       void **pcursor,
1551                                       uint32_t *pseqno)
1552 {
1553     uint16_t tag;
1554     uint16_t type;
1555     mrp_msg_value_t value;
1556     size_t size;
1557
1558     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1559         tag != RESPROTO_SEQUENCE_NO || type != MRP_MSG_FIELD_UINT32)
1560     {
1561         *pseqno = INVALID_SEQNO;
1562         return false;
1563     }
1564
1565     *pseqno = value.u32;
1566     return true;
1567 }
1568
1569
1570 static pa_bool_t resource_fetch_request(mrp_msg_t *msg,
1571                                         void **pcursor,
1572                                         uint16_t *preqtype)
1573 {
1574     uint16_t tag;
1575     uint16_t type;
1576     mrp_msg_value_t value;
1577     size_t size;
1578
1579     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1580         tag != RESPROTO_REQUEST_TYPE || type != MRP_MSG_FIELD_UINT16)
1581     {
1582         *preqtype = INVALID_REQUEST;
1583         return false;
1584     }
1585
1586     *preqtype = value.u16;
1587     return true;
1588 }
1589
1590 static pa_bool_t resource_fetch_status(mrp_msg_t *msg,
1591                                        void **pcursor,
1592                                        int *pstatus)
1593 {
1594     uint16_t tag;
1595     uint16_t type;
1596     mrp_msg_value_t value;
1597     size_t size;
1598
1599     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1600         tag != RESPROTO_REQUEST_STATUS || type != MRP_MSG_FIELD_SINT16)
1601     {
1602         *pstatus = EINVAL;
1603         return FALSE;
1604     }
1605
1606     *pstatus = value.s16;
1607     return TRUE;
1608 }
1609
1610 static pa_bool_t resource_fetch_rset_id(mrp_msg_t *msg,
1611                                         void **pcursor,
1612                                         uint32_t *pid)
1613 {
1614     uint16_t tag;
1615     uint16_t type;
1616     mrp_msg_value_t value;
1617     size_t size;
1618
1619     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1620         tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
1621     {
1622         *pid = INVALID_ID;
1623         return FALSE;
1624     }
1625
1626     *pid = value.u32;
1627     return TRUE;
1628 }
1629
1630 static pa_bool_t resource_fetch_rset_state(mrp_msg_t *msg,
1631                                            void **pcursor,
1632                                            mrp_resproto_state_t *pstate)
1633 {
1634     uint16_t tag;
1635     uint16_t type;
1636     mrp_msg_value_t value;
1637     size_t size;
1638
1639     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1640         tag != RESPROTO_RESOURCE_STATE || type != MRP_MSG_FIELD_UINT16)
1641     {
1642         *pstate = 0;
1643         return FALSE;
1644     }
1645
1646     *pstate = value.u16;
1647     return TRUE;
1648 }
1649
1650
1651 static pa_bool_t resource_fetch_rset_mask(mrp_msg_t *msg,
1652                                           void **pcursor,
1653                                           mrp_resproto_state_t *pmask)
1654 {
1655     uint16_t tag;
1656     uint16_t type;
1657     mrp_msg_value_t value;
1658     size_t size;
1659
1660     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1661         tag != RESPROTO_RESOURCE_GRANT || type != MRP_MSG_FIELD_UINT32)
1662     {
1663         *pmask = 0;
1664         return FALSE;
1665     }
1666
1667     *pmask = value.u32;
1668     return TRUE;
1669 }
1670
1671 static pa_bool_t resource_transport_create(struct userdata *u,
1672                                            pa_murphyif *murphyif)
1673 {
1674     static mrp_transport_evt_t ev = {
1675         { .recvmsg     = resource_recv_msg },
1676         { .recvmsgfrom = resource_recvfrom_msg },
1677         .closed        = resource_xport_closed_evt,
1678         .connection    = NULL
1679     };
1680
1681     resource_interface *rif;
1682
1683     pa_assert(u);
1684     pa_assert(murphyif);
1685
1686     rif = &murphyif->resource;
1687
1688     if (!rif->transp)
1689         rif->transp = mrp_transport_create(murphyif->ml, rif->atype, &ev, u,0);
1690
1691     return rif->transp ? TRUE : FALSE;
1692 }
1693
1694 static void resource_transport_destroy(pa_murphyif *murphyif)
1695 {
1696     resource_interface *rif;
1697
1698     pa_assert(murphyif);
1699     rif = &murphyif->resource;
1700
1701     if (rif->transp)
1702         mrp_transport_destroy(rif->transp);
1703
1704     rif->transp = NULL;
1705     rif->connected = FALSE;
1706 }
1707
1708 static void connect_attempt(pa_mainloop_api *a,
1709                              pa_time_event *e,
1710                              const struct timeval *t,
1711                              void *data)
1712 {
1713     struct userdata *u = (struct userdata *)data;
1714     pa_murphyif *murphyif;
1715     resource_interface *rif;
1716     
1717     int state;
1718
1719     pa_assert(u);
1720     pa_assert_se((murphyif = u->murphyif));
1721
1722     rif = &murphyif->resource;
1723
1724     if (!resource_transport_create(u, murphyif))
1725         schedule_connect(u, rif);
1726     else {
1727         state = resource_transport_connect(rif);
1728
1729         switch (state) {
1730
1731         case CONNECTING:
1732             resource_set_create_all(u);
1733             cancel_schedule(u, rif);
1734             break;
1735
1736         case CONNECTED:
1737             cancel_schedule(u, rif);
1738             break;
1739             
1740         case DISCONNECTED:
1741             schedule_connect(u, rif);
1742             break;
1743         }
1744     }
1745 }
1746
1747 static void schedule_connect(struct userdata *u, resource_interface *rif)
1748 {
1749     pa_core *core;
1750     pa_mainloop_api *mainloop;
1751     struct timeval when;
1752     pa_time_event *tev;
1753
1754     pa_assert(u);
1755     pa_assert(rif);
1756     pa_assert_se((core = u->core));
1757     pa_assert_se((mainloop = core->mainloop));
1758
1759     pa_gettimeofday(&when);
1760     pa_timeval_add(&when, rif->connect.period);
1761
1762     if ((tev = rif->connect.evt))
1763         mainloop->time_restart(tev, &when);
1764     else {
1765         rif->connect.evt = mainloop->time_new(mainloop, &when,
1766                                               connect_attempt, u);
1767     }
1768 }
1769
1770 static void cancel_schedule(struct userdata *u, resource_interface *rif)
1771 {
1772     pa_core *core;
1773     pa_mainloop_api *mainloop;
1774     pa_time_event *tev;
1775
1776     pa_assert(u);
1777     pa_assert(rif);
1778     pa_assert_se((core = u->core));
1779     pa_assert_se((mainloop = core->mainloop));
1780
1781     if ((tev = rif->connect.evt)) {
1782         mainloop->time_free(tev);
1783         rif->connect.evt = NULL;
1784     }
1785 }
1786
1787 static int node_put_rset(struct userdata *u, mir_node *node, rset_data *rset)
1788 {
1789     pa_murphyif *murphyif;
1790     resource_interface *rif;
1791     pa_proplist *pl;
1792
1793     pa_assert(u);
1794     pa_assert(node);
1795     pa_assert(rset);
1796     pa_assert(rset->id);
1797
1798     pa_assert(node->implement == mir_stream);
1799     pa_assert(node->direction == mir_input || node->direction == mir_output);
1800
1801     pa_assert_se((murphyif = u->murphyif));
1802     rif = &murphyif->resource;
1803
1804     pa_log_debug("setting rsetid %s for node %s", rset->id, node->amname);
1805
1806     pa_xfree(node->rsetid);
1807     node->rsetid = pa_xstrdup(rset->id);
1808
1809     if (!(pl = get_node_proplist(u, node))) {
1810         pa_log("can't obtain property list for node %s", node->amname);
1811         return -1;
1812     }
1813
1814     if ((pa_proplist_sets(pl, PA_PROP_RESOURCE_SET_ID, node->rsetid) < 0)) {
1815         pa_log("failed to set '" PA_PROP_RESOURCE_SET_ID "' property "
1816                "of '%s' node", node->amname);
1817         return -1;
1818     }
1819
1820     if (!rset_hashmap_put(u, node->rsetid, node)) {
1821         pa_log("conflicting rsetid %s for %s", node->rsetid, node->amname);
1822         return -1;
1823     }
1824
1825     return 0;
1826 }
1827
1828 static void node_enforce_resource_policy(struct userdata *u,
1829                                          mir_node *node,
1830                                          rset_data *rset)
1831 {
1832     int req;
1833
1834     pa_assert(node);
1835     pa_assert(rset);
1836     pa_assert(rset->policy);
1837     
1838
1839     if (pa_streq(rset->policy, "relaxed"))
1840         req = PA_STREAM_RUN;
1841     else if (pa_streq(rset->policy, "strict")) {
1842         if (rset->state == RSET_RELEASE)
1843             req = PA_STREAM_KILL;
1844         else {
1845             if (rset->grant)
1846                 req = PA_STREAM_RUN;
1847             else
1848                 req = PA_STREAM_BLOCK;
1849         }
1850     }
1851     else {
1852         req = PA_STREAM_BLOCK;
1853     }
1854
1855     pa_stream_state_change(u, node, req);
1856 }
1857
1858 static rset_data *rset_data_dup(rset_data *orig)
1859 {
1860     rset_data *dup;
1861
1862     pa_assert(orig);
1863     pa_assert(orig->id);
1864     pa_assert(orig->policy);
1865
1866     dup = pa_xnew0(rset_data, 1);
1867
1868     dup->id      = pa_xstrdup(orig->id);
1869     dup->autorel = orig->autorel;
1870     dup->state   = orig->state;
1871     dup->grant   = orig->grant;
1872     dup->policy  = pa_xstrdup(orig->policy);
1873
1874     return dup;
1875 }
1876
1877 static void rset_data_copy(rset_data *dst, rset_data *src)
1878 {
1879     rset_data *dup;
1880
1881     pa_assert(dst);
1882     pa_assert(src);
1883     pa_assert(src->id);
1884     pa_assert(src->policy);
1885
1886     pa_xfree((void *)dst->id);
1887     pa_xfree((void *)dst->policy);
1888
1889     dst->id      = pa_xstrdup(src->id);
1890     dst->autorel = src->autorel;
1891     dst->state   = src->state;
1892     dst->grant   = src->grant;
1893     dst->policy  = pa_xstrdup(src->policy);
1894 }
1895
1896
1897 static void rset_data_update(rset_data *dst, rset_data *src)
1898 {
1899     rset_data *dup;
1900
1901     pa_assert(dst);
1902     pa_assert(dst->id);
1903     pa_assert(src);
1904     pa_assert(src->id);
1905     pa_assert(src->policy);
1906
1907     pa_assert_se(pa_streq(src->id, dst->id));
1908
1909     pa_xfree((void *)dst->policy);
1910
1911     dst->autorel = src->autorel;
1912     dst->state   = src->state;
1913     dst->grant   = src->grant;
1914     dst->policy  = pa_xstrdup(src->policy);
1915 }
1916
1917
1918 static void rset_data_free(rset_data *rset)
1919 {
1920     if (rset) {
1921         pa_xfree((void *)rset->id);
1922         pa_xfree((void *)rset->policy);
1923         pa_xfree(rset);
1924     }
1925 }
1926
1927 static void pid_hashmap_free(void *p, void *userdata)
1928 {
1929     pid_hash *ph = (pid_hash *)p;
1930
1931     (void)userdata;
1932
1933     if (ph) {
1934         pa_xfree((void *)ph->pid);
1935         rset_data_free(ph->rset);
1936         pa_xfree(ph);
1937     }
1938 }
1939
1940 static int pid_hashmap_put(struct userdata *u, const char *pid,
1941                            mir_node *node, rset_data *rset)
1942 {
1943     pa_murphyif *murphyif;
1944     resource_interface *rif;
1945     pid_hash *ph;
1946
1947     pa_assert(u);
1948     pa_assert(pid);
1949     pa_assert(node || rset);
1950     pa_assert_se((murphyif = u->murphyif));
1951     
1952     rif = &murphyif->resource;
1953
1954     ph = pa_xnew0(pid_hash, 1);
1955     ph->pid = pa_xstrdup(pid);
1956     ph->node = node;
1957     ph->rset = rset;
1958
1959     if (pa_hashmap_put(rif->nodes.pid, ph->pid, ph) == 0)
1960         return 0;
1961     else
1962         pid_hashmap_free(ph, NULL);
1963
1964     return -1;
1965 }
1966
1967 static mir_node *pid_hashmap_get_node(struct userdata *u, const char *pid)
1968 {
1969     pa_murphyif *murphyif;
1970     resource_interface *rif;
1971     pid_hash *ph;
1972
1973     pa_assert(u);
1974     pa_assert(pid);
1975     pa_assert(murphyif = u->murphyif);
1976     
1977     rif = &murphyif->resource;
1978
1979     if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
1980         return ph->node;
1981
1982     return NULL;
1983 }
1984
1985 static rset_data *pid_hashmap_get_rset(struct userdata *u, const char *pid)
1986 {
1987     pa_murphyif *murphyif;
1988     resource_interface *rif;
1989     pid_hash *ph;
1990
1991     pa_assert(u);
1992     pa_assert(pid);
1993     pa_assert(murphyif = u->murphyif);
1994     
1995     rif = &murphyif->resource;
1996
1997     if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
1998         return ph->rset;
1999
2000     return NULL;
2001 }
2002
2003 static mir_node *pid_hashmap_remove_node(struct userdata *u, const char *pid)
2004 {
2005     pa_murphyif *murphyif;
2006     resource_interface *rif;
2007     mir_node *node;
2008     pid_hash *ph;
2009
2010     pa_assert(u);
2011     pa_assert_se((murphyif = u->murphyif));
2012
2013     rif = &murphyif->resource;
2014
2015     if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
2016         node = NULL;
2017     else if (!(node = ph->node))
2018         pa_hashmap_put(rif->nodes.pid, ph->pid, ph);
2019     else
2020         pid_hashmap_free(ph, NULL);
2021
2022     return node;
2023 }
2024
2025 static rset_data *pid_hashmap_remove_rset(struct userdata *u, const char *pid)
2026 {
2027     pa_murphyif *murphyif;
2028     resource_interface *rif;
2029     rset_data *rset;
2030     pid_hash *ph;
2031
2032     pa_assert(u);
2033     pa_assert(pid);
2034
2035     pa_assert_se((murphyif = u->murphyif));
2036
2037     rif = &murphyif->resource;
2038
2039     if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
2040         rset = NULL;
2041     else if (!(rset = ph->rset))
2042         pa_hashmap_put(rif->nodes.pid, ph->pid, ph);
2043     else {
2044         ph->rset = NULL;
2045         pid_hashmap_free(ph, NULL);
2046     }
2047
2048     return rset;
2049 }
2050
2051
2052 static void rset_hashmap_free(void *r, void *userdata)
2053 {
2054     rset_hash *rh = (rset_hash *)r;
2055
2056     (void)userdata;
2057
2058     if (rh) {
2059         pa_xfree(rh->nodes);
2060         rset_data_free(rh->rset);
2061         pa_xfree(rh);
2062     }
2063 }
2064
2065 static rset_hash *rset_hashmap_put(struct userdata *u,
2066                                    const char *rsetid,
2067                                    mir_node *node)
2068 {
2069     pa_murphyif *murphyif;
2070     resource_interface *rif;
2071     rset_hash *rh;
2072     rset_data *rset;
2073     size_t i;
2074     mir_node *n;
2075
2076     pa_assert(u);
2077     pa_assert(rsetid);
2078     pa_assert(node);
2079     pa_assert_se((murphyif = u->murphyif));
2080     
2081     rif = &murphyif->resource;
2082
2083     if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid))) {
2084         for (i = 0;  i < rh->nnode;  i++) {
2085             if (rh->nodes[i] == n)
2086                 return NULL;
2087         }
2088
2089         i = rh->nnode++;
2090         rh->nodes = pa_xrealloc(rh->nodes, sizeof(mir_node *) * (rh->nnode+1));
2091     }
2092     else {
2093         rset = pa_xnew0(rset_data, 1);
2094
2095         rset->id = pa_xstrdup(rsetid);
2096         rset->policy = pa_xstrdup("unknown");
2097
2098         rh = pa_xnew0(rset_hash, 1);
2099
2100         rh->nnode = 1;
2101         rh->nodes = pa_xnew0(mir_node *, 2);
2102         rh->rset  = rset;
2103
2104         pa_hashmap_put(rif->nodes.rsetid, rh->rset->id, rh);
2105
2106         i = 0;
2107     }
2108
2109     rh->nodes[i+0] = node;
2110     rh->nodes[i+1] = NULL;
2111
2112     return rh;
2113 }
2114
2115 static rset_hash *rset_hashmap_get(struct userdata *u, const char *rsetid)
2116 {
2117     pa_murphyif *murphyif;
2118     resource_interface *rif;
2119     rset_hash *rh;
2120
2121     pa_assert(u);
2122     pa_assert(rsetid);
2123     pa_assert(murphyif = u->murphyif);
2124     
2125     rif = &murphyif->resource;
2126
2127     if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid)))
2128         return rh;
2129
2130     return NULL;
2131 }
2132
2133 static int rset_hashmap_remove(struct userdata *u,
2134                                const char *rsetid,
2135                                mir_node *node)
2136 {
2137     pa_murphyif *murphyif;
2138     resource_interface *rif;
2139     rset_hash *rh;
2140     size_t i,j;
2141
2142     pa_assert(u);
2143     pa_assert_se((murphyif = u->murphyif));
2144
2145     rif = &murphyif->resource;
2146
2147     if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid))) {
2148
2149         for (i = 0;  i < rh->nnode;  i++) {
2150             if (node == rh->nodes[i]) {
2151                 if (rh->nnode <= 1) {
2152                     pa_hashmap_remove(rif->nodes.rsetid, rsetid);
2153                     rset_hashmap_free(rh, NULL);
2154                     return 0;
2155                 }
2156                 else {
2157                     for (j = i;  j < rh->nnode;  j++)
2158                         rh->nodes[j] = rh->nodes[j+1];
2159
2160                     rh->nnode--;
2161
2162                     return 0;
2163                 }
2164             }
2165         }
2166     }
2167
2168     return -1;
2169 }
2170
2171 #endif
2172
2173 static pa_proplist *get_node_proplist(struct userdata *u, mir_node *node)
2174 {
2175     pa_core *core;
2176     pa_sink_input *i;
2177     pa_source_output *o;
2178
2179     pa_assert(u);
2180     pa_assert(node);
2181     pa_assert_se((core = u->core));
2182     
2183     if (node->implement == mir_stream && node->paidx != PA_IDXSET_INVALID) {
2184         if (node->direction == mir_input) {
2185             if ((i = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
2186                 return i->proplist;
2187         }
2188         else if (node->direction == mir_output) {
2189             if ((o = pa_idxset_get_by_index(core->source_outputs,node->paidx)))
2190                 return o->proplist;
2191         }
2192     }
2193
2194     return NULL;
2195 }
2196
2197 static const char *get_node_pid(struct userdata *u, mir_node *node)
2198 {
2199     pa_proplist *pl;
2200
2201     pa_assert(u);
2202  
2203     if (node && (pl = get_node_proplist(u, node)))
2204         return pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_ID);
2205
2206     return NULL;
2207 }
2208
2209 /*
2210  * Local Variables:
2211  * c-basic-offset: 4
2212  * indent-tabs-mode: nil
2213  * End:
2214  *
2215  */