1 /* IMPL.C       (c) Copyright Roger Bowler, 1999-2009                */
2 /*              Hercules Initialization Module                       */
3 
4 /*-------------------------------------------------------------------*/
5 /* This module initializes the Hercules S/370 or ESA/390 emulator.   */
6 /* It builds the system configuration blocks, creates threads for    */
7 /* central processors, HTTP server, logger task and activates the    */
8 /* control panel which runs under the main thread when in foreground */
9 /* mode.                                                             */
10 /*-------------------------------------------------------------------*/
11 
12 #include "hstdinc.h"
13 
14 #define _IMPL_C_
15 #define _HENGINE_DLL_
16 
17 #include "hercules.h"
18 #include "opcode.h"
19 #include "devtype.h"
20 #include "herc_getopt.h"
21 #include "hostinfo.h"
22 #include "history.h"
23 
24 /* (delayed_exit function defined in config.c) */
25 extern void delayed_exit (int exit_code);
26 
27 /* forward define process_script_file (ISW20030220-3) */
28 int process_script_file(char *,int);
29 
30 static LOGCALLBACK  log_callback=NULL;
31 
32 /*-------------------------------------------------------------------*/
33 /* Register a LOG callback                                           */
34 /*-------------------------------------------------------------------*/
registerLogCallback(LOGCALLBACK lcb)35 DLL_EXPORT void registerLogCallback(LOGCALLBACK lcb)
36 {
37     log_callback=lcb;
38 }
39 
40 /*-------------------------------------------------------------------*/
41 /* Signal handler for SIGINT signal                                  */
42 /*-------------------------------------------------------------------*/
sigint_handler(int signo)43 static void sigint_handler (int signo)
44 {
45 //  logmsg ("impl.c: sigint handler entered for thread %lu\n",/*debug*/
46 //          thread_id());                                     /*debug*/
47 
48     UNREFERENCED(signo);
49 
50     signal(SIGINT, sigint_handler);
51     /* Ignore signal unless presented on console thread */
52     if ( !equal_threads( thread_id(), sysblk.cnsltid ) )
53         return;
54 
55     /* Exit if previous SIGINT request was not actioned */
56     if (sysblk.sigintreq)
57     {
58         /* Release the configuration */
59         release_config();
60         delayed_exit(1);
61     }
62 
63     /* Set SIGINT request pending flag */
64     sysblk.sigintreq = 1;
65 
66     /* Activate instruction stepping */
67     sysblk.inststep = 1;
68     SET_IC_TRACE;
69     return;
70 } /* end function sigint_handler */
71 
72 /*-------------------------------------------------------------------*/
73 /* Signal handler for SIGTERM signal                                 */
74 /*-------------------------------------------------------------------*/
sigterm_handler(int signo)75 static void sigterm_handler (int signo)
76 {
77 //  logmsg ("impl.c: sigterm handler entered for thread %lu\n",/*debug*/
78 //          thread_id());                                      /*debug*/
79 
80     UNREFERENCED(signo);
81 
82     signal(SIGTERM, sigterm_handler);
83     /* Ignore signal unless presented on main program (impl) thread */
84     if ( !equal_threads( thread_id(), sysblk.impltid ) )
85         return;
86 
87     /* Initiate system shutdown */
88     do_shutdown();
89 
90     return;
91 } /* end function sigterm_handler */
92 
93 #if defined( _MSVC_ )
94 
95 /*-------------------------------------------------------------------*/
96 /* Signal handler for Windows signals                                */
97 /*-------------------------------------------------------------------*/
console_ctrl_handler(DWORD signo)98 BOOL WINAPI console_ctrl_handler (DWORD signo)
99 {
100     int i;
101 
102     SetConsoleCtrlHandler(console_ctrl_handler, FALSE);   // turn handler off while processing
103 
104     switch ( signo )
105     {
106         case CTRL_BREAK_EVENT:
107             logmsg(_("HHCIN050ICtrl-Break intercepted. Interrupt Key depressed simulated.\n"));
108 
109             OBTAIN_INTLOCK(NULL);
110 
111             ON_IC_INTKEY;
112 
113             /* Signal waiting CPUs that an interrupt is pending */
114             WAKEUP_CPUS_MASK (sysblk.waiting_mask);
115 
116             RELEASE_INTLOCK(NULL);
117 
118             SetConsoleCtrlHandler(console_ctrl_handler, TRUE);  // reset handler
119             return TRUE;
120             break;
121         case CTRL_C_EVENT:
122             logmsg(_("HHCIN022I Ctrl-C intercepted\n"));
123             SetConsoleCtrlHandler(console_ctrl_handler, TRUE);  // reset handler
124             return TRUE;
125             break;
126         case CTRL_CLOSE_EVENT:
127         case CTRL_SHUTDOWN_EVENT:
128         case CTRL_LOGOFF_EVENT:
129             if ( !sysblk.shutdown )  // (system shutdown not initiated)
130             {
131                 logmsg(_("HHCIN021ICLOSE Event received, SHUTDOWN Immediate starting...\n"));
132                 sysblk.shutimmed = TRUE;
133                 do_shutdown();
134 
135 //                logmsg("%s(%d): return from shutdown\n", __FILE__, __LINE__ ); /* debug */
136 
137                 for ( i = 0; i < 120; i++ )
138                 {
139                     if ( sysblk.shutdown && sysblk.shutfini )
140                     {
141 //                        logmsg("%s(%d): %d shutdown completed\n",  /* debug */
142 //                                __FILE__, __LINE__, i );           /* debug */
143                         sleep(1);
144                         break;
145                     }
146                     else
147                     {
148 //                        logmsg("%s(%d): %d waiting for shutdown to complete\n",   /* debug */
149 //                                __FILE__, __LINE__, i );                          /* debug */
150                         sleep(1);
151                     }
152                 }
153 
154                 socket_deinit();
155             }
156             else
157             {
158                 logmsg(_("HHCIN023W CLOSE Event received, SHUTDOWN previously requested...\n"));
159             }
160             usleep(10000);
161             return FALSE;
162             break;
163         default:
164             return FALSE;
165     }
166 
167 } /* end function console_ctrl_handler */
168 #endif
169 
170 #if !defined(NO_SIGABEND_HANDLER)
watchdog_thread(void * arg)171 static void *watchdog_thread(void *arg)
172 {
173 S64 savecount[MAX_CPU_ENGINES];
174 int i;
175 
176     UNREFERENCED(arg);
177 
178     /* Set watchdog priority just below cpu priority
179        such that it will not invalidly detect an
180        inoperable cpu */
181     if(sysblk.cpuprio >= 0)
182         setpriority(PRIO_PROCESS, 0, sysblk.cpuprio+1);
183 
184     for (i = 0; i < MAX_CPU_ENGINES; i ++) savecount[i] = -1;
185 
186     while(!sysblk.shutdown)
187     {
188         for (i = 0; i < MAX_CPU; i++)
189         {
190 //          obtain_lock (&sysblk.cpulock[i]);
191             if (IS_CPU_ONLINE(i)
192              && sysblk.regs[i]->cpustate == CPUSTATE_STARTED
193              && (!WAITSTATE(&sysblk.regs[i]->psw)
194 #if defined(_FEATURE_WAITSTATE_ASSIST)
195              && !(sysblk.regs[i]->sie_active && WAITSTATE(&sysblk.regs[i]->guestregs->psw))
196 #endif
197                                            ))
198             {
199                 /* If the cpu is running but not executing
200                    instructions then it must be malfunctioning */
201                 if((INSTCOUNT(sysblk.regs[i]) == (U64)savecount[i])
202                   && !HDC1(debug_watchdog_signal, sysblk.regs[i]) )
203                 {
204                     /* Send signal to looping CPU */
205                     signal_thread(sysblk.cputid[i], SIGUSR1);
206                     savecount[i] = -1;
207                 }
208                 else
209                     /* Save current instcount */
210                     savecount[i] = INSTCOUNT(sysblk.regs[i]);
211             }
212             else
213                 /* mark savecount invalid as CPU not in running state */
214                 savecount[i] = -1;
215 //          release_lock (&sysblk.cpulock[i]);
216         }
217         /* Sleep for 20 seconds */
218         SLEEP(20);
219     }
220 
221     return NULL;
222 }
223 #endif /*!defined(NO_SIGABEND_HANDLER)*/
224 
log_do_callback(void * dummy)225 void *log_do_callback(void *dummy)
226 {
227     char *msgbuf;
228     int msgcnt = -1,msgnum;
229     UNREFERENCED(dummy);
230     while(msgcnt)
231     {
232         if((msgcnt = log_read(&msgbuf, &msgnum, LOG_BLOCK)))
233         {
234             log_callback(msgbuf,msgcnt);
235         }
236     }
237     return(NULL);
238 }
239 
getCommandHandler(void)240 DLL_EXPORT  COMMANDHANDLER getCommandHandler(void)
241 {
242     return(panel_command);
243 }
244 
245 /*-------------------------------------------------------------------*/
246 /* Process .RC file thread                                           */
247 /*-------------------------------------------------------------------*/
248 
process_rc_file(void * dummy)249 void* process_rc_file (void* dummy)
250 {
251 char   *rcname;                         /* hercules.rc name pointer  */
252 int     is_default_rc  = 0;             /* 1 == default name used    */
253 int     numcpu         = 0;             /* #of ONLINE & STOPPED CPUs */
254 int     i;                              /* (work)                    */
255 
256     UNREFERENCED(dummy);
257 
258     /* Wait for all installed/configured CPUs to
259        come ONLINE and enter the STOPPED state */
260 
261     OBTAIN_INTLOCK(NULL);
262 
263     for (;;)
264     {
265         numcpu = 0;
266         for (i = 0; i < MAX_CPU_ENGINES; i++)
267             if (IS_CPU_ONLINE(i) &&
268                 CPUSTATE_STOPPED == sysblk.regs[i]->cpustate)
269                 numcpu++;
270         if (numcpu == sysblk.numcpu)
271             break;
272         RELEASE_INTLOCK(NULL);
273         usleep( 10 * 1000 );
274         OBTAIN_INTLOCK(NULL);
275     }
276 
277     RELEASE_INTLOCK(NULL);
278 
279     /* Wait for panel thread to engage */
280 
281     while (!sysblk.panel_init)
282         usleep( 10 * 1000 );
283 
284     /* Obtain the name of the hercules.rc file or default */
285 
286     if (!(rcname = getenv("HERCULES_RC")))
287     {
288         rcname = "hercules.rc";
289         is_default_rc = 1;
290     }
291 
292 #if defined(OPTION_HAO)
293     /* Initialize the Hercules Automatic Operator */
294 
295     if ( !hao_initialize() )
296         logmsg(_("HHCIN004S Cannot create HAO thread: %s\n"),
297                 strerror(errno));
298 #endif /* defined(OPTION_HAO) */
299 
300     /* Run the script processor for this file */
301 
302     if (process_script_file(rcname,1) != 0)
303         if (ENOENT == errno)
304             if (!is_default_rc)
305                 logmsg(_("HHCPN995E .RC file \"%s\" not found.\n"),
306                     rcname);
307         // (else error message already issued)
308 
309     return NULL;
310 }
311 
312 
313 /*-------------------------------------------------------------------*/
314 /* IMPL main entry point                                             */
315 /*-------------------------------------------------------------------*/
impl(int argc,char * argv[])316 DLL_EXPORT int impl(int argc, char *argv[])
317 {
318 char   *cfgfile;                        /* -> Configuration filename */
319 int     c;                              /* Work area for getopt      */
320 int     arg_error = 0;                  /* 1=Invalid arguments       */
321 char   *msgbuf;                         /*                           */
322 int     msgnum;                         /*                           */
323 int     msgcnt;                         /*                           */
324 TID     rctid;                          /* RC file thread identifier */
325 TID     logcbtid;                       /* RC file thread identifier */
326 
327     SET_THREAD_NAME("impl");
328 
329     /* Initialize 'hostinfo' BEFORE display_version is called */
330     init_hostinfo( &hostinfo );
331 
332 #ifdef _MSVC_
333     /* Initialize sockets package */
334     VERIFY( socket_init() == 0 );
335 #endif
336 
337     /* Ensure hdl_shut is called in case of shutdown
338        hdl_shut will ensure entries are only called once */
339     atexit(hdl_shut);
340 
341     set_codepage(NULL);
342 
343     /* Clear the system configuration block */
344     memset (&sysblk, 0, sizeof(SYSBLK));
345 
346     /* Save thread ID of main program */
347     sysblk.impltid = thread_id();
348 
349     /* Save TOD of when we were first IMPL'ed */
350     time( &sysblk.impltime );
351 
352 #ifdef OPTION_MSGHLD
353     /* Set the default timeout value */
354     sysblk.keep_timeout_secs = 120;
355 #endif
356 
357     /* Initialize thread creation attributes so all of hercules
358        can use them at any time when they need to create_thread
359     */
360     initialize_detach_attr (DETACHED);
361     initialize_join_attr   (JOINABLE);
362 
363     /* Copy length for regs */
364     sysblk.regs_copy_len = (int)((uintptr_t)&sysblk.dummyregs.regs_copy_end
365                                - (uintptr_t)&sysblk.dummyregs);
366 
367     /* Set the daemon_mode flag indicating whether we running in
368        background/daemon mode or not (meaning both stdout/stderr
369        are redirected to a non-tty device). Note that this flag
370        needs to be set before logger_init gets called since the
371        logger_logfile_write function relies on its setting.
372     */
373     sysblk.daemon_mode = !isatty(STDERR_FILENO) && !isatty(STDOUT_FILENO);
374 
375     /* Initialize the logmsg pipe and associated logger thread.
376        This causes all subsequent logmsg's to be redirected to
377        the logger facility for handling by virtue of stdout/stderr
378        being redirected to the logger facility.
379     */
380     logger_init();
381 
382     /* Now display the version information again after logger_init
383        has been called so that either the panel display thread or the
384        external gui can see the version which was previously possibly
385        only displayed to the actual physical screen the first time we
386        did it further above (depending on whether we're running in
387        daemon_mode (external gui mode) or not). This it the call that
388        the panel thread or the one the external gui actually "sees".
389        The first call further above wasn't seen by either since it
390        was issued before logger_init was called and thus got written
391        directly to the physical screen whereas this one will be inter-
392        cepted and handled by the logger facility thereby allowing the
393        panel thread or external gui to "see" it and thus display it.
394     */
395     display_version (stdout, "Hercules ", TRUE);
396 
397 #if defined(OPTION_DYNAMIC_LOAD)
398     /* Initialize the hercules dynamic loader */
399     hdl_main();
400 #endif /* defined(OPTION_DYNAMIC_LOAD) */
401 
402 #ifdef EXTERNALGUI
403     /* Set GUI flag if specified as final argument */
404     if (argc >= 1 && strncmp(argv[argc-1],"EXTERNALGUI",11) == 0)
405     {
406 #if defined(OPTION_DYNAMIC_LOAD)
407         if (hdl_load("dyngui",HDL_LOAD_DEFAULT) != 0)
408         {
409             usleep(10000); /* (give logger thread time to issue
410                                preceding HHCHD007E message) */
411             logmsg(_("HHCIN008S DYNGUI.DLL load failed; Hercules terminated.\n"));
412             delayed_exit(1);
413         }
414 #endif /* defined(OPTION_DYNAMIC_LOAD) */
415         argc--;
416     }
417 #endif /*EXTERNALGUI*/
418 
419 #if !defined(WIN32) && !defined(HAVE_STRERROR_R)
420     strerror_r_init();
421 #endif
422 
423 #if defined(OPTION_SCSI_TAPE)
424     initialize_lock (&sysblk.stape_lock);
425     initialize_condition (&sysblk.stape_getstat_cond);
426     InitializeListHead (&sysblk.stape_mount_link);
427     InitializeListHead (&sysblk.stape_status_link);
428 #endif /* defined(OPTION_SCSI_TAPE) */
429 
430     /* Get name of configuration file or default to hercules.cnf */
431     if(!(cfgfile = getenv("HERCULES_CNF")))
432         cfgfile = "hercules.cnf";
433 
434     /* Process the command line options */
435     while ((c = getopt(argc, argv, "f:p:l:db:")) != EOF)
436     {
437 
438         switch (c) {
439         case 'f':
440             cfgfile = optarg;
441             break;
442 #if defined(OPTION_DYNAMIC_LOAD)
443         case 'p':
444             if(optarg)
445                 hdl_setpath(strdup(optarg));
446             break;
447         case 'l':
448             {
449             char *dllname, *strtok_str;
450                 for(dllname = strtok_r(optarg,", ",&strtok_str);
451                     dllname;
452                     dllname = strtok_r(NULL,", ",&strtok_str))
453                     hdl_load(dllname, HDL_LOAD_DEFAULT);
454             }
455             break;
456 #endif /* defined(OPTION_DYNAMIC_LOAD) */
457         case 'b':
458             sysblk.logofile=optarg;
459             break;
460         case 'd':
461             sysblk.daemon_mode = 1;
462             break;
463         default:
464             arg_error = 1;
465 
466         } /* end switch(c) */
467     } /* end while */
468 
469     if (optind < argc)
470         arg_error = 1;
471 
472     /* Terminate if invalid arguments were detected */
473     if (arg_error)
474     {
475         logmsg("usage: %s [-f config-filename] [-d] [-b logo-filename]"
476 #if defined(OPTION_DYNAMIC_LOAD)
477                 " [-p dyn-load-dir] [[-l dynmod-to-load]...]"
478 #endif /* defined(OPTION_DYNAMIC_LOAD) */
479                 " [> logfile]\n",
480                 argv[0]);
481         delayed_exit(1);
482     }
483 
484     /* Register the SIGINT handler */
485     if ( signal (SIGINT, sigint_handler) == SIG_ERR )
486     {
487         logmsg(_("HHCIN001S Cannot register SIGINT handler: %s\n"),
488                 strerror(errno));
489         delayed_exit(1);
490     }
491 
492     /* Register the SIGTERM handler */
493     if ( signal (SIGTERM, sigterm_handler) == SIG_ERR )
494     {
495         logmsg(_("HHCIN009S Cannot register SIGTERM handler: %s\n"),
496                 strerror(errno));
497         delayed_exit(1);
498     }
499 
500 #if defined( _MSVC_ )
501     /* Register the Window console ctrl handlers */
502     if (SetConsoleCtrlHandler(console_ctrl_handler, TRUE) == FALSE)
503     {
504         logmsg(_("HHCIN010S Cannot register ConsoleCtrl handler: %s\n"),
505                 strerror(errno));
506         delayed_exit(1);
507     }
508 #endif
509 
510 #if defined(HAVE_DECL_SIGPIPE) && HAVE_DECL_SIGPIPE
511     /* Ignore the SIGPIPE signal, otherwise Hercules may terminate with
512        Broken Pipe error if the printer driver writes to a closed pipe */
513     if ( signal (SIGPIPE, SIG_IGN) == SIG_ERR )
514     {
515         logmsg(_("HHCIN002E Cannot suppress SIGPIPE signal: %s\n"),
516                 strerror(errno));
517     }
518 #endif
519 
520 #if defined( OPTION_WAKEUP_SELECT_VIA_PIPE )
521     {
522         int fds[2];
523         initialize_lock(&sysblk.cnslpipe_lock);
524         initialize_lock(&sysblk.sockpipe_lock);
525         sysblk.cnslpipe_flag=0;
526         sysblk.sockpipe_flag=0;
527         VERIFY( create_pipe(fds) >= 0 );
528         sysblk.cnslwpipe=fds[1];
529         sysblk.cnslrpipe=fds[0];
530         VERIFY( create_pipe(fds) >= 0 );
531         sysblk.sockwpipe=fds[1];
532         sysblk.sockrpipe=fds[0];
533     }
534 #endif // defined( OPTION_WAKEUP_SELECT_VIA_PIPE )
535 
536 #if !defined(NO_SIGABEND_HANDLER)
537     {
538     struct sigaction sa;
539         sa.sa_sigaction = (void*)&sigabend_handler;
540 #ifdef SA_NODEFER
541         sa.sa_flags = SA_NODEFER;
542 #else
543         sa.sa_flags = 0;
544 #endif
545 
546         /* Explictily initialize sa_mask to its default.        @PJJ */
547         sigemptyset(&sa.sa_mask);
548 
549         if( sigaction(SIGILL, &sa, NULL)
550          || sigaction(SIGFPE, &sa, NULL)
551          || sigaction(SIGSEGV, &sa, NULL)
552          || sigaction(SIGBUS, &sa, NULL)
553          || sigaction(SIGUSR1, &sa, NULL)
554          || sigaction(SIGUSR2, &sa, NULL) )
555         {
556             logmsg(_("HHCIN003S Cannot register SIGILL/FPE/SEGV/BUS/USR "
557                     "handler: %s\n"),
558                     strerror(errno));
559             delayed_exit(1);
560         }
561     }
562 #endif /*!defined(NO_SIGABEND_HANDLER)*/
563 
564     /* Build system configuration */
565     build_config (cfgfile);
566 
567     /* System initialisation time */
568     sysblk.todstart = hw_clock() << 8;
569 
570 #ifdef OPTION_MIPS_COUNTING
571     /* Initialize "maxrates" command reporting intervals */
572     curr_int_start_time = time( NULL );
573     prev_int_start_time = curr_int_start_time;
574 #endif
575 
576 #if !defined(NO_SIGABEND_HANDLER)
577     /* Start the watchdog */
578     if ( create_thread (&sysblk.wdtid, DETACHED,
579                         watchdog_thread, NULL, "watchdog_thread") )
580     {
581         logmsg(_("HHCIN004S Cannot create watchdog thread: %s\n"),
582                 strerror(errno));
583         delayed_exit(1);
584     }
585 #endif /*!defined(NO_SIGABEND_HANDLER)*/
586 
587 #ifdef OPTION_SHARED_DEVICES
588     /* Start the shared server */
589     if (sysblk.shrdport)
590         if ( create_thread (&sysblk.shrdtid, DETACHED,
591                             shared_server, NULL, "shared_server") )
592         {
593             logmsg(_("HHCIN006S Cannot create shared_server thread: %s\n"),
594                     strerror(errno));
595             delayed_exit(1);
596         }
597 
598     /* Retry pending connections */
599     {
600         DEVBLK *dev;
601         TID     tid;
602 
603         for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
604             if (dev->connecting)
605                 if ( create_thread (&tid, DETACHED,
606                            *dev->hnd->init, dev, "device connecting thread")
607                    )
608                 {
609                     logmsg(_("HHCIN007S Cannot create %4.4X connection thread: %s\n"),
610                             dev->devnum, strerror(errno));
611                     delayed_exit(1);
612                 }
613     }
614 #endif
615 
616     /* Start up the RC file processing thread */
617     create_thread(&rctid,DETACHED,
618                   process_rc_file,NULL,"process_rc_file");
619 
620     if(log_callback)
621     {
622         // 'herclin' called us. IT'S in charge. Create its requested
623         // logmsg intercept callback function and return back to it.
624         create_thread(&logcbtid,DETACHED,
625                       log_do_callback,NULL,"log_do_callback");
626         return(0);
627     }
628 
629     //---------------------------------------------------------------
630     // The below functions will not return until Hercules is shutdown
631     //---------------------------------------------------------------
632 
633     /* Activate the control panel */
634     if(!sysblk.daemon_mode)
635         panel_display ();
636     else
637     {
638 #if defined(OPTION_DYNAMIC_LOAD)
639         if(daemon_task)
640             daemon_task ();
641         else
642 #endif /* defined(OPTION_DYNAMIC_LOAD) */
643         {
644             /* Tell RC file and HAO threads they may now proceed */
645             sysblk.panel_init = 1;
646 
647             /* Retrieve messages from logger and write to stderr */
648             while (1)
649                 if((msgcnt = log_read(&msgbuf, &msgnum, LOG_BLOCK)))
650                     if(isatty(STDERR_FILENO))
651                         fwrite(msgbuf,msgcnt,1,stderr);
652         }
653     }
654 
655     //  -----------------------------------------------------
656     //      *** Hercules has been shutdown (PAST tense) ***
657     //  -----------------------------------------------------
658 
659     ASSERT( sysblk.shutdown );  // (why else would we be here?!)
660 
661 #ifdef _MSVC_
662     SetConsoleCtrlHandler(console_ctrl_handler, FALSE);
663     socket_deinit();
664 #endif
665 #ifdef DEBUG
666     fprintf(stdout, _("IMPL EXIT\n"));
667 #endif
668     fprintf(stdout, _("HHCIN099I Hercules terminated\n"));
669     fflush(stdout);
670     usleep(10000);
671     return 0;
672 } /* end function main */
673 
674 
675 /*-------------------------------------------------------------------*/
676 /* System cleanup                                                    */
677 /*-------------------------------------------------------------------*/
system_cleanup(void)678 DLL_EXPORT void system_cleanup (void)
679 {
680 //  logmsg("HHCIN950I Begin system cleanup\n");
681     /*
682         Currently only called by hdlmain,c's HDL_FINAL_SECTION
683         after the main 'hercules' module has been unloaded, but
684         that could change at some time in the future.
685 
686         The above and below logmsg's are commented out since this
687         function currently doesn't do anything yet. Once it DOES
688         something, they should be uncommented.
689     */
690 //  logmsg("HHCIN959I System cleanup complete\n");
691 }
692