hw.c

Go to the documentation of this file.
00001 /**
00002  * @file hw.c
00003  * @brief This file implements the device signal handler functions.
00004  */
00005  
00006 /*
00007  * This file is part of liberipc.
00008  *
00009  * liberipc is free software: you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation, either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * liberipc is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00021  */
00022 
00023 /**
00024  * Copyright (C) 2008 iRex Technologies B.V. 
00025  * All rights reserved.
00026  *
00027  * Based on code found in libosso library by Kimmo Hämäläinen <kimmo.hamalainen@nokia.com>
00028  */
00029 
00030 #include "internal.h"
00031 #include "eripc.h"
00032 #include <unistd.h>
00033 #include <errno.h>
00034 #include <assert.h>
00035 #include <string.h>
00036     
00037 static char *fix_name(const char* raw_name)
00038 {
00039     char *name = strdup(raw_name);
00040     if (name == NULL) {
00041         ULOG_CRIT_F("fix_name failed: out of memory");
00042         return NULL;
00043     }
00044     
00045     // replace dashes (-) with underscores (_) as dbus chokes on dashes
00046     unsigned int i;
00047     for (i=0; i<strlen(name); i++)
00048     {
00049         if (name[i] == '-')
00050             name[i] = '_';
00051     }
00052     return name;
00053 }    
00054 
00055 static void _get_byte_array(DBusMessageIter *iter, eripc_arg_t *arg)
00056 {
00057     arg->type = ERIPC_TYPE_DATA;
00058     dbus_message_iter_get_fixed_array(iter, &(arg->value.data),
00059                                       &(arg->data_len));
00060 }
00061 
00062 eripc_arg_t* _get_args(DBusMessageIter *iter)
00063 {
00064     int type, idx = 0;
00065     eripc_arg_t *arg_array;
00066 
00067     arg_array = calloc(1, sizeof(eripc_arg_t) * (ERIPC_MAX_ARGS + 1));
00068     if (arg_array == NULL) {
00069             ULOG_ERR_F("calloc() failed: %s", strerror(errno));
00070             return NULL;
00071     }
00072 
00073     while ((type = dbus_message_iter_get_arg_type(iter))
00074            != DBUS_TYPE_INVALID) {
00075 
00076             int i;
00077             unsigned int u;
00078             double d;
00079             char c, *s;
00080 
00081             switch (type) {
00082                     case DBUS_TYPE_BOOLEAN:
00083                             arg_array[idx].type = ERIPC_TYPE_BOOL; 
00084                             dbus_message_iter_get_basic(iter, &i);
00085                             arg_array[idx].value.b = i ? 1 : 0;
00086                             ++idx;
00087                             break;
00088                     case DBUS_TYPE_INT32:
00089                             arg_array[idx].type = ERIPC_TYPE_INT; 
00090                             dbus_message_iter_get_basic(iter, &i);
00091                             arg_array[idx].value.i = i;
00092                             ++idx;
00093                             break;
00094                     case DBUS_TYPE_UINT32:
00095                             arg_array[idx].type = ERIPC_TYPE_UINT; 
00096                             dbus_message_iter_get_basic(iter, &u);
00097                             arg_array[idx].value.u = u;
00098                             ++idx;
00099                             break;
00100                     case DBUS_TYPE_DOUBLE:
00101                             arg_array[idx].type = ERIPC_TYPE_DOUBLE; 
00102                             dbus_message_iter_get_basic(iter, &d);
00103                             arg_array[idx].value.d = d;
00104                             ++idx;
00105                             break;
00106                     case DBUS_TYPE_BYTE:
00107                             arg_array[idx].type = ERIPC_TYPE_BYTE; 
00108                             dbus_message_iter_get_basic(iter, &c);
00109                             arg_array[idx].value.by = c;
00110                             ++idx;
00111                             break;
00112                     case DBUS_TYPE_STRING:
00113                             arg_array[idx].type = ERIPC_TYPE_STRING; 
00114                             dbus_message_iter_get_basic(iter, &s);
00115                             arg_array[idx].value.s = s;
00116                             ++idx;
00117                             break;
00118                     case DBUS_TYPE_ARRAY:
00119                             /* only byte arrays are supported */
00120                             if (dbus_message_iter_get_element_type(iter)
00121                                 == DBUS_TYPE_BYTE) {
00122                                     _get_byte_array(iter,
00123                                                     &arg_array[idx]);
00124                                     ++idx;
00125                             } else {
00126                                     ULOG_ERR_F("arrays of type %d not"
00127                                                " supported",
00128                                     dbus_message_iter_get_element_type(iter));
00129                             }
00130                             break;
00131                     default:
00132                             ULOG_ERR_F("type %d not supported", type);
00133                             break;
00134             }
00135 
00136             dbus_message_iter_next(iter);
00137     }
00138 
00139     arg_array[idx].type = ERIPC_TYPE_INVALID;
00140 
00141     return arg_array;
00142 }
00143 
00144 
00145 static void generic_signal_handler(osso_context_t *osso,
00146                                    DBusMessage *msg,
00147                                    _osso_callback_data_t *data,
00148                                    eripc_bus_t dbus_type)
00149 {
00150     eripc_event_info_t info;
00151     DBusMessageIter iter;
00152     eripc_handler_t *cb;
00153     char id_buf[MAX_MSGID_LEN + 1];
00154 
00155     ULOG_DEBUG_F("entered");
00156     assert(osso != NULL && data != NULL);
00157 
00158     memset(&info, 0, sizeof(info));
00159 
00160     info.service = dbus_message_get_sender(msg);
00161     info.path = dbus_message_get_path(msg);
00162     info.interface = dbus_message_get_interface(msg);
00163     info.name = dbus_message_get_member(msg);
00164     info.event_type = data->event_type;
00165     info.bus_type = dbus_type;
00166 
00167     /* create a dummy reply message for later use (workaround for
00168      * D-Bus library API) */
00169     if (osso->reply_dummy == NULL &&
00170         dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL) {
00171         DBusMessage *reply;
00172         reply = dbus_message_new_method_return(msg);
00173         if (reply == NULL) {
00174             ULOG_WARN_F("could not create reply_dummy");
00175         } else {
00176             osso->reply_dummy = reply;
00177         }
00178     }
00179     if (osso->error_dummy == NULL &&
00180         dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL) {
00181         DBusMessage *reply;
00182         reply = dbus_message_new_error(msg, "org.foo.dummy", NULL);
00183         if (reply == NULL) {
00184             ULOG_WARN_F("could not create error_dummy");
00185         } else { 
00186             osso->error_dummy = reply;
00187         }
00188     }
00189 
00190     if ((dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL
00191         && !dbus_message_get_no_reply(msg)) ||
00192         dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
00193             _make_id(dbus_type, dbus_message_get_sender(msg),
00194                            dbus_message_get_serial(msg), id_buf);
00195             info.message_id = id_buf;
00196     }
00197 
00198     if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_ERROR) {
00199             info.error = dbus_message_get_error_name(msg);
00200     }
00201 
00202     if (dbus_message_iter_init(msg, &iter)) {
00203             info.args = _get_args(&iter);
00204     }
00205 
00206     cb = data->user_cb;
00207     (*cb)((eripc_context_t*)osso, &info, data->user_data);
00208 
00209     if (info.args != NULL) {
00210             free((void*) info.args);
00211             info.args = NULL;
00212     }
00213 }
00214 
00215 
00216 inline static eripc_error_t _set_handler(eripc_context_t *context,
00217                                          const char *service,
00218                                          const char *object_path,
00219                                          const char *interface,
00220                                          const char *name,
00221                                          const char *match,
00222                                          const char *sys_match,
00223                                          _osso_handler_f *event_cb,
00224                                          eripc_event_t event_type,
00225                                          eripc_handler_t *user_handler,
00226                                          void *user_data,
00227                                          int handler_id,
00228                                          gboolean call_once_per_handler_id,
00229                                          eripc_bus_t bus_type)
00230 {
00231         DBusError error;
00232         _osso_callback_data_t *cb_data;
00233 
00234         cb_data = calloc(1, sizeof(_osso_callback_data_t));
00235         if (cb_data == NULL) {
00236                 ULOG_ERR_F("calloc failed");
00237                 return ERIPC_ERROR_OOM;
00238         }
00239 
00240         cb_data->user_cb = user_handler;
00241         cb_data->user_data = user_data;
00242         cb_data->match_rule = match;
00243         cb_data->event_type = event_type;
00244         cb_data->bus_type = bus_type;
00245         cb_data->data = cb_data;
00246 
00247         if (service != NULL) {
00248                 cb_data->service = strdup(service);
00249                 if (cb_data->service == NULL) {
00250                         goto _set_handler_oom4;
00251                 }
00252         }
00253         if (object_path != NULL) {
00254                 cb_data->path = strdup(object_path);
00255                 if (cb_data->path == NULL) {
00256                         goto _set_handler_oom3;
00257                 }
00258         }
00259         if (interface != NULL) {
00260                 cb_data->interface = strdup(interface);
00261                 if (cb_data->interface == NULL) {
00262                         goto _set_handler_oom2;
00263                 }
00264         }
00265         if (name != NULL) {
00266                 cb_data->name = strdup(name);
00267                 if (cb_data->name == NULL) {
00268                         goto _set_handler_oom1;
00269                 }
00270         }
00271 
00272         dbus_error_init(&error);
00273         
00274         if (bus_type == ERIPC_BUS_SYSTEM || bus_type == ERIPC_BUS_BOTH) {
00275                 if (!dbus_connection_get_is_connected(context->sys_conn)) {
00276                         ULOG_ERR_F("connection to system bus is not open");
00277                         goto _set_handler_failed_match;
00278                 }
00279 
00280                 dbus_bus_add_match(context->sys_conn, sys_match, &error);
00281 
00282                 if (dbus_error_is_set(&error)) {
00283                         ULOG_ERR_F("dbus_bus_add_match failed: %s",
00284                                    error.message);
00285                         dbus_error_free(&error);
00286                         goto _set_handler_failed_match;
00287                 }
00288         }
00289 
00290         if (bus_type == ERIPC_BUS_SESSION || bus_type == ERIPC_BUS_BOTH) {
00291                 if (!dbus_connection_get_is_connected(context->conn)) {
00292                         ULOG_ERR_F("connection to session bus is not open");
00293                         goto _set_handler_failed_match;
00294                 }
00295 
00296                 dbus_bus_add_match(context->conn, match, &error);
00297 
00298                 if (dbus_error_is_set(&error)) {
00299                         ULOG_ERR_F("dbus_bus_add_match failed: %s",
00300                                    error.message);
00301                         dbus_error_free(&error);
00302 
00303                         if (bus_type == ERIPC_BUS_BOTH) {
00304                                 dbus_bus_remove_match(context->sys_conn,
00305                                                       match, NULL);
00306                         }
00307 
00308                         goto _set_handler_failed_match;
00309                 }
00310         }
00311 
00312         if (_eripc_set_handler((_eripc_context_t*)context, event_cb,
00313                                cb_data, handler_id,
00314                                call_once_per_handler_id)) {
00315                 return ERIPC_ERROR_SUCCESS;
00316         } else {
00317                 return ERIPC_ERROR;
00318         }
00319 
00320 _set_handler_oom1:
00321         free(cb_data->interface);
00322 _set_handler_oom2:
00323         free(cb_data->path);
00324 _set_handler_oom3:
00325         free(cb_data->service);
00326 _set_handler_oom4:
00327         free(cb_data);
00328         return ERIPC_ERROR_OOM;
00329 
00330 _set_handler_failed_match:
00331         free(cb_data->name);
00332         free(cb_data->interface);
00333         free(cb_data->path);
00334         free(cb_data->service);
00335         free(cb_data);
00336         return ERIPC_ERROR;
00337 }
00338 
00339 static eripc_error_t compose_match(const eripc_event_info_t *info,
00340                                    char **match)
00341 {
00342         size_t free_space = ERIPC_MAX_MATCH_SIZE;
00343         char buf[ERIPC_MAX_MATCH_SIZE + 1];
00344         buf[0] = '\0';
00345 
00346         *match = malloc(ERIPC_MAX_MATCH_SIZE + 1);
00347         if (*match == NULL) {
00348                 return ERIPC_ERROR_OOM;
00349         }
00350         (*match)[0] = '\0';
00351 
00352         if (info->service != NULL) {
00353                 snprintf(buf, ERIPC_MAX_MATCH_SIZE, "sender='%s',",
00354                          info->service);
00355                 strncat(*match, buf, free_space);
00356                 free_space -= strlen(buf);
00357         }
00358         
00359         if (info->path != NULL) {
00360                 snprintf(buf, ERIPC_MAX_MATCH_SIZE, "path='%s',", info->path);
00361                 strncat(*match, buf, free_space);
00362                 free_space -= strlen(buf);
00363         }
00364         if (info->interface != NULL) {
00365                 snprintf(buf, ERIPC_MAX_MATCH_SIZE, "interface='%s',",
00366                          info->interface);
00367                 strncat(*match, buf, free_space);
00368                 free_space -= strlen(buf);
00369         }
00370         if (info->name != NULL) {
00371                 snprintf(buf, ERIPC_MAX_MATCH_SIZE, "member='%s',",
00372                          info->name);
00373                 strncat(*match, buf, free_space);
00374                 free_space -= strlen(buf);
00375         }
00376         if (info->event_type != 0) {
00377                 switch(info->event_type)
00378                 {
00379                 case ERIPC_EVENT_MESSAGE:
00380                     snprintf(buf, ERIPC_MAX_MATCH_SIZE, "type='method_call',");
00381                     break;
00382                 case ERIPC_EVENT_SIGNAL:
00383                     snprintf(buf, ERIPC_MAX_MATCH_SIZE, "type='signal',");
00384                     break;
00385                 case ERIPC_EVENT_REPLY:
00386                     snprintf(buf, ERIPC_MAX_MATCH_SIZE, "type='method_reply',");
00387                     break;
00388                 case ERIPC_EVENT_ERROR:
00389                 default:
00390                     snprintf(buf, ERIPC_MAX_MATCH_SIZE, "type='error',");
00391                     break;
00392                 }
00393                 strncat(*match, buf, free_space);
00394                 free_space -= strlen(buf);
00395         }
00396 
00397         if (free_space < ERIPC_MAX_MATCH_SIZE) {
00398                 /* remove the last comma */
00399                 (*match)[strlen(*match) - 1] = '\0';
00400         } else {
00401                 free(*match);
00402                 *match = NULL;
00403         }
00404         
00405         return ERIPC_ERROR_SUCCESS;
00406 }
00407 
00408 
00409 eripc_error_t eripc_set_signal_handler(eripc_context_t *context,
00410                                        eripc_handler_t *handler,
00411                                        void *user_data,
00412                                        eripc_bus_t bus_type,
00413                                        const char *source,
00414                                        const char *name,
00415                                        int *handler_id)
00416 {
00417     eripc_error_t error = ERIPC_ERROR_SUCCESS;
00418     eripc_event_info_t info;
00419     
00420     char *interface = fix_name(source);
00421     memset(&info, 0, sizeof(info));
00422     info.event_type = ERIPC_EVENT_SIGNAL;
00423     info.bus_type = bus_type;
00424     info.interface = interface;
00425     info.name = name;
00426     
00427     error = eripc_set_event_handler(context, &info, 
00428                                     handler, user_data, handler_id);
00429     
00430     free(interface);
00431     
00432     return error;
00433 }
00434 
00435 
00436 eripc_error_t eripc_set_message_handler(eripc_context_t *context,
00437                                        eripc_handler_t *handler,
00438                                        void *user_data,
00439                                        eripc_bus_t bus_type,
00440                                        const char *source,
00441                                        const char *name,
00442                                        int *handler_id)
00443 {
00444     eripc_error_t error = ERIPC_ERROR_SUCCESS;
00445     eripc_event_info_t info;
00446     
00447     char *interface = fix_name(source);
00448     memset(&info, 0, sizeof(info));
00449     info.event_type = ERIPC_EVENT_MESSAGE;
00450     info.bus_type = bus_type;
00451     info.interface = interface;
00452     info.name = name;
00453     
00454     error = eripc_set_event_handler(context, &info, 
00455                                     handler, user_data, handler_id);
00456     
00457     free(interface);
00458     
00459     return error;
00460 }
00461 
00462 
00463 eripc_error_t eripc_set_event_handler(eripc_context_t *context,
00464                                       const eripc_event_info_t *info,
00465                                       eripc_handler_t *handler,
00466                                       void *user_data,
00467                                       int *handler_id)
00468 {
00469         eripc_error_t error = ERIPC_ERROR_SUCCESS;
00470         _osso_handler_f *event_cb = NULL;
00471         char *tmp_match = NULL, *match = NULL, *sys_match = NULL;
00472         int new_handler_id;
00473         eripc_error_t ret;
00474         eripc_bus_t bus_type;
00475 
00476         ULOG_DEBUG_F("entered");
00477 
00478         if (context == NULL || handler == NULL) {
00479                 ULOG_ERR_F("invalid arguments");
00480                 return ERIPC_ERROR_INVALID;
00481         }
00482 
00483         if (info == NULL) {
00484                 ULOG_ERR_F("info structure must be provided");
00485                 return ERIPC_ERROR_INVALID;
00486         }
00487 
00488         if (info->bus_type == ERIPC_BUS_IRRELEVANT) {
00489                 bus_type = ERIPC_BUS_BOTH;
00490         } else {
00491                 bus_type = info->bus_type;
00492         }
00493 
00494         new_handler_id = context->next_handler_id++;
00495 
00496         event_cb = generic_signal_handler;
00497 
00498         ret = compose_match(info, &tmp_match);
00499         ULOG_DEBUG_F("match='%s'", tmp_match);
00500         
00501         if (ret == ERIPC_ERROR_SUCCESS) {
00502                 if (bus_type == ERIPC_BUS_BOTH
00503                     || bus_type == ERIPC_BUS_SESSION) {
00504                         match = tmp_match;
00505                 }
00506                 if (bus_type == ERIPC_BUS_BOTH
00507                     || bus_type == ERIPC_BUS_SYSTEM) {
00508                         if (match == NULL) {
00509                                 sys_match = tmp_match;
00510                         } else {
00511                                 sys_match = strdup(tmp_match);
00512                                 if (sys_match == NULL) {
00513                                         ULOG_ERR_F("strdup failed");
00514                                         free(tmp_match);
00515                                         error = ERIPC_ERROR_OOM;
00516                                         goto _oom_exit;
00517                                 }
00518                         }
00519                 }
00520                 error = _set_handler(context,
00521                                      info->service,
00522                                      info->path,
00523                                      info->interface,
00524                                      info->name,
00525                                      match,
00526                                      sys_match,
00527                                      event_cb,
00528                                      info->event_type, // FIXME was 0, event_type ignored
00529                                      handler,
00530                                      user_data,
00531                                      new_handler_id,
00532                                      FALSE, bus_type);
00533         } else {
00534                 error = ret;
00535         }
00536 
00537 _oom_exit:
00538         if (handler_id != NULL) {
00539                 if (error == ERIPC_ERROR_SUCCESS) {
00540                         *handler_id = new_handler_id;
00541                 } else {
00542                         *handler_id = 0;
00543                 }
00544         }
00545         return error;
00546 }
00547 
00548 eripc_error_t eripc_unset_handler(eripc_context_t *context,
00549                                   int handler_id)
00550 {
00551         ULOG_DEBUG_F("entered");
00552 
00553         if (context == NULL || handler_id == 0) {
00554                 ULOG_ERR_F("invalid arguments");
00555                 return ERIPC_ERROR_INVALID;
00556         }
00557 
00558         if (_unset_handler((_eripc_context_t*)context, handler_id)) {
00559                 return ERIPC_ERROR_SUCCESS;
00560         } else {
00561                 /* id was not found */
00562                 return ERIPC_ERROR_INVALID;
00563         }
00564 }
Generated by  doxygen 1.6.2-20100208