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

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

Dirty fix for Ctrl-C on wait client.

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