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

Last change on this file since 8480 was 8480, checked in by bird, 23 years ago

Fix for the inherited pipes, save files and stdin.

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