한국어
Linux Programming
 

Linux에서 dbus-c++바인딩으로 서비스를 제공하는 프로세스 구현 방법을 소개한다.

참고로 dbus-c++ 바인더는 2011년 이후로 개발이 중지된 상태이지만 여기선 간단히 개념을 설명하기 위해 사용하였다. 현재 C++위한 바인더로 dbus-cxx, dbus-cpp, Qt D-Bus, GDBus 등이 있다.

 

먼저 아래명령으로 c++바인딩 패키지를 설치한다.

apt-get install libdbus-c++-bin libdbus-c++-dev

 

작업 폴더를 만들고 아래의 예제처럼 xml로 인터페이스를 작성한다.

아래의 예제에서 type="s" name="name" direction="in" 이것은 string타입의 입력 인자를 의미한다.

interface.xml

<?xml version="1.0" ?>
<node name="/org/freedesktop/DBus/Examples/Echo">
  <interface name="org.freedesktop.DBus.EchoDemo">
    <method name="HelloString">
      <arg type="s" name="name" direction="in"/>
      <arg type="s" name="greeting" direction="out"/>
    </method>

    <signal name="EchoCount">
      <arg type="y" name="count"/>
    </signal>
  </interface>
</node>

 

아래표는 D-Bus에서 사용하는 타입들이다.

 

Category

Conventional Name

Code

Description

reserved

INVALID

0 (ASCII NUL)

Not a valid type code, used to terminate signatures

fixed, basic

BYTE

121 (ASCII 'y')

8-bit unsigned integer

fixed, basic

BOOLEAN

98 (ASCII 'b')

Boolean value, 0 is FALSE and 1 is TRUE. Everything else is invalid.

fixed, basic

INT16

110 (ASCII 'n')

16-bit signed integer

fixed, basic

UINT16

113 (ASCII 'q')

16-bit unsigned integer

fixed, basic

INT32

105 (ASCII 'i')

32-bit signed integer

fixed, basic

UINT32

117 (ASCII 'u')

32-bit unsigned integer

fixed, basic

INT64

120 (ASCII 'x')

64-bit signed integer

fixed, basic

UINT64

116 (ASCII 't')

64-bit unsigned integer

fixed, basic

DOUBLE

100 (ASCII 'd')

IEEE 754 double

string-like, basic

STRING

115 (ASCII 's')

UTF-8 string (must be valid UTF-8). Must be nul terminated and contain no other nul bytes.

string-like, basic

OBJECT_PATH

111 (ASCII 'o')

Name of an object instance

string-like, basic

SIGNATURE

103 (ASCII 'g')

A type signature

container

ARRAY

97 (ASCII 'a')

Array

container

STRUCT

114 (ASCII 'r'), 40 (ASCII '('), 41 (ASCII ')')

Struct; type code 114 'r' is reserved for use in bindings and implementations to represent the general concept of a struct, and must not appear in signatures used on D-Bus.

container

VARIANT

118 (ASCII 'v')

Variant type (the type of the value is part of the value itself)

container

DICT_ENTRY

101 (ASCII 'e'), 123 (ASCII '{'), 125 (ASCII '}')

Entry in a dict or map (array of key-value pairs). Type code 101 'e' is reserved for use in bindings and implementations to represent the general concept of a dict or dict-entry, and must not appear in signatures used on D-Bus.

fixed, basic

UNIX_FD

104 (ASCII 'h')

Unix file descriptor

reserved

(reserved)

109 (ASCII 'm')

Reserved for a 'maybe' type compatible with the one in GVariant, and must not appear in signatures used on D-Bus until specified here

reserved

(reserved)

42 (ASCII '*')

Reserved for use in bindings/implementations to represent any single complete type, and must not appear in signatures used on D-Bus.

reserved

(reserved)

63 (ASCII '?')

Reserved for use in bindings/implementations to represent any basic type, and must not appear in signatures used on D-Bus.

reserved

(reserved)

64 (ASCII '@'), 38 (ASCII '&'), 94 (ASCII '^')

Reserved for internal use by bindings/implementations, and must not appear in signatures used on D-Bus. GVariant uses these type-codes to encode calling conventions.

 

작성된 xml을 service를 제공하는 쪽에서 사용 할 헤더파일로 변환한다.

dbusxx-xml2cpp interface.xml --adaptor=echo-interface-adaptor.h  

 

변환된 헤더파일은 대략 아래와 같다.

echo-interface-adaptor.h

/*
 * This file was automatically generated by dbusxx-xml2cpp; DO NOT EDIT!
 */

#ifndef __dbusxx__echo_interface_adaptor_h__ADAPTOR_MARSHAL_H
#define __dbusxx__echo_interface_adaptor_h__ADAPTOR_MARSHAL_H

#include <dbus-c++/dbus.h>
#include <cassert>

namespace org {
namespace freedesktop {
namespace DBus {

class EchoDemo_adaptor
: public ::DBus::InterfaceAdaptor
{
public:

    EchoDemo_adaptor()
    : ::DBus::InterfaceAdaptor("org.freedesktop.DBus.EchoDemo")
    {
        register_method(EchoDemo_adaptor, HelloString, _HelloString_stub);
    }

    ::DBus::IntrospectedInterface *introspect() const 
    {
        static ::DBus::IntrospectedArgument HelloString_args[] = 
        {
            { "name", "s", true },
            { "greeting", "s", false },
            { 0, 0, 0 }
        };
        static ::DBus::IntrospectedArgument EchoCount_args[] = 
        {
            { "count", "y", false },
            { 0, 0, 0 }
        };
        static ::DBus::IntrospectedMethod EchoDemo_adaptor_methods[] = 
        {
            { "HelloString", HelloString_args },
            { 0, 0 }
        };
        static ::DBus::IntrospectedMethod EchoDemo_adaptor_signals[] = 
        {
            { "EchoCount", EchoCount_args },
            { 0, 0 }
        };
        static ::DBus::IntrospectedProperty EchoDemo_adaptor_properties[] = 
        {
            { 0, 0, 0, 0 }
        };
        static ::DBus::IntrospectedInterface EchoDemo_adaptor_interface = 
        {
            "org.freedesktop.DBus.EchoDemo",
            EchoDemo_adaptor_methods,
            EchoDemo_adaptor_signals,
            EchoDemo_adaptor_properties
        };
        return &EchoDemo_adaptor_interface;
    }

public:

    /* properties exposed by this interface, use
     * property() and property(value) to get and set a particular property
     */

public:

    /* methods exported by this interface,
     * you will have to implement them in your ObjectAdaptor
     */
    virtual std::string HelloString(const std::string& name) = 0;

public:

    /* signal emitters for this interface
     */
    void EchoCount(const uint8_t& arg1)
    {
        ::DBus::SignalMessage sig("EchoCount");
        ::DBus::MessageIter wi = sig.writer();
        wi << arg1;
        emit_signal(sig);
    }

private:

    /* unmarshalers (to unpack the DBus message before calling the actual interface method)
     */
    ::DBus::Message _HelloString_stub(const ::DBus::CallMessage &call)
    {
        ::DBus::MessageIter ri = call.reader();

        std::string argin1; ri >> argin1;
        std::string argout1 = HelloString(argin1);
        ::DBus::ReturnMessage reply(call);
        ::DBus::MessageIter wi = reply.writer();
        wi << argout1;
        return reply;
    }
};

} } } 
#endif //__dbusxx__echo_interface_adaptor_h__ADAPTOR_MARSHAL_H

 

이제 위의 클래스를 상속받고 가상함수를 오버라이드하여 정의한다.

echo-server.h

#include "echo-interface-adaptor.h"

class EchoServer : public org::freedesktop::DBus::EchoDemo_adaptor,
                   public DBus::IntrospectableAdaptor,
                   public DBus::ObjectAdaptor
{
public:
    EchoServer(DBus::Connection * conn);
    virtual ~EchoServer();

public:
    virtual std::string HelloString(const std::string& name);
};

 

echo-server.cpp

#include "echo-server.h"
#include <stdio.h>

EchoServer::EchoServer(DBus::Connection * conn)
: DBus::ObjectAdaptor(*conn, "/org/freedesktop/DBus/Examples/Echo")
{

}

EchoServer::~EchoServer()
{

}

std::string EchoServer::HelloString(const std::string& name)
{
    printf("Input string : %s\n", name.c_str() );
    std::string ret = name + " world!!";
    return ret;
}

 

main함수에서는 service를 위한 SessionBus 연결 및 객체를 생성한다.

main.cpp

#include "echo-server.h"
#include <unistd.h>
#include <future>
#include <stdio.h>

DBus::BusDispatcher dispatcher;

int main()
{
    DBus::default_dispatcher = &dispatcher;
    DBus::_init_threading();
    DBus::Connection conn = DBus::Connection::SessionBus();
    conn.request_name("org.freedesktop.DBus.EchoService");

    EchoServer server(&conn);

    uint8_t count = 0;

    std::future<void>fut = std::async(std::launch::async, [=] {
        dispatcher.enter();
    });

    // emit EchoSignal every seconds.
    while ( true )
    {
        printf("emit signal 'EchoSignal' count : %d\n", count);
        server.EchoCount(count++);
        sleep(1);
    }
    return 0;
}

 

dbus-c++-1 라이브러리 및 INCLUDE Path를 컴파일 시에 인자로 넘겨준다.

 

이제 구현 테스트를 위한 방법으로 우분투 소프트웨어 센터에서 "D-Feet" 검색하고 설치한다.

어플리케이션을 실행한 상태에서 D-Feet을 실행하고 Session Bus 탭을 선택 후 "echo"를 검색하면 아래처럼 보일 것이다.

d-feet.png

 

오른쪽 Object path영역에서 인터페이스를 펼치면 아래처럼 서비스쪽의 Methods 및 Signals 등이 보인다.

Methods에서 HelloString을 더블클릭한다.

d-feet1.png

 

Input영역에 문자열 입력 후 Execute를 클릭한다.

d-feet2.png

 

그러면 service 쪽 출력내용이 다음과 같이 보일 것이다.

dbus_test.png

 

이 서비스 프로세스와 통신하는 클라이언트는 파이썬으로 구현, 테스트 예제: https://makersweb.net/python/6517

윈도우에서 Qt D-Bus를 사용한 예제: https://makersweb.net/qt/13174

이 글과 연관된 다른 글
  1. [2019/06/30] GDBus 튜토리얼(GDBus tutorial) by makersweb (3457)
  2. [2019/05/02] Windows에서 Qt D-Bus를 사용하여 프로세스간 통신(IPC) by makersweb (1299)
  3. [2018/03/12] pydbus 바인딩을 이용하여 서비스 데몬과 D-Bus통신 by makersweb (693)
  4. [2015/02/28] [IPC]D-Bus 소개 by makersweb (25297)
번호 제목 글쓴이 날짜 조회 수
35 리눅스 오디오 스택과 아키텍처 file makersweb 2020.09.02 21
34 wayland-scanner 를 통해 Wayland 프로토콜 코드생성 makersweb 2020.06.08 4
33 Wayland 의 Client Application 프로그래밍 기본 루틴 makersweb 2020.06.04 21
32 Wayland 의 주요 객체들 makersweb 2020.06.04 13
31 Weston 의 설명 및 관련 컴포넌트 makersweb 2020.06.03 14
30 64비트 리눅스에서 32비트 응용프로그램을 실행하려면 makersweb 2020.02.29 303
29 initramfs (initial ram file system: 초기 램 파일 시스템) makersweb 2020.02.25 80
28 플랫폼 디바이스 드라이버 개발 시 많이 사용되는 커널 API 및 매크로 makersweb 2020.01.28 335
27 PATH에 새로운 경로 추가 makersweb 2019.09.19 89
26 리눅스 컴파일러 최신으로 업데이트 linux 2018.12.26 1160
25 libblkid - USB Storage의 정보 가져오기 makersweb 2018.10.18 241
24 tslib 크로스 컴파일과 터치스크린 보정 makersweb 2018.08.02 1011
» Ubuntu Linux에서 dbus-c++바인딩 D-Bus 테스트 file makersweb 2018.03.07 3638
22 NFS를 통해 파일시스템 공유 makersweb 2018.03.05 663
21 Wayland에 대한 간단한 소개 file makersweb 2017.12.29 1527
20 Ubuntu16.04에서 weston구동 file makersweb 2017.12.28 413
19 UVC 장치를 사용할때 v4l2: select timeout 에러 발생 makersweb 2017.12.27 643
18 [IPC]D-Bus 소개 file makersweb 2015.02.28 25297
17 리눅스 데스크탑 환경 종류 pjk 2015.02.11 4114
16 디바이스 드라이버에 대해서 makersweb 2014.04.19 3754