390e9c5b0c94c4aef05bea8165e2af870a1b79fd
[ipc/ipcdev.git] / linux / src / utils / LAD_client.c
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) && (PLATFORM_SDK_VERSION < 23)
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;
110 /*
111  *  ======== LAD_connect ========
112  */
113 LAD_Status LAD_connect(LAD_ClientHandle * handle)
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);
235 /*
236  *  ======== LAD_disconnect ========
237  */
238 LAD_Status LAD_disconnect(LAD_ClientHandle handle)
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);
300 /*
301  *  ======== LAD_getResponse ========
302  */
303 LAD_Status LAD_getResponse(LAD_ClientHandle handle, union LAD_ResponseObj *rsp)
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);
326 /*
327  *  ======== LAD_putCommand ========
328  */
329 LAD_Status LAD_putCommand(struct LAD_CommandObj *cmd)
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);
364 /*
365  *  ======== initWrappers ========
366  */
367 static LAD_Status initWrappers(Void)
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     }
386 /*
387  *  ======== openCommandFIFO ========
388  */
389 static Bool openCommandFIFO(Void)
391     time_t currentTime;
392     time_t startTime;
393     struct stat statBuf;
394     double delta = 0;
395     int commandFIFOFd = 0;
396     int flags;
398     startTime = time ((time_t *) 0);
399     while (delta <= LAD_CONNECTTIMEOUT) {
400         /* check if FIFO exists. LAD daemon will be creating it,
401          * we don't want the client to create it */
402         if (stat(commandFIFOFileName, &statBuf) == 0) {
403             /* open a file for writing to FIFO, non-blocking */
404             commandFIFOFd = open(commandFIFOFileName, O_WRONLY | O_TRUNC | O_NONBLOCK);
405             if (commandFIFOFd != -1) {
406                 close(commandFIFOFd);
407                 break;
408             }
409         }
410         PRINTVERBOSE0("\nLAD_connect: LAD is not yet running, will retry\n")
411         usleep(100);
412         currentTime = time ((time_t *) 0);
413         delta = difftime(currentTime, startTime);
414     }
416     if (delta > LAD_CONNECTTIMEOUT) {
417         PRINTVERBOSE0("\nERROR: timed out waiting for LAD to be started\n");
418         return(FALSE);
419     }
421     /* open a file for writing to FIFO */
422     commandFIFOFilePtr = fopen(commandFIFOFileName, "w");
424     if (commandFIFOFilePtr == NULL) {
425         PRINTVERBOSE2("\nERROR: failed to open %s, errno = %x\n",
426             commandFIFOFileName, errno)
427         return(FALSE);
428     }
430     /* make sure FIFO fd doesn't exist for 'fork() -> exec*()'ed child */
431     flags = fcntl(fileno(commandFIFOFilePtr), F_GETFD);
432     if (flags != -1) {
433         fcntl(fileno(commandFIFOFilePtr), F_SETFD, flags | FD_CLOEXEC);
434     }
436     return(TRUE);