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