source: trunk/emx/src/emxload/emxload.c@ 3669

Last change on this file since 3669 was 1454, checked in by bird, 21 years ago

Joined with the fork() tree from netlabs.cvs.

  • Property cvs2svn:cvs-rev set to 1.8
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 20.3 KB
Line 
1/* emxload.c -- Keep OS/2 programs in memory
2 Copyright (c) 1993-1998 Eberhard Mattes
3
4This file is part of emxload.
5
6emxload is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emxload is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emxload; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
20
21#define NO_EXAPIS
22#include <stdio.h>
23#include <stdlib.h>
24#include <stdarg.h>
25#include <string.h>
26#include <limits.h>
27#include <time.h>
28#include <alloca.h>
29#include <sys/emxload.h>
30#include <emx/emxload.h>
31#define INCL_DOSPROCESS
32#define INCL_DOSNMPIPES
33#define INCL_DOSSEMAPHORES
34#define INCL_DOSDEVICES
35#define INCL_DOSMISC
36#define INCL_DOSERRORS
37#include <os2.h>
38
39/* ------------------------------ SERVER ------------------------------ */
40
41/* This is the maximum number of processes that can be managed by the
42 emxload server. */
43
44#define MAX_PROCS 256
45
46/* A process table entry. PID is the process ID, STOP is the time at
47 which the program will be unloaded (0 means never unload), NAME is
48 the path name of the executable. If NAME is NULL, the slot is
49 empty. */
50
51typedef struct
52{
53 int pid;
54 time_t stop;
55 char *name;
56} proc;
57
58/* The name of the pipe. */
59static char const pipe_name[] = _EMXLOAD_PIPENAME;
60
61/* The table of the processes. */
62static proc table[MAX_PROCS];
63
64/* This many entries of the process table are initialized. */
65static int procs = 0;
66
67/* Mutex semaphore for protecting the process table. */
68static HMTX hmtxTable;
69
70/* This event semaphore is posted when the process table has been
71 changed. */
72static HEV hevReady;
73
74/* The server pipe handle. */
75static HPIPE hp;
76
77/* These variables are used for debugging. Use `-debug FNAME' instead
78 of `-server' on the command line to start the server manually in
79 debug mode. You can also use the `EMXLOAD_DEBUG_FILE' environment
80 variable to set the file name. In that case, debugging output will
81 also be enabled for `-server'. If `debug_file' is non-NULL, the
82 server will print informational messages and error message to
83 FNAME. Usually, the server is started detached and doesn't display
84 anything. */
85static char *debug_fname = NULL;
86static FILE *debug_file = NULL;
87
88/* Prototypes. */
89
90static void s_unload_all (void);
91static void debug (const char *fmt, ...)
92#ifdef __GNUC__
93 __attribute__ ((__format__ (__printf__, 1, 2)))
94#endif
95 ;
96
97/* Debugging output. This function is called like printf(). */
98
99static void debug (const char *fmt, ...)
100{
101 va_list arg_ptr;
102
103 if (debug_file == NULL)
104 return;
105 va_start (arg_ptr, fmt);
106 vfprintf (debug_file, fmt, arg_ptr);
107 va_end (arg_ptr);
108 fflush (debug_file);
109}
110
111
112/* Load the program NAME into memory and create a process table entry.
113 The program will be unloaded at the time indicated by STOP. If
114 STOP is 0, the program won't be unloaded automatically. This
115 function is called only while the process table is locked. */
116
117static void s_load (const char *name, time_t stop)
118{
119 int i;
120 RESULTCODES result;
121 uDB_t dbgbuf;
122 CHAR objbuf[16];
123 PCHAR args;
124 size_t len;
125 APIRET rc;
126
127 /* Find an unused slot in the process table. */
128
129 for (i = 0; i < procs; ++i)
130 if (table[i].name == NULL)
131 break;
132 if (i >= procs)
133 {
134 if (procs >= MAX_PROCS)
135 {
136 debug ("Too many programs\n");
137 return;
138 }
139 i = procs++;
140 table[i].pid = -1;
141 table[i].name = NULL;
142 table[i].stop = 0;
143 }
144
145 /* Build the command line. */
146
147 len = strlen (name);
148 args = alloca (len + 3);
149 memcpy (args, name, len);
150 args[len+0] = 0;
151 args[len+1] = 0;
152 args[len+2] = 0;
153
154 /* Load the program without running it. Originally, we used
155 EXEC_LOAD, but emxload lost its children under certain
156 circumstances, perhaps due to a bug in the OS/2 kernel. */
157
158 rc = DosExecPgm (objbuf, sizeof (objbuf), EXEC_TRACE, args, NULL,
159 &result, name);
160 if (rc == 0)
161 {
162 /* Now connect to the debuggee. If we didn't do this, the child
163 process would be killed by OS/2 after 10 minutes. */
164
165 memset (&dbgbuf, 0, sizeof (dbgbuf));
166 dbgbuf.Addr = 0; /* Sever connection to avoid deadlock */
167 dbgbuf.Pid = result.codeTerminate;
168 dbgbuf.Tid = 0; /* Reserved */
169 dbgbuf.Cmd = DBG_C_Connect; /* Connect */
170 dbgbuf.Value = DBG_L_386; /* Level */
171 rc = DosDebug (&dbgbuf);
172 if (rc == 0 && dbgbuf.Cmd == DBG_N_Success)
173 {
174 /* Fill-in the process table entry. */
175
176 table[i].stop = stop;
177 table[i].name = strdup (name);
178 if (table[i].name == NULL)
179 {
180 debug ("Out of memory\n");
181 s_unload_all ();
182 exit (2);
183 }
184 table[i].pid = result.codeTerminate;
185
186 /* Notify the other thread. */
187
188 DosPostEventSem (hevReady);
189 }
190 else
191 debug ("Cannot connect to %s, rc=%lu\n", name, rc);
192 }
193 else
194 debug ("Cannot load %s, rc=%lu\n", name, rc);
195}
196
197
198/* Unload the program in process table entry I. The process table
199 entry may be unused, see unload_all(). After calling unload(), the
200 process table entry will be marked unused. */
201
202static void s_unload (int i)
203{
204 uDB_t dbgbuf;
205
206 if (table[i].name != NULL)
207 {
208 memset (&dbgbuf, 0, sizeof (dbgbuf));
209 dbgbuf.Pid = table[i].pid;
210 dbgbuf.Cmd = DBG_C_Term;
211 if (DosDebug (&dbgbuf) != 0)
212 debug ("Cannot unload %s\n", table[i].name);
213 free (table[i].name);
214 table[i].name = NULL;
215 table[i].pid = -1;
216 }
217}
218
219
220/* Unload all programs. */
221
222static void s_unload_all (void)
223{
224 int i;
225
226 for (i = 0; i < procs; ++i)
227 s_unload (i);
228}
229
230
231/* Automatically unload programs. This function never returns. */
232
233static void auto_unload (void)
234{
235 int i, next_set, more;
236 ULONG timeout, post_count, rc;
237 time_t now, next;
238
239 for (;;)
240 {
241
242 /* Request exclusive access to the process table. */
243
244 DosRequestMutexSem (hmtxTable, SEM_INDEFINITE_WAIT);
245 do
246 {
247
248 /* Scan the table, unloading programs which have expired.
249 Compute the time to sleep until the next program will
250 expire. To improve accuracy of the timing, we repeat
251 this loop until no program has been unloaded. */
252
253 time (&now);
254 next = 0; next_set = FALSE; more = FALSE;
255 for (i = 0; i < procs; ++i)
256 if (table[i].name != NULL && table[i].stop != 0)
257 {
258 if (now >= table[i].stop)
259 {
260 debug ("Unloading %s\n", table[i].name);
261 s_unload (i);
262 more = TRUE;
263 }
264 else if (!next_set)
265 {
266 next = table[i].stop;
267 next_set = TRUE;
268 }
269 else if (table[i].stop < next)
270 next = table[i].stop;
271 }
272 } while (more);
273 DosReleaseMutexSem (hmtxTable);
274
275 if (next_set)
276 debug ("Waiting for %u seconds\n", (unsigned)(next - now));
277 else
278 debug ("Waiting forever\n");
279
280 /* Wait until next program expires or until a thread changes the
281 process table. */
282
283 timeout = (next_set ? (next - now) * 1000 : SEM_INDEFINITE_WAIT);
284 rc = DosWaitEventSem (hevReady, timeout);
285 rc = DosResetEventSem (hevReady, &post_count);
286 }
287}
288
289
290/* Send answer to client. Return 0 on success, -1 on failure. */
291
292static int s_answer (const answer *ans)
293{
294 ULONG rc, cb;
295
296 rc = DosWrite (hp, ans, sizeof (answer), &cb);
297 return (rc == 0 ? 0 : -1);
298}
299
300
301/* Send acknowledge to client. */
302
303static void s_ack (void)
304{
305 answer ans;
306
307 ans.ans_code = 0;
308 s_answer (&ans);
309}
310
311
312/* Load or unload a program. This function is called by the thread
313 which reads the pipe. */
314
315static void s_load_unload (const request *req)
316{
317 int i;
318 time_t now, stop;
319
320 stop = 0;
321
322 /* Compute the time when the program will be unloaded. */
323
324 if (req->req_code == _EMXLOAD_LOAD && req->seconds != _EMXLOAD_INDEFINITE)
325 {
326 time (&now);
327 stop = now + req->seconds;
328 }
329
330 /* Request exclusive access to the process table. */
331
332 DosRequestMutexSem (hmtxTable, SEM_INDEFINITE_WAIT);
333
334 /* Scan table for a matching entry. If the program is already in
335 the table, update the time-out or unload the program. */
336
337 for (i = 0; i < procs; ++i)
338 if (table[i].name != NULL && strcasecmp (table[i].name, req->name) == 0)
339 {
340 if (req->req_code != _EMXLOAD_LOAD)
341 s_unload (i);
342 else
343 {
344 table[i].stop = stop;
345 DosPostEventSem (hevReady);
346 }
347 break;
348 }
349
350 /* Load the program if the program is not in the table. */
351
352 if (req->req_code == _EMXLOAD_LOAD && i >= procs)
353 s_load (req->name, stop);
354 if (req->req_code == _EMXLOAD_UNLOAD_WAIT)
355 s_ack ();
356 DosReleaseMutexSem (hmtxTable);
357}
358
359
360/* Send a list of preloaded programs to the client. */
361
362static void s_list (void)
363{
364 int i;
365 time_t now;
366 answer ans;
367
368 /* Request exclusive access to the process table. */
369
370 DosRequestMutexSem (hmtxTable, SEM_INDEFINITE_WAIT);
371
372 /* Get the current time. */
373
374 time (&now);
375
376 /* List all the process table entries. */
377
378 for (i = 0; i < procs; ++i)
379 if (table[i].name != NULL)
380 {
381 if (table[i].stop == 0)
382 ans.seconds = _EMXLOAD_INDEFINITE;
383 else
384 {
385 ans.seconds = table[i].stop - now;
386 if (ans.seconds < 0)
387 ans.seconds = 0;
388 }
389 _strncpy (ans.name, table[i].name, sizeof (ans.name));
390 ans.ans_code = 0;
391 if (s_answer (&ans) != 0)
392 break;
393 }
394
395 ans.ans_code = 1;
396 s_answer (&ans);
397 DosReleaseMutexSem (hmtxTable);
398}
399
400
401static void read_pipe (void)
402{
403 ULONG rc, len;
404 request req;
405
406 for (;;)
407 {
408 rc = DosRead (hp, &req, sizeof (req), &len);
409 if (rc == ERROR_BROKEN_PIPE)
410 break;
411 if (rc != 0)
412 {
413 debug ("Error code %lu\n", rc);
414 s_unload_all ();
415 exit (2);
416 }
417 if (len == 0)
418 break;
419 if (len != sizeof (request))
420 debug ("Invalid record length: %lu\n", len);
421 else
422 switch (req.req_code)
423 {
424 case _EMXLOAD_LOAD:
425 case _EMXLOAD_UNLOAD:
426 case _EMXLOAD_UNLOAD_WAIT:
427 s_load_unload (&req);
428 break;
429 case _EMXLOAD_STOP:
430 case _EMXLOAD_STOP_WAIT:
431 s_unload_all ();
432 exit (0);
433 case _EMXLOAD_LIST:
434 s_list ();
435 break;
436 default:
437 debug ("Unknown request code: %d\n", req.req_code);
438 break;
439 }
440 }
441}
442
443
444/* Handle connections to the pipe. This function never returns. */
445
446static void connections (void)
447{
448 ULONG rc;
449
450 for (;;)
451 {
452 rc = DosConnectNPipe (hp);
453 debug ("Connected\n");
454 read_pipe ();
455 rc = DosDisConnectNPipe (hp);
456 debug ("Disconnected\n");
457 }
458}
459
460
461/* This is an additional thread. It handles connections to the named
462 pipe. */
463
464static void thread (void *arg)
465{
466 connections ();
467}
468
469
470/* This is the main thread of the server. */
471
472static void server (void)
473{
474 ULONG rc;
475
476 /* Set our priority to foreground server. This decreases the time
477 clients have to wait on a heavily loaded system. Moreover, we
478 may have inherited idle-time priority from our parent... */
479
480 DosSetPriority (PRTYS_PROCESS, PRTYC_FOREGROUNDSERVER, 0, 0);
481
482 /* Open the debug output file if the `-debug FNAME' option is used
483 or the EMXLOAD_DEBUG_FILE environment variable is set. */
484
485 if (debug_fname == NULL)
486 debug_fname = getenv ("EMXLOAD_DEBUG_FILE");
487 if (debug_fname != NULL)
488 debug_file = fopen (debug_fname, "w");
489
490 /* Close all the file handles as we inherit the file handles of the
491 parent process. If we didn't close the file handles, the parent
492 couldn't delete, rename etc. the files. This should be done
493 before creating the pipe to avoid a timing window. */
494
495 {
496 LONG req;
497 ULONG i, cur;
498
499 req = 0; cur = 0;
500 if (DosSetRelMaxFH (&req, &cur) == 0)
501 for (i = 0; i < cur; ++i)
502 if (!(debug_file != NULL && i == fileno (debug_file)))
503 DosClose ((HFILE)i);
504 }
505
506 /* Change to the root directory of all non-removable drivers and of
507 the current drive (in case it's removable). This is for allowing
508 the user to rename or delete the directories which were the
509 current directories when emxload was started. */
510
511 {
512 ULONG drive, drive_map, parmlen, datalen, rc;
513 CHAR parm[2], data[1];
514 static CHAR dir[] = "A:\\";
515
516 DosError (FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
517 if (DosQueryCurrentDisk (&drive, &drive_map) == 0)
518 {
519 for (drive = 0; drive < 26; ++drive)
520 if (drive_map & (1 << drive))
521 {
522 parm[0] = 0; parm[1] = drive; data[0] = 0;
523 parmlen = 2; datalen = 0;
524 if (DosDevIOCtl ((HFILE)-1, 8, 0x20, parm, 2, &parmlen,
525 data, 1, &datalen) == 0
526 && datalen == 1 && data[0] == 1)
527 {
528 dir[0] = (CHAR)('A' + drive);
529 rc = DosSetCurrentDir (dir);
530 debug ("DosSetCurrentDir(%s) -> %lu\n", dir, rc);
531 }
532 }
533 }
534 DosSetCurrentDir ("\\");
535 DosError (FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
536 }
537
538 /* Create the named pipe. */
539
540 rc = DosCreateNPipe (pipe_name, &hp, NP_NOINHERIT|NP_ACCESS_DUPLEX,
541 NP_WAIT|NP_TYPE_MESSAGE|NP_READMODE_MESSAGE|1,
542 0x1000, 0x1000, 0);
543 if (rc == ERROR_PIPE_BUSY)
544 {
545 debug ("An emxload server is already running\n");
546 exit (1);
547 }
548 if (rc != 0)
549 {
550 debug ("Error code %lu\n", rc);
551 exit (2);
552 }
553
554 /* Create the semaphores. */
555
556 rc = DosCreateMutexSem (NULL, &hmtxTable, 0, FALSE);
557 rc = DosCreateEventSem (NULL, &hevReady, 0, FALSE);
558
559 /* Start the second thread. */
560
561 if (_beginthread (thread, NULL, 0x8000, NULL) == -1)
562 {
563 debug ("Cannot start thread\n");
564 exit (2);
565 }
566
567 auto_unload ();
568}
569
570
571/* ------------------------------ CLIENT ------------------------------ */
572
573/* This flag is set by the -u and -uw command line options. All
574 programs given on the command line will be unloaded instead of
575 loaded. */
576static int unload_flag = FALSE;
577
578/* This flag is set by the -uw command line option. Wait for
579 completion. */
580static int unload_wait = FALSE;
581
582/* The number of seconds to keep the programs in memory. May be
583 _EMXLOAD_INDEFINITE. */
584static int seconds;
585
586/* Prototypes. */
587
588static void usage (void);
589
590
591/* Handle one request for loading or unloading (client side). */
592
593static void c_load_unload (const char *name)
594{
595 if (unload_flag)
596 {
597 if (_emxload_unload (name, unload_wait) != 0)
598 fprintf (stderr, "emxload: cannot unload %s\n", name);
599 }
600 else
601 {
602 if (_emxload_prog (name, seconds) != 0)
603 fprintf (stderr, "emxload: cannot load %s\n", name);
604 }
605}
606
607
608/* Parse the command line for the client. */
609
610static void client (int argc, char **argv)
611{
612 int i, mul;
613 int load_gcc, load_gpp, load_gobjc, load_gnat, load_omf;
614 char *p, *q;
615
616 /* Set the default values. */
617
618 seconds = 10 * 60;
619 load_gcc = FALSE; load_gpp = FALSE; load_gobjc = FALSE; load_gnat = FALSE;
620 load_omf = FALSE;
621 i = 1;
622
623 /* Parse the options. */
624
625 while (i < argc && argv[i][0] == '-')
626 {
627 p = argv[i++];
628 if (strcmp (p, "-e") == 0)
629 seconds = _EMXLOAD_INDEFINITE;
630 else if (strcmp (p, "-u") == 0)
631 unload_flag = TRUE;
632 else if (strcmp (p, "-uw") == 0)
633 {
634 unload_flag = TRUE;
635 unload_wait = TRUE;
636 }
637 else if (strncmp (p, "-m", 2) == 0 || strncmp (p, "-s", 2) == 0)
638 {
639 mul = (p[1] == 'm' ? 60 : 1);
640 p += 2;
641 if (*p == 0)
642 {
643 if (i >= argc)
644 usage ();
645 p = argv[i++];
646 }
647 errno = 0;
648 seconds = (int)strtol (p, &q, 10);
649 if (seconds < 1 || errno != 0 || q == p || *q != 0)
650 usage ();
651 seconds *= mul;
652 }
653 else if (strcmp (p, "-g++") == 0)
654 load_gpp = TRUE;
655 else if (strcmp (p, "-gcc") == 0)
656 load_gcc = TRUE;
657 else if (strcmp (p, "-gobjc") == 0)
658 load_gobjc = TRUE;
659 else if (strcmp (p, "-gnat") == 0)
660 load_gnat = TRUE;
661 else if (strcmp (p, "-omf") == 0)
662 load_omf = TRUE;
663 else
664 usage ();
665 }
666
667 /* At least program must be given. */
668
669 if (i >= argc && !(load_gcc || load_gpp || load_gobjc || load_gnat
670 || load_omf))
671 usage ();
672
673 /* Connect to the server and keep the connection open. */
674
675 if (!unload_flag && _emxload_connect () != 0)
676 {
677 fputs ("emxload: cannot start emxload server\n", stderr);
678 exit (2);
679 }
680
681 /* Load or unload the programs. */
682
683 if (load_gcc || load_gpp || load_gobjc || load_gnat)
684 {
685 c_load_unload ("gcc");
686 c_load_unload ("as");
687 c_load_unload ("ld");
688 c_load_unload ("emxbind");
689 }
690 if (load_gcc || load_gpp || load_gobjc)
691 c_load_unload ("cpp");
692 if (load_gcc)
693 c_load_unload ("cc1");
694 if (load_gpp)
695 c_load_unload ("cc1plus");
696 if (load_gobjc)
697 c_load_unload ("cc1obj");
698 if (load_gnat)
699 {
700 c_load_unload ("gnat1");
701 c_load_unload ("gnatbind");
702 }
703 if (load_omf)
704 {
705 c_load_unload ("emxomf");
706 c_load_unload ("emxomfld");
707 c_load_unload ("link386");
708 }
709 while (i < argc)
710 c_load_unload (argv[i++]);
711}
712
713
714/* List preloaded programs (client side). */
715
716static void c_list (void)
717{
718 char name[260], timeout[20], *p;
719 int seconds, header;
720
721 if (_emxload_list_start () != 0)
722 return;
723
724 header = FALSE;
725 while (_emxload_list_get (name, sizeof (name), &seconds) == 0)
726 {
727 if (!header)
728 {
729 puts ("Time-out ³ Program");
730 puts ("ÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ");
731 header = TRUE;
732 }
733 if (seconds == _EMXLOAD_INDEFINITE)
734 strcpy (timeout, "NONE");
735 else
736 sprintf (timeout, "%d'%.2d", seconds / 60, seconds % 60);
737 for (p = name; *p != 0; ++p)
738 if (*p == '/')
739 *p = '\\';
740 printf ("%8s ³ %s\n", timeout, name);
741 }
742}
743
744/* ------------------------------- MAIN ------------------------------- */
745
746/* How to call this program. */
747
748static void usage (void)
749{
750 fputs ("emxload " VERSION INNOTEK_VERSION " -- "
751 "Copyright (c) 1993-1996 by Eberhard Mattes\n\n"
752 "Usage: emxload [-m <limit>] [-s <limit>] [-e] [-u[w]]\n"
753 " [-gcc] [-g++] [-gobjc] [-gnat] [-omf] <program>...\n"
754 " emxload -l\n"
755 " emxload -q\n"
756 " emxload -qw\n\n"
757 "Options:\n\n"
758 "-m <limit> Unload programs after <limit> minutes (default: 10)\n"
759 "-s <limit> Unload programs after <limit> seconds (default: 600)\n"
760 "-e No limit\n"
761 "-u Unload programs (overrides -m, -s and -e)\n"
762 "-uw Like -u, but wait until completed\n"
763 "-gcc Load gcc, cpp, cc1, as, ld, emxbind\n"
764 "-g++ Load gcc, cpp, cc1plus, as, ld, emxbind\n"
765 "-gobjc Load gcc, cpp, cc1obj, as, ld, emxbind\n"
766 "-gnat Load gcc, gnat1, as, ld, gnatbind, emxbind\n"
767 "-omf Load emxomf, emxomfld, link386\n"
768 "-l List preloaded programs\n"
769 "-q Stop server\n"
770 "-qw Like -w, but wait until completed\n", stderr);
771 exit (1);
772}
773
774
775/* Main line. */
776
777int main (int argc, char **argv)
778{
779 if (argc == 2 && strcmp (argv[1], "-server") == 0)
780 server ();
781 else if (argc == 3 && strcmp (argv[1], "-debug") == 0)
782 {
783 debug_fname = argv[2];
784 server ();
785 }
786 else if (argc == 2 && strcmp (argv[1], "-l") == 0)
787 c_list ();
788 else if (argc == 2 && strcmp (argv[1], "-q") == 0)
789 _emxload_stop (FALSE);
790 else if (argc == 2 && strcmp (argv[1], "-qw") == 0)
791 _emxload_stop (TRUE);
792 else
793 client (argc, argv);
794 return 0;
795}
Note: See TracBrowser for help on using the repository browser.