1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1996-2020. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 /**
21  *
22  *  File:     heart.c
23  *  Purpose:  Portprogram for supervision of the Erlang emulator.
24  *
25  *  Synopsis: heart
26  *
27  *  SPAWNING FROM ERLANG
28  *
29  *  This program is started from Erlang as follows,
30  *
31  *      Port = open_port({spawn, 'heart'}, [{packet, 2}]),
32  *
33  *  ROLE OF THIS PORT PROGRAM
34  *
35  *  This program is started by the Erlang emulator. It  communicates
36  *  with the emulator through file descriptor 0 (standard input).
37  *
38  *  MESSAGE FORMAT
39  *
40  *  All messages have the following format (a value in parentheses
41  *  indicate field length in bytes),
42  *
43  *      {Length(2), Operation(1)}
44  *
45  *  START ACK
46  *
47  *  When this program has started it sends an START ACK message to Erlang.
48  *
49  *  HEART_BEATING
50  *
51  *  This program expects a heart beat message. If it does not receive a
52  *  heart beat message from Erlang within heart_beat_timeout seconds, it
53  *  reboots the system. The system is rebooted by executing the command
54  *  stored in the environment variable HEART_COMMAND.
55  *
56  *  BLOCKING DESCRIPTORS
57  *
58  *  All file descriptors in this program are blocking. This can lead
59  *  to deadlocks. The emulator reads and writes are blocking.
60  *
61  *  STANDARD INPUT, OUTPUT AND ERROR
62  *
63  *  This program communicates with Erlang through the standard
64  *  input and output file descriptors (0 and 1). These descriptors
65  *  (and the standard error descriptor 2) must NOT be closed
66  *  explicitely by this program at termination (in UNIX it is
67  *  taken care of by the operating system itself).
68  *
69  *  END OF FILE
70  *
71  *  If a read from a file descriptor returns zero (0), it means
72  *  that there is no process at the other end of the connection
73  *  having the connection open for writing (end-of-file).
74  *
75  */
76 
77 #ifdef HAVE_CONFIG_H
78 #  include "config.h"
79 #endif
80 
81 #ifdef __WIN32__
82 #include <windows.h>
83 #include <io.h>
84 #include <fcntl.h>
85 #include <process.h>
86 #endif
87 
88 /*
89  * Implement time correction using times() call even on Linuxes
90  * that can simulate gethrtime with clock_gettime, no use implementing
91  * a phony gethrtime in this file as the time questions are so infrequent.
92  */
93 
94 #include <stdio.h>
95 #include <stddef.h>
96 #include <stdlib.h>
97 
98 #include <stdarg.h>
99 
100 #include <string.h>
101 #include <time.h>
102 #include <errno.h>
103 
104 #if !defined(__WIN32__)
105 #  include <sys/types.h>
106 #  include <netinet/in.h>
107 #  include <sys/time.h>
108 #  include <unistd.h>
109 #  include <signal.h>
110 #  if defined(OS_MONOTONIC_TIME_USING_TIMES)
111 #    include <sys/times.h>
112 #    include <limits.h>
113 #  endif
114 #endif
115 
116 #ifdef __clang_analyzer__
117    /* CodeChecker does not seem to understand inline asm in FD_ZERO */
118 #  undef FD_ZERO
119 #  define FD_ZERO(FD_SET_PTR) memset(FD_SET_PTR, 0, sizeof(fd_set))
120 #endif
121 
122 #define HEART_COMMAND_ENV          "HEART_COMMAND"
123 #define ERL_CRASH_DUMP_SECONDS_ENV "ERL_CRASH_DUMP_SECONDS"
124 #define HEART_KILL_SIGNAL          "HEART_KILL_SIGNAL"
125 #define HEART_NO_KILL              "HEART_NO_KILL"
126 
127 
128 #define MSG_HDR_SIZE         (2)
129 #define MSG_HDR_PLUS_OP_SIZE (3)
130 #define MSG_BODY_SIZE        (2048)
131 #define MSG_TOTAL_SIZE       (2050)
132 
133 unsigned char cmd[MSG_BODY_SIZE];
134 
135 struct msg {
136   unsigned short len;
137   unsigned char op;
138   unsigned char fill[MSG_BODY_SIZE]; /* one too many */
139 };
140 
141 /* operations */
142 #define  HEART_ACK       (1)
143 #define  HEART_BEAT      (2)
144 #define  SHUT_DOWN       (3)
145 #define  SET_CMD         (4)
146 #define  CLEAR_CMD       (5)
147 #define  GET_CMD         (6)
148 #define  HEART_CMD       (7)
149 #define  PREPARING_CRASH (8)
150 
151 
152 /*  Maybe interesting to change */
153 
154 /* Times in seconds */
155 #define  SELECT_TIMEOUT               5  /* Every 5 seconds we reset the
156 					    watchdog timer */
157 
158 /* heart_beat_timeout is the maximum gap in seconds between two
159    consecutive heart beat messages from Erlang. */
160 
161 int heart_beat_timeout = 60;
162 /* All current platforms have a process identifier that
163    fits in an unsigned long and where 0 is an impossible or invalid value */
164 unsigned long heart_beat_kill_pid = 0;
165 
166 /* reasons for reboot */
167 #define  R_TIMEOUT          (1)
168 #define  R_CLOSED           (2)
169 #define  R_ERROR            (3)
170 #define  R_SHUT_DOWN        (4)
171 #define  R_CRASHING         (5) /* Doing a crash dump and we will wait for it */
172 
173 
174 /*  macros */
175 
176 #define  NULLFDS  ((fd_set *) NULL)
177 #define  NULLTV   ((struct timeval *) NULL)
178 
179 /*  prototypes */
180 
181 static int message_loop(int, int);
182 static void do_terminate(int, int);
183 static int notify_ack(int);
184 static int heart_cmd_reply(int, char *);
185 static int write_message(int, struct msg *);
186 static int read_message(int, struct msg *);
187 static int read_skip(int, char *, int, int);
188 static int read_fill(int, char *, int);
189 static void print_error(const char *,...);
190 static void debugf(const char *,...);
191 static void init_timestamp(void);
192 static time_t timestamp(time_t *);
193 static int  wait_until_close_write_or_env_tmo(int);
194 
195 #ifdef __WIN32__
196 static BOOL enable_privilege(void);
197 static BOOL do_shutdown(int);
198 static void print_last_error(void);
199 static HANDLE start_reader_thread(void);
200 static DWORD WINAPI reader(LPVOID);
201 #define read _read
202 #define write _write
203 #endif
204 
205 /*  static variables */
206 
207 static char program_name[256];
208 static int erlin_fd = 0, erlout_fd = 1; /* std in and out */
209 static int debug_on = 0;
210 #ifdef __WIN32__
211 static HANDLE hreader_thread;
212 static HANDLE hevent_dataready;
213 static struct msg m, *mp = &m;
214 static int   tlen;			/* total message length */
215 static FILE* conh;
216 
217 #endif
218 
219 static int
is_env_set(char * key)220 is_env_set(char *key)
221 {
222 #ifdef __WIN32__
223     char buf[1];
224     DWORD sz = (DWORD) sizeof(buf);
225     SetLastError(0);
226     sz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) buf, sz);
227     return sz || GetLastError() != ERROR_ENVVAR_NOT_FOUND;
228 #else
229     return getenv(key) != NULL;
230 #endif
231 }
232 
233 static char *
get_env(char * key)234 get_env(char *key)
235 {
236 #ifdef __WIN32__
237     DWORD size = 32;
238     char  *value=NULL;
239     wchar_t *wcvalue = NULL;
240     wchar_t wckey[256];
241     int len;
242 
243     MultiByteToWideChar(CP_UTF8, 0, key, -1, wckey, 256);
244 
245     while (1) {
246 	DWORD nsz;
247 	if (wcvalue)
248 	    free(wcvalue);
249 	wcvalue = malloc(size*sizeof(wchar_t));
250 	if (!wcvalue) {
251 	    print_error("Failed to allocate memory. Terminating...");
252 	    exit(1);
253 	}
254 	SetLastError(0);
255 	nsz = GetEnvironmentVariableW(wckey, wcvalue, size);
256 	if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
257 	    free(wcvalue);
258 	    return NULL;
259 	}
260 	if (nsz <= size) {
261 	    len = WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, NULL, 0, NULL, NULL);
262 	    value = malloc(len*sizeof(char));
263 	    if (!value) {
264 		print_error("Failed to allocate memory. Terminating...");
265 		exit(1);
266 	    }
267 	    WideCharToMultiByte(CP_UTF8, 0, wcvalue, -1, value, len, NULL, NULL);
268 	    free(wcvalue);
269 	    return value;
270 	}
271 	size = nsz;
272     }
273 #else
274     return getenv(key);
275 #endif
276 }
277 
278 static void
free_env_val(char * value)279 free_env_val(char *value)
280 {
281 #ifdef __WIN32__
282     if (value)
283 	free(value);
284 #endif
285 }
286 
287 /*
288  *  main
289  */
get_arguments(int argc,char ** argv)290 static void get_arguments(int argc, char** argv) {
291     int i = 1;
292     int h = -1;
293     unsigned long p = 0;
294 
295     while (i < argc) {
296 	switch (argv[i][0]) {
297 	case '-':
298 	    switch (argv[i][1]) {
299 	    case 'h':
300 		if (strcmp(argv[i], "-ht") == 0)
301 		    if (sscanf(argv[i+1],"%i",&h) ==1)
302 			if ((h > 10) && (h <= 65535)) {
303 			    heart_beat_timeout = h;
304 			    fprintf(stderr,"heart_beat_timeout = %d\n",h);
305 			    i++;
306 			}
307 		break;
308 	    case 'p':
309 		if (strcmp(argv[i], "-pid") == 0)
310 		    if (sscanf(argv[i+1],"%lu",&p) ==1){
311 			heart_beat_kill_pid = p;
312 			fprintf(stderr,"heart_beat_kill_pid = %lu\n",p);
313 			i++;
314 		    }
315 		break;
316 #ifdef __WIN32__
317 	    case 's':
318 		if (strcmp(argv[i], "-shutdown") == 0){
319 		    do_shutdown(1);
320 		    exit(0);
321 		}
322 		break;
323 #endif
324 	    default:
325 		;
326 	    }
327 	    break;
328 	default:
329 	    ;
330 	}
331 	i++;
332     }
333     debugf("arguments -ht %d -pid %lu\n",h,p);
334 }
335 
main(int argc,char ** argv)336 int main(int argc, char **argv) {
337 
338     if (is_env_set("HEART_DEBUG")) {
339 	fprintf(stderr, "heart: debug is ON!\r\n");
340 	debug_on = 1;
341     }
342 
343     get_arguments(argc,argv);
344 #ifdef __WIN32__
345     if (debug_on) {
346 	if(!is_env_set("ERLSRV_SERVICE_NAME")) {
347 	    /* this redirects stderr to a separate console (for debugging purposes)*/
348 	    erlin_fd = _dup(0);
349 	    erlout_fd = _dup(1);
350 	    AllocConsole();
351 	    conh = freopen("CONOUT$","w",stderr);
352 	    if (conh != NULL)
353 		fprintf(conh,"console alloced\n");
354 	}
355 	debugf("stderr\n");
356     }
357     _setmode(erlin_fd,_O_BINARY);
358     _setmode(erlout_fd,_O_BINARY);
359 #endif
360     strncpy(program_name, argv[0], sizeof(program_name));
361     program_name[sizeof(program_name)-1] = '\0';
362     notify_ack(erlout_fd);
363     cmd[0] = '\0';
364     do_terminate(erlin_fd,message_loop(erlin_fd,erlout_fd));
365     return 0;
366 }
367 
368 /*
369  * message loop
370  */
371 static int
message_loop(erlin_fd,erlout_fd)372 message_loop(erlin_fd, erlout_fd)
373      int   erlin_fd, erlout_fd;
374 {
375   int   i;
376   time_t now, last_received;
377 #ifdef __WIN32__
378   DWORD wresult;
379 #else
380   fd_set read_fds;
381   int   max_fd;
382   struct timeval timeout;
383   int   tlen;			/* total message length */
384   struct msg m, *mp = &m;
385 #endif
386 
387   init_timestamp();
388   timestamp(&now);
389   last_received = now;
390 #ifdef __WIN32__
391   hevent_dataready = CreateEvent(NULL,FALSE,FALSE,NULL);
392   hreader_thread = start_reader_thread();
393 #else
394   max_fd = erlin_fd;
395 #endif
396 
397   while (1) {
398       /* REFACTOR: below to select/tmo function */
399 #ifdef __WIN32__
400 	wresult = WaitForSingleObject(hevent_dataready,SELECT_TIMEOUT*1000+ 2);
401 	if (wresult == WAIT_FAILED) {
402 		print_last_error();
403 		return R_ERROR;
404 	}
405 
406 	if (wresult == WAIT_TIMEOUT) {
407 		debugf("wait timed out\n");
408 		i = 0;
409 	} else {
410 		debugf("wait ok\n");
411 		i = 1;
412 	}
413 #else
414     FD_ZERO(&read_fds);         /* ZERO on each turn */
415     FD_SET(erlin_fd, &read_fds);
416     timeout.tv_sec = SELECT_TIMEOUT;  /* On Linux timeout is modified
417 					 by select */
418     timeout.tv_usec = 0;
419     if ((i = select(max_fd + 1, &read_fds, NULLFDS, NULLFDS, &timeout)) < 0) {
420       print_error("error in select.");
421       return R_ERROR;
422     }
423 #endif
424     /*
425      * Maybe heart beat time-out
426      * If we havn't got anything in 60 seconds we reboot, even if we may
427      * have got something in the last 5 seconds. We may end up here if
428      * the system clock is adjusted with more than 55 seconds, but we
429      * regard this as en error and reboot anyway.
430      */
431     timestamp(&now);
432     if (now > last_received + heart_beat_timeout) {
433 	print_error("heart-beat time-out, no activity for %lu seconds",
434 		    (unsigned long) (now - last_received));
435 		return R_TIMEOUT;
436     }
437     /*
438      * Do not check fd-bits if select timeout
439      */
440     if (i == 0) {
441       continue;
442     }
443     /*
444      * Message from ERLANG
445      */
446 #ifdef __WIN32__
447 	if (wresult == WAIT_OBJECT_0) {
448 		if (tlen < 0) {
449 #else
450     if (FD_ISSET(erlin_fd, &read_fds)) {
451 		if ((tlen = read_message(erlin_fd, mp)) < 0) {
452 #endif
453 			print_error("error in read_message.");
454 			return R_ERROR;
455 		}
456 		if ((tlen > MSG_HDR_SIZE) && (tlen <= MSG_TOTAL_SIZE)) {
457 			switch (mp->op) {
458 			case HEART_BEAT:
459 				timestamp(&last_received);
460 				break;
461 			case SHUT_DOWN:
462 				return R_SHUT_DOWN;
463 			case SET_CMD:
464 				/* override the HEART_COMMAND_ENV command */
465 			        memcpy(&cmd, &(mp->fill[0]),
466 				       tlen-MSG_HDR_PLUS_OP_SIZE);
467 			        cmd[tlen-MSG_HDR_PLUS_OP_SIZE] = '\0';
468 			        notify_ack(erlout_fd);
469 			        break;
470 			case CLEAR_CMD:
471 				/* use the HEART_COMMAND_ENV command */
472 				cmd[0] = '\0';
473 				notify_ack(erlout_fd);
474 				break;
475 			case GET_CMD:
476 				/* send back command string */
477 			        {
478 				    char *env = NULL;
479 				    char *command
480 					= (cmd[0]
481 					   ? (char *)cmd
482 					   : (env = get_env(HEART_COMMAND_ENV)));
483 				    /* Not set and not in env  return "" */
484 				    if (!command) command = "";
485 				    heart_cmd_reply(erlout_fd, command);
486 				    free_env_val(env);
487 				}
488 			        break;
489 			case PREPARING_CRASH:
490 				/* Erlang has reached a crushdump point (is crashing for sure) */
491 				print_error("Erlang is crashing .. (waiting for crash dump file)");
492 				return R_CRASHING;
493 			default:
494 				/* ignore all other messages */
495 				break;
496 			}
497 		} else if (tlen == 0) {
498 		/* Erlang has closed its end */
499 		print_error("Erlang has closed.");
500 		return R_CLOSED;
501     }
502 		/* Junk erroneous messages */
503     }
504   }
505 }
506 
507 #if defined(__WIN32__)
508 static void
kill_old_erlang(int reason)509 kill_old_erlang(int reason){
510     HANDLE erlh;
511     DWORD exit_code;
512     char* envvar = NULL;
513 
514     envvar = get_env(HEART_NO_KILL);
515     if (envvar && strcmp(envvar, "TRUE") == 0)
516       return;
517 
518     if(heart_beat_kill_pid != 0){
519 	if((erlh = OpenProcess(PROCESS_TERMINATE |
520 			       SYNCHRONIZE |
521 			       PROCESS_QUERY_INFORMATION ,
522 			       FALSE,
523 			       (DWORD) heart_beat_kill_pid)) == NULL){
524 	    return;
525 	}
526 	if(!TerminateProcess(erlh, 1)){
527 	    CloseHandle(erlh);
528 	    return;
529 	}
530 	if(WaitForSingleObject(erlh,5000) != WAIT_OBJECT_0){
531 	    print_error("Old process did not die, "
532 			"WaitForSingleObject timed out.");
533 	    CloseHandle(erlh);
534 	    return;
535 	}
536 	if(!GetExitCodeProcess(erlh, &exit_code)){
537 	    print_error("Old process did not die, "
538 			"GetExitCodeProcess failed.");
539 	}
540 	CloseHandle(erlh);
541     }
542 }
543 #else
544 static void
kill_old_erlang(int reason)545 kill_old_erlang(int reason)
546 {
547     pid_t pid;
548     int i, res;
549     int sig = SIGKILL;
550     char *envvar = NULL;
551 
552     envvar = get_env(HEART_NO_KILL);
553     if (envvar && strcmp(envvar, "TRUE") == 0)
554       return;
555 
556     if(heart_beat_kill_pid != 0){
557         pid = (pid_t) heart_beat_kill_pid;
558         if (reason == R_CLOSED) {
559             print_error("Wait 5 seconds for Erlang to terminate nicely");
560             for (i=0; i < 5; ++i) {
561                 res = kill(pid, 0); /* check if alive */
562                 if (res < 0 && errno == ESRCH)
563                     return;
564                 sleep(1);
565             }
566             print_error("Erlang still alive, kill it");
567         }
568 
569         envvar = get_env(HEART_KILL_SIGNAL);
570         if (envvar && strcmp(envvar, "SIGABRT") == 0) {
571             print_error("kill signal SIGABRT requested");
572             sig = SIGABRT;
573         }
574 
575 	res = kill(pid,sig);
576 	for(i=0; i < 5 && res == 0; ++i){
577 	    sleep(1);
578 	    res = kill(pid,sig);
579 	}
580 	if(errno != ESRCH){
581 	    print_error("Unable to kill old process, "
582 			"kill failed (tried multiple times).");
583 	}
584     }
585 }
586 #endif
587 
588 #ifdef __WIN32__
win_system(char * command)589 void win_system(char *command)
590 {
591     char *comspec;
592     char * cmdbuff;
593     char * extra = " /C ";
594     wchar_t *wccmdbuff;
595     char *env;
596     STARTUPINFOW start;
597     SECURITY_ATTRIBUTES attr;
598     PROCESS_INFORMATION info;
599     int len;
600 
601     if (!debug_on) {
602 	len = MultiByteToWideChar(CP_UTF8, 0, command, -1, NULL, 0);
603 	wccmdbuff = malloc(len*sizeof(wchar_t));
604 	if (!wccmdbuff) {
605 	    print_error("Failed to allocate memory. Terminating...");
606 	    exit(1);
607 	}
608 	MultiByteToWideChar(CP_UTF8, 0, command, -1, wccmdbuff, len);
609 	_wsystem(wccmdbuff);
610 	return;
611     }
612     comspec = env = get_env("COMSPEC");
613     if (!comspec)
614 	comspec = "CMD.EXE";
615     cmdbuff = malloc(strlen(command) + strlen(comspec) + strlen(extra) + 1);
616     if (!cmdbuff) {
617 	print_error("Failed to allocate memory. Terminating...");
618 	exit(1);
619     }
620     strcpy(cmdbuff, comspec);
621     strcat(cmdbuff, extra);
622     strcat(cmdbuff, command);
623     free_env_val(env);
624 
625     debugf("running \"%s\"\r\n", cmdbuff);
626 
627     memset (&start, 0, sizeof (start));
628     start.cb = sizeof (start);
629     start.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
630     start.wShowWindow = SW_HIDE;
631     start.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
632     start.hStdOutput = GetStdHandle(STD_ERROR_HANDLE);
633     start.hStdError = GetStdHandle(STD_ERROR_HANDLE);
634 
635     attr.nLength = sizeof(attr);
636     attr.lpSecurityDescriptor = NULL;
637     attr.bInheritHandle = TRUE;
638 
639     fflush(stderr);
640 
641     len = MultiByteToWideChar(CP_UTF8, 0, cmdbuff, -1, NULL, 0);
642     wccmdbuff = malloc(len*sizeof(wchar_t));
643     if (!wccmdbuff) {
644 	print_error("Failed to allocate memory. Terminating...");
645 	exit(1);
646     }
647     MultiByteToWideChar(CP_UTF8, 0, cmdbuff, -1, wccmdbuff, len);
648 
649     if (!CreateProcessW(NULL,
650 			wccmdbuff,
651 			&attr,
652 			NULL,
653 			TRUE,
654 			0,
655 			NULL,
656 			NULL,
657 			&start,
658 			&info)) {
659 	debugf("Could not create process for the command %s.\r\n", cmdbuff);
660     }
661     WaitForSingleObject(info.hProcess,INFINITE);
662     free(cmdbuff);
663     free(wccmdbuff);
664 }
665 #endif /* defined(__WIN32__) */
666 
667 /*
668  * do_terminate
669  */
670 static void
do_terminate(int erlin_fd,int reason)671 do_terminate(int erlin_fd, int reason) {
672   int ret = 0, tmo=0;
673   char *tmo_env;
674 
675   switch (reason) {
676   case R_SHUT_DOWN:
677     break;
678   case R_CRASHING:
679     if (is_env_set(ERL_CRASH_DUMP_SECONDS_ENV)) {
680 	tmo_env = get_env(ERL_CRASH_DUMP_SECONDS_ENV);
681 	tmo = atoi(tmo_env);
682 	print_error("Waiting for dump - timeout set to %d seconds.", tmo);
683 	wait_until_close_write_or_env_tmo(tmo);
684 	free_env_val(tmo_env);
685     }
686     /* fall through */
687   case R_TIMEOUT:
688   case R_CLOSED:
689   case R_ERROR:
690   default:
691     {
692 #if defined(__WIN32__) /* Not VxWorks */
693 	if(!cmd[0]) {
694 	    char *command = get_env(HEART_COMMAND_ENV);
695 	    if(!command)
696 		print_error("Would reboot. Terminating.");
697 	    else {
698 		kill_old_erlang(reason);
699 		/* High prio combined with system() works badly indeed... */
700 		SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
701 		win_system(command);
702 		print_error("Executed \"%s\". Terminating.",command);
703 	    }
704 	    free_env_val(command);
705 	} else {
706 	    kill_old_erlang(reason);
707 	    /* High prio combined with system() works badly indeed... */
708 	    SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
709 	    win_system(&cmd[0]);
710 	    print_error("Executed \"%s\". Terminating.",cmd);
711 	}
712 #else
713 	if(!cmd[0]) {
714 	    char *command = get_env(HEART_COMMAND_ENV);
715 	    if(!command)
716 		print_error("Would reboot. Terminating.");
717 	    else {
718 		kill_old_erlang(reason);
719 		ret = system(command);
720 		print_error("Executed \"%s\" -> %d. Terminating.",command, ret);
721 	    }
722 	    free_env_val(command);
723 	} else {
724 	    kill_old_erlang(reason);
725 	    ret = system((char*)&cmd[0]);
726 	    print_error("Executed \"%s\" -> %d. Terminating.",cmd, ret);
727 	}
728 #endif
729     }
730     break;
731   } /* switch(reason) */
732 }
733 
734 
735 /* Waits until something happens on socket or handle
736  *
737  * Uses global variables erlin_fd or hevent_dataready
738  */
wait_until_close_write_or_env_tmo(int tmo)739 int wait_until_close_write_or_env_tmo(int tmo) {
740     int i = 0;
741 
742 #ifdef __WIN32__
743     DWORD wresult;
744     DWORD wtmo = INFINITE;
745 
746     if (tmo >= 0) {
747 	wtmo = tmo*1000 + 2;
748     }
749 
750     wresult = WaitForSingleObject(hevent_dataready, wtmo);
751     if (wresult == WAIT_FAILED) {
752 	print_last_error();
753 	return -1;
754     }
755 
756     if (wresult == WAIT_TIMEOUT) {
757 	debugf("wait timed out\n");
758 	i = 0;
759     } else {
760 	debugf("wait ok\n");
761 	i = 1;
762     }
763 #else
764     fd_set read_fds;
765     int   max_fd;
766     struct timeval timeout;
767     struct timeval *tptr = NULL;
768 
769     max_fd = erlin_fd; /* global */
770 
771     if (tmo >= 0) {
772 	timeout.tv_sec  = tmo;  /* On Linux timeout is modified by select */
773 	timeout.tv_usec = 0;
774 	tptr = &timeout;
775     }
776 
777     FD_ZERO(&read_fds);
778     FD_SET(erlin_fd, &read_fds);
779     if ((i = select(max_fd + 1, &read_fds, NULLFDS, NULLFDS, tptr)) < 0) {
780 	print_error("error in select.");
781 	return -1;
782     }
783 #endif
784     return i;
785 }
786 
787 
788 /*
789  * notify_ack
790  *
791  * Sends an HEART_ACK.
792  */
793 static int
notify_ack(fd)794 notify_ack(fd)
795   int   fd;
796 {
797   struct msg m;
798 
799   m.op = HEART_ACK;
800   m.len = htons(1);
801   return write_message(fd, &m);
802 }
803 
804 
805 /*
806  * send back current command
807  *
808  * Sends an HEART_CMD.
809  */
810 static int
heart_cmd_reply(int fd,char * s)811 heart_cmd_reply(int fd, char *s)
812 {
813   struct msg m;
814   int len = strlen(s);
815 
816   /* if s >= MSG_BODY_SIZE, return a write
817    * failure immediately.
818    */
819   if (len >= sizeof(m.fill))
820       return -1;
821 
822   m.op = HEART_CMD;
823   m.len = htons(len + 1);	/* Include Op */
824   strcpy((char*)m.fill, s);
825 
826   return write_message(fd, &m);
827 }
828 
829 
830 /*
831  *  write_message
832  *
833  *  Writes a message to a blocking file descriptor. Returns the total
834  *  size of the message written (always > 0), or -1 if error.
835  *
836  *  A message which is too short or too long, is not written. The return
837  *  value is then MSG_HDR_SIZE (2), as if the message had been written.
838  *  Is this really necessary? Can't we assume that the length is ok?
839  *  FIXME.
840  */
841 static int
write_message(fd,mp)842 write_message(fd, mp)
843   int   fd;
844   struct msg *mp;
845 {
846   int len = ntohs(mp->len);
847 
848   if ((len == 0) || (len > MSG_BODY_SIZE)) {
849     return MSG_HDR_SIZE;
850   }				/* cc68k wants (char *) */
851   if (write(fd, (char *) mp, len + MSG_HDR_SIZE) != len + MSG_HDR_SIZE) {
852     return -1;
853   }
854   return len + MSG_HDR_SIZE;
855 }
856 
857 /*
858  *  read_message
859  *
860  *  Reads a message from a blocking file descriptor. Returns the total
861  *  size of the message read (> 0), 0 if eof, and < 0 if error.
862  *
863  *  Note: The return value MSG_HDR_SIZE means a message of total size
864  *  MSG_HDR_SIZE, i.e. without even an operation field.
865  *
866  *  If the size of the message is larger than MSG_TOTAL_SIZE, the total
867  *  number of bytes read is returned, but the buffer contains a truncated
868  *  message.
869  */
870 static int
read_message(fd,mp)871 read_message(fd, mp)
872   int   fd;
873   struct msg *mp;
874 {
875   int   rlen, i;
876   unsigned char* tmp;
877 
878   if ((i = read_fill(fd, (char *) mp, MSG_HDR_SIZE)) != MSG_HDR_SIZE) {
879     /* < 0 is an error; = 0 is eof */
880     return i;
881   }
882 
883   tmp = (unsigned char*) &(mp->len);
884   rlen = (*tmp * 256) + *(tmp+1);
885   if (rlen == 0) {
886     return MSG_HDR_SIZE;
887   }
888   if (rlen > MSG_BODY_SIZE) {
889     if ((i = read_skip(fd, (((char *) mp) + MSG_HDR_SIZE),
890 		       MSG_BODY_SIZE, rlen)) != rlen) {
891       return i;
892     } else {
893       return rlen + MSG_HDR_SIZE;
894     }
895   }
896   if ((i = read_fill(fd, ((char *) mp + MSG_HDR_SIZE), rlen)) != rlen) {
897     return i;
898   }
899   return rlen + MSG_HDR_SIZE;
900 }
901 
902 /*
903  *  read_fill
904  *
905  *  Reads len bytes into buf from a blocking fd. Returns total number of
906  *  bytes read (i.e. len) , 0 if eof, or < 0 if error. len must be > 0.
907  */
908 static int
read_fill(fd,buf,len)909 read_fill(fd, buf, len)
910   int   fd, len;
911   char *buf;
912 {
913   int   i, got = 0;
914 
915   do {
916     if ((i = read(fd, buf + got, len - got)) <= 0) {
917       return i;
918     }
919     got += i;
920   } while (got < len);
921   return len;
922 }
923 
924 /*
925  *  read_skip
926  *
927  *  Reads len bytes into buf from a blocking fd, but puts not more than
928  *  maxlen bytes in buf. Returns total number of bytes read ( > 0),
929  *  0 if eof, or < 0 if error. len > maxlen > 0 must hold.
930  */
931 static int
read_skip(fd,buf,maxlen,len)932 read_skip(fd, buf, maxlen, len)
933   int   fd, maxlen, len;
934   char *buf;
935 {
936   int   i, got = 0;
937   char  c;
938 
939   if ((i = read_fill(fd, buf, maxlen)) <= 0) {
940     return i;
941   }
942   do {
943     if ((i = read(fd, &c, 1)) <= 0) {
944       return i;
945     }
946     got += i;
947   } while (got < len - maxlen);
948   return len;
949 }
950 
951 /*
952  *  print_error
953  */
954 static void
print_error(const char * format,...)955 print_error(const char *format,...)
956 {
957   va_list args;
958   time_t now;
959   char *timestr;
960 
961   va_start(args, format);
962   time(&now);
963   timestr = ctime(&now);
964   fprintf(stderr, "%s: %.*s: ", program_name, (int) strlen(timestr)-1, timestr);
965   vfprintf(stderr, format, args);
966   va_end(args);
967   fprintf(stderr, "\r\n");
968 }
969 
970 static void
debugf(const char * format,...)971 debugf(const char *format,...)
972 {
973   va_list args;
974 
975   if (debug_on) {
976       va_start(args, format);
977       fprintf(stderr, "Heart: ");
978       vfprintf(stderr, format, args);
979       va_end(args);
980       fprintf(stderr, "\r\n");
981   }
982 }
983 
984 #ifdef __WIN32__
print_last_error()985 void print_last_error() {
986 	LPTSTR lpMsgBuf;
987 	FormatMessage(
988 		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
989 		NULL,
990 		GetLastError(),
991 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
992 		(LPTSTR) &lpMsgBuf,
993 		0,
994 		NULL
995 	);
996 
997 	/* Display the string.*/
998 	fprintf(stderr,"GetLastError:%s\n",lpMsgBuf);
999 
1000 	/* Free the buffer. */
1001 	LocalFree( lpMsgBuf );
1002 }
1003 
1004 
enable_privilege()1005 static BOOL enable_privilege() {
1006 	HANDLE ProcessHandle;
1007 	DWORD DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
1008 	HANDLE TokenHandle;
1009 	TOKEN_PRIVILEGES Tpriv;
1010 	LUID luid;
1011 	ProcessHandle = GetCurrentProcess();
1012 	OpenProcessToken(ProcessHandle, DesiredAccess, &TokenHandle);
1013 	LookupPrivilegeValue(0,SE_SHUTDOWN_NAME,&luid);
1014 	Tpriv.PrivilegeCount = 1;
1015 	Tpriv.Privileges[0].Luid = luid;
1016 	Tpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1017 	return AdjustTokenPrivileges(TokenHandle,FALSE,&Tpriv,0,0,0);
1018 }
1019 
do_shutdown(int really_shutdown)1020 static BOOL do_shutdown(int really_shutdown) {
1021     enable_privilege();
1022     if (really_shutdown) {
1023 	if (InitiateSystemShutdown(NULL,"shutdown by HEART",10,TRUE,TRUE))
1024 	    return TRUE;
1025     } else if (InitiateSystemShutdown(NULL,
1026 				      "shutdown by HEART\n"
1027 				      "will be interrupted",
1028 				      30,TRUE,TRUE)) {
1029 	AbortSystemShutdown(NULL);
1030 	return TRUE;
1031     }
1032     return FALSE;
1033 }
1034 
reader(LPVOID lpvParam)1035 DWORD WINAPI reader(LPVOID lpvParam) {
1036 
1037 	while (1) {
1038 		debugf("reader is reading\n");
1039 		tlen = read_message(erlin_fd, mp);
1040 		debugf("reader setting event\n");
1041 		SetEvent(hevent_dataready);
1042 		if(tlen == 0)
1043 		    break;
1044 	}
1045 	return 0;
1046 }
1047 
start_reader_thread(void)1048 HANDLE start_reader_thread(void) {
1049 	DWORD tid;
1050 	HANDLE thandle;
1051 	if ((thandle = (HANDLE)
1052 	     _beginthreadex(NULL,0,reader,NULL,0,&tid)) == NULL) {
1053 		print_last_error();
1054 		exit(1);
1055 	}
1056 	return thandle;
1057 }
1058 #endif
1059 
1060 #if defined(__WIN32__)
1061 
1062 #  define TICK_MASK 0x7FFFFFFFUL
1063 
init_timestamp(void)1064 void init_timestamp(void)
1065 {
1066 }
1067 
timestamp(time_t * res)1068 time_t timestamp(time_t *res)
1069 {
1070     static time_t extra = 0;
1071     static unsigned last_ticks = 0;
1072     unsigned this_ticks;
1073     time_t r;
1074 
1075     this_ticks = GetTickCount() & TICK_MASK;
1076 
1077     if (this_ticks < last_ticks) {
1078 	extra += (time_t) ((TICK_MASK + 1) / 1000);
1079     }
1080 
1081     last_ticks = this_ticks;
1082 
1083     r = ((time_t) (this_ticks / 1000)) + extra;
1084 
1085     if (res != NULL)
1086 	*res = r;
1087     return r;
1088 }
1089 
1090 #elif defined(OS_MONOTONIC_TIME_USING_GETHRTIME) || defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
1091 
1092 #if defined(OS_MONOTONIC_TIME_USING_CLOCK_GETTIME)
1093 typedef long long SysHrTime;
1094 
1095 SysHrTime sys_gethrtime(void);
1096 
sys_gethrtime(void)1097 SysHrTime sys_gethrtime(void)
1098 {
1099     struct timespec ts;
1100     long long result;
1101     if (clock_gettime(MONOTONIC_CLOCK_ID,&ts) != 0) {
1102 	print_error("Fatal, could not get clock_monotonic value, terminating! "
1103 		    "errno = %d\n", errno);
1104 	exit(1);
1105     }
1106     result = ((long long) ts.tv_sec) * 1000000000LL +
1107 	((long long) ts.tv_nsec);
1108     return (SysHrTime) result;
1109 }
1110 #else
1111 typedef hrtime_t SysHrTime;
1112 #define sys_gethrtime() gethrtime()
1113 #endif
1114 
init_timestamp(void)1115 void init_timestamp(void)
1116 {
1117 }
1118 
timestamp(time_t * res)1119 time_t timestamp(time_t *res)
1120 {
1121     SysHrTime ht = sys_gethrtime();
1122     time_t r = (time_t) (ht / 1000000000);
1123     if (res != NULL)
1124 	*res = r;
1125     return r;
1126 }
1127 
1128 #elif defined(OS_MONOTONIC_TIME_USING_TIMES)
1129 
1130 #  ifdef NO_SYSCONF
1131 #    include <sys/param.h>
1132 #    define TICKS_PER_SEC()	HZ
1133 #  else
1134 #    define TICKS_PER_SEC()	sysconf(_SC_CLK_TCK)
1135 #  endif
1136 
1137 #  define TICK_MASK 0x7FFFFFFFUL
1138 
1139 static unsigned tps;
1140 
init_timestamp(void)1141 void init_timestamp(void)
1142 {
1143     tps = TICKS_PER_SEC();
1144 }
1145 
timestamp(time_t * res)1146 time_t timestamp(time_t *res)
1147 {
1148     static time_t extra = 0;
1149     static clock_t last_ticks = 0;
1150     clock_t this_ticks;
1151     struct tms dummy;
1152     time_t r;
1153 
1154     this_ticks = (times(&dummy) & TICK_MASK);
1155 
1156     if (this_ticks < last_ticks) {
1157 	extra += (time_t) ((TICK_MASK + 1) / tps);
1158     }
1159 
1160     last_ticks = this_ticks;
1161 
1162     r = ((time_t) (this_ticks / tps)) + extra;
1163 
1164     if (res != NULL)
1165 	*res = r;
1166     return r;
1167 }
1168 
1169 #else
1170 
init_timestamp(void)1171 void init_timestamp(void)
1172 {
1173 }
1174 
timestamp(time_t * res)1175 time_t timestamp(time_t *res)
1176 {
1177     return time(res);
1178 }
1179 
1180 #endif
1181