SDOCM00105512: Support unnamed Linux MessageQs
[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     String tmpString;
97 #if DAEMON
98     pid_t pid;
99     pid_t sid;
100 #endif
102     /* if more than two args: turn "ON" launch status printfs */
103     if (argc > 2) {
104         printf("\nLAD starting up...");
105     }
107     /* check for env variable indicating server exe repository */
108     serverDir = getenv("LAD_SERVERPATH");
109     if (argc > 2) {
110         if (serverDir != NULL) {
111             printf("\nLAD_SERVERPATH = %s\n", serverDir);
112         }
113         else {
114             printf("\nLAD_SERVERPATH = <NULL>\n");
115         }
116     }
118     /* change to LAD's working directory */
119     if ((chdir(LAD_WORKINGDIR)) < 0) {
121         /* if can't change directory assume it needs to be created, do it */
122         if ((mkdir(LAD_WORKINGDIR, 0666)) < 0) {
123             printf("\nERROR: Failed to create LAD's working directory!\n");
124             exit(EXIT_FAILURE);
125         }
126         /* now change to the new directory */
127         if ((chdir(LAD_WORKINGDIR)) < 0) {
128             printf("\nERROR: Failed to change to LAD's working directory!\n");
129             exit(EXIT_FAILURE);
130         }
131     }
133     /* process command line args */
134     if (argc > 1) {
135         logPtr = fopen(argv[1], "w");
136         if (logPtr == NULL) {
137             printf("\nERROR: unable to open log file %s\n", argv[1]);
138             exit(EXIT_FAILURE);
139         }
140         else {
141             logFile = TRUE;
142             if (argc > 2) {
143                 printf("\nOpened log file: %s", argv[1]);
144             }
145             /* close log file upon LAD termination */
146             flags = fcntl(fileno(logPtr), F_GETFD);
147             if (flags != -1) {
148                 fcntl(fileno(logPtr), F_SETFD, flags | FD_CLOEXEC);
149             }
150         }
151     }
153 #if DAEMON
154     /* fork off a child process */
155     pid = fork();
157     /* if fork of child failed then exit immediately; no child created */
158     if (pid < 0) {
159         printf("\nERROR: Failed to fork child process!");
160         exit(EXIT_FAILURE);
161     }
163     /* if pid > 0 this is the parent; time to die ... */
164     if (pid > 0) {
165         if (argc > 2) {
166             printf("\nSpawned daemon: %s\n\n", argv[0]);
167         }
168         exit(EXIT_SUCCESS);
169     }
171     /* child continues from here (pid == 0) ... */
173     /* change file mode mask */
174     umask(0);
176     /* create new session ID for the child */
177     sid = setsid();
179     /* exit with failure code if failed to get session ID... */
180     if (sid < 0) {
181         printf("\nERROR: Failed to acquire new session ID!");
182         exit(EXIT_FAILURE);
183     }
185     /* disassociate from the standard file descriptors */
186     close(STDIN_FILENO);
187     close(STDOUT_FILENO);
188     close(STDERR_FILENO);
190 #endif
192     LOG0("\nInitializing LAD... ")
194     /* TODO:L make sure LAD is not already running? */
196     /* initialize client info arrays */
197     for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
198         clientConnected[i] = FALSE;
199         responseFIFOFilePtr[i] = NULL;
200     }
202     /* if command FIFO exists from previous LAD session delete it now */
203     unlink(commandFIFOFile);
205     /* create command FIFO */
206     statusIO = mkfifo(commandFIFOFile, 0777);
207     if (statusIO != 0) {
208         LOG2("\nERROR: unable to create %s, errno = %x\n", commandFIFOFile,
209             errno)
210         return(0);
211     }
213     /* set FIFO permissions to read/write */
214     chmod(commandFIFOFile, 0666);
216 opencommandFIFO:
218     /* now open file for FIFO - will block until writer arrives... */
219     LOG1("\n    opening FIFO: %s\n", commandFIFOFile)
220     commandFIFOFilePtr = fopen(commandFIFOFile, "r");
221     if (commandFIFOFilePtr == NULL) {
222         LOG0("\nERROR: unable to open command FIFO\n")
223         unlink(commandFIFOFile);
224         return(0);
225     }
227     /* COMMAND PROCESSING LOOP */
228     while (1) {
229         LOG0("Retrieving command...\n")
231         /* read the next command packet */
232         n = fread(&cmd, LAD_COMMANDLENGTH, 1, commandFIFOFilePtr);
234         /*
235          * if last client closes FIFO then it must be closed and reopened ...
236          */
237         if (!n) {
238             LOG1("    EOF detected on FIFO, closing FIFO: %s\n", commandFIFOFile)
239             fclose(commandFIFOFilePtr);
241             goto opencommandFIFO;
242         }
244         /* cleanup for any connected/started clients that have departed */
245         cleanupDepartedClients();
247         command = cmd.cmd;
248         clientId = cmd.clientId;
250         /* process individual commands */
251         switch (command) {
252           /*
253            * Since cmd is a union of rcv and snd structs, don't write
254            * any snd elements before all rcv elements have been referenced
255            * (either saved in separate variables or passed to a function).
256            *
257            * cmd.cmd has already been saved in 'command'
258            * cmd.clientId has already been saved in 'clientId'
259            */
260           case LAD_CONNECT:
261             connectToLAD(cmd.args.connect.name, cmd.args.connect.pid,
262                          cmd.args.connect.protocol, NULL);
264             break;
266           case LAD_DISCONNECT:
267             disconnectFromLAD(clientId);
269             break;
271           case LAD_NAMESERVER_SETUP:
272             LOG0("LAD_NAMESERVER_SETUP: calling NameServer_setup()...\n")
274             rsp.status = NameServer_setup();
276             LOG1("    status = %d\n", rsp.status)
277             LOG0("DONE\n")
279             break;
281           case LAD_NAMESERVER_DESTROY:
282             LOG0("LAD_NAMESERVER_DESTROY: calling NameServer_destroy()...\n")
284             rsp.status = NameServer_destroy();
286             LOG1("    status = %d\n", rsp.status)
287             LOG0("DONE\n")
289             break;
291           case LAD_NAMESERVER_PARAMS_INIT:
292             LOG0("LAD_NAMESERVER_PARAMS_INIT: calling NameServer_Params_init()...\n")
294             NameServer_Params_init(&rsp.params);
296             LOG0("DONE\n")
298             break;
300           case LAD_NAMESERVER_CREATE:
301             LOG1("LAD_NAMESERVER_CREATE: calling NameServer_create('%s')...\n", cmd.args.create.name)
303             rsp.handle = NameServer_create(cmd.args.create.name,
304                                                &cmd.args.create.params);
306             LOG1("    handle = %p\n", rsp.handle)
307             LOG0("DONE\n")
309             break;
311           case LAD_NAMESERVER_DELETE:
312             LOG1("LAD_NAMESERVER_DELETE: calling NameServer_delete(%p)...\n", cmd.args.delete.handle)
314             rsp.delete.handle = cmd.args.delete.handle;
315             rsp.delete.status = NameServer_delete(&rsp.delete.handle);
317             LOG1("    status = %d\n", rsp.status)
318             LOG0("DONE\n")
320             break;
322           case LAD_NAMESERVER_ADDUINT32:
323             LOG1("LAD_NAMESERVER_ADDUINT32: calling NameServer_addUInt32(%p, ", cmd.args.addUInt32.handle)
324             LOG2("'%s', 0x%x)...\n", cmd.args.addUInt32.name, cmd.args.addUInt32.val)
326             rsp.entryPtr = NameServer_addUInt32(
327                 cmd.args.addUInt32.handle,
328                 cmd.args.addUInt32.name,
329                 cmd.args.addUInt32.val);
331             LOG1("    entryPtr = %p\n", rsp.entryPtr)
332             LOG0("DONE\n")
334             break;
336           case LAD_NAMESERVER_GETUINT32:
337             LOG2("LAD_NAMESERVER_GETUINT32: calling NameServer_getUInt32(%p, '%s')...\n", cmd.args.getUInt32.handle, cmd.args.getUInt32.name)
339             if (cmd.args.getUInt32.procId[0] == (UInt16)-1) {
340                 procIdPtr = NULL;
341             }
342             else {
343                 procIdPtr = cmd.args.getUInt32.procId;
344             }
345             rsp.status = NameServer_getUInt32(
346                 cmd.args.getUInt32.handle,
347                 cmd.args.getUInt32.name,
348                 &rsp.getUInt32.val,
349                 procIdPtr);
351             LOG1("    value = 0x%x\n", rsp.getUInt32.val)
352             LOG1("    status = %d\n", rsp.status)
353             LOG0("DONE\n")
355             break;
357           case LAD_NAMESERVER_REMOVE:
358             LOG2("LAD_NAMESERVER_REMOVE: calling NameServer_remove(%p, '%s')...\n", cmd.args.remove.handle, cmd.args.remove.name)
360             rsp.status = NameServer_remove(cmd.args.remove.handle,
361                                                cmd.args.remove.name);
363             LOG1("    status = %d\n", rsp.status)
364             LOG0("DONE\n")
366             break;
368           case LAD_NAMESERVER_REMOVEENTRY:
369             LOG2("LAD_NAMESERVER_REMOVEENTRY: calling NameServer_removeEntry(%p, %p)...\n", cmd.args.removeEntry.handle, cmd.args.removeEntry.entryPtr)
371             rsp.status = NameServer_removeEntry(
372                 cmd.args.removeEntry.handle,
373                 cmd.args.removeEntry.entryPtr);
375             LOG1("    status = %d\n", rsp.status)
376             LOG0("DONE\n")
378             break;
380           case LAD_MESSAGEQ_GETCONFIG:
381             LOG0("LAD_MESSAGEQ_GETCONFIG: calling MessageQ_getConfig()...\n")
383             MessageQ_getConfig(&rsp.messageQGetConfig.cfg);
384             rsp.messageQGetConfig.status = 0;
386             LOG1("    status = %d\n", rsp.messageQGetConfig.status)
387             LOG0("DONE\n")
389             break;
391           case LAD_MESSAGEQ_SETUP:
392             LOG0("LAD_MESSAGEQ_SETUP: calling MessageQ_setup()...\n")
394             rsp.setup.status = MessageQ_setup(&cmd.args.messageQSetup.cfg);
395             rsp.setup.nameServerHandle = MessageQ_getNameServerHandle();
397             LOG1("    status = %d\n", rsp.setup.status)
398             LOG0("DONE\n")
400             break;
402           case LAD_MESSAGEQ_DESTROY:
403             LOG0("LAD_MESSAGEQ_DESTROY: calling MessageQ_destroy()...\n")
405             rsp.status = MessageQ_destroy();
407             LOG1("    status = %d\n", rsp.status)
408             LOG0("DONE\n")
410             break;
412           case LAD_MESSAGEQ_CREATE:
413             LOG2("LAD_MESSAGEQ_CREATE: calling MessageQ_create(%p, %p)...\n",
414                     cmd.args.messageQCreate.name,
415                     &cmd.args.messageQCreate.params);
417             tmpString = (cmd.args.messageQCreate.name[0] == '\0') ? NULL :
418                 cmd.args.messageQCreate.name;
420             handle = MessageQ_create(tmpString, &cmd.args.messageQCreate.params);
421             rsp.messageQCreate.serverHandle = handle;
423             if (handle) {
424                 rsp.messageQCreate.queueId = MessageQ_getQueueId(handle);
425                 MessageQ_setQueueOwner(handle, clientPID[clientId]);
426                 rsp.messageQCreate.status = 0;
427             }
428             else {
429                 rsp.messageQCreate.status = -1;
430             }
432             LOG1("    status = %d\n", rsp.messageQCreate.status)
433             LOG0("DONE\n")
435             break;
437           case LAD_MESSAGEQ_DELETE:
438             LOG1("LAD_MESSAGEQ_DELETE: calling MessageQ_delete(%p)...\n", cmd.args.messageQDelete.serverHandle)
440             rsp.messageQDelete.status =
441                 MessageQ_delete((MessageQ_Handle *)&cmd.args.messageQDelete.serverHandle);
443             LOG1("    status = %d\n", rsp.messageQDelete.status)
444             LOG0("DONE\n")
446             break;
448           case LAD_MESSAGEQ_MSGINIT:
449             LOG1("LAD_MESSAGEQ_MSGINIT: calling MessageQ_msgInit(%p)...\n", &rsp.msgInit.msg);
451             MessageQ_msgInit(&rsp.msgInit.msg);
452             rsp.msgInit.status = 0;
454             LOG1("    status = %d\n", rsp.msgInit.status)
455             LOG0("DONE\n")
457             break;
459           case LAD_MULTIPROC_GETCONFIG:
460             LOG0("LAD_MULTIPROC_GETCONFIG: calling MultiProc_getConfig()...\n")
462             MultiProc_getConfig(&rsp.multiprocGetConfig.cfg);
463             rsp.multiprocGetConfig.status = 0;
465             LOG1("    status = %d\n", rsp.multiprocGetConfig.status)
466             LOG0("DONE\n")
468             break;
470           case LAD_EXIT:
471             goto exitNow;
473             break;
475           default:
476             LOG1("\nUnrecognized command: 0x%x\n", command)
478             break;
479         }
481         switch (command) {
482           case LAD_CONNECT:
483           case LAD_DISCONNECT:
484             break;
486           case LAD_NAMESERVER_SETUP:
487           case LAD_NAMESERVER_DESTROY:
488           case LAD_NAMESERVER_PARAMS_INIT:
489           case LAD_NAMESERVER_CREATE:
490           case LAD_NAMESERVER_DELETE:
491           case LAD_NAMESERVER_ADDUINT32:
492           case LAD_NAMESERVER_GETUINT32:
493           case LAD_NAMESERVER_REMOVE:
494           case LAD_NAMESERVER_REMOVEENTRY:
495           case LAD_MESSAGEQ_GETCONFIG:
496           case LAD_MESSAGEQ_SETUP:
497           case LAD_MESSAGEQ_DESTROY:
498           case LAD_MESSAGEQ_CREATE:
499           case LAD_MESSAGEQ_DELETE:
500           case LAD_MESSAGEQ_MSGINIT:
501           case LAD_MULTIPROC_GETCONFIG:
502             LOG0("Sending response...\n");
504             fwrite(&rsp, LAD_RESPONSELENGTH, 1, responseFIFOFilePtr[clientId]);
505             fflush(responseFIFOFilePtr[clientId]);
507             break;
509           default:
510             break;
511         }
512     }
514 exitNow:
515     if (logFile) {
516         LOG0("\n\nLAD IS SELF TERMINATING...\n\n")
517         fclose(logPtr);
518     }
519     unlink(commandFIFOFile);
521     return(0);
526 /*
527  *  ======== assignClientId ========
528  */
529 static LAD_ClientHandle assignClientId(Void)
531     Int clientId = -1;
532     Int i;
534     /* scan connection status flags to acquire a clientId */
535     for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
536         /* if slot open then provisionally select this clientId */
537         if (clientConnected[i] == FALSE) {
538              clientId = i;
539              break;
540         }
541     }
543     return(clientId);
547 /*
548  *  ======== cleanupDepartedClients ========
549  */
550 static Void cleanupDepartedClients(Void)
552     Int killStat;
553     Int i;
555     /* scan all connections to verify client processes still exist... */
556     for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
558         /* if connected... */
559         if (clientConnected[i] == TRUE) {
561             /* check if the client process (PID) still exists */
562             /*
563              * NOTE - calling kill with signal value of 0 will perform
564              * error checking, but not actually send a signal.  Will use this
565              * error check to see if PID still exists.  An alternative was
566              * to call getpgid.  This worked, but is apparently limited to
567              * USE_XOPEN_EXTENDED configurations.
568              */
569             killStat = kill(clientPID[i], 0);
570             if ((killStat == -1) && (errno == ESRCH)) {
572                 LOG1("\nDETECTED CONNECTED CLIENT #%d HAS DEPARTED!", i)
574                 /* will always need to do the disconnect... */
575                 LOG0("\nDoing DISCONNECT on behalf of client...")
576                 doDisconnect(i);
578                 MessageQ_cleanupOwner(clientPID[i]);
579 //                NameServer_cleanupOwner(clientPID[i]);
581                 LOG0("DONE\n")
582             }
583         }
584     }
588 /*
589  *  ======== connectToLAD ========
590  */
591 static Int connectToLAD(String clientName, Int pid, String clientProto, Int *clientIdPtr)
593     Int clientId = -1;
594     Bool connectDenied = FALSE;
595     Int status = LAD_SUCCESS;
596     FILE * filePtr;
597     Int statusIO;
599     /*
600      * TODO:L here, and everywhere parse FIFO strings: should
601      * add full error checking for incomplete or corrupted
602      * strings? Since LAD to ladclient comms are "closed", and
603      * tested, the likelihood of problems seems very low.
604      * But should still consider doing full error checking
605      * and, hopefully, recovery.
606      */
607     LOG0("\nLAD_CONNECT: \n")
608     LOG1("    client FIFO name = %s\n", clientName)
609     LOG1("    client PID = %d\n", pid)
611     /* first check for proper communication protocol */
612     if (strcmp(clientProto, LAD_PROTOCOLVERSION) != 0) {
614         /* if no match then reject the request */
615         LOG0("    ERROR: mismatch in communication protocol!\n")
616         LOG1("        LAD protocol = %s\n", LAD_PROTOCOLVERSION)
617         LOG1("        client protocol = %s\n", clientProto)
618         status = LAD_INVALIDVERSION;
620         /* set flag so know to close FIFO after response */
621         connectDenied = TRUE;
623         /* now jump forward a bit to send response */
624         goto openResponseFIFO;
625     }
627     /* determine this client's ID */
628     clientId = assignClientId();
630     /* if failed to acquire an ID then exit early */
631     if (clientId == -1) {
632         LOG0("    no free handle; too many connections!\n")
633         status = LAD_ACCESSDENIED;
635         /* set flag so know to close FIFO after response */
636         connectDenied = TRUE;
637     }
638     else {
639         LOG1("    assigned client handle = %d\n", clientId)
641         /* save the client's FIFO name for when disconnect */
642         strcpy(clientFIFOName[clientId], clientName);
643     }
645 openResponseFIFO:
647     /* create the dedicated response FIFO to the client */
648     statusIO = mkfifo(clientName, 0777);
649     if (statusIO != 0) {
651         LOG2("\nERROR: unable to mkfifo %s, errno = %x\n", clientName, errno)
653         status = LAD_IOFAILURE;
655         /* send no response; connection request will timeout */
656         goto doneconnect;
657     }
659     LOG1("    FIFO %s created\n", clientName)
661     /* set FIFO permissions to read/write */
662     chmod(clientName, 0666);
664     filePtr = fopen(clientName, "w");
665     if (filePtr == NULL) {
666         LOG1("\nERROR: unable to open response FIFO %s\n", clientName)
668         /* if failed open, still need to delete the FIFO */
669         unlink(clientName);
671         status = LAD_IOFAILURE;
673         /* send no response; connection request will timeout */
674         goto doneconnect;
675     }
677     LOG1("    FIFO %s opened for writing\n", clientName)
679     /*
680      * set "this client is connected" flag; this client ID is now "owned", and
681      * is no longer provisional
682      */
683     if (connectDenied == FALSE) {
684         responseFIFOFilePtr[clientId] = filePtr;
685         clientPID[clientId] = pid;
686         clientConnected[clientId] = TRUE;
687     }
689     rsp.connect.assignedId = clientId;
690     rsp.status = status;
692     /* put response to FIFO */
693     fwrite(&rsp, LAD_RESPONSELENGTH, 1, filePtr);
694     fflush(filePtr);
696     LOG0("    sent response\n")
698     /* if connection was denied, must now close FIFO */
699     if (connectDenied == TRUE) {
700         LOG1("    connect denied; closing FIFO %s\n", clientName)
701         fclose(filePtr);
702         unlink(clientName);
703     }
705     LOG0("DONE\n")
707 doneconnect:
708     if (clientIdPtr != NULL) {
709         *clientIdPtr = clientId;
710     }
712     return(status);
716 /*
717  *  ======== disconnectFromLAD ========
718  */
719 static Void disconnectFromLAD(Int clientId)
721     LOG0("\nLAD_DISCONNECT: ")
723     LOG1("\n    client handle = %x", clientId)
724     doDisconnect(clientId);
726     LOG0("DONE\n")
728     return;
732 /*
733  *  ======== doDisconnect ========
734  */
735 static Void doDisconnect(Int clientId)
737     /* set "this client is not connected" flag */
738     clientConnected[clientId] = FALSE;
740     /* close and remove the response FIFO */
741     LOG2("\n    closing FIFO %s (filePtr=%p)\n",
742         clientFIFOName[clientId], responseFIFOFilePtr[clientId])
743     fclose(responseFIFOFilePtr[clientId]);
745     LOG1("    done, unlinking %s\n", clientFIFOName[clientId]);
746     unlink(clientFIFOName[clientId]);