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

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

Corrected memory name. (was renamed due to zombie)

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