1 /*
2 * Copyright (c) 2012-2014, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 /*
33 * ======== LAD_client.c ========
34 */
35 #include <ti/ipc/Std.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <unistd.h>
45 #include <time.h>
46 #include <pthread.h>
48 #include <ladclient.h>
49 #include <_lad.h>
52 /* traces in this file are controlled via _LAD_Client_verbose */
53 Bool _LAD_Client_verbose = FALSE;
54 #define verbose _LAD_Client_verbose
56 typedef struct _LAD_ClientInfo {
57 Bool connectedToLAD; /* connection status */
58 UInt PID; /* client's process ID */
59 Char responseFIFOName[LAD_MAXLENGTHFIFONAME]; /* response FIFO name */
60 FILE *responseFIFOFilePtr; /* FIFO file pointer */
61 } _LAD_ClientInfo;
63 static Bool initialized = FALSE;
64 static String commandFIFOFileName = LAD_COMMANDFIFO;
65 static FILE *commandFIFOFilePtr = NULL;
66 static _LAD_ClientInfo clientInfo[LAD_MAXNUMCLIENTS];
68 static LAD_Status initWrappers(Void);
69 static Bool openCommandFIFO(Void);
71 #if defined(IPC_BUILDOS_ANDROID)
72 static pthread_mutex_t modGate = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
73 #else
74 // only _NP (non-portable) type available in CG tools which we're using
75 static pthread_mutex_t modGate = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
76 #endif
79 /*
80 * LAD_findHandle() - finds the LAD_ClientHandle for the calling pid (process ID).
81 *
82 * Assumes that there is only one client per process, which has to be the
83 * case since the pid is used to construct the responseFIFOFileName.
84 *
85 * Multiple threads within a process can all connect since each thread gets
86 * its own pid (which might be an OS-specific thing, some OSes (even some
87 * Linux implementations) use the same process pid for every thread within
88 * a process).
89 *
90 * Returns either the found "handle", or LAD_MAXNUMCLIENTS if the handle
91 * can't be found.
92 */
93 LAD_ClientHandle LAD_findHandle(Void)
94 {
95 Int i;
96 Int pid;
98 pid = getpid();
100 for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
101 if (clientInfo[i].PID == pid &&
102 clientInfo[i].connectedToLAD == TRUE) {
103 break;
104 }
105 }
107 return i;
108 }
110 /*
111 * ======== LAD_connect ========
112 */
113 LAD_Status LAD_connect(LAD_ClientHandle * handle)
114 {
115 Char responseFIFOName[LAD_MAXLENGTHFIFONAME];
116 LAD_Status status = LAD_SUCCESS;
117 time_t currentTime;
118 time_t startTime;
119 struct stat statBuf;
120 double delta;
121 Int assignedId;
122 FILE * filePtr;
123 int flags;
124 Int n;
125 Int pid;
126 struct LAD_CommandObj cmd;
127 union LAD_ResponseObj rsp;
129 /* sanity check arg */
130 if (handle == NULL) {
131 return(LAD_INVALIDARG);
132 }
134 /* check and initialize on first connect request */
135 if (initialized == FALSE) {
137 /* TODO:M does this need to be atomized? */
138 status = initWrappers();
139 if (status != LAD_SUCCESS) {
140 return(status);
141 }
142 initialized = TRUE;
143 }
145 /* get caller's process ID */
146 pid = getpid();
148 /* form name for dedicated response FIFO */
149 sprintf(responseFIFOName, "%s%d", LAD_RESPONSEFIFOPATH, pid);
151 PRINTVERBOSE2("\nLAD_connect: PID = %d, fifoName = %s\n", pid,
152 responseFIFOName)
154 /* check if FIFO already exists; if yes, reject the request */
155 if (stat(responseFIFOName, &statBuf) == 0) {
156 PRINTVERBOSE0("\nLAD_connect: already connected; request denied!\n")
157 return(LAD_ACCESSDENIED);
158 }
160 cmd.cmd = LAD_CONNECT;
161 strcpy(cmd.args.connect.name, responseFIFOName);
162 strcpy(cmd.args.connect.protocol, LAD_PROTOCOLVERSION);
163 cmd.args.connect.pid = pid;
165 if ((status = LAD_putCommand(&cmd)) != LAD_SUCCESS) {
166 return(status);
167 }
169 /* now open the dedicated response FIFO for this client */
170 startTime = time ((time_t *) 0);
171 while ((filePtr = fopen(responseFIFOName, "r")) == NULL) {
172 /* insert wait to yield, so LAD can process connect command sooner */
173 usleep(100);
174 currentTime = time ((time_t *) 0);
175 delta = difftime(currentTime, startTime);
176 if (delta > LAD_CONNECTTIMEOUT) {
177 pthread_mutex_unlock(&modGate);
179 return(LAD_IOFAILURE);
180 }
181 }
183 /* make sure FIFO fd doesn't exist for 'fork() -> exec*()'ed child */
184 flags = fcntl(fileno(filePtr), F_GETFD);
185 if (flags != -1) {
186 fcntl(fileno(filePtr), F_SETFD, flags | FD_CLOEXEC);
187 }
189 /* now get LAD's response to the connection request */
190 n = fread(&rsp, LAD_RESPONSELENGTH, 1, filePtr);
192 /* need to unlock mutex obtained by LAD_putCommand() */
193 pthread_mutex_unlock(&modGate);
195 if (n) {
196 PRINTVERBOSE0("\nLAD_connect: got response\n")
198 /* extract LAD's response code and the client ID */
199 status = rsp.connect.status;
201 /* if a successful connect ... */
202 if (status == LAD_SUCCESS) {
203 assignedId = rsp.connect.assignedId;
204 *handle = assignedId;
206 /* setup client info */
207 clientInfo[assignedId].PID = pid;
208 clientInfo[assignedId].responseFIFOFilePtr = filePtr;
209 strcpy(clientInfo[assignedId].responseFIFOName, responseFIFOName);
210 clientInfo[assignedId].connectedToLAD = TRUE;
212 PRINTVERBOSE1(" status == LAD_SUCCESS, assignedId=%d\n",
213 assignedId);
214 }
215 else {
216 PRINTVERBOSE1(" status != LAD_SUCCESS (status=%d)\n", status);
217 }
218 }
219 else {
220 PRINTVERBOSE0(
221 "\nLAD_connect: 0 bytes read when getting LAD response!\n")
222 status = LAD_IOFAILURE;
223 }
225 /* if connect failed, close client side of FIFO (LAD closes its side) */
226 if (status != LAD_SUCCESS) {
227 PRINTVERBOSE0("\nLAD_connect failed: closing client-side of FIFO...\n")
228 fclose(filePtr);
229 }
231 return(status);
232 }
235 /*
236 * ======== LAD_disconnect ========
237 */
238 LAD_Status LAD_disconnect(LAD_ClientHandle handle)
239 {
240 LAD_Status status = LAD_SUCCESS;
241 Bool waiting = TRUE;
242 struct stat statBuf;
243 time_t currentTime;
244 time_t startTime;
245 double delta;
246 struct LAD_CommandObj cmd;
248 /* sanity check args */
249 if (handle >= LAD_MAXNUMCLIENTS) {
250 return (LAD_INVALIDARG);
251 }
253 /* check for initialization and connection */
254 if ((initialized == FALSE) ||
255 (clientInfo[handle].connectedToLAD == FALSE)) {
256 return (LAD_NOTCONNECTED);
257 }
259 cmd.cmd = LAD_DISCONNECT;
260 cmd.clientId = handle;
262 if ((status = LAD_putCommand(&cmd)) != LAD_SUCCESS) {
263 return(status);
264 }
266 /* on success, close the dedicated response FIFO */
267 fclose(clientInfo[handle].responseFIFOFilePtr);
269 /* need to unlock mutex obtained by LAD_putCommand() */
270 pthread_mutex_unlock(&modGate);
272 /* now wait for LAD to close the connection ... */
273 startTime = time ((time_t *) 0);
274 while (waiting == TRUE) {
276 /* do a minimal wait, to yield, so LAD can disconnect */
277 usleep(1);
278 currentTime = time ((time_t *) 0);
280 /* check to see if LAD has shutdown FIFO yet... */
281 if (stat(clientInfo[handle].responseFIFOName, &statBuf) != 0) {
282 waiting = FALSE; /* yes, so done */
283 }
284 /* if not, check for timeout */
285 else {
286 delta = difftime(currentTime, startTime);
287 if (delta > LAD_DISCONNECTTIMEOUT) {
288 PRINTVERBOSE0("\nLAD_disconnect: timeout waiting for LAD!\n")
289 return(LAD_IOFAILURE);
290 }
291 }
292 }
294 /* reset connection status flag */
295 clientInfo[handle].connectedToLAD = FALSE;
297 return(status);
298 }
300 /*
301 * ======== LAD_getResponse ========
302 */
303 LAD_Status LAD_getResponse(LAD_ClientHandle handle, union LAD_ResponseObj *rsp)
304 {
305 LAD_Status status = LAD_SUCCESS;
306 Int n;
308 PRINTVERBOSE1("LAD_getResponse: client = %d\n", handle)
310 n = fread(rsp, LAD_RESPONSELENGTH, 1,
311 clientInfo[handle].responseFIFOFilePtr);
313 pthread_mutex_unlock(&modGate);
315 if (n == 0) {
316 PRINTVERBOSE0("LAD_getResponse: n = 0!\n")
317 status = LAD_IOFAILURE;
318 }
319 else {
320 PRINTVERBOSE0("LAD_getResponse: got response\n")
321 }
323 return(status);
324 }
326 /*
327 * ======== LAD_putCommand ========
328 */
329 LAD_Status LAD_putCommand(struct LAD_CommandObj *cmd)
330 {
331 LAD_Status status = LAD_SUCCESS;
332 Int stat;
333 Int n;
335 PRINTVERBOSE1("\nLAD_putCommand: cmd = %d\n", cmd->cmd);
337 pthread_mutex_lock(&modGate);
339 n = fwrite(cmd, LAD_COMMANDLENGTH, 1, commandFIFOFilePtr);
341 if (n == 0) {
342 PRINTVERBOSE0("\nLAD_putCommand: fwrite returned 0!\n")
343 status = LAD_IOFAILURE;
344 }
345 else {
346 stat = fflush(commandFIFOFilePtr);
348 if (stat == (Int) EOF) {
349 PRINTVERBOSE0("\nLAD_putCommand: stat for fflush = EOF!\n")
350 status = LAD_IOFAILURE;
351 }
352 }
354 if (status != LAD_SUCCESS) {
355 pthread_mutex_unlock(&modGate);
356 }
358 PRINTVERBOSE1("LAD_putCommand: status = %d\n", status)
360 return(status);
361 }
364 /*
365 * ======== initWrappers ========
366 */
367 static LAD_Status initWrappers(Void)
368 {
369 Int i;
371 /* initialize the client info structures */
372 for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
373 clientInfo[i].connectedToLAD = FALSE;
374 clientInfo[i].responseFIFOFilePtr = NULL;
375 }
377 /* now open LAD's command FIFO */
378 if (openCommandFIFO() == FALSE) {
379 return(LAD_IOFAILURE);
380 }
381 else {
382 return(LAD_SUCCESS);
383 }
384 }
386 /*
387 * ======== openCommandFIFO ========
388 */
389 static Bool openCommandFIFO(Void)
390 {
391 int flags;
393 /* open a file for writing to FIFO */
394 commandFIFOFilePtr = fopen(commandFIFOFileName, "w");
396 if (commandFIFOFilePtr == NULL) {
397 PRINTVERBOSE2("\nERROR: failed to open %s, errno = %x\n",
398 commandFIFOFileName, errno)
399 return(FALSE);
400 }
402 /* make sure FIFO fd doesn't exist for 'fork() -> exec*()'ed child */
403 flags = fcntl(fileno(commandFIFOFilePtr), F_GETFD);
404 if (flags != -1) {
405 fcntl(fileno(commandFIFOFilePtr), F_SETFD, flags | FD_CLOEXEC);
406 }
408 return(TRUE);
409 }