Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX D-BUS 2026

D-Bus
Süreçlerarası İletişim

systemd, BlueZ ve NetworkManager'ın ortak dili — object model, sd-bus C API, Python bağlaması ve güvenlik politikaları.

00 D-Bus nedir

D-Bus, Linux'ta süreçler arası iletişim için standart haline gelmiş mesaj yönlendirme protokolüdür; gömülü sistemlerde kaçınılmaz çünkü systemd, BlueZ ve NetworkManager'ın tamamı D-Bus üzerinden konuşur.

D-Bus (Desktop Bus), 2002 yılında freedesktop.org bünyesinde Red Hat ve GNOME geliştiricileri tarafından standartlaştırıldı. Başlangıçta masaüstü uygulamaları arasında servis keşfi ve olay bildirimi sağlamak için tasarlandı; ancak zamanla Linux sisteminin omurgasına dönüştü. Bugün systemd, servis yönetimini tamamen D-Bus üzerinden yürütür.

Gömülü Linux'ta D-Bus'tan kaçınmak neredeyse imkânsızdır. BlueZ Bluetooth yığını, NetworkManager ağ konfigürasyonu, udisks2 depolama yönetimi ve PulseAudio ses altyapısı — hepsi D-Bus arayüzü açar. Bu daemon'larla etkileşime girmek için D-Bus protokolünü anlamak zorunludur.

IPC mekanizmaları karşılaştırması

MekanizmaKapsamServis keşfiAsync eventOverhead
Unix pipeParent-childYokYokÇok düşük
Unix socketTek makineManuel (path)epoll/selectDüşük
Shared memoryTek makineYokYok (poll gerekli)Çok düşük
D-BusTek makineOtomatik (bus adı)Signal mekanizmasıOrta (daemon)
gRPCManuel (host:port)Server streamingDüşük (binary)
MQTTTopic tabanlıSubscribeDüşük

D-Bus daemon mimarisi

D-Bus, hub-and-spoke modelinde çalışır. Merkezdeki dbus-daemon süreci tüm mesajları yönlendirir. Hiçbir süreç doğrudan başka bir sürece bağlanmaz; her mesaj önce daemon'a gider, daemon hedef süreci bulur ve iletir.

  Süreç A                  dbus-daemon                  Süreç B
     │                          │                           │
     │── connect ──────────────▶│                           │
     │◀─ unique name (:1.47) ───│                           │
     │                          │◀── connect ───────────────│
     │                          │─── unique name (:1.91) ──▶│
     │                          │                           │
     │── method_call ──────────▶│                           │
     │   dest: org.example.Svc  │── route ─────────────────▶│
     │                          │                           │── dispatch
     │                          │◀── method_return ─────────│
     │◀── method_return ────────│                           │

Session bus ve system bus

System bus tüm kullanıcıların paylaştığı veriyoludur. Root olarak çalışan daemon'lar burada bulunur. Soket: /run/dbus/system_bus_socket. Gömülü sistemlerde birincil bus budur.

Session bus her kullanıcı oturumuna özeldir. Masaüstü uygulamaları bu bus'ı kullanır. $DBUS_SESSION_BUS_ADDRESS ortam değişkeniyle adreslenir. Gömülü headless sistemlerde genellikle yoktur.

bash
# Kurulum
apt install dbus libdbus-1-dev

# D-Bus daemon durumu
systemctl status dbus

# System bus socket
ls -la /run/dbus/system_bus_socket
# srw-rw-rw- 1 root messagebus 0 ... /run/dbus/system_bus_socket

# Session bus adresi (desktop ortamında)
echo $DBUS_SESSION_BUS_ADDRESS
# unix:path=/run/user/1000/bus

Embedded Linux'ta hafif alternatifler

dbus-brokerSadece routing mantığı, policy yönetimi ayrı süreçte. Daha az bellek, daha iyi performans. Modern dağıtımlarda dbus-daemon'ın yerini alıyor.
kdbusKernel-space mesaj yönlendirme; context switch maliyetini ortadan kaldırır. Şu an mainline Linux'ta yok; D-Bus protocol düzeyinde uyumlu.
sd-buslibsystemd içindeki D-Bus implementasyonu. Doğrudan socket'e yazabilir, daemon bypass mümkün (kdbus/AF_UNIX). En sade ve hızlı C API.
NOT

Modern systemd tabanlı sistemlerde dbus.service, dbus-daemon yerine dbus-broker çalıştırabilir. API tamamen uyumludur; socket yolu değişmez. systemctl status dbus.service ile hangi implementasyonun çalıştığını görebilirsiniz.

Bu bölümde

  • D-Bus'ın kökeni ve gömülü Linux'taki rolü
  • Pipe, socket, shared memory ve D-Bus karşılaştırması
  • Hub-and-spoke daemon mimarisi: mesaj yönlendirme akışı
  • System bus ve session bus ayrımı
  • dbus-broker ve sd-bus: hafif embedded alternatifleri

01 Temel kavramlar

D-Bus'ın dört temel kavramı — bus name, object path, interface ve member — birlikte bir servis API'sini benzersiz şekilde adresler.

Bus name

Bus name, bir sürecin D-Bus üzerindeki kimliğidir. İki türü vardır:

Well-known nameİnsan tarafından okunabilir, kalıcı isim. Örn: org.freedesktop.NetworkManager. Servis başladığında talep eder; kapatıldığında serbest kalır.
Unique nameDaemon tarafından bağlantı anında atanan geçici isim. Format: :1.42. Her bağlantı için tekil, değiştirilemez.

