2019/01/11

D-Bus 보안, 세션, 오브젝트, 서비스, 커널 이벤트, API

보안 (systemwide mode of message bus)
 D-Bus의 개념
  D-Bus는 몇가지 버스로 구성된다.
   system bus:
    부팅 시간에 시작함. 이 버스는 운영체제와 데몬들에의해 사용됨.
    임의의 응용프로그램들이 시스템 이벤트를 spoof하지 못하도록 보안이 되어있음.
   
session bus:
 사용자 로긴시에 동작함(private).
 사용자 응용프로그램에서의 session bus는 통신에 주로 사용된다.
 시스템 버스로 부터 메세지를 받을 수 있으며, 반대로 보내는 것에는 제약이 있다.

Objects, Messages, Services의 개념도 알아둘 필요가 있다.
 Objects
  - D-Bus는 주고 받는 모든 메세지의 소스와 목적지가 있는 peer-to-peer 프로토콜이다.
    메세지를 주고 받는데 쓰이는 주소들은 object path로 나타낸다.
    Objects의 집합을 가진 D-Bus를 사용하는 모든 응용프로그램들에서 메세지들은 어플리케이션이 아닌 특정한 objects로 전달되거나 그것으로 부터 전송받으며 object path로 구분한다.
    모든 object들은 하나 혹은 그 이상의 인터페이스를 제공하는데, 인터페이스는 메쏘드 이름의 namespace로 사용되며 단일 object는 여러 메쏘드를 가지고, 메쏘드는 여러 인터페이스를 가진다.

object ---- method 1  ---- interface 1 
                      ---- interface 2
       ---- method 2
       ---- method 3
     
Messages
 header와 body로 구성됨.
 
header:
 message의 meta data. routing information, 데이터(body)의 타입.(네가지 타입의 메세지가 존재함)

 method calls, method returns, signals, errors
  data: type code + data + type code + data ... (type: byte/32bit/64bit integer/doubles/strings)

Services
 - D-Bus에서 가장 상위 레벨로 현재 계속적으로 구현중에 있다.
   응용프로그램은 버스에 service를 등록할 수 있고, 등록에 성공하면 service를 획득하게 된다.
   다른 응용프로그램들은 특정한 service가 버스에 존재하는지 검사할 수 있고 그 service가 아직 시작전이면 시작할 수 있는지 버스에 문의할 수 있다. (org.freedesktop.DBus)
 
Native Objects 와 Object Paths
 java나 python, GObject와 같은 native object를 사용하지 않고,
 Low-level D-Bus 프로토콜과 libdbus API에서도 native object 대신 object path라는 native object 들을 higher-level binding한것을 제공한다. (/org/kde/kspread/sheets/3/cells/4/5)

Methods 와 Signals
 Object들은 두 멤버를 가지는데, method와 signal이다.
 Method는 Object안에서 발생하는 것이고, signal은 object로 부터 그 object를 주시하고 있는(관계된) 서비스? 들에게 broadcast 되는 것을 말한다.

Interfaces
 인터페이스는 method와 signal의 그룹으로 이름이 지어지는데,
 Glib, Qt, Java에서 사용된다. (org.freedesktop.DBus.Introspectable)

Proxies
 다른 프로세스에 있는 (remote) object들이 proxy를 통해 버스의 object에 접근할 수 있다.
 Proxy를 호출하게 되면 D-Bus는 method 호출, reply message를 기다리고, return value, return from native method.
 proxy를 사용하지 않는 경우는 다음과 같이 프로그램할 수 있다.

<pre>
Message message = new Message("/remote/object/path", "MethodName", arg1, arg2);
Connection connection = getBusConnection();
connection.send(message);
Message reply = connection.waitForReply(message);
if (reply.isError()) {

} else {
     Object returnValue = reply.getReturnValue();
}
</pre>

Proxy를 사용한 경우
<pre>
Proxy proxy = new Proxy(getBusConnection(), "/remote/object/path");
Object returnValue = proxy.MethodName(arg1, arg2);
</pre>

Bus Names
 각 응용프로그램들은 bus daemon으로 연결되며 daemon은 unique한 이름을 할당한다.
 : (콜론)으로 시작되는 bus 이름은 데몬이 살아있는 동안은 재사용되지 않는다.
 그것은 응용프로그램으로 부터 같은 이름으로 반복적으로 호출되기 때문이다.
 한 예를들면 :34-907 이라고 할당이 되는데, 콜론뒤의 번호는 특별한 의미를 지니지는 않는다.

Addresses
 D-Bus의 주소는 서버 측에서는 듣는것으로,
 클라이언트에서는 접속하는 용도로 쓰인다.
 Message bus daemon을 쓰는 경우에는 libdbus는 자동으로 환경변수를 읽어 bus daemon의 주소를 알아온다.

Big Conceptual Picture
<pre>
Address -> [Bus Name] -> Path -> Interface -> Method
Messages - Behind the Scenes
Calling a Method - Behind the Scenes
Emitting a Signal - Behind the Scenes
Introspection
</pre>

Kernel Event Layer
 kernel event layer는 고속의 netlink socket을 사용한 사용자 영역으로의 비동기 kernel-to-user 통신 메카니즘인데,
 이 메카니즘은 커널이 D-Bus signal을 보낼 수 있도록 D-Bus에 포함시킬 수 있다.

실행 예
<pre>
#include <dbus/dbus.h>
DBusError error;
DBusConnection *conn;
dbus_error_init (&error);
conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
if (!conn) {
    fprintf (stderr, "%s: %s\n",
             err.name, err.message);
    return 1;
}
dbus_bus_acquire_service (conn, "org.pirate.parrot", 0, &err);
if (dbus_error_is_set (&err)) {
    fprintf (stderr, "%s: %s\n",
             err.name, err.message);
    dbus_connection_disconnect (conn);
    return;
}
DBusMessage *msg;
DBusMessageIter iter;
/* create a new message of type signal */
msg = dbus_message_new_signal(
         "org/pirate/parrot/attr",
         "org.pirate.parrot.attr", "Feathers");
