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