Object path

Bir servis içindeki nesne hiyerarşisini belirtir. Unix dosya yoluna benzer sözdizimi kullanır:

object paths
# systemd — root manager nesnesi
/org/freedesktop/systemd1

# Belirli bir unit nesnesi
/org/freedesktop/systemd1/unit/ssh_2eservice

# NetworkManager — aktif bağlantı
/org/freedesktop/NetworkManager/ActiveConnection/1

# BlueZ — Bluetooth adapter
/org/bluez/hci0

# BlueZ — bağlı cihaz
/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF

Interface

Bir nesnenin sunduğu API grubuna verilen isim. Bir nesne birden fazla interface implemente edebilir. Her D-Bus nesnesinin implemente etmesi beklenen standart interface'ler vardır:

org.freedesktop.DBus.IntrospectableIntrospect() metodu — nesnenin XML tanımını döner.
org.freedesktop.DBus.PropertiesGet(), Set(), GetAll() — property erişimi.
org.freedesktop.DBus.ObjectManagerGetManagedObjects() — alt nesne hiyerarşisini döner (BlueZ kullanır).

Method, Signal, Property

TürYönYanıtKullanım
MethodClient → Service → ClientVar (method_return veya error)İstek-yanıt, RPC çağrısı
SignalService → (tüm subscriber'lar)YokAsenkron olay bildirimi, broadcast
PropertyGet/Set via DBus.PropertiesVarServis durum değişkenleri

Tür sistemi (type signatures)

D-Bus mesajları self-describing'dir: her parametre bir type signature ile tanımlanır. Bu signature introspection XML'inde ve API dokümantasyonunda görünür.

Type codeC karşılığıAçıklama
yuint8_tByte
bint (0/1)Boolean
iint32_t32-bit signed integer
uuint32_t32-bit unsigned integer
tuint64_t64-bit unsigned integer
ddoubleIEEE 754 double
schar*UTF-8 string (null-terminated)
ochar*Object path
vvariantDinamik tür — içinde başka bir tür
a{sv}GHashTable*String-variant sözlük (en yaygın dict)
aschar**String dizisi
(iu)structint32 + uint32 tuple

Bu bölümde

  • Bus name: well-known (org.freedesktop.NetworkManager) ve unique (:1.42) farkı
  • Object path: nesne hiyerarşisini adresleyen Unix-path benzeri sözdizimi
  • Interface: metod ve sinyal koleksiyonu; standart interface'ler
  • Method, signal ve property: üç etkileşim türü ve farkları
  • D-Bus type signatures: s, i, v, a{sv} gibi temel kodlar

02 Introspection ve gdbus

gdbus ve busctl araçlarıyla D-Bus servislerini canlı keşfedebilir, introspection XML'ini okuyabilir ve komut satırından method çağrısı yapabilirsiniz.

busctl ile servis listesi

bash
# System bus üzerindeki tüm servisler
busctl list

# Çıktı (kısaltılmış):
# NAME                              PID PROCESS         USER
# :1.1                                1 systemd         root
# :1.2                              423 NetworkManager  root
# org.freedesktop.NetworkManager   423 NetworkManager  root
# org.freedesktop.systemd1           1 systemd         root
# org.bluez                        512 bluetoothd      root

# Servis ağaç yapısı
busctl tree org.freedesktop.systemd1

# Belirli nesnenin introspection
busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1

gdbus ile introspection

bash
# NetworkManager root nesnesini incele
gdbus introspect --system \
  --dest org.freedesktop.NetworkManager \
  --object-path /

# Çıktı (XML fragment):
# <node>
#   <interface name="org.freedesktop.NetworkManager">
#     <method name="GetDevices">
#       <arg type="ao" name="devices" direction="out"/>
#     </method>
#     <signal name="StateChanged">
#       <arg type="u" name="state"/>
#     </signal>
#     <property name="State" type="u" access="read"/>
#   </interface>
# </node>

# Tüm nesneleri recursive listele
gdbus introspect --system \
  --dest org.freedesktop.NetworkManager \
  --object-path / \
  --recurse

gdbus call ile method çağırma

bash
# NetworkManager sürümünü sorgula
gdbus call --system \
  --dest org.freedesktop.NetworkManager \
  --object-path /org/freedesktop/NetworkManager \
  --method org.freedesktop.DBus.Properties.Get \
  "org.freedesktop.NetworkManager" "Version"
# (<'1.44.2'>,)

# systemd ListUnits çağrısı
gdbus call --system \
  --dest org.freedesktop.systemd1 \
  --object-path /org/freedesktop/systemd1 \
  --method org.freedesktop.systemd1.Manager.ListUnits

gdbus monitor ile signal izleme

bash
# NetworkManager'dan tüm sinyalleri izle
gdbus monitor --system \
  --dest org.freedesktop.NetworkManager

# Çıktı:
# /org/freedesktop/NetworkManager: org.freedesktop.NetworkManager.StateChanged (uint32 70,)
# /org/freedesktop/NetworkManager/ActiveConnection/1: \
#   org.freedesktop.DBus.Properties.PropertiesChanged ...

# Tüm system bus sinyalleri (çok verbose)
gdbus monitor --system

busctl call ve monitor

bash
# busctl call: tür signature sonra argümanlar
busctl call org.freedesktop.hostname1 \
  /org/freedesktop/hostname1 \
  org.freedesktop.hostname1 \
  GetHostname

# Property okuma
busctl get-property org.freedesktop.hostname1 \
  /org/freedesktop/hostname1 \
  org.freedesktop.hostname1 \
  Hostname

# Gerçek zamanlı signal izleme
busctl monitor org.freedesktop.NetworkManager

# JSON formatında çıktı (scripting için kullanışlı)
busctl --json=short get-property \
  org.freedesktop.systemd1 \
  /org/freedesktop/systemd1 \
  org.freedesktop.systemd1.Manager \
  Version
NOT

dbus-send eski araçtır; bugün busctl tercih edilmelidir. busctl systemd ile birlikte gelir, JSON çıktı desteği sunar ve type signature sözdizimi daha okunaklıdır. dbus-send hâlâ bazı eski betiklerde görülebilir.

Bu bölümde

  • busctl list, busctl tree, busctl introspect — servis keşfi
  • gdbus introspect — XML introspection okuma
  • gdbus call — komut satırından method çağrısı
  • gdbus monitor / busctl monitor — gerçek zamanlı signal izleme
  • busctl JSON çıktısı — scripting entegrasyonu

03 Python GDBus bağlaması

PyGObject kütüphanesi aracılığıyla GDBus Python API'si, D-Bus servislerine proxy nesneleri üzerinden tip-güvenli erişim sunar.

Bağlantı kurma

dbus_connect.py
from gi.repository import Gio, GLib

# System bus bağlantısı (senkron)
bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
print(f"Bağlantı UUID: {bus.get_guid()}")

# Session bus bağlantısı
session_bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)

