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