mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	flatpak: add flatpak module
Pass LoopUtils to callbacks to make it easier to reschedule timeouts.
This commit is contained in:
		
							parent
							
								
									a5b39019d1
								
							
						
					
					
						commit
						be528ba7c2
					
				
					 17 changed files with 826 additions and 259 deletions
				
			
		| 
						 | 
				
			
			@ -376,7 +376,8 @@ static const PinosRegistryEvents registry_events = {
 | 
			
		|||
typedef bool (*PinosDemarshalFunc) (void *object, void *data, size_t size);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
do_flush_event (SpaSource *source,
 | 
			
		||||
do_flush_event (SpaLoopUtils *utils,
 | 
			
		||||
                SpaSource    *source,
 | 
			
		||||
                void         *data)
 | 
			
		||||
{
 | 
			
		||||
  PinosContextImpl *impl = data;
 | 
			
		||||
| 
						 | 
				
			
			@ -394,7 +395,8 @@ on_need_flush (PinosListener   *listener,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_context_data (SpaSource *source,
 | 
			
		||||
on_context_data (SpaLoopUtils *utils,
 | 
			
		||||
                 SpaSource    *source,
 | 
			
		||||
                 int           fd,
 | 
			
		||||
                 SpaIO         mask,
 | 
			
		||||
                 void         *data)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,21 +35,6 @@
 | 
			
		|||
#include <pinos/client/loop.h>
 | 
			
		||||
#include <pinos/client/log.h>
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaSource source;
 | 
			
		||||
  SpaList link;
 | 
			
		||||
 | 
			
		||||
  bool close;
 | 
			
		||||
  union {
 | 
			
		||||
    SpaSourceIOFunc io;
 | 
			
		||||
    SpaSourceIdleFunc idle;
 | 
			
		||||
    SpaSourceEventFunc event;
 | 
			
		||||
    SpaSourceTimerFunc timer;
 | 
			
		||||
    SpaSourceSignalFunc signal;
 | 
			
		||||
  } func;
 | 
			
		||||
  int signal_number;
 | 
			
		||||
} SpaSourceImpl;
 | 
			
		||||
 | 
			
		||||
#define DATAS_SIZE (4096 * 8)
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +68,24 @@ typedef struct {
 | 
			
		|||
  uint8_t        buffer_data[DATAS_SIZE];
 | 
			
		||||
} PinosLoopImpl;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaSource source;
 | 
			
		||||
 | 
			
		||||
  PinosLoopImpl *impl;
 | 
			
		||||
  SpaList link;
 | 
			
		||||
 | 
			
		||||
  bool close;
 | 
			
		||||
  union {
 | 
			
		||||
    SpaSourceIOFunc io;
 | 
			
		||||
    SpaSourceIdleFunc idle;
 | 
			
		||||
    SpaSourceEventFunc event;
 | 
			
		||||
    SpaSourceTimerFunc timer;
 | 
			
		||||
    SpaSourceSignalFunc signal;
 | 
			
		||||
  } func;
 | 
			
		||||
  int signal_number;
 | 
			
		||||
  bool enabled;
 | 
			
		||||
} SpaSourceImpl;
 | 
			
		||||
 | 
			
		||||
static inline uint32_t
 | 
			
		||||
spa_io_to_epoll (SpaIO mask)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -221,7 +224,8 @@ loop_invoke (SpaLoop       *loop,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
event_func (SpaSource *source,
 | 
			
		||||
event_func (SpaLoopUtils *utils,
 | 
			
		||||
            SpaSource    *source,
 | 
			
		||||
            void         *data)
 | 
			
		||||
{
 | 
			
		||||
  PinosLoopImpl *impl = data;
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +318,7 @@ static void
 | 
			
		|||
source_io_func (SpaSource *source)
 | 
			
		||||
{
 | 
			
		||||
  SpaSourceImpl *impl = SPA_CONTAINER_OF (source, SpaSourceImpl, source);
 | 
			
		||||
  impl->func.io (source, source->fd, source->rmask, source->data);
 | 
			
		||||
  impl->func.io (&impl->impl->utils, source, source->fd, source->rmask, source->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaSource *
 | 
			
		||||
| 
						 | 
				
			
			@ -337,6 +341,7 @@ loop_add_io (SpaLoopUtils    *utils,
 | 
			
		|||
  source->source.data = data;
 | 
			
		||||
  source->source.fd = fd;
 | 
			
		||||
  source->source.mask = mask;
 | 
			
		||||
  source->impl = impl;
 | 
			
		||||
  source->close = close;
 | 
			
		||||
  source->func.io = func;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -360,11 +365,12 @@ static void
 | 
			
		|||
source_idle_func (SpaSource *source)
 | 
			
		||||
{
 | 
			
		||||
  SpaSourceImpl *impl = SPA_CONTAINER_OF (source, SpaSourceImpl, source);
 | 
			
		||||
  impl->func.idle (source, source->data);
 | 
			
		||||
  impl->func.idle (&impl->impl->utils, source, source->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaSource *
 | 
			
		||||
loop_add_idle (SpaLoopUtils      *utils,
 | 
			
		||||
               bool               enabled,
 | 
			
		||||
               SpaSourceIdleFunc  func,
 | 
			
		||||
               void              *data)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -379,6 +385,7 @@ loop_add_idle (SpaLoopUtils      *utils,
 | 
			
		|||
  source->source.func = source_idle_func;
 | 
			
		||||
  source->source.data = data;
 | 
			
		||||
  source->source.fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK);
 | 
			
		||||
  source->impl = impl;
 | 
			
		||||
  source->close = true;
 | 
			
		||||
  source->source.mask = SPA_IO_IN;
 | 
			
		||||
  source->func.idle = func;
 | 
			
		||||
| 
						 | 
				
			
			@ -387,6 +394,7 @@ loop_add_idle (SpaLoopUtils      *utils,
 | 
			
		|||
 | 
			
		||||
  spa_list_insert (&impl->source_list, &source->link);
 | 
			
		||||
 | 
			
		||||
  if (enabled)
 | 
			
		||||
    spa_loop_utils_enable_idle (&impl->utils, &source->source, true);
 | 
			
		||||
 | 
			
		||||
  return &source->source;
 | 
			
		||||
| 
						 | 
				
			
			@ -396,16 +404,18 @@ static void
 | 
			
		|||
loop_enable_idle (SpaSource *source,
 | 
			
		||||
                  bool       enabled)
 | 
			
		||||
{
 | 
			
		||||
  SpaSourceImpl *impl = SPA_CONTAINER_OF (source, SpaSourceImpl, source);
 | 
			
		||||
  uint64_t count;
 | 
			
		||||
 | 
			
		||||
  if (enabled) {
 | 
			
		||||
  if (enabled && !impl->enabled) {
 | 
			
		||||
    count = 1;
 | 
			
		||||
    if (write (source->fd, &count, sizeof (uint64_t)) != sizeof (uint64_t))
 | 
			
		||||
      pinos_log_warn ("loop %p: failed to write idle fd: %s", source, strerror (errno));
 | 
			
		||||
  } else {
 | 
			
		||||
  } else if (!enabled && impl->enabled) {
 | 
			
		||||
    if (read (source->fd, &count, sizeof (uint64_t)) != sizeof (uint64_t))
 | 
			
		||||
      pinos_log_warn ("loop %p: failed to read idle fd: %s", source, strerror (errno));
 | 
			
		||||
  }
 | 
			
		||||
  impl->enabled = enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
| 
						 | 
				
			
			@ -417,7 +427,7 @@ source_event_func (SpaSource *source)
 | 
			
		|||
  if (read (source->fd, &count, sizeof (uint64_t)) != sizeof (uint64_t))
 | 
			
		||||
    pinos_log_warn ("loop %p: failed to read event fd: %s", source, strerror (errno));
 | 
			
		||||
 | 
			
		||||
  impl->func.event (source, source->data);
 | 
			
		||||
  impl->func.event (&impl->impl->utils, source, source->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaSource *
 | 
			
		||||
| 
						 | 
				
			
			@ -437,6 +447,7 @@ loop_add_event (SpaLoopUtils       *utils,
 | 
			
		|||
  source->source.data = data;
 | 
			
		||||
  source->source.fd = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK);
 | 
			
		||||
  source->source.mask = SPA_IO_IN;
 | 
			
		||||
  source->impl = impl;
 | 
			
		||||
  source->close = true;
 | 
			
		||||
  source->func.event = func;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -465,7 +476,7 @@ source_timer_func (SpaSource *source)
 | 
			
		|||
  if (read (source->fd, &expires, sizeof (uint64_t)) != sizeof (uint64_t))
 | 
			
		||||
    pinos_log_warn ("loop %p: failed to read timer fd: %s", source, strerror (errno));
 | 
			
		||||
 | 
			
		||||
  impl->func.timer (source, source->data);
 | 
			
		||||
  impl->func.timer (&impl->impl->utils, source, source->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaSource *
 | 
			
		||||
| 
						 | 
				
			
			@ -485,6 +496,7 @@ loop_add_timer (SpaLoopUtils       *utils,
 | 
			
		|||
  source->source.data = data;
 | 
			
		||||
  source->source.fd = timerfd_create (CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
 | 
			
		||||
  source->source.mask = SPA_IO_IN;
 | 
			
		||||
  source->impl = impl;
 | 
			
		||||
  source->close = true;
 | 
			
		||||
  source->func.timer = func;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -532,7 +544,7 @@ source_signal_func (SpaSource *source)
 | 
			
		|||
  if (read (source->fd, &signal_info, sizeof (signal_info)) != sizeof (signal_info))
 | 
			
		||||
    pinos_log_warn ("loop %p: failed to read signal fd: %s", source, strerror (errno));
 | 
			
		||||
 | 
			
		||||
  impl->func.signal (source, impl->signal_number, source->data);
 | 
			
		||||
  impl->func.signal (&impl->impl->utils, source, impl->signal_number, source->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaSource *
 | 
			
		||||
| 
						 | 
				
			
			@ -557,6 +569,7 @@ loop_add_signal (SpaLoopUtils        *utils,
 | 
			
		|||
  source->source.fd = signalfd (-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
 | 
			
		||||
  sigprocmask (SIG_BLOCK, &mask, NULL);
 | 
			
		||||
  source->source.mask = SPA_IO_IN;
 | 
			
		||||
  source->impl = impl;
 | 
			
		||||
  source->close = true;
 | 
			
		||||
  source->func.signal = func;
 | 
			
		||||
  source->signal_number = signal_number;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -409,7 +409,8 @@ do_node_init (PinosStream *stream)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_timeout (SpaSource *source,
 | 
			
		||||
on_timeout (SpaLoopUtils *utils,
 | 
			
		||||
            SpaSource    *source,
 | 
			
		||||
            void         *data)
 | 
			
		||||
{
 | 
			
		||||
  PinosStream *stream = data;
 | 
			
		||||
| 
						 | 
				
			
			@ -515,7 +516,8 @@ handle_rtnode_event (PinosStream  *stream,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_rtsocket_condition (SpaSource    *source,
 | 
			
		||||
on_rtsocket_condition (SpaLoopUtils *utils,
 | 
			
		||||
                       SpaSource    *source,
 | 
			
		||||
                       int           fd,
 | 
			
		||||
                       SpaIO         mask,
 | 
			
		||||
                       void         *data)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,7 +57,8 @@ post_hook (SpaLoopControl *ctrl,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
do_stop (SpaSource *source,
 | 
			
		||||
do_stop (SpaLoopUtils *utils,
 | 
			
		||||
         SpaSource    *source,
 | 
			
		||||
         void         *data)
 | 
			
		||||
{
 | 
			
		||||
  PinosThreadMainLoopImpl *impl = data;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,4 +3,4 @@ load-module libpinos-module-protocol-native
 | 
			
		|||
load-module libpinos-module-suspend-on-idle
 | 
			
		||||
load-module libpinos-module-spa --pattern snow
 | 
			
		||||
load-module libpinos-module-autolink
 | 
			
		||||
load-module libpinos-module-access
 | 
			
		||||
load-module libpinos-module-flatpak
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,13 +6,13 @@ pinos_module_c_args = [
 | 
			
		|||
  '-D_GNU_SOURCE',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
pinos_module_access = shared_library('pinos-module-access', [ 'module-access.c' ],
 | 
			
		||||
pinos_module_flatpak = shared_library('pinos-module-flatpak', [ 'module-flatpak.c' ],
 | 
			
		||||
  c_args : pinos_module_c_args,
 | 
			
		||||
  include_directories : [configinc, spa_inc],
 | 
			
		||||
  link_with : spalib,
 | 
			
		||||
  install : true,
 | 
			
		||||
  install_dir : '@0@/pinos-0.1'.format(get_option('libdir')),
 | 
			
		||||
  dependencies : [mathlib, dl_lib, pinos_dep, pinoscore_dep],
 | 
			
		||||
  dependencies : [dbus_dep, mathlib, dl_lib, pinos_dep, pinoscore_dep],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
pinos_module_autolink = shared_library('pinos-module-autolink', [ 'module-autolink.c' ],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,188 +0,0 @@
 | 
			
		|||
/* Pinos
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include "pinos/server/core.h"
 | 
			
		||||
#include "pinos/server/module.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  PinosCore       *core;
 | 
			
		||||
  PinosProperties *properties;
 | 
			
		||||
 | 
			
		||||
  PinosListener  check_send;
 | 
			
		||||
  PinosListener  check_dispatch;
 | 
			
		||||
} ModuleImpl;
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
check_global_owner (PinosCore   *core,
 | 
			
		||||
                    PinosClient *client,
 | 
			
		||||
                    PinosGlobal *global)
 | 
			
		||||
{
 | 
			
		||||
  pinos_log_debug ("%p", global);
 | 
			
		||||
 | 
			
		||||
  if (global == NULL)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  pinos_log_debug ("%p", global->owner);
 | 
			
		||||
 | 
			
		||||
  if (global->owner == NULL)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  pinos_log_debug ("%d %d", global->owner->ucred.uid, client->ucred.uid);
 | 
			
		||||
 | 
			
		||||
  if (global->owner->ucred.uid == client->ucred.uid)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
do_view_global (PinosAccess      *access,
 | 
			
		||||
                PinosClient      *client,
 | 
			
		||||
                PinosGlobal      *global)
 | 
			
		||||
{
 | 
			
		||||
  if (global->type == client->core->type.link) {
 | 
			
		||||
    PinosLink *link = global->object;
 | 
			
		||||
 | 
			
		||||
    pinos_log_debug ("link %p: global %p %p %p %p", link, global->owner, client, link->output, link->input);
 | 
			
		||||
 | 
			
		||||
    /* we must be able to see both nodes */
 | 
			
		||||
    if (link->output && !check_global_owner (client->core, client, link->output->node->global))
 | 
			
		||||
      return SPA_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
    pinos_log_debug ("link %p: global %p %p %p %p", link, global->owner, client, link->output, link->input);
 | 
			
		||||
 | 
			
		||||
    if (link->input && !check_global_owner (client->core, client, link->input->node->global))
 | 
			
		||||
      return SPA_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
    pinos_log_debug ("link %p: global %p %p %p %p", link, global->owner, client, link->output, link->input);
 | 
			
		||||
  }
 | 
			
		||||
  else if (!check_global_owner (client->core, client, global))
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
do_create_node (PinosAccess      *access,
 | 
			
		||||
                PinosAccessData  *data,
 | 
			
		||||
                const char       *factory_name,
 | 
			
		||||
                const char       *name,
 | 
			
		||||
                PinosProperties  *properties)
 | 
			
		||||
{
 | 
			
		||||
  data->res = SPA_RESULT_OK;
 | 
			
		||||
  data->complete_cb (data);
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static void
 | 
			
		||||
check_timeout (SpaSource *source,
 | 
			
		||||
               void      *d)
 | 
			
		||||
{
 | 
			
		||||
  PinosAccessData *data = d;
 | 
			
		||||
 | 
			
		||||
  data->res = SPA_RESULT_OK;
 | 
			
		||||
  data->complete_cb (data);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
do_create_client_node (PinosAccess      *access,
 | 
			
		||||
                       PinosAccessData  *data,
 | 
			
		||||
                       const char       *name,
 | 
			
		||||
                       PinosProperties  *properties)
 | 
			
		||||
{
 | 
			
		||||
#if 0
 | 
			
		||||
  struct timespec value;
 | 
			
		||||
  SpaSource *timer;
 | 
			
		||||
 | 
			
		||||
  pinos_log_debug ("access %p: check %s %p", access, name, properties);
 | 
			
		||||
 | 
			
		||||
  timer = pinos_loop_add_timer (data->resource->core->main_loop->loop,
 | 
			
		||||
                                check_timeout,
 | 
			
		||||
                                data->async_copy (data, 0));
 | 
			
		||||
  value.tv_sec = 3;
 | 
			
		||||
  value.tv_nsec = 0;
 | 
			
		||||
  pinos_loop_update_timer (data->resource->core->main_loop->loop,
 | 
			
		||||
                           timer,
 | 
			
		||||
                           &value,
 | 
			
		||||
                           NULL,
 | 
			
		||||
                           false);
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_RETURN_ASYNC (0);
 | 
			
		||||
#else
 | 
			
		||||
  data->res = SPA_RESULT_OK;
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PinosAccess access_checks =
 | 
			
		||||
{
 | 
			
		||||
  do_view_global,
 | 
			
		||||
  do_create_node,
 | 
			
		||||
  do_create_client_node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static ModuleImpl *
 | 
			
		||||
module_new (PinosCore       *core,
 | 
			
		||||
            PinosProperties *properties)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl;
 | 
			
		||||
 | 
			
		||||
  impl = calloc (1, sizeof (ModuleImpl));
 | 
			
		||||
  pinos_log_debug ("module %p: new", impl);
 | 
			
		||||
 | 
			
		||||
  impl->core = core;
 | 
			
		||||
  impl->properties = properties;
 | 
			
		||||
 | 
			
		||||
  core->access = &access_checks;
 | 
			
		||||
 | 
			
		||||
  return impl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static void
 | 
			
		||||
module_destroy (ModuleImpl *impl)
 | 
			
		||||
{
 | 
			
		||||
  pinos_log_debug ("module %p: destroy", impl);
 | 
			
		||||
 | 
			
		||||
  pinos_global_destroy (impl->global);
 | 
			
		||||
 | 
			
		||||
  pinos_signal_remove (&impl->global_added);
 | 
			
		||||
  pinos_signal_remove (&impl->global_removed);
 | 
			
		||||
  pinos_signal_remove (&impl->port_added);
 | 
			
		||||
  pinos_signal_remove (&impl->port_removed);
 | 
			
		||||
  pinos_signal_remove (&impl->port_unlinked);
 | 
			
		||||
  pinos_signal_remove (&impl->link_state_changed);
 | 
			
		||||
  free (impl);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
pinos__module_init (PinosModule * module, const char * args)
 | 
			
		||||
{
 | 
			
		||||
  module_new (module->core, NULL);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										717
									
								
								pinos/modules/module-flatpak.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										717
									
								
								pinos/modules/module-flatpak.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,717 @@
 | 
			
		|||
/* Pinos
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include <dbus/dbus.h>
 | 
			
		||||
 | 
			
		||||
#include "pinos/client/utils.h"
 | 
			
		||||
#include "pinos/server/core.h"
 | 
			
		||||
#include "pinos/server/module.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  PinosCore       *core;
 | 
			
		||||
  PinosProperties *properties;
 | 
			
		||||
 | 
			
		||||
  DBusConnection  *bus;
 | 
			
		||||
 | 
			
		||||
  PinosListener    global_added;
 | 
			
		||||
  PinosListener    global_removed;
 | 
			
		||||
 | 
			
		||||
  SpaList          client_list;
 | 
			
		||||
  PinosAccess      access;
 | 
			
		||||
 | 
			
		||||
  SpaSource       *dispatch_event;
 | 
			
		||||
} ModuleImpl;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  ModuleImpl  *impl;
 | 
			
		||||
  SpaList      link;
 | 
			
		||||
  PinosClient *client;
 | 
			
		||||
  bool         is_sandboxed;
 | 
			
		||||
  SpaList      async_pending;
 | 
			
		||||
} ClientInfo;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaList          link;
 | 
			
		||||
  bool             handled;
 | 
			
		||||
  ClientInfo      *info;
 | 
			
		||||
  char            *handle;
 | 
			
		||||
  PinosAccessData *access_data;
 | 
			
		||||
} AsyncPending;
 | 
			
		||||
 | 
			
		||||
static ClientInfo *
 | 
			
		||||
find_client_info (ModuleImpl *impl, PinosClient *client)
 | 
			
		||||
{
 | 
			
		||||
  ClientInfo *info;
 | 
			
		||||
 | 
			
		||||
  spa_list_for_each (info, &impl->client_list, link) {
 | 
			
		||||
    if (info->client == client)
 | 
			
		||||
      return info;
 | 
			
		||||
  }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
close_request (AsyncPending *p)
 | 
			
		||||
{
 | 
			
		||||
  DBusMessage *m = NULL;
 | 
			
		||||
  ModuleImpl *impl = p->info->impl;
 | 
			
		||||
 | 
			
		||||
  if (!(m = dbus_message_new_method_call ("org.freedesktop.portal.Request",
 | 
			
		||||
                                          p->handle,
 | 
			
		||||
                                          "org.freedesktop.portal.Request",
 | 
			
		||||
                                          "Close"))) {
 | 
			
		||||
    pinos_log_error ("Failed to create message");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!dbus_connection_send (impl->bus, m, NULL))
 | 
			
		||||
    pinos_log_error ("Failed to send message");
 | 
			
		||||
 | 
			
		||||
  dbus_message_unref(m);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static AsyncPending *
 | 
			
		||||
find_pending (ClientInfo *cinfo, const char *handle)
 | 
			
		||||
{
 | 
			
		||||
  AsyncPending *p;
 | 
			
		||||
 | 
			
		||||
  spa_list_for_each (p, &cinfo->async_pending, link) {
 | 
			
		||||
    if (strcmp (p->handle, handle) == 0)
 | 
			
		||||
      return p;
 | 
			
		||||
  }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
free_pending (PinosAccessData *d)
 | 
			
		||||
{
 | 
			
		||||
  AsyncPending *p = d->user_data;
 | 
			
		||||
 | 
			
		||||
  if (!p->handled)
 | 
			
		||||
    close_request(p);
 | 
			
		||||
 | 
			
		||||
  spa_list_remove (&p->link);
 | 
			
		||||
  free (p->handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
add_pending (ClientInfo *cinfo, const char *handle, PinosAccessData *access_data)
 | 
			
		||||
{
 | 
			
		||||
  AsyncPending *p;
 | 
			
		||||
  PinosAccessData *ad;
 | 
			
		||||
 | 
			
		||||
  ad = access_data->async_copy (access_data, sizeof (AsyncPending));
 | 
			
		||||
  ad->free_cb = free_pending;
 | 
			
		||||
 | 
			
		||||
  p = ad->user_data;
 | 
			
		||||
  p->info = cinfo;
 | 
			
		||||
  p->handle = strdup (handle);
 | 
			
		||||
  p->access_data = ad;
 | 
			
		||||
  p->handled = false;
 | 
			
		||||
 | 
			
		||||
  spa_list_insert (cinfo->async_pending.prev, &p->link);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
client_info_free (ClientInfo *cinfo)
 | 
			
		||||
{
 | 
			
		||||
  AsyncPending *p, *tmp;
 | 
			
		||||
 | 
			
		||||
  spa_list_for_each_safe (p, tmp, &cinfo->async_pending, link) {
 | 
			
		||||
    p->access_data->res = SPA_RESULT_NO_PERMISSION;
 | 
			
		||||
    p->access_data->complete_cb (p->access_data);
 | 
			
		||||
  }
 | 
			
		||||
  spa_list_remove (&cinfo->link);
 | 
			
		||||
  free (cinfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
client_is_sandboxed (PinosClient *cl)
 | 
			
		||||
{
 | 
			
		||||
  char data[2048], *ptr;
 | 
			
		||||
  size_t n, size;
 | 
			
		||||
  const char *state = NULL;
 | 
			
		||||
  const char *current;
 | 
			
		||||
  bool result;
 | 
			
		||||
  int fd;
 | 
			
		||||
  pid_t pid;
 | 
			
		||||
 | 
			
		||||
  if (cl->ucred_valid) {
 | 
			
		||||
    pinos_log_info ("client has trusted pid %d", cl->ucred.pid);
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    pinos_log_info ("no trusted pid found, assuming not sandboxed\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pid = cl->ucred.pid;
 | 
			
		||||
 | 
			
		||||
  sprintf (data, "/proc/%u/cgroup", pid);
 | 
			
		||||
  fd = open (data, O_RDONLY | O_CLOEXEC, 0);
 | 
			
		||||
  if (fd == -1)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  size = sizeof (data);
 | 
			
		||||
  ptr = data;
 | 
			
		||||
 | 
			
		||||
  while (size > 0) {
 | 
			
		||||
    int r;
 | 
			
		||||
 | 
			
		||||
    if ((r = read (fd, data, size)) < 0) {
 | 
			
		||||
      if (errno == EINTR)
 | 
			
		||||
        continue;
 | 
			
		||||
      else
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (r == 0)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    ptr += r;
 | 
			
		||||
    size -= r;
 | 
			
		||||
  }
 | 
			
		||||
  close (fd);
 | 
			
		||||
 | 
			
		||||
  result = false;
 | 
			
		||||
  while ((current = pinos_split_walk (data, "\n", &n, &state)) != NULL) {
 | 
			
		||||
    if (strncmp (current, "1:name=systemd:", strlen ("1:name=systemd:")) == 0) {
 | 
			
		||||
      const char *p = strstr (current, "flatpak-");
 | 
			
		||||
      if (p && p - current < n) {
 | 
			
		||||
        pinos_log_info ("found a flatpak cgroup, assuming sandboxed\n");
 | 
			
		||||
        result = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
check_global_owner (PinosCore   *core,
 | 
			
		||||
                    PinosClient *client,
 | 
			
		||||
                    PinosGlobal *global)
 | 
			
		||||
{
 | 
			
		||||
  if (global == NULL)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (global->owner == NULL)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  if (global->owner->ucred.uid == client->ucred.uid)
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
do_view_global (PinosAccess      *access,
 | 
			
		||||
                PinosClient      *client,
 | 
			
		||||
                PinosGlobal      *global)
 | 
			
		||||
{
 | 
			
		||||
  if (global->type == client->core->type.link) {
 | 
			
		||||
    PinosLink *link = global->object;
 | 
			
		||||
 | 
			
		||||
    /* we must be able to see both nodes */
 | 
			
		||||
    if (link->output && !check_global_owner (client->core, client, link->output->node->global))
 | 
			
		||||
      return SPA_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
    if (link->input && !check_global_owner (client->core, client, link->input->node->global))
 | 
			
		||||
      return SPA_RESULT_ERROR;
 | 
			
		||||
  }
 | 
			
		||||
  else if (!check_global_owner (client->core, client, global))
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
do_create_node (PinosAccess      *access,
 | 
			
		||||
                PinosAccessData  *data,
 | 
			
		||||
                const char       *factory_name,
 | 
			
		||||
                const char       *name,
 | 
			
		||||
                PinosProperties  *properties)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = SPA_CONTAINER_OF (access, ModuleImpl, access);
 | 
			
		||||
  ClientInfo *cinfo = find_client_info (impl, data->resource->client);
 | 
			
		||||
 | 
			
		||||
  if (cinfo->is_sandboxed)
 | 
			
		||||
    data->res = SPA_RESULT_NO_PERMISSION;
 | 
			
		||||
  else
 | 
			
		||||
    data->res = SPA_RESULT_OK;
 | 
			
		||||
 | 
			
		||||
  data->complete_cb (data);
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static DBusHandlerResult
 | 
			
		||||
portal_response (DBusConnection *connection, DBusMessage *msg, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
  ClientInfo *cinfo = user_data;
 | 
			
		||||
 | 
			
		||||
  if (dbus_message_is_signal (msg, "org.freedesktop.portal.Request", "Response")) {
 | 
			
		||||
    uint32_t response = 2;
 | 
			
		||||
    DBusError error;
 | 
			
		||||
    AsyncPending *p;
 | 
			
		||||
    PinosAccessData *d;
 | 
			
		||||
 | 
			
		||||
    dbus_error_init (&error);
 | 
			
		||||
 | 
			
		||||
    dbus_connection_remove_filter (connection, portal_response, cinfo);
 | 
			
		||||
 | 
			
		||||
    if (!dbus_message_get_args (msg, &error, DBUS_TYPE_UINT32, &response, DBUS_TYPE_INVALID)) {
 | 
			
		||||
        pinos_log_error("failed to parse Response: %s", error.message);
 | 
			
		||||
        dbus_error_free(&error);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    p = find_pending (cinfo, dbus_message_get_path (msg));
 | 
			
		||||
    if (p == NULL)
 | 
			
		||||
        return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
 | 
			
		||||
    p->handled = true;
 | 
			
		||||
    d = p->access_data;
 | 
			
		||||
 | 
			
		||||
    pinos_log_debug ("portal check result: %d", response);
 | 
			
		||||
 | 
			
		||||
    d->res = response == 0 ? SPA_RESULT_OK : SPA_RESULT_NO_PERMISSION;
 | 
			
		||||
    d->complete_cb (d);
 | 
			
		||||
 | 
			
		||||
    return DBUS_HANDLER_RESULT_HANDLED;
 | 
			
		||||
  }
 | 
			
		||||
  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
do_create_client_node (PinosAccess      *access,
 | 
			
		||||
                       PinosAccessData  *data,
 | 
			
		||||
                       const char       *name,
 | 
			
		||||
                       PinosProperties  *properties)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = SPA_CONTAINER_OF (access, ModuleImpl, access);
 | 
			
		||||
  ClientInfo *cinfo = find_client_info (impl, data->resource->client);
 | 
			
		||||
  DBusMessage *m = NULL, *r = NULL;
 | 
			
		||||
  DBusError error;
 | 
			
		||||
  pid_t pid;
 | 
			
		||||
  DBusMessageIter msg_iter;
 | 
			
		||||
  DBusMessageIter dict_iter;
 | 
			
		||||
  const char *handle;
 | 
			
		||||
  const char *device;
 | 
			
		||||
 | 
			
		||||
  if (!cinfo->is_sandboxed) {
 | 
			
		||||
    data->res = SPA_RESULT_OK;
 | 
			
		||||
    data->complete_cb (data);
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pinos_log_info ("ask portal for client %p", cinfo->client);
 | 
			
		||||
 | 
			
		||||
  dbus_error_init(&error);
 | 
			
		||||
 | 
			
		||||
  if (!(m = dbus_message_new_method_call ("org.freedesktop.portal.Desktop",
 | 
			
		||||
                                          "/org/freedesktop/portal/desktop",
 | 
			
		||||
                                          "org.freedesktop.portal.Device",
 | 
			
		||||
                                          "AccessDevice")))
 | 
			
		||||
    goto no_method_call;
 | 
			
		||||
 | 
			
		||||
  device = "camera";
 | 
			
		||||
 | 
			
		||||
  pid = cinfo->client->ucred.pid;
 | 
			
		||||
  if (!dbus_message_append_args(m,
 | 
			
		||||
                                DBUS_TYPE_UINT32, &pid,
 | 
			
		||||
                                DBUS_TYPE_INVALID))
 | 
			
		||||
    goto message_failed;
 | 
			
		||||
 | 
			
		||||
  dbus_message_iter_init_append(m, &msg_iter);
 | 
			
		||||
  dbus_message_iter_open_container (&msg_iter, DBUS_TYPE_ARRAY, "s", &dict_iter);
 | 
			
		||||
  dbus_message_iter_append_basic (&dict_iter, DBUS_TYPE_STRING, &device);
 | 
			
		||||
  dbus_message_iter_close_container (&msg_iter, &dict_iter);
 | 
			
		||||
 | 
			
		||||
  dbus_message_iter_open_container (&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter);
 | 
			
		||||
  dbus_message_iter_close_container (&msg_iter, &dict_iter);
 | 
			
		||||
 | 
			
		||||
  if (!(r = dbus_connection_send_with_reply_and_block (impl->bus, m, -1, &error)))
 | 
			
		||||
    goto send_failed;
 | 
			
		||||
 | 
			
		||||
  dbus_message_unref(m);
 | 
			
		||||
 | 
			
		||||
  if (!dbus_message_get_args (r, &error, DBUS_TYPE_OBJECT_PATH, &handle, DBUS_TYPE_INVALID))
 | 
			
		||||
    goto parse_failed;
 | 
			
		||||
 | 
			
		||||
  dbus_message_unref (r);
 | 
			
		||||
 | 
			
		||||
  dbus_bus_add_match (impl->bus,
 | 
			
		||||
                     "type='signal',interface='org.freedesktop.portal.Request'",
 | 
			
		||||
                     &error);
 | 
			
		||||
  dbus_connection_flush (impl->bus);
 | 
			
		||||
  if (dbus_error_is_set (&error))
 | 
			
		||||
    goto subscribe_failed;
 | 
			
		||||
 | 
			
		||||
  dbus_connection_add_filter (impl->bus, portal_response, cinfo, NULL);
 | 
			
		||||
 | 
			
		||||
  add_pending (cinfo, handle, data);
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_RETURN_ASYNC (0);
 | 
			
		||||
 | 
			
		||||
no_method_call:
 | 
			
		||||
  pinos_log_error ("Failed to create message");
 | 
			
		||||
  return SPA_RESULT_NO_PERMISSION;
 | 
			
		||||
message_failed:
 | 
			
		||||
  dbus_message_unref(m);
 | 
			
		||||
  return SPA_RESULT_NO_PERMISSION;
 | 
			
		||||
send_failed:
 | 
			
		||||
  pinos_log_error ("Failed to call portal: %s", error.message);
 | 
			
		||||
  dbus_error_free(&error);
 | 
			
		||||
  dbus_message_unref(m);
 | 
			
		||||
  return SPA_RESULT_NO_PERMISSION;
 | 
			
		||||
parse_failed:
 | 
			
		||||
  pinos_log_error ("Failed to parse AccessDevice result: %s", error.message);
 | 
			
		||||
  dbus_error_free (&error);
 | 
			
		||||
  dbus_message_unref (r);
 | 
			
		||||
  return SPA_RESULT_NO_PERMISSION;
 | 
			
		||||
subscribe_failed:
 | 
			
		||||
  pinos_log_error ("Failed to subscribe to Request signal: %s", error.message);
 | 
			
		||||
  dbus_error_free(&error);
 | 
			
		||||
  return SPA_RESULT_NO_PERMISSION;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PinosAccess access_checks =
 | 
			
		||||
{
 | 
			
		||||
  do_view_global,
 | 
			
		||||
  do_create_node,
 | 
			
		||||
  do_create_client_node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_global_added (PinosListener *listener,
 | 
			
		||||
                 PinosCore     *core,
 | 
			
		||||
                 PinosGlobal   *global)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = SPA_CONTAINER_OF (listener, ModuleImpl, global_added);
 | 
			
		||||
 | 
			
		||||
  if (global->type == impl->core->type.client) {
 | 
			
		||||
    PinosClient *client = global->object;
 | 
			
		||||
    ClientInfo *cinfo;
 | 
			
		||||
 | 
			
		||||
    cinfo = calloc (1, sizeof (ClientInfo));
 | 
			
		||||
    cinfo->impl = impl;
 | 
			
		||||
    cinfo->client = client;
 | 
			
		||||
    cinfo->is_sandboxed = client_is_sandboxed (client);
 | 
			
		||||
    cinfo->is_sandboxed = true;
 | 
			
		||||
    spa_list_init (&cinfo->async_pending);
 | 
			
		||||
 | 
			
		||||
    spa_list_insert (impl->client_list.prev, &cinfo->link);
 | 
			
		||||
 | 
			
		||||
    pinos_log_debug ("module %p: client %p added", impl, client);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_global_removed (PinosListener *listener,
 | 
			
		||||
                   PinosCore     *core,
 | 
			
		||||
                   PinosGlobal   *global)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = SPA_CONTAINER_OF (listener, ModuleImpl, global_removed);
 | 
			
		||||
 | 
			
		||||
  if (global->type == impl->core->type.client) {
 | 
			
		||||
    PinosClient *client = global->object;
 | 
			
		||||
    ClientInfo *cinfo;
 | 
			
		||||
 | 
			
		||||
    if ((cinfo = find_client_info (impl, client)))
 | 
			
		||||
      client_info_free (cinfo);
 | 
			
		||||
 | 
			
		||||
    pinos_log_debug ("module %p: client %p removed", impl, client);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
dispatch_cb (SpaLoopUtils *utils, SpaSource *source, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
 | 
			
		||||
  if (dbus_connection_dispatch (impl->bus) == DBUS_DISPATCH_COMPLETE)
 | 
			
		||||
    pinos_loop_enable_idle (impl->core->main_loop->loop, source, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
dispatch_status (DBusConnection *conn, DBusDispatchStatus status, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
 | 
			
		||||
  pinos_loop_enable_idle (impl->core->main_loop->loop,
 | 
			
		||||
                          impl->dispatch_event,
 | 
			
		||||
                          status == DBUS_DISPATCH_COMPLETE ? false : true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline SpaIO
 | 
			
		||||
dbus_to_io (DBusWatch *watch)
 | 
			
		||||
{
 | 
			
		||||
  SpaIO mask;
 | 
			
		||||
  unsigned int flags;
 | 
			
		||||
 | 
			
		||||
  /* no watch flags for disabled watches */
 | 
			
		||||
  if (!dbus_watch_get_enabled (watch))
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  flags = dbus_watch_get_flags (watch);
 | 
			
		||||
  mask = SPA_IO_HUP | SPA_IO_ERR;
 | 
			
		||||
 | 
			
		||||
  if (flags & DBUS_WATCH_READABLE)
 | 
			
		||||
    mask |= SPA_IO_IN;
 | 
			
		||||
  if (flags & DBUS_WATCH_WRITABLE)
 | 
			
		||||
    mask |= SPA_IO_OUT;
 | 
			
		||||
 | 
			
		||||
  return mask;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline unsigned int
 | 
			
		||||
io_to_dbus (SpaIO mask)
 | 
			
		||||
{
 | 
			
		||||
  unsigned int flags = 0;
 | 
			
		||||
 | 
			
		||||
  if (mask & SPA_IO_IN)
 | 
			
		||||
    flags |= DBUS_WATCH_READABLE;
 | 
			
		||||
  if (mask & SPA_IO_OUT)
 | 
			
		||||
    flags |= DBUS_WATCH_WRITABLE;
 | 
			
		||||
  if (mask & SPA_IO_HUP)
 | 
			
		||||
    flags |= DBUS_WATCH_HANGUP;
 | 
			
		||||
  if (mask & SPA_IO_ERR)
 | 
			
		||||
    flags |= DBUS_WATCH_ERROR;
 | 
			
		||||
  return flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
handle_io_event (SpaLoopUtils *utils,
 | 
			
		||||
                 SpaSource    *source,
 | 
			
		||||
                 int           fd,
 | 
			
		||||
                 SpaIO         mask,
 | 
			
		||||
                 void         *userdata)
 | 
			
		||||
{
 | 
			
		||||
  DBusWatch *watch = userdata;
 | 
			
		||||
 | 
			
		||||
  if (!dbus_watch_get_enabled (watch)) {
 | 
			
		||||
    pinos_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  dbus_watch_handle (watch, io_to_dbus (mask));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dbus_bool_t
 | 
			
		||||
add_watch (DBusWatch *watch, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
  SpaSource *source;
 | 
			
		||||
 | 
			
		||||
  pinos_log_debug ("add watch %p %d", watch, dbus_watch_get_unix_fd (watch));
 | 
			
		||||
 | 
			
		||||
  /* we dup because dbus tends to add the same fd multiple times and our epoll
 | 
			
		||||
   * implementation does not like that */
 | 
			
		||||
  source = pinos_loop_add_io (impl->core->main_loop->loop,
 | 
			
		||||
                              dup (dbus_watch_get_unix_fd (watch)),
 | 
			
		||||
                              dbus_to_io (watch),
 | 
			
		||||
                              true,
 | 
			
		||||
                              handle_io_event,
 | 
			
		||||
                              watch);
 | 
			
		||||
 | 
			
		||||
  dbus_watch_set_data (watch, source, NULL);
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
remove_watch (DBusWatch *watch, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
  SpaSource *source;
 | 
			
		||||
 | 
			
		||||
  if ((source = dbus_watch_get_data (watch)))
 | 
			
		||||
    pinos_loop_destroy_source (impl->core->main_loop->loop, source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
toggle_watch (DBusWatch *watch, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
  SpaSource *source;
 | 
			
		||||
 | 
			
		||||
  source = dbus_watch_get_data (watch);
 | 
			
		||||
 | 
			
		||||
  pinos_loop_update_io (impl->core->main_loop->loop,
 | 
			
		||||
                        source,
 | 
			
		||||
                        dbus_to_io (watch));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
handle_timer_event (SpaLoopUtils *utils, SpaSource *source, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  DBusTimeout *timeout = userdata;
 | 
			
		||||
  uint64_t t;
 | 
			
		||||
  struct timespec ts;
 | 
			
		||||
 | 
			
		||||
  if (dbus_timeout_get_enabled (timeout)) {
 | 
			
		||||
    t = dbus_timeout_get_interval (timeout) * SPA_NSEC_PER_MSEC;
 | 
			
		||||
    ts.tv_sec = t / SPA_NSEC_PER_SEC;
 | 
			
		||||
    ts.tv_nsec = t % SPA_NSEC_PER_SEC;
 | 
			
		||||
    spa_loop_utils_update_timer (utils, source, &ts, NULL, false);
 | 
			
		||||
 | 
			
		||||
    dbus_timeout_handle (timeout);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dbus_bool_t
 | 
			
		||||
add_timeout (DBusTimeout *timeout, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
  SpaSource *source;
 | 
			
		||||
  struct timespec ts;
 | 
			
		||||
  uint64_t t;
 | 
			
		||||
 | 
			
		||||
  if (!dbus_timeout_get_enabled (timeout))
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  source = pinos_loop_add_timer (impl->core->main_loop->loop,
 | 
			
		||||
                                 handle_timer_event,
 | 
			
		||||
                                 timeout);
 | 
			
		||||
 | 
			
		||||
  dbus_timeout_set_data (timeout, source, NULL);
 | 
			
		||||
 | 
			
		||||
  t = dbus_timeout_get_interval (timeout) * SPA_NSEC_PER_MSEC;
 | 
			
		||||
  ts.tv_sec = t / SPA_NSEC_PER_SEC;
 | 
			
		||||
  ts.tv_nsec = t % SPA_NSEC_PER_SEC;
 | 
			
		||||
  pinos_loop_update_timer (impl->core->main_loop->loop,
 | 
			
		||||
                           source,
 | 
			
		||||
                           &ts,
 | 
			
		||||
                           NULL,
 | 
			
		||||
                           false);
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
remove_timeout (DBusTimeout *timeout, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
  SpaSource *source;
 | 
			
		||||
 | 
			
		||||
  if ((source = dbus_timeout_get_data (timeout)))
 | 
			
		||||
    pinos_loop_destroy_source (impl->core->main_loop->loop, source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
toggle_timeout (DBusTimeout *timeout, void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
  SpaSource *source;
 | 
			
		||||
  struct timespec ts, *tsp;
 | 
			
		||||
 | 
			
		||||
  source = dbus_timeout_get_data (timeout);
 | 
			
		||||
 | 
			
		||||
  if (dbus_timeout_get_enabled (timeout)) {
 | 
			
		||||
    uint64_t t = dbus_timeout_get_interval (timeout) * SPA_NSEC_PER_MSEC;
 | 
			
		||||
    ts.tv_sec = t / SPA_NSEC_PER_SEC;
 | 
			
		||||
    ts.tv_nsec = t % SPA_NSEC_PER_SEC;
 | 
			
		||||
    tsp = &ts;
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    tsp = NULL;
 | 
			
		||||
  }
 | 
			
		||||
  pinos_loop_update_timer (impl->core->main_loop->loop, source, tsp, NULL, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
wakeup_main (void *userdata)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl = userdata;
 | 
			
		||||
 | 
			
		||||
  pinos_loop_enable_idle (impl->core->main_loop->loop,
 | 
			
		||||
                          impl->dispatch_event,
 | 
			
		||||
                          true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static ModuleImpl *
 | 
			
		||||
module_new (PinosCore       *core,
 | 
			
		||||
            PinosProperties *properties)
 | 
			
		||||
{
 | 
			
		||||
  ModuleImpl *impl;
 | 
			
		||||
  DBusError error;
 | 
			
		||||
 | 
			
		||||
  dbus_error_init(&error);
 | 
			
		||||
 | 
			
		||||
  impl = calloc (1, sizeof (ModuleImpl));
 | 
			
		||||
  pinos_log_debug ("module %p: new", impl);
 | 
			
		||||
 | 
			
		||||
  impl->core = core;
 | 
			
		||||
  impl->properties = properties;
 | 
			
		||||
  impl->access = access_checks;
 | 
			
		||||
 | 
			
		||||
  impl->bus = dbus_bus_get_private (DBUS_BUS_SESSION, &error);
 | 
			
		||||
  if (impl->bus == NULL)
 | 
			
		||||
    goto error;
 | 
			
		||||
 | 
			
		||||
  impl->dispatch_event = pinos_loop_add_idle (core->main_loop->loop,
 | 
			
		||||
                                              false,
 | 
			
		||||
                                              dispatch_cb,
 | 
			
		||||
                                              impl);
 | 
			
		||||
 | 
			
		||||
  dbus_connection_set_exit_on_disconnect (impl->bus, false);
 | 
			
		||||
  dbus_connection_set_dispatch_status_function (impl->bus, dispatch_status, impl, NULL);
 | 
			
		||||
  dbus_connection_set_watch_functions (impl->bus, add_watch, remove_watch, toggle_watch, impl, NULL);
 | 
			
		||||
  dbus_connection_set_timeout_functions (impl->bus, add_timeout, remove_timeout, toggle_timeout, impl, NULL);
 | 
			
		||||
  dbus_connection_set_wakeup_main_function (impl->bus, wakeup_main, impl, NULL);
 | 
			
		||||
 | 
			
		||||
  core->access = &impl->access;
 | 
			
		||||
 | 
			
		||||
  spa_list_init (&impl->client_list);
 | 
			
		||||
 | 
			
		||||
  pinos_signal_add (&core->global_added, &impl->global_added, on_global_added);
 | 
			
		||||
  pinos_signal_add (&core->global_removed, &impl->global_removed, on_global_removed);
 | 
			
		||||
 | 
			
		||||
  return impl;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
  pinos_log_error ("Failed to connect to system bus: %s", error.message);
 | 
			
		||||
  dbus_error_free (&error);
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static void
 | 
			
		||||
module_destroy (ModuleImpl *impl)
 | 
			
		||||
{
 | 
			
		||||
  pinos_log_debug ("module %p: destroy", impl);
 | 
			
		||||
 | 
			
		||||
  dbus_connection_close (impl->bus);
 | 
			
		||||
  dbus_connection_unref (impl->bus);
 | 
			
		||||
  free (impl);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
pinos__module_init (PinosModule * module, const char * args)
 | 
			
		||||
{
 | 
			
		||||
  module_new (module->core, NULL);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +120,8 @@ on_before_iterate (PinosListener *listener,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
connection_data (SpaSource *source,
 | 
			
		||||
connection_data (SpaLoopUtils *utils,
 | 
			
		||||
                 SpaSource    *source,
 | 
			
		||||
                 int           fd,
 | 
			
		||||
                 SpaIO         mask,
 | 
			
		||||
                 void         *data)
 | 
			
		||||
| 
						 | 
				
			
			@ -330,7 +331,8 @@ err:
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
socket_data (SpaSource *source,
 | 
			
		||||
socket_data (SpaLoopUtils *utils,
 | 
			
		||||
             SpaSource    *source,
 | 
			
		||||
             int           fd,
 | 
			
		||||
             SpaIO         mask,
 | 
			
		||||
             void         *data)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,7 +77,8 @@ node_info_free (NodeInfo *info)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
idle_timeout (SpaSource *source,
 | 
			
		||||
idle_timeout (SpaLoopUtils *utils,
 | 
			
		||||
              SpaSource    *source,
 | 
			
		||||
              void         *data)
 | 
			
		||||
{
 | 
			
		||||
  NodeInfo *info = data;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,6 +102,7 @@ pinos_client_new (PinosCore       *core,
 | 
			
		|||
  this->properties = properties;
 | 
			
		||||
 | 
			
		||||
  spa_list_init (&this->resource_list);
 | 
			
		||||
  pinos_signal_init (&this->properties_changed);
 | 
			
		||||
  pinos_signal_init (&this->resource_added);
 | 
			
		||||
  pinos_signal_init (&this->resource_removed);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +186,8 @@ pinos_client_update_properties (PinosClient     *client,
 | 
			
		|||
  client->info.change_mask = 1 << 0;
 | 
			
		||||
  client->info.props = client->properties ? &client->properties->dict : NULL;
 | 
			
		||||
 | 
			
		||||
  pinos_signal_emit (&client->properties_changed, client);
 | 
			
		||||
 | 
			
		||||
  spa_list_for_each (resource, &client->resource_list, link) {
 | 
			
		||||
    pinos_client_notify_info (resource, &client->info);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,9 @@ struct _PinosClient {
 | 
			
		|||
  PinosGlobal *global;
 | 
			
		||||
 | 
			
		||||
  PinosProperties *properties;
 | 
			
		||||
  PINOS_SIGNAL (properties_changed, (PinosListener *listener,
 | 
			
		||||
                                     PinosClient   *client));
 | 
			
		||||
 | 
			
		||||
  PinosClientInfo  info;
 | 
			
		||||
  bool             ucred_valid;
 | 
			
		||||
  struct ucred     ucred;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -111,7 +111,8 @@ do_loop (void *user_data)
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
do_stop (SpaSource *source,
 | 
			
		||||
do_stop (SpaLoopUtils *utils,
 | 
			
		||||
         SpaSource    *source,
 | 
			
		||||
         void         *data)
 | 
			
		||||
{
 | 
			
		||||
  PinosDataLoopImpl *impl = data;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,8 @@ typedef struct
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
process_work_queue (SpaSource *source,
 | 
			
		||||
process_work_queue (SpaLoopUtils *utils,
 | 
			
		||||
                    SpaSource    *source,
 | 
			
		||||
                    void         *data)
 | 
			
		||||
{
 | 
			
		||||
  PinosWorkQueueImpl *impl = data;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,7 +124,10 @@ typedef void (*SpaNotify) (void *data);
 | 
			
		|||
#define SPA_ID_INVALID  ((uint32_t)0xffffffff)
 | 
			
		||||
 | 
			
		||||
#define SPA_NSEC_PER_SEC  (1000000000ll)
 | 
			
		||||
#define SPA_NSEC_PER_MSEC (1000000ll)
 | 
			
		||||
#define SPA_NSEC_PER_USEC (1000ll)
 | 
			
		||||
#define SPA_USEC_PER_SEC  (1000000ll)
 | 
			
		||||
#define SPA_USEC_PER_MSEC (1000ll)
 | 
			
		||||
#define SPA_MSEC_PER_SEC  (1000ll)
 | 
			
		||||
 | 
			
		||||
#define SPA_TIMESPEC_TO_TIME(ts) ((ts)->tv_sec * SPA_NSEC_PER_SEC + (ts)->tv_nsec)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -126,17 +126,22 @@ struct _SpaLoopControl {
 | 
			
		|||
#define spa_loop_control_leave(l)              (l)->leave(l)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
typedef void (*SpaSourceIOFunc)     (SpaSource *source,
 | 
			
		||||
typedef void (*SpaSourceIOFunc)     (SpaLoopUtils *utils,
 | 
			
		||||
                                     SpaSource    *source,
 | 
			
		||||
                                     int           fd,
 | 
			
		||||
                                     SpaIO         mask,
 | 
			
		||||
                                     void         *data);
 | 
			
		||||
typedef void (*SpaSourceIdleFunc)   (SpaSource *source,
 | 
			
		||||
typedef void (*SpaSourceIdleFunc)   (SpaLoopUtils *utils,
 | 
			
		||||
                                     SpaSource    *source,
 | 
			
		||||
                                     void         *data);
 | 
			
		||||
typedef void (*SpaSourceEventFunc)  (SpaSource *source,
 | 
			
		||||
typedef void (*SpaSourceEventFunc)  (SpaLoopUtils *utils,
 | 
			
		||||
                                     SpaSource    *source,
 | 
			
		||||
                                     void         *data);
 | 
			
		||||
typedef void (*SpaSourceTimerFunc)  (SpaSource *source,
 | 
			
		||||
typedef void (*SpaSourceTimerFunc)  (SpaLoopUtils *utils,
 | 
			
		||||
                                     SpaSource    *source,
 | 
			
		||||
                                     void         *data);
 | 
			
		||||
typedef void (*SpaSourceSignalFunc) (SpaSource *source,
 | 
			
		||||
typedef void (*SpaSourceSignalFunc) (SpaLoopUtils *utils,
 | 
			
		||||
                                     SpaSource    *source,
 | 
			
		||||
                                     int           signal_number,
 | 
			
		||||
                                     void         *data);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +165,7 @@ struct _SpaLoopUtils {
 | 
			
		|||
                                     SpaIO               mask);
 | 
			
		||||
 | 
			
		||||
  SpaSource *  (*add_idle)          (SpaLoopUtils       *utils,
 | 
			
		||||
                                     bool                enabled,
 | 
			
		||||
                                     SpaSourceIdleFunc   func,
 | 
			
		||||
                                     void               *data);
 | 
			
		||||
  void         (*enable_idle)       (SpaSource          *source,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue