source: trunk/tools/CmdQd/CmdQd.c@ 6623

Last change on this file since 6623 was 6623, checked in by bird, 24 years ago

Don't use CMD.EXE unless needed. Cache PATH search results.

File size: 48.8 KB
Line 
1/* $Id: CmdQd.c,v 1.3 2001-09-01 22:48:10 bird Exp $
2 *
3 * Command Queue Daemon / Client.
4 *
5 * Designed to execute commands asyncronus using multiple workers,
6 * and when all commands are submitted wait for them to complete.
7 *
8 * Copyright (c) 2001 knut st. osmundsen (kosmunds@csc.com)
9 *
10 * GPL
11 *
12 */
13
14
15/** @design Command Queue Daemon.
16 * This command daemon orginated as tool to exploit SMP and UNI systems better
17 * building large programs, but also when building one specific component of
18 * that program. It is gonna work just like the gnu make -j option.
19 *
20 * @subsection Work flow
21 *
22 * 1. Init daemon process. Creates a daemon process with a given number of
23 * workers. This is a detached process.
24 * 2. Submit jobs to the daemon. The daemon will queue the jobs and the
25 * workers will start working at once there is a job for them.
26 * 3. The nmake script will issue a wait command. We will now wait for all
27 * jobs to finish and in the mean time we'll display output from the jobs.
28 * Failing jobs will be queued up and show when all jobs are finished.
29 * 4. Two options: kill the daemon or start at step 2 again.
30 *
31 *
32 * @subsection Client <-> Daemon communication
33 *
34 * Client and daemon is one and the same executable. This has some advantages
35 * like implicit preloading of the client, fewer source files and fewer programs
36 * to install.
37 *
38 * The communication between the client and the daemon will use shared memory
39 * and an mutex semaphore which directs the conversation. The shared memory
40 * block is allocated by the daemon and will have a quite simple layout:
41 * Mutex Semaphore.
42 * Message Type.
43 * Message specific data:
44 * - Submit job:
45 * Returcode ignore. (4 bytes)
46 * Command to execute. (1 Kb)
47 * Current directory. (260 bytes)
48 * Environment block. (about 62KB)
49 * - Submit job response:
50 * Success/failure indicator.
51 *
52 * - Wait:
53 * Nothing.
54 * - Wait response:
55 * More output indicator.
56 * Success/failure indicator.
57 * Job output (about 63KB)
58 *
59 * - Kill:
60 * Nothing.
61 * - Kill response:
62 * Success/failure indicator.
63 *
64 * The shared memory block is 64KB.
65 *
66 *
67 * @subsection The Workers
68 *
69 * The workers is individual threads which waits for a job to be submitted to
70 * execution. Each worker is two threads. The job is executed thru a spawnvpe
71 * call and all stdout is read by the 1st worker thread, stderr is read by the
72 * 2nd thread. (Initially we've merged the two pipes and used one thread.)
73 * The output will be buffered (up to 4 MB). When the job is completed we'll
74 * put the output into either the success queue or the failure queue
75 * depending on the result.
76 *
77 *
78 */
79
80
81/*******************************************************************************
82* Header Files *
83*******************************************************************************/
84#include <stdio.h>
85#include <string.h>
86#include <stdlib.h>
87#include <stdarg.h>
88#include <assert.h>
89#include <direct.h>
90#include <signal.h>
91#include <process.h>
92
93#define INCL_BASE
94#include <os2.h>
95
96
97/*******************************************************************************
98* Defined Constants And Macros *
99*******************************************************************************/
100#define SHARED_MEM_NAME "\\SHAREMEM\\CmdQd"
101#define SHARED_MEM_SIZE 65536
102#define IDLE_TIMEOUT_MS -1 //(60*1000*3)
103#define OUTPUT_CHUNK (8192-8)
104
105#define HF_STDIN 0
106#define HF_STDOUT 1
107#define HF_STDERR 2
108
109/*******************************************************************************
110* Structures and Typedefs *
111*******************************************************************************/
112typedef struct SharedMem
113{
114 HEV hevClient; /* Client will wait on this. */
115 HEV hevDaemon; /* Daemon will wait on this. */
116 HMTX hmtx; /* Owner of the shared memory. */
117 HMTX hmtxClient; /* Take and release this for each */
118 /* client -> server -> client talk. */
119 enum
120 {
121 msgUnknown = 0,
122 msgSubmit = 1,
123 msgSubmitResponse = 2,
124 msgWait = 3,
125 msgWaitResponse = 4,
126 msgKill = 5,
127 msgKillResponse = 6
128 } enmMsgType;
129
130 union
131 {
132 struct Submit
133 {
134 unsigned rcIgnore; /* max return code to accept as good. */
135 char szCommand[1024]; /* job command. */
136 char szCurrentDir[CCHMAXPATH]; /* current directory. */
137 int cchEnv; /* Size of the environment block */
138 char szzEnv[SHARED_MEM_SIZE - CCHMAXPATH - 1024 - 4 - 4 - 4 - 4 - 4 - 4];
139 /* Environment block. */
140 } Submit;
141
142 struct SubmitResponse
143 {
144 BOOL fRc; /* Success idicator. */
145 } SubmitResponse;
146
147
148 struct Wait
149 {
150 int iNothing; /* Dummy. */
151 } Wait;
152
153 struct WaitResponse
154 {
155 BOOL fMore; /* More data. */
156 int rc; /* return code of first failing job. */
157 /* only valid if fMore == FALSE. */
158 char szOutput[SHARED_MEM_SIZE- 4 - 4 - 4 - 4 - 4 - 4 - 4];
159 /* The output of one or more jobs. */
160 } WaitResponse;
161
162
163 struct Kill
164 {
165 int iNothing; /* dummy. */
166 } Kill;
167
168 struct KillResponse
169 {
170 BOOL fRc; /* Success idicator. */
171 } KillResponse;
172
173 } u1;
174
175} SHAREDMEM, *PSHAREDMEM;
176
177
178typedef struct JobOutput
179{
180 struct JobOutput * pNext; /* Pointer to next output chunk. */
181 int cchOutput; /* Bytes used of the szOutput member. */
182 char szOutput[OUTPUT_CHUNK]; /* Output. */
183} JOBOUTPUT, *PJOBOUTPUT;
184
185
186typedef struct Job
187{
188 struct Job * pNext; /* Pointer to next job. */
189 int rc; /* Result. */
190 PJOBOUTPUT pJobOutput; /* Output. */
191 struct Submit JobInfo; /* Job. */
192} JOB, *PJOB;
193
194
195typedef struct PathCache
196{
197 char szPath[4096 - CCHMAXPATH * 3]; /* The path which this is valid for. */
198 char szCurDir[CCHMAXPATH]; /* The current dir this is valid for. */
199 char szProgram[CCHMAXPATH]; /* The program. */
200 char szResult[CCHMAXPATH]; /* The result. */
201} PATHCACHE, *PPATHCACHE;
202
203
204/*******************************************************************************
205* Global Variables *
206*******************************************************************************/
207PSHAREDMEM pShrMem; /* Pointer to the shared memory. */
208
209PJOB pJobQueue; /* Linked list of jobs. */
210PJOB pJobQueueEnd; /* Last job entry. */
211HMTX hmtxJobQueue; /* Read/Write mutex. */
212HEV hevJobQueue; /* Incomming job event sem. */
213ULONG cJobs; /* Count of jobs submitted. */
214
215HMTX hmtxJobQueueFine; /* Read/Write mutex for the next two queues. */
216HEV hevJobQueueFine; /* Posted when there is more output. */
217PJOB pJobCompleted; /* Linked list of successful jobs. */
218PJOB pJobCompletedLast; /* Last successful job entry. */
219PJOB pJobFailed; /* Linked list of failed jobs. */
220PJOB pJobFailedLast; /* Last failed job entry. */
221ULONG cJobsFinished; /* Count of jobs finished (failed or completed). */
222
223HMTX hmtxExec; /* Execute childs mutex sem. Required */
224 /* since we redirect standard files handles */
225 /* and changes the currentdirectory. */
226
227/*******************************************************************************
228* Internal Functions *
229*******************************************************************************/
230void syntax(void);
231
232/* operations */
233int Init(const char *arg0, int cWorkers);
234int Daemon(int cWorkers);
235int DaemonInit(int cWorkers);
236void signalhandler(int sig);
237void Worker(void * iWorkerId);
238char*WorkerArguments(char *pszArg, const char *pszzEnv, const char *pszCommand, char *pszCurDir, PPATHCACHE pPathCache);
239char*fileNormalize(char *pszFilename, char *pszCurDir);
240APIRET fileExist(const char *pszFilename);
241int Submit(int rcIgnore);
242int Wait(void);
243int Kill(void);
244
245/* shared memory helpers */
246int shrmemCreate(void);
247int shrmemOpen(void);
248void shrmemFree(void);
249int shrmemSendDaemon(BOOL fWait);
250int shrmemSendClient(int enmMsgTypeResponse);
251
252/* error handling */
253void _Optlink Error(const char *pszFormat, ...);
254
255
256int main(int argc, char **argv)
257{
258
259 /*
260 * Display help.
261 */
262 if (argc < 2 || (argv[1][0] == '-'))
263 {
264 syntax();
265 if (argc < 2)
266 {
267 printf("\n!syntax error!");
268 return -1;
269 }
270 return 0;
271 }
272
273 /*
274 * String switch on command.
275 */
276 if (!stricmp(argv[1], "submit"))
277 {
278 int rcIgnore = 0;
279 if (argc == 2)
280 {
281 printf("fatal error: There is no job to submit...\n");
282 return -1;
283 }
284 if (argv[2][0] == '-' && (rcIgnore = atoi(argv[2]+1)) <= 0)
285 {
286 printf("syntax error: Invalid ignore return code number...\n");
287 return -1;
288 }
289 return Submit(rcIgnore);
290 }
291 else if (!stricmp(argv[1], "wait"))
292 {
293 return Wait();
294 }
295 else if (!strcmp(argv[1], "kill"))
296 {
297 return Kill();
298 }
299 else if (!strcmp(argv[1], "init"))
300 {
301 if (argc != 3 || atoi(argv[2]) <= 0 || atoi(argv[2]) >= 256)
302 {
303 printf("fatal error: invalid/missing number of workers.\n");
304 return -1;
305 }
306 return Init(argv[0], atoi(argv[2]));
307 }
308 else if (!strcmp(argv[1], "!Daemon!"))
309 {
310 if (argc != 3 || atoi(argv[2]) <= 0)
311 {
312 printf("fatal error: no worker count specified or to many parameters.\n");
313 return -2;
314 }
315
316 return Daemon(atoi(argv[2]));
317 }
318 else
319 {
320 syntax();
321 printf("\n!invalid command '%s'.\n", argv[1]);
322 return -1;
323 }
324
325 //return 0;
326}
327
328
329/**
330 * Display syntax.
331 */
332void syntax(void)
333{
334 printf(
335 "Command Queue Daemon v0.0.1\n"
336 "---------------------------\n"
337 "syntax: CmdQd.exe <command> [args]\n"
338 "\n"
339 "commands:\n"
340 " init <workers>\n"
341 " Initiates the command queue daemon with the given number of workers.\n"
342 "\n"
343 " submit [-<n>] <command> [args]\n"
344 " Submits a command to the daemon.\n"
345 " Use '-<n>' to tell use to ignore return code 0-n.\n"
346 " \n"
347 " wait\n"
348 " Wait for all commands which are queued up to complete.\n"
349 " rc = count of failing commands.\n"
350 " \n"
351 " kill\n"
352 " Kills the daemon. Daemon will automatically die after\n"
353 " idling for some time.\n"
354 "\n"
355 "Copyright (c) 2001 knut st. osmundsen (kosmunds@csc.com)\n"
356 );
357}
358
359
360/**
361 * Starts a daemon process.
362 * @returns 0 on success.
363 * -4 on error.
364 * @param arg0 Executable filename.
365 * @param cWorkers Number of workers to start.
366 */
367int Init(const char *arg0, int cWorkers)
368{
369 int rc;
370 RESULTCODES Res; /* dummy, unused */
371 char szArg[CCHMAXPATH + 32];
372
373 if (!getenv("COMSPEC"))
374 {
375 Error("Fatal error: env. var. COMSPEC not found!\n");
376 return 0;
377 }
378
379 sprintf(&szArg[0], "%s\t!Daemon! %d", arg0, cWorkers);
380 szArg[strlen(arg0)] = '\0';
381 rc = DosExecPgm(NULL, 0, EXEC_BACKGROUND, &szArg[0], NULL, &Res, &szArg[0]);
382 if (rc)
383 Error("Fatal error: Failed to start daemon. rc=%d\n");
384 return rc;
385}
386
387
388/**
389 * This process is to be a daemon with a given number of works.
390 * @returns 0 on success.
391 * -4 on error.
392 * @param cWorkers Number of workers to start.
393 * @sketch
394 */
395int Daemon(int cWorkers)
396{
397 int rc;
398
399 /*
400 * Init Shared memory
401 */
402 rc = shrmemCreate();
403 if (rc)
404 return rc;
405
406 /*
407 * Init queues and semaphores.
408 */
409 rc = DaemonInit(cWorkers);
410 if (rc)
411 {
412 shrmemFree();
413 return rc;
414 }
415
416 /*
417 * Do work!
418 */
419 rc = shrmemSendDaemon(TRUE);
420 while (!rc)
421 {
422 switch (pShrMem->enmMsgType)
423 {
424 case msgSubmit:
425 {
426 PJOB pJob;
427
428 /*
429 * Make job entry.
430 */
431 _heap_check();
432 pJob = malloc((int)&((PJOB)0)->JobInfo.szzEnv[pShrMem->u1.Submit.cchEnv]);
433 if (pJob)
434 {
435 _heap_check();
436 memcpy(&pJob->JobInfo, &pShrMem->u1.Submit,
437 (int)&((struct Submit *)0)->szzEnv[pShrMem->u1.Submit.cchEnv]);
438 _heap_check();
439 pJob->rc = -1;
440 pJob->pNext = NULL;
441 pJob->pJobOutput = NULL;
442
443 /*
444 * Insert the entry.
445 */
446 rc = DosRequestMutexSem(hmtxJobQueue, SEM_INDEFINITE_WAIT);
447 if (rc)
448 break;
449 if (!pJobQueue)
450 pJobQueueEnd = pJobQueue = pJob;
451 else
452 {
453 pJobQueueEnd->pNext = pJob;
454 pJobQueueEnd = pJob;
455 }
456 cJobs++;
457 DosReleaseMutexSem(hmtxJobQueue);
458
459 /*
460 * Post the queue to wake up workers.
461 */
462 DosPostEventSem(hevJobQueue);
463 pShrMem->u1.SubmitResponse.fRc = TRUE;
464 }
465 else
466 {
467 Error("Internal Error: Out of memory (line=%d)\n", __LINE__);
468 pShrMem->u1.SubmitResponse.fRc = FALSE;
469 }
470 pShrMem->enmMsgType = msgSubmitResponse;
471 rc = shrmemSendDaemon(TRUE);
472 break;
473 }
474
475
476 case msgWait:
477 {
478 PJOB pJob = NULL;
479 PJOBOUTPUT pJobOutput = NULL;
480 char * psz;
481 int cch = 0;
482 char * pszOutput;
483 int cchOutput;
484 int rcFailure = 0;
485 BOOL fMore = TRUE;
486 ULONG ulIgnore;
487 void * pv;
488
489 DosPostEventSem(hevJobQueueFine); /* just so we don't get stuck in the loop... */
490 do
491 {
492 /* init response message */
493 pShrMem->enmMsgType = msgWaitResponse;
494 pShrMem->u1.WaitResponse.szOutput[0] = '\0';
495 pszOutput = &pShrMem->u1.WaitResponse.szOutput[0];
496 cchOutput = sizeof(pShrMem->u1.WaitResponse.szOutput) - 1;
497
498 /*
499 * Wait for output.
500 */
501 rc = DosWaitEventSem(hevJobQueueFine, SEM_INDEFINITE_WAIT);
502 if (rc)
503 break;
504
505 /*
506 * Copy output - Optimized so we don't cause to many context switches.
507 */
508 do
509 {
510 /*
511 * Next job.
512 */
513 if (!pJobOutput)
514 {
515 rc = DosRequestMutexSem(hmtxJobQueueFine, SEM_INDEFINITE_WAIT);
516 if (rc)
517 break;
518 pv = pJob;
519 pJob = pJobCompleted;
520 if (pJob)
521 {
522 pJobCompleted = pJob->pNext;
523 if (!pJobCompleted)
524 pJobCompletedLast = NULL;
525 }
526
527 if (!pJob && cJobs == cJobsFinished)
528 { /* all jobs finished, we may start output failures. */
529 pJob = pJobFailed;
530 if (pJob)
531 {
532 if (rcFailure == 0)
533 rcFailure = pJob->rc;
534
535 pJobFailed = pJob->pNext;
536 if (!pJobFailed)
537 pJobFailedLast = NULL;
538 }
539 else
540 fMore = FALSE;
541 }
542 else
543 DosResetEventSem(hevJobQueueFine, &ulIgnore); /* No more output, prepare wait. */
544 DosReleaseMutexSem(hmtxJobQueueFine);
545
546 if (pJob && pJob->pJobOutput)
547 {
548 pJobOutput = pJob->pJobOutput;
549 psz = pJobOutput->szOutput;
550 cch = pJobOutput->cchOutput;
551 }
552 if (pv)
553 free(pv);
554 }
555
556 /*
557 * Anything to output?
558 */
559 if (pJobOutput)
560 {
561 /*
562 * Copy output.
563 */
564 do
565 {
566 if (cch)
567 { /* copy */
568 int cchCopy = min(cch, cchOutput);
569 memcpy(pszOutput, psz, cchCopy);
570 psz += cchCopy; cch -= cchCopy;
571 pszOutput += cchCopy; cchOutput -= cchCopy;
572 }
573 if (!cch)
574 { /* next chunk */
575 pv = pJobOutput;
576 pJobOutput = pJobOutput->pNext;
577 if (pJobOutput)
578 {
579 psz = &pJobOutput->szOutput[0];
580 cch = pJobOutput->cchOutput;
581 }
582 free(pv);
583 }
584 } while (cch && cchOutput);
585 }
586 else
587 break; /* no more output, let's send what we got. */
588
589 } while (!rc && fMore && cchOutput);
590
591 /*
592 * We've got a message to send.
593 */
594 if (rc)
595 break;
596 *pszOutput = '\0';
597 pShrMem->u1.WaitResponse.rc = rcFailure;
598 pShrMem->u1.WaitResponse.fMore = fMore;
599 rc = shrmemSendDaemon(TRUE);
600 } while (!rc && fMore);
601 break;
602 }
603
604
605 case msgKill:
606 {
607 pShrMem->enmMsgType = msgKillResponse;
608 pShrMem->u1.KillResponse.fRc = TRUE;
609 shrmemSendDaemon(FALSE);
610 rc = -1;
611 break;
612 }
613
614 default:
615 Error("Internal error: Invalid message id %d\n", pShrMem->enmMsgType);
616 pShrMem->enmMsgType = msgUnknown;
617 rc = shrmemSendDaemon(TRUE);
618 }
619 }
620
621 /*
622 * Cleanup.
623 */
624 shrmemFree();
625 DosCloseMutexSem(hmtxJobQueue);
626 DosCloseMutexSem(hmtxJobQueueFine);
627 DosCloseEventSem(hevJobQueueFine);
628 DosCloseMutexSem(hmtxExec);
629 DosCloseEventSem(hevJobQueue);
630
631 _dump_allocated(16);
632
633 return 0;
634}
635
636
637/**
638 * Help which does most of the daemon init stuff.
639 * @returns 0 on success.
640 * @param cWorkers Number of worker threads to start.
641 */
642int DaemonInit(int cWorkers)
643{
644 int rc;
645 int i;
646
647 /*
648 * Init queues and semaphores.
649 */
650 rc = DosCreateEventSem(NULL, &hevJobQueue, 0, FALSE);
651 if (!rc)
652 {
653 rc = DosCreateMutexSem(NULL, &hmtxJobQueue, 0, FALSE);
654 if (!rc)
655 {
656 rc = DosCreateMutexSem(NULL, &hmtxJobQueueFine, 0, FALSE);
657 if (!rc)
658 {
659 rc = DosCreateEventSem(NULL, &hevJobQueueFine, 0, FALSE);
660 if (!rc)
661 {
662 rc = DosCreateMutexSem(NULL, &hmtxExec, 0, FALSE);
663 if (!rc)
664 {
665 /*
666 * Start workers.
667 */
668 rc = 0;
669 for (i = 0; i < cWorkers; i++)
670 if (_beginthread(Worker, NULL, 64*1024, (void*)i) == -1)
671 {
672 Error("Fatal error: failed to create worker thread no. %d\n", i);
673 rc = -1;
674 break;
675 }
676 if (!rc)
677 {
678 DosSetMaxFH(cWorkers * 3 + 20);
679 return 0; /* success! */
680 }
681
682 /* failure */
683 DosCloseMutexSem(hmtxExec);
684 }
685 else
686 Error("Fatal error: failed to create exec mutex. rc=%d", rc);
687 DosCloseEventSem(hevJobQueueFine);
688 }
689 else
690 Error("Fatal error: failed to create job queue fine event sem. rc=%d", rc);
691 DosCloseMutexSem(hmtxJobQueueFine);
692 }
693 else
694 Error("Fatal error: failed to create job queue fine mutex. rc=%d", rc);
695 DosCloseMutexSem(hmtxJobQueue);
696 }
697 else
698 Error("Fatal error: failed to create job queue mutex. rc=%d", rc);
699 DosCloseEventSem(hevJobQueue);
700 }
701 else
702 Error("Fatal error: failed to create job queue event sem. rc=%d", rc);
703
704 return rc;
705}
706
707
708/**
709 * Daemon signal handler.
710 */
711void signalhandler(int sig)
712{
713 sig = sig;
714 shrmemFree();
715 exit(-42);
716}
717
718
719/**
720 * Worker thread.
721 * @param iWorkerId The worker process id.
722 * @sketch
723 */
724void Worker(void * iWorkerId)
725{
726 PATHCACHE PathCache;
727 memset(&PathCache, 0, sizeof(PathCache));
728
729 while (!DosWaitEventSem(hevJobQueue, SEM_INDEFINITE_WAIT))
730 {
731 PJOB pJob;
732
733 /*
734 * Get job.
735 */
736 if (DosRequestMutexSem(hmtxJobQueue, SEM_INDEFINITE_WAIT))
737 return;
738 pJob = pJobQueue;
739 if (pJob)
740 {
741 if (pJob != pJobQueueEnd)
742 pJobQueue = pJob->pNext;
743 else
744 {
745 ULONG ulIgnore;
746 pJobQueue = pJobQueueEnd = NULL;
747 DosResetEventSem(hevJobQueue, &ulIgnore);
748 }
749 }
750 DosReleaseMutexSem(hmtxJobQueue);
751
752 /*
753 * Execute job.
754 */
755 if (pJob)
756 {
757 int rc;
758 char szArg[4096];
759 char szObj[256];
760 PJOBOUTPUT pJobOutput = NULL;
761 PJOBOUTPUT pJobOutputLast = NULL;
762 RESULTCODES Res;
763 PID pid;
764 HFILE hStdOut = HF_STDOUT;
765 HFILE hStdErr = HF_STDERR;
766 HFILE hStdOutSave = -1;
767 HFILE hStdErrSave = -1;
768 HPIPE hPipeR = NULLHANDLE;
769 HPIPE hPipeW = NULLHANDLE;
770
771 //printf("debug-%d: start %s\n", iWorkerId, pJob->JobInfo.szCommand);
772
773 /*
774 * Redirect output and start process.
775 */
776 WorkerArguments(szArg, &pJob->JobInfo.szzEnv, &pJob->JobInfo.szCommand[0],
777 &pJob->JobInfo.szCurrentDir[0], &PathCache);
778 rc = DosCreatePipe(&hPipeR, &hPipeW, sizeof(pJobOutput->szOutput) - 1);
779 if (rc)
780 {
781 Error("Internal Error: Failed to create pipe! rc=%d\n", rc);
782 return;
783 }
784
785 if (DosRequestMutexSem(hmtxExec, SEM_INDEFINITE_WAIT))
786 {
787 DosClose(hPipeR);
788 DosClose(hPipeW);
789 return;
790 }
791
792 pJob->pJobOutput = pJobOutput = pJobOutputLast = malloc(sizeof(JOBOUTPUT));
793 pJobOutput->pNext = NULL;
794 pJobOutput->cchOutput = sprintf(pJobOutput->szOutput, "Job: %s\n", pJob->JobInfo.szCommand);
795
796 rc = DosSetDefaultDisk( pJob->JobInfo.szCurrentDir[0] >= 'a'
797 ? pJob->JobInfo.szCurrentDir[0] - 'a' + 1
798 : pJob->JobInfo.szCurrentDir[0] - 'A' + 1);
799 rc += DosSetCurrentDir(pJob->JobInfo.szCurrentDir);
800 if (!rc)
801 {
802 assert( pJob->JobInfo.szzEnv[pJob->JobInfo.cchEnv-1] == '\0'
803 && pJob->JobInfo.szzEnv[pJob->JobInfo.cchEnv-2] == '\0');
804 DosDupHandle(HF_STDOUT, &hStdOutSave);
805 DosDupHandle(HF_STDERR, &hStdErrSave);
806 DosDupHandle(hPipeW, &hStdOut);
807 DosDupHandle(hPipeW, &hStdErr);
808 rc = DosExecPgm(szObj, sizeof(szObj), EXEC_ASYNCRESULT,
809 szArg, pJob->JobInfo.szzEnv, &Res, szArg);
810 DosClose(hStdOut); hStdOut = HF_STDOUT;
811 DosClose(hStdErr); hStdErr = HF_STDERR;
812 DosDupHandle(hStdOutSave, &hStdOut);
813 DosDupHandle(hStdErrSave, &hStdErr);
814 DosClose(hStdOutSave);
815 DosClose(hStdErrSave);
816 DosReleaseMutexSem(hmtxExec);
817 DosClose(hPipeW);
818
819
820 /*
821 * Read Output.
822 */
823 if (!rc)
824 {
825 ULONG cchRead;
826 ULONG cchRead2 = 0;
827
828 cchRead = sizeof(pJobOutput->szOutput) - 1;
829 while (((rc = DosRead(hPipeR,
830 &pJobOutput->szOutput[pJobOutput->cchOutput],
831 cchRead, &cchRead2)) == NO_ERROR
832 || rc == ERROR_MORE_DATA)
833 && cchRead2 != 0)
834 {
835 pJobOutput->cchOutput += cchRead2;
836 pJobOutput->szOutput[pJobOutput->cchOutput] = '\0';
837
838 /* prepare next read */
839 cchRead = sizeof(pJobOutput->szOutput) - pJobOutput->cchOutput - 1;
840 if (cchRead < 16)
841 {
842 pJobOutput = pJobOutput->pNext = malloc(sizeof(JOBOUTPUT));
843 pJobOutput->pNext = NULL;
844 pJobOutput->cchOutput = 0;
845 cchRead = sizeof(pJobOutput->szOutput) - 1;
846 }
847 cchRead2 = 0;
848 }
849 rc = 0;
850 }
851
852 /* finished reading */
853 DosClose(hPipeR);
854
855 /*
856 * Get result.
857 */
858 if (!rc)
859 {
860 DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &Res, &pid, Res.codeTerminate);
861 if ( Res.codeResult <= pJob->JobInfo.rcIgnore
862 && Res.codeTerminate == TC_EXIT)
863 pJob->rc = 0;
864 else
865 {
866 pJob->rc = -1;
867 rc = sprintf(szArg, "failed with rc=%d term=%d\n", Res.codeResult, Res.codeTerminate);
868 if (rc + pJobOutput->cchOutput + 1 >= sizeof(pJobOutput->szOutput))
869 {
870 pJobOutput = pJobOutput->pNext = malloc(sizeof(JOBOUTPUT));
871 pJobOutput->pNext = NULL;
872 pJobOutput->cchOutput = 0;
873 }
874 strcpy(&pJobOutput->szOutput[pJobOutput->cchOutput], szArg);
875 pJobOutput->cchOutput += rc;
876 }
877 }
878 else
879 {
880 pJobOutput->cchOutput += sprintf(&pJobOutput->szOutput[pJobOutput->cchOutput],
881 "DosExecPgm failed with rc=%d for command %s %s\n"
882 " obj=%s\n",
883 rc, szArg, pJob->JobInfo.szCommand, szObj);
884 pJob->rc = -1;
885 }
886 }
887 else
888 {
889 /*
890 * ChDir failed.
891 */
892 DosReleaseMutexSem(hmtxExec);
893 pJobOutput->cchOutput += sprintf(&pJobOutput->szOutput[pJobOutput->cchOutput ],
894 "Failed to set current directory to: %s\n",
895 pJob->JobInfo.szCurrentDir);
896 pJob->rc = -1;
897 DosClose(hPipeR);
898 }
899
900
901 /*
902 * Insert result in result queue.
903 */
904 if (DosRequestMutexSem(hmtxJobQueueFine, SEM_INDEFINITE_WAIT))
905 return;
906 pJob->pNext = NULL;
907 if (!pJob->rc) /* 0 on success. */
908 {
909 if (pJobCompletedLast)
910 pJobCompletedLast->pNext = pJob;
911 else
912 pJobCompleted = pJob;
913 pJobCompletedLast = pJob;
914 }
915 else
916 {
917 if (pJobFailedLast)
918 pJobFailedLast->pNext = pJob;
919 else
920 pJobFailed = pJob;
921 pJobFailedLast = pJob;
922 }
923 cJobsFinished++;
924 DosReleaseMutexSem(hmtxJobQueueFine);
925 /* wake up Wait. */
926 DosPostEventSem(hevJobQueueFine);
927 //printf("debug-%d: fine\n", iWorkerId);
928 }
929 }
930 iWorkerId = iWorkerId;
931}
932
933
934/**
935 * Builds the input to DosExecPgm.
936 * Will execute programs directly and command thru the shell.
937 *
938 * @returns pszArg.
939 * @param pszArg Arguments to DosExecPgm.(output)
940 * Assumes that the buffer is large enought.
941 * @param pszzEnv Pointer to environment block.
942 * @param pszCommand Command to execute.
943 * @param pszCurDir From where the command is to executed.
944 * @param pPathCache Used to cache the last path, executable, and the search result.
945 */
946char *WorkerArguments(char *pszArg, const char *pszzEnv, const char *pszCommand, char *pszCurDir, PPATHCACHE pPathCache)
947{
948 BOOL fCMD = FALSE;
949 const char *psz;
950 const char *psz2;
951 char * pszW;
952 char ch;
953 int cch;
954 APIRET rc;
955
956 /*
957 * Check if this is multiple command separated by either &, && or |.
958 * Currently ignoring quotes for this test.
959 */
960 if ( strchr(pszCommand, '&')
961 || strchr(pszCommand, '|'))
962 {
963 strcpy(pszArg, "cmd.exe"); /* doesn't use comspec, just defaults to cmd.exe in all cases. */
964 fCMD = TRUE;
965 psz2 = pszCommand; /* start of arguments. */
966 }
967 else
968 {
969 char chEnd = ' ';
970
971 /*
972 * Parse out the first name.
973 */
974 for (psz = pszCommand; *psz == '\t' || *psz == ' ';) //strip(,'L');
975 psz++;
976 if (*psz == '"' || *psz == '\'')
977 chEnd = *psz++;
978 psz2 = psz;
979 if (chEnd == ' ')
980 {
981 while ((ch = *psz) != '\0' && ch != ' ' && ch != '\t')
982 psz++;
983 }
984 else
985 {
986 while ((ch = *psz) != '\0' && ch != chEnd)
987 psz++;
988 }
989 *pszArg = '\0';
990 strncat(pszArg, psz2, psz - psz2);
991 psz2 = psz+1; /* start of arguments. */
992 }
993
994
995 /*
996 * Resolve the executable name if not qualified.
997 * NB! We doesn't fully support references to other driveletters yet. (TODO/BUGBUG)
998 */
999 /* correct slashes */
1000 pszW = pszArg;
1001 while ((pszW = strchr(pszW, '//')) != NULL)
1002 *pszW++ = '\\';
1003
1004 /* make sure it ends with .exe */
1005 pszW = pszArg + strlen(pszArg) - 1;
1006 while (pszW > pszArg && *pszW != '.' && *pszW != '\\')
1007 pszW--;
1008 if (*pszW != '.')
1009 strcat(pszArg, ".exe");
1010
1011 if (pszArg[1] != ':' || *pszArg == *pszCurDir)
1012 {
1013 rc = -1; /* indicate that we've not found the file. */
1014
1015 /* relative path? - expand it */
1016 if (strchr(pszArg, '\\') || pszArg[1] == ':')
1017 { /* relative path - expand it and check for file existence */
1018 fileNormalize(pszArg, pszCurDir);
1019 rc = fileExist(pszArg);
1020 }
1021 else
1022 { /* Search path. */
1023 const char *pszPath = pszzEnv;
1024 while (*pszPath != '\0' && strncmp(pszPath, "PATH=", 5))
1025 pszPath += strlen(pszPath) + 1;
1026
1027 if (pszPath && *pszPath != '\0')
1028 {
1029 /* check cache */
1030 if ( !strcmp(pPathCache->szProgram, pszArg)
1031 && !strcmp(pPathCache->szPath, pszPath)
1032 && !strcmp(pPathCache->szCurDir, pszCurDir)
1033 )
1034 {
1035 strcpy(pszArg, pPathCache->szResult);
1036 rc = fileExist(pszArg);
1037 }
1038
1039 if (rc)
1040 { /* search path */
1041 char szResult[CCHMAXPATH];
1042 rc = DosSearchPath(SEARCH_IGNORENETERRS, pszPath, pszArg, &szResult[0] , sizeof(szResult));
1043 if (!rc)
1044 {
1045 strcpy(pszArg, szResult);
1046
1047 /* update cache */
1048 strcpy(pPathCache->szProgram, pszArg);
1049 strcpy(pPathCache->szPath, pszPath);
1050 strcpy(pPathCache->szCurDir, pszCurDir);
1051 strcpy(pPathCache->szResult, szResult);
1052 }
1053 }
1054 }
1055 }
1056 }
1057 /* else nothing to do - assume full path (btw. we don't have the current dir for other drives anyway :-) */
1058 else
1059 rc = !fCMD ? fileExist(pszArg) : NO_ERROR;
1060
1061 /* In case of error use CMD */
1062 if (rc && !fCMD)
1063 {
1064 strcpy(pszArg, "cmd.exe"); /* doesn't use comspec, just defaults to cmd.exe in all cases. */
1065 fCMD = TRUE;
1066 psz2 = pszCommand; /* start of arguments. */
1067 }
1068
1069
1070 /*
1071 * Complete the argument string.
1072 * ---
1073 * szArg current holds the command.
1074 * psz2 points to the first parameter. (needs strip(,'L'))
1075 */
1076 while ((ch = *psz2) != '\0' && (ch == '\t' || ch == ' '))
1077 psz2++;
1078
1079 pszW = pszArg + strlen(pszArg) + 1;
1080 cch = strlen(psz2);
1081 if (!fCMD)
1082 {
1083 memcpy(pszW, psz2, ++cch);
1084 pszW[cch] = '\0';
1085 }
1086 else
1087 {
1088 strcpy(pszW, "/C \"");
1089 pszW += strlen(pszW);
1090 memcpy(pszW, psz2, cch);
1091 memcpy(pszW + cch, "\"\0", 3);
1092 }
1093
1094 return pszArg;
1095}
1096
1097
1098
1099/**
1100 * Normalizes the path slashes for the filename. It will partially expand paths too.
1101 * @returns pszFilename
1102 * @param pszFilename Pointer to filename string. Not empty string!
1103 * Much space to play with.
1104 * @remark (From fastdep.)
1105 */
1106char *fileNormalize(char *pszFilename, char *pszCurDir)
1107{
1108 char * psz;
1109 int aiSlashes[CCHMAXPATH/2];
1110 int cSlashes;
1111 int i;
1112
1113 /*
1114 * Init stuff.
1115 */
1116 for (i = 1, cSlashes; pszCurDir[i] != '\0'; i++)
1117 {
1118 if (pszCurDir[i] == '/')
1119 pszCurDir[i] = '\\';
1120 if (pszCurDir[i] == '\\')
1121 aiSlashes[cSlashes++] = i;
1122 }
1123 if (pszCurDir[i-1] != '\\')
1124 {
1125 aiSlashes[cSlashes] = i;
1126 pszCurDir[i++] = '\\';
1127 pszCurDir[i] = '\0';
1128 }
1129
1130
1131 /* expand path? */
1132 pszFilename = psz;
1133 if (pszFilename[1] != ':')
1134 { /* relative path */
1135 int iSlash;
1136 char szFile[CCHMAXPATH];
1137 char * psz = szFile;
1138
1139 strcpy(szFile, pszFilename);
1140 iSlash = *psz == '\\' ? 1 : cSlashes;
1141 while (*psz != '\0')
1142 {
1143 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
1144 { /* up one directory */
1145 if (iSlash > 0)
1146 iSlash--;
1147 psz += 3;
1148 }
1149 else if (*psz == '.' && psz[1] == '\\')
1150 { /* no change */
1151 psz += 2;
1152 }
1153 else
1154 { /* completed expantion! */
1155 strncpy(pszFilename, pszCurDir, aiSlashes[iSlash]+1);
1156 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
1157 break;
1158 }
1159 }
1160 }
1161 /* else: assume full path */
1162
1163 return psz;
1164}
1165
1166/**
1167 * Checks if a given file exist.
1168 * @returns 0 if the file exists. (NO_ERROR)
1169 * 2 if the file doesn't exist. (ERROR_FILE_NOT_FOUND)
1170 * @param pszFilename Name of the file to check existance for.
1171 */
1172APIRET fileExist(const char *pszFilename)
1173{
1174 FILESTATUS3 fsts3;
1175 return DosQueryPathInfo(pszFilename, FIL_STANDARD, &fsts3, sizeof(fsts3));
1176}
1177
1178
1179/**
1180 * Submits a command to the daemon.
1181 * @returns 0 on success.
1182 * -3 on failure.
1183 * @param rcIgnore Ignores returcodes ranging from 0 to rcIgnore.
1184 */
1185int Submit(int rcIgnore)
1186{
1187 int cch;
1188 int rc;
1189 char * psz;
1190 PPIB ppib;
1191 PTIB ptib;
1192
1193 DosGetInfoBlocks(&ptib, &ppib);
1194 rc = shrmemOpen();
1195 if (rc)
1196 return rc;
1197
1198 /*
1199 * Build message.
1200 */
1201 pShrMem->enmMsgType = msgSubmit;
1202 pShrMem->u1.Submit.rcIgnore = rcIgnore;
1203 _getcwd(pShrMem->u1.Submit.szCurrentDir, sizeof(pShrMem->u1.Submit.szCurrentDir));
1204
1205 /* command */
1206 psz = ppib->pib_pchcmd;
1207 psz += strlen(psz) + 1 + 7; /* 7 = strlen("submit ")*/
1208 while (*psz == ' ' || *psz == '\t')
1209 psz++;
1210 if (*psz == '-')
1211 {
1212 while (*psz != ' ' && *psz != '\t')
1213 psz++;
1214 while (*psz == ' ' || *psz == '\t')
1215 psz++;
1216 }
1217 cch = strlen(psz) + 1;
1218 if (cch > sizeof(pShrMem->u1.Submit.szCommand))
1219 {
1220 Error("Fatal error: Command too long.\n");
1221 shrmemFree();
1222 return -1;
1223 }
1224 if (*psz == '"' && psz[cch-2] == '"') /* remove start & end quotes if any */
1225 {
1226 cch--;
1227 psz++;
1228 }
1229 memcpy(&pShrMem->u1.Submit.szCommand[0], psz, cch);
1230
1231 /* environment */
1232 for (cch = 1, psz = ppib->pib_pchenv; *psz != '\0';)
1233 {
1234 int cchVar = strlen(psz) + 1;
1235 cch += cchVar;
1236 psz += cchVar;
1237 }
1238 if ( ppib->pib_pchenv[cch-2] != '\0'
1239 || ppib->pib_pchenv[cch-1] != '\0')
1240 {
1241 Error("internal error\n");
1242 return -1;
1243 }
1244 if (cch > sizeof(pShrMem->u1.Submit.szzEnv))
1245 {
1246 Error("Fatal error: environment is to bit, cchEnv=%d\n", cch);
1247 shrmemFree();
1248 return -ERROR_BAD_ENVIRONMENT;
1249 }
1250 pShrMem->u1.Submit.cchEnv = cch;
1251 memcpy(&pShrMem->u1.Submit.szzEnv[0], ppib->pib_pchenv, cch);
1252
1253
1254 /*
1255 * Send message and get respons.
1256 */
1257 rc = shrmemSendClient(msgSubmitResponse);
1258 if (rc)
1259 {
1260 shrmemFree();
1261 return rc;
1262 }
1263
1264 rc = !pShrMem->u1.SubmitResponse.fRc;
1265 shrmemFree();
1266 return rc;
1267}
1268
1269
1270/**
1271 * Waits for the commands to complete.
1272 * Will write all output from completed command to stdout.
1273 * Will write failing commands last.
1274 * @returns Count of failing commands.
1275 */
1276int Wait(void)
1277{
1278 int rc;
1279
1280 rc = shrmemOpen();
1281 if (rc)
1282 return rc;
1283 do
1284 {
1285 pShrMem->enmMsgType = msgWait;
1286 pShrMem->u1.Wait.iNothing = 0;
1287 rc = shrmemSendClient(msgWaitResponse);
1288 if (rc)
1289 {
1290 shrmemFree();
1291 return -1;
1292 }
1293 printf("%s", pShrMem->u1.WaitResponse.szOutput);
1294 /*
1295 * Release the client mutex if more data and yield the CPU.
1296 * So we can submit more work. (Odin nmake lib...)
1297 */
1298 if (pShrMem->u1.WaitResponse.fMore)
1299 {
1300 DosReleaseMutexSem(pShrMem->hmtxClient);
1301 DosSleep(0);
1302 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
1303 if (rc)
1304 {
1305 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
1306 shrmemFree();
1307 return -1;
1308 }
1309 }
1310 } while (pShrMem->u1.WaitResponse.fMore);
1311
1312 rc = pShrMem->u1.WaitResponse.rc;
1313 shrmemFree();
1314 return rc;
1315}
1316
1317
1318/**
1319 * Sends a kill command to the daemon to kill it and its workers.
1320 * @returns 0.
1321 */
1322int Kill(void)
1323{
1324 int rc;
1325
1326 rc = shrmemOpen();
1327 if (rc)
1328 return rc;
1329
1330 pShrMem->enmMsgType = msgKill;
1331 pShrMem->u1.Kill.iNothing = 0;
1332 rc = shrmemSendClient(msgKillResponse);
1333 if (!rc)
1334 rc = !pShrMem->u1.KillResponse.fRc;
1335
1336 shrmemFree();
1337 return rc;
1338}
1339
1340
1341/**
1342 * Creates the shared memory area.
1343 * The creator owns the memory when created.
1344 * @returns 0 on success. Error code on error.
1345 */
1346int shrmemCreate(void)
1347{
1348 int rc;
1349 rc = DosAllocSharedMem((PPVOID)(PVOID)&pShrMem,
1350 SHARED_MEM_NAME,
1351 SHARED_MEM_SIZE,
1352 PAG_COMMIT | PAG_READ | PAG_WRITE);
1353 if (rc)
1354 {
1355 Error("Fatal error: Failed to create shared memory object. rc=%d\n", rc);
1356 return rc;
1357 }
1358
1359 rc = DosCreateEventSem(NULL, &pShrMem->hevDaemon, DC_SEM_SHARED, FALSE);
1360 if (rc)
1361 {
1362 Error("Fatal error: Failed to create daemon event semaphore. rc=%d\n", rc);
1363 DosFreeMem(pShrMem);
1364 return rc;
1365 }
1366
1367 rc = DosCreateEventSem(NULL, &pShrMem->hevClient, DC_SEM_SHARED, FALSE);
1368 if (rc)
1369 {
1370 Error("Fatal error: Failed to create client event semaphore. rc=%d\n", rc);
1371 DosCloseEventSem(pShrMem->hevDaemon);
1372 DosFreeMem(pShrMem);
1373 return rc;
1374 }
1375
1376 rc = DosCreateMutexSem(NULL, &pShrMem->hmtx, DC_SEM_SHARED, TRUE);
1377 if (rc)
1378 {
1379 Error("Fatal error: Failed to create mutex semaphore. rc=%d\n", rc);
1380 DosCloseEventSem(pShrMem->hevClient);
1381 DosCloseEventSem(pShrMem->hevDaemon);
1382 DosFreeMem(pShrMem);
1383 return rc;
1384 }
1385
1386 rc = DosCreateMutexSem(NULL, &pShrMem->hmtxClient, DC_SEM_SHARED, FALSE);
1387 if (rc)
1388 {
1389 Error("Fatal error: Failed to create client mutex semaphore. rc=%d\n", rc);
1390 DosCloseEventSem(pShrMem->hevClient);
1391 DosCloseEventSem(pShrMem->hevClient);
1392 DosCloseEventSem(pShrMem->hevDaemon);
1393 DosFreeMem(pShrMem);
1394 return rc;
1395 }
1396
1397
1398 /*
1399 * Install signal handlers.
1400 */
1401 signal(SIGSEGV, signalhandler);
1402 signal(SIGTERM, signalhandler);
1403 signal(SIGABRT, signalhandler);
1404 signal(SIGINT, signalhandler);
1405 signal(SIGBREAK,signalhandler);
1406
1407 return rc;
1408}
1409
1410
1411/**
1412 * Opens the shared memory and the semaphores.
1413 * The caller is owner of the memory upon successful return.
1414 * @returns 0 on success. Error code on error.
1415 */
1416int shrmemOpen(void)
1417{
1418 int rc;
1419
1420 rc = DosGetNamedSharedMem((PPVOID)(PVOID)&pShrMem,
1421 SHARED_MEM_NAME,
1422 PAG_READ | PAG_WRITE);
1423 if (rc)
1424 {
1425 Error("Fatal error: Failed to open shared memory. rc=%d\n");
1426 return rc;
1427 }
1428
1429 rc = DosOpenEventSem(NULL, &pShrMem->hevClient);
1430 if (rc)
1431 {
1432 Error("Fatal error: Failed to open client event semaphore. rc=%d\n");
1433 DosFreeMem(pShrMem);
1434 return rc;
1435 }
1436
1437 rc = DosOpenEventSem(NULL, &pShrMem->hevDaemon);
1438 if (rc)
1439 {
1440 Error("Fatal error: Failed to open daemon event semaphore. rc=%d\n");
1441 DosCloseEventSem(pShrMem->hevClient);
1442 DosFreeMem(pShrMem);
1443 return rc;
1444 }
1445
1446 rc = DosOpenMutexSem(NULL, &pShrMem->hmtx);
1447 if (rc)
1448 {
1449 Error("Fatal error: Failed to open mutex semaphore. rc=%d\n");
1450 DosCloseEventSem(pShrMem->hevClient);
1451 DosCloseEventSem(pShrMem->hevDaemon);
1452 DosFreeMem(pShrMem);
1453 return rc;
1454 }
1455
1456 rc = DosOpenMutexSem(NULL, &pShrMem->hmtxClient);
1457 if (rc)
1458 {
1459 Error("Fatal error: Failed to open client mutex semaphore. rc=%d\n");
1460 DosCloseEventSem(pShrMem->hevClient);
1461 DosCloseEventSem(pShrMem->hevDaemon);
1462 DosCloseMutexSem(pShrMem->hmtx);
1463 DosFreeMem(pShrMem);
1464 return rc;
1465 }
1466
1467 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
1468 if (rc)
1469 {
1470 Error("Fatal error: Failed to open aquire ownership of client mutex semaphore. rc=%d\n");
1471 shrmemFree();
1472 return rc;
1473 }
1474
1475 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
1476 if (rc)
1477 {
1478 Error("Fatal error: Failed to open aquire ownership of mutex semaphore. rc=%d\n");
1479 shrmemFree();
1480 return rc;
1481 }
1482
1483
1484 /*
1485 * Install signal handlers.
1486 */
1487 signal(SIGSEGV, signalhandler);
1488 signal(SIGTERM, signalhandler);
1489 signal(SIGABRT, signalhandler);
1490 signal(SIGINT, signalhandler);
1491 signal(SIGBREAK,signalhandler);
1492
1493 return rc;
1494}
1495
1496
1497/**
1498 * Frees the shared memory and the associated semaphores.
1499 */
1500void shrmemFree(void)
1501{
1502 if (!pShrMem)
1503 return;
1504 DosReleaseMutexSem(pShrMem->hmtxClient);
1505 DosReleaseMutexSem(pShrMem->hmtx);
1506 DosCloseMutexSem(pShrMem->hmtxClient);
1507 DosCloseMutexSem(pShrMem->hmtx);
1508 DosCloseEventSem(pShrMem->hevClient);
1509 DosCloseEventSem(pShrMem->hevDaemon);
1510 DosFreeMem(pShrMem);
1511 pShrMem = NULL;
1512}
1513
1514
1515/**
1516 * Daemon sends a message.
1517 * Upon we don't own the shared memory any longer.
1518 * @returns 0 on success. Error code on error.
1519 * -1 on timeout.
1520 * @param fWait Wait for new message.
1521 */
1522int shrmemSendDaemon(BOOL fWait)
1523{
1524 ULONG ulDummy;
1525 int rc;
1526
1527 /* send message */
1528 DosResetEventSem(pShrMem->hevDaemon, &ulDummy);
1529 rc = DosReleaseMutexSem(pShrMem->hmtx);
1530 if (!rc)
1531 rc = DosPostEventSem(pShrMem->hevClient);
1532
1533 /* wait for next message */
1534 if (!rc && fWait)
1535 {
1536 do
1537 {
1538 rc = DosWaitEventSem(pShrMem->hevDaemon, IDLE_TIMEOUT_MS);
1539 } while (rc == ERROR_TIMEOUT && pJobQueue);
1540
1541 if (rc == ERROR_TIMEOUT)
1542 {
1543 DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
1544 shrmemFree();
1545 return -1;
1546 }
1547
1548 if (!rc)
1549 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
1550 if (rc && rc != ERROR_INTERRUPT)
1551 Error("Internal error: failed to get next message from daemon, rc=%d\n", rc);
1552 }
1553 else
1554 Error("Internal error: failed to send message from daemon, rc=%d\n", rc);
1555 return rc;
1556}
1557
1558
1559/**
1560 * Client sends a message.
1561 * Upon we don't own the shared memory any longer.
1562 * @returns 0 on success. Error code on error.
1563 * @param enmMsgTypeResponse The expected response on this message.
1564 */
1565int shrmemSendClient(int enmMsgTypeResponse)
1566{
1567 ULONG ulDummy;
1568 int rc;
1569
1570 /* send message */
1571 DosResetEventSem(pShrMem->hevClient, &ulDummy);
1572 rc = DosReleaseMutexSem(pShrMem->hmtx);
1573 if (!rc)
1574 rc = DosPostEventSem(pShrMem->hevDaemon);
1575
1576 /* wait for response */
1577 if (!rc)
1578 {
1579 rc = DosWaitEventSem(pShrMem->hevClient, SEM_INDEFINITE_WAIT);
1580 if (!rc)
1581 {
1582 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
1583 if (!rc && pShrMem->enmMsgType != enmMsgTypeResponse)
1584 {
1585 Error("Internal error: Invalid response message. response=%d expected=%d\n",
1586 pShrMem->enmMsgType, enmMsgTypeResponse);
1587 return -1;
1588 }
1589 }
1590 if (rc && rc != ERROR_INTERRUPT)
1591 Error("Internal error: failed to get response message from daemon, rc=%d\n", rc);
1592 }
1593 else
1594 Error("Internal error: failed to send message to daemon, rc=%d\n", rc);
1595
1596 return rc;
1597}
1598
1599
1600/**
1601 * printf lookalike used to print all run-tim errors.
1602 * @param pszFormat Format string.
1603 * @param ... Arguments (optional).
1604 */
1605void Error(const char *pszFormat, ...)
1606{
1607 va_list arg;
1608
1609 va_start(arg, pszFormat);
1610 vfprintf(stdout, pszFormat, arg);
1611 va_end(arg);
1612}
1613
Note: See TracBrowser for help on using the repository browser.