425 lines
12 KiB
C
425 lines
12 KiB
C
/*
|
|
* Copyright (C) 2007-2024 Kim Woelders
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies of the Software, its documentation and marketing & publicity
|
|
* materials, and acknowledgment shall be given in the documentation, materials
|
|
* and software packages that this Software was used.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include <dbus/dbus.h>
|
|
|
|
#include "E.h"
|
|
#include "edbus.h"
|
|
#include "events.h"
|
|
#include "ipc.h"
|
|
#include "xwin.h"
|
|
|
|
#define ENABLE_INTROSPECTION 1
|
|
|
|
#define DEBUG_DBUS 1
|
|
#if DEBUG_DBUS
|
|
#define Dprintf(fmt...) if(EDebug(EDBUG_TYPE_DBUS))Eprintf(fmt)
|
|
#define D2printf(fmt...) if(EDebug(EDBUG_TYPE_DBUS)>1)Eprintf(fmt)
|
|
#else
|
|
#define Dprintf(fmt...)
|
|
#define D2printf(fmt...)
|
|
#endif
|
|
|
|
typedef struct {
|
|
char *name;
|
|
DBusConnection *conn;
|
|
DBusWatch *watch;
|
|
int fd;
|
|
} DbusData;
|
|
|
|
static DbusData dbus_data;
|
|
|
|
static int db_efd = 0;
|
|
|
|
static dbus_bool_t
|
|
DbusWatchAdd(DBusWatch *watch, void *data __UNUSED__)
|
|
{
|
|
dbus_data.watch = watch;
|
|
dbus_data.fd = dbus_watch_get_unix_fd(watch);
|
|
|
|
D2printf("DbusWatchAdd fd=%d flags=%d\n", dbus_data.fd,
|
|
dbus_watch_get_flags(dbus_data.watch));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
DbusWatchRemove(DBusWatch *watch __UNUSED__, void *data __UNUSED__)
|
|
{
|
|
D2printf("DbusWatchRemove\n");
|
|
}
|
|
|
|
#if 0 /* Don't need this */
|
|
static void
|
|
DbusWatchToggle(DBusWatch *watch __UNUSED__, void *data __UNUSED__)
|
|
{
|
|
D2printf("DbusWatchToggle\n");
|
|
}
|
|
#else
|
|
#define DbusWatchToggle NULL
|
|
#endif
|
|
|
|
static void
|
|
DbusReplyString(DBusConnection *conn, DBusMessage *msg, const char *str)
|
|
{
|
|
DBusMessage *reply;
|
|
DBusMessageIter args;
|
|
|
|
reply = dbus_message_new_method_return(msg);
|
|
|
|
dbus_message_iter_init_append(reply, &args);
|
|
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &str))
|
|
goto done;
|
|
if (!dbus_connection_send(conn, reply, NULL))
|
|
goto done;
|
|
dbus_connection_flush(conn);
|
|
|
|
done:
|
|
dbus_message_unref(reply);
|
|
}
|
|
|
|
static void
|
|
DbusIpcReply(void *data, const char *str)
|
|
{
|
|
DBusMessage *msg = (DBusMessage *) data;
|
|
|
|
if (!str)
|
|
str = "ok";
|
|
DbusReplyString(dbus_data.conn, msg, str);
|
|
}
|
|
|
|
static void
|
|
DbusMethodCommand(DBusConnection *conn, DBusMessage *msg)
|
|
{
|
|
DBusMessageIter args;
|
|
const char *param = "";
|
|
|
|
if (!dbus_message_iter_init(msg, &args) ||
|
|
dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
|
|
{
|
|
DbusReplyString(conn, msg, "String arg required\n");
|
|
return;
|
|
}
|
|
else
|
|
dbus_message_iter_get_basic(&args, ¶m);
|
|
|
|
IpcExecReply(param, DbusIpcReply, msg);
|
|
}
|
|
|
|
#if ENABLE_INTROSPECTION
|
|
/* *INDENT-OFF* */
|
|
static const char dbus_introspect_data[] =
|
|
"<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'\n"
|
|
"'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n"
|
|
"<node>\n"
|
|
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
|
|
" <method name='Introspect'>\n"
|
|
" <arg name='data' direction='out' type='s'/>\n"
|
|
" </method>\n"
|
|
" </interface>\n"
|
|
" <interface name='%s'>\n"
|
|
" <method name='Command'>\n"
|
|
" <arg name='data' direction='out' type='s'/>\n"
|
|
" </method>\n"
|
|
" <signal name='Signal'>\n"
|
|
" <arg name='data' direction='in' type='s'/>\n"
|
|
" </signal>\n"
|
|
" </interface>\n"
|
|
"</node>";
|
|
/* *INDENT-ON* */
|
|
|
|
static void
|
|
DbusMsgIntrospect(DBusConnection *conn, DBusMessage *msg)
|
|
{
|
|
char buf[1024];
|
|
|
|
D2printf("Introspect\n");
|
|
|
|
Esnprintf(buf, sizeof(buf), dbus_introspect_data, dbus_data.name);
|
|
if (!strcmp(dbus_message_get_path(msg), "/"))
|
|
DbusReplyString(conn, msg, buf);
|
|
else
|
|
DbusReplyString(conn, msg, "");
|
|
}
|
|
#endif
|
|
|
|
static DBusHandlerResult
|
|
DbusMsgHandler(DBusConnection *conn, DBusMessage *msg,
|
|
void *user_data __UNUSED__)
|
|
{
|
|
int msg_type;
|
|
const char *msg_dest, *msg_ifc, *msg_memb;
|
|
|
|
D2printf("DbusMsgHandler\n");
|
|
|
|
msg_type = dbus_message_get_type(msg);
|
|
msg_dest = dbus_message_get_destination(msg);
|
|
msg_ifc = dbus_message_get_interface(msg);
|
|
msg_memb = dbus_message_get_member(msg);
|
|
|
|
switch (msg_type)
|
|
{
|
|
default: /* Should not be possible */
|
|
Dprintf("MESSAGE type=%d\n", msg_type);
|
|
break;
|
|
|
|
case DBUS_MESSAGE_TYPE_METHOD_CALL:
|
|
Dprintf("METHOD %s %s\n", msg_ifc, msg_memb);
|
|
if (strcmp(msg_dest, dbus_data.name))
|
|
break;
|
|
if (!strcmp(msg_ifc, "org.e16"))
|
|
{
|
|
if (!strcmp(msg_memb, "Command"))
|
|
DbusMethodCommand(conn, msg);
|
|
else
|
|
DbusReplyString(conn, msg, "Error");
|
|
}
|
|
#if ENABLE_INTROSPECTION
|
|
else if (!strcmp(msg_ifc, "org.freedesktop.DBus.Introspectable"))
|
|
{
|
|
if (!strcmp(msg_memb, "Introspect"))
|
|
DbusMsgIntrospect(conn, msg);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case DBUS_MESSAGE_TYPE_SIGNAL:
|
|
Dprintf("SIGNAL %s %s\n", msg_ifc, msg_memb);
|
|
if (!strcmp(msg_ifc, "org.e16"))
|
|
{
|
|
Dprintf("... for me!\n");
|
|
}
|
|
break;
|
|
}
|
|
|
|
D2printf("sender = %s\n", dbus_message_get_sender(msg));
|
|
D2printf("dest = %s\n", dbus_message_get_destination(msg));
|
|
D2printf("path = %s\n", dbus_message_get_path(msg));
|
|
D2printf("interface = %s\n", dbus_message_get_interface(msg));
|
|
D2printf("member = %s\n", dbus_message_get_member(msg));
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
}
|
|
|
|
static void
|
|
DbusHandleFd(void)
|
|
{
|
|
int rc;
|
|
|
|
D2printf("DbusHandleFd flags=%d\n", dbus_watch_get_flags(dbus_data.watch));
|
|
dbus_watch_handle(dbus_data.watch,
|
|
DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE);
|
|
|
|
for (;;)
|
|
{
|
|
D2printf("DbusHandleFd: Dispatch flags=%d\n",
|
|
dbus_watch_get_flags(dbus_data.watch));
|
|
rc = dbus_connection_dispatch(dbus_data.conn);
|
|
if (rc == DBUS_DISPATCH_COMPLETE)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
DbusInit(void)
|
|
{
|
|
DBusError dberr;
|
|
int err;
|
|
char buf[128];
|
|
|
|
if (Mode.wm.window)
|
|
{
|
|
sprintf(buf, "org.e16.wm.p%u", (unsigned int)Mode.wm.pid);
|
|
}
|
|
else
|
|
{
|
|
const char *s;
|
|
|
|
s = strchr(Dpy.name, ':');
|
|
if (!s)
|
|
return;
|
|
sprintf(buf, "org.e16.wm.d%ds%d", atoi(s + 1), Dpy.screen);
|
|
}
|
|
dbus_data.name = Estrdup(buf);
|
|
Esetenv("ENL_DBUS_NAME", dbus_data.name);
|
|
|
|
dbus_error_init(&dberr);
|
|
|
|
dbus_data.fd = -1;
|
|
|
|
dbus_data.conn = dbus_bus_get(DBUS_BUS_SESSION, &dberr);
|
|
if (dbus_error_is_set(&dberr))
|
|
goto bail_out;
|
|
dbus_connection_set_exit_on_disconnect(dbus_data.conn, FALSE);
|
|
|
|
err = dbus_bus_request_name(dbus_data.conn, dbus_data.name,
|
|
DBUS_NAME_FLAG_DO_NOT_QUEUE, &dberr);
|
|
if (dbus_error_is_set(&dberr))
|
|
goto bail_out;
|
|
if (err != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
|
|
{
|
|
Eprintf("*** DbusInit error: Not Primary Owner (%d)\n", err);
|
|
return;
|
|
}
|
|
|
|
Esnprintf(buf, sizeof(buf), "type='signal',destination='%s'",
|
|
dbus_data.name);
|
|
if (EDebug(EDBUG_TYPE_DBUS) > 1) /* Catch all signals if extra debug is enabled */
|
|
dbus_bus_add_match(dbus_data.conn, "type='signal'", &dberr);
|
|
else
|
|
dbus_bus_add_match(dbus_data.conn, buf, &dberr);
|
|
if (dbus_error_is_set(&dberr))
|
|
goto bail_out;
|
|
Esnprintf(buf, sizeof(buf), "type='method_call',destination='%s'",
|
|
dbus_data.name);
|
|
dbus_bus_add_match(dbus_data.conn, buf, &dberr);
|
|
if (dbus_error_is_set(&dberr))
|
|
goto bail_out;
|
|
#if 0 /* Debug */
|
|
dbus_bus_add_match(dbus_data.conn, "type='method_return'", &dberr);
|
|
if (dbus_error_is_set(&dberr))
|
|
goto bail_out;
|
|
dbus_bus_add_match(dbus_data.conn, "type='error'", &dberr);
|
|
if (dbus_error_is_set(&dberr))
|
|
goto bail_out;
|
|
#endif
|
|
|
|
if (!dbus_connection_add_filter(dbus_data.conn, DbusMsgHandler, NULL, NULL))
|
|
return;
|
|
|
|
dbus_connection_set_watch_functions(dbus_data.conn,
|
|
DbusWatchAdd, DbusWatchRemove,
|
|
DbusWatchToggle, NULL, NULL);
|
|
|
|
/* Handle pending D-Bus stuff */
|
|
DbusHandleFd();
|
|
db_efd = EventFdRegister(dbus_data.fd, DbusHandleFd);
|
|
return;
|
|
|
|
bail_out:
|
|
if (dbus_error_is_set(&dberr))
|
|
{
|
|
Eprintf("*** DbusInit error: %s\n", dberr.message);
|
|
dbus_error_free(&dberr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
#if 0 /* No need? */
|
|
void
|
|
DbusExit(void)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
_DbusSendCommand(DBusBusType bus, const char *dest, const char *path,
|
|
const char *meth, const char *args)
|
|
{
|
|
int err;
|
|
DBusError dberr;
|
|
DBusConnection *conn;
|
|
DBusMessage *msg, *rpl;
|
|
DBusMessageIter iter;
|
|
const char *iface = dest;
|
|
|
|
err = -1;
|
|
msg = rpl = NULL;
|
|
dbus_error_init(&dberr);
|
|
|
|
conn = dbus_bus_get(bus, &dberr);
|
|
if (dbus_error_is_set(&dberr))
|
|
goto quit;
|
|
|
|
msg = dbus_message_new_method_call(dest, path, iface, meth);
|
|
|
|
if (args)
|
|
{
|
|
const char *s = args;
|
|
int nr, type, value;
|
|
dbus_message_iter_init_append(msg, &iter);
|
|
for (;;)
|
|
{
|
|
type = *s++;
|
|
if (type == '\0')
|
|
break;
|
|
if (type == ';')
|
|
continue;
|
|
if (*s++ != '=')
|
|
break;
|
|
nr = -1;
|
|
if (type == 'u')
|
|
{
|
|
sscanf(s, "%u%n", &value, &nr);
|
|
if (nr <= 0)
|
|
goto quit;
|
|
s += nr;
|
|
if (!dbus_message_iter_append_basic(&iter,
|
|
DBUS_TYPE_UINT32, &value))
|
|
goto quit;
|
|
}
|
|
else
|
|
{
|
|
goto quit; /* Unknown type */
|
|
}
|
|
}
|
|
}
|
|
|
|
rpl = dbus_connection_send_with_reply_and_block(conn, msg, 100, &dberr);
|
|
if (!rpl)
|
|
goto quit;
|
|
|
|
dbus_connection_flush(conn);
|
|
|
|
err = 0;
|
|
|
|
quit:
|
|
if (dbus_error_is_set(&dberr))
|
|
{
|
|
Eprintf("*** Dbus error: %s\n", dberr.message);
|
|
dbus_error_free(&dberr);
|
|
}
|
|
|
|
if (msg)
|
|
dbus_message_unref(msg);
|
|
if (rpl)
|
|
dbus_message_unref(rpl);
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
DbusRequestLogout(void)
|
|
{
|
|
return _DbusSendCommand(DBUS_BUS_SESSION, "org.gnome.SessionManager",
|
|
"/org/gnome/SessionManager", "Logout", "u=0");
|
|
}
|
|
|
|
int
|
|
DbusRequestShutdown(void)
|
|
{
|
|
return _DbusSendCommand(DBUS_BUS_SESSION, "org.gnome.SessionManager",
|
|
"/org/gnome/SessionManager", "Shutdown", NULL);
|
|
}
|