/* -*- mode: C; mode: fold; -*- */ /* Copyright (C) 2009-2017,2018 John E. Davis This file is part of the S-Lang Library. The S-Lang Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The S-Lang Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_WAIT_H # include #endif #include #include #include /* WCONTINUED is not defined on Hurd, in waitflags.h. */ #ifndef WCONTINUED # define WCONTINUED 0 /* Debian patch used 8, but 0 is safer */ #endif SLANG_MODULE(fork); static int fork_intrinsic (void) { int ret = fork (); if (ret == -1) (void) SLerrno_set_errno (errno); return ret; } typedef struct { int pid; int exited; int exit_status; int signal; int coredump; int stopped; int continued; } Waitpid_Type; static SLang_CStruct_Field_Type Waitpid_Struct [] = { MAKE_CSTRUCT_FIELD(Waitpid_Type, pid, "pid", SLANG_INT_TYPE, 0), MAKE_CSTRUCT_FIELD(Waitpid_Type, exited, "exited", SLANG_INT_TYPE, 0), MAKE_CSTRUCT_FIELD(Waitpid_Type, exit_status, "exit_status", SLANG_INT_TYPE, 0), MAKE_CSTRUCT_FIELD(Waitpid_Type, signal, "signal", SLANG_INT_TYPE, 0), MAKE_CSTRUCT_FIELD(Waitpid_Type, coredump, "coredump", SLANG_INT_TYPE, 0), MAKE_CSTRUCT_FIELD(Waitpid_Type, stopped, "stopped", SLANG_INT_TYPE, 0), MAKE_CSTRUCT_FIELD(Waitpid_Type, continued, "continued", SLANG_INT_TYPE, 0), SLANG_END_CSTRUCT_TABLE }; static void waitpid_intrinsic (int *pid, int *options) { int status, ret; Waitpid_Type s; while (-1 == (ret = waitpid ((pid_t)*pid, &status, *options))) { if (errno == EINTR) { if (-1 != SLang_handle_interrupt ()) continue; } (void) SLerrno_set_errno (errno); (void) SLang_push_null (); return; } memset ((char *)&s, 0, sizeof(Waitpid_Type)); if (WIFEXITED(status)) { s.exited = 1; s.exit_status = WEXITSTATUS(status); } if (WIFSIGNALED(status)) { s.signal = WTERMSIG(status); #ifdef WCOREDUMP s.coredump = WCOREDUMP(status) != 0; #endif } if (WIFSTOPPED(status)) s.stopped = WSTOPSIG(status); #ifdef WIFCONTINUED s.continued = WIFCONTINUED(status); #endif s.pid = ret; (void) SLang_push_cstruct ((VOID_STAR)&s, Waitpid_Struct); } static char **pop_argv (SLang_Array_Type **atp) { SLang_Array_Type *at; char **argv; SLuindex_Type i, num, argc; char **strp; *atp = NULL; if (-1 == SLang_pop_array_of_type (&at, SLANG_STRING_TYPE)) return NULL; num = at->num_elements; if (NULL == (argv = (char **)SLmalloc ((num+1)*sizeof(char *)))) { SLang_free_array (at); return NULL; } strp = (char **)at->data; argc = 0; for (i = 0; i < num; i++) { if (strp[i] != NULL) argv[argc++] = strp[i]; } argv[argc] = NULL; *atp = at; return argv; } #define CALL_EXECV 1 #define CALL_EXECVP 2 #define CALL_EXECVE 3 static int call_what (int what, char *path, char **argv, char **envp) { while (1) { int ret; switch (what) { case CALL_EXECV: ret = execv (path, argv); break; case CALL_EXECVP: ret = execvp (path, argv); break; case CALL_EXECVE: ret = execve (path, argv, envp); break; } if (ret == 0) return 0; /* should never happen */ SLerrno_set_errno (errno); if (errno == EINTR) { if (-1 != SLang_handle_interrupt ()) continue; } break; } return -1; } static int exec_what (int what, int has_envp) { SLang_Array_Type *at_argv = NULL; SLang_Array_Type *at_envp = NULL; char **argv = NULL, **envp = NULL; char *path = NULL; int status = -1; if (has_envp) { if (NULL == (envp = pop_argv (&at_envp))) goto free_and_return; } if (NULL == (argv = pop_argv (&at_argv))) goto free_and_return; if (-1 == SLang_pop_slstring (&path)) goto free_and_return; status = call_what (what, path, argv, envp); free_and_return: if (path != NULL) SLang_free_slstring (path); if (argv != NULL) SLfree ((char *)argv); if (at_argv != NULL) SLang_free_array (at_argv); if (envp != NULL) SLfree ((char *)envp); if (at_envp != NULL) SLang_free_array (at_envp); return status; } static int execv_intrin (void) { if (SLang_Num_Function_Args != 2) SLang_verror (SL_Usage_Error, "Usage: ret = execv(path, argv[]);"); return exec_what (CALL_EXECV, 0); } static int execvp_intrin (void) { if (SLang_Num_Function_Args != 2) SLang_verror (SL_Usage_Error, "Usage: ret = execvp(path, argv[]);"); return exec_what (CALL_EXECVP, 0); } static int execve_intrin (void) { if (SLang_Num_Function_Args != 3) SLang_verror (SL_Usage_Error, "Usage: ret = execve(path, argv[], env[]);"); return exec_what (CALL_EXECVE, 1); } static void _exit_intrin (int *s) { (void) fflush (stdout); (void) fflush (stderr); _exit (*s); } static void pipe_intrin (void) { int fds[2]; SLFile_FD_Type *f0; SLFile_FD_Type *f1; while (-1 == pipe (fds)) { if (errno == EINTR) { if (-1 != SLang_handle_interrupt ()) continue; } SLerrno_set_errno (errno); SLang_verror (SL_OS_Error, "pipe failed: %s", SLerrno_strerror(errno)); return; } f0 = SLfile_create_fd ("*pipe*", fds[0]); f1 = SLfile_create_fd ("*pipe*", fds[1]); if ((NULL != f0) && (NULL != f1)) { /* Ignore errors and allow the free_fd routines to clean up */ (void) SLfile_push_fd (f0); (void) SLfile_push_fd (f1); } SLfile_free_fd (f1); SLfile_free_fd (f0); } static SLang_IConstant_Type Module_IConstants [] = { MAKE_ICONSTANT("WNOHANG", WNOHANG), MAKE_ICONSTANT("WUNTRACED", WUNTRACED), MAKE_ICONSTANT("WCONTINUED", WCONTINUED), SLANG_END_ICONST_TABLE }; static SLang_Intrin_Fun_Type Module_Intrinsics [] = { MAKE_INTRINSIC_0("fork", fork_intrinsic, SLANG_INT_TYPE), MAKE_INTRINSIC_2("waitpid", waitpid_intrinsic, SLANG_VOID_TYPE, SLANG_INT_TYPE, SLANG_INT_TYPE), MAKE_INTRINSIC_0("execv", execv_intrin, SLANG_INT_TYPE), MAKE_INTRINSIC_0("execvp", execvp_intrin, SLANG_INT_TYPE), MAKE_INTRINSIC_0("execve", execve_intrin, SLANG_INT_TYPE), MAKE_INTRINSIC_0("pipe", pipe_intrin, SLANG_VOID_TYPE), MAKE_INTRINSIC_1("_exit", _exit_intrin, SLANG_VOID_TYPE, SLANG_INT_TYPE), SLANG_END_INTRIN_FUN_TABLE }; int init_fork_module_ns (char *ns_name) { SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name); if (ns == NULL) return -1; if ((-1 == SLns_add_intrin_fun_table (ns, Module_Intrinsics, NULL)) || (-1 == SLns_add_iconstant_table (ns, Module_IConstants, NULL))) return -1; return 0; } /* This function is optional */ void deinit_fork_module (void) { }