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