315 строки
7.2 KiB
C
315 строки
7.2 KiB
C
/* -*- 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 <stdio.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_WAIT_H
|
|
# include <sys/wait.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
#include <slang.h>
|
|
|
|
/* 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)
|
|
{
|
|
}
|