c16708288a8c6edee110f5b505512eeb701ae694
[ipc/ipcdev.git] / linux / src / daemon / lad.c
1 /*
2  * Copyright (c) 2012-2013, 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.c ========
34  */
36 #include <ti/ipc/Std.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <signal.h>
46 #include <unistd.h>
48 #include <ti/ipc/MessageQ.h>
49 #include <_MessageQ.h>
50 #include <ti/ipc/NameServer.h>
51 #include <_NameServer.h>
53 #include <ladclient.h>
54 #include <_lad.h>
56 #define DAEMON        1           /* 1 = run as a daemon; 0 = run as app */
58 Bool logFile = FALSE;
59 FILE *logPtr = NULL;
61 static String commandFIFOFile = LAD_COMMANDFIFO;
62 static FILE *commandFIFOFilePtr = NULL;
63 static String serverDir;
65 /* LAD client info arrays */
66 static Bool clientConnected[LAD_MAXNUMCLIENTS];
67 static UInt clientPID[LAD_MAXNUMCLIENTS];
68 static Char clientFIFOName[LAD_MAXNUMCLIENTS][LAD_MAXLENGTHFIFONAME];
69 static FILE * responseFIFOFilePtr[LAD_MAXNUMCLIENTS];
71 /* local internal routines */
72 static LAD_ClientHandle assignClientId(Void);
73 static Void cleanupDepartedClients(Void);
74 static Int connectToLAD(String clientName, Int pid, String clientProto, Int *clientIdPtr);
75 static Void disconnectFromLAD(Int clientId);
76 static Void doDisconnect(Int clientId);
78 struct LAD_CommandObj cmd;
79 union LAD_ResponseObj rsp;
83 /*
84  *  ======== main ========
85  */
86 int main(int argc, char * argv[])
87 {
88     MessageQ_Handle handle;
89     UInt16 *procIdPtr;
90     Int statusIO;
91     Int clientId;
92     Int command;
93     Int flags;
94     Int i;
95     Int n;
96 #if DAEMON
97     pid_t pid;
98     pid_t sid;
99 #endif
101     /* if more than two args: turn "ON" launch status printfs */
102     if (argc > 2) {
103         printf("\nLAD starting up...");
104     }
106     /* check for env variable indicating server exe repository */
107     serverDir = getenv("LAD_SERVERPATH");
108     if (argc > 2) {
109         if (serverDir != NULL) {
110             printf("\nLAD_SERVERPATH = %s\n", serverDir);
111         }
112         else {
113             printf("\nLAD_SERVERPATH = <NULL>\n");
114         }
115     }
117     /* change to LAD's working directory */
118     if ((chdir(LAD_WORKINGDIR)) < 0) {
120         /* if can't change directory assume it needs to be created, do it */
121         if ((mkdir(LAD_WORKINGDIR, 0666)) < 0) {
122             printf("\nERROR: Failed to create LAD's working directory!\n");
123             exit(EXIT_FAILURE);
124         }
125         /* now change to the new directory */
126         if ((chdir(LAD_WORKINGDIR)) < 0) {
127             printf("\nERROR: Failed to change to LAD's working directory!\n");
128             exit(EXIT_FAILURE);
129         }
130     }
132     /* process command line args */
133     if (argc > 1) {
134         logPtr = fopen(argv[1], "w");
135         if (logPtr == NULL) {
136             printf("\nERROR: unable to open log file %s\n", argv[1]);
137             exit(EXIT_FAILURE);
138         }
139         else {
140             logFile = TRUE;
141             if (argc > 2) {
142                 printf("\nOpened log file: %s", argv[1]);
143             }
144             /* close log file upon LAD termination */
145             flags = fcntl(fileno(logPtr), F_GETFD);
146             if (flags != -1) {
147                 fcntl(fileno(logPtr), F_SETFD, flags | FD_CLOEXEC);
148             }
149         }
150     }
152 #if DAEMON
153     /* fork off a child process */
154     pid = fork();
156     /* if fork of child failed then exit immediately; no child created */
157     if (pid < 0) {
158         printf("\nERROR: Failed to fork child process!");
159         exit(EXIT_FAILURE);
160     }
162     /* if pid > 0 this is the parent; time to die ... */
163     if (pid > 0) {
164         if (argc > 2) {
165             printf("\nSpawned daemon: %s\n\n", argv[0]);
166         }
167         exit(EXIT_SUCCESS);
168     }
170     /* child continues from here (pid == 0) ... */
172     /* change file mode mask */
173     umask(0);
175     /* create new session ID for the child */
176     sid = setsid();
178     /* exit with failure code if failed to get session ID... */
179     if (sid < 0) {
180         printf("\nERROR: Failed to acquire new session ID!");
181         exit(EXIT_FAILURE);
182     }
184     /* disassociate from the standard file descriptors */
185     close(STDIN_FILENO);
186     close(STDOUT_FILENO);
187     close(STDERR_FILENO);
189 #endif
191     LOG0("\nInitializing LAD... ")
193     /* TODO:L make sure LAD is not already running? */
195     /* initialize client info arrays */
196     for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
197         clientConnected[i] = FALSE;
198         responseFIFOFilePtr[i] = NULL;
199     }
201     /* if command FIFO exists from previous LAD session delete it now */
202     unlink(commandFIFOFile);
204     /* create command FIFO */
205     statusIO = mkfifo(commandFIFOFile, 0777);
206     if (statusIO != 0) {
207         LOG2("\nERROR: unable to create %s, errno = %x\n", commandFIFOFile,
208             errno)
209         return(0);
210     }
212     /* set FIFO permissions to read/write */
213     chmod(commandFIFOFile, 0666);
215 opencommandFIFO:
217     /* now open file for FIFO - will block until writer arrives... */
218     LOG1("\n    opening FIFO: %s\n", commandFIFOFile)
219     commandFIFOFilePtr = fopen(commandFIFOFile, "r");
220     if (commandFIFOFilePtr == NULL) {
221         LOG0("\nERROR: unable to open command FIFO\n")
222         unlink(commandFIFOFile);
223         return(0);
224     }
226     /* COMMAND PROCESSING LOOP */
227     while (1) {
228         LOG0("Retrieving command...\n")
230         /* read the next command packet */
231         n = fread(&cmd, LAD_COMMANDLENGTH, 1, commandFIFOFilePtr);
233         /*
234          * if last client closes FIFO then it must be closed and reopened ...
235          */
236         if (!n) {
237             LOG1("    EOF detected on FIFO, closing FIFO: %s\n", commandFIFOFile)
238             fclose(commandFIFOFilePtr);
240             goto opencommandFIFO;
241         }
243         /* cleanup for any connected/started clients that have departed */
244         cleanupDepartedClients();
246         command = cmd.cmd;
247         clientId = cmd.clientId;
249         /* process individual commands */
250         switch (command) {
251           /*
252            * Since cmd is a union of rcv and snd structs, don't write
253            * any snd elements before all rcv elements have been referenced
254            * (either saved in separate variables or passed to a function).
255            *
256            * cmd.cmd has already been saved in 'command'
257            * cmd.clientId has already been saved in 'clientId'
258            */
259           case LAD_CONNECT:
260             connectToLAD(cmd.args.connect.name, cmd.args.connect.pid,
261                          cmd.args.connect.protocol, NULL);
263             break;
265           case LAD_DISCONNECT:
266             disconnectFromLAD(clientId);
268             break;
270           case LAD_NAMESERVER_SETUP:
271             LOG0("LAD_NAMESERVER_SETUP: calling NameServer_setup()...\n")
273             rsp.status = NameServer_setup();
275             LOG1("    status = %d\n", rsp.status)
276             LOG0("DONE\n")
278             break;
280           case LAD_NAMESERVER_DESTROY:
281             LOG0("LAD_NAMESERVER_DESTROY: calling NameServer_destroy()...\n")
283             rsp.status = NameServer_destroy();
285             LOG1("    status = %d\n", rsp.status)
286             LOG0("DONE\n")
288             break;
290           case LAD_NAMESERVER_PARAMS_INIT:
291             LOG0("LAD_NAMESERVER_PARAMS_INIT: calling NameServer_Params_init()...\n")
293             NameServer_Params_init(&rsp.params);
295             LOG0("DONE\n")
297             break;
299           case LAD_NAMESERVER_CREATE:
300             LOG1("LAD_NAMESERVER_CREATE: calling NameServer_create('%s')...\n", cmd.args.create.name)
302             rsp.handle = NameServer_create(cmd.args.create.name,
303                                                &cmd.args.create.params);
305             LOG1("    handle = %p\n", rsp.handle)
306             LOG0("DONE\n")
308             break;
310           case LAD_NAMESERVER_DELETE:
311             LOG1("LAD_NAMESERVER_DELETE: calling NameServer_delete(%p)...\n", cmd.args.delete.handle)
313             rsp.delete.handle = cmd.args.delete.handle;
314             rsp.delete.status = NameServer_delete(&rsp.delete.handle);
316             LOG1("    status = %d\n", rsp.status)
317             LOG0("DONE\n")
319             break;
321           case LAD_NAMESERVER_ADDUINT32:
322             LOG1("LAD_NAMESERVER_ADDUINT32: calling NameServer_addUInt32(%p, ", cmd.args.addUInt32.handle)
323             LOG2("'%s', 0x%x)...\n", cmd.args.addUInt32.name, cmd.args.addUInt32.val)
325             rsp.entryPtr = NameServer_addUInt32(
326                 cmd.args.addUInt32.handle,
327                 cmd.args.addUInt32.name,
328                 cmd.args.addUInt32.val);
330             LOG1("    entryPtr = %p\n", rsp.entryPtr)
331             LOG0("DONE\n")
333             break;
335           case LAD_NAMESERVER_GETUINT32:
336             LOG2("LAD_NAMESERVER_GETUINT32: calling NameServer_getUInt32(%p, '%s')...\n", cmd.args.getUInt32.handle, cmd.args.getUInt32.name)
338             if (cmd.args.getUInt32.procId[0] == (UInt16)-1) {
339                 procIdPtr = NULL;
340             }
341             else {
342                 procIdPtr = cmd.args.getUInt32.procId;
343             }
344             rsp.status = NameServer_getUInt32(
345                 cmd.args.getUInt32.handle,
346                 cmd.args.getUInt32.name,
347                 &rsp.getUInt32.val,
348                 procIdPtr);
350             LOG1("    value = 0x%x\n", rsp.getUInt32.val)
351             LOG1("    status = %d\n", rsp.status)
352             LOG0("DONE\n")
354             break;
356           case LAD_NAMESERVER_REMOVE:
357             LOG2("LAD_NAMESERVER_REMOVE: calling NameServer_remove(%p, '%s')...\n", cmd.args.remove.handle, cmd.args.remove.name)
359             rsp.status = NameServer_remove(cmd.args.remove.handle,
360                                                cmd.args.remove.name);
362             LOG1("    status = %d\n", rsp.status)
363             LOG0("DONE\n")
365             break;
367           case LAD_NAMESERVER_REMOVEENTRY:
368             LOG2("LAD_NAMESERVER_REMOVEENTRY: calling NameServer_removeEntry(%p, %p)...\n", cmd.args.removeEntry.handle, cmd.args.removeEntry.entryPtr)
370             rsp.status = NameServer_removeEntry(
371                 cmd.args.removeEntry.handle,
372                 cmd.args.removeEntry.entryPtr);
374             LOG1("    status = %d\n", rsp.status)
375             LOG0("DONE\n")
377             break;
379           case LAD_MESSAGEQ_GETCONFIG:
380             LOG0("LAD_MESSAGEQ_GETCONFIG: calling MessageQ_getConfig()...\n")
382             MessageQ_getConfig(&rsp.messageQGetConfig.cfg);
383             rsp.messageQGetConfig.status = 0;
385             LOG1("    status = %d\n", rsp.messageQGetConfig.status)
386             LOG0("DONE\n")
388             break;
390           case LAD_MESSAGEQ_SETUP:
391             LOG0("LAD_MESSAGEQ_SETUP: calling MessageQ_setup()...\n")
393             rsp.setup.status = MessageQ_setup(&cmd.args.messageQSetup.cfg);
394             rsp.setup.nameServerHandle = MessageQ_getNameServerHandle();
396             LOG1("    status = %d\n", rsp.setup.status)
397             LOG0("DONE\n")
399             break;
401           case LAD_MESSAGEQ_DESTROY:
402             LOG0("LAD_MESSAGEQ_DESTROY: calling MessageQ_destroy()...\n")
404             rsp.status = MessageQ_destroy();
406             LOG1("    status = %d\n", rsp.status)
407             LOG0("DONE\n")
409             break;
411           case LAD_MESSAGEQ_CREATE:
412             LOG2("LAD_MESSAGEQ_CREATE: calling MessageQ_create(%p, %p)...\n", cmd.args.messageQCreate.name, &cmd.args.messageQCreate.params)
414             handle = MessageQ_create(cmd.args.messageQCreate.name,
415                 &cmd.args.messageQCreate.params);
416             rsp.messageQCreate.serverHandle = handle;
418             if (handle) {
419                 rsp.messageQCreate.queueId = MessageQ_getQueueId(handle);
420                 MessageQ_setQueueOwner(handle, clientPID[clientId]);
421                 rsp.messageQCreate.status = 0;
422             }
423             else {
424                 rsp.messageQCreate.status = -1;
425             }
427             LOG1("    status = %d\n", rsp.messageQCreate.status)
428             LOG0("DONE\n")
430             break;
432           case LAD_MESSAGEQ_DELETE:
433             LOG1("LAD_MESSAGEQ_DELETE: calling MessageQ_delete(%p)...\n", cmd.args.messageQDelete.serverHandle)
435             rsp.messageQDelete.status =
436                 MessageQ_delete((MessageQ_Handle *)&cmd.args.messageQDelete.serverHandle);
438             LOG1("    status = %d\n", rsp.messageQDelete.status)
439             LOG0("DONE\n")
441             break;
443           case LAD_MESSAGEQ_MSGINIT:
444             LOG1("LAD_MESSAGEQ_MSGINIT: calling MessageQ_msgInit(%p)...\n", &rsp.msgInit.msg);
446             MessageQ_msgInit(&rsp.msgInit.msg);
447             rsp.msgInit.status = 0;
449             LOG1("    status = %d\n", rsp.msgInit.status)
450             LOG0("DONE\n")
452             break;
454           case LAD_MULTIPROC_GETCONFIG:
455             LOG0("LAD_MULTIPROC_GETCONFIG: calling MultiProc_getConfig()...\n")
457             MultiProc_getConfig(&rsp.multiprocGetConfig.cfg);
458             rsp.multiprocGetConfig.status = 0;
460             LOG1("    status = %d\n", rsp.multiprocGetConfig.status)
461             LOG0("DONE\n")
463             break;
465           case LAD_EXIT:
466             goto exitNow;
468             break;
470           default:
471             LOG1("\nUnrecognized command: 0x%x\n", command)
473             break;
474         }
476         switch (command) {
477           case LAD_CONNECT:
478           case LAD_DISCONNECT:
479             break;
481           case LAD_NAMESERVER_SETUP:
482           case LAD_NAMESERVER_DESTROY:
483           case LAD_NAMESERVER_PARAMS_INIT:
484           case LAD_NAMESERVER_CREATE:
485           case LAD_NAMESERVER_DELETE:
486           case LAD_NAMESERVER_ADDUINT32:
487           case LAD_NAMESERVER_GETUINT32:
488           case LAD_NAMESERVER_REMOVE:
489           case LAD_NAMESERVER_REMOVEENTRY:
490           case LAD_MESSAGEQ_GETCONFIG:
491           case LAD_MESSAGEQ_SETUP:
492           case LAD_MESSAGEQ_DESTROY:
493           case LAD_MESSAGEQ_CREATE:
494           case LAD_MESSAGEQ_DELETE:
495           case LAD_MESSAGEQ_MSGINIT:
496           case LAD_MULTIPROC_GETCONFIG:
497             LOG0("Sending response...\n");
499             fwrite(&rsp, LAD_RESPONSELENGTH, 1, responseFIFOFilePtr[clientId]);
500             fflush(responseFIFOFilePtr[clientId]);
502             break;
504           default:
505             break;
506         }
507     }
509 exitNow:
510     if (logFile) {
511         LOG0("\n\nLAD IS SELF TERMINATING...\n\n")
512         fclose(logPtr);
513     }
514     unlink(commandFIFOFile);
516     return(0);
521 /*
522  *  ======== assignClientId ========
523  */
524 static LAD_ClientHandle assignClientId(Void)
526     Int clientId = -1;
527     Int i;
529     /* scan connection status flags to acquire a clientId */
530     for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
531         /* if slot open then provisionally select this clientId */
532         if (clientConnected[i] == FALSE) {
533              clientId = i;
534              break;
535         }
536     }
538     return(clientId);
542 /*
543  *  ======== cleanupDepartedClients ========
544  */
545 static Void cleanupDepartedClients(Void)
547     Int killStat;
548     Int i;
550     /* scan all connections to verify client processes still exist... */
551     for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
553         /* if connected... */
554         if (clientConnected[i] == TRUE) {
556             /* check if the client process (PID) still exists */
557             /*
558              * NOTE - calling kill with signal value of 0 will perform
559              * error checking, but not actually send a signal.  Will use this
560              * error check to see if PID still exists.  An alternative was
561              * to call getpgid.  This worked, but is apparently limited to
562              * USE_XOPEN_EXTENDED configurations.
563              */
564             killStat = kill(clientPID[i], 0);
565             if ((killStat == -1) && (errno == ESRCH)) {
567                 LOG1("\nDETECTED CONNECTED CLIENT #%d HAS DEPARTED!", i)
569                 /* will always need to do the disconnect... */
570                 LOG0("\nDoing DISCONNECT on behalf of client...")
571                 doDisconnect(i);
573                 MessageQ_cleanupOwner(clientPID[i]);
574 //                NameServer_cleanupOwner(clientPID[i]);
576                 LOG0("DONE\n")
577             }
578         }
579     }
583 /*
584  *  ======== connectToLAD ========
585  */
586 static Int connectToLAD(String clientName, Int pid, String clientProto, Int *clientIdPtr)
588     Int clientId = -1;
589     Bool connectDenied = FALSE;
590     Int status = LAD_SUCCESS;
591     FILE * filePtr;
592     Int statusIO;
594     /*
595      * TODO:L here, and everywhere parse FIFO strings: should
596      * add full error checking for incomplete or corrupted
597      * strings? Since LAD to ladclient comms are "closed", and
598      * tested, the likelihood of problems seems very low.
599      * But should still consider doing full error checking
600      * and, hopefully, recovery.
601      */
602     LOG0("\nLAD_CONNECT: \n")
603     LOG1("    client FIFO name = %s\n", clientName)
604     LOG1("    client PID = %d\n", pid)
606     /* first check for proper communication protocol */
607     if (strcmp(clientProto, LAD_PROTOCOLVERSION) != 0) {
609         /* if no match then reject the request */
610         LOG0("    ERROR: mismatch in communication protocol!\n")
611         LOG1("        LAD protocol = %s\n", LAD_PROTOCOLVERSION)
612         LOG1("        client protocol = %s\n", clientProto)
613         status = LAD_INVALIDVERSION;
615         /* set flag so know to close FIFO after response */
616         connectDenied = TRUE;
618         /* now jump forward a bit to send response */
619         goto openResponseFIFO;
620     }
622     /* determine this client's ID */
623     clientId = assignClientId();
625     /* if failed to acquire an ID then exit early */
626     if (clientId == -1) {
627         LOG0("    no free handle; too many connections!\n")
628         status = LAD_ACCESSDENIED;
630         /* set flag so know to close FIFO after response */
631         connectDenied = TRUE;
632     }
633     else {
634         LOG1("    assigned client handle = %d\n", clientId)
636         /* save the client's FIFO name for when disconnect */
637         strcpy(clientFIFOName[clientId], clientName);
638     }
640 openResponseFIFO:
642     /* create the dedicated response FIFO to the client */
643     statusIO = mkfifo(clientName, 0777);
644     if (statusIO != 0) {
646         LOG2("\nERROR: unable to mkfifo %s, errno = %x\n", clientName, errno)
648         status = LAD_IOFAILURE;
650         /* send no response; connection request will timeout */
651         goto doneconnect;
652     }
654     LOG1("    FIFO %s created\n", clientName)
656     /* set FIFO permissions to read/write */
657     chmod(clientName, 0666);
659     filePtr = fopen(clientName, "w");
660     if (filePtr == NULL) {
661         LOG1("\nERROR: unable to open response FIFO %s\n", clientName)
663         /* if failed open, still need to delete the FIFO */
664         unlink(clientName);
666         status = LAD_IOFAILURE;
668         /* send no response; connection request will timeout */
669         goto doneconnect;
670     }
672     LOG1("    FIFO %s opened for writing\n", clientName)
674     /*
675      * set "this client is connected" flag; this client ID is now "owned", and
676      * is no longer provisional
677      */
678     if (connectDenied == FALSE) {
679         responseFIFOFilePtr[clientId] = filePtr;
680         clientPID[clientId] = pid;
681         clientConnected[clientId] = TRUE;
682     }
684     rsp.connect.assignedId = clientId;
685     rsp.status = status;
687     /* put response to FIFO */
688     fwrite(&rsp, LAD_RESPONSELENGTH, 1, filePtr);
689     fflush(filePtr);
691     LOG0("    sent response\n")
693     /* if connection was denied, must now close FIFO */
694     if (connectDenied == TRUE) {
695         LOG1("    connect denied; closing FIFO %s\n", clientName)
696         fclose(filePtr);
697         unlink(clientName);
698     }
700     LOG0("DONE\n")
702 doneconnect:
703     if (clientIdPtr != NULL) {
704         *clientIdPtr = clientId;
705     }
707     return(status);
711 /*
712  *  ======== disconnectFromLAD ========
713  */
714 static Void disconnectFromLAD(Int clientId)
716     LOG0("\nLAD_DISCONNECT: ")
718     LOG1("\n    client handle = %x", clientId)
719     doDisconnect(clientId);
721     LOG0("DONE\n")
723     return;
727 /*
728  *  ======== doDisconnect ========
729  */
730 static Void doDisconnect(Int clientId)
732     /* set "this client is not connected" flag */
733     clientConnected[clientId] = FALSE;
735     /* close and remove the response FIFO */
736     LOG2("\n    closing FIFO %s (filePtr=%p)\n",
737         clientFIFOName[clientId], responseFIFOFilePtr[clientId])
738     fclose(responseFIFOFilePtr[clientId]);
740     LOG1("    done, unlinking %s\n", clientFIFOName[clientId]);
741     unlink(clientFIFOName[clientId]);