Proxy oluşturma ve method çağırma

nm_version.py
from gi.repository import Gio, GLib

bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)

# NetworkManager proxy'si oluştur
proxy = Gio.DBusProxy.new_sync(
    bus,
    Gio.DBusProxyFlags.NONE,
    None,                                          # interface info
    "org.freedesktop.NetworkManager",              # bus name
    "/org/freedesktop/NetworkManager",             # object path
    "org.freedesktop.NetworkManager",              # interface
    None
)

# Property okuma (DBus.Properties.Get)
version = proxy.get_cached_property("Version")
print(f"NetworkManager sürümü: {version.get_string()}")

# GetDevices method çağrısı
devices = proxy.call_sync(
    "GetDevices",
    None,
    Gio.DBusCallFlags.NONE,
    -1,
    None
)
# devices: GLib.Variant("(ao)", ...) — object path dizisi
device_paths = devices.unpack()[0]
for path in device_paths:
    print(f"  Aygıt: {path}")

Signal bağlama

nm_signal.py
from gi.repository import Gio, GLib

def on_state_changed(proxy, sender, signal_name, params):
    if signal_name == "StateChanged":
        state = params.unpack()[0]
        states = {
            20: "asleep", 30: "disconnected",
            40: "disconnecting", 50: "connecting",
            60: "connected_local", 70: "connected_site",
            80: "connected_global"
        }
        print(f"NM durum: {states.get(state, state)}")

bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)

proxy = Gio.DBusProxy.new_sync(
    bus, Gio.DBusProxyFlags.NONE, None,
    "org.freedesktop.NetworkManager",
    "/org/freedesktop/NetworkManager",
    "org.freedesktop.NetworkManager",
    None
)

# g-signal: tüm sinyaller bu callback'e düşer
proxy.connect("g-signal", on_state_changed)

loop = GLib.MainLoop()
loop.run()  # Ctrl+C ile durdur

Aktif bağlantı bilgisi alma

active_connection.py
from gi.repository import Gio, GLib

def get_active_connections():
    bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)

    nm = Gio.DBusProxy.new_sync(
        bus, Gio.DBusProxyFlags.NONE, None,
        "org.freedesktop.NetworkManager",
        "/org/freedesktop/NetworkManager",
        "org.freedesktop.NetworkManager",
        None
    )

    # ActiveConnections property: object path dizisi
    active = nm.get_cached_property("ActiveConnections")
    if not active:
        print("Aktif bağlantı yok")
        return

    for path in active.unpack():
        conn_proxy = Gio.DBusProxy.new_sync(
            bus, Gio.DBusProxyFlags.NONE, None,
            "org.freedesktop.NetworkManager",
            path,
            "org.freedesktop.NetworkManager.Connection.Active",
            None
        )
        iface  = conn_proxy.get_cached_property("Id")
        state  = conn_proxy.get_cached_property("State")
        iptype = conn_proxy.get_cached_property("Type")
        print(f"  [{iptype.get_string()}] {iface.get_string()} — durum: {state.get_uint32()}")

get_active_connections()
NOT

Python'da D-Bus için iki ana kütüphane vardır: dbus-python (eski, GLib event loop gerektirir) ve PyGObject üzerinden gi.repository.Gio (modern, GDBus kullanır). Yeni projelerde gi.repository.Gio tercih edin. Kurulum: apt install python3-gi.

Bu bölümde

  • Gio.bus_get_sync() ile system/session bus bağlantısı
  • Gio.DBusProxy.new_sync() ile proxy oluşturma
  • proxy.call_sync() ile senkron method çağrısı
  • proxy.connect('g-signal', callback) ile signal bağlama
  • NetworkManager aktif bağlantı bilgisi alma — gerçek kullanım örneği

