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

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

Added some show*jobs features.

File size: 78.5 KB
Line 
1/* $Id: CmdQd.c,v 1.9 2001-11-24 02:21:39 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 * - Show jobs:
62 * Nothing.
63 * - Show jobs reponse:
64 * More output indicator.
65 * Job listing (about 63KB)
66 *
67 * - Show failed jobs:
68 * Nothing.
69 * - Show failed jobs reponse:
70 * More output indicator.
71 * Job listing (about 63KB)
72 *
73 * - Show (successfully) completed jobs:
74 * Nothing.
75 * - Show completed jobs reponse:
76 * More output indicator.
77 * Job listing (about 63KB)
78 *
79 * - Show running jobs:
80 * Nothing.
81 * - Show running jobs reponse:
82 * More output indicator.
83 * Job listing (about 63KB)
84 *
85 * - Kill:
86 * Nothing.
87 * - Kill response:
88 * Success/failure indicator.
89 *
90 * - Dies:
91 * Nothing. This is a message to the client saying that the
92 * daemon is dying or allready dead.
93 *
94 * The shared memory block is 64KB.
95 *
96 *
97 * @subsection The Workers
98 *
99 * The workers is individual threads which waits for a job to be submitted to
100 * execution. If the job only contains a single executable program to execute
101 * (no & or &&) it will be executed using DosExecPgm. If it's a multi program
102 * or command job it will be executed by CMD.EXE.
103 *
104 * Output will be read using unamed pipe and buffered. When the job is
105 * completed we'll put the output into either the success queue or the failure
106 * queue depending on the result.
107 *
108 * Note. Process startup needs to be serialized in order to be able to redirect
109 * stdout. We're using a mutex for this.
110 *
111 */
112
113
114/*******************************************************************************
115* Header Files *
116*******************************************************************************/
117#include <stdio.h>
118#include <string.h>
119#include <stdlib.h>
120#include <stdarg.h>
121#include <assert.h>
122#include <direct.h>
123#include <signal.h>
124#include <process.h>
125
126#define INCL_BASE
127#include <os2.h>
128
129
130/*
131 * Memory debugging.
132 */
133#ifdef DEBUGMEMORY
134void my_free(void *);
135void *my_malloc(size_t);
136
137#undef free
138#undef malloc
139
140#define free(pv) my_free(pv)
141#define malloc(cb) my_malloc(cb)
142
143#endif
144
145/*******************************************************************************
146* Defined Constants And Macros *
147*******************************************************************************/
148#define SHARED_MEM_NAME "\\SHAREMEM\\CmdQd"
149#define SHARED_MEM_SIZE 65536
150#define IDLE_TIMEOUT_MS -1 //(60*1000*3)
151#define OUTPUT_CHUNK (8192-8)
152
153#define HF_STDIN 0
154#define HF_STDOUT 1
155#define HF_STDERR 2
156
157/*******************************************************************************
158* Structures and Typedefs *
159*******************************************************************************/
160typedef struct SharedMem
161{
162 HEV hevClient; /* Client will wait on this. */
163 HEV hevDaemon; /* Daemon will wait on this. */
164 HMTX hmtx; /* Owner of the shared memory. */
165 HMTX hmtxClient; /* Take and release this for each */
166 /* client -> server -> client talk. */
167 enum
168 {
169 msgUnknown = 0,
170 msgSubmit = 1,
171 msgSubmitResponse = 2,
172 msgWait = 3,
173 msgWaitResponse = 4,
174 msgKill = 5,
175 msgKillResponse = 6,
176 msgShowJobs = 7,
177 msgShowJobsResponse = 8,
178 msgShowRunningJobs = 9,
179 msgShowRunningJobsResponse = 10,
180 msgShowCompletedJobs = 11,
181 msgShowCompletedJobsResponse = 12,
182 msgShowFailedJobs = 13,
183 msgShowFailedJobsResponse = 14,
184 msgDying = 0xff
185 } enmMsgType;
186
187 union
188 {
189 struct Submit
190 {
191 unsigned rcIgnore; /* max return code to accept as good. */
192 char szCommand[1024]; /* job command. */
193 char szCurrentDir[CCHMAXPATH]; /* current directory. */
194 int cchEnv; /* Size of the environment block */
195 char szzEnv[SHARED_MEM_SIZE - CCHMAXPATH - 1024 - 4 - 4 - 4 - 4 - 4 - 4];
196 /* Environment block. */
197 } Submit;
198 struct SubmitResponse
199 {
200 BOOL fRc; /* Success idicator. */
201 } SubmitResponse;
202
203
204 struct Wait
205 {
206 int iNothing; /* Dummy. */
207 } Wait;
208 struct WaitResponse
209 {
210 BOOL fMore; /* More data. */
211 int rc; /* return code of first failing job. */
212 /* only valid if fMore == FALSE. */
213 char szOutput[SHARED_MEM_SIZE- 4 - 4 - 4 - 4 - 4 - 4 - 4];
214 /* The output of one or more jobs. */
215 } WaitResponse;
216
217
218 struct Kill
219 {
220 int iNothing; /* dummy. */
221 } Kill;
222 struct KillResponse
223 {
224 BOOL fRc; /* Success idicator. */
225 } KillResponse;
226
227
228 struct ShowJobs
229 {
230 int iNothing; /* Dummy. */
231 } ShowJobs;
232 struct ShowJobsResponse
233 {
234 BOOL fMore; /* More data. */
235 char szOutput[SHARED_MEM_SIZE- 4 - 4 - 4 - 4 - 4 - 4];
236 /* The listing of jobs. */
237 } ShowJobsResponse;
238
239
240 struct ShowRunningJobs
241 {
242 int iNothing; /* Dummy. */
243 } ShowRunningJobs;
244 struct ShowRunningJobsResponse
245 {
246 BOOL fMore; /* More data. */
247 char szOutput[SHARED_MEM_SIZE- 4 - 4 - 4 - 4 - 4 - 4];
248 /* The listing of jobs. */
249 } ShowRunningJobsResponse;
250
251
252 struct ShowCompletedJobs
253 {
254 int iNothing; /* Dummy. */
255 } ShowCompletedJobs;
256 struct ShowCompletedJobsResponse
257 {
258 BOOL fMore; /* More data. */
259 char szOutput[SHARED_MEM_SIZE- 4 - 4 - 4 - 4 - 4 - 4];
260 /* The listing of jobs. */
261 } ShowCompletedJobsResponse;
262
263
264 struct ShowFailedJobs
265 {
266 int iNothing; /* Dummy. */
267 } ShowFailedJobs;
268 struct ShowFailedJobsResponse
269 {
270 BOOL fMore; /* More data. */
271 char szOutput[SHARED_MEM_SIZE- 4 - 4 - 4 - 4 - 4 - 4];
272 /* The listing of jobs. */
273 } ShowFailedJobsResponse;
274
275 } u1;
276
277} SHAREDMEM, *PSHAREDMEM;
278
279
280typedef struct JobOutput
281{
282 struct JobOutput * pNext; /* Pointer to next output chunk. */
283 int cchOutput; /* Bytes used of the szOutput member. */
284 char szOutput[OUTPUT_CHUNK]; /* Output. */
285} JOBOUTPUT, *PJOBOUTPUT;
286
287
288typedef struct Job
289{
290 struct Job * pNext; /* Pointer to next job. */
291 int rc; /* Result. */
292 PJOBOUTPUT pJobOutput; /* Output. */
293 struct Submit JobInfo; /* Job. */
294} JOB, *PJOB;
295
296
297typedef struct PathCache
298{
299 char szPath[4096 - CCHMAXPATH * 3]; /* The path which this is valid for. */
300 char szCurDir[CCHMAXPATH]; /* The current dir this is valid for. */
301 char szProgram[CCHMAXPATH]; /* The program. */
302 char szResult[CCHMAXPATH]; /* The result. */
303} PATHCACHE, *PPATHCACHE;
304
305
306/*******************************************************************************
307* Global Variables *
308*******************************************************************************/
309PSHAREDMEM pShrMem; /* Pointer to the shared memory. */
310
311HMTX hmtxJobQueue; /* Read/Write mutex for the two jobs queues below. */
312HEV hevJobQueue; /* Incomming job event sem. */
313PJOB pJobQueue; /* Linked list of jobs. */
314PJOB pJobQueueEnd; /* Last job entry. */
315ULONG cJobs; /* Count of jobs submitted. */
316PJOB pJobRunning; /* Linked list of jobs. */
317PJOB pJobRunningEnd; /* Last job entry. */
318
319HMTX hmtxJobQueueFine; /* Read/Write mutex for the next two queues. */
320HEV hevJobQueueFine; /* Posted when there is more output. */
321PJOB pJobCompleted; /* Linked list of successful jobs. */
322PJOB pJobCompletedLast; /* Last successful job entry. */
323PJOB pJobFailed; /* Linked list of failed jobs. */
324PJOB pJobFailedLast; /* Last failed job entry. */
325ULONG cJobsFinished; /* Count of jobs finished (failed or completed). */
326
327HMTX hmtxExec; /* Execute childs mutex sem. Required */
328 /* since we redirect standard files handles */
329 /* and changes the currentdirectory. */
330
331/*******************************************************************************
332* Internal Functions *
333*******************************************************************************/
334void syntax(void);
335
336/* operations */
337int Init(const char *arg0, int cWorkers);
338int Daemon(int cWorkers);
339int DaemonInit(int cWorkers);
340void signalhandlerDaemon(int sig);
341void signalhandlerClient(int sig);
342void Worker(void * iWorkerId);
343char*WorkerArguments(char *pszArg, const char *pszzEnv, const char *pszCommand, char *pszCurDir, PPATHCACHE pPathCache);
344char*fileNormalize(char *pszFilename, char *pszCurDir);
345APIRET fileExist(const char *pszFilename);
346int Submit(int rcIgnore);
347int Wait(void);
348int QueryRunning(void);
349int Kill(void);
350int ShowJobs(void);
351int ShowRunningJobs(void);
352int ShowCompletedJobs(void);
353int ShowFailedJobs(void);
354/* shared memory helpers */
355int shrmemCreate(void);
356int shrmemOpen(void);
357void shrmemFree(void);
358int shrmemSendDaemon(BOOL fWait);
359int shrmemSendClient(int enmMsgTypeResponse);
360
361/* error handling */
362void _Optlink Error(const char *pszFormat, ...);
363
364
365int main(int argc, char **argv)
366{
367
368 /*
369 * Display help.
370 */
371 if (argc < 2 || (argv[1][0] == '-'))
372 {
373 syntax();
374 if (argc < 2)
375 {
376 printf("\n!syntax error!");
377 return -1;
378 }
379 return 0;
380 }
381
382 /*
383 * String switch on command.
384 */
385 if (!stricmp(argv[1], "submit"))
386 {
387 int rcIgnore = 0;
388 if (argc == 2)
389 {
390 printf("fatal error: There is no job to submit...\n");
391 return -1;
392 }
393 if (argv[2][0] == '-' && (rcIgnore = atoi(argv[2]+1)) <= 0)
394 {
395 printf("syntax error: Invalid ignore return code number...\n");
396 return -1;
397 }
398 return Submit(rcIgnore);
399 }
400 else if (!stricmp(argv[1], "wait"))
401 {
402 return Wait();
403 }
404 else if (!strcmp(argv[1], "queryrunning"))
405 {
406 return QueryRunning();
407 }
408 else if (!strcmp(argv[1], "kill"))
409 {
410 return Kill();
411 }
412 else if (!strcmp(argv[1], "showjobs"))
413 {
414 return ShowJobs();
415 }
416 else if (!strcmp(argv[1], "showrunningjobs"))
417 {
418 return ShowRunningJobs();
419 }
420 else if (!strcmp(argv[1], "showcompletedjobs"))
421 {
422 return ShowCompletedJobs();
423 }
424 else if (!strcmp(argv[1], "showfailedjobs"))
425 {
426 return ShowFailedJobs();
427 }
428 else if (!strcmp(argv[1], "init"))
429 {
430 if (argc != 3 || atoi(argv[2]) <= 0 || atoi(argv[2]) >= 256)
431 {
432 printf("fatal error: invalid/missing number of workers.\n");
433 return -1;
434 }
435 return Init(argv[0], atoi(argv[2]));
436 }
437 else if (!strcmp(argv[1], "!Daemon!"))
438 {
439 if (argc != 3 || atoi(argv[2]) <= 0)
440 {
441 printf("fatal error: no worker count specified or to many parameters.\n");
442 return -2;
443 }
444
445 return Daemon(atoi(argv[2]));
446 }
447 else
448 {
449 syntax();
450 printf("\n!invalid command '%s'.\n", argv[1]);
451 return -1;
452 }
453
454 //return 0;
455}
456
457
458/**
459 * Display syntax.
460 */
461void syntax(void)
462{
463 printf(
464 "Command Queue Daemon v0.0.2\n"
465 "---------------------------\n"
466 "syntax: CmdQd.exe <command> [args]\n"
467 "\n"
468 "commands:\n"
469 " init <workers>\n"
470 " Initiates the command queue daemon with the given number of workers.\n"
471 "\n"
472 " submit [-<n>] <command> [args]\n"
473 " Submits a command to the daemon.\n"
474 " Use '-<n>' to tell use to ignore return code 0-n.\n"
475 "\n"
476 " wait\n"
477 " Wait for all commands which are queued up to complete.\n"
478 " rc = count of failing commands.\n"
479 "\n"
480 " kill\n"
481 " Kills the daemon. Daemon will automatically die after\n"
482 " idling for some time.\n"
483 "\n"
484 " queryrunning\n"
485 " Checks if the daemon is running.\n"
486 " rc = 0 if running; rc != 0 if not running.\n"
487 "\n"
488 " showjobs - shows jobs queued for execution.\n"
489 " showrunningjobs - shows jobs currently running.\n"
490 " showcompletedjobs - shows jobs succesfully executed.\n"
491 " showfailedjobs - shows jobs which failed.\n"
492 "\n"
493 "Copyright (c) 2001 knut st. osmundsen (kosmunds@csc.com)\n"
494 );
495}
496
497
498/**
499 * Starts a daemon process.
500 * @returns 0 on success.
501 * -4 on error.
502 * @param arg0 Executable filename.
503 * @param cWorkers Number of workers to start.
504 */
505int Init(const char *arg0, int cWorkers)
506{
507 int rc;
508 RESULTCODES Res; /* dummy, unused */
509 char szArg[CCHMAXPATH + 32];
510
511 sprintf(&szArg[0], "%s\t!Daemon! %d", arg0, cWorkers);
512 szArg[strlen(arg0)] = '\0';
513 rc = DosExecPgm(NULL, 0, EXEC_BACKGROUND, &szArg[0], NULL, &Res, &szArg[0]);
514 if (rc)
515 Error("Fatal error: Failed to start daemon. rc=%d\n");
516 return rc;
517}
518
519
520/**
521 * This process is to be a daemon with a given number of works.
522 * @returns 0 on success.
523 * -4 on error.
524 * @param cWorkers Number of workers to start.
525 * @sketch
526 */
527int Daemon(int cWorkers)
528{
529 int rc;
530
531 /*
532 * Init Shared memory
533 */
534 rc = shrmemCreate();
535 if (rc)
536 return rc;
537
538 /*
539 * Init queues and semaphores.
540 */
541 rc = DaemonInit(cWorkers);
542 if (rc)
543 {
544 shrmemFree();
545 return rc;
546 }
547
548 /*
549 * Do work!
550 */
551 rc = shrmemSendDaemon(TRUE);
552 while (!rc)
553 {
554 switch (pShrMem->enmMsgType)
555 {
556 case msgSubmit:
557 {
558 PJOB pJob;
559
560 /*
561 * Make job entry.
562 */
563 _heap_check();
564 pJob = malloc((int)&((PJOB)0)->JobInfo.szzEnv[pShrMem->u1.Submit.cchEnv]);
565 if (pJob)
566 {
567 _heap_check();
568 memcpy(&pJob->JobInfo, &pShrMem->u1.Submit,
569 (int)&((struct Submit *)0)->szzEnv[pShrMem->u1.Submit.cchEnv]);
570 _heap_check();
571 pJob->rc = -1;
572 pJob->pNext = NULL;
573 pJob->pJobOutput = NULL;
574
575 /*
576 * Insert the entry.
577 */
578 rc = DosRequestMutexSem(hmtxJobQueue, SEM_INDEFINITE_WAIT);
579 if (rc)
580 break;
581 if (!pJobQueue)
582 pJobQueueEnd = pJobQueue = pJob;
583 else
584 {
585 pJobQueueEnd->pNext = pJob;
586 pJobQueueEnd = pJob;
587 }
588 cJobs++;
589 DosReleaseMutexSem(hmtxJobQueue);
590
591 /*
592 * Post the queue to wake up workers.
593 */
594 DosPostEventSem(hevJobQueue);
595 pShrMem->u1.SubmitResponse.fRc = TRUE;
596 }
597 else
598 {
599 Error("Internal Error: Out of memory (line=%d)\n", __LINE__);
600 pShrMem->u1.SubmitResponse.fRc = FALSE;
601 }
602 pShrMem->enmMsgType = msgSubmitResponse;
603 rc = shrmemSendDaemon(TRUE);
604 break;
605 }
606
607
608 case msgWait:
609 {
610 PJOB pJob = NULL;
611 PJOBOUTPUT pJobOutput = NULL;
612 char * psz;
613 int cch = 0;
614 char * pszOutput;
615 int cchOutput;
616 int rcFailure = 0;
617 BOOL fMore = TRUE;
618 ULONG ulIgnore;
619 void * pv;
620
621 DosPostEventSem(hevJobQueueFine); /* just so we don't get stuck in the loop... */
622 do
623 {
624 /* init response message */
625 pShrMem->enmMsgType = msgWaitResponse;
626 pShrMem->u1.WaitResponse.szOutput[0] = '\0';
627 pszOutput = &pShrMem->u1.WaitResponse.szOutput[0];
628 cchOutput = sizeof(pShrMem->u1.WaitResponse.szOutput) - 1;
629
630 /*
631 * Wait for output.
632 */
633 /*rc = DosWaitEventSem(hevJobQueueFine, SEM_INDEFINITE_WAIT); - there is some timing problem here, */
634 rc = DosWaitEventSem(hevJobQueueFine, 1000); /* timeout after 1 second. */
635 if (rc && rc != ERROR_TIMEOUT)
636 break;
637 rc = NO_ERROR; /* in case of TIMEOUT */
638
639 /*
640 * Copy output - Optimized so we don't cause to many context switches.
641 */
642 do
643 {
644 /*
645 * Next job.
646 */
647 if (!pJobOutput)
648 {
649 rc = DosRequestMutexSem(hmtxJobQueueFine, SEM_INDEFINITE_WAIT);
650 if (rc)
651 break;
652 pv = pJob;
653 pJob = pJobCompleted;
654 if (pJob)
655 {
656 pJobCompleted = pJob->pNext;
657 if (!pJobCompleted)
658 pJobCompletedLast = NULL;
659 }
660
661 if (!pJob && cJobs == cJobsFinished)
662 { /* all jobs finished, we may start output failures. */
663 pJob = pJobFailed;
664 if (pJob)
665 {
666 if (rcFailure == 0)
667 rcFailure = pJob->rc;
668
669 pJobFailed = pJob->pNext;
670 if (!pJobFailed)
671 pJobFailedLast = NULL;
672 }
673 else
674 fMore = FALSE;
675 }
676 else
677 DosResetEventSem(hevJobQueueFine, &ulIgnore); /* No more output, prepare wait. */
678 DosReleaseMutexSem(hmtxJobQueueFine);
679
680 if (pJob && pJob->pJobOutput)
681 {
682 pJobOutput = pJob->pJobOutput;
683 psz = pJobOutput->szOutput;
684 cch = pJobOutput->cchOutput;
685 }
686 if (pv)
687 free(pv);
688 }
689
690 /*
691 * Anything to output?
692 */
693 if (pJobOutput)
694 {
695 /*
696 * Copy output.
697 */
698 do
699 {
700 if (cch)
701 { /* copy */
702 int cchCopy = min(cch, cchOutput);
703 memcpy(pszOutput, psz, cchCopy);
704 psz += cchCopy; cch -= cchCopy;
705 pszOutput += cchCopy; cchOutput -= cchCopy;
706 }
707 if (!cch)
708 { /* next chunk */
709 pv = pJobOutput;
710 pJobOutput = pJobOutput->pNext;
711 if (pJobOutput)
712 {
713 psz = &pJobOutput->szOutput[0];
714 cch = pJobOutput->cchOutput;
715 }
716 free(pv);
717 }
718 } while (cch && cchOutput);
719 }
720 else
721 break; /* no more output, let's send what we got. */
722
723 } while (!rc && fMore && cchOutput);
724
725 /*
726 * We've got a message to send.
727 */
728 if (rc)
729 break;
730 *pszOutput = '\0';
731 pShrMem->u1.WaitResponse.rc = rcFailure;
732 pShrMem->u1.WaitResponse.fMore = fMore;
733 rc = shrmemSendDaemon(TRUE);
734 } while (!rc && fMore);
735
736 /*
737 * Check if the wait client died.
738 */
739 if (rc == ERROR_ALREADY_POSTED) /* seems like this is the rc we get. */
740 {
741 /*
742 * BUGBUG: This code is really fishy, but I'm to tired to make a real fix now.
743 * Hopefully this solves my current problem.
744 */
745 ULONG ulDummy;
746 rc = DosRequestMutexSem(pShrMem->hmtx, 500);
747 rc = DosResetEventSem(pShrMem->hevClient, &ulDummy);
748 pShrMem->enmMsgType = msgUnknown;
749 rc = shrmemSendDaemon(TRUE);
750 }
751 break;
752 }
753
754
755 case msgKill:
756 {
757 pShrMem->enmMsgType = msgKillResponse;
758 pShrMem->u1.KillResponse.fRc = TRUE;
759 shrmemSendDaemon(FALSE);
760 rc = -1;
761 break;
762 }
763
764
765 case msgShowJobs:
766 {
767 /*
768 * Gain access to the job list.
769 */
770 rc = DosRequestMutexSem(hmtxJobQueue, SEM_INDEFINITE_WAIT);
771 if (!rc)
772 {
773 int iJob = 0;
774 PJOB pJob = pJobQueue;
775
776 /*
777 * Big loop making and sending all messages.
778 */
779 do
780 {
781 int cch;
782 char * pszOutput;
783 int cchOutput;
784
785 /*
786 * Make one message.
787 */
788 pShrMem->enmMsgType = msgShowJobsResponse;
789 pszOutput = &pShrMem->u1.ShowJobsResponse.szOutput[0];
790 cchOutput = sizeof(pShrMem->u1.ShowJobsResponse.szOutput) - 1;
791
792 /*
793 * Insert job info.
794 */
795 while (pJob)
796 {
797 char szTmp[8192]; /* this is sufficient for one job. */
798
799 /*
800 * Format output in temporary buffer and check if
801 * it's space left in the share buffer.
802 */
803 cch = sprintf(szTmp,
804 "------------------ Job %d\n"
805 " command: %s\n"
806 " curdir: %s\n"
807 " rcIgnore: %d\n",
808 iJob,
809 pJob->JobInfo.szCommand,
810 pJob->JobInfo.szCurrentDir,
811 pJob->JobInfo.rcIgnore);
812 if (cch > cchOutput)
813 break;
814
815 /*
816 * Copy from temporary to shared buffer.
817 */
818 memcpy(pszOutput, szTmp, cch);
819 pszOutput += cch;
820 cchOutput -= cch;
821
822 /*
823 * Next job.
824 */
825 pJob = pJob->pNext;
826 iJob++;
827 }
828
829 /*
830 * Send the message.
831 */
832 *pszOutput = '\0';
833 pShrMem->u1.ShowJobsResponse.fMore = pJob != NULL;
834 rc = shrmemSendDaemon(TRUE);
835
836 } while (!rc && pJob);
837
838
839 /*
840 * Release the job list.
841 */
842 DosReleaseMutexSem(hmtxJobQueue);
843 }
844 else
845 {
846 /* init response message */
847 pShrMem->enmMsgType = msgShowJobsResponse;
848 sprintf(&pShrMem->u1.ShowJobsResponse.szOutput[0],
849 "Internal Error. Requesting of hmtxJobQueue failed with rc=%d\n",
850 rc);
851 rc = shrmemSendDaemon(TRUE);
852 }
853
854
855 /*
856 * Check if the waiting client died.
857 */
858 if (rc == ERROR_ALREADY_POSTED) /* seems like this is the rc we get. */
859 {
860 /*
861 * BUGBUG: This code is really fishy, but I'm to tired to make a real fix now.
862 * Hopefully this solves my current problem.
863 */
864 ULONG ulDummy;
865 rc = DosRequestMutexSem(pShrMem->hmtx, 500);
866 rc = DosResetEventSem(pShrMem->hevClient, &ulDummy);
867 pShrMem->enmMsgType = msgUnknown;
868 rc = shrmemSendDaemon(TRUE);
869 }
870 break;
871 }
872
873
874 case msgShowFailedJobs:
875 {
876 /*
877 * Gain access to the finished job list.
878 */
879 rc = DosRequestMutexSem(hmtxJobQueueFine, SEM_INDEFINITE_WAIT);
880 if (!rc)
881 {
882 int iJob = 0;
883 PJOB pJob = pJobFailed;
884
885 /*
886 * Big loop making and sending all messages.
887 */
888 do
889 {
890 int cch;
891 char * pszOutput;
892 int cchOutput;
893
894 /*
895 * Make one message.
896 */
897 pShrMem->enmMsgType = msgShowFailedJobsResponse;
898 pszOutput = &pShrMem->u1.ShowFailedJobsResponse.szOutput[0];
899 cchOutput = sizeof(pShrMem->u1.ShowFailedJobsResponse.szOutput) - 1;
900
901 /*
902 * Insert job info.
903 */
904 while (pJob)
905 {
906 char szTmp[8192]; /* this is sufficient for one job. */
907
908 /*
909 * Format output in temporary buffer and check if
910 * it's space left in the share buffer.
911 */
912 cch = sprintf(szTmp,
913 "------------------ Failed Job %d\n"
914 " command: %s\n"
915 " curdir: %s\n"
916 " rc: %d (rcIgnore=%d)\n",
917 iJob,
918 pJob->JobInfo.szCommand,
919 pJob->JobInfo.szCurrentDir,
920 pJob->rc,
921 pJob->JobInfo.rcIgnore);
922 if (cch > cchOutput)
923 break;
924
925 /*
926 * Copy from temporary to shared buffer.
927 */
928 memcpy(pszOutput, szTmp, cch);
929 pszOutput += cch;
930 cchOutput -= cch;
931
932 /*
933 * Next job.
934 */
935 pJob = pJob->pNext;
936 iJob++;
937 }
938
939 /*
940 * Send the message.
941 */
942 *pszOutput = '\0';
943 pShrMem->u1.ShowFailedJobsResponse.fMore = pJob != NULL;
944 rc = shrmemSendDaemon(TRUE);
945
946 } while (!rc && pJob);
947
948
949 /*
950 * Release the job list.
951 */
952 DosReleaseMutexSem(hmtxJobQueueFine);
953 }
954 else
955 {
956 /* init response message */
957 pShrMem->enmMsgType = msgShowFailedJobsResponse;
958 sprintf(&pShrMem->u1.ShowFailedJobsResponse.szOutput[0],
959 "Internal Error. Requesting of hmtxJobQueue failed with rc=%d\n",
960 rc);
961 rc = shrmemSendDaemon(TRUE);
962 }
963
964
965 /*
966 * Check if the waiting client died.
967 */
968 if (rc == ERROR_ALREADY_POSTED) /* seems like this is the rc we get. */
969 {
970 /*
971 * BUGBUG: This code is really fishy, but I'm to tired to make a real fix now.
972 * Hopefully this solves my current problem.
973 */
974 ULONG ulDummy;
975 rc = DosRequestMutexSem(pShrMem->hmtx, 500);
976 rc = DosResetEventSem(pShrMem->hevClient, &ulDummy);
977 pShrMem->enmMsgType = msgUnknown;
978 rc = shrmemSendDaemon(TRUE);
979 }
980 break;
981 }
982
983
984 case msgShowRunningJobs:
985 {
986 /*
987 * Gain access to the job list.
988 */
989 rc = DosRequestMutexSem(hmtxJobQueue, SEM_INDEFINITE_WAIT);
990 if (!rc)
991 {
992 int iJob = 0;
993 PJOB pJob = pJobRunning;
994
995 /*
996 * Big loop making and sending all messages.
997 */
998 do
999 {
1000 int cch;
1001 char * pszOutput;
1002 int cchOutput;
1003
1004 /*
1005 * Make one message.
1006 */
1007 pShrMem->enmMsgType = msgShowRunningJobsResponse;
1008 pszOutput = &pShrMem->u1.ShowRunningJobsResponse.szOutput[0];
1009 cchOutput = sizeof(pShrMem->u1.ShowRunningJobsResponse.szOutput) - 1;
1010
1011 /*
1012 * Insert job info.
1013 */
1014 while (pJob)
1015 {
1016 char szTmp[8192]; /* this is sufficient for one job. */
1017
1018 /*
1019 * Format output in temporary buffer and check if
1020 * it's space left in the share buffer.
1021 */
1022 cch = sprintf(szTmp,
1023 "------------------ Running Job %d\n"
1024 " command: %s\n"
1025 " curdir: %s\n"
1026 " rcIgnore: %d\n",
1027 iJob,
1028 pJob->JobInfo.szCommand,
1029 pJob->JobInfo.szCurrentDir,
1030 pJob->JobInfo.rcIgnore);
1031 if (cch > cchOutput)
1032 break;
1033
1034 /*
1035 * Copy from temporary to shared buffer.
1036 */
1037 memcpy(pszOutput, szTmp, cch);
1038 pszOutput += cch;
1039 cchOutput -= cch;
1040
1041 /*
1042 * Next job.
1043 */
1044 pJob = pJob->pNext;
1045 iJob++;
1046 }
1047
1048 /*
1049 * Send the message.
1050 */
1051 *pszOutput = '\0';
1052 pShrMem->u1.ShowRunningJobsResponse.fMore = pJob != NULL;
1053 rc = shrmemSendDaemon(TRUE);
1054
1055 } while (!rc && pJob);
1056
1057
1058 /*
1059 * Release the job list.
1060 */
1061 DosReleaseMutexSem(hmtxJobQueue);
1062 }
1063 else
1064 {
1065 /* init response message */
1066 pShrMem->enmMsgType = msgShowRunningJobsResponse;
1067 sprintf(&pShrMem->u1.ShowRunningJobsResponse.szOutput[0],
1068 "Internal Error. Requesting of hmtxJobQueue failed with rc=%d\n",
1069 rc);
1070 rc = shrmemSendDaemon(TRUE);
1071 }
1072
1073
1074 /*
1075 * Check if the waiting client died.
1076 */
1077 if (rc == ERROR_ALREADY_POSTED) /* seems like this is the rc we get. */
1078 {
1079 /*
1080 * BUGBUG: This code is really fishy, but I'm to tired to make a real fix now.
1081 * Hopefully this solves my current problem.
1082 */
1083 ULONG ulDummy;
1084 rc = DosRequestMutexSem(pShrMem->hmtx, 500);
1085 rc = DosResetEventSem(pShrMem->hevClient, &ulDummy);
1086 pShrMem->enmMsgType = msgUnknown;
1087 rc = shrmemSendDaemon(TRUE);
1088 }
1089 break;
1090 }
1091
1092
1093
1094 case msgShowCompletedJobs:
1095 {
1096 /*
1097 * Gain access to the finished job list.
1098 */
1099 rc = DosRequestMutexSem(hmtxJobQueueFine, SEM_INDEFINITE_WAIT);
1100 if (!rc)
1101 {
1102 int iJob = 0;
1103 PJOB pJob = pJobCompleted;
1104
1105 /*
1106 * Big loop making and sending all messages.
1107 */
1108 do
1109 {
1110 int cch;
1111 char * pszOutput;
1112 int cchOutput;
1113
1114 /*
1115 * Make one message.
1116 */
1117 pShrMem->enmMsgType = msgShowCompletedJobsResponse;
1118 pszOutput = &pShrMem->u1.ShowCompletedJobsResponse.szOutput[0];
1119 cchOutput = sizeof(pShrMem->u1.ShowCompletedJobsResponse.szOutput) - 1;
1120
1121 /*
1122 * Insert job info.
1123 */
1124 while (pJob)
1125 {
1126 char szTmp[8192]; /* this is sufficient for one job. */
1127
1128 /*
1129 * Format output in temporary buffer and check if
1130 * it's space left in the share buffer.
1131 */
1132 cch = sprintf(szTmp,
1133 "------------------ Completed Job %d\n"
1134 " command: %s\n"
1135 " curdir: %s\n"
1136 " rcIgnore: %d\n",
1137 iJob,
1138 pJob->JobInfo.szCommand,
1139 pJob->JobInfo.szCurrentDir,
1140 pJob->JobInfo.rcIgnore);
1141 if (cch > cchOutput)
1142 break;
1143
1144 /*
1145 * Copy from temporary to shared buffer.
1146 */
1147 memcpy(pszOutput, szTmp, cch);
1148 pszOutput += cch;
1149 cchOutput -= cch;
1150
1151 /*
1152 * Next job.
1153 */
1154 pJob = pJob->pNext;
1155 iJob++;
1156 }
1157
1158 /*
1159 * Send the message.
1160 */
1161 *pszOutput = '\0';
1162 pShrMem->u1.ShowCompletedJobsResponse.fMore = pJob != NULL;
1163 rc = shrmemSendDaemon(TRUE);
1164
1165 } while (!rc && pJob);
1166
1167
1168 /*
1169 * Release the finished job list.
1170 */
1171 DosReleaseMutexSem(hmtxJobQueueFine);
1172 }
1173 else
1174 {
1175 /* init response message */
1176 pShrMem->enmMsgType = msgShowCompletedJobsResponse;
1177 sprintf(&pShrMem->u1.ShowCompletedJobsResponse.szOutput[0],
1178 "Internal Error. Requesting of hmtxJobQueue failed with rc=%d\n",
1179 rc);
1180 rc = shrmemSendDaemon(TRUE);
1181 }
1182
1183
1184 /*
1185 * Check if the waiting client died.
1186 */
1187 if (rc == ERROR_ALREADY_POSTED) /* seems like this is the rc we get. */
1188 {
1189 /*
1190 * BUGBUG: This code is really fishy, but I'm to tired to make a real fix now.
1191 * Hopefully this solves my current problem.
1192 */
1193 ULONG ulDummy;
1194 rc = DosRequestMutexSem(pShrMem->hmtx, 500);
1195 rc = DosResetEventSem(pShrMem->hevClient, &ulDummy);
1196 pShrMem->enmMsgType = msgUnknown;
1197 rc = shrmemSendDaemon(TRUE);
1198 }
1199 break;
1200 }
1201
1202
1203
1204
1205 default:
1206 Error("Internal error: Invalid message id %d\n", pShrMem->enmMsgType);
1207 pShrMem->enmMsgType = msgUnknown;
1208 rc = shrmemSendDaemon(TRUE);
1209 }
1210 }
1211
1212 /*
1213 * Set dying msg type. shrmemFree posts the hevClient so clients
1214 * waiting for the daemon to respond will quit.
1215 */
1216 pShrMem->enmMsgType = msgDying;
1217
1218 /*
1219 * Cleanup.
1220 */
1221 shrmemFree();
1222 DosCloseMutexSem(hmtxJobQueue);
1223 DosCloseMutexSem(hmtxJobQueueFine);
1224 DosCloseEventSem(hevJobQueueFine);
1225 DosCloseMutexSem(hmtxExec);
1226 DosCloseEventSem(hevJobQueue);
1227
1228 _dump_allocated(16);
1229
1230 return 0;
1231}
1232
1233
1234/**
1235 * Help which does most of the daemon init stuff.
1236 * @returns 0 on success.
1237 * @param cWorkers Number of worker threads to start.
1238 */
1239int DaemonInit(int cWorkers)
1240{
1241 int rc;
1242 int i;
1243
1244 /*
1245 * Init queues and semaphores.
1246 */
1247 rc = DosCreateEventSem(NULL, &hevJobQueue, 0, FALSE);
1248 if (!rc)
1249 {
1250 rc = DosCreateMutexSem(NULL, &hmtxJobQueue, 0, FALSE);
1251 if (!rc)
1252 {
1253 rc = DosCreateMutexSem(NULL, &hmtxJobQueueFine, 0, FALSE);
1254 if (!rc)
1255 {
1256 rc = DosCreateEventSem(NULL, &hevJobQueueFine, 0, FALSE);
1257 if (!rc)
1258 {
1259 rc = DosCreateMutexSem(NULL, &hmtxExec, 0, FALSE);
1260 if (!rc)
1261 {
1262 /*
1263 * Start workers.
1264 */
1265 rc = 0;
1266 for (i = 0; i < cWorkers; i++)
1267 if (_beginthread(Worker, NULL, 64*1024, (void*)i) == -1)
1268 {
1269 Error("Fatal error: failed to create worker thread no. %d\n", i);
1270 rc = -1;
1271 break;
1272 }
1273 if (!rc)
1274 {
1275 DosSetMaxFH(cWorkers * 6 + 20);
1276 return 0; /* success! */
1277 }
1278
1279 /* failure */
1280 DosCloseMutexSem(hmtxExec);
1281 }
1282 else
1283 Error("Fatal error: failed to create exec mutex. rc=%d", rc);
1284 DosCloseEventSem(hevJobQueueFine);
1285 }
1286 else
1287 Error("Fatal error: failed to create job queue fine event sem. rc=%d", rc);
1288 DosCloseMutexSem(hmtxJobQueueFine);
1289 }
1290 else
1291 Error("Fatal error: failed to create job queue fine mutex. rc=%d", rc);
1292 DosCloseMutexSem(hmtxJobQueue);
1293 }
1294 else
1295 Error("Fatal error: failed to create job queue mutex. rc=%d", rc);
1296 DosCloseEventSem(hevJobQueue);
1297 }
1298 else
1299 Error("Fatal error: failed to create job queue event sem. rc=%d", rc);
1300
1301 return rc;
1302}
1303
1304
1305/**
1306 * Daemon signal handler.
1307 */
1308void signalhandlerDaemon(int sig)
1309{
1310 /*
1311 * Set dying msg type. shrmemFree posts the hevClient so clients
1312 * waiting for the daemon to respond will quit.
1313 */
1314 pShrMem->enmMsgType = msgDying;
1315
1316 /*
1317 * Free and exit.
1318 */
1319 shrmemFree();
1320 exit(-42);
1321 sig = sig;
1322}
1323
1324
1325/**
1326 * Client signal handler.
1327 */
1328void signalhandlerClient(int sig)
1329{
1330 shrmemFree();
1331 exit(-42);
1332 sig = sig;
1333}
1334
1335
1336
1337/**
1338 * Worker thread.
1339 * @param iWorkerId The worker process id.
1340 * @sketch
1341 */
1342void Worker(void * iWorkerId)
1343{
1344 PATHCACHE PathCache;
1345 memset(&PathCache, 0, sizeof(PathCache));
1346
1347 while (!DosWaitEventSem(hevJobQueue, SEM_INDEFINITE_WAIT))
1348 {
1349 PJOB pJob;
1350
1351 /*
1352 * Get job.
1353 */
1354 if (DosRequestMutexSem(hmtxJobQueue, SEM_INDEFINITE_WAIT))
1355 return;
1356 pJob = pJobQueue;
1357 if (pJob)
1358 {
1359 /* remove from input queue */
1360 if (pJob != pJobQueueEnd)
1361 pJobQueue = pJob->pNext;
1362 else
1363 {
1364 ULONG ulIgnore;
1365 pJobQueue = pJobQueueEnd = NULL;
1366 DosResetEventSem(hevJobQueue, &ulIgnore);
1367 }
1368
1369 /* insert into running */
1370 pJob ->pNext = NULL;
1371 if (pJobRunningEnd)
1372 pJobRunningEnd->pNext = pJob;
1373 else
1374 pJobRunning = pJobRunningEnd = pJob;
1375 }
1376 DosReleaseMutexSem(hmtxJobQueue);
1377
1378 /*
1379 * Execute job.
1380 */
1381 if (pJob)
1382 {
1383 int rc;
1384 char szArg[4096];
1385 char szObj[256];
1386 PJOBOUTPUT pJobOutput = NULL;
1387 PJOBOUTPUT pJobOutputLast = NULL;
1388 RESULTCODES Res;
1389 PID pid;
1390 HFILE hStdOut = HF_STDOUT;
1391 HFILE hStdErr = HF_STDERR;
1392 HFILE hStdOutSave = -1;
1393 HFILE hStdErrSave = -1;
1394 HPIPE hPipeR = NULLHANDLE;
1395 HPIPE hPipeW = NULLHANDLE;
1396
1397 //printf("debug-%d: start %s\n", iWorkerId, pJob->JobInfo.szCommand);
1398
1399 /*
1400 * Redirect output and start process.
1401 */
1402 WorkerArguments(&szArg[0], &pJob->JobInfo.szzEnv[0], &pJob->JobInfo.szCommand[0],
1403 &pJob->JobInfo.szCurrentDir[0], &PathCache);
1404 rc = DosCreatePipe(&hPipeR, &hPipeW, sizeof(pJobOutput->szOutput) - 1);
1405 if (rc)
1406 {
1407 Error("Internal Error: Failed to create pipe! rc=%d\n", rc);
1408 return;
1409 }
1410
1411 if (DosRequestMutexSem(hmtxExec, SEM_INDEFINITE_WAIT))
1412 {
1413 DosClose(hPipeR);
1414 DosClose(hPipeW);
1415 return;
1416 }
1417
1418 pJob->pJobOutput = pJobOutput = pJobOutputLast = malloc(sizeof(JOBOUTPUT));
1419 pJobOutput->pNext = NULL;
1420 pJobOutput->cchOutput = sprintf(pJobOutput->szOutput, "Job: %s\n", pJob->JobInfo.szCommand);
1421
1422 rc = DosSetDefaultDisk( pJob->JobInfo.szCurrentDir[0] >= 'a'
1423 ? pJob->JobInfo.szCurrentDir[0] - 'a' + 1
1424 : pJob->JobInfo.szCurrentDir[0] - 'A' + 1);
1425 rc += DosSetCurrentDir(pJob->JobInfo.szCurrentDir);
1426 if (!rc)
1427 {
1428 assert( pJob->JobInfo.szzEnv[pJob->JobInfo.cchEnv-1] == '\0'
1429 && pJob->JobInfo.szzEnv[pJob->JobInfo.cchEnv-2] == '\0');
1430 DosDupHandle(HF_STDOUT, &hStdOutSave);
1431 DosDupHandle(HF_STDERR, &hStdErrSave);
1432 DosDupHandle(hPipeW, &hStdOut);
1433 DosDupHandle(hPipeW, &hStdErr);
1434 rc = DosExecPgm(szObj, sizeof(szObj), EXEC_ASYNCRESULT,
1435 szArg, pJob->JobInfo.szzEnv, &Res, szArg);
1436 DosClose(hStdOut); hStdOut = HF_STDOUT;
1437 DosClose(hStdErr); hStdErr = HF_STDERR;
1438 DosDupHandle(hStdOutSave, &hStdOut);
1439 DosDupHandle(hStdErrSave, &hStdErr);
1440 DosClose(hStdOutSave);
1441 DosClose(hStdErrSave);
1442 DosReleaseMutexSem(hmtxExec);
1443 DosClose(hPipeW);
1444
1445
1446 /*
1447 * Read Output.
1448 */
1449 if (!rc)
1450 {
1451 ULONG cchRead;
1452 ULONG cchRead2 = 0;
1453
1454 cchRead = sizeof(pJobOutput->szOutput) - pJobOutput->cchOutput - 1;
1455 while (((rc = DosRead(hPipeR,
1456 &pJobOutput->szOutput[pJobOutput->cchOutput],
1457 cchRead, &cchRead2)) == NO_ERROR
1458 || rc == ERROR_MORE_DATA)
1459 && cchRead2 != 0)
1460 {
1461 pJobOutput->cchOutput += cchRead2;
1462 pJobOutput->szOutput[pJobOutput->cchOutput] = '\0';
1463
1464 /* prepare next read */
1465 cchRead = sizeof(pJobOutput->szOutput) - pJobOutput->cchOutput - 1;
1466 if (cchRead < 16)
1467 {
1468 pJobOutput = pJobOutput->pNext = malloc(sizeof(JOBOUTPUT));
1469 pJobOutput->pNext = NULL;
1470 pJobOutput->cchOutput = 0;
1471 cchRead = sizeof(pJobOutput->szOutput) - 1;
1472 }
1473 cchRead2 = 0;
1474 }
1475 rc = 0;
1476 }
1477
1478 /* finished reading */
1479 DosClose(hPipeR);
1480
1481 /*
1482 * Get result.
1483 */
1484 if (!rc)
1485 {
1486 DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &Res, &pid, Res.codeTerminate);
1487 if ( Res.codeResult <= pJob->JobInfo.rcIgnore
1488 && Res.codeTerminate == TC_EXIT)
1489 pJob->rc = 0;
1490 else
1491 {
1492 pJob->rc = -1;
1493 rc = sprintf(szArg, "failed with rc=%d term=%d\n", Res.codeResult, Res.codeTerminate);
1494 if (rc + pJobOutput->cchOutput + 1 >= sizeof(pJobOutput->szOutput))
1495 {
1496 pJobOutput = pJobOutput->pNext = malloc(sizeof(JOBOUTPUT));
1497 pJobOutput->pNext = NULL;
1498 pJobOutput->cchOutput = 0;
1499 }
1500 strcpy(&pJobOutput->szOutput[pJobOutput->cchOutput], szArg);
1501 pJobOutput->cchOutput += rc;
1502 }
1503 }
1504 else
1505 {
1506 pJobOutput->cchOutput += sprintf(&pJobOutput->szOutput[pJobOutput->cchOutput],
1507 "DosExecPgm failed with rc=%d for command %s %s\n"
1508 " obj=%s\n",
1509 rc, szArg, pJob->JobInfo.szCommand, szObj);
1510 pJob->rc = -1;
1511 }
1512 }
1513 else
1514 {
1515 /*
1516 * ChDir failed.
1517 */
1518 DosReleaseMutexSem(hmtxExec);
1519 pJobOutput->cchOutput += sprintf(&pJobOutput->szOutput[pJobOutput->cchOutput ],
1520 "Failed to set current directory to: %s\n",
1521 pJob->JobInfo.szCurrentDir);
1522 pJob->rc = -1;
1523 DosClose(hPipeR);
1524 }
1525
1526
1527 /*
1528 * Remove from the running queue.
1529 */
1530 if (DosRequestMutexSem(hmtxJobQueue, SEM_INDEFINITE_WAIT))
1531 return;
1532
1533 if (pJobRunning != pJob)
1534 {
1535 PJOB pJobCur = pJobRunning;
1536 while (pJobCur)
1537 {
1538 if (pJobCur->pNext != pJob)
1539 {
1540 pJobCur->pNext = pJob->pNext;
1541 if (pJob == pJobRunningEnd)
1542 pJobRunningEnd = pJobCur;
1543 break;
1544 }
1545 pJobCur = pJobCur->pNext;
1546 }
1547 }
1548 else
1549 pJobRunning = pJobRunningEnd = NULL;
1550
1551 DosReleaseMutexSem(hmtxJobQueue);
1552
1553
1554 /*
1555 * Insert result in result queue.
1556 */
1557 if (DosRequestMutexSem(hmtxJobQueueFine, SEM_INDEFINITE_WAIT))
1558 return;
1559 pJob->pNext = NULL;
1560 if (!pJob->rc) /* 0 on success. */
1561 {
1562 if (pJobCompletedLast)
1563 pJobCompletedLast->pNext = pJob;
1564 else
1565 pJobCompleted = pJob;
1566 pJobCompletedLast = pJob;
1567 }
1568 else
1569 {
1570 if (pJobFailedLast)
1571 pJobFailedLast->pNext = pJob;
1572 else
1573 pJobFailed = pJob;
1574 pJobFailedLast = pJob;
1575 }
1576 cJobsFinished++;
1577 DosReleaseMutexSem(hmtxJobQueueFine);
1578 /* wake up Wait. */
1579 DosPostEventSem(hevJobQueueFine);
1580 //printf("debug-%d: fine\n", iWorkerId);
1581 }
1582 }
1583 iWorkerId = iWorkerId;
1584}
1585
1586
1587/**
1588 * Builds the input to DosExecPgm.
1589 * Will execute programs directly and command thru the shell.
1590 *
1591 * @returns pszArg.
1592 * @param pszArg Arguments to DosExecPgm.(output)
1593 * Assumes that the buffer is large enought.
1594 * @param pszzEnv Pointer to environment block.
1595 * @param pszCommand Command to execute.
1596 * @param pszCurDir From where the command is to executed.
1597 * @param pPathCache Used to cache the last path, executable, and the search result.
1598 */
1599char *WorkerArguments(char *pszArg, const char *pszzEnv, const char *pszCommand, char *pszCurDir, PPATHCACHE pPathCache)
1600{
1601 BOOL fCMD = FALSE;
1602 const char *psz;
1603 const char *psz2;
1604 char * pszW;
1605 char ch;
1606 int cch;
1607 APIRET rc;
1608
1609 /*
1610 * Check if this is multiple command separated by either &, && or |.
1611 * Currently ignoring quotes for this test.
1612 */
1613 if ( strchr(pszCommand, '&')
1614 || strchr(pszCommand, '|'))
1615 {
1616 strcpy(pszArg, "cmd.exe"); /* doesn't use comspec, just defaults to cmd.exe in all cases. */
1617 fCMD = TRUE;
1618 psz2 = pszCommand; /* start of arguments. */
1619 }
1620 else
1621 {
1622 char chEnd = ' ';
1623
1624 /*
1625 * Parse out the first name.
1626 */
1627 for (psz = pszCommand; *psz == '\t' || *psz == ' ';) //strip(,'L');
1628 psz++;
1629 if (*psz == '"' || *psz == '\'')
1630 chEnd = *psz++;
1631 psz2 = psz;
1632 if (chEnd == ' ')
1633 {
1634 while ((ch = *psz) != '\0' && ch != ' ' && ch != '\t')
1635 psz++;
1636 }
1637 else
1638 {
1639 while ((ch = *psz) != '\0' && ch != chEnd)
1640 psz++;
1641 }
1642 *pszArg = '\0';
1643 strncat(pszArg, psz2, psz - psz2);
1644 psz2 = psz+1; /* start of arguments. */
1645 }
1646
1647
1648 /*
1649 * Resolve the executable name if not qualified.
1650 * NB! We doesn't fully support references to other driveletters yet. (TODO/BUGBUG)
1651 */
1652 /* correct slashes */
1653 pszW = pszArg;
1654 while ((pszW = strchr(pszW, '//')) != NULL)
1655 *pszW++ = '\\';
1656
1657 /* make sure it ends with .exe */
1658 pszW = pszArg + strlen(pszArg) - 1;
1659 while (pszW > pszArg && *pszW != '.' && *pszW != '\\')
1660 pszW--;
1661 if (*pszW != '.')
1662 strcat(pszArg, ".exe");
1663
1664 if (pszArg[1] != ':' || *pszArg == *pszCurDir)
1665 {
1666 rc = -1; /* indicate that we've not found the file. */
1667
1668 /* relative path? - expand it */
1669 if (strchr(pszArg, '\\') || pszArg[1] == ':')
1670 { /* relative path - expand it and check for file existence */
1671 fileNormalize(pszArg, pszCurDir);
1672 rc = fileExist(pszArg);
1673 }
1674 else
1675 { /* Search path. */
1676 const char *pszPath = pszzEnv;
1677 while (*pszPath != '\0' && strncmp(pszPath, "PATH=", 5))
1678 pszPath += strlen(pszPath) + 1;
1679
1680 if (pszPath && *pszPath != '\0')
1681 {
1682 /* check cache */
1683 if ( !strcmp(pPathCache->szProgram, pszArg)
1684 && !strcmp(pPathCache->szPath, pszPath)
1685 && !strcmp(pPathCache->szCurDir, pszCurDir)
1686 )
1687 {
1688 strcpy(pszArg, pPathCache->szResult);
1689 rc = fileExist(pszArg);
1690 }
1691
1692 if (rc)
1693 { /* search path */
1694 char szResult[CCHMAXPATH];
1695 rc = DosSearchPath(SEARCH_IGNORENETERRS, (PSZ)pszPath, pszArg, &szResult[0] , sizeof(szResult));
1696 if (!rc)
1697 {
1698 strcpy(pszArg, szResult);
1699
1700 /* update cache */
1701 strcpy(pPathCache->szProgram, pszArg);
1702 strcpy(pPathCache->szPath, pszPath);
1703 strcpy(pPathCache->szCurDir, pszCurDir);
1704 strcpy(pPathCache->szResult, szResult);
1705 }
1706 }
1707 }
1708 }
1709 }
1710 /* else nothing to do - assume full path (btw. we don't have the current dir for other drives anyway :-) */
1711 else
1712 rc = !fCMD ? fileExist(pszArg) : NO_ERROR;
1713
1714 /* In case of error use CMD */
1715 if (rc && !fCMD)
1716 {
1717 strcpy(pszArg, "cmd.exe"); /* doesn't use comspec, just defaults to cmd.exe in all cases. */
1718 fCMD = TRUE;
1719 psz2 = pszCommand; /* start of arguments. */
1720 }
1721
1722
1723 /*
1724 * Complete the argument string.
1725 * ---
1726 * szArg current holds the command.
1727 * psz2 points to the first parameter. (needs strip(,'L'))
1728 */
1729 while ((ch = *psz2) != '\0' && (ch == '\t' || ch == ' '))
1730 psz2++;
1731
1732 pszW = pszArg + strlen(pszArg) + 1;
1733 cch = strlen(psz2);
1734 if (!fCMD)
1735 {
1736 memcpy(pszW, psz2, ++cch);
1737 pszW[cch] = '\0';
1738 }
1739 else
1740 {
1741 strcpy(pszW, "/C \"");
1742 pszW += strlen(pszW);
1743 memcpy(pszW, psz2, cch);
1744 memcpy(pszW + cch, "\"\0", 3);
1745 }
1746
1747 return pszArg;
1748}
1749
1750
1751
1752/**
1753 * Normalizes the path slashes for the filename. It will partially expand paths too.
1754 * @returns pszFilename
1755 * @param pszFilename Pointer to filename string. Not empty string!
1756 * Much space to play with.
1757 * @remark (From fastdep.)
1758 */
1759char *fileNormalize(char *pszFilename, char *pszCurDir)
1760{
1761 char * psz;
1762 int aiSlashes[CCHMAXPATH/2];
1763 int cSlashes;
1764 int i;
1765
1766 /*
1767 * Init stuff.
1768 */
1769 for (i = 1, cSlashes; pszCurDir[i] != '\0'; i++)
1770 {
1771 if (pszCurDir[i] == '/')
1772 pszCurDir[i] = '\\';
1773 if (pszCurDir[i] == '\\')
1774 aiSlashes[cSlashes++] = i;
1775 }
1776 if (pszCurDir[i-1] != '\\')
1777 {
1778 aiSlashes[cSlashes] = i;
1779 pszCurDir[i++] = '\\';
1780 pszCurDir[i] = '\0';
1781 }
1782
1783
1784 /* expand path? */
1785 pszFilename = psz;
1786 if (pszFilename[1] != ':')
1787 { /* relative path */
1788 int iSlash;
1789 char szFile[CCHMAXPATH];
1790 char * psz = szFile;
1791
1792 strcpy(szFile, pszFilename);
1793 iSlash = *psz == '\\' ? 1 : cSlashes;
1794 while (*psz != '\0')
1795 {
1796 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
1797 { /* up one directory */
1798 if (iSlash > 0)
1799 iSlash--;
1800 psz += 3;
1801 }
1802 else if (*psz == '.' && psz[1] == '\\')
1803 { /* no change */
1804 psz += 2;
1805 }
1806 else
1807 { /* completed expantion! */
1808 strncpy(pszFilename, pszCurDir, aiSlashes[iSlash]+1);
1809 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
1810 break;
1811 }
1812 }
1813 }
1814 /* else: assume full path */
1815
1816 return psz;
1817}
1818
1819/**
1820 * Checks if a given file exist.
1821 * @returns 0 if the file exists. (NO_ERROR)
1822 * 2 if the file doesn't exist. (ERROR_FILE_NOT_FOUND)
1823 * @param pszFilename Name of the file to check existance for.
1824 */
1825APIRET fileExist(const char *pszFilename)
1826{
1827 FILESTATUS3 fsts3;
1828 return DosQueryPathInfo((PSZ)pszFilename, FIL_STANDARD, &fsts3, sizeof(fsts3));
1829}
1830
1831
1832/**
1833 * Submits a command to the daemon.
1834 * @returns 0 on success.
1835 * -3 on failure.
1836 * @param rcIgnore Ignores returcodes ranging from 0 to rcIgnore.
1837 */
1838int Submit(int rcIgnore)
1839{
1840 int cch;
1841 int rc;
1842 char * psz;
1843 PPIB ppib;
1844 PTIB ptib;
1845
1846 DosGetInfoBlocks(&ptib, &ppib);
1847 rc = shrmemOpen();
1848 if (rc)
1849 return rc;
1850
1851 /*
1852 * Build message.
1853 */
1854 pShrMem->enmMsgType = msgSubmit;
1855 pShrMem->u1.Submit.rcIgnore = rcIgnore;
1856 _getcwd(pShrMem->u1.Submit.szCurrentDir, sizeof(pShrMem->u1.Submit.szCurrentDir));
1857
1858 /* command */
1859 psz = ppib->pib_pchcmd;
1860 psz += strlen(psz) + 1 + 7; /* 7 = strlen("submit ")*/
1861 while (*psz == ' ' || *psz == '\t')
1862 psz++;
1863 if (*psz == '-')
1864 {
1865 while (*psz != ' ' && *psz != '\t')
1866 psz++;
1867 while (*psz == ' ' || *psz == '\t')
1868 psz++;
1869 }
1870 cch = strlen(psz) + 1;
1871 if (cch > sizeof(pShrMem->u1.Submit.szCommand))
1872 {
1873 Error("Fatal error: Command too long.\n");
1874 shrmemFree();
1875 return -1;
1876 }
1877 if (*psz == '"' && psz[cch-2] == '"') /* remove start & end quotes if any */
1878 {
1879 cch--;
1880 psz++;
1881 }
1882 memcpy(&pShrMem->u1.Submit.szCommand[0], psz, cch);
1883
1884 /* environment */
1885 for (cch = 1, psz = ppib->pib_pchenv; *psz != '\0';)
1886 {
1887 int cchVar = strlen(psz) + 1;
1888 cch += cchVar;
1889 psz += cchVar;
1890 }
1891 if ( ppib->pib_pchenv[cch-2] != '\0'
1892 || ppib->pib_pchenv[cch-1] != '\0')
1893 {
1894 Error("internal error\n");
1895 return -1;
1896 }
1897 if (cch > sizeof(pShrMem->u1.Submit.szzEnv))
1898 {
1899 Error("Fatal error: environment is to bit, cchEnv=%d\n", cch);
1900 shrmemFree();
1901 return -ERROR_BAD_ENVIRONMENT;
1902 }
1903 pShrMem->u1.Submit.cchEnv = cch;
1904 memcpy(&pShrMem->u1.Submit.szzEnv[0], ppib->pib_pchenv, cch);
1905
1906
1907 /*
1908 * Send message and get respons.
1909 */
1910 rc = shrmemSendClient(msgSubmitResponse);
1911 if (rc)
1912 {
1913 shrmemFree();
1914 return rc;
1915 }
1916
1917 rc = !pShrMem->u1.SubmitResponse.fRc;
1918 shrmemFree();
1919 return rc;
1920}
1921
1922
1923/**
1924 * Waits for the commands to complete.
1925 * Will write all output from completed command to stdout.
1926 * Will write failing commands last.
1927 * @returns Count of failing commands.
1928 */
1929int Wait(void)
1930{
1931 int rc;
1932
1933 rc = shrmemOpen();
1934 if (rc)
1935 return rc;
1936 do
1937 {
1938 pShrMem->enmMsgType = msgWait;
1939 pShrMem->u1.Wait.iNothing = 0;
1940 rc = shrmemSendClient(msgWaitResponse);
1941 if (rc)
1942 {
1943 shrmemFree();
1944 return -1;
1945 }
1946 printf("%s", pShrMem->u1.WaitResponse.szOutput);
1947 /*
1948 * Release the client mutex if more data and yield the CPU.
1949 * So we can submit more work. (Odin nmake lib...)
1950 */
1951 if (pShrMem->u1.WaitResponse.fMore)
1952 {
1953 DosReleaseMutexSem(pShrMem->hmtxClient);
1954 DosSleep(0);
1955 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
1956 if (rc)
1957 {
1958 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
1959 shrmemFree();
1960 return -1;
1961 }
1962 }
1963 } while (pShrMem->u1.WaitResponse.fMore);
1964
1965 rc = pShrMem->u1.WaitResponse.rc;
1966 shrmemFree();
1967 return rc;
1968}
1969
1970
1971/**
1972 * Checks if the daemon is running.
1973 */
1974int QueryRunning(void)
1975{
1976 APIRET rc;
1977 rc = DosGetNamedSharedMem((PPVOID)(PVOID)&pShrMem,
1978 SHARED_MEM_NAME,
1979 PAG_READ | PAG_WRITE);
1980 if (!rc)
1981 DosFreeMem(pShrMem);
1982
1983 return rc;
1984}
1985
1986
1987/**
1988 * Sends a kill command to the daemon to kill it and its workers.
1989 * @returns 0.
1990 */
1991int Kill(void)
1992{
1993 int rc;
1994
1995 rc = shrmemOpen();
1996 if (rc)
1997 return rc;
1998
1999 pShrMem->enmMsgType = msgKill;
2000 pShrMem->u1.Kill.iNothing = 0;
2001 rc = shrmemSendClient(msgKillResponse);
2002 if (!rc)
2003 rc = !pShrMem->u1.KillResponse.fRc;
2004
2005 shrmemFree();
2006 return rc;
2007}
2008
2009
2010/**
2011 * Shows the current queued commands.
2012 * Will write to stdout.
2013 * @returns 0 or -1 usually.
2014 */
2015int ShowJobs(void)
2016{
2017 int rc;
2018
2019 rc = shrmemOpen();
2020 if (rc)
2021 return rc;
2022 do
2023 {
2024 pShrMem->enmMsgType = msgShowJobs;
2025 pShrMem->u1.ShowJobs.iNothing = 0;
2026 rc = shrmemSendClient(msgShowJobsResponse);
2027 if (rc)
2028 {
2029 shrmemFree();
2030 return -1;
2031 }
2032 printf("%s", pShrMem->u1.ShowJobsResponse.szOutput);
2033 /*
2034 * Release the client mutex if more data and yield the CPU.
2035 * So we can submit more work. (Odin nmake lib...)
2036 */
2037 if (pShrMem->u1.ShowJobsResponse.fMore)
2038 {
2039 DosReleaseMutexSem(pShrMem->hmtxClient);
2040 DosSleep(0);
2041 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2042 if (rc)
2043 {
2044 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2045 shrmemFree();
2046 return -1;
2047 }
2048 }
2049 } while (pShrMem->u1.ShowJobsResponse.fMore);
2050
2051 shrmemFree();
2052 return rc;
2053}
2054
2055
2056/**
2057 * Shows the current running jobs (not the output).
2058 * Will write to stdout.
2059 * @returns 0 or -1 usually.
2060 */
2061int ShowRunningJobs(void)
2062{
2063 int rc;
2064
2065 rc = shrmemOpen();
2066 if (rc)
2067 return rc;
2068 do
2069 {
2070 pShrMem->enmMsgType = msgShowRunningJobs;
2071 pShrMem->u1.ShowRunningJobs.iNothing = 0;
2072 rc = shrmemSendClient(msgShowRunningJobsResponse);
2073 if (rc)
2074 {
2075 shrmemFree();
2076 return -1;
2077 }
2078 printf("%s", pShrMem->u1.ShowRunningJobsResponse.szOutput);
2079 /*
2080 * Release the client mutex if more data and yield the CPU.
2081 * So we can submit more work. (Odin nmake lib...)
2082 */
2083 if (pShrMem->u1.ShowRunningJobsResponse.fMore)
2084 {
2085 DosReleaseMutexSem(pShrMem->hmtxClient);
2086 DosSleep(0);
2087 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2088 if (rc)
2089 {
2090 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2091 shrmemFree();
2092 return -1;
2093 }
2094 }
2095 } while (pShrMem->u1.ShowRunningJobsResponse.fMore);
2096
2097 shrmemFree();
2098 return rc;
2099}
2100
2101
2102/**
2103 * Shows the current queue of successfully completed jobs (not the output).
2104 * Will write to stdout.
2105 * @returns 0 or -1 usually.
2106 */
2107int ShowCompletedJobs(void)
2108{
2109 int rc;
2110
2111 rc = shrmemOpen();
2112 if (rc)
2113 return rc;
2114 do
2115 {
2116 pShrMem->enmMsgType = msgShowCompletedJobs;
2117 pShrMem->u1.ShowCompletedJobs.iNothing = 0;
2118 rc = shrmemSendClient(msgShowCompletedJobsResponse);
2119 if (rc)
2120 {
2121 shrmemFree();
2122 return -1;
2123 }
2124 printf("%s", pShrMem->u1.ShowCompletedJobsResponse.szOutput);
2125 /*
2126 * Release the client mutex if more data and yield the CPU.
2127 * So we can submit more work. (Odin nmake lib...)
2128 */
2129 if (pShrMem->u1.ShowCompletedJobsResponse.fMore)
2130 {
2131 DosReleaseMutexSem(pShrMem->hmtxClient);
2132 DosSleep(0);
2133 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2134 if (rc)
2135 {
2136 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2137 shrmemFree();
2138 return -1;
2139 }
2140 }
2141 } while (pShrMem->u1.ShowCompletedJobsResponse.fMore);
2142
2143 shrmemFree();
2144 return rc;
2145}
2146
2147
2148/**
2149 * Shows the current queue of failed jobs (not the output).
2150 * Will write to stdout.
2151 * @returns 0 or -1 usually.
2152 */
2153int ShowFailedJobs(void)
2154{
2155 int rc;
2156
2157 rc = shrmemOpen();
2158 if (rc)
2159 return rc;
2160 do
2161 {
2162 pShrMem->enmMsgType = msgShowFailedJobs;
2163 pShrMem->u1.ShowFailedJobs.iNothing = 0;
2164 rc = shrmemSendClient(msgShowFailedJobsResponse);
2165 if (rc)
2166 {
2167 shrmemFree();
2168 return -1;
2169 }
2170 printf("%s", pShrMem->u1.ShowFailedJobsResponse.szOutput);
2171 /*
2172 * Release the client mutex if more data and yield the CPU.
2173 * So we can submit more work. (Odin nmake lib...)
2174 */
2175 if (pShrMem->u1.ShowFailedJobsResponse.fMore)
2176 {
2177 DosReleaseMutexSem(pShrMem->hmtxClient);
2178 DosSleep(0);
2179 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2180 if (rc)
2181 {
2182 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2183 shrmemFree();
2184 return -1;
2185 }
2186 }
2187 } while (pShrMem->u1.ShowFailedJobsResponse.fMore);
2188
2189 shrmemFree();
2190 return rc;
2191}
2192
2193
2194
2195/**
2196 * Creates the shared memory area.
2197 * The creator owns the memory when created.
2198 * @returns 0 on success. Error code on error.
2199 */
2200int shrmemCreate(void)
2201{
2202 int rc;
2203 rc = DosAllocSharedMem((PPVOID)(PVOID)&pShrMem,
2204 SHARED_MEM_NAME,
2205 SHARED_MEM_SIZE,
2206 PAG_COMMIT | PAG_READ | PAG_WRITE);
2207 if (rc)
2208 {
2209 Error("Fatal error: Failed to create shared memory object. rc=%d\n", rc);
2210 return rc;
2211 }
2212
2213 rc = DosCreateEventSem(NULL, &pShrMem->hevDaemon, DC_SEM_SHARED, FALSE);
2214 if (rc)
2215 {
2216 Error("Fatal error: Failed to create daemon event semaphore. rc=%d\n", rc);
2217 DosFreeMem(pShrMem);
2218 return rc;
2219 }
2220
2221 rc = DosCreateEventSem(NULL, &pShrMem->hevClient, DC_SEM_SHARED, FALSE);
2222 if (rc)
2223 {
2224 Error("Fatal error: Failed to create client event semaphore. rc=%d\n", rc);
2225 DosCloseEventSem(pShrMem->hevDaemon);
2226 DosFreeMem(pShrMem);
2227 return rc;
2228 }
2229
2230 rc = DosCreateMutexSem(NULL, &pShrMem->hmtx, DC_SEM_SHARED, TRUE);
2231 if (rc)
2232 {
2233 Error("Fatal error: Failed to create mutex semaphore. rc=%d\n", rc);
2234 DosCloseEventSem(pShrMem->hevClient);
2235 DosCloseEventSem(pShrMem->hevDaemon);
2236 DosFreeMem(pShrMem);
2237 return rc;
2238 }
2239
2240 rc = DosCreateMutexSem(NULL, &pShrMem->hmtxClient, DC_SEM_SHARED, FALSE);
2241 if (rc)
2242 {
2243 Error("Fatal error: Failed to create client mutex semaphore. rc=%d\n", rc);
2244 DosCloseEventSem(pShrMem->hevClient);
2245 DosCloseEventSem(pShrMem->hevClient);
2246 DosCloseEventSem(pShrMem->hevDaemon);
2247 DosFreeMem(pShrMem);
2248 return rc;
2249 }
2250
2251
2252 /*
2253 * Install signal handlers.
2254 */
2255 signal(SIGSEGV, signalhandlerDaemon);
2256 signal(SIGTERM, signalhandlerDaemon);
2257 signal(SIGABRT, signalhandlerDaemon);
2258 signal(SIGINT, signalhandlerDaemon);
2259 signal(SIGBREAK,signalhandlerDaemon);
2260
2261 return rc;
2262}
2263
2264
2265/**
2266 * Opens the shared memory and the semaphores.
2267 * The caller is owner of the memory upon successful return.
2268 * @returns 0 on success. Error code on error.
2269 */
2270int shrmemOpen(void)
2271{
2272 int rc;
2273
2274 rc = DosGetNamedSharedMem((PPVOID)(PVOID)&pShrMem,
2275 SHARED_MEM_NAME,
2276 PAG_READ | PAG_WRITE);
2277 if (rc)
2278 {
2279 Error("Fatal error: Failed to open shared memory. rc=%d\n");
2280 return rc;
2281 }
2282
2283 rc = DosOpenEventSem(NULL, &pShrMem->hevClient);
2284 if (rc)
2285 {
2286 Error("Fatal error: Failed to open client event semaphore. rc=%d\n");
2287 DosFreeMem(pShrMem);
2288 return rc;
2289 }
2290
2291 rc = DosOpenEventSem(NULL, &pShrMem->hevDaemon);
2292 if (rc)
2293 {
2294 Error("Fatal error: Failed to open daemon event semaphore. rc=%d\n");
2295 DosCloseEventSem(pShrMem->hevClient);
2296 DosFreeMem(pShrMem);
2297 return rc;
2298 }
2299
2300 rc = DosOpenMutexSem(NULL, &pShrMem->hmtx);
2301 if (rc)
2302 {
2303 Error("Fatal error: Failed to open mutex semaphore. rc=%d\n");
2304 DosCloseEventSem(pShrMem->hevClient);
2305 DosCloseEventSem(pShrMem->hevDaemon);
2306 DosFreeMem(pShrMem);
2307 return rc;
2308 }
2309
2310 rc = DosOpenMutexSem(NULL, &pShrMem->hmtxClient);
2311 if (rc)
2312 {
2313 Error("Fatal error: Failed to open client mutex semaphore. rc=%d\n");
2314 DosCloseEventSem(pShrMem->hevClient);
2315 DosCloseEventSem(pShrMem->hevDaemon);
2316 DosCloseMutexSem(pShrMem->hmtx);
2317 DosFreeMem(pShrMem);
2318 return rc;
2319 }
2320
2321 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2322 if (rc)
2323 {
2324 Error("Fatal error: Failed to open take ownership of client mutex semaphore. rc=%d\n");
2325 shrmemFree();
2326 return rc;
2327 }
2328
2329 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2330 if (rc)
2331 {
2332 Error("Fatal error: Failed to open take ownership of mutex semaphore. rc=%d\n");
2333 shrmemFree();
2334 return rc;
2335 }
2336
2337
2338 /*
2339 * Install signal handlers.
2340 */
2341 signal(SIGSEGV, signalhandlerClient);
2342 signal(SIGTERM, signalhandlerClient);
2343 signal(SIGABRT, signalhandlerClient);
2344 signal(SIGINT, signalhandlerClient);
2345 signal(SIGBREAK,signalhandlerClient);
2346
2347 return rc;
2348}
2349
2350
2351/**
2352 * Frees the shared memory and the associated semaphores.
2353 */
2354void shrmemFree(void)
2355{
2356 if (!pShrMem)
2357 return;
2358 /* wakeup any clients */
2359 DosPostEventSem(pShrMem->hevClient);
2360 /* free stuff */
2361 DosReleaseMutexSem(pShrMem->hmtxClient);
2362 DosReleaseMutexSem(pShrMem->hmtx);
2363 DosCloseMutexSem(pShrMem->hmtxClient);
2364 DosCloseMutexSem(pShrMem->hmtx);
2365 DosCloseEventSem(pShrMem->hevClient);
2366 DosCloseEventSem(pShrMem->hevDaemon);
2367 DosFreeMem(pShrMem);
2368 pShrMem = NULL;
2369}
2370
2371
2372/**
2373 * Daemon sends a message.
2374 * Upon we don't own the shared memory any longer.
2375 * @returns 0 on success. Error code on error.
2376 * -1 on timeout.
2377 * @param fWait Wait for new message.
2378 */
2379int shrmemSendDaemon(BOOL fWait)
2380{
2381 ULONG ulDummy;
2382 int rc;
2383
2384 /* send message */
2385 DosResetEventSem(pShrMem->hevDaemon, &ulDummy);
2386 rc = DosReleaseMutexSem(pShrMem->hmtx);
2387 if (!rc)
2388 rc = DosPostEventSem(pShrMem->hevClient);
2389
2390 /* wait for next message */
2391 if (!rc && fWait)
2392 {
2393 do
2394 {
2395 rc = DosWaitEventSem(pShrMem->hevDaemon, IDLE_TIMEOUT_MS);
2396 } while (rc == ERROR_TIMEOUT && pJobQueue);
2397
2398 if (rc == ERROR_TIMEOUT)
2399 {
2400 DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2401 shrmemFree();
2402 return -1;
2403 }
2404
2405 if (!rc)
2406 {
2407 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2408 if (rc == ERROR_SEM_OWNER_DIED)
2409 {
2410 DosCloseMutexSem(pShrMem->hmtx);
2411 pShrMem->hmtx = NULLHANDLE;
2412 rc = DosCreateMutexSem(NULL, &pShrMem->hmtx, DC_SEM_SHARED, TRUE);
2413 }
2414 }
2415
2416 if (rc && rc != ERROR_INTERRUPT)
2417 Error("Internal error: failed to get next message from daemon, rc=%d\n", rc);
2418 }
2419 else
2420 Error("Internal error: failed to send message from daemon, rc=%d\n", rc);
2421 return rc;
2422}
2423
2424
2425/**
2426 * Client sends a message.
2427 * Upon we don't own the shared memory any longer.
2428 * @returns 0 on success. Error code on error.
2429 * @param enmMsgTypeResponse The expected response on this message.
2430 */
2431int shrmemSendClient(int enmMsgTypeResponse)
2432{
2433 ULONG ulDummy;
2434 int rc;
2435
2436 /* send message */
2437 DosResetEventSem(pShrMem->hevClient, &ulDummy);
2438 rc = DosReleaseMutexSem(pShrMem->hmtx);
2439 if (!rc)
2440 rc = DosPostEventSem(pShrMem->hevDaemon);
2441
2442 /* wait for response */
2443 if (!rc)
2444 {
2445 rc = DosWaitEventSem(pShrMem->hevClient, SEM_INDEFINITE_WAIT);
2446 if (!rc)
2447 {
2448 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2449 if (rc == ERROR_SEM_OWNER_DIED)
2450 {
2451 Error("Internal error: shared mem mutex owner died.\n");
2452 return -1;
2453 }
2454
2455 if (!rc && pShrMem->enmMsgType != enmMsgTypeResponse)
2456 {
2457 if (pShrMem->enmMsgType != msgDying)
2458 Error("Internal error: Invalid response message. response=%d expected=%d\n",
2459 pShrMem->enmMsgType, enmMsgTypeResponse);
2460 else
2461 Error("Fatal error: daemon just died!\n");
2462 return -1;
2463 }
2464 }
2465 if (rc && rc != ERROR_INTERRUPT)
2466 Error("Internal error: failed to get response message from daemon, rc=%d\n", rc);
2467 }
2468 else
2469 Error("Internal error: failed to send message to daemon, rc=%d\n", rc);
2470
2471 return rc;
2472}
2473
2474
2475/**
2476 * printf lookalike used to print all run-tim errors.
2477 * @param pszFormat Format string.
2478 * @param ... Arguments (optional).
2479 */
2480void Error(const char *pszFormat, ...)
2481{
2482 va_list arg;
2483
2484 va_start(arg, pszFormat);
2485 vfprintf(stdout, pszFormat, arg);
2486 va_end(arg);
2487}
2488
2489
2490#ifdef DEBUGMEMORY
2491void my_free(void *pv)
2492{
2493 DosFreeMem((PVOID)((unsigned)pv & 0xffff0000));
2494}
2495
2496void *my_malloc(size_t cb)
2497{
2498 APIRET rc;
2499 PVOID pv;
2500 ULONG cbAlloc;
2501 char szMsg[200];
2502
2503 cbAlloc = (cb + 0x1fff) & (~0x0fff);
2504
2505 rc = DosAllocMem(&pv, cbAlloc, PAG_READ | PAG_WRITE);
2506 if (!rc)
2507 {
2508 rc = DosSetMem(pv, cbAlloc - 0x1000, PAG_READ | PAG_WRITE | PAG_COMMIT);
2509 if (rc)
2510 __interrupt(3);
2511 if (cb & 0xfff)
2512 pv = (PVOID)((unsigned)pv + 0x1000 - (cb & 0x0fff));
2513 }
2514
2515 strcpy(szMsg, "malloc(");
2516 _itoa(cb, szMsg + strlen(szMsg), 16);
2517 strcat(szMsg, ") -> ");
2518 _itoa(pv, szMsg + strlen(szMsg), 16);
2519 strcat(szMsg, "\r\n");
2520
2521 DosPutMessage(1, strlen(szMsg), szMsg);
2522
2523 return rc ? NULL : pv;
2524}
2525#endif
Note: See TracBrowser for help on using the repository browser.