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

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

Now does it work....

File size: 86.5 KB
Line 
1/* $Id: CmdQd.c,v 1.18 2002-05-24 03:39:08 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 HFILE hDummy;
1462 static BOOL fStdClosed = FALSE;
1463 int rc;
1464 int rcDbg;
1465 char szArg[4096];
1466 char szObj[256];
1467 PJOBOUTPUT pJobOutput = NULL;
1468 PJOBOUTPUT pJobOutputLast = NULL;
1469 RESULTCODES Res;
1470 PID pid;
1471 HPIPE hPipeR = -1;
1472 HPIPE hPipeW = -1;
1473 HFILE hStdErr;
1474 HFILE hStdOut;
1475
1476 //printf("debug-%d: start %s\n", iWorkerId, pJob->JobInfo.szCommand);
1477
1478 /*
1479 * Redirect output and start process.
1480 */
1481 WorkerArguments(&szArg[0], &pJob->JobInfo.szzEnv[0], &pJob->JobInfo.szCommand[0],
1482 &pJob->JobInfo.szCurrentDir[0], &PathCache);
1483 pJob->pJobOutput = pJobOutput = pJobOutputLast = malloc(sizeof(JOBOUTPUT));
1484 pJobOutput->pNext = NULL;
1485 pJobOutput->cchOutput = sprintf(pJobOutput->szOutput, "Job: %s\n", pJob->JobInfo.szCommand);
1486
1487 if (DosRequestMutexSem(hmtxExec, SEM_INDEFINITE_WAIT))
1488 {
1489 Error("Internal Error: Failed to take exec mutex! rc=%d\n", rc);
1490 return;
1491 }
1492
1493 if (!fStdClosed)
1494 { /* only do this once! */
1495 HFILE hf;
1496 fclose(stdin);
1497 DosClose(HF_STDIN);
1498 hDummy = HF_STDIN;
1499 for (hf = 1; hf < 20; hf++)
1500 if (!(rcDbg = DosDupHandle(hf, &hDummy)))
1501 break;
1502 assert(!rcDbg);
1503 rcDbg = DosSetFHState(hDummy, OPEN_FLAGS_NOINHERIT); assert(!rcDbg);
1504 fclose(stdout); DosClose(HF_STDOUT);
1505 fclose(stderr); DosClose(HF_STDERR);
1506 hStdOut = HF_STDOUT; rcDbg = DosDupHandle(hDummy, &hStdOut);assert(!rcDbg);
1507 assert(hStdOut == HF_STDOUT);
1508 hStdErr = HF_STDERR; rcDbg = DosDupHandle(hDummy, &hStdErr);assert(!rcDbg);
1509 assert(hStdErr == HF_STDERR);
1510 fStdClosed = TRUE;
1511 }
1512
1513 rc = DosCreatePipe(&hPipeR, &hPipeW, sizeof(pJobOutput->szOutput) - 1);
1514 if (rc)
1515 {
1516 DosReleaseMutexSem(hmtxExec);
1517 Error("Internal Error: Failed to create pipe! rc=%d\n", rc);
1518 return;
1519 }
1520 assert(hPipeW != HF_STDOUT && hPipeW != HF_STDERR);
1521 assert(hPipeR != HF_STDOUT && hPipeR != HF_STDERR);
1522
1523 rc = DosSetDefaultDisk( pJob->JobInfo.szCurrentDir[0] >= 'a'
1524 ? pJob->JobInfo.szCurrentDir[0] - 'a' + 1
1525 : pJob->JobInfo.szCurrentDir[0] - 'A' + 1);
1526 rc += DosSetCurrentDir(pJob->JobInfo.szCurrentDir);
1527 if (!rc)
1528 {
1529 hStdErr = HF_STDERR;
1530 hStdOut = HF_STDOUT;
1531 assert( pJob->JobInfo.szzEnv[pJob->JobInfo.cchEnv-1] == '\0'
1532 && pJob->JobInfo.szzEnv[pJob->JobInfo.cchEnv-2] == '\0');
1533 rcDbg = DosDupHandle(hPipeW, &hStdOut); assert(!rcDbg);
1534 rcDbg = DosDupHandle(hPipeW, &hStdErr); assert(!rcDbg);
1535 rcDbg = DosClose(hPipeW); assert(!rcDbg);
1536 rcDbg = DosSetFHState(hPipeR, OPEN_FLAGS_NOINHERIT); assert(!rcDbg);
1537 rc = DosExecPgm(szObj, sizeof(szObj), EXEC_ASYNCRESULT,
1538 szArg, pJob->JobInfo.szzEnv, &Res, szArg);
1539 /* Placeholders, prevents clashes... */
1540 hStdOut = HF_STDOUT;
1541 rcDbg = DosDupHandle(hDummy, &hStdOut); assert(!rcDbg);
1542 hStdErr = HF_STDERR;
1543 rcDbg = DosDupHandle(hDummy, &hStdErr); assert(!rcDbg);
1544 DosReleaseMutexSem(hmtxExec);
1545
1546
1547 /*
1548 * Read Output.
1549 */
1550 if (!rc)
1551 {
1552 ULONG cchRead;
1553 ULONG cchRead2 = 0;
1554
1555 cchRead = sizeof(pJobOutput->szOutput) - pJobOutput->cchOutput - 1;
1556 while (((rc = DosRead(hPipeR,
1557 &pJobOutput->szOutput[pJobOutput->cchOutput],
1558 cchRead, &cchRead2)) == NO_ERROR
1559 || rc == ERROR_MORE_DATA)
1560 && cchRead2 != 0)
1561 {
1562 pJobOutput->cchOutput += cchRead2;
1563 pJobOutput->szOutput[pJobOutput->cchOutput] = '\0';
1564
1565 /* prepare next read */
1566 cchRead = sizeof(pJobOutput->szOutput) - pJobOutput->cchOutput - 1;
1567 if (cchRead < 16)
1568 {
1569 pJobOutput = pJobOutput->pNext = malloc(sizeof(JOBOUTPUT));
1570 pJobOutput->pNext = NULL;
1571 pJobOutput->cchOutput = 0;
1572 cchRead = sizeof(pJobOutput->szOutput) - 1;
1573 }
1574 cchRead2 = 0;
1575 }
1576 rc = 0;
1577 }
1578
1579 /* finished reading */
1580 DosClose(hPipeR);
1581
1582 /*
1583 * Get result.
1584 */
1585 if (!rc)
1586 {
1587 DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &Res, &pid, Res.codeTerminate);
1588 if ( Res.codeResult <= pJob->JobInfo.rcIgnore
1589 && Res.codeTerminate == TC_EXIT)
1590 pJob->rc = 0;
1591 else
1592 {
1593 pJob->rc = -1;
1594 rc = sprintf(szArg, "failed with rc=%d term=%d\n", Res.codeResult, Res.codeTerminate);
1595 if (rc + pJobOutput->cchOutput + 1 >= sizeof(pJobOutput->szOutput))
1596 {
1597 pJobOutput = pJobOutput->pNext = malloc(sizeof(JOBOUTPUT));
1598 pJobOutput->pNext = NULL;
1599 pJobOutput->cchOutput = 0;
1600 }
1601 strcpy(&pJobOutput->szOutput[pJobOutput->cchOutput], szArg);
1602 pJobOutput->cchOutput += rc;
1603 }
1604 }
1605 else
1606 {
1607 pJobOutput->cchOutput += sprintf(&pJobOutput->szOutput[pJobOutput->cchOutput],
1608 "DosExecPgm failed with rc=%d for command %s %s\n"
1609 " obj=%s\n",
1610 rc, szArg, pJob->JobInfo.szCommand, szObj);
1611 pJob->rc = -1;
1612 }
1613 }
1614 else
1615 {
1616 /*
1617 * ChDir failed.
1618 */
1619 DosReleaseMutexSem(hmtxExec);
1620 pJobOutput->cchOutput += sprintf(&pJobOutput->szOutput[pJobOutput->cchOutput ],
1621 "Failed to set current directory to: %s (rc=%d)\n",
1622 pJob->JobInfo.szCurrentDir, rc);
1623 pJob->rc = -1;
1624 DosClose(hPipeR);
1625 DosClose(hPipeW);
1626 }
1627
1628
1629 /*
1630 * Remove from the running queue.
1631 */
1632 if (DosRequestMutexSem(hmtxJobQueue, SEM_INDEFINITE_WAIT))
1633 return;
1634
1635 if (pJobRunning != pJob)
1636 {
1637 PJOB pJobCur = pJobRunning;
1638 while (pJobCur)
1639 {
1640 if (pJobCur->pNext == pJob)
1641 {
1642 pJobCur->pNext = pJob->pNext;
1643 if (pJob == pJobRunningEnd)
1644 pJobRunningEnd = pJobCur;
1645 break;
1646 }
1647 pJobCur = pJobCur->pNext;
1648 }
1649 }
1650 else
1651 pJobRunning = pJobRunningEnd = NULL;
1652
1653 DosReleaseMutexSem(hmtxJobQueue);
1654
1655
1656 /*
1657 * Insert result in result queue.
1658 */
1659 if (DosRequestMutexSem(hmtxJobQueueFine, SEM_INDEFINITE_WAIT))
1660 return;
1661 pJob->pNext = NULL;
1662 if (!pJob->rc) /* 0 on success. */
1663 {
1664 if (pJobCompletedLast)
1665 pJobCompletedLast->pNext = pJob;
1666 else
1667 pJobCompleted = pJob;
1668 pJobCompletedLast = pJob;
1669 }
1670 else
1671 {
1672 if (pJobFailedLast)
1673 pJobFailedLast->pNext = pJob;
1674 else
1675 pJobFailed = pJob;
1676 pJobFailedLast = pJob;
1677 }
1678 cJobsFinished++;
1679 DosReleaseMutexSem(hmtxJobQueueFine);
1680 /* wake up Wait. */
1681 DosPostEventSem(hevJobQueueFine);
1682 //printf("debug-%d: fine\n", iWorkerId);
1683 }
1684 }
1685 iWorkerId = iWorkerId;
1686}
1687
1688
1689/**
1690 * Builds the input to DosExecPgm.
1691 * Will execute programs directly and command thru the shell.
1692 *
1693 * @returns pszArg.
1694 * @param pszArg Arguments to DosExecPgm.(output)
1695 * Assumes that the buffer is large enought.
1696 * @param pszzEnv Pointer to environment block.
1697 * @param pszCommand Command to execute.
1698 * @param pszCurDir From where the command is to executed.
1699 * @param pPathCache Used to cache the last path, executable, and the search result.
1700 */
1701char *WorkerArguments(char *pszArg, const char *pszzEnv, const char *pszCommand, char *pszCurDir, PPATHCACHE pPathCache)
1702{
1703 BOOL fCMD = FALSE;
1704 const char *psz;
1705 const char *psz2;
1706 char * pszW;
1707 char ch;
1708 int cch;
1709 APIRET rc;
1710
1711 /*
1712 * Check if this is multiple command separated by either &, && or |.
1713 * Currently ignoring quotes for this test.
1714 */
1715 if ( strchr(pszCommand, '&')
1716 || strchr(pszCommand, '|')
1717 || strchr(pszCommand, '@'))
1718 {
1719 strcpy(pszArg, "cmd.exe"); /* doesn't use comspec, just defaults to cmd.exe in all cases. */
1720 fCMD = TRUE;
1721 psz2 = pszCommand; /* start of arguments. */
1722 }
1723 else
1724 {
1725 char chEnd = ' ';
1726
1727 /*
1728 * Parse out the first name.
1729 */
1730 for (psz = pszCommand; *psz == '\t' || *psz == ' ';) //strip(,'L');
1731 psz++;
1732 if (*psz == '"' || *psz == '\'')
1733 chEnd = *psz++;
1734 psz2 = psz;
1735 if (chEnd == ' ')
1736 {
1737 while ((ch = *psz) != '\0' && ch != ' ' && ch != '\t')
1738 psz++;
1739 }
1740 else
1741 {
1742 while ((ch = *psz) != '\0' && ch != chEnd)
1743 psz++;
1744 }
1745 *pszArg = '\0';
1746 strncat(pszArg, psz2, psz - psz2);
1747 psz2 = psz+1; /* start of arguments. */
1748 }
1749
1750
1751 /*
1752 * Resolve the executable name if not qualified.
1753 * NB! We doesn't fully support references to other driveletters yet. (TODO/BUGBUG)
1754 */
1755 /* correct slashes */
1756 pszW = pszArg;
1757 while ((pszW = strchr(pszW, '//')) != NULL)
1758 *pszW++ = '\\';
1759
1760 /* make sure it ends with .exe */
1761 pszW = pszArg + strlen(pszArg) - 1;
1762 while (pszW > pszArg && *pszW != '.' && *pszW != '\\')
1763 pszW--;
1764 if (*pszW != '.')
1765 strcat(pszArg, ".exe");
1766
1767 if (pszArg[1] != ':' || *pszArg == *pszCurDir)
1768 {
1769 rc = -1; /* indicate that we've not found the file. */
1770
1771 /* relative path? - expand it */
1772 if (strchr(pszArg, '\\') || pszArg[1] == ':')
1773 { /* relative path - expand it and check for file existence */
1774 fileNormalize(pszArg, pszCurDir);
1775 pszCurDir[strlen(pszCurDir)-1] = '\0'; /* remove slash */
1776 rc = fileExist(pszArg);
1777 }
1778 else
1779 { /* Search path. */
1780 const char *pszPath = pszzEnv;
1781 while (*pszPath != '\0' && strncmp(pszPath, "PATH=", 5))
1782 pszPath += strlen(pszPath) + 1;
1783
1784 if (pszPath && *pszPath != '\0')
1785 {
1786 /* check cache */
1787 if ( !strcmp(pPathCache->szProgram, pszArg)
1788 && !strcmp(pPathCache->szPath, pszPath)
1789 && !strcmp(pPathCache->szCurDir, pszCurDir)
1790 )
1791 {
1792 strcpy(pszArg, pPathCache->szResult);
1793 rc = fileExist(pszArg);
1794 }
1795
1796 if (rc)
1797 { /* search path */
1798 char szResult[CCHMAXPATH];
1799 rc = DosSearchPath(SEARCH_IGNORENETERRS, (PSZ)pszPath, pszArg, &szResult[0] , sizeof(szResult));
1800 if (!rc)
1801 {
1802 strcpy(pszArg, szResult);
1803
1804 /* update cache */
1805 strcpy(pPathCache->szProgram, pszArg);
1806 strcpy(pPathCache->szPath, pszPath);
1807 strcpy(pPathCache->szCurDir, pszCurDir);
1808 strcpy(pPathCache->szResult, szResult);
1809 }
1810 }
1811 }
1812 }
1813 }
1814 /* else nothing to do - assume full path (btw. we don't have the current dir for other drives anyway :-) */
1815 else
1816 rc = !fCMD ? fileExist(pszArg) : NO_ERROR;
1817
1818 /* In case of error use CMD */
1819 if (rc && !fCMD)
1820 {
1821 strcpy(pszArg, "cmd.exe"); /* doesn't use comspec, just defaults to cmd.exe in all cases. */
1822 fCMD = TRUE;
1823 psz2 = pszCommand; /* start of arguments. */
1824 }
1825
1826
1827 /*
1828 * Complete the argument string.
1829 * ---
1830 * szArg current holds the command.
1831 * psz2 points to the first parameter. (needs strip(,'L'))
1832 */
1833 while ((ch = *psz2) != '\0' && (ch == '\t' || ch == ' '))
1834 psz2++;
1835
1836 pszW = pszArg + strlen(pszArg) + 1;
1837 cch = strlen(psz2);
1838 if (!fCMD)
1839 {
1840 memcpy(pszW, psz2, ++cch);
1841 pszW[cch] = '\0';
1842 }
1843 else
1844 {
1845 strcpy(pszW, "/C \"");
1846 pszW += strlen(pszW);
1847 memcpy(pszW, psz2, cch);
1848 memcpy(pszW + cch, "\"\0", 3);
1849 }
1850
1851 return pszArg;
1852}
1853
1854
1855
1856/**
1857 * Normalizes the path slashes for the filename. It will partially expand paths too.
1858 * @returns pszFilename
1859 * @param pszFilename Pointer to filename string. Not empty string!
1860 * Much space to play with.
1861 * @remark (From fastdep.)
1862 * @remark BOGUS CODE! Recheck it please!
1863 */
1864char *fileNormalize(char *pszFilename, char *pszCurDir)
1865{
1866 char * pszRet = pszFilename;
1867 int aiSlashes[CCHMAXPATH/2];
1868 int cSlashes;
1869 int i;
1870
1871 /*
1872 * Init stuff.
1873 */
1874 for (i = 1, cSlashes = 0; pszCurDir[i] != '\0'; i++)
1875 {
1876 if (pszCurDir[i] == '/')
1877 pszCurDir[i] = '\\';
1878 if (pszCurDir[i] == '\\')
1879 aiSlashes[cSlashes++] = i;
1880 }
1881 if (pszCurDir[i-1] != '\\')
1882 {
1883 aiSlashes[cSlashes] = i;
1884 pszCurDir[i++] = '\\';
1885 pszCurDir[i] = '\0';
1886 }
1887
1888
1889 /* expand path? */
1890 if (pszFilename[1] != ':')
1891 { /* relative path */
1892 int iSlash;
1893 char szFile[CCHMAXPATH];
1894 char * psz = szFile;
1895
1896 strcpy(szFile, pszFilename);
1897 iSlash = *psz == '\\' ? 1 : cSlashes;
1898 while (*psz != '\0')
1899 {
1900 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
1901 { /* up one directory */
1902 if (iSlash > 0)
1903 iSlash--;
1904 psz += 3;
1905 }
1906 else if (*psz == '.' && psz[1] == '\\')
1907 { /* no change */
1908 psz += 2;
1909 }
1910 else
1911 { /* completed expantion! */
1912 strncpy(pszFilename, pszCurDir, aiSlashes[iSlash]+1);
1913 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
1914 break;
1915 }
1916 }
1917 }
1918 /* else: assume full path */
1919
1920 return pszRet;
1921}
1922
1923/**
1924 * Checks if a given file exist.
1925 * @returns 0 if the file exists. (NO_ERROR)
1926 * 2 if the file doesn't exist. (ERROR_FILE_NOT_FOUND)
1927 * @param pszFilename Name of the file to check existance for.
1928 */
1929APIRET fileExist(const char *pszFilename)
1930{
1931 FILESTATUS3 fsts3;
1932 return DosQueryPathInfo((PSZ)pszFilename, FIL_STANDARD, &fsts3, sizeof(fsts3));
1933}
1934
1935
1936/**
1937 * Submits a command to the daemon.
1938 * @returns 0 on success.
1939 * -3 on failure.
1940 * @param rcIgnore Ignores returcodes ranging from 0 to rcIgnore.
1941 */
1942int Submit(int rcIgnore)
1943{
1944 int cch;
1945 int rc;
1946 char * psz;
1947 PPIB ppib;
1948 PTIB ptib;
1949
1950 DosGetInfoBlocks(&ptib, &ppib);
1951 rc = shrmemOpen();
1952 if (rc)
1953 return rc;
1954
1955 /*
1956 * Build message.
1957 */
1958 pShrMem->enmMsgType = msgSubmit;
1959 pShrMem->u1.Submit.rcIgnore = rcIgnore;
1960 _getcwd(pShrMem->u1.Submit.szCurrentDir, sizeof(pShrMem->u1.Submit.szCurrentDir));
1961
1962 /* command */
1963 psz = ppib->pib_pchcmd;
1964 psz += strlen(psz) + 1 + 7; /* 7 = strlen("submit ")*/
1965 while (*psz == ' ' || *psz == '\t')
1966 psz++;
1967 if (*psz == '-')
1968 {
1969 while (*psz != ' ' && *psz != '\t')
1970 psz++;
1971 while (*psz == ' ' || *psz == '\t')
1972 psz++;
1973 }
1974 cch = strlen(psz) + 1;
1975 if (cch > sizeof(pShrMem->u1.Submit.szCommand))
1976 {
1977 Error("Fatal error: Command too long.\n", rc);
1978 shrmemFree();
1979 return -1;
1980 }
1981 if (*psz == '"' && psz[cch-2] == '"') /* remove start & end quotes if any */
1982 {
1983 cch--;
1984 psz++;
1985 }
1986 memcpy(&pShrMem->u1.Submit.szCommand[0], psz, cch);
1987
1988 /* environment */
1989 for (cch = 1, psz = ppib->pib_pchenv; *psz != '\0';)
1990 {
1991 int cchVar = strlen(psz) + 1;
1992 cch += cchVar;
1993 psz += cchVar;
1994 }
1995 if ( ppib->pib_pchenv[cch-2] != '\0'
1996 || ppib->pib_pchenv[cch-1] != '\0')
1997 {
1998 Error("internal error\n");
1999 return -1;
2000 }
2001 if (cch > sizeof(pShrMem->u1.Submit.szzEnv))
2002 {
2003 Error("Fatal error: environment is to bit, cchEnv=%d\n", cch);
2004 shrmemFree();
2005 return -ERROR_BAD_ENVIRONMENT;
2006 }
2007 pShrMem->u1.Submit.cchEnv = cch;
2008 memcpy(&pShrMem->u1.Submit.szzEnv[0], ppib->pib_pchenv, cch);
2009
2010
2011 /*
2012 * Send message and get respons.
2013 */
2014 rc = shrmemSendClient(msgSubmitResponse);
2015 if (rc)
2016 {
2017 shrmemFree();
2018 return rc;
2019 }
2020
2021 rc = !pShrMem->u1.SubmitResponse.fRc;
2022 shrmemFree();
2023 return rc;
2024}
2025
2026
2027/**
2028 * Waits for the commands to complete.
2029 * Will write all output from completed command to stdout.
2030 * Will write failing commands last.
2031 * @returns Count of failing commands.
2032 */
2033int Wait(void)
2034{
2035 int rc;
2036
2037 rc = shrmemOpen();
2038 if (rc)
2039 return rc;
2040 do
2041 {
2042 pShrMem->enmMsgType = msgWait;
2043 pShrMem->u1.Wait.iNothing = 0;
2044 rc = shrmemSendClient(msgWaitResponse);
2045 if (rc)
2046 {
2047 shrmemFree();
2048 return -1;
2049 }
2050 printf("%s", pShrMem->u1.WaitResponse.szOutput);
2051 /*
2052 * Release the client mutex if more data and yield the CPU.
2053 * So we can submit more work. (Odin nmake lib...)
2054 */
2055 if (pShrMem->u1.WaitResponse.fMore)
2056 {
2057 DosReleaseMutexSem(pShrMem->hmtxClient);
2058 DosSleep(0);
2059 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2060 if (rc)
2061 {
2062 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2063 shrmemFree();
2064 return -1;
2065 }
2066 }
2067 } while (pShrMem->u1.WaitResponse.fMore);
2068
2069 rc = pShrMem->u1.WaitResponse.rc;
2070 shrmemFree();
2071 return rc;
2072}
2073
2074
2075/**
2076 * Checks if the daemon is running.
2077 */
2078int QueryRunning(void)
2079{
2080 APIRET rc;
2081 rc = DosGetNamedSharedMem((PPVOID)(PVOID)&pShrMem,
2082 pszSharedMem,
2083 PAG_READ | PAG_WRITE);
2084 if (!rc)
2085 DosFreeMem(pShrMem);
2086
2087 return rc;
2088}
2089
2090
2091/**
2092 * Sends a kill command to the daemon to kill it and its workers.
2093 * @returns 0.
2094 */
2095int Kill(void)
2096{
2097 int rc;
2098
2099 rc = shrmemOpen();
2100 if (rc)
2101 return rc;
2102
2103 pShrMem->enmMsgType = msgKill;
2104 pShrMem->u1.Kill.iNothing = 0;
2105 rc = shrmemSendClient(msgKillResponse);
2106 if (!rc)
2107 rc = !pShrMem->u1.KillResponse.fRc;
2108
2109 shrmemFree();
2110 return rc;
2111}
2112
2113
2114/**
2115 * Shows the current queued commands.
2116 * Will write to stdout.
2117 * @returns 0 or -1 usually.
2118 */
2119int ShowJobs(void)
2120{
2121 int rc;
2122
2123 rc = shrmemOpen();
2124 if (rc)
2125 return rc;
2126 do
2127 {
2128 pShrMem->enmMsgType = msgShowJobs;
2129 pShrMem->u1.ShowJobs.iNothing = 0;
2130 rc = shrmemSendClient(msgShowJobsResponse);
2131 if (rc)
2132 {
2133 shrmemFree();
2134 return -1;
2135 }
2136 printf("%s", pShrMem->u1.ShowJobsResponse.szOutput);
2137 /*
2138 * Release the client mutex if more data and yield the CPU.
2139 * So we can submit more work. (Odin nmake lib...)
2140 */
2141 if (pShrMem->u1.ShowJobsResponse.fMore)
2142 {
2143 DosReleaseMutexSem(pShrMem->hmtxClient);
2144 DosSleep(0);
2145 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2146 if (rc)
2147 {
2148 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2149 shrmemFree();
2150 return -1;
2151 }
2152 }
2153 } while (pShrMem->u1.ShowJobsResponse.fMore);
2154
2155 shrmemFree();
2156 return rc;
2157}
2158
2159
2160/**
2161 * Shows the current running jobs (not the output).
2162 * Will write to stdout.
2163 * @returns 0 or -1 usually.
2164 */
2165int ShowRunningJobs(void)
2166{
2167 int rc;
2168
2169 rc = shrmemOpen();
2170 if (rc)
2171 return rc;
2172 do
2173 {
2174 pShrMem->enmMsgType = msgShowRunningJobs;
2175 pShrMem->u1.ShowRunningJobs.iNothing = 0;
2176 rc = shrmemSendClient(msgShowRunningJobsResponse);
2177 if (rc)
2178 {
2179 shrmemFree();
2180 return -1;
2181 }
2182 printf("%s", pShrMem->u1.ShowRunningJobsResponse.szOutput);
2183 /*
2184 * Release the client mutex if more data and yield the CPU.
2185 * So we can submit more work. (Odin nmake lib...)
2186 */
2187 if (pShrMem->u1.ShowRunningJobsResponse.fMore)
2188 {
2189 DosReleaseMutexSem(pShrMem->hmtxClient);
2190 DosSleep(0);
2191 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2192 if (rc)
2193 {
2194 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2195 shrmemFree();
2196 return -1;
2197 }
2198 }
2199 } while (pShrMem->u1.ShowRunningJobsResponse.fMore);
2200
2201 shrmemFree();
2202 return rc;
2203}
2204
2205
2206/**
2207 * Shows the current queue of successfully completed jobs (not the output).
2208 * Will write to stdout.
2209 * @returns 0 or -1 usually.
2210 */
2211int ShowCompletedJobs(void)
2212{
2213 int rc;
2214
2215 rc = shrmemOpen();
2216 if (rc)
2217 return rc;
2218 do
2219 {
2220 pShrMem->enmMsgType = msgShowCompletedJobs;
2221 pShrMem->u1.ShowCompletedJobs.iNothing = 0;
2222 rc = shrmemSendClient(msgShowCompletedJobsResponse);
2223 if (rc)
2224 {
2225 shrmemFree();
2226 return -1;
2227 }
2228 printf("%s", pShrMem->u1.ShowCompletedJobsResponse.szOutput);
2229 /*
2230 * Release the client mutex if more data and yield the CPU.
2231 * So we can submit more work. (Odin nmake lib...)
2232 */
2233 if (pShrMem->u1.ShowCompletedJobsResponse.fMore)
2234 {
2235 DosReleaseMutexSem(pShrMem->hmtxClient);
2236 DosSleep(0);
2237 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2238 if (rc)
2239 {
2240 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2241 shrmemFree();
2242 return -1;
2243 }
2244 }
2245 } while (pShrMem->u1.ShowCompletedJobsResponse.fMore);
2246
2247 shrmemFree();
2248 return rc;
2249}
2250
2251
2252/**
2253 * Shows the current queue of failed jobs (not the output).
2254 * Will write to stdout.
2255 * @returns 0 or -1 usually.
2256 */
2257int ShowFailedJobs(void)
2258{
2259 int rc;
2260
2261 rc = shrmemOpen();
2262 if (rc)
2263 return rc;
2264 do
2265 {
2266 pShrMem->enmMsgType = msgShowFailedJobs;
2267 pShrMem->u1.ShowFailedJobs.iNothing = 0;
2268 rc = shrmemSendClient(msgShowFailedJobsResponse);
2269 if (rc)
2270 {
2271 shrmemFree();
2272 return -1;
2273 }
2274 printf("%s", pShrMem->u1.ShowFailedJobsResponse.szOutput);
2275 /*
2276 * Release the client mutex if more data and yield the CPU.
2277 * So we can submit more work. (Odin nmake lib...)
2278 */
2279 if (pShrMem->u1.ShowFailedJobsResponse.fMore)
2280 {
2281 DosReleaseMutexSem(pShrMem->hmtxClient);
2282 DosSleep(0);
2283 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2284 if (rc)
2285 {
2286 Error("Fatal error: failed to get client mutex. rc=%d\n", rc);
2287 shrmemFree();
2288 return -1;
2289 }
2290 }
2291 } while (pShrMem->u1.ShowFailedJobsResponse.fMore);
2292
2293 shrmemFree();
2294 return rc;
2295}
2296
2297
2298
2299/**
2300 * Creates the shared memory area.
2301 * The creator owns the memory when created.
2302 * @returns 0 on success. Error code on error.
2303 */
2304int shrmemCreate(void)
2305{
2306 int rc;
2307 rc = DosAllocSharedMem((PPVOID)(PVOID)&pShrMem,
2308 pszSharedMem,
2309 SHARED_MEM_SIZE,
2310 PAG_COMMIT | PAG_READ | PAG_WRITE);
2311 if (rc)
2312 {
2313 Error("Fatal error: Failed to create shared memory object. rc=%d\n", rc);
2314 return rc;
2315 }
2316
2317 rc = DosCreateEventSem(NULL, &pShrMem->hevDaemon, DC_SEM_SHARED, FALSE);
2318 if (rc)
2319 {
2320 Error("Fatal error: Failed to create daemon event semaphore. rc=%d\n", rc);
2321 DosFreeMem(pShrMem);
2322 return rc;
2323 }
2324
2325 rc = DosCreateEventSem(NULL, &pShrMem->hevClient, DC_SEM_SHARED, FALSE);
2326 if (rc)
2327 {
2328 Error("Fatal error: Failed to create client event semaphore. rc=%d\n", rc);
2329 DosCloseEventSem(pShrMem->hevDaemon);
2330 DosFreeMem(pShrMem);
2331 return rc;
2332 }
2333
2334 rc = DosCreateMutexSem(NULL, &pShrMem->hmtx, DC_SEM_SHARED, TRUE);
2335 if (rc)
2336 {
2337 Error("Fatal error: Failed to create mutex semaphore. rc=%d\n", rc);
2338 DosCloseEventSem(pShrMem->hevClient);
2339 DosCloseEventSem(pShrMem->hevDaemon);
2340 DosFreeMem(pShrMem);
2341 return rc;
2342 }
2343
2344 rc = DosCreateMutexSem(NULL, &pShrMem->hmtxClient, DC_SEM_SHARED, FALSE);
2345 if (rc)
2346 {
2347 Error("Fatal error: Failed to create client mutex semaphore. rc=%d\n", rc);
2348 DosCloseEventSem(pShrMem->hevClient);
2349 DosCloseEventSem(pShrMem->hevClient);
2350 DosCloseEventSem(pShrMem->hevDaemon);
2351 DosFreeMem(pShrMem);
2352 return rc;
2353 }
2354
2355
2356 /*
2357 * Install signal handlers.
2358 */
2359 signal(SIGSEGV, signalhandlerDaemon);
2360 signal(SIGTERM, signalhandlerDaemon);
2361 signal(SIGABRT, signalhandlerDaemon);
2362 signal(SIGINT, signalhandlerDaemon);
2363 signal(SIGBREAK,signalhandlerDaemon);
2364
2365 return rc;
2366}
2367
2368
2369/**
2370 * Opens the shared memory and the semaphores.
2371 * The caller is owner of the memory upon successful return.
2372 * @returns 0 on success. Error code on error.
2373 */
2374int shrmemOpen(void)
2375{
2376 int rc;
2377 ULONG ulIgnore;
2378
2379 /*
2380 * Get memory.
2381 */
2382 rc = DosGetNamedSharedMem((PPVOID)(PVOID)&pShrMem,
2383 pszSharedMem,
2384 PAG_READ | PAG_WRITE);
2385 if (rc)
2386 {
2387 Error("Fatal error: Failed to open shared memory. rc=%d\n", rc);
2388 return rc;
2389 }
2390
2391
2392 /*
2393 * Open semaphores.
2394 */
2395 rc = DosOpenEventSem(NULL, &pShrMem->hevClient);
2396 if (rc)
2397 {
2398 Error("Fatal error: Failed to open client event semaphore. rc=%d\n", rc);
2399 DosFreeMem(pShrMem);
2400 return rc;
2401 }
2402
2403 rc = DosOpenEventSem(NULL, &pShrMem->hevDaemon);
2404 if (rc)
2405 {
2406 Error("Fatal error: Failed to open daemon event semaphore. rc=%d\n", rc);
2407 DosCloseEventSem(pShrMem->hevClient);
2408 DosFreeMem(pShrMem);
2409 return rc;
2410 }
2411
2412 rc = DosOpenMutexSem(NULL, &pShrMem->hmtx);
2413 if (rc)
2414 {
2415 /* try correct client died situation */
2416 if (rc == ERROR_SEM_OWNER_DIED)
2417 {
2418 pShrMem->enmMsgType = msgSharedMemOwnerDied;
2419 DosResetEventSem(pShrMem->hevClient, &ulIgnore);
2420 DosPostEventSem(pShrMem->hevDaemon);
2421 if (DosWaitEventSem(pShrMem->hevClient, 2000))
2422 {
2423 Error("Fatal error: Failed to open mutex semaphore. (owner dead) rc=%d\n", rc);
2424 shrmemFree();
2425 return rc;
2426 }
2427 rc = DosOpenMutexSem(NULL, &pShrMem->hmtx);
2428 }
2429
2430 if (rc)
2431 {
2432 Error("Fatal error: Failed to open mutex semaphore. rc=%d\n", rc);
2433 DosCloseEventSem(pShrMem->hevClient);
2434 DosCloseEventSem(pShrMem->hevDaemon);
2435 DosFreeMem(pShrMem);
2436 return rc;
2437 }
2438 }
2439
2440 rc = DosOpenMutexSem(NULL, &pShrMem->hmtxClient);
2441 if (rc)
2442 {
2443 /* try correct client died situation */
2444 if (rc == ERROR_SEM_OWNER_DIED)
2445 {
2446 pShrMem->enmMsgType = msgClientOwnerDied;
2447 DosResetEventSem(pShrMem->hevClient, &ulIgnore);
2448 DosPostEventSem(pShrMem->hevDaemon);
2449 if (DosWaitEventSem(pShrMem->hevClient, 2000))
2450 {
2451 Error("Fatal error: Failed to open client mutex semaphore. (owner dead) rc=%d\n", rc);
2452 shrmemFree();
2453 return rc;
2454 }
2455 rc = DosOpenMutexSem(NULL, &pShrMem->hmtxClient);
2456 }
2457
2458 if (rc)
2459 {
2460 Error("Fatal error: Failed to open client mutex semaphore. rc=%d\n", rc);
2461 DosCloseEventSem(pShrMem->hevClient);
2462 DosCloseEventSem(pShrMem->hevDaemon);
2463 DosCloseMutexSem(pShrMem->hmtx);
2464 DosFreeMem(pShrMem);
2465 return rc;
2466 }
2467 }
2468
2469
2470 /*
2471 * Before we request semaphores we need to have signal handlers installed.
2472 */
2473 signal(SIGSEGV, signalhandlerClient);
2474 signal(SIGTERM, signalhandlerClient);
2475 signal(SIGABRT, signalhandlerClient);
2476 signal(SIGINT, signalhandlerClient);
2477 signal(SIGBREAK,signalhandlerClient);
2478
2479
2480 /*
2481 * Request the necessary semaphores to be able to talk to the daemon.
2482 */
2483 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2484 if (rc)
2485 {
2486 /* try correct client died situation */
2487 if (rc == ERROR_SEM_OWNER_DIED)
2488 {
2489 pShrMem->enmMsgType = msgClientOwnerDied;
2490 DosResetEventSem(pShrMem->hevClient, &ulIgnore);
2491 DosPostEventSem(pShrMem->hevDaemon);
2492 if (DosWaitEventSem(pShrMem->hevClient, 2000))
2493 {
2494 Error("Fatal error: Failed to take ownership of client mutex semaphore. (owner dead) rc=%d\n", rc);
2495 shrmemFree();
2496 return rc;
2497 }
2498 rc = DosRequestMutexSem(pShrMem->hmtxClient, SEM_INDEFINITE_WAIT);
2499 }
2500
2501 if (rc)
2502 {
2503 Error("Fatal error: Failed to take ownership of client mutex semaphore. rc=%d\n", rc);
2504 shrmemFree();
2505 return rc;
2506 }
2507 }
2508
2509 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2510 if (rc)
2511 {
2512 /* try correct client died situation */
2513 if (rc == ERROR_SEM_OWNER_DIED)
2514 {
2515 pShrMem->enmMsgType = msgSharedMemOwnerDied;
2516 DosResetEventSem(pShrMem->hevClient, &ulIgnore);
2517 DosPostEventSem(pShrMem->hevDaemon);
2518 if (DosWaitEventSem(pShrMem->hevClient, 2000))
2519 {
2520 Error("Fatal error: Failed to take ownership of mutex mutex semaphore. (owner dead) rc=%d\n", rc);
2521 shrmemFree();
2522 return rc;
2523 }
2524 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2525 }
2526
2527 if (rc)
2528 {
2529 Error("Fatal error: Failed to take ownership of mutex semaphore. rc=%d\n", rc);
2530 shrmemFree();
2531 return rc;
2532 }
2533 }
2534
2535
2536 return rc;
2537}
2538
2539
2540/**
2541 * Frees the shared memory and the associated semaphores.
2542 */
2543void shrmemFree(void)
2544{
2545 if (!pShrMem)
2546 return;
2547 /* wakeup any clients */
2548 DosPostEventSem(pShrMem->hevClient);
2549 /* free stuff */
2550 DosReleaseMutexSem(pShrMem->hmtxClient);
2551 DosReleaseMutexSem(pShrMem->hmtx);
2552 DosCloseMutexSem(pShrMem->hmtxClient);
2553 DosCloseMutexSem(pShrMem->hmtx);
2554 DosCloseEventSem(pShrMem->hevClient);
2555 DosCloseEventSem(pShrMem->hevDaemon);
2556 DosFreeMem(pShrMem);
2557 pShrMem = NULL;
2558}
2559
2560
2561/**
2562 * Daemon sends a message.
2563 * Upon we don't own the shared memory any longer.
2564 * @returns 0 on success. Error code on error.
2565 * -1 on timeout.
2566 * @param fWait Wait for new message.
2567 */
2568int shrmemSendDaemon(BOOL fWait)
2569{
2570 ULONG ulDummy;
2571 int rc;
2572
2573 /* send message */
2574 DosResetEventSem(pShrMem->hevDaemon, &ulDummy);
2575 rc = DosReleaseMutexSem(pShrMem->hmtx);
2576 if (!rc)
2577 rc = DosPostEventSem(pShrMem->hevClient);
2578
2579 /* wait for next message */
2580 if (!rc && fWait)
2581 {
2582 do
2583 {
2584 rc = DosWaitEventSem(pShrMem->hevDaemon, IDLE_TIMEOUT_MS);
2585 } while (rc == ERROR_TIMEOUT && pJobQueue);
2586
2587 if (rc == ERROR_TIMEOUT)
2588 {
2589 DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2590 shrmemFree();
2591 return -1;
2592 }
2593
2594 if (!rc)
2595 {
2596 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2597 if (rc == ERROR_SEM_OWNER_DIED)
2598 {
2599 DosCloseMutexSem(pShrMem->hmtx);
2600 pShrMem->hmtx = NULLHANDLE;
2601 rc = DosCreateMutexSem(NULL, &pShrMem->hmtx, DC_SEM_SHARED, TRUE);
2602 }
2603 }
2604
2605 if (rc && rc != ERROR_INTERRUPT)
2606 Error("Internal error: failed to get next message from daemon, rc=%d\n", rc);
2607 }
2608 else if (!fWait)
2609 Error("Internal error: failed to send message from daemon, rc=%d\n", rc);
2610 return rc;
2611}
2612
2613
2614/**
2615 * Client sends a message.
2616 * Upon we don't own the shared memory any longer.
2617 * @returns 0 on success. Error code on error.
2618 * @param enmMsgTypeResponse The expected response on this message.
2619 */
2620int shrmemSendClient(int enmMsgTypeResponse)
2621{
2622 ULONG ulDummy;
2623 int rc;
2624
2625 /* send message */
2626 DosResetEventSem(pShrMem->hevClient, &ulDummy);
2627 rc = DosReleaseMutexSem(pShrMem->hmtx);
2628 if (!rc)
2629 rc = DosPostEventSem(pShrMem->hevDaemon);
2630
2631 /* wait for response */
2632 if (!rc)
2633 {
2634 rc = DosWaitEventSem(pShrMem->hevClient, SEM_INDEFINITE_WAIT);
2635 if (!rc)
2636 {
2637 rc = DosRequestMutexSem(pShrMem->hmtx, SEM_INDEFINITE_WAIT);
2638 if (rc == ERROR_SEM_OWNER_DIED)
2639 {
2640 Error("Internal error: shared mem mutex owner died.\n");
2641 return -1;
2642 }
2643
2644 if (!rc && pShrMem->enmMsgType != enmMsgTypeResponse)
2645 {
2646 if (pShrMem->enmMsgType != msgDying)
2647 Error("Internal error: Invalid response message. response=%d expected=%d\n",
2648 pShrMem->enmMsgType, enmMsgTypeResponse);
2649 else
2650 Error("Fatal error: daemon just died!\n");
2651 return -1;
2652 }
2653 }
2654 if (rc && rc != ERROR_INTERRUPT)
2655 Error("Internal error: failed to get response message from daemon, rc=%d\n", rc);
2656 }
2657 else
2658 Error("Internal error: failed to send message to daemon, rc=%d\n", rc);
2659
2660 return rc;
2661}
2662
2663
2664/**
2665 * printf lookalike used to print all run-tim errors.
2666 * @param pszFormat Format string.
2667 * @param ... Arguments (optional).
2668 */
2669void Error(const char *pszFormat, ...)
2670{
2671 va_list arg;
2672 /* won't workin in daemon mode... */
2673 va_start(arg, pszFormat);
2674 if (!fDaemon)
2675 vfprintf(stdout, pszFormat, arg);
2676 else
2677 {
2678 FILE *phFile = fopen(".\\cmdqd.log", "a+");
2679 if (phFile)
2680 {
2681 fprintf(phFile, "%d:", getpid());
2682 vfprintf(phFile, pszFormat, arg);
2683 fclose(phFile);
2684 }
2685 }
2686 va_end(arg);
2687}
2688
2689
2690#ifdef DEBUGMEMORY
2691void my_free(void *pv)
2692{
2693 DosFreeMem((PVOID)((unsigned)pv & 0xffff0000));
2694}
2695
2696void *my_malloc(size_t cb)
2697{
2698 APIRET rc;
2699 PVOID pv;
2700 ULONG cbAlloc;
2701 char szMsg[200];
2702
2703 cbAlloc = (cb + 0x1fff) & (~0x0fff);
2704
2705 rc = DosAllocMem(&pv, cbAlloc, PAG_READ | PAG_WRITE);
2706 if (!rc)
2707 {
2708 rc = DosSetMem(pv, cbAlloc - 0x1000, PAG_READ | PAG_WRITE | PAG_COMMIT);
2709 if (rc)
2710 __interrupt(3);
2711 if (cb & 0xfff)
2712 pv = (PVOID)((unsigned)pv + 0x1000 - (cb & 0x0fff));
2713 }
2714
2715 strcpy(szMsg, "malloc(");
2716 _itoa(cb, szMsg + strlen(szMsg), 16);
2717 strcat(szMsg, ") -> ");
2718 _itoa(pv, szMsg + strlen(szMsg), 16);
2719 strcat(szMsg, "\r\n");
2720
2721 DosPutMessage(1, strlen(szMsg), szMsg);
2722
2723 return rc ? NULL : pv;
2724}
2725#endif
Note: See TracBrowser for help on using the repository browser.