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 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #ifdef ISC32
26 #define _POSIX_SOURCE
27 #define _XOPEN_SOURCE
28 #endif
29
30 #include <sys/times.h> /* ! */
31 #include <time.h>
32 #include <signal.h>
33 #include <sys/wait.h>
34 #include <sys/uio.h>
35 #include <termios.h>
36 #include <ctype.h>
37 #include <sys/utsname.h>
38 #include <sys/select.h>
39
40 #ifdef ISC32
41 #include <sys/bsdtypes.h>
42 #endif
43
44 #include <termios.h>
45 #ifdef HAVE_FCNTL_H
46 #include <fcntl.h>
47 #endif
48 #ifdef HAVE_SYS_IOCTL_H
49 #include <sys/ioctl.h>
50 #endif
51
52 #ifdef ADDRESS_SANITIZER
53 # include <sanitizer/asan_interface.h>
54 #endif
55
56 #define ERTS_WANT_BREAK_HANDLING
57 #define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
58 #include "sys.h"
59 #include "erl_thr_progress.h"
60
61 #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
62 #define __DARWIN__ 1
63 #endif
64
65 #include "erl_threads.h"
66
67 #include "erl_mseg.h"
68
69 #define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
70 * vector sock_sendv().
71 */
72 /*
73 * Don't need global.h, but bif_table.h (included by bif.h),
74 * won't compile otherwise
75 */
76 #include "global.h"
77 #include "bif.h"
78
79 #include "erl_check_io.h"
80 #include "erl_cpu_topology.h"
81 #include "erl_osenv.h"
82 #include "erl_dyn_lock_check.h"
83 extern int driver_interrupt(int, int);
84 extern void do_break(void);
85
86 extern void erl_sys_args(int*, char**);
87
88 /* The following two defs should probably be moved somewhere else */
89
90 extern void erts_sys_init_float(void);
91
92 #ifdef DEBUG
93 static int debug_log = 0;
94 #endif
95
96 static erts_atomic32_t have_prepared_crash_dump;
97 #define ERTS_PREPARED_CRASH_DUMP \
98 ((int) erts_atomic32_xchg_nob(&have_prepared_crash_dump, 1))
99
100 erts_atomic_t sys_misc_mem_sz;
101
102 static void smp_sig_notify(int signum);
103 static int sig_notify_fds[2] = {-1, -1};
104
105 #ifdef ERTS_SYS_SUSPEND_SIGNAL
106 static int sig_suspend_fds[2] = {-1, -1};
107 #endif
108
109
110 jmp_buf erts_sys_sigsegv_jmp;
111
112 static int crashdump_companion_cube_fd = -1;
113
114 /********************* General functions ****************************/
115
116 /* This is used by both the drivers and general I/O, must be set early */
117 static int max_files = -1;
118
119 /*
120 * a few variables used by the break handler
121 */
122 erts_atomic32_t erts_break_requested;
123 #define ERTS_SET_BREAK_REQUESTED \
124 erts_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 1)
125 #define ERTS_UNSET_BREAK_REQUESTED \
126 erts_atomic32_set_nob(&erts_break_requested, (erts_aint32_t) 0)
127
128
129 /* set early so the break handler has access to initial mode */
130 static struct termios initial_tty_mode;
131 static int replace_intr = 0;
132 /* assume yes initially, ttsl_init will clear it */
133 int using_oldshell = 1;
134
135 UWord
erts_sys_get_page_size(void)136 erts_sys_get_page_size(void)
137 {
138 #if defined(_SC_PAGESIZE)
139 return (UWord) sysconf(_SC_PAGESIZE);
140 #elif defined(HAVE_GETPAGESIZE)
141 return (UWord) getpagesize();
142 #else
143 return (UWord) 4*1024; /* Guess 4 KB */
144 #endif
145 }
146
147 Uint
erts_sys_misc_mem_sz(void)148 erts_sys_misc_mem_sz(void)
149 {
150 Uint res = erts_check_io_size();
151 res += erts_atomic_read_mb(&sys_misc_mem_sz);
152 return res;
153 }
154
155 /*
156 * reset the terminal to the original settings on exit
157 */
sys_tty_reset(int exit_code)158 void sys_tty_reset(int exit_code)
159 {
160 if (using_oldshell && !replace_intr) {
161 SET_BLOCKING(0);
162 }
163 else if (isatty(0)) {
164 tcsetattr(0,TCSANOW,&initial_tty_mode);
165 }
166 }
167
168 #ifdef __tile__
169 /* Direct malloc to spread memory around the caches of multiple tiles. */
170 #include <malloc.h>
171 #if defined(MALLOC_USE_HASH)
172 MALLOC_USE_HASH(1);
173 #endif
174 #endif
175
176
177 #ifdef ERTS_THR_HAVE_SIG_FUNCS
178
179 /*
180 * Child thread inherits parents signal mask at creation. In order to
181 * guarantee that the main thread will receive all SIGINT, and
182 * SIGUSR1 signals sent to the process, we block these signals in the
183 * parent thread when creating a new thread.
184 */
185
186 static sigset_t thr_create_sigmask;
187
188 #endif /* #ifdef ERTS_THR_HAVE_SIG_FUNCS */
189
190 typedef struct {
191 #ifdef ERTS_THR_HAVE_SIG_FUNCS
192 sigset_t saved_sigmask;
193 #endif
194 int sched_bind_data;
195 } erts_thr_create_data_t;
196
197 /*
198 * thr_create_prepare() is called in parent thread before thread creation.
199 * Returned value is passed as argument to thr_create_cleanup().
200 */
201 static void *
thr_create_prepare(void)202 thr_create_prepare(void)
203 {
204 erts_thr_create_data_t *tcdp;
205
206 tcdp = erts_alloc(ERTS_ALC_T_TMP, sizeof(erts_thr_create_data_t));
207
208 #ifdef ERTS_THR_HAVE_SIG_FUNCS
209 erts_thr_sigmask(SIG_BLOCK, &thr_create_sigmask, &tcdp->saved_sigmask);
210 #endif
211 tcdp->sched_bind_data = erts_sched_bind_atthrcreate_prepare();
212
213 return (void *) tcdp;
214 }
215
216
217 /* thr_create_cleanup() is called in parent thread after thread creation. */
218 static void
thr_create_cleanup(void * vtcdp)219 thr_create_cleanup(void *vtcdp)
220 {
221 erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp;
222
223 erts_sched_bind_atthrcreate_parent(tcdp->sched_bind_data);
224
225 #ifdef ERTS_THR_HAVE_SIG_FUNCS
226 /* Restore signalmask... */
227 erts_thr_sigmask(SIG_SETMASK, &tcdp->saved_sigmask, NULL);
228 #endif
229
230 erts_free(ERTS_ALC_T_TMP, tcdp);
231 }
232
233 static void
thr_create_prepare_child(void * vtcdp)234 thr_create_prepare_child(void *vtcdp)
235 {
236 erts_thr_create_data_t *tcdp = (erts_thr_create_data_t *) vtcdp;
237
238 #ifdef ERTS_ENABLE_LOCK_COUNT
239 erts_lcnt_thread_setup();
240 #endif
241
242 erts_sched_bind_atthrcreate_child(tcdp->sched_bind_data);
243 }
244
245 void
erts_sys_pre_init(void)246 erts_sys_pre_init(void)
247 {
248 erts_thr_init_data_t eid = ERTS_THR_INIT_DATA_DEF_INITER;
249
250 erts_printf_add_cr_to_stdout = 1;
251 erts_printf_add_cr_to_stderr = 1;
252
253 eid.thread_create_child_func = thr_create_prepare_child;
254 /* Before creation in parent */
255 eid.thread_create_prepare_func = thr_create_prepare;
256 /* After creation in parent */
257 eid.thread_create_parent_func = thr_create_cleanup,
258
259 /* Must be done really early. */
260 sys_init_signal_stack();
261
262 #ifdef ERTS_ENABLE_LOCK_COUNT
263 erts_lcnt_pre_thr_init();
264 #endif
265
266 erts_thr_init(&eid);
267
268 #ifdef ERTS_ENABLE_LOCK_COUNT
269 erts_lcnt_post_thr_init();
270 #endif
271
272 #ifdef ERTS_ENABLE_LOCK_CHECK
273 erts_lc_init();
274 #endif
275 #ifdef ERTS_DYN_LOCK_CHECK
276 erts_dlc_init();
277 #endif
278
279 erts_init_sys_time_sup();
280
281
282 erts_atomic32_init_nob(&erts_break_requested, 0);
283 erts_atomic32_init_nob(&have_prepared_crash_dump, 0);
284
285
286 erts_atomic_init_nob(&sys_misc_mem_sz, 0);
287
288 {
289 /*
290 * Unfortunately we depend on fd 0,1,2 in the old shell code.
291 * So if for some reason we do not have those open when we start
292 * we have to open them here. Not doing this can cause the emulator
293 * to deadlock when reaping the fd_driver ports :(
294 */
295 int fd;
296 /* Make sure fd 0 is open */
297 if ((fd = open("/dev/null", O_RDONLY)) != 0)
298 close(fd);
299 /* Make sure fds 1 and 2 are open */
300 while (fd < 3) {
301 fd = open("/dev/null", O_WRONLY);
302 }
303 close(fd);
304 }
305
306 /* We need a file descriptor to close in the crashdump creation.
307 * We close this one to be sure we can get a fd for our real file ...
308 * so, we create one here ... a stone to carry all the way home.
309 */
310
311 crashdump_companion_cube_fd = open("/dev/null", O_RDONLY);
312
313 /* don't lose it, there will be cake */
314 }
315
erts_sys_scheduler_init(void)316 void erts_sys_scheduler_init(void) {
317 sys_thread_init_signal_stack();
318 }
319
320 void
erl_sys_init(void)321 erl_sys_init(void)
322 {
323
324 #ifdef USE_SETLINEBUF
325 setlinebuf(stdout);
326 #else
327 setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
328 #endif
329
330 erts_sys_init_float();
331
332 /* we save this so the break handler can set and reset it properly */
333 /* also so that we can reset on exit (break handler or not) */
334 if (isatty(0)) {
335 tcgetattr(0,&initial_tty_mode);
336 }
337 tzset(); /* Required at least for NetBSD with localtime_r() */
338 }
339
340 /* signal handling */
341
sys_signal(int sig,SIGFUNC func)342 SIGFUNC sys_signal(int sig, SIGFUNC func)
343 {
344 struct sigaction act, oact;
345
346 sigemptyset(&act.sa_mask);
347 act.sa_flags = 0;
348 act.sa_handler = func;
349 sigaction(sig, &act, &oact);
350 return(oact.sa_handler);
351 }
352
353 #undef sigprocmask
354 #define sigprocmask erts_thr_sigmask
355
sys_sigblock(int sig)356 void sys_sigblock(int sig)
357 {
358 sigset_t mask;
359
360 sigemptyset(&mask);
361 sigaddset(&mask, sig);
362 sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL);
363 }
364
sys_sigrelease(int sig)365 void sys_sigrelease(int sig)
366 {
367 sigset_t mask;
368
369 sigemptyset(&mask);
370 sigaddset(&mask, sig);
371 sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL);
372 }
373
374 #ifdef ERTS_HAVE_TRY_CATCH
erts_sys_sigsegv_handler(int signo)375 void erts_sys_sigsegv_handler(int signo) {
376 if (signo == SIGSEGV) {
377 longjmp(erts_sys_sigsegv_jmp, 1);
378 }
379 }
380 #endif
381
382 /*
383 * Function returns 1 if we can read from all values in between
384 * start and stop.
385 */
386 int
erts_sys_is_area_readable(char * start,char * stop)387 erts_sys_is_area_readable(char *start, char *stop) {
388 #ifdef ADDRESS_SANITIZER
389 return __asan_region_is_poisoned(start, stop-start) == NULL;
390 #else
391 int fds[2];
392 if (!pipe(fds)) {
393 /* We let write try to figure out if the pointers are readable */
394 int res = write(fds[1], start, (char*)stop - (char*)start);
395 if (res == -1) {
396 close(fds[0]);
397 close(fds[1]);
398 return 0;
399 }
400 close(fds[0]);
401 close(fds[1]);
402 return 1;
403 }
404 return 0;
405 #endif
406 }
407
408 static ERTS_INLINE int
prepare_crash_dump(int secs)409 prepare_crash_dump(int secs)
410 {
411 #define NUFBUF (3)
412 int i;
413 char env[21]; /* enough to hold any 64-bit integer */
414 size_t envsz;
415 DeclareTmpHeapNoproc(heap,NUFBUF);
416 Port *heart_port;
417 Eterm *hp = heap;
418 Eterm list = NIL;
419 int has_heart = 0;
420
421 UseTmpHeapNoproc(NUFBUF);
422
423 if (ERTS_PREPARED_CRASH_DUMP)
424 return 0; /* We have already been called */
425
426 heart_port = erts_get_heart_port();
427
428 /* Positive secs means an alarm must be set
429 * 0 or negative means no alarm
430 *
431 * Set alarm before we try to write to a port
432 * we don't want to hang on a port write with
433 * no alarm.
434 *
435 */
436
437 if (secs >= 0) {
438 alarm((unsigned int)secs);
439 }
440
441 /* close all viable sockets via emergency close callbacks.
442 * Specifically we want to close epmd sockets.
443 */
444
445 erts_emergency_close_ports();
446
447 if (heart_port) {
448 has_heart = 1;
449 list = CONS(hp, make_small(8), list); hp += 2;
450 /* send to heart port, CMD = 8, i.e. prepare crash dump =o */
451 erts_port_output(NULL, ERTS_PORT_SIG_FLG_FORCE_IMM_CALL, heart_port,
452 heart_port->common.id, list, NULL);
453 }
454
455 /* Make sure we have a fd for our crashdump file. */
456 close(crashdump_companion_cube_fd);
457
458 envsz = sizeof(env);
459 i = erts_sys_explicit_8bit_getenv("ERL_CRASH_DUMP_NICE", env, &envsz);
460 if (i != 0) {
461 int nice_val;
462 nice_val = (i != 1) ? 0 : atoi(env);
463 if (nice_val > 39) {
464 nice_val = 39;
465 }
466 erts_silence_warn_unused_result(nice(nice_val));
467 }
468
469 UnUseTmpHeapNoproc(NUFBUF);
470 #undef NUFBUF
471 return has_heart;
472 }
473
erts_sys_prepare_crash_dump(int secs)474 int erts_sys_prepare_crash_dump(int secs)
475 {
476 return prepare_crash_dump(secs);
477 }
478
signal_notify_requested(Eterm type)479 static void signal_notify_requested(Eterm type) {
480 Process* p = NULL;
481 Eterm msg, *hp;
482 ErtsProcLocks locks = 0;
483 ErlOffHeap *ohp;
484
485 Eterm id = erts_whereis_name_to_id(NULL, am_erl_signal_server);
486
487 if ((p = (erts_pid2proc_opt(NULL, 0, id, 0, ERTS_P2P_FLG_INC_REFC))) != NULL) {
488 ErtsMessage *msgp = erts_alloc_message_heap(p, &locks, 3, &hp, &ohp);
489
490 /* erl_signal_server ! {notify, sighup} */
491 msg = TUPLE2(hp, am_notify, type);
492 erts_queue_message(p, locks, msgp, msg, am_system);
493
494 if (locks)
495 erts_proc_unlock(p, locks);
496 erts_proc_dec_refc(p);
497 }
498 }
499
500
501 static ERTS_INLINE void
break_requested(void)502 break_requested(void)
503 {
504 /*
505 * just set a flag - checked for and handled by
506 * scheduler threads erts_check_io() (not signal handler).
507 */
508 if (ERTS_BREAK_REQUESTED)
509 erts_exit(ERTS_INTR_EXIT, "");
510
511 ERTS_SET_BREAK_REQUESTED;
512 /* Wake aux thread to get handle break */
513 erts_aux_thread_poke();
514 }
515
request_break(int signum)516 static RETSIGTYPE request_break(int signum)
517 {
518 smp_sig_notify(signum);
519 }
520
521 #ifdef ETHR_UNUSABLE_SIGUSRX
522 #warning "Unusable SIGUSR1 & SIGUSR2. Disabling use of these signals"
523
524 #else
525
526 #ifdef ERTS_SYS_SUSPEND_SIGNAL
527 void
sys_thr_suspend(erts_tid_t tid)528 sys_thr_suspend(erts_tid_t tid) {
529 erts_thr_kill(tid, ERTS_SYS_SUSPEND_SIGNAL);
530 }
531
532 void
sys_thr_resume(erts_tid_t tid)533 sys_thr_resume(erts_tid_t tid) {
534 int i = 0, res;
535 do {
536 res = write(sig_suspend_fds[1],&i,sizeof(i));
537 } while (res < 0 && errno == EAGAIN);
538 }
539 #endif
540
541 #ifdef ERTS_SYS_SUSPEND_SIGNAL
542 #if (defined(SIG_SIGSET) || defined(SIG_SIGNAL))
suspend_signal(void)543 static RETSIGTYPE suspend_signal(void)
544 #else
545 static RETSIGTYPE suspend_signal(int signum)
546 #endif
547 {
548 int res, buf[1], tmp_errno = errno;
549 do {
550 res = read(sig_suspend_fds[0], buf, sizeof(int));
551 } while (res < 0 && errno == EINTR);
552
553 /* restore previous errno in case read changed it */
554 errno = tmp_errno;
555 }
556 #endif /* #ifdef ERTS_SYS_SUSPEND_SIGNAL */
557
558 #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
559
560 /*
561 Signal Action Comment
562 ─────────────────────────────────────────────────────────────
563 SIGHUP Term Hangup detected on controlling terminal or death of controlling process
564 !SIGINT Term Interrupt from keyboard
565 SIGQUIT Core Quit from keyboard
566 !SIGILL Core Illegal Instruction
567 SIGABRT Core Abort signal from abort(3)
568 !SIGFPE Core Floating point exception
569 !SIGKILL Term Kill signal
570 !SIGSEGV Core Invalid memory reference
571 !SIGPIPE Term Broken pipe: write to pipe with no readers
572 SIGALRM Term Timer signal from alarm(2)
573 SIGTERM Term Termination signal
574 SIGUSR1 Term User-defined signal 1
575 SIGUSR2 Term User-defined signal 2
576 !SIGCHLD Ign Child stopped or terminated
577 !SIGCONT Cont Continue if stopped
578 SIGSTOP Stop Stop process
579 SIGTSTP Stop Stop typed at terminal
580 !SIGTTIN Stop Terminal input for background process
581 !SIGTTOU Stop Terminal output for background process
582 */
583
584
585 static ERTS_INLINE int
signalterm_to_signum(Eterm signal)586 signalterm_to_signum(Eterm signal)
587 {
588 switch (signal) {
589 case am_sighup: return SIGHUP;
590 /* case am_sigint: return SIGINT; */
591 case am_sigquit: return SIGQUIT;
592 /* case am_sigill: return SIGILL; */
593 case am_sigabrt: return SIGABRT;
594 /* case am_sigsegv: return SIGSEGV; */
595 case am_sigalrm: return SIGALRM;
596 case am_sigterm: return SIGTERM;
597 case am_sigusr1: return SIGUSR1;
598 case am_sigusr2: return SIGUSR2;
599 case am_sigchld: return SIGCHLD;
600 case am_sigstop: return SIGSTOP;
601 case am_sigtstp: return SIGTSTP;
602 default: return 0;
603 }
604 }
605
606 static ERTS_INLINE Eterm
signum_to_signalterm(int signum)607 signum_to_signalterm(int signum)
608 {
609 switch (signum) {
610 case SIGHUP: return am_sighup;
611 /* case SIGINT: return am_sigint; */ /* ^c */
612 case SIGQUIT: return am_sigquit; /* ^\ */
613 /* case SIGILL: return am_sigill; */
614 case SIGABRT: return am_sigabrt;
615 /* case SIGSEGV: return am_sigsegv; */
616 case SIGALRM: return am_sigalrm;
617 case SIGTERM: return am_sigterm;
618 case SIGUSR1: return am_sigusr1;
619 case SIGUSR2: return am_sigusr2;
620 case SIGCHLD: return am_sigchld;
621 case SIGSTOP: return am_sigstop;
622 case SIGTSTP: return am_sigtstp; /* ^z */
623 default: return am_error;
624 }
625 }
626
generic_signal_handler(int signum)627 static RETSIGTYPE generic_signal_handler(int signum)
628 {
629 smp_sig_notify(signum);
630 }
631
erts_set_signal(Eterm signal,Eterm type)632 int erts_set_signal(Eterm signal, Eterm type) {
633 int signum;
634 if ((signum = signalterm_to_signum(signal)) > 0) {
635 if (type == am_ignore) {
636 sys_signal(signum, SIG_IGN);
637 } else if (type == am_default) {
638 sys_signal(signum, SIG_DFL);
639 } else {
640 sys_signal(signum, generic_signal_handler);
641 }
642 return 1;
643 }
644 return 0;
645 }
646
647 /* Disable break */
erts_set_ignore_break(void)648 void erts_set_ignore_break(void) {
649 /*
650 * Ignore signals that can be sent to the VM by
651 * typing certain key combinations at the
652 * controlling terminal...
653 */
654 sys_signal(SIGINT, SIG_IGN); /* Ctrl-C */
655 sys_signal(SIGQUIT, SIG_IGN); /* Ctrl-\ */
656 sys_signal(SIGTSTP, SIG_IGN); /* Ctrl-Z */
657 }
658
659 /* Don't use ctrl-c for break handler but let it be
660 used by the shell instead (see user_drv.erl) */
erts_replace_intr(void)661 void erts_replace_intr(void) {
662 struct termios mode;
663
664 if (isatty(0)) {
665 tcgetattr(0, &mode);
666
667 /* here's an example of how to replace ctrl-c with ctrl-u */
668 /* mode.c_cc[VKILL] = 0;
669 mode.c_cc[VINTR] = CKILL; */
670
671 mode.c_cc[VINTR] = 0; /* disable ctrl-c */
672 tcsetattr(0, TCSANOW, &mode);
673 replace_intr = 1;
674 }
675 }
676
init_break_handler(void)677 void init_break_handler(void)
678 {
679 sys_signal(SIGINT, request_break);
680 sys_signal(SIGQUIT, generic_signal_handler);
681 }
682
sys_init_suspend_handler(void)683 void sys_init_suspend_handler(void)
684 {
685 #ifdef ERTS_SYS_SUSPEND_SIGNAL
686 sys_signal(ERTS_SYS_SUSPEND_SIGNAL, suspend_signal);
687 #endif
688 }
689
690 void
erts_sys_unix_later_init(void)691 erts_sys_unix_later_init(void)
692 {
693 sys_signal(SIGTERM, generic_signal_handler);
694 #ifndef ETHR_UNUSABLE_SIGUSRX
695 sys_signal(SIGUSR1, generic_signal_handler);
696 #endif /* #ifndef ETHR_UNUSABLE_SIGUSRX */
697
698 /* Ignore SIGCHLD to ensure orphaned processes don't turn into zombies on
699 * death when we're pid 1. */
700 sys_signal(SIGCHLD, SIG_IGN);
701 }
702
sys_max_files(void)703 int sys_max_files(void)
704 {
705 return max_files;
706 }
707
708 /************************** OS info *******************************/
709
710 /* Used by erlang:info/1. */
711 /* (This code was formerly in drv.XXX/XXX_os_drv.c) */
712
713 char os_type[] = "unix";
714
715 static int
get_number(char ** str_ptr)716 get_number(char **str_ptr)
717 {
718 char* s = *str_ptr; /* Pointer to beginning of string. */
719 char* dot; /* Pointer to dot in string or NULL. */
720
721 if (!isdigit((int) *s))
722 return 0;
723 if ((dot = strchr(s, '.')) == NULL) {
724 *str_ptr = s+strlen(s);
725 return atoi(s);
726 } else {
727 *dot = '\0';
728 *str_ptr = dot+1;
729 return atoi(s);
730 }
731 }
732
os_flavor(char * namebuf,unsigned size)733 void os_flavor(char* namebuf, unsigned size) {
734 struct utsname uts; /* Information about the system. */
735 char* s;
736
737 (void) uname(&uts);
738 for (s = uts.sysname; *s; s++) {
739 if (isupper((int) *s)) {
740 *s = tolower((int) *s);
741 }
742 }
743 strcpy(namebuf, uts.sysname);
744 }
745
os_version(int * pMajor,int * pMinor,int * pBuild)746 void os_version(int *pMajor, int *pMinor, int *pBuild) {
747 struct utsname uts; /* Information about the system. */
748 char* release; /* Pointer to the release string:
749 * X.Y or X.Y.Z. */
750
751 (void) uname(&uts);
752 #ifdef _AIX
753 /* AIX stores the major in version and minor in release */
754 *pMajor = atoi(uts.version);
755 *pMinor = atoi(uts.release);
756 *pBuild = 0; /* XXX: get oslevel for AIX or TR on i */
757 #else
758 release = uts.release;
759 *pMajor = get_number(&release); /* Pointer to major version. */
760 *pMinor = get_number(&release); /* Pointer to minor version. */
761 *pBuild = get_number(&release); /* Pointer to build number. */
762 #endif
763 }
764
erts_do_break_handling(void)765 void erts_do_break_handling(void)
766 {
767 struct termios temp_mode;
768 int saved = 0;
769
770 /*
771 * Most functions that do_break() calls are intentionally not thread safe;
772 * therefore, make sure that all threads but this one are blocked before
773 * proceeding!
774 */
775 erts_thr_progress_block();
776
777 /* during break we revert to initial settings */
778 /* this is done differently for oldshell */
779 if (using_oldshell && !replace_intr) {
780 SET_BLOCKING(1);
781 }
782 else if (isatty(0)) {
783 tcgetattr(0,&temp_mode);
784 tcsetattr(0,TCSANOW,&initial_tty_mode);
785 saved = 1;
786 }
787
788 /* call the break handling function, reset the flag */
789 do_break();
790
791 ERTS_UNSET_BREAK_REQUESTED;
792
793 fflush(stdout);
794
795 /* after break we go back to saved settings */
796 if (using_oldshell && !replace_intr) {
797 SET_NONBLOCKING(1);
798 }
799 else if (saved) {
800 tcsetattr(0,TCSANOW,&temp_mode);
801 }
802
803 erts_thr_progress_unblock();
804 }
805
806
807 /* Fills in the systems representation of the jam/beam process identifier.
808 ** The Pid is put in STRING representation in the supplied buffer,
809 ** no interpretatione of this should be done by the rest of the
810 ** emulator. The buffer should be at least 21 bytes long.
811 */
sys_get_pid(char * buffer,size_t buffer_size)812 void sys_get_pid(char *buffer, size_t buffer_size){
813 pid_t p = getpid();
814 /* Assume the pid is scalar and can rest in an unsigned long... */
815 erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
816 }
817
818
sys_init_io(void)819 void sys_init_io(void) { }
erts_sys_alloc_init(void)820 void erts_sys_alloc_init(void) { }
821
822 extern const char pre_loaded_code[];
823 extern Preload pre_loaded[];
824
825 #if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
erts_sys_aligned_alloc(UWord alignment,UWord size)826 void *erts_sys_aligned_alloc(UWord alignment, UWord size)
827 {
828 #ifdef HAVE_POSIX_MEMALIGN
829 void *ptr = NULL;
830 int error;
831 ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */
832 error = posix_memalign(&ptr, (size_t) alignment, (size_t) size);
833 #if HAVE_ERTS_MSEG
834 if (error || !ptr) {
835 erts_mseg_clear_cache();
836 error = posix_memalign(&ptr, (size_t) alignment, (size_t) size);
837 }
838 #endif
839 if (error) {
840 errno = error;
841 return NULL;
842 }
843 if (!ptr)
844 errno = ENOMEM;
845 ASSERT(!ptr || (((UWord) ptr) & (alignment - 1)) == 0);
846 return ptr;
847 #else
848 # error "Missing erts_sys_aligned_alloc() implementation"
849 #endif
850 }
851
erts_sys_aligned_free(UWord alignment,void * ptr)852 void erts_sys_aligned_free(UWord alignment, void *ptr)
853 {
854 ASSERT(alignment && (alignment & (alignment-1)) == 0); /* power of 2 */
855 free(ptr);
856 }
857
erts_sys_aligned_realloc(UWord alignment,void * ptr,UWord size,UWord old_size)858 void *erts_sys_aligned_realloc(UWord alignment, void *ptr, UWord size, UWord old_size)
859 {
860 void *new_ptr = erts_sys_aligned_alloc(alignment, size);
861 if (new_ptr) {
862 UWord copy_size = old_size < size ? old_size : size;
863 sys_memcpy(new_ptr, ptr, (size_t) copy_size);
864 erts_sys_aligned_free(alignment, ptr);
865 }
866 return new_ptr;
867 }
868
869 #endif
870
erts_sys_alloc(ErtsAlcType_t t,void * x,Uint sz)871 void *erts_sys_alloc(ErtsAlcType_t t, void *x, Uint sz)
872 {
873 void *res = malloc((size_t) sz);
874 #if HAVE_ERTS_MSEG
875 if (!res) {
876 erts_mseg_clear_cache();
877 return malloc((size_t) sz);
878 }
879 #endif
880 return res;
881 }
882
erts_sys_realloc(ErtsAlcType_t t,void * x,void * p,Uint sz)883 void *erts_sys_realloc(ErtsAlcType_t t, void *x, void *p, Uint sz)
884 {
885 void *res = realloc(p, (size_t) sz);
886 #if HAVE_ERTS_MSEG
887 if (!res) {
888 erts_mseg_clear_cache();
889 return realloc(p, (size_t) sz);
890 }
891 #endif
892 return res;
893 }
894
erts_sys_free(ErtsAlcType_t t,void * x,void * p)895 void erts_sys_free(ErtsAlcType_t t, void *x, void *p)
896 {
897 free(p);
898 }
899
900 /* Return a pointer to a vector of names of preloaded modules */
901
902 Preload*
sys_preloaded(void)903 sys_preloaded(void)
904 {
905 return pre_loaded;
906 }
907
908 /* Return a pointer to preloaded code for module "module" */
909 unsigned char*
sys_preload_begin(Preload * p)910 sys_preload_begin(Preload* p)
911 {
912 return p->code;
913 }
914
915 /* Clean up if allocated */
sys_preload_end(Preload * p)916 void sys_preload_end(Preload* p)
917 {
918 /* Nothing */
919 }
920
921 /* Read a key from console, used by break.c
922 Here we assume that all schedulers are stopped so that erl_poll
923 does not interfere with the select below.
924 */
sys_get_key(int fd)925 int sys_get_key(int fd) {
926 int c, ret;
927 unsigned char rbuf[64] = {0};
928 fd_set fds;
929
930 fflush(stdout); /* Flush query ??? */
931
932 FD_ZERO(&fds);
933 FD_SET(fd,&fds);
934
935 ret = select(fd+1, &fds, NULL, NULL, NULL);
936
937 if (ret == 1) {
938 do {
939 c = read(fd,rbuf,64);
940 } while (c < 0 && errno == EAGAIN);
941 if (c <= 0)
942 return c;
943 }
944 return rbuf[0];
945 }
946
947
948 extern int erts_initialized;
949 void
erl_assert_error(const char * expr,const char * func,const char * file,int line)950 erl_assert_error(const char* expr, const char* func, const char* file, int line)
951 {
952 fflush(stdout);
953 fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n",
954 file, line, func, expr);
955 fflush(stderr);
956 abort();
957 }
958
959 #ifdef DEBUG
960
961 void
erl_debug(char * fmt,...)962 erl_debug(char* fmt, ...)
963 {
964 char sbuf[1024]; /* Temporary buffer. */
965 va_list va;
966
967 if (debug_log) {
968 va_start(va, fmt);
969 vsprintf(sbuf, fmt, va);
970 va_end(va);
971 fprintf(stderr, "%s", sbuf);
972 }
973 }
974
975 #endif /* DEBUG */
976
977 static erts_tid_t sig_dispatcher_tid;
978
979 static void
smp_sig_notify(int signum)980 smp_sig_notify(int signum)
981 {
982 int res;
983 do {
984 /* write() is async-signal safe (according to posix) */
985 res = write(sig_notify_fds[1], &signum, sizeof(int));
986 } while (res < 0 && errno == EINTR);
987 if (res != sizeof(int)) {
988 char msg[] =
989 "smp_sig_notify(): Failed to notify signal-dispatcher thread "
990 "about received signal";
991 erts_silence_warn_unused_result(write(2, msg, sizeof(msg)));
992 abort();
993 }
994 }
995
996 static void *
signal_dispatcher_thread_func(void * unused)997 signal_dispatcher_thread_func(void *unused)
998 {
999 #ifdef ERTS_ENABLE_LOCK_CHECK
1000 erts_lc_set_thread_name("signal_dispatcher");
1001 #endif
1002 while (1) {
1003 union {int signum; char buf[4];} sb;
1004 Eterm signal;
1005 int res, i = 0;
1006 /* Block on read() waiting for a signal notification to arrive... */
1007
1008 do {
1009 res = read(sig_notify_fds[0], (void *) &sb.buf[i], sizeof(int) - i);
1010 i += res > 0 ? res : 0;
1011 } while ((i < sizeof(int) && res >= 0) || (res < 0 && errno == EINTR));
1012
1013 if (res < 0) {
1014 erts_exit(ERTS_ABORT_EXIT,
1015 "signal-dispatcher thread got unexpected error: %s (%d)\n",
1016 erl_errno_id(errno),
1017 errno);
1018 }
1019 /*
1020 * NOTE 1: The signal dispatcher thread should not do work
1021 * that takes a substantial amount of time (except
1022 * perhaps in test and debug builds). It needs to
1023 * be responsive, i.e, it should only dispatch work
1024 * to other threads.
1025 *
1026 * NOTE 2: The signal dispatcher thread is not a blockable
1027 * thread (i.e., not a thread managed by the
1028 * erl_thr_progress module). This is intentional.
1029 * We want to be able to interrupt writing of a crash
1030 * dump by hitting C-c twice. Since it isn't a
1031 * blockable thread it is important that it doesn't
1032 * change the state of any data that a blocking thread
1033 * expects to have exclusive access to (unless the
1034 * signal dispatcher itself explicitly is blocking all
1035 * blockable threads).
1036 */
1037 switch (sb.signum) {
1038 case 0: continue;
1039 case SIGINT:
1040 break_requested();
1041 break;
1042 default:
1043 if ((signal = signum_to_signalterm(sb.signum)) == am_error) {
1044 erts_exit(ERTS_ABORT_EXIT,
1045 "signal-dispatcher thread received unknown "
1046 "signal notification: '%d'\n",
1047 sb.signum);
1048 }
1049 signal_notify_requested(signal);
1050 }
1051 ERTS_LC_ASSERT(!erts_thr_progress_is_blocking());
1052 }
1053 return NULL;
1054 }
1055
1056 static void
init_smp_sig_notify(void)1057 init_smp_sig_notify(void)
1058 {
1059 erts_thr_opts_t thr_opts = ERTS_THR_OPTS_DEFAULT_INITER;
1060 thr_opts.detached = 1;
1061 thr_opts.name = "sys_sig_dispatcher";
1062
1063 if (pipe(sig_notify_fds) < 0) {
1064 erts_exit(ERTS_ABORT_EXIT,
1065 "Failed to create signal-dispatcher pipe: %s (%d)\n",
1066 erl_errno_id(errno),
1067 errno);
1068 }
1069
1070 /* Start signal handler thread */
1071 erts_thr_create(&sig_dispatcher_tid,
1072 signal_dispatcher_thread_func,
1073 NULL,
1074 &thr_opts);
1075 }
1076
1077 static void
init_smp_sig_suspend(void)1078 init_smp_sig_suspend(void) {
1079 #ifdef ERTS_SYS_SUSPEND_SIGNAL
1080 if (pipe(sig_suspend_fds) < 0) {
1081 erts_exit(ERTS_ABORT_EXIT,
1082 "Failed to create sig_suspend pipe: %s (%d)\n",
1083 erl_errno_id(errno),
1084 errno);
1085 }
1086 #endif
1087 }
1088
1089 #ifdef __DARWIN__
1090
1091 int erts_darwin_main_thread_pipe[2];
1092 int erts_darwin_main_thread_result_pipe[2];
1093
initialize_darwin_main_thread_pipes(void)1094 static void initialize_darwin_main_thread_pipes(void)
1095 {
1096 if (pipe(erts_darwin_main_thread_pipe) < 0 ||
1097 pipe(erts_darwin_main_thread_result_pipe) < 0) {
1098 erts_exit(ERTS_ERROR_EXIT,"Fatal error initializing Darwin main thread stealing");
1099 }
1100 }
1101
1102 #endif
1103 void
erts_sys_main_thread(void)1104 erts_sys_main_thread(void)
1105 {
1106 #ifdef __DARWIN__
1107 initialize_darwin_main_thread_pipes();
1108 #else
1109 /* Become signal receiver thread... */
1110 #ifdef ERTS_ENABLE_LOCK_CHECK
1111 erts_lc_set_thread_name("signal_receiver");
1112 #endif
1113 #endif
1114 smp_sig_notify(0); /* Notify initialized */
1115
1116 /* Wait for a signal to arrive... */
1117
1118 #ifdef __DARWIN__
1119 while (1) {
1120 /*
1121 * The wx driver needs to be able to steal the main thread for Cocoa to
1122 * work properly.
1123 */
1124 fd_set readfds;
1125 int res;
1126
1127 FD_ZERO(&readfds);
1128 FD_SET(erts_darwin_main_thread_pipe[0], &readfds);
1129 res = select(erts_darwin_main_thread_pipe[0] + 1, &readfds, NULL, NULL, NULL);
1130 if (res > 0 && FD_ISSET(erts_darwin_main_thread_pipe[0],&readfds)) {
1131 void* (*func)(void*);
1132 void* arg;
1133 void *resp;
1134 res = read(erts_darwin_main_thread_pipe[0],&func,sizeof(void* (*)(void*)));
1135 if (res != sizeof(void* (*)(void*)))
1136 break;
1137 res = read(erts_darwin_main_thread_pipe[0],&arg,sizeof(void*));
1138 if (res != sizeof(void*))
1139 break;
1140 resp = (*func)(arg);
1141 write(erts_darwin_main_thread_result_pipe[1],&resp,sizeof(void *));
1142 }
1143
1144 if (res == -1 && errno != EINTR)
1145 break;
1146 }
1147 /* Something broke with the main thread pipe, so we ignore it for now.
1148 Most probably erts has closed this pipe and is about to exit. */
1149 #endif /* #ifdef __DARWIN__ */
1150
1151 while (1) {
1152 #ifdef DEBUG
1153 int res =
1154 #else
1155 (void)
1156 #endif
1157 select(0, NULL, NULL, NULL, NULL);
1158 ASSERT(res < 0);
1159 ASSERT(errno == EINTR);
1160 }
1161 }
1162
1163 void
erl_sys_args(int * argc,char ** argv)1164 erl_sys_args(int* argc, char** argv)
1165 {
1166 ASSERT(argc && argv);
1167
1168 max_files = erts_check_io_max_files();
1169
1170 init_smp_sig_notify();
1171 init_smp_sig_suspend();
1172
1173 erts_sys_env_init();
1174 }
1175