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