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

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

Updated docs.

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