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"를 검색하면 아래처럼 보일 것이다.

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

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

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

이 서비스 프로세스와 통신하는 클라이언트는 파이썬으로 구현, 테스트 예제: https://makersweb.net/python/6517
윈도우에서 Qt D-Bus를 사용한 예제: https://makersweb.net/qt/13174
| 번호 | 제목 | 글쓴이 | 날짜 | 조회 수 | 
|---|---|---|---|---|
| 25 | libblkid - USB Storage의 정보 가져오기 | makersweb | 2018.10.18 | 2881 | 
| 24 | tslib 크로스 컴파일과 터치스크린 보정 | makersweb | 2018.08.02 | 4766 | 
| » | 
					Ubuntu Linux에서 dbus-c++바인딩 D-Bus 테스트
															 | 																																																												makersweb | 2018.03.07 | 10109 | 
| 22 | NFS를 통해 파일시스템 공유 | makersweb | 2018.03.05 | 3119 | 
| 21 | 
					Wayland에 대한 간단한 소개
															 | 																																																												makersweb | 2017.12.29 | 5855 | 
| 20 | 
					Ubuntu16.04에서 weston구동
															 | 																																																												makersweb | 2017.12.28 | 3753 | 
| 19 | UVC 장치를 사용할때 v4l2: select timeout 에러 발생 | makersweb | 2017.12.27 | 5123 | 
| 18 | 
					[IPC]D-Bus 소개
															 | 																																																												makersweb | 2015.02.28 | 32430 | 
| 17 | 리눅스 데스크탑 환경 종류 | pjk | 2015.02.11 | 6914 | 
| 16 | 디바이스 드라이버에 대해서 | makersweb | 2014.04.19 | 6994 | 
| 15 | 
					리눅스 커널 소스코드 구성도
															 | 																																																												makersweb | 2014.03.04 | 8085 | 
| 14 | read() 함수, write() 함수 | makersweb | 2014.03.04 | 14363 | 
| 13 | 리눅스 디렉터리 구조 | makersweb | 2014.02.28 | 5280 | 
| 12 | 1. make | pjk | 2014.02.05 | 4335 | 
| 11 | 2. 간단한 Makefile | pjk | 2014.02.05 | 5131 | 
| 10 | 3. 매크로(Macro) 와 확장자(Suffix) 규칙 | pjk | 2014.02.05 | 4731 | 
| 9 | 4. Makefile를 작성할 때 알면 좋은 것들 | pjk | 2014.02.05 | 6199 | 
| 8 | 5. make 중요 옵션 정리 | pjk | 2014.02.05 | 5103 | 
| 7 | 6. Makefile 작성의 가이드라인 | pjk | 2014.02.05 | 4181 | 
| 6 | mmap() 함수, munmap() 함수 | pjk | 2014.02.05 | 18764 |