04 C ile dbus-1 API

libdbus (dbus-1) low-level C API'si, D-Bus protokolüne doğrudan erişim sağlar; verbose olmakla birlikte daemon bağımlılığı olmadan kullanılabilir.

Bağlantı ve method çağrısı

dbus1_call.c
#include <stdio.h>
#include <stdlib.h>
#include <dbus/dbus.h>

int main(void)
{
    DBusError   err;
    DBusConnection *conn;
    DBusMessage    *msg, *reply;
    char           *version = NULL;

    dbus_error_init(&err);

    /* System bus'a bağlan */
    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "Bağlantı hatası: %s\n", err.message);
        dbus_error_free(&err);
        return 1;
    }

    /* Method call mesajı oluştur */
    msg = dbus_message_new_method_call(
        "org.freedesktop.NetworkManager",    /* destination */
        "/org/freedesktop/NetworkManager",    /* object path */
        "org.freedesktop.DBus.Properties",   /* interface */
        "Get"                                 /* method */
    );

    /* Argümanları ekle: interface adı + property adı */
    const char *iface = "org.freedesktop.NetworkManager";
    const char *prop  = "Version";
    dbus_message_append_args(
        msg,
        DBUS_TYPE_STRING, &iface,
        DBUS_TYPE_STRING, &prop,
        DBUS_TYPE_INVALID
    );

    /* Senkron çağrı (5 saniye timeout) */
    reply = dbus_connection_send_with_reply_and_block(
        conn, msg, 5000, &err
    );
    if (!reply) {
        fprintf(stderr, "Yanıt hatası: %s\n", err.message);
        dbus_error_free(&err);
        goto cleanup;
    }

    /* Yanıttan variant içindeki string'i oku */
    DBusMessageIter iter, sub;
    dbus_message_iter_init(reply, &iter);
    if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) {
        dbus_message_iter_recurse(&iter, &sub);
        if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
            dbus_message_iter_get_basic(&sub, &version);
            printf("NM Sürümü: %s\n", version);
        }
    }

cleanup:
    if (reply) dbus_message_unref(reply);
    dbus_message_unref(msg);
    /* dbus_connection_unref(conn) — paylaşılan bağlantı, unref etme */
    return 0;
}

Derleme

bash
gcc dbus1_call.c -o dbus1_call \
    $(pkg-config --cflags --libs dbus-1)

# Cross-compile (ARM)
arm-linux-gnueabihf-gcc dbus1_call.c -o dbus1_call \
    $(pkg-config --cflags --libs dbus-1)

Argüman tipleri ve DBusMessageIter

DBUS_TYPE_STRINGC: const char*. dbus_message_iter_get_basic() ile const char* alınır.
DBUS_TYPE_INT32C: dbus_int32_t. Signed 32-bit integer.
DBUS_TYPE_UINT32C: dbus_uint32_t. Unsigned 32-bit integer.
DBUS_TYPE_BOOLEANC: dbus_bool_t. 0 veya 1.
DBUS_TYPE_VARIANTİçine dbus_message_iter_recurse() ile girilir.
DBUS_TYPE_ARRAYİçine dbus_message_iter_recurse() + dbus_message_iter_next() döngüsü ile girilir.
UYARI

dbus_bus_get() paylaşılan bir bağlantı döndürür. Bu bağlantıyı dbus_connection_unref() ile kapatmayın — referans sayısı sıfıra düştüğünde bağlantı kapatılır ve aynı süreçteki diğer D-Bus kullanıcıları etkilenir. Özel bağlantı için dbus_bus_get_private() kullanın.

Bu bölümde

  • dbus_bus_get(DBUS_BUS_SYSTEM) ile bağlantı kurma
  • dbus_message_new_method_call() ve argüman ekleme
  • dbus_connection_send_with_reply_and_block() — senkron çağrı
  • DBusMessageIter ile yanıt ayrıştırma
  • Bellek yönetimi: dbus_message_unref()

05 sd-bus (libsystemd)

sd-bus, libsystemd içinde gelen modern D-Bus implementasyonudur; dbus-1'e göre çok daha sade API sunar ve gömülü sistemlerde tercih edilen çözümdür.

sd-bus vs dbus-1 karşılaştırması

Özellikdbus-1 (libdbus)sd-bus (libsystemd)
API karmaşıklığıYüksek (verbose iterator)Düşük (tek satır call)
PerformansOrtaDaha iyi (direkt socket)
Bağımlılıklibdbus-1libsystemd (zaten var)
Async event loopManuelsd-event entegrasyonu
Vtable APIYokVar (servis yazımı kolay)
Kdbus/AF_UNIXHayırEvet (otomatik seçim)

sd-bus ile method çağrısı

sdbus_call.c
#include <stdio.h>
#include <systemd/sd-bus.h>

int main(void)
{
    sd_bus *bus  = NULL;
    sd_bus_error error = SD_BUS_ERROR_NULL;
    sd_bus_message *reply = NULL;
    const char *version;
    int rc;

    /* System bus'a bağlan */
    rc = sd_bus_open_system(&bus);
    if (rc < 0) {
        fprintf(stderr, "Bağlantı hatası: %s\n", strerror(-rc));
        return 1;
    }

    /* Property oku — tek satırda! */
    rc = sd_bus_get_property(
        bus,
        "org.freedesktop.NetworkManager",    /* destination  */
        "/org/freedesktop/NetworkManager",    /* object path  */
        "org.freedesktop.NetworkManager",    /* interface    */
        "Version",                            /* property     */
        &error,
        &reply,
        "s"                                   /* type sig     */
    );
    if (rc < 0) {
        fprintf(stderr, "Property hatası: %s\n", error.message);
        goto out;
    }

    sd_bus_message_read(reply, "s", &version);
    printf("NM sürümü: %s\n", version);

out:
    sd_bus_error_free(&error);
    sd_bus_message_unref(reply);
    sd_bus_unref(bus);
    return rc < 0 ? 1 : 0;
}

