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

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

Corrected a few bugs. Introduced JobId and multiple daemons.

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