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

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

Last try. Now it should work!!!

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