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