/* build the signal's payload up */
dbus_message_iter_init (msg, &iter);
dbus_message_iter_append_string (&iter, "Shiny");
dbus_message_iter_append_string (&iter,
                                 "Well Groomed");
/* send the message */
if (!dbus_connection_send (conn, msg, NULL))
        fprintf (stderr, "error sending message\n");
/* drop the reference count on the message */
dbus_message_unref (msg);
/* flush the connection buffer */
dbus_connection_flush (conn);
if (conn)
       dbus_connection_disconnect (conn);
</pre>

D-BUS API
개요
D-BUS 를 이용한 간단한 샘플 프로그램을 확인 후 실제로 사용하는 API 에 대해 알아본다.
D-BUS 라이브러리
http://dbus.freedesktop.org/doc/dbus/api/html/modules.html

API 정리
DBusServer
DBusWatchList watchs list : 변수를 read/write 콜백함수가 호출되도록 함 - dbus_watch_handle(watch, flags) 함수 호출시 콜백함수가 동작함.

DBusTimeoutList timeout : 주기적으로 동작할 함수를 등록함.

dbus_server_init_base () / dbus_server_listen() 함수 호출에 의해 생성되는 서버측 정보를 가짐

hal 모듈에서는 서버 구조체를 이용하여 작업

powersave 모듈은 서버 구조체를 이용하지 않도록 되어 있음

DBusConnection
#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
#define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local"
enum DBusBusType
DBUS_BUS_SESSION - The login session bus.
DBUS_BUS_SYSTEM - The systemwide bus.
DBUS_BUS_STARTER
- dbus_bus_get()

  - 시스템 디폴트 값을 이용 (환경 변수 이용)
  - 이미 알려진 메시지 버스를 자동으로 open 할때 사용함.

- connection_try_from_address_entry()

  - 직접 어드레스 정보를 이용하여 connection을 생성함

- dbus_bus_request_name (DBusConnection *connection, const char *name, unsigned int flags, DBusError      *error)

  - DBUS_SERVICE_DBUS 에서 RequestName method 를 호출하여 name 이 등록되어 있는지 확인하고 등록한다.

- dbus_bus_add_match(DBusConnection *connection, const char *rule, DBusError *error)

  - DBUS_SERVICE_DBUS 에서 AddMatch method 를 호출하여 rule 에 맞는 메시지만을 받을 수 있도록 한다.

- dbus_connection_send (DBusConnection *connection, DBusMessage  *message, dbus_uint32_t  *serial)

  - 메시지를 전송하고 블럭킹되지 않고 다음 작업을 진행한다.

- dbus_connection_send_with_reply (DBusConnection *connection, DBusMessage  *message, DBusPendingCall   **pending_return, int timeout_milliseconds)

  - 메시지를 전송후 reply 가 오거나 timeout이 발생할 경우의 동작할 pending을 등록한다. (논블록킹)

- dbus_connection_read_write (DBusConnection *connection, int timeout_milliseconds)

   - 논블로킹 모드로 메시지를 쓰거나 읽는다.

- dbus_connection_pop_message (DBusConnection *connection)

  - 메시지를 하나씩 얻어 온다.
DBusMessage
DBUS_MESSAGE_TYPE_INVALID 0
DBUS_MESSAGE_TYPE_METHOD_CALL 1
DBUS_MESSAGE_TYPE_METHOD_RETURN 2
DBUS_MESSAGE_TYPE_ERROR 3
DBUS_MESSAGE_TYPE_SIGNAL 4
- dbus_message_new_signal (const char *path, const char *interface, const char *name)

  - 시그널 메시지를 생성한다.

- dbus_message_new_method_call(const char *destination, const char *path, const char *interface, const char *method)

  - 메쏘드 메시지를 생성한다.

- dbus_message_new_method_return(DBusMessage *method_call)

  -  DBUS_MESSAGE_TYPE_METHOD_RETURN  타입의 메시지를 생성한다. 
DBusMessageIter
- dbus_message_iter_init_append (DBusMessage *message, DBusMessageIter *iter)

  - 메시지에 가변 변수가 등록 가능하도록 초기화한다

- dbus_message_iter_append_basic (DBusMessageIter *iter, int type, const void *value)

  - 메시지에 변수타입과 값을 추가한다.
DBusError
error.message 를 변수를 직접 접근하여 에러메시지를 얻을 수 있다.
- dbus_error_init (DBusError *error)
- dbus_error_is_set (const DBusError *error)
- dbus_error_free(DBusError *error)
DBusPendingCall
- dbus_pending_call_block(DBusPendingCall *pending)

  - 콜백함수가 완료 될때까지 블럭킹된다

- dbus_pending_call_steal_reply(DBusPendingCall *pending)

  -   Reply 값을 반환한다.
DBusAddressEntry
method : unix, tcp ...
key : path, tmpdir, abstract ...
value
Gnome Screen Saver 메시지 예
dbus-monitor 를 실행하여 dbus 메시지를 확인
signal sender=:1.11 -> dest=(null destination) path=/org/gnome/ScreenSaver; interface=org.gnome.ScreenSaver; member=SessionPowerManagementIdleChanged boolean false

signal sender=:1.11 -> dest=(null destination) path=/org/gnome/ScreenSaver; interface=org.gnome.ScreenSaver; member=SessionPowerManagementIdleChanged boolean false

댓글 없음:

댓글 쓰기