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

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

Tried to make some kind of handshake to fix dead mutexes.

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