1150 lines
28 KiB
C
1150 lines
28 KiB
C
/* dbus_service.c
|
|
*
|
|
* D-BUS Service Utilities
|
|
*
|
|
* Provides MINIMAL utilities for construction of D-BUS "Services".
|
|
*
|
|
* Copyright(C) Jason Vas Dias, Red Hat Inc., 2005
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation at
|
|
* http://www.fsf.org/licensing/licenses/gpl.txt
|
|
* and included in this software distribution as the "LICENSE" file.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <linux/limits.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/select.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/ioctl.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#include <syslog.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
extern size_t strnlen(const char *s, size_t maxlen);
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <ifaddrs.h>
|
|
#include <search.h>
|
|
#include <getopt.h>
|
|
typedef void (*__free_fn_t) (void *__nodep);
|
|
extern void tdestroy (void *__root, __free_fn_t __freefct);
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#define DBUS_API_SUBJECT_TO_CHANGE "Very Annoying and Silly!"
|
|
#include <dbus/dbus.h>
|
|
|
|
#include <named/dbus_service.h>
|
|
|
|
typedef struct dbcs_s
|
|
{
|
|
DBusConnection *connection;
|
|
DBusDispatchStatus dispatchStatus;
|
|
uint32_t status;
|
|
dbus_svc_WatchHandler wh;
|
|
void * wh_arg;
|
|
const char * unique_name;
|
|
dbus_svc_MessageHandler mh;
|
|
void * def_mh_obj;
|
|
dbus_svc_MessageHandler mf;
|
|
void * def_mf_obj;
|
|
dbus_svc_ShutdownHandler sh;
|
|
void * sh_obj;
|
|
dbus_svc_ErrorHandler eh;
|
|
dbus_svc_ErrorHandler dh;
|
|
/*{ glibc b-trees: */
|
|
void * roots;
|
|
void * timeouts;
|
|
void * watches;
|
|
void * filters;
|
|
/*}*/
|
|
int n;
|
|
fd_set r_fds;
|
|
fd_set s_r_fds;
|
|
fd_set w_fds;
|
|
fd_set s_w_fds;
|
|
fd_set e_fds;
|
|
fd_set s_e_fds;
|
|
DBusMessage *currentMessage;
|
|
int rejectMessage;
|
|
} DBusConnectionState;
|
|
|
|
typedef struct root_s
|
|
{
|
|
char *path;
|
|
char *if_prefix;
|
|
DBUS_SVC cs;
|
|
dbus_svc_MessageHandler mh;
|
|
void *object;
|
|
void *tree;
|
|
} Root;
|
|
|
|
typedef struct mhn_s
|
|
{
|
|
char *path;
|
|
dbus_svc_MessageHandler mh;
|
|
void *object;
|
|
} MessageHandlerNode;
|
|
|
|
typedef struct mfn_s
|
|
{
|
|
DBusConnectionState *cs;
|
|
dbus_svc_MessageHandler mf;
|
|
void *obj;
|
|
int n_matches;
|
|
char **matches;
|
|
} MessageFilterNode;
|
|
|
|
typedef struct dbto_s
|
|
{
|
|
DBusTimeout *to;
|
|
DBusConnectionState *cs;
|
|
struct timeval tv;
|
|
} DBusConnectionTimeout;
|
|
|
|
static void no_free( void *p){ p=0; }
|
|
|
|
static int ptr_key_comparator( const void *p1, const void *p2 )
|
|
{
|
|
return
|
|
( (p1 == p2)
|
|
? 0
|
|
:( (p1 > p2)
|
|
? 1
|
|
: -1
|
|
)
|
|
);
|
|
}
|
|
|
|
static DBusHandlerResult
|
|
default_message_filter
|
|
( DBusConnection *connection,
|
|
DBusMessage *message,
|
|
void *p
|
|
)
|
|
{
|
|
DBusConnectionState *cs = p;
|
|
uint32_t type =dbus_message_get_type( message ),
|
|
serial =dbus_message_get_serial( message );
|
|
uint8_t reply =dbus_message_get_no_reply( message )==0;
|
|
const char
|
|
*path = dbus_message_get_path( message ),
|
|
*dest = dbus_message_get_destination( message ),
|
|
*member = dbus_message_get_member( message ),
|
|
*interface=dbus_message_get_interface( message ),
|
|
*sender =dbus_message_get_sender( message ),
|
|
*signature=dbus_message_get_signature( message );
|
|
connection = connection;
|
|
if(cs->mf)
|
|
return
|
|
(*(cs->mf))( cs, type, reply, serial, dest, path, member, interface, 0L,
|
|
sender, signature, message, 0L, 0L, 0L, cs->def_mf_obj
|
|
) ;
|
|
return HANDLED;
|
|
}
|
|
|
|
uint8_t
|
|
dbus_svc_add_filter
|
|
( DBusConnectionState *cs, dbus_svc_MessageHandler mh, void *obj, int n_matches, ... )
|
|
{
|
|
DBusError error;
|
|
va_list va;
|
|
char *m;
|
|
|
|
va_start(va, n_matches );
|
|
|
|
cs->mf = mh;
|
|
cs->def_mf_obj = obj;
|
|
|
|
if ( ! dbus_connection_add_filter (cs->connection, default_message_filter, cs, NULL))
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_add_filter: dbus_connection_add_filter failed");
|
|
va_end(va);
|
|
return( 0 );
|
|
}
|
|
|
|
if( n_matches )
|
|
{
|
|
memset(&error,'\0',sizeof(DBusError));
|
|
dbus_error_init(&error);
|
|
while( n_matches-- )
|
|
{
|
|
m = va_arg(va, char* ) ;
|
|
|
|
dbus_bus_add_match(cs->connection, m, &error);
|
|
|
|
if( dbus_error_is_set(&error))
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_add_filter: dbus_bus_add_match failed for %s: %s %s",
|
|
m, error.name, error.message
|
|
);
|
|
va_end(va);
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
return( 1 );
|
|
}
|
|
|
|
|
|
uint8_t
|
|
dbus_svc_get_args_va(DBusConnectionState *cs, DBusMessage* msg, dbus_svc_DataType firstType, va_list va)
|
|
{
|
|
DBusError error;
|
|
memset(&error,'\0',sizeof(DBusError));
|
|
dbus_error_init(&error);
|
|
if( (!dbus_message_get_args_valist(msg, &error, firstType, va)) || dbus_error_is_set(&error) )
|
|
{
|
|
if( dbus_error_is_set(&error) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_get_args failed: %s %s",error.name, error.message);
|
|
dbus_error_free(&error);
|
|
}else
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_get_args failed: dbus_message_get_args_valist failed");
|
|
return( 0 );
|
|
}
|
|
return( 1 );
|
|
}
|
|
|
|
uint8_t
|
|
dbus_svc_get_args(DBusConnectionState *cs, DBusMessage* msg, dbus_svc_DataType firstType, ...)
|
|
{
|
|
va_list va;
|
|
uint8_t r;
|
|
va_start(va, firstType);
|
|
r = dbus_svc_get_args_va( cs, msg, firstType, va);
|
|
va_end(va);
|
|
return r;
|
|
}
|
|
|
|
uint8_t
|
|
dbus_svc_send_va
|
|
( DBusConnectionState *cs,
|
|
dbus_svc_MessageType type,
|
|
int32_t reply_serial,
|
|
uint32_t *new_serial,
|
|
const char *destination,
|
|
const char *path,
|
|
const char *interface,
|
|
const char *member,
|
|
dbus_svc_DataType firstType,
|
|
va_list va
|
|
)
|
|
{
|
|
DBusMessageIter iter;
|
|
char *e;
|
|
DBusMessage *msg =
|
|
dbus_svc_new_message
|
|
( cs,
|
|
type,
|
|
reply_serial,
|
|
destination,
|
|
path,
|
|
interface,
|
|
member
|
|
);
|
|
|
|
if(msg == 0L)
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: dbus_svc_new_message failed");
|
|
return 0;
|
|
}
|
|
|
|
if( type != DBUS_MESSAGE_TYPE_ERROR )
|
|
{
|
|
if( !dbus_message_append_args_valist( msg, firstType, va ) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: dbus_message_append_args_valist failed");
|
|
return 0;
|
|
}
|
|
}else
|
|
{
|
|
if( firstType == DBUS_TYPE_STRING )
|
|
{
|
|
e = 0L;
|
|
e = va_arg( va, char* );
|
|
if( (e == 0L) || !dbus_message_set_error_name( msg, e ) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: dbus_message_set_error_name failed");
|
|
return 0;
|
|
}
|
|
firstType = va_arg(va, int);
|
|
if( firstType == DBUS_TYPE_STRING )
|
|
{
|
|
e = 0L;
|
|
e = va_arg( va, char* );
|
|
if( e == 0L )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: NULL error message");
|
|
return 0;
|
|
}
|
|
dbus_message_iter_init_append (msg, &iter);
|
|
if( !dbus_message_iter_append_basic
|
|
(&iter, DBUS_TYPE_STRING, &e)
|
|
)
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: dbus_message_iter_append_basic failed");
|
|
return 0;
|
|
}
|
|
}
|
|
}else
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: unhandled type for error name: %c", firstType);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if( !dbus_connection_send(cs->connection, msg, new_serial) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: dbus_message_send failed");
|
|
return 0;
|
|
}
|
|
if( cs->dh != 0L ) (*(cs->dh))("Sending message");
|
|
dbus_connection_flush(cs->connection);
|
|
return 1;
|
|
}
|
|
|
|
uint8_t
|
|
dbus_svc_send
|
|
( DBusConnectionState *cs,
|
|
dbus_svc_MessageType type,
|
|
int32_t reply_serial,
|
|
uint32_t *new_serial,
|
|
const char *destination,
|
|
const char *path,
|
|
const char *interface,
|
|
const char *member,
|
|
dbus_svc_DataType firstType,
|
|
...
|
|
)
|
|
{
|
|
uint8_t r;
|
|
va_list va;
|
|
va_start(va, firstType);
|
|
r = dbus_svc_send_va(cs, type, reply_serial, new_serial, destination, path,interface,member,firstType,va);
|
|
va_end(va);
|
|
return ( r ) ;
|
|
}
|
|
|
|
dbus_svc_MessageHandle
|
|
dbus_svc_new_message
|
|
( DBusConnectionState* cs,
|
|
dbus_svc_MessageType type,
|
|
int32_t reply_serial,
|
|
const char *destination,
|
|
const char *path,
|
|
const char *interface,
|
|
const char *member
|
|
)
|
|
{
|
|
DBusMessage *msg = dbus_message_new(type);
|
|
|
|
if( msg == 0L)
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_new_message: dbus_message_set_reply_serial failed");
|
|
return 0;
|
|
}
|
|
|
|
if( reply_serial != -1 )
|
|
{
|
|
if( !dbus_message_set_reply_serial(msg,reply_serial) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_new_message: dbus_message_set_reply_serial failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if( (destination !=0L) && !dbus_message_set_destination(msg, destination) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_new_message: dbus_message_set_destination failed");
|
|
return 0;
|
|
}
|
|
|
|
if( !dbus_message_set_path(msg, path) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_new_message: dbus_message_set_path failed");
|
|
return 0;
|
|
}
|
|
|
|
if( ! dbus_message_set_interface(msg,interface) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_new_message: dbus_message_set_interface failed - %s", interface);
|
|
return 0;
|
|
}
|
|
|
|
if( !dbus_message_set_member(msg,member) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_new_message: dbus_message_set_member failed");
|
|
return 0;
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
extern uint8_t
|
|
dbus_svc_send_message
|
|
(
|
|
DBusConnectionState *cs,
|
|
dbus_svc_MessageHandle msg,
|
|
uint32_t *new_serial
|
|
)
|
|
{
|
|
if( !dbus_connection_send(cs->connection, msg, new_serial) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: dbus_message_send failed");
|
|
return 0;
|
|
}
|
|
if( cs->dh != 0L ) (*(cs->dh))("Sending message");
|
|
dbus_connection_flush(cs->connection);
|
|
return 1;
|
|
}
|
|
|
|
uint8_t
|
|
dbus_svc_message_append_args(DBusConnectionState *cs, dbus_svc_MessageHandle msg, dbus_svc_DataType firstType, ...)
|
|
{
|
|
va_list va;
|
|
va_start(va, firstType);
|
|
if( !dbus_message_append_args_valist( msg, firstType, va ) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_send: dbus_message_append_args failed");
|
|
return 0;
|
|
}
|
|
va_end(va);
|
|
return ( 1 ) ;
|
|
}
|
|
|
|
dbus_svc_MessageHandle
|
|
dbus_svc_call
|
|
( DBusConnectionState *cs,
|
|
const char *destination,
|
|
const char *path,
|
|
const char *member,
|
|
const char *interface,
|
|
dbus_svc_DataType firstType,
|
|
...
|
|
)
|
|
{
|
|
DBusMessage *message=0L, *reply=0L;
|
|
va_list va;
|
|
DBusError error;
|
|
int reply_timeout=20000;
|
|
|
|
va_start(va, firstType);
|
|
|
|
memset(&error,'\0',sizeof(DBusError));
|
|
dbus_error_init(&error);
|
|
|
|
if(( message =
|
|
dbus_message_new_method_call
|
|
( destination,
|
|
path,
|
|
interface,
|
|
member
|
|
)
|
|
) == 0L
|
|
)
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_call: dbus_message_new_method_call failed");
|
|
va_end(va);
|
|
return(0L);
|
|
};
|
|
|
|
if( !dbus_message_append_args_valist( message, firstType, va ) )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_call: dbus_message_append_args_valist failed");
|
|
va_end(va);
|
|
return(0L);
|
|
}
|
|
|
|
if((reply =
|
|
dbus_connection_send_with_reply_and_block
|
|
(cs->connection,
|
|
message, reply_timeout,
|
|
&error
|
|
)
|
|
) == 0L
|
|
)
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("dbus_svc_call: dbus_message_send_with_reply_and_block failed: %s %s",
|
|
error.name, error.message
|
|
);
|
|
va_end(va);
|
|
return(0L);
|
|
}
|
|
return reply;
|
|
}
|
|
|
|
dbus_svc_MessageIterator
|
|
dbus_svc_message_iterator_new( DBusConnectionState *cs, DBusMessage *msg)
|
|
{
|
|
DBusMessageIter *iter = malloc( sizeof(DBusMessageIter) );
|
|
void *p =cs;
|
|
p++;
|
|
if( iter != 0L )
|
|
{
|
|
if( !dbus_message_iter_init( msg, iter ))
|
|
{
|
|
free( iter ) ;
|
|
iter = 0L;
|
|
}
|
|
}
|
|
return iter;
|
|
}
|
|
|
|
uint32_t
|
|
dbus_svc_message_next_arg_type( DBusConnectionState *cs, dbus_svc_MessageIterator iter )
|
|
{
|
|
void *p =cs;
|
|
p++;
|
|
return dbus_message_iter_get_arg_type( iter );
|
|
}
|
|
|
|
void
|
|
dbus_svc_message_next_arg( DBusConnectionState *cs, dbus_svc_MessageIterator iter, void *value )
|
|
{
|
|
void *p =cs;
|
|
p++;
|
|
dbus_message_iter_get_basic( iter, value );
|
|
dbus_message_iter_next( iter );
|
|
}
|
|
|
|
uint32_t
|
|
dbus_svc_message_element_type(DBusConnectionState *cs , dbus_svc_MessageIterator iter)
|
|
{
|
|
void *p =cs;
|
|
p++;
|
|
return dbus_message_iter_get_element_type(iter);
|
|
}
|
|
|
|
void
|
|
dbus_svc_message_get_elements( DBusConnectionState *cs , dbus_svc_MessageIterator iter, uint32_t *n, void *array )
|
|
{
|
|
void *p =cs;
|
|
p++;
|
|
dbus_message_iter_get_fixed_array( iter, n, array);
|
|
}
|
|
|
|
void dbus_svc_message_iterator_free( DBusConnectionState *cs, dbus_svc_MessageIterator iter )
|
|
{
|
|
void *p =cs;
|
|
p++;
|
|
free( iter );
|
|
}
|
|
|
|
uint8_t dbus_svc_message_type( DBusMessage *msg )
|
|
{
|
|
return dbus_message_get_type( msg );
|
|
}
|
|
|
|
static DBusConnectionState *
|
|
dbcs_new( DBusConnection *connection )
|
|
{
|
|
DBusConnectionState *dbcs = (DBusConnectionState *) malloc( sizeof(DBusConnectionState) );
|
|
if ( dbcs )
|
|
{
|
|
memset( dbcs, '\0', sizeof( DBusConnectionState ));
|
|
dbcs->connection = connection;
|
|
}
|
|
return(dbcs);
|
|
}
|
|
|
|
static DBusConnectionTimeout *
|
|
timeout_new( DBusTimeout *timeout )
|
|
{
|
|
DBusConnectionTimeout *to = (DBusConnectionTimeout *) malloc ( sizeof(DBusConnectionTimeout) );
|
|
if( to != 0L )
|
|
{
|
|
to->to = timeout;
|
|
dbus_timeout_set_data(timeout, to, 0L);
|
|
if( dbus_timeout_get_enabled(timeout) )
|
|
gettimeofday(&(to->tv),0L);
|
|
else
|
|
{
|
|
to->tv.tv_sec = 0 ;
|
|
to->tv.tv_usec = 0 ;
|
|
}
|
|
}
|
|
return( to );
|
|
}
|
|
|
|
static dbus_bool_t
|
|
add_timeout( DBusTimeout *timeout, void *csp )
|
|
{
|
|
DBusConnectionState *cs = csp;
|
|
DBusConnectionTimeout *to = timeout_new(timeout);
|
|
if( cs->dh != 0L ) (*(cs->dh))("add_timeout: %d", dbus_timeout_get_interval(timeout));
|
|
to->cs = cs;
|
|
if ( to )
|
|
{
|
|
if( tsearch((void*)to, &(cs->timeouts), ptr_key_comparator) != 0L )
|
|
return TRUE;
|
|
}
|
|
if( cs->eh != 0L ) (*(cs->eh))("add_timeout: out of memory");
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
remove_timeout( DBusTimeout *timeout, void *csp )
|
|
{
|
|
DBusConnectionState *cs = csp;
|
|
DBusConnectionTimeout *to = dbus_timeout_get_data(timeout);
|
|
if( (to != 0L) && (to->to == timeout) )
|
|
{
|
|
if( cs->dh != 0L ) (*(cs->dh))("remove_timeout: %d", dbus_timeout_get_interval(to->to));
|
|
if( tdelete((const void*)to, &(cs->timeouts), ptr_key_comparator) != 0L )
|
|
{
|
|
free(to);
|
|
}else
|
|
if( cs->eh != 0L ) (*(cs->eh))("remove_timeout: can't happen?!?: timeout data %p not found", to);
|
|
}else
|
|
if( cs->eh != 0L ) (*(cs->eh))("remove_timeout: can't happen?!?: timeout %p did not record data %p %p",
|
|
timeout, to, ((to != 0L) ? to->to : 0L)
|
|
);
|
|
}
|
|
|
|
static void
|
|
toggle_timeout( DBusTimeout *timeout, void *csp )
|
|
{
|
|
DBusConnectionState *cs = csp;
|
|
DBusConnectionTimeout **top = tfind( (const void*) dbus_timeout_get_data(timeout),
|
|
&(cs->timeouts),
|
|
ptr_key_comparator
|
|
),
|
|
*to=0L;
|
|
if( (top != 0L) && ((to=*top) != 0L) && (to->to == timeout) )
|
|
{
|
|
if( cs->dh != 0L ) (*(cs->dh))("toggle_timeout: %d", dbus_timeout_get_interval(to->to));
|
|
if( dbus_timeout_get_enabled(timeout) )
|
|
gettimeofday(&(to->tv),0L);
|
|
else
|
|
{
|
|
to->tv.tv_sec = 0 ;
|
|
to->tv.tv_usec = 0 ;
|
|
}
|
|
}else
|
|
if( cs->eh != 0L ) (*(cs->eh))("toggle_timeout: can't happen?!?: timeout %p %s %p %p", timeout,
|
|
((to==0L) ? "did not record data" : "not found"),
|
|
to, ((to != 0L) ? to->to : 0L)
|
|
);
|
|
}
|
|
|
|
static void
|
|
process_timeout( const void *p, const VISIT which, const int level)
|
|
{
|
|
DBusConnectionState *cs;
|
|
void * const *tpp = p;
|
|
DBusConnectionTimeout *to;
|
|
struct timeval tv;
|
|
float now, then, interval;
|
|
int l = level ? 1 : 0;
|
|
l=l;
|
|
|
|
gettimeofday(&tv,0L);
|
|
|
|
if( (tpp != 0L) && (*tpp != 0L) && ((which == postorder) || (which == leaf)) )
|
|
{
|
|
to = (DBusConnectionTimeout*)*tpp;
|
|
cs = to->cs;
|
|
if ( !dbus_timeout_get_enabled(to->to) )
|
|
return;
|
|
cs = dbus_timeout_get_data(to->to);
|
|
then = ((float)to->tv.tv_sec) + (((float)to->tv.tv_usec) / 1000000.0);
|
|
if( then != 0.0 )
|
|
{
|
|
interval = ((float)dbus_timeout_get_interval(to->to)) / 1000.0;
|
|
now = ((float)tv.tv_sec) + (( (float)tv.tv_usec) / 1000000.0);
|
|
if( (now - then) >= interval )
|
|
{
|
|
if( cs->dh != 0L ) (*(cs->dh))("handle_timeout: %d - %f %f %f", dbus_timeout_get_interval(to->to), then, now, interval);
|
|
dbus_timeout_handle( to->to );
|
|
to->tv=tv;
|
|
}
|
|
}else
|
|
{
|
|
to->tv = tv;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
process_timeouts ( DBusConnectionState *cs )
|
|
{
|
|
twalk( cs->timeouts, process_timeout );
|
|
}
|
|
|
|
static void
|
|
set_watch_fds( DBusWatch *watch, DBusConnectionState *cs )
|
|
{
|
|
uint8_t flags = dbus_watch_get_flags(watch);
|
|
int fd = dbus_watch_get_fd(watch);
|
|
|
|
if ( cs->n <= fd )
|
|
cs->n = fd + 1;
|
|
|
|
if ( dbus_watch_get_enabled(watch) )
|
|
{
|
|
if ( flags & DBUS_WATCH_READABLE )
|
|
{
|
|
FD_SET(fd , &(cs->r_fds));
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_ENABLE | WATCH_READ, cs->wh_arg );
|
|
}else
|
|
{
|
|
FD_CLR(fd , &(cs->r_fds));
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_READ, cs->wh_arg );
|
|
}
|
|
|
|
if ( flags & DBUS_WATCH_WRITABLE )
|
|
{
|
|
FD_SET(fd , &(cs->w_fds));
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_ENABLE | WATCH_WRITE, cs->wh_arg );
|
|
}else
|
|
{
|
|
FD_CLR(fd , &(cs->w_fds));
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_WRITE, cs->wh_arg );
|
|
}
|
|
if ( flags & DBUS_WATCH_ERROR )
|
|
{
|
|
FD_SET(fd , &(cs->e_fds));
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_ENABLE | WATCH_ERROR, cs->wh_arg );
|
|
}else
|
|
{
|
|
FD_CLR(fd , &(cs->e_fds));
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_ERROR, cs->wh_arg );
|
|
}
|
|
}else
|
|
{
|
|
if( FD_ISSET( fd, &(cs->r_fds)) )
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_READ, cs->wh_arg );
|
|
FD_CLR(fd , &(cs->r_fds));
|
|
|
|
if( FD_ISSET( fd, &(cs->w_fds)) )
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_WRITE, cs->wh_arg );
|
|
FD_CLR(fd , &(cs->w_fds));
|
|
|
|
if( FD_ISSET( fd, &(cs->e_fds)) )
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))( fd, WATCH_ERROR, cs->wh_arg );
|
|
FD_CLR(fd , &(cs->e_fds));
|
|
}
|
|
}
|
|
|
|
static dbus_bool_t
|
|
add_watch ( DBusWatch *watch, void *csp )
|
|
{
|
|
DBusConnectionState *cs = csp;
|
|
|
|
dbus_watch_set_data(watch, cs, no_free );
|
|
if( cs->dh != 0L ) (*(cs->dh))("add_watch: %d", dbus_watch_get_fd(watch));
|
|
if( tsearch((const void*)watch,&(cs->watches),ptr_key_comparator) == 0L )
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("add_watch: out of memory");
|
|
return FALSE;
|
|
}
|
|
set_watch_fds(watch,cs);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
remove_watch ( DBusWatch *watch, void *csp )
|
|
{
|
|
DBusConnectionState *cs = csp;
|
|
int fd = dbus_watch_get_fd(watch);
|
|
if( tdelete((const void*)watch, &(cs->watches), ptr_key_comparator) == 0L )
|
|
if( cs->eh != 0L ) (*(cs->eh))("remove_watch: can't happen?!?: watch not found");
|
|
if( cs->dh != 0L ) (*(cs->dh))("remove_watch: %d", dbus_watch_get_fd(watch));
|
|
FD_CLR(fd , &(cs->r_fds));
|
|
FD_CLR(fd , &(cs->w_fds));
|
|
FD_CLR(fd , &(cs->e_fds));
|
|
if( cs->wh != 0L )
|
|
(*(cs->wh))(dbus_watch_get_fd(watch), WATCH_READ | WATCH_WRITE | WATCH_ERROR, cs->wh_arg );
|
|
}
|
|
|
|
static void
|
|
toggle_watch ( DBusWatch *watch, void *csp )
|
|
{
|
|
DBusConnectionState *cs = csp;
|
|
if( cs->dh != 0L ) (*(cs->dh))("toggle_watch: %d", dbus_watch_get_fd(watch));
|
|
set_watch_fds(watch,cs);
|
|
}
|
|
|
|
static void
|
|
process_watch( const void *p, const VISIT which, const int level)
|
|
{
|
|
void * const *wpp=p;
|
|
DBusWatch *w;
|
|
int fd;
|
|
uint8_t flags;
|
|
DBusConnectionState *cs;
|
|
int l = level ? 1 : 0;
|
|
l=l;
|
|
|
|
if((wpp != 0L) && (*wpp != 0L) && ( (which == postorder) || (which == leaf) ) )
|
|
{
|
|
w = (DBusWatch*)*wpp;
|
|
cs = dbus_watch_get_data( w );
|
|
if( cs == 0 )
|
|
return;
|
|
if( ! dbus_watch_get_enabled(w) )
|
|
return;
|
|
fd = dbus_watch_get_fd( w );
|
|
flags = dbus_watch_get_flags( w );
|
|
if( cs->dh != 0L ) (*(cs->dh))("handle_watch: %d", dbus_watch_get_fd( w ));
|
|
if ( (flags & DBUS_WATCH_READABLE) && (FD_ISSET(fd, &(cs->s_r_fds))) )
|
|
dbus_watch_handle(w, DBUS_WATCH_READABLE);
|
|
if ( (flags & DBUS_WATCH_WRITABLE) && (FD_ISSET(fd, &(cs->s_w_fds))) )
|
|
dbus_watch_handle(w, DBUS_WATCH_READABLE);
|
|
if ( (flags & DBUS_WATCH_ERROR) && (FD_ISSET(fd, &(cs->s_e_fds))) )
|
|
dbus_watch_handle(w, DBUS_WATCH_ERROR);
|
|
}
|
|
}
|
|
|
|
static void
|
|
process_watches ( DBusConnectionState *cs )
|
|
{
|
|
twalk( cs->watches, process_watch );
|
|
}
|
|
|
|
void dbus_svc_handle_watch( DBusConnectionState *cs, int fd, dbus_svc_WatchFlags action )
|
|
{
|
|
switch( action & 7 )
|
|
{
|
|
case WATCH_READ:
|
|
FD_SET(fd, &(cs->s_r_fds));
|
|
break;
|
|
|
|
case WATCH_WRITE:
|
|
FD_SET(fd, &(cs->s_w_fds));
|
|
break;
|
|
|
|
case WATCH_ERROR:
|
|
FD_SET(fd, &(cs->s_e_fds));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dispatch_status
|
|
( DBusConnection *connection,
|
|
DBusDispatchStatus new_status,
|
|
void *csp
|
|
)
|
|
{
|
|
connection=connection;
|
|
DBusConnectionState *cs = csp;
|
|
cs->dispatchStatus = new_status;
|
|
}
|
|
|
|
void
|
|
dbus_svc_main_loop( DBusConnectionState *cs, void (*idle_handler)(DBusConnectionState *) )
|
|
{
|
|
struct timeval timeout={0,200000};
|
|
int n_fds;
|
|
|
|
while( cs->status != SHUTDOWN )
|
|
{
|
|
cs->s_r_fds = cs->r_fds;
|
|
cs->s_w_fds = cs->w_fds;
|
|
cs->s_e_fds = cs->e_fds;
|
|
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec= 200000;
|
|
|
|
if ( (n_fds = select(cs->n, &(cs->s_r_fds), &(cs->s_w_fds), &(cs->s_e_fds), &timeout)) < 0 )
|
|
{
|
|
if (errno != EINTR)
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))( "select failed: %d : %s", errno, strerror(errno));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( n_fds > 0 )
|
|
process_watches(cs);
|
|
|
|
process_timeouts(cs);
|
|
|
|
if ( cs->dispatchStatus != DBUS_DISPATCH_COMPLETE )
|
|
dbus_connection_dispatch( cs->connection );
|
|
|
|
if( idle_handler != 0L )
|
|
(*idle_handler)(cs);
|
|
}
|
|
}
|
|
|
|
void dbus_svc_dispatch(DBusConnectionState *cs)
|
|
{
|
|
process_watches(cs);
|
|
|
|
FD_ZERO(&(cs->s_r_fds));
|
|
FD_ZERO(&(cs->s_w_fds));
|
|
FD_ZERO(&(cs->s_e_fds));
|
|
|
|
process_timeouts(cs);
|
|
|
|
while ( cs->dispatchStatus != DBUS_DISPATCH_COMPLETE )
|
|
dbus_connection_dispatch( cs->connection );
|
|
}
|
|
|
|
void
|
|
dbus_svc_quit( DBusConnectionState *cs )
|
|
{
|
|
cs->status = SHUTDOWN;
|
|
}
|
|
|
|
static DBusConnectionState *
|
|
connection_setup
|
|
( DBusConnection *connection,
|
|
dbus_svc_WatchHandler wh,
|
|
dbus_svc_ErrorHandler eh,
|
|
dbus_svc_ErrorHandler dh,
|
|
void *wh_arg
|
|
)
|
|
{
|
|
DBusConnectionState *cs = dbcs_new( connection );
|
|
|
|
if ( cs == 0L )
|
|
{
|
|
if(eh)(*(eh))("connection_setup: out of memory");
|
|
goto fail;
|
|
}
|
|
cs->wh = wh;
|
|
cs->wh_arg = wh_arg;
|
|
cs->eh = eh;
|
|
cs->dh = dh;
|
|
|
|
if (!dbus_connection_set_watch_functions
|
|
( cs->connection,
|
|
add_watch,
|
|
remove_watch,
|
|
toggle_watch,
|
|
cs,
|
|
no_free
|
|
)
|
|
)
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("connection_setup: dbus_connection_set_watch_functions failed");
|
|
goto fail;
|
|
}
|
|
|
|
if (!dbus_connection_set_timeout_functions
|
|
( connection,
|
|
add_timeout,
|
|
remove_timeout,
|
|
toggle_timeout,
|
|
cs,
|
|
no_free
|
|
)
|
|
)
|
|
{
|
|
if( cs->eh != 0L ) (*(cs->eh))("connection_setup: dbus_connection_set_timeout_functions failed");
|
|
goto fail;
|
|
}
|
|
|
|
dbus_connection_set_dispatch_status_function
|
|
( connection,
|
|
dispatch_status,
|
|
cs,
|
|
no_free
|
|
);
|
|
|
|
if (dbus_connection_get_dispatch_status (connection) != DBUS_DISPATCH_COMPLETE)
|
|
dbus_connection_ref(connection);
|
|
|
|
return cs;
|
|
|
|
fail:
|
|
if( cs != 0L )
|
|
free(cs);
|
|
|
|
dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL);
|
|
dbus_connection_set_watch_functions (connection, NULL, NULL, NULL, NULL, NULL);
|
|
dbus_connection_set_timeout_functions (connection, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
return 0L;
|
|
}
|
|
|
|
DBusConnectionState *
|
|
dbus_svc_init
|
|
(
|
|
dbus_svc_DBUS_TYPE bus,
|
|
char *name,
|
|
dbus_svc_WatchHandler wh ,
|
|
dbus_svc_ErrorHandler eh ,
|
|
dbus_svc_ErrorHandler dh ,
|
|
void *wh_arg
|
|
)
|
|
{
|
|
DBusConnection *connection;
|
|
DBusError error;
|
|
DBusConnectionState *cs;
|
|
char *session_bus_address=0L;
|
|
|
|
memset(&error,'\0',sizeof(DBusError));
|
|
|
|
dbus_error_init(&error);
|
|
|
|
switch( bus )
|
|
{
|
|
/* DBUS_PRIVATE_* bus types are the only type which allow reconnection if the dbus-daemon is restarted
|
|
*/
|
|
case DBUS_PRIVATE_SYSTEM:
|
|
|
|
if ( (connection = dbus_connection_open_private("unix:path=/var/run/dbus/system_bus_socket", &error)) == 0L )
|
|
{
|
|
if(eh)(*eh)("dbus_svc_init failed: %s %s",error.name, error.message);
|
|
return ( 0L );
|
|
}
|
|
|
|
if ( ! dbus_bus_register(connection,&error) )
|
|
{
|
|
if(eh)(*eh)("dbus_bus_register failed: %s %s", error.name, error.message);
|
|
dbus_connection_close(connection);
|
|
free(connection);
|
|
return ( 0L );
|
|
}
|
|
break;
|
|
|
|
case DBUS_PRIVATE_SESSION:
|
|
|
|
session_bus_address = getenv("DBUS_SESSION_BUS_ADDRESS");
|
|
if ( session_bus_address == 0L )
|
|
{
|
|
if(eh)(*eh)("dbus_svc_init failed: DBUS_SESSION_BUS_ADDRESS environment variable not set");
|
|
return ( 0L );
|
|
}
|
|
|
|
if ( (connection = dbus_connection_open_private(session_bus_address, &error)) == 0L )
|
|
{
|
|
if(eh)(*eh)("dbus_svc_init failed: %s %s",error.name, error.message);
|
|
return ( 0L );
|
|
}
|
|
|
|
if ( ! dbus_bus_register(connection,&error) )
|
|
{
|
|
if(eh)(*eh)("dbus_bus_register failed: %s %s", error.name, error.message);
|
|
dbus_connection_close(connection);
|
|
free(connection);
|
|
return ( 0L );
|
|
}
|
|
break;
|
|
|
|
case DBUS_SYSTEM:
|
|
case DBUS_SESSION:
|
|
|
|
if ( (connection = dbus_bus_get (bus, &error)) == 0L )
|
|
{
|
|
if(eh)(*eh)("dbus_svc_init failed: %s %s",error.name, error.message);
|
|
return ( 0L );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if(eh)(*eh)("dbus_svc_init failed: unknown bus type %d", bus);
|
|
return ( 0L );
|
|
}
|
|
|
|
dbus_connection_set_exit_on_disconnect(connection, FALSE);
|
|
|
|
if ( (cs = connection_setup(connection, wh, eh, dh, wh_arg)) == 0L )
|
|
{
|
|
if(eh)(*eh)("dbus_svc_init failed: connection_setup failed");
|
|
return( 0L );
|
|
}
|
|
|
|
if( name == 0L )
|
|
return( cs );
|
|
|
|
cs->unique_name = dbus_bus_get_unique_name(connection);
|
|
|
|
switch
|
|
( dbus_bus_request_name
|
|
( connection, name, DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT , &error
|
|
)
|
|
)
|
|
{
|
|
case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
|
|
break;
|
|
case DBUS_REQUEST_NAME_REPLY_EXISTS:
|
|
case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
|
|
case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
|
|
if(eh)(*eh)("dbus_svc_init: dbus_bus_request_name failed: Name already registered");
|
|
goto give_up;
|
|
default:
|
|
if(eh)(*eh)("dbus_svc_init: dbus_bus_request_name failed: %s %s", error.name, error.message);
|
|
goto give_up;
|
|
}
|
|
return ( cs );
|
|
|
|
give_up:
|
|
dbus_connection_close( connection );
|
|
dbus_connection_unref( connection );
|
|
if( cs )
|
|
{
|
|
dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL);
|
|
dbus_connection_set_watch_functions (connection, NULL, NULL, NULL, NULL, NULL);
|
|
dbus_connection_set_timeout_functions (connection, NULL, NULL, NULL, NULL, NULL);
|
|
free(cs);
|
|
}
|
|
return ( 0L );
|
|
}
|
|
|
|
const char *dbus_svc_unique_name(DBusConnectionState *cs)
|
|
{
|
|
return cs->unique_name;
|
|
}
|
|
|
|
void
|
|
dbus_svc_shutdown ( DBusConnectionState *cs )
|
|
{
|
|
if (!dbus_connection_set_watch_functions
|
|
( cs->connection,
|
|
NULL, NULL, NULL, NULL, NULL
|
|
)
|
|
) if( cs->eh != 0L ) (*(cs->eh))("connection_shutdown: dbus_connection_set_watch_functions: No Memory."
|
|
"Setting watch functions to NULL failed."
|
|
);
|
|
|
|
if (!dbus_connection_set_timeout_functions
|
|
( cs->connection,
|
|
NULL, NULL, NULL, NULL, NULL
|
|
)
|
|
) if( cs->eh != 0L ) (*(cs->eh))("connection_shutdown: dbus_connection_set_timeout_functions: No Memory."
|
|
"Setting timeout functions to NULL failed."
|
|
);
|
|
|
|
dbus_connection_set_dispatch_status_function (cs->connection, NULL, NULL, NULL);
|
|
|
|
tdestroy( cs->timeouts, free);
|
|
cs->timeouts=0L;
|
|
tdestroy( cs->watches, no_free);
|
|
cs->watches=0L;
|
|
|
|
dbus_connection_close( cs->connection );
|
|
dbus_connection_unref( cs->connection );
|
|
|
|
free( cs );
|
|
}
|