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