1/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
2 * @{
3 * @file
4 *
5 * Connection data.
6 *
7 * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote
18 *    products derived from this software without specific prior
19 *    written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33#include <unistd.h>
34#include <assert.h>
35#include <cstring>
36#include <errno.h>
37
38#include "Connection.h"
39
40//#define LOG_VERBOSE
41#include "log.h"
42
43
44//------------------------------------------------------------------------------
45Connection::Connection(void)
46{
47    connectionData = NULL;
48    // Set invalid socketDescriptor
49    socketDescriptor = -1;
50}
51
52
53//------------------------------------------------------------------------------
54Connection::Connection(int socketDescriptor, sockaddr_un *remote)
55{
56    assert(NULL != remote);
57    assert(-1 != socketDescriptor);
58
59    this->socketDescriptor = socketDescriptor;
60    this->remote = *remote;
61    connectionData = NULL;
62}
63
64
65//------------------------------------------------------------------------------
66Connection::~Connection(void)
67{
68    LOG_V(" closing Connection...");
69    if (socketDescriptor != -1)
70        close(socketDescriptor);
71    LOG_I(" Socket connection closed.");
72}
73
74
75//------------------------------------------------------------------------------
76bool Connection::connect(const char *dest)
77{
78    int32_t len;
79
80    assert(NULL != dest);
81
82    LOG_I(" Connecting to %s socket", dest);
83    remote.sun_family = AF_UNIX;
84    strncpy(remote.sun_path, dest, sizeof(remote.sun_path) - 1);
85    if ((socketDescriptor = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
86        LOG_ERRNO("Can't open stream socket.");
87        return false;
88    }
89    len = strlen(remote.sun_path) + sizeof(remote.sun_family);
90    // The Daemon socket is in the Abstract Domain(LINUX ONLY!)
91    remote.sun_path[0] = 0;
92    if (::connect(socketDescriptor, (struct sockaddr *) &remote, len) < 0) {
93        LOG_ERRNO("connect()");
94        return false;
95    }
96    return true;
97}
98
99
100//------------------------------------------------------------------------------
101size_t Connection::readData(void *buffer, uint32_t len)
102{
103    return readData(buffer, len, -1);
104}
105
106
107//------------------------------------------------------------------------------
108size_t Connection::readData(void *buffer, uint32_t len, int32_t timeout)
109{
110    size_t ret = 0;
111    struct timeval tv;
112    struct timeval *ptv = NULL;
113    fd_set readfds;
114
115    assert(NULL != buffer);
116    assert(socketDescriptor != -1);
117
118    if (timeout >= 0) {
119        // Calculate timeout value
120        tv.tv_sec = timeout / 1000;
121        tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
122        ptv = &tv;
123    }
124
125    FD_ZERO(&readfds);
126    FD_SET(socketDescriptor, &readfds);
127    ret = select(socketDescriptor + 1, &readfds, NULL, NULL, ptv);
128
129    // check for read error
130    if ((int)ret == -1) {
131        LOG_ERRNO("select");
132        return -1;
133    }
134
135    // Handle case of no descriptor ready
136    if (0 == ret) {
137        LOG_W(" Timeout during select() / No more notifications.");
138        return -2;
139    }
140
141    // one or more descriptors are ready
142
143    // finally check if fd has been selected -> must socketDescriptor
144    if (!FD_ISSET(socketDescriptor, &readfds)) {
145        LOG_ERRNO("no fd is set, select");
146        return ret;
147    }
148
149    ret = recv(socketDescriptor, buffer, len, MSG_DONTWAIT);
150    if (ret == 0) {
151        LOG_V(" readData(): peer orderly closed connection.");
152    }
153
154    return ret;
155}
156
157
158//------------------------------------------------------------------------------
159size_t Connection::writeData(void *buffer, uint32_t len)
160{
161    size_t ret;
162
163    assert(NULL != buffer);
164    assert(-1 != socketDescriptor);
165
166    ret = send(socketDescriptor, buffer, len, 0);
167    if (ret != len) {
168        LOG_ERRNO("could not send all data, because send");
169        LOG_E("ret = %d", ret);
170        ret = -1;
171    }
172
173    return ret;
174}
175
176
177//------------------------------------------------------------------------------
178int Connection::waitData(int32_t timeout)
179{
180    size_t ret;
181    struct timeval tv;
182    struct timeval *ptv = NULL;
183    fd_set readfds;
184
185    assert(socketDescriptor != -1);
186
187    if (timeout >= 0) {
188        // Calculate timeout value
189        tv.tv_sec = timeout / 1000;
190        tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
191        ptv = &tv;
192    }
193
194    FD_ZERO(&readfds);
195    FD_SET(socketDescriptor, &readfds);
196    ret = select(socketDescriptor + 1, &readfds, NULL, NULL, ptv);
197
198    // check for read error
199    if ((int)ret == -1) {
200        LOG_ERRNO("select");
201        return ret;
202    } else if (ret == 0) {
203        LOG_E("select() timed out");
204        return -1;
205    }
206
207    return 0;
208}
209
210/** @} */
211