sd_bus_call_method ile method çağrısı

sdbus_method.c
/* systemd servisini yeniden başlat */
int restart_service(sd_bus *bus, const char *unit)
{
    sd_bus_error error  = SD_BUS_ERROR_NULL;
    sd_bus_message *reply = NULL;
    int rc;

    rc = sd_bus_call_method(
        bus,
        "org.freedesktop.systemd1",
        "/org/freedesktop/systemd1",
        "org.freedesktop.systemd1.Manager",
        "RestartUnit",
        &error,
        &reply,
        "ss",          /* iki string argüman */
        unit,           /* "ssh.service" gibi */
        "replace"      /* mode */
    );

    if (rc < 0)
        fprintf(stderr, "Hata: %s — %s\n",
                error.name, error.message);
    else {
        const char *job_path;
        sd_bus_message_read(reply, "o", &job_path);
        printf("Job: %s\n", job_path);
    }

    sd_bus_error_free(&error);
    sd_bus_message_unref(reply);
    return rc;
}

Signal subscribe

sdbus_signal.c
/* Signal callback */
static int on_unit_new(sd_bus_message *m, void *userdata,
                       sd_bus_error *ret_err)
{
    const char *id, *path;
    sd_bus_message_read(m, "so", &id, &path);
    printf("Yeni unit: %s — %s\n", id, path);
    return 0;
}

/* Subscribe */
sd_bus_slot *slot = NULL;
rc = sd_bus_match_signal(
    bus,
    &slot,
    "org.freedesktop.systemd1",           /* sender */
    "/org/freedesktop/systemd1",           /* path */
    "org.freedesktop.systemd1.Manager",   /* interface */
    "UnitNew",                             /* member */
    on_unit_new,
    NULL
);

/* Event loop */
while (1) {
    rc = sd_bus_process(bus, NULL);
    if (rc < 0) break;
    if (rc > 0) continue; /* daha işlenecek mesaj var */
    sd_bus_wait(bus, UINT64_MAX); /* yeni mesaj bekle */
}
Makefile
CFLAGS  := $(shell pkg-config --cflags libsystemd)
LDFLAGS := $(shell pkg-config --libs   libsystemd)

sdbus_call: sdbus_call.c
	$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)

Bu bölümde

  • sd_bus_open_system() / sd_bus_open_user() ile bağlantı
  • sd_bus_call_method() — tek satır method call, type-safe
  • sd_bus_get_property() — property okuma
  • sd_bus_match_signal() — signal subscribe
  • sd_bus_process() + sd_bus_wait() — basit event loop

06 Servis yazmak: method ve signal

sd-bus vtable API'si ile D-Bus üzerinden method, property ve signal sunan tam bir C servisi oluşturabilirsiniz.

Vtable tanımı

temp_sensor_service.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <systemd/sd-bus.h>

#define SERVICE  "com.example.TempSensor"
#define PATH     "/com/example/TempSensor"
#define IFACE    "com.example.TempSensor"

static int32_t g_temperature = 230; /* °C × 10 */
static sd_bus *g_bus = NULL;

/* GetTemperature method handler */
static int method_get_temperature(sd_bus_message *m, void *ud,
                                   sd_bus_error *err)
{
    printf("GetTemperature çağrıldı\n");
    return sd_bus_reply_method_return(m, "i", g_temperature);
}

/* SetTemperature method handler */
static int method_set_temperature(sd_bus_message *m, void *ud,
                                   sd_bus_error *err)
{
    int32_t new_temp;
    int rc = sd_bus_message_read(m, "i", &new_temp);
    if (rc < 0)
        return rc;

    if (new_temp < -400 || new_temp > 1500) {
        return sd_bus_error_setf(err,
            SD_BUS_ERROR_INVALID_ARGS,
            "Sıcaklık aralık dışı: %d", new_temp);
    }

    g_temperature = new_temp;

    /* TemperatureChanged sinyali yayınla */
    sd_bus_emit_signal(g_bus, PATH, IFACE,
                       "TemperatureChanged", "i", g_temperature);

    return sd_bus_reply_method_return(m, "");
}

/* Property getter */
static int prop_get_temperature(sd_bus *bus, const char *path,
    const char *interface, const char *property,
    sd_bus_message *reply, void *ud, sd_bus_error *err)
{
    return sd_bus_message_append(reply, "i", g_temperature);
}

/* Vtable — servisin tüm API tanımı */
static const sd_bus_vtable sensor_vtable[] = {
    SD_BUS_VTABLE_START(0),

    SD_BUS_METHOD("GetTemperature", "", "i",
                  method_get_temperature, SD_BUS_VTABLE_UNPRIVILEGED),

    SD_BUS_METHOD("SetTemperature", "i", "",
                  method_set_temperature, SD_BUS_VTABLE_UNPRIVILEGED),

    SD_BUS_PROPERTY("Temperature", "i", prop_get_temperature,
                    0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),

    SD_BUS_SIGNAL("TemperatureChanged", "i", 0),

    SD_BUS_VTABLE_END
};

