2 Copyright (c) 2014, McAfee, Inc.
6 Redistribution and use in source and binary forms, with or without modification,
7 are permitted provided that the following conditions are met:
9 Redistributions of source code must retain the above copyright notice, this list
10 of conditions and the following disclaimer.
12 Redistributions in binary form must reproduce the above copyright notice, this
13 list of conditions and the following disclaimer in the documentation and/or other
14 materials provided with the distribution.
16 Neither the name of McAfee, Inc. nor the names of its contributors may be used
17 to endorse or promote products derived from this software without specific prior
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
28 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 OF THE POSSIBILITY OF SUCH DAMAGE.
34 * \brief Ipc Client Source File
36 * This file implements the IPC Client API functions used by Security framework.
39 #include <dbus/dbus.h>
44 #include <sys/types.h>
48 #include "IpcClient.h"
49 #include "IpcStructs.h"
50 #include "TSCErrorCodes.h"
52 static DBusHandlerResult _IpcClientMsgFilter(DBusConnection *dbconn, DBusMessage *dbmsg, void *data);
53 static bool _IpcClientInit(IpcClientInfo *pInfo);
54 static void _IpcClientDeInit(IpcClientInfo *pInfo);
55 static void _IpcHandleAsyncReply(DBusPendingCall *pPendingCall, void *pThreadData);
56 static void _free_str_array(char*** arr, int size);
59 * Initializes and returns handle to client side IPC.
61 TSC_IPC_HANDLE IpcClientOpen(void)
63 IpcClientInfo *pInfo = NULL;
64 pInfo = calloc(1, sizeof(IpcClientInfo));
68 snprintf(pInfo->pid, TSC_REQ_STR_LEN, "%d", getpid());
69 snprintf(pInfo->req_name, TSC_REQ_STR_LEN, "%s%d", TSC_DBUS_CLIENT, getpid());
72 if (!_IpcClientInit(pInfo))
75 return (TSC_IPC_HANDLE)pInfo;
81 return INVALID_IPC_HANDLE;
85 * Close the client-side IPC and release the resources.
87 void IpcClientClose(TSC_IPC_HANDLE hIpc)
89 if (hIpc == INVALID_IPC_HANDLE)
92 IpcClientInfo *pInfo = (IpcClientInfo *)hIpc;
93 _IpcClientDeInit(pInfo);
98 * Gives the prefix for the unique id of message sent.
100 char *_GetMsgIdPrefix(void)
102 char szIdPrefix[MSGHANDLE_LEN] = {0};
103 if (snprintf(szIdPrefix, MSGHANDLE_LEN, TSC_MID_PREFIX_FORMAT, getpid(), pthread_self()) >= 0)
104 return strdup(szIdPrefix);
110 * Requests the Security framework's IPC server and returns back the reply.
112 int TSCSendMessageN(TSC_IPC_HANDLE hIpc, const char *service_name, const char *szMethod, int argc,
113 char **argv, int *argc_reply, char ***argv_reply, int timeout_milliseconds)
115 IpcClientInfo *pInfo = (IpcClientInfo *)hIpc;
116 DBusMessage *dbmsg = NULL;
117 DBusMessage *reply_msg = NULL;
118 DBusMessageIter dbiter;
120 char *pArgItem = NULL;
124 int iErr = TSC_ERROR_MODULE_GENERIC;
125 int iSize = TSC_REPLY_MSG_COUNT_AVERAGE;
129 // TODO: Avoid multiple exits.
131 return TSC_ERROR_INVALID_HANDLE;
133 if (pInfo->dbconn == NULL)
134 return TSC_ERROR_INVALID_HANDLE;
136 if (argc < 0 || !szMethod)
137 return TSC_ERROR_INVALID_PARAM;
139 *argv_reply = malloc(iSize * sizeof(char *));
142 iErr = TSC_ERROR_INSUFFICIENT_RES;
146 dbus_error_init(&dberr);
148 dbmsg = dbus_message_new_method_call(service_name, TSC_DBUS_PATH, TSC_DBUS_INTERFACE, szMethod);
151 iErr = TSC_ERROR_INSUFFICIENT_RES;
154 dbus_message_iter_init_append(dbmsg, &dbiter);
156 // Add the message unique id as first element (the prefix).
157 char *szIdPrefix = _GetMsgIdPrefix();
158 if (!szIdPrefix || !dbus_message_iter_append_basic(&dbiter, DBUS_TYPE_STRING, &szIdPrefix))
160 DDBG("client_lib: Failed to append id prefix for %s.\n", szMethod);
165 for (i = 0; i < argc; i++)
167 if (!dbus_validate_utf8(argv[i], &dberr))
169 DDBG("%s", "client_lib: Not valid utf8 string\n");
172 if (!dbus_message_iter_append_basic(&dbiter, DBUS_TYPE_STRING, &argv[i]))
174 DDBG("client_lib: %s failed to append arguments\n", pInfo->pid);
179 if (timeout_milliseconds == 0)
180 timeout_milliseconds = DBUS_TIMEOUT_INFINITE;
182 reply_msg = dbus_connection_send_with_reply_and_block(pInfo->dbconn, dbmsg,
183 timeout_milliseconds,
186 if (reply_msg == NULL && dbus_error_is_set(&dberr))
188 DDBG("client_lib: Failed to end %s\n", dberr.message);
192 if (reply_msg == NULL)
194 DDBG("%s\n", "client_lib: reply message is NULL");
199 if (!dbus_message_iter_init(reply_msg, &dbiter))
201 DDBG("%s\n", "client_lib: Message has no arguments.");
207 if (dbus_message_iter_get_arg_type(&dbiter) != DBUS_TYPE_STRING)
209 DDBG("client_lib: %s argument is not string\n", pInfo->pid);
213 dbus_message_iter_get_basic(&dbiter, &pArgItem);
216 DDBG("client_lib: %s arg is NULL\n", pInfo->pid);
220 if (!(pBuf = strdup((const char*)pArgItem)))
224 (*argv_reply)[j++] = pBuf;
228 iSize += TSC_REPLY_MSG_COUNT_AVERAGE;
229 *argv_reply = realloc(*argv_reply, sizeof(char*) * iSize);
235 } while (dbus_message_iter_has_next(&dbiter) && dbus_message_iter_next(&dbiter));
245 free((*argv_reply)[--j]);
251 dbus_message_unref(dbmsg);
253 dbus_message_unref(reply_msg);
259 * Block current thread till message is sent and structure is updated in child thread.
261 void BlockTillSent(SharedData *pSharedData)
263 pthread_mutex_lock(&pSharedData->lock);
264 while (pSharedData->iSent == 0)
265 pthread_cond_wait(&pSharedData->cond, &pSharedData->lock);
266 pthread_mutex_unlock(&pSharedData->lock);
270 * Unblock parent thread as the message structures have been updated.
271 * Unblocks only 1 thread associated with the condition variable.
272 * Broadcast avoided as only waiting thread is expected.
274 void UnblockOnSent(SharedData *pSharedData)
276 pthread_mutex_lock(&pSharedData->lock);
277 pSharedData->iSent = 1;
278 pthread_cond_signal(&pSharedData->cond);
279 pthread_mutex_unlock(&pSharedData->lock);
283 * Creates a thread for asynchronous task.
284 * TODO: Merge with TCS module.
286 int _RunDetachedThread(void *pfWorkerFunc, void *pThreadData)
294 if (pthread_attr_init(&attr) != 0)
297 if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
300 if (pthread_create(&thread, &attr, pfWorkerFunc, pThreadData) != 0)
303 if (pthread_attr_destroy(&attr) != 0)
305 // As the thread is already running, return different error code.
317 void *_SendMessageWorker(void *pData)
320 DBusPendingCall *pPendingCall = NULL;
321 ThreadData *pThreadData = (ThreadData *) pData;
322 ThreadData *pPendingData = NULL;
327 if (pThreadData == NULL)
329 DDBG("%s\n", "Nothing to send from thread. Deadlock!!");
333 // Duplicate the User data to create the pending User data.
334 pPendingData = _AllocThreadData(NULL, DEF_TIMEOUT, NULL, pThreadData->pCallBack,
335 pThreadData->pPrivate);
336 if (pPendingData == NULL)
338 DDBG("%s\n", "Duplicating user data in thread failed. Deadlock!!");
342 // Ownership of pThreadData remains with 'parent' thread.
343 result = dbus_connection_send_with_reply(pThreadData->pConn, pThreadData->pSharedData->pMsg,
344 &pPendingCall, pThreadData->timeout_milliseconds);
345 if (!result || !pPendingCall)
347 DDBG("%s\n", "client_lib: SendAsync failed.");
351 if (!pPendingData->pCallBack)
352 dbus_pending_call_cancel(pPendingCall);
354 dbus_pending_call_set_notify(pPendingCall, _IpcHandleAsyncReply, pPendingData,
357 // Set the values for parent thread to allow cancellation or further async action if needed.
358 if (_CreateClientCallHandle(&pThreadData->pSharedData->pCallHandle,
359 pThreadData->pSharedData->szCallHandlePrefix,
360 dbus_message_get_serial(pThreadData->pSharedData->pMsg),
363 dbus_pending_call_cancel(pPendingCall);
367 UnblockOnSent(pThreadData->pSharedData);
368 // WARNING: Do not use pThreadData beyond this point, as parent thread may destroy it.
370 dbus_pending_call_block(pPendingCall);
376 if (iSentFailed && pThreadData)
378 DDBG("%s\n", "Error in send messsage worker. Unblocking..");
379 UnblockOnSent(pThreadData->pSharedData);
382 DDBG("%s\n", "Finished send message worker.");
388 * Requests the Security framework's IPC server asynchronously.
390 int TSCSendMessageAsync(TSC_IPC_HANDLE hIpc, const char *service_name, const char *szMethod,
391 int argc, char **argv, TSC_CALL_HANDLE *phCallHandle, TSCCallback pCallback,
392 void *pPrivate, int timeout_milliseconds)
394 DDBG("%s %s\n", "sendmessage async ", service_name);
395 IpcClientInfo *pInfo = (IpcClientInfo *)hIpc;
396 DBusMessage *pMsg = NULL;
397 DBusMessageIter dbiter;
399 ThreadData *pThreadData = NULL;
401 int iErr = TSC_ERROR_MODULE_GENERIC;
403 // TODO: Avoid multiple exits.
405 return TSC_ERROR_INVALID_HANDLE;
407 if (pInfo->dbconn == NULL)
408 return TSC_ERROR_INVALID_HANDLE;
410 if (argc < 0 || !szMethod)
411 return TSC_ERROR_INVALID_PARAM;
413 dbus_error_init(&dberr);
414 pMsg = dbus_message_new_method_call(service_name, TSC_DBUS_PATH, TSC_DBUS_INTERFACE, szMethod);
418 iErr = TSC_ERROR_INSUFFICIENT_RES;
422 dbus_message_iter_init_append(pMsg, &dbiter);
424 // Add the message unique id as first element (the prefix).
425 char *szIdPrefix = _GetMsgIdPrefix();
426 if (!szIdPrefix || !dbus_message_iter_append_basic(&dbiter, DBUS_TYPE_STRING, &szIdPrefix))
428 DDBG("client_lib: Failed to append id prefix for %s.\n", szMethod);
433 for (i = 0; i < argc; i++)
435 if (!dbus_validate_utf8(argv[i], &dberr))
437 DDBG("%s", "client_lib: Not valid utf8 string\n");
440 if (!dbus_message_iter_append_basic(&dbiter, DBUS_TYPE_STRING, &argv[i]))
442 DDBG("client_lib: %s failed to append arguments\n", pInfo->pid);
447 // Reply is discarded and not sent back to client, if no callback available.
449 dbus_message_set_no_reply(pMsg, TRUE);
451 SharedData *pSharedData = _CreateSharedData(szIdPrefix, pMsg);
456 iErr = TSC_ERROR_INSUFFICIENT_RES;
460 if (timeout_milliseconds == 0)
461 timeout_milliseconds = DBUS_TIMEOUT_INFINITE;
463 pThreadData = _AllocThreadData(pInfo->dbconn, timeout_milliseconds, pSharedData, pCallback,
467 _FreeSharedData(pSharedData);
468 iErr = TSC_ERROR_INSUFFICIENT_RES;
472 // Assert that the message is ready to be used.
473 if (pThreadData->pSharedData->iSent)
475 DDBG("%s\n", "client_lib: Sent flag already set!!");
479 DDBG("Before sending: %d\n", dbus_message_get_serial(pMsg));
480 if (_RunDetachedThread(_SendMessageWorker, pThreadData) == -1)
482 DDBG("%s\n", "client_lib: Running thread failed!!");
486 BlockTillSent(pThreadData->pSharedData);
487 DDBG("After sending: %d\n", dbus_message_get_serial(pMsg));
491 *phCallHandle = (TSC_CALL_HANDLE)pThreadData->pSharedData->pCallHandle;
492 //TODO: should add here
493 DDBG("method unique id :%s\n", ((ClientCallHandle*) phCallHandle)->idUnique);
494 strncpy(pThreadData->pSharedData->pCallHandle->service_name, service_name, TSC_SERVER_NAME_LEN);
498 // The caller does not want to call special methods (cancel, get progress) later.
499 _FreeClientCallHandle(pThreadData->pSharedData->pCallHandle);
500 pThreadData->pSharedData->pCallHandle = NULL;
507 dbus_message_unref(pMsg);
510 _FreeThreadData(pThreadData);
516 * Releases the asynchronous call handle.
518 void TSCFreeSentMessageHandle(TSC_CALL_HANDLE hCallHandle)
520 _FreeClientCallHandle((ClientCallHandle *)hCallHandle);
524 * Callback when reply is received from IPC server for asynchronous method call.
526 static void _IpcHandleAsyncReply(DBusPendingCall *pPendingCall, void *pThreadData)
532 char *pArgItem = NULL;
534 DBusMessageIter dbiter;
535 DBusMessage *pMsg = NULL;
536 int iErr = TSC_ERROR_MODULE_GENERIC;
537 int iSize = TSC_REPLY_MSG_COUNT_AVERAGE;
538 ThreadData *pData = (ThreadData *)pThreadData;
540 argv = malloc(iSize * sizeof(char *));
543 iErr = TSC_ERROR_INSUFFICIENT_RES;
547 dbus_error_init(&dberr);
549 pMsg = dbus_pending_call_steal_reply(pPendingCall);
552 DDBG("%s\n", "client_lib: reply message is NULL");
557 if (!dbus_message_iter_init(pMsg, &dbiter))
559 DDBG("%s\n", "client_lib: Async reply has no arguments");
565 if (dbus_message_iter_get_arg_type(&dbiter) != DBUS_TYPE_STRING)
567 DDBG("%s\n", "client_lib: Reply argument is not string");
571 dbus_message_iter_get_basic(&dbiter, &pArgItem);
574 DDBG("%s\n", "client_lib: Failed getting string arg from reply");
578 if (!(pBuf = strdup((const char*)pArgItem)))
586 iSize += TSC_REPLY_MSG_COUNT_AVERAGE;
587 argv = realloc(argv, sizeof(char*) * iSize);
593 } while (dbus_message_iter_has_next(&dbiter) && dbus_message_iter_next(&dbiter));
596 // Continue passing the returned values to callback.
600 // Reset values being returned.
601 _free_str_array(&argv, i);
605 dbus_message_unref(pMsg);
607 dbus_pending_call_unref(pPendingCall);
609 if (pData && pData->pCallBack)
610 (*(pData->pCallBack))(pData->pPrivate, argc, (const char**)argv);
612 _free_str_array(&argv, i);
614 DDBG("%s\n", "client_lib: Async Reply Completed.");
619 * Cancels an asynchronous request previously made to the Security framework's IPC server.
620 * On success, releases the handle of the previously called asynchronous method.
622 int TSCCancelMessage(TSC_IPC_HANDLE hIpc, TSC_CALL_HANDLE hCallHandle)
624 IpcClientInfo *pInfo = (IpcClientInfo *)hIpc;
625 ClientCallHandle *pCallHandle = (ClientCallHandle *)hCallHandle;
631 DDBG("%s\n", "client_lib: CANCELing.");
632 if (!pInfo || !pCallHandle)
635 DDBG("%s\n", "prepare cancel");
636 // Cancel the message call locally.
637 dbus_pending_call_cancel(pCallHandle->pPendingCall);
639 // Now request the server to abort the running of the method.
640 argv_req[0] = pCallHandle->idUnique;
641 DDBG("cancel method unique id:%s\n", argv_req[0]);
642 //TODO: need change here for cancel message
644 TSC_CALL_HANDLE handle = NULL;
645 iResult = TSCSendMessageAsync((TSC_IPC_HANDLE)pInfo, pCallHandle->service_name,
646 TSC_FN_CANCELMETHOD, 1, argv_req, &handle, NULL, NULL,
648 DDBG("%s\n", "client_lib: Sent Cancel to server.");
652 _FreeClientCallHandle(pCallHandle);
660 static DBusHandlerResult _IpcClientMsgFilter(DBusConnection *dbconn, DBusMessage *dbmsg, void *data)
662 IpcClientInfo *info = (IpcClientInfo*) data;
663 if (dbus_message_is_signal(dbmsg, DBUS_INTERFACE_LOCAL, "Disconnected"))
665 DDBG("client_lib: %s disconnected by signal\n", info->pid);
667 return DBUS_HANDLER_RESULT_HANDLED;
670 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
673 static bool _IpcClientInit(IpcClientInfo *pInfo)
681 if (!dbus_threads_init_default())
683 DDBG("failed dbus_threads_init_default %s\n", "");
687 dbus_error_init(&dberr);
690 if (pInfo->dbconn != NULL)
693 pInfo->dbconn = dbus_bus_get(DBUS_BUS_SYSTEM, &dberr);
695 if (pInfo->dbconn == NULL && dbus_error_is_set(&dberr))
697 DDBG("client_lib: %s error in get connection %s\n", pInfo->pid, dberr.message);
703 DDBG("client_lib: %s get connection is NULL\n", pInfo->pid);
707 dbus_connection_set_exit_on_disconnect(pInfo->dbconn, false);
709 if (!dbus_connection_add_filter(pInfo->dbconn, _IpcClientMsgFilter, pInfo, NULL))
711 DDBG("client_lib: %s failed to add filter %s\n", pInfo->pid, dberr.message);
715 ret = dbus_bus_request_name(pInfo->dbconn, pInfo->req_name, DBUS_NAME_FLAG_REPLACE_EXISTING, &dberr);
717 if (ret == -1 && dbus_error_is_set(&dberr))
719 DDBG("client_lib: %s failed to request name %s\n", pInfo->pid, dberr.message);
723 if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
725 DDBG("client_lib: %s failed, it is not primary owner %d\n", pInfo->req_name, ret);
729 dbus_error_free(&dberr);
735 dbus_connection_unref(pInfo->dbconn);
736 pInfo->dbconn = NULL;
738 dbus_error_free(&dberr);
744 static void _IpcClientDeInit(IpcClientInfo *pInfo)
755 dbus_error_init(&dberr);
756 ret = dbus_bus_release_name(pInfo->dbconn, pInfo->req_name, &dberr);
758 if (ret == -1 && dbus_error_is_set(&dberr))
759 DDBG("client_lib: %s failed to release name %s\n", pInfo->pid, dberr.message);
761 dbus_error_free(&dberr);
763 dbus_connection_remove_filter(pInfo->dbconn, _IpcClientMsgFilter, pInfo);
764 dbus_connection_unref(pInfo->dbconn);
765 pInfo->dbconn = NULL;
769 * Release all the elements of the array and assign it to NULL.
771 static void _free_str_array(char*** arr, int size)