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

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

Corrected minor bug.

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