int main(void)
{
    sd_bus_slot *slot = NULL;
    int rc;

    rc = sd_bus_open_system(&g_bus);
    if (rc < 0) { fprintf(stderr, "Bus: %s\n", strerror(-rc)); return 1; }

    /* Nesneyi vtable ile yayınla */
    rc = sd_bus_add_object_vtable(g_bus, &slot, PATH, IFACE,
                                   sensor_vtable, NULL);
    if (rc < 0) { fprintf(stderr, "Vtable: %s\n", strerror(-rc)); goto out; }

    /* Well-known isim talep et */
    rc = sd_bus_request_name(g_bus, SERVICE, 0);
    if (rc < 0) { fprintf(stderr, "Name: %s\n", strerror(-rc)); goto out; }

    printf("Servis hazır: %s\n", SERVICE);

    /* Event loop */
    while (1) {
        rc = sd_bus_process(g_bus, NULL);
        if (rc < 0) break;
        if (rc > 0) continue;
        sd_bus_wait(g_bus, UINT64_MAX);
    }

out:
    sd_bus_slot_unref(slot);
    sd_bus_unref(g_bus);
    return rc < 0 ? 1 : 0;
}

Test

bash
# Servisi arka planda başlat (policy dosyası gerekli, bkz. bölüm 07)
sudo ./temp_sensor_service &

# GetTemperature çağrısı
busctl call com.example.TempSensor \
  /com/example/TempSensor \
  com.example.TempSensor \
  GetTemperature
# i 230

# SetTemperature çağrısı
busctl call com.example.TempSensor \
  /com/example/TempSensor \
  com.example.TempSensor \
  SetTemperature i 280

# Property okuma
busctl get-property com.example.TempSensor \
  /com/example/TempSensor \
  com.example.TempSensor \
  Temperature
# i 280
NOT

SD_BUS_METHOD() makrosunun ikinci parametresi giriş tip imzası, üçüncüsü çıkış tip imzasıdır. "" boş anlamına gelir. SD_BUS_VTABLE_UNPRIVILEGED flag'ı, root olmayan kullanıcıların bu metodu çağırmasına izin verir. Ayrıcalıklı işlemler için bu flag çıkarılır ve polkit entegrasyonu yapılır.

Bu bölümde

  • SD_BUS_VTABLE_START / SD_BUS_VTABLE_END ile vtable yapısı
  • SD_BUS_METHOD, SD_BUS_PROPERTY, SD_BUS_SIGNAL makroları
  • sd_bus_reply_method_return() ile yanıt gönderme
  • sd_bus_request_name() ile well-known isim kaydı
  • sd_bus_emit_signal() ile sinyal yayınlama

07 Güvenlik: policy dosyaları

D-Bus güvenliği XML policy dosyalarıyla yönetilir; sistem bus'ta servis açmak veya mesaj göndermek için bu dosyaların doğru yapılandırılması zorunludur.

Policy dosyası konumu ve yapısı

System bus policy dosyaları /etc/dbus-1/system.d/ dizinine yerleştirilir. Dosya adı genellikle bus adıyla eşleşir: com.example.TempSensor.conf.

/etc/dbus-1/system.d/com.example.TempSensor.conf
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE busconfig PUBLIC
 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">

<busconfig>
  <!-- Servis sahibi: yalnızca root çalıştırabilir -->
  <policy user="root">
    <allow own="com.example.TempSensor"/>
    <allow send_destination="com.example.TempSensor"/>
    <allow receive_sender="com.example.TempSensor"/>
  </policy>

  <!-- Varsayılan: tüm kullanıcılar GetTemperature çağırabilir -->
  <policy context="default">
    <allow send_destination="com.example.TempSensor"
           send_interface="com.example.TempSensor"
           send_member="GetTemperature"/>
    <allow receive_sender="com.example.TempSensor"/>
  </policy>

  <!-- operator grubu: SetTemperature de çağırabilir -->
  <policy group="operator">
    <allow send_destination="com.example.TempSensor"
           send_interface="com.example.TempSensor"
           send_member="SetTemperature"/>
  </policy>
</busconfig>

İzin türleri

ownWell-known bus adını talep etme izni. <allow own="com.example.Foo"/>
send_destinationHedefe mesaj gönderme izni. Bus adı veya * wildcard.
send_interfaceBelirli bir interface'e mesaj gönderme kısıtlaması.
send_memberBelirli bir method veya signal adına kısıtlama.
receive_senderBelirli bir kaynaktan sinyal alma izni.
context="default"Başka policy eşleşmezse uygulanır. Genellikle kısıtlayıcı.

Policy yeniden yükleme ve systemd activation

bash
# Policy dosyasını kopyala
sudo cp com.example.TempSensor.conf /etc/dbus-1/system.d/
sudo chmod 644 /etc/dbus-1/system.d/com.example.TempSensor.conf

# dbus-daemon'ı yeniden yükle (servis yeniden başlatılmaz)
sudo systemctl reload dbus

# Policy hata logları
journalctl -u dbus --since "1 min ago"

systemd D-Bus activation

Bir D-Bus servisini, istemci mesaj gönderdiğinde otomatik başlatmak için systemd activation kullanılır:

/usr/share/dbus-1/system-services/com.example.TempSensor.service
[D-BUS Service]
Name=com.example.TempSensor
Exec=/usr/bin/temp-sensor-service
User=root
SystemdService=com.example.TempSensor.service
/etc/systemd/system/com.example.TempSensor.service
[Unit]
Description=Temperature Sensor D-Bus Service
After=dbus.service

[Service]
Type=dbus
BusName=com.example.TempSensor
ExecStart=/usr/bin/temp-sensor-service
User=root
Restart=on-failure

[Install]
WantedBy=multi-user.target
UYARI

Policy dosyasında <allow send_destination="*"/> veya <allow own="*"/> gibi wildcard izinler asla kullanmayın. Bu, herhangi bir sürecin herhangi bir servise mesaj göndermesine ya da var olan bir servis adını ele geçirmesine olanak tanır. Her zaman servis adlarını ve interface'leri açıkça belirtin.

Bu bölümde

  • Policy XML formatı: own, send_destination, send_interface, receive_sender
  • Kullanıcı, grup ve context="default" bazlı izin katmanları
  • Policy yeniden yükleme: systemctl reload dbus
  • systemd D-Bus activation: [D-BUS Service] dosyası + systemd unit

08 Embedded'da D-Bus alternatifleri

dbus-daemon'ın performans maliyeti gömülü kaynak kısıtlı sistemlerde sorun yaratabilir; dbus-broker, kdbus ve domain-specific alternatiflerin doğru seçimi için karşılaştırmalı bir değerlendirme gereklidir.

dbus-daemon sorunları

Geleneksel dbus-daemon her mesaj için iki context switch gerektirir: gönderen → daemon → alıcı. Yüksek frekanslı mesajlaşmada (saniyede yüzlerce mesaj) bu gecikme birikir. Ayrıca daemon'ın fork-exec modeli başlangıç gecikmesi ekler.

ÇözümLatencyBellekDurum
dbus-daemon~100 µs~3 MBYaygın, stabil
dbus-broker~50 µs~1.5 MBFedora/RHEL varsayılanı
kdbus (kernel)~5 µsÇok azMainline'da yok
sd-bus (direkt)~10 µs~0.5 MBDaemon bypass mümkün

dbus-broker

dbus-broker, routing mantığını basitleştirmek amacıyla sıfırdan yazılmıştır. Güvenlik policy'si ayrı bir süreç (dbus-broker-launch) tarafından yönetilir. API tamamen uyumludur — mevcut kod değiştirilmeden çalışır.

bash
# dbus-broker kurulumu (Debian/Ubuntu)
apt install dbus-broker

# dbus.service'i dbus-broker ile değiştir
systemctl enable --now dbus-broker.service
systemctl disable dbus.service

# Çalışma kontrolü
systemctl status dbus-broker.service
busctl list # Hâlâ aynı çıktı

Domain-specific alternatifler

nanomsg / NNGHigh-performance message passing library. TCP/IPC transport, pattern tabanlı (REQ/REP, PUB/SUB). D-Bus servis keşfi yok ama çok düşük latency.
ZeroMQAsenkron mesajlaşma kütüphanesi. Çok güçlü pattern desteği. D-Bus'tan farklı kullanım senaryosu — genellikle distributed sistemler için.
MQTT (lokal)Mosquitto lokal broker ile process-arası pub/sub. IoT protokolü olduğundan ağ köprüsü de kolaydır. D-Bus introspection yok.
Unix socket (custom)En düşük overhead. Kendi protokolünü (protobuf, msgpack) üstüne kurar. Servis keşfi manuel yapılır.

Ne zaman D-Bus kullanmalı

NOT

D-Bus'ı tercih edin: systemd zaten hedef sistemde varsa; BlueZ, NetworkManager veya udisks2 ile entegrasyon gerekiyorsa; servis keşfi ve introspection değerliyse; varolan D-Bus API'lerine bağlanılıyorsa. Yalnızca iki uygulama arasında yüksek frekanslı veri aktarımı için Unix socket veya shared memory daha uygun olabilir.

Bu bölümde

  • dbus-daemon latency sorunu ve context switch maliyeti
  • dbus-broker: daha hızlı routing, tam API uyumluluğu
  • kdbus: kernel-space routing (mainline'da henüz yok)
  • nanomsg, ZeroMQ, MQTT, Unix socket alternatifleri
  • D-Bus seçim kriterleri: systemd bağımlılığı ve servis entegrasyonu

09 Pratik: BlueZ D-Bus API

BlueZ, Bluetooth yığınının tamamını D-Bus üzerinden açar; Python ile adapter yönetimi, cihaz tarama ve GATT characteristic okuma yapılabilir.

BlueZ nesne hiyerarşisi

bash
# BlueZ nesne ağacını gör
busctl tree org.bluez

# Çıktı:
# └─ /org/bluez
#    └─ /org/bluez/hci0
#       ├─ /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF
#       │  ├─ /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0001
#       │  │  ├─ /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0001/char0002
#       │  │  └─ /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0001/char0004

# Adapter interface'ini incele
busctl introspect org.bluez /org/bluez/hci0 \
  org.bluez.Adapter1

Python ile Bluetooth cihaz tarama

bluez_scan.py
import time
from gi.repository import Gio, GLib

BLUEZ_SERVICE   = "org.bluez"
ADAPTER_PATH    = "/org/bluez/hci0"
ADAPTER_IFACE   = "org.bluez.Adapter1"
OBJ_MGR_IFACE   = "org.freedesktop.DBus.ObjectManager"

def get_adapter(bus):
    return Gio.DBusProxy.new_sync(
        bus, Gio.DBusProxyFlags.NONE, None,
        BLUEZ_SERVICE, ADAPTER_PATH, ADAPTER_IFACE, None
    )

def on_interfaces_added(conn, sender, path, iface, signal, params, userdata):
    obj_path, ifaces = params.unpack()
    if "org.bluez.Device1" in ifaces:
        dev = ifaces["org.bluez.Device1"]
        name    = dev.get("Name", "?")
        addr    = dev.get("Address", "?")
        rssi    = dev.get("RSSI", "?")
        print(f"  Yeni cihaz: {name} [{addr}] RSSI={rssi}")

def scan_bluetooth(duration_sec=10):
    bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
    adapter = get_adapter(bus)

    # InterfacesAdded sinyaline subscribe ol
    bus.signal_subscribe(
        BLUEZ_SERVICE,
        OBJ_MGR_IFACE,
        "InterfacesAdded",
        None,
        None,
        Gio.DBusSignalFlags.NONE,
        on_interfaces_added,
        None
    )

    # Taramayı başlat
    adapter.call_sync("StartDiscovery", None,
                      Gio.DBusCallFlags.NONE, -1, None)
    print(f"Tarama başladı ({duration_sec}s)...")

    loop = GLib.MainLoop()
    GLib.timeout_add_seconds(duration_sec, loop.quit)
    loop.run()

    # Taramayı durdur
    adapter.call_sync("StopDiscovery", None,
                      Gio.DBusCallFlags.NONE, -1, None)
    print("Tarama tamamlandı.")

if __name__ == "__main__":
    scan_bluetooth()

GATT characteristic okuma

gatt_read.py
from gi.repository import Gio, GLib

BLUEZ_SERVICE  = "org.bluez"
GATT_CHAR_IFACE = "org.bluez.GattCharacteristic1"

def read_characteristic(char_path):
    """GATT characteristic değerini oku (Raw bytes döner)."""
    bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
    char_proxy = Gio.DBusProxy.new_sync(
        bus, Gio.DBusProxyFlags.NONE, None,
        BLUEZ_SERVICE, char_path, GATT_CHAR_IFACE, None
    )

    # ReadValue metodunu çağır (options: boş dict)
    result = char_proxy.call_sync(
        "ReadValue",
        GLib.Variant("(a{sv})", [{}]),
        Gio.DBusCallFlags.NONE,
        -1,
        None
    )
    raw_bytes = bytes(result.unpack()[0])
    print(f"Raw: {raw_bytes.hex()}")
    return raw_bytes

def notify_characteristic(char_path):
    """GATT notification'ları dinle."""
    bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
    char_proxy = Gio.DBusProxy.new_sync(
        bus, Gio.DBusProxyFlags.NONE, None,
        BLUEZ_SERVICE, char_path, GATT_CHAR_IFACE, None
    )

    def on_prop_changed(proxy, changed_props, invalidated):
        props = changed_props.unpack()
        if "Value" in props:
            val = bytes(props["Value"])
            print(f"Notification: {val.hex()}")

    char_proxy.connect("g-properties-changed", on_prop_changed)
    char_proxy.call_sync("StartNotify", None,
                         Gio.DBusCallFlags.NONE, -1, None)

    loop = GLib.MainLoop()
    loop.run()

# Kullanım örneği — BLE sıcaklık sensörü (UUID 2A6E)
# char_path = "/org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/service0001/char0002"
# read_characteristic(char_path)

GetManagedObjects ile tüm BlueZ nesnelerini listeleme

bluez_objects.py
from gi.repository import Gio

def list_bluez_objects():
    bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
    obj_mgr = Gio.DBusProxy.new_sync(
        bus, Gio.DBusProxyFlags.NONE, None,
        "org.bluez", "/",
        "org.freedesktop.DBus.ObjectManager", None
    )

    result = obj_mgr.call_sync("GetManagedObjects", None,
                                Gio.DBusCallFlags.NONE, -1, None)
    objects = result.unpack()[0]

    for path, ifaces in objects.items():
        if "org.bluez.Device1" in ifaces:
            dev = ifaces["org.bluez.Device1"]
            print(f"{path}")
            print(f"  Name:    {dev.get('Name', 'N/A')}")
            print(f"  Address: {dev.get('Address', 'N/A')}")
            print(f"  Paired:  {dev.get('Paired', False)}")
            print(f"  Connected: {dev.get('Connected', False)}")

list_bluez_objects()
NOT

BlueZ'i programatik kullanmak için bluetoothd'nin çalışması ve hedef adaptörün Powered: yes durumunda olması gerekir. busctl set-property org.bluez /org/bluez/hci0 org.bluez.Adapter1 Powered b true komutuyla adaptörü açabilirsiniz.

Bu bölümde

  • BlueZ nesne hiyerarşisi: Adapter1, Device1, GattCharacteristic1
  • InterfacesAdded sinyali ile yeni cihaz tespiti
  • StartDiscovery / StopDiscovery ile tarama yönetimi
  • GattCharacteristic1.ReadValue() ile GATT okuma
  • StartNotify + g-properties-changed ile GATT notification
  • GetManagedObjects ile tüm BlueZ nesne ağacını alma