dbus+select+tcp socket

好久不见. 提交于 2019-11-26 04:58:54
#include <dbus/dbus.h>
#include <iostream>
#include <unistd.h>
#include <sys/select.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <assert.h>

using namespace std;

/**
 * A watch bind with a fd(owned by the socket),
 * when fd is ready to read data or write from transport, a notify event occurs
 */
struct watchlist_t {
    DBusWatch *watch;
    struct watchlist_t *next;
};

static struct watchlist_t *watchlist = NULL;

struct timeoutlist_t{
    DBusTimeout *timeout;
    struct timeoutlist_t *next;
};

static struct timeoutlist_t *timeoutlist = NULL;

const char *objectPaths[] = {
    "/com/redflag",
    "/test/signal/server"           /*//org/freedesktop/csy",*/
};

/* Object handlers and object path handlers */

DBusHandlerResult subsection_com_redflag_handler(DBusConnection*, DBusMessage*, void*);

DBusHandlerResult object_dbus_handler(DBusConnection*, DBusMessage*, void*);


void object_unregister_handler(DBusConnection*, void*);

int wait_socket(int port);

DBusObjectPathVTable objectPathVTable[] =
{
    {
        .unregister_function = NULL,
        .message_function = subsection_com_redflag_handler
    },
    {
        .unregister_function = NULL,
        .message_function = object_dbus_handler
    }
};

struct seriallist_t {
    dbus_uint32_t serial;
    struct seriallist_t *next;
};

static seriallist_t *seriallist = NULL;

// debug facilities
void err_quit(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    fprintf(stderr, fmt, args);
    va_end(args);
    exit(1);
}
void set_nonblock(int socket) {
    int flags;
    flags = fcntl(socket,F_GETFL,0);
    assert(flags != -1);
    fcntl(socket, F_SETFL, flags | O_NONBLOCK);
}
char* _verbose_watch(DBusWatch *watch)
{
    const char *s_flags[] = { "readable", "writeable"};

    static char p[1024] = "", ret[1024] = "";
    if (dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE)
    {
        strncpy(p, s_flags[0], strlen(s_flags[0]));
    }
    else if (dbus_watch_get_flags(watch) & DBUS_WATCH_WRITABLE)
    {
        if (p[0])
        {
            strncat(p, "&", strlen("&"));
        }
        strncat(p, s_flags[1], strlen(s_flags[1]));
    }
    int on=1;
    int fd = dbus_watch_get_socket(watch);
    set_nonblock(fd);
    sprintf(ret, ":fd is %d, %s", fd, p);
    return ret;
}

char *_verbose_message(DBusMessage *msg) {
    static char s_msg[1024] = "", ret[1024] = "";
    int bc = sprintf(s_msg, "\ttype: %s\n\tpath: %s\n\tmember: %s\n\t",
                     dbus_message_type_to_string(dbus_message_get_type(msg)),
                     dbus_message_get_path(msg),
                     dbus_message_get_member(msg));
    strncpy(ret, s_msg, bc + 1);
    if (dbus_message_get_serial(msg))
    {
        bc = sprintf(s_msg, "serial: %ud\n\t", dbus_message_get_reply_serial(msg));
        strncat(ret, s_msg, bc + 1);
    }

    DBusMessageIter args, subargs;
    char *s;
    int i;
    dbus_message_iter_init(msg, &args);
    bc = sprintf(s_msg, "args: ");
    strncat(ret, s_msg, bc + 1);

    while (DBUS_TYPE_INVALID != dbus_message_iter_get_arg_type(&args))
    {
        switch (dbus_message_iter_get_arg_type(&args))
        {
            case DBUS_TYPE_STRING:
            {
                dbus_message_iter_get_basic(&args, &s);
                bc = sprintf(s_msg, " %s", s);
                strncat(ret, s_msg, bc + 1);
            }
                break;
            case DBUS_TYPE_INT32:
            {
                dbus_message_iter_get_basic(&args, &i);
                bc = sprintf(s_msg, " %d", i);
                strncat(ret, s_msg, bc + 1);
            }
                break;
            case DBUS_TYPE_ARRAY:
            {
                dbus_message_iter_recurse(&args, &subargs);
                strcat(ret, " [ ");
                while (dbus_message_iter_get_arg_type(&subargs) != DBUS_TYPE_INVALID)
                {
                    switch (dbus_message_iter_get_arg_type(&subargs))
                    {
                        case DBUS_TYPE_STRING:
                        {
                            dbus_message_iter_get_basic(&subargs, &s);
                            bc = sprintf(s_msg, " %s", s);
                            strncat(ret, s_msg, bc + 1);
                        }
                            break;
                        case DBUS_TYPE_INT32:
                        {
                            dbus_message_iter_get_basic(&subargs, &i);
                            bc = sprintf(s_msg, " %d", i);
                            strncat(ret, s_msg, bc + 1);
                        }
                            break;
                        default:
                            break;
                    }
                    dbus_message_iter_next(&subargs);
                }
                strcat(ret, " ] ");
            }
                break;
            default:
                break;
        }
        dbus_message_iter_next(&args);
    }
    return ret;
}

char * _verbose_timeout(DBusTimeout *timeout)
{
    static char ret[1024] = "";
    sprintf(ret, "timeout: %d\n", dbus_timeout_get_interval(timeout));
    return ret;
}

DBusHandlerResult handle_method_return(DBusConnection *conn, DBusMessage *reply)
{
    struct seriallist_t *sl = seriallist;
    while (sl != NULL)
    {
        if (sl->serial == dbus_message_get_reply_serial(reply))
        {
            printf("reply_msg:\t%s\n", _verbose_message(reply));
            return DBUS_HANDLER_RESULT_HANDLED;
        }
        sl = sl->next;
    }
    return DBUS_HANDLER_RESULT_HANDLED;
}

int reply_to_method_call(DBusMessage *msg, DBusConnection *conn)
{
    DBusMessage *reply;
    DBusMessageIter args;
    DBusError err;

    if (!dbus_message_iter_init(msg, &args))
    {
        err_quit("arg init error.\n");
    }

    dbus_int32_t id;
    char *content;

    dbus_error_init(&err);
    dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &id,
                          DBUS_TYPE_STRING, &content, DBUS_TYPE_INVALID);
    if (dbus_error_is_set(&err))
    {
        err_quit("get arg error.\n");
    }

    reply = dbus_message_new_method_return(msg);
    if (NULL == reply)
    {
        err_quit("Memory is not enough.\n");
    }
    printf("received call args: %d: %s\n", id, content);

    const char *comment = "reply to method call from com.redflag.csy.";
    dbus_message_iter_init_append(reply, &args);
    dbus_message_append_args( reply,
                              DBUS_TYPE_STRING, &comment, DBUS_TYPE_INVALID);
    dbus_uint32_t serial;
    dbus_connection_send(conn, reply, &serial);
    dbus_connection_flush(conn);

    printf("build reply msg and send: \n%s\n", _verbose_message(reply));
    dbus_message_unref(reply);

    return 0;
}

dbus_bool_t watchAddNotify(DBusWatch *watch, void *data)
{
    struct watchlist_t *wl;
    for (wl = watchlist; wl != NULL; wl = wl->next)
    {
        if (wl->watch == watch)
        {
            return TRUE;
        }
    }

    printf("watchAdd: %s\n", _verbose_watch(watch));
    wl = dbus_new(struct watchlist_t, 1);
    if (NULL == wl)
        return FALSE;

    wl->watch = watch;
    wl->next = watchlist;
    watchlist = wl;
    return TRUE;
}

void watchRemoveNotify(DBusWatch *watch, void *data)
{
    struct watchlist_t *wl, *pre;
    for (pre = wl = watchlist; wl != NULL; pre = wl, wl = wl->next)
    {
        if (wl->watch == watch)
        {
            printf("watchRemove: %d\n", dbus_watch_get_socket(watch));
            if (wl == watchlist)
            {
                watchlist = wl->next;
                dbus_free(wl);
            }
            else{
                pre->next = wl->next;
                dbus_free(wl);
            }
            break;
        }
    }
}

void watchToggleNotify(DBusWatch *watch, void *data)
{
    if (watch == NULL)
    {
        err_quit("line %d: watch shold not be null.", __LINE__);
    }
    printf("toggleNotify: watch %d toggled %s",
               dbus_watch_get_socket(watch),
               dbus_watch_get_enabled(watch) ? "enable":"disabled");
}

dbus_bool_t watchHandler(int sockfd) {
    struct watchlist_t *wl;
    fd_set rfds, wfds, efds;
    int maxid = -1, fd;
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);
    FD_ZERO(&efds);
    struct timeval timeout;
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    char buffer[256];
    // prepare all readble and writeable fds
    for (wl = watchlist; wl != NULL; wl = wl->next)
    {
        if (!dbus_watch_get_enabled(wl->watch))
        {
            continue;
        }
        fd = dbus_watch_get_socket(wl->watch);
        if (fd & DBUS_WATCH_READABLE)
        {
            FD_SET(fd, &rfds);
            maxid = (maxid < fd ? fd : maxid);
        }

/*        if (fd & DBUS_WATCH_WRITABLE)
        {
            FD_SET(fd, &wfds);
            maxid = (maxid < fd ? fd : maxid);
        }

        if (fd & DBUS_WATCH_ERROR)
        {
            FD_SET(fd, &efds);
            maxid = (maxid < fd ? fd : maxid);
        }*/
    }

    FD_SET(sockfd, &rfds);
    maxid = (maxid < sockfd ? sockfd : maxid);


    cout<<"before select"<<endl;
    //int ret = select(maxid + 1, &rfds, &wfds, &efds, NULL);
    int ret = select(maxid + 1, &rfds, NULL, NULL, &timeout);
    cout<<"select get ret is "<<ret<<endl;
    if (ret <= 0)
        return FALSE;

    if (FD_ISSET(sockfd, &rfds))
    {
        cout<<"sockfd get data, OK"<<endl;
        int newSocketFd = accept(sockfd, NULL, NULL);
        if (newSocketFd >= 0)
        {
            cout<<"new socket fd is "<<newSocketFd<<endl;
            memset(buffer, 0, sizeof(buffer));
            int rc = recv(newSocketFd, buffer, sizeof(buffer), 0);
            if (rc >= 0)
            {
                int len = rc;
                cout<<rc<<" bytes received, data is: "<< buffer;
                rc = send(newSocketFd, buffer, len, 0);
            }
        }
    }
    
    //call dbus_watch_handler is a must, it uses internal predefined watch handler to do some thing
    for (wl = watchlist; wl != NULL; wl = wl->next)
    {
        fd = dbus_watch_get_socket(wl->watch);

        if (FD_ISSET(fd, &rfds))
        {
            printf("read fd is %d, ret is %d\n", fd, ret);
            dbus_watch_handle(wl->watch, DBUS_WATCH_READABLE);
        }

        if (FD_ISSET(fd, &wfds))
        {
            printf("write fd is %d, ret is %d\n", fd, ret);
            dbus_watch_handle(wl->watch, DBUS_WATCH_WRITABLE);
        }

        if (FD_ISSET(fd, &efds))
        {
            printf("error fd is %d, ret is %d\n", fd, ret);
            dbus_watch_handle(wl->watch, DBUS_WATCH_ERROR);
        }
    }
    return TRUE;
}

dbus_bool_t timeoutAddNotify(DBusTimeout *timeout, void *data)
{
    struct timeoutlist_t *tl;
    for (tl = timeoutlist; tl != NULL; tl = tl->next)
    {
        if (tl->timeout == timeout)
            return TRUE;
    }
    tl = dbus_new(struct timeoutlist_t, 1);
    if (NULL == tl)
        return FALSE;

    tl->timeout = timeout;
    fprintf(stdout, "timeoutAdd: %s\n", _verbose_timeout(timeout));
    tl->next = timeoutlist;
    timeoutlist = tl;
    return TRUE;
}

void timeoutRemoveNotify(DBusTimeout *timeout, void *data)
{
    struct timeoutlist_t *pre = NULL, *tl = timeoutlist;
    while (tl != NULL)
    {
        if (tl->timeout == timeout)
        {
            if (pre == NULL)
                timeoutlist = tl->next;
            else
                pre->next = tl->next;
            fprintf(stdout, "timeoutRemove:%s\n", _verbose_timeout(timeout));
            break;
        }
        pre = tl;
        tl = tl->next;
    }
}

void timeoutToggleNotify(DBusTimeout *timeout, void *data)
{
    fprintf(stdout, "timeoutToggle: %s\n", _verbose_timeout(timeout));
}

/**
 * in this function, we vall dbus_timeout_handle to handle all timeout events,
 * it will call internal predefined handler to process
*/
void timeoutHandle()
{
    struct timeoutlist_t *tl = timeoutlist;
    for (; tl != NULL; tl = tl->next)
    {
        if (dbus_timeout_get_enabled(tl->timeout))
        {
            printf("timeoutHandle: %s\n", _verbose_timeout(tl->timeout));
            dbus_timeout_handle(tl->timeout);
        }
    }
}

DBusHandlerResult msgFilter(DBusConnection *conn, DBusMessage *msg, void* data)
{
    printf("incomming msg: %s\n", _verbose_message(msg));

    switch (dbus_message_get_type(msg))
    {
        case DBUS_MESSAGE_TYPE_METHOD_CALL:
        {
            cout<<"msg Filter method "<<__LINE__<<endl;
            if (!strcmp(dbus_message_get_member(msg), "ignore"))
            {
                DBusMessage *errMsg;
                errMsg = dbus_message_new_error(msg,
                                                "com.redflag.csy.IgnoreService",
                                                "this demonstrate the filter.");
                dbus_connection_send(conn, errMsg, NULL);
                cout<<"method call "<<endl;
                return DBUS_HANDLER_RESULT_HANDLED;
            }
            else{
                break;
            }
        }
        case DBUS_MESSAGE_TYPE_METHOD_RETURN:
        // never reach here.
            break;
        case DBUS_MESSAGE_TYPE_SIGNAL:
            cout<<"msgFilter signal "<<__LINE__<<endl;
            break;
        case DBUS_MESSAGE_TYPE_ERROR:
            break;
        default:
            break;
    }

    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

DBusHandlerResult subsection_com_redflag_handler(DBusConnection* conn, DBusMessage* msg, void* data)
{
    if ( strncmp(dbus_message_get_path(msg), objectPaths[0], strlen(objectPaths[0])) != 0 )
    {
        printf("subsection_com_redflag_handler: something wrong.\n");
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    if ( strncmp(dbus_message_get_member(msg), "pseudo", 6) == 0 )
    {
        reply_to_method_call(msg, conn);
        printf("subsection_com_redflag_handler: handled.\n");
        return DBUS_HANDLER_RESULT_HANDLED;
    }
    else
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

DBusHandlerResult object_dbus_handler(DBusConnection* conn,
                                      DBusMessage* msg, void* data)
{
    if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
    {
        printf("object_dbus_handler: method_return.\n");
        DBusHandlerResult ret = handle_method_return( conn, msg );
        printf("object_dbus_handler: handled.\n");
        return ret;
    }

    fprintf(stdout, "object_dbus_handler: cannot handle.\n");
    fprintf(stdout, "\t%s\n", _verbose_message(msg));
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

void object_unregister_handler(DBusConnection* conn, void* data)
{
    printf("object_unregister_handler:\n");
}

// send dbus signal to other dbus program
void send_random_signal(DBusConnection *conn)
{
    //DBusMessage *msg;
}

void pendingCallNotify(DBusPendingCall *pending, void *data)
{
    DBusMessage *msg = dbus_pending_call_steal_reply(pending);
    printf("pendingCallNotify: %s\n", _verbose_message(msg));
}

//call other dbus program method and get reply
void send_random_method_expecting_reply(DBusConnection *conn)
{
    //DBusMessage *msg;
}

int dbus_process_msg_loop(int sockfd)
{
    DBusConnection *conn;
    DBusError *perr;

    //1. connect to dbus
    perr = dbus_new(DBusError, 1);
    dbus_error_init(perr);
    conn = dbus_bus_get(DBUS_BUS_SESSION, perr);

    if (dbus_error_is_set(perr))
    {
        err_quit("connection failed.\n");
    }

    //2. request dbus name
    int ret = dbus_bus_request_name(conn, "test.signal.server",
                                    DBUS_NAME_FLAG_REPLACE_EXISTING, perr);
//    ret = dbus_bus_request_name(conn, "test.signal.method",
//                                        DBUS_NAME_FLAG_REPLACE_EXISTING, perr);

    if (dbus_error_is_set(perr))
    {
        err_quit("request name failed\n");
    }
    if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret)
    {
        err_quit("not primary owner\n");
    }

    //3. register some handler
    //dbus_bus_add_match(conn,
    //                   "type='method_call',path='/org/freedesktop/csy'"
    //                   ",interface='com.redflag.sycao',member='faked'", perr);
    dbus_bus_add_match(conn, "type='signal'",perr);
    if (dbus_error_is_set(perr))
    {
        printf("%s\n", perr->message);
        err_quit("add match failed\n");
    }

    //dbus_connection_add_filter(conn, msgFilter, NULL, NULL);

    dbus_connection_set_watch_functions(conn,
                                        watchAddNotify,
                                        watchRemoveNotify,
                                        watchToggleNotify,
                                        NULL, NULL);

    dbus_connection_set_timeout_functions(conn,
                                          timeoutAddNotify,
                                          timeoutRemoveNotify,
                                          timeoutToggleNotify,
                                          NULL, NULL);

    printf("main: registered path\n");
    uint i;
    for (i = 0; i < sizeof(objectPaths) / sizeof(objectPaths[0]); i++)
    {
        if (i < 1)
        {
            //dbus_connection_register_fallback(conn,
            //                                  objectPaths[i],
            //                                  &objectPathVTable[i], NULL);
//            dbus_connection_register_object_path(conn2,
//                                                 objectPaths[i],
//                                                 &objectPathVTable[i], NULL);
        }
        else
        {
            dbus_connection_register_object_path(conn,
                                                 objectPaths[i],
                                                 &objectPathVTable[i], NULL);

        }
    }

    printf("main: registered %d objectHandlers.\n", i);

    //4. main loop: wait and process incoming msgs
    // there are several steps :

    //    a. check if any watch is ready for read (incoming data prepared)

    //    or write (outgoing data prepared), and process it.

    //    b. check if any timeout occurred ( i know now method_call that

    //    needs a reply will set a timeout ), and process it.

    //    c. call dispatch will do:

    //        i. parse incoming raw data if has.

    //        ii. process any pending call ( bind with a reply message );

    //        ii. call any filter registered.

    //        iv. call any object path handler registered.

    //        ps: a single dispatch call processes at most one message.

    int isockfd = wait_socket(5125);

    while (true)
    {
        DBusDispatchStatus status;
        watchHandler(isockfd);
        timeoutHandle();
        status = dbus_connection_dispatch(conn);

        switch (status)
        {
            case DBUS_DISPATCH_DATA_REMAINS:
                printf("DBUS_DISPATCH_DATA_REMAINS\n");
                continue;
            case DBUS_DISPATCH_COMPLETE:
                break;
            case DBUS_DISPATCH_NEED_MEMORY:
                break;
            default:
                break;
        }
        send_random_signal(conn);
        send_random_method_expecting_reply(conn);
        sleep(2);
    }
    return 0;
}

int wait_socket(int port)
{
    int sockfd, portno;
    struct sockaddr_in serv_addr;

    sockfd = socket(AF_INET, SOCK_STREAM|O_NONBLOCK, 0);
    if (sockfd < 0)
    {
        printf("ERROR opening socket\n");
        return -1;
    }
    cout<<"create socket\n";
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))<0)
    {
        printf("error in set sockopt\n");
        return -1;
    }
    int on = 1;
    // set socket to be nonblocking
    int rc = ioctl(sockfd, FIONBIO, (char*)&on);
    if (rc < 0)
    {
        perror("ioctl");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    //initialize serv_addr to zeros
    bzero((char*)&serv_addr, sizeof(serv_addr));

    portno = port;//*(int *)arg;//atoi(argv[1]);

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(portno);
    serv_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        printf("Error on binding\n");
        return -1;
    }
    cout<<"bind socket\n";

    listen(sockfd, 5);
    cout<<"listen socket\n";

    return sockfd;
}

int main(int argc,  char* argv[])
{
    int sockfd = 0;
    cout<<"sockfd is "<<sockfd<<endl;
    dbus_process_msg_loop(sockfd);
    return 0;
}

一个select同时监听socket和dbus。

 

g++ -o myserver main.cpp -ldbus-1

参考以下文章:http://bbs.chinaunix.net/thread-2004983-1-1.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!