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

Last change on this file since 10367 was 10254, checked in by bird, 22 years ago

Yet another standard handle closing fix.

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