#include <sys/wait.h>
#include <alloca.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <mksh/dosys.h>
#include <mksh/macro.h>
#include <mksh/misc.h>
#include <sched.h>
#include <spawn.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <ulimit.h>
#include <unistd.h>
#include <stdlib.h>
#include <libintl.h>
static posix_spawnattr_t *nice_attr(posix_spawnattr_t *attr, int nice_prio);
static pid_t spawn_vp(char *name, char **argv, char **envp, posix_spawnattr_t *attr, Boolean ignore_error, pathpt vroot_path);
int
my_open(const char *path, int oflag, mode_t mode) {
int res = open(path, oflag, mode);
if (res < 0 && (errno == ESTALE || errno == EAGAIN)) {
res = open(path, oflag, mode);
}
return res;
}
void
redirect_io(char *stdout_file, char *stderr_file)
{
int i;
(void) closefrom(3);
if ((i = my_open(stdout_file,
O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC,
S_IREAD | S_IWRITE)) < 0) {
fatal_mksh(gettext("Couldn't open standard out temp file `%s': %s"),
stdout_file,
errmsg(errno));
} else {
if (dup2(i, 1) == -1) {
fatal_mksh("*** Error: dup2(3, 1) failed: %s",
errmsg(errno));
}
close(i);
}
if (stderr_file == NULL) {
if (dup2(1, 2) == -1) {
fatal_mksh("*** Error: dup2(1, 2) failed: %s",
errmsg(errno));
}
} else if ((i = my_open(stderr_file,
O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC,
S_IREAD | S_IWRITE)) < 0) {
fatal_mksh(gettext("Couldn't open standard error temp file `%s': %s"),
stderr_file,
errmsg(errno));
} else {
if (dup2(i, 2) == -1) {
fatal_mksh("*** Error: dup2(3, 2) failed: %s",
errmsg(errno));
}
close(i);
}
}
static posix_spawnattr_t *
nice_attr(posix_spawnattr_t *attr, int nice_prio)
{
struct sched_param param;
int policy, prio_min, prio_max;
if ((policy = sched_getscheduler(0)) == -1 ||
sched_getparam(0, ¶m) != 0) {
return (NULL);
}
prio_min = sched_get_priority_min(policy);
prio_max = sched_get_priority_max(policy);
if (prio_min == -1 || prio_max == -1)
return (NULL);
param.sched_priority -= nice_prio;
if (param.sched_priority < prio_min)
param.sched_priority = prio_min;
else if (param.sched_priority > prio_max)
param.sched_priority = prio_max;
if (posix_spawnattr_init(attr) != 0)
return (NULL);
if (posix_spawnattr_setflags(attr, POSIX_SPAWN_SETSCHEDPARAM) != 0 ||
posix_spawnattr_setschedparam(attr, ¶m) != 0) {
(void) posix_spawnattr_destroy(attr);
return (NULL);
}
return (attr);
}
pid_t
doshell(wchar_t *command, Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio)
{
char *argv[4];
int argv_index = 0;
posix_spawnattr_t attr;
posix_spawnattr_t *attrp = NULL;
int cmd_argv_index;
int err;
int length;
Name shell = getvar(shell_name);
char *shellname;
char *tmp_mbs_buffer;
if (IS_EQUAL(shell->string_mb, "")) {
shell = shell_name;
}
if ((shellname = strrchr(shell->string_mb, (int) slash_char)) == NULL) {
shellname = shell->string_mb;
} else {
shellname++;
}
argv[argv_index++] = shellname;
argv[argv_index++] = (char*)(ignore_error ? "-c" : "-ce");
if ((length = wcslen(command)) >= MAXPATHLEN) {
tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
(void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1);
cmd_argv_index = argv_index;
argv[argv_index++] = strdup(tmp_mbs_buffer);
retmem_mb(tmp_mbs_buffer);
} else {
WCSTOMBS(mbs_buffer, command);
cmd_argv_index = argv_index;
argv[argv_index++] = strdup(mbs_buffer);
}
argv[argv_index] = NULL;
(void) fflush(stdout);
if (nice_prio != 0)
attrp = nice_attr(&attr, nice_prio);
err = posix_spawn(&childPid, shell->string_mb, NULL, attrp,
argv, environ);
if (err != 0) {
(void) fprintf(stderr,
gettext("Could not load Shell from `%s': %s\n"),
shell->string_mb,
errmsg(err));
childPid = -1;
}
if (attrp != NULL)
(void) posix_spawnattr_destroy(attrp);
retmem_mb(argv[cmd_argv_index]);
return (childPid);
}
static pid_t
spawn_vp(char *name, char **argv, char **envp, posix_spawnattr_t *attr, Boolean ignore_error, pathpt vroot_path)
{
Name shell = getvar(shell_name);
char *shellname;
char *shargv[4];
Name tmp_shell;
pid_t pid;
if (IS_EQUAL(shell->string_mb, "")) {
shell = shell_name;
}
for (int i = 0; i < 5; i++) {
pid = spawn_vroot(name,
argv + 1,
envp,
attr,
vroot_path,
VROOT_DEFAULT);
if (pid >= 0)
return (pid);
switch (errno) {
case ENOEXEC:
case ENOENT:
shellname = strrchr(shell->string_mb, (int) slash_char);
if (shellname == NULL) {
shellname = shell->string_mb;
} else {
shellname++;
}
shargv[0] = shellname;
shargv[1] = (char*)(ignore_error ? "-c" : "-ce");
shargv[2] = argv[0];
shargv[3] = NULL;
tmp_shell = getvar(shell_name);
if (IS_EQUAL(tmp_shell->string_mb, "")) {
tmp_shell = shell_name;
}
return (spawn_vroot(tmp_shell->string_mb,
shargv,
envp,
attr,
vroot_path,
VROOT_DEFAULT));
case ETXTBSY:
(void) sleep((unsigned) i);
case EAGAIN:
break;
default:
return (-1);
}
}
return (-1);
}
pid_t
doexec(wchar_t *command, Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio)
{
int arg_count = 5;
char **argv;
posix_spawnattr_t attr;
posix_spawnattr_t *attrp = NULL;
int length;
char **p;
wchar_t *q;
wchar_t *t;
char *tmp_mbs_buffer;
for (t = command; *t != (int) nul_char; t++) {
if (iswspace(*t)) {
arg_count++;
}
}
argv = (char **)alloca(arg_count * (sizeof(char *)));
p = &argv[1];
if ((length = wcslen(command)) >= MAXPATHLEN) {
tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
(void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1);
argv[0] = strdup(tmp_mbs_buffer);
retmem_mb(tmp_mbs_buffer);
} else {
WCSTOMBS(mbs_buffer, command);
argv[0] = strdup(mbs_buffer);
}
for (t = command; *t;) {
if (p >= &argv[arg_count]) {
WCSTOMBS(mbs_buffer, command);
fatal_mksh(gettext("Command `%s' has more than %d arguments"),
mbs_buffer,
arg_count);
}
q = t;
while (!iswspace(*t) && (*t != (int) nul_char)) {
t++;
}
if (*t) {
for (*t++ = (int) nul_char; iswspace(*t); t++);
}
if ((length = wcslen(q)) >= MAXPATHLEN) {
tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
(void) wcstombs(tmp_mbs_buffer, q, (length * MB_LEN_MAX) + 1);
*p++ = strdup(tmp_mbs_buffer);
retmem_mb(tmp_mbs_buffer);
} else {
WCSTOMBS(mbs_buffer, q);
*p++ = strdup(mbs_buffer);
}
}
*p = NULL;
(void) fflush(stdout);
if (nice_prio != 0)
attrp = nice_attr(&attr, nice_prio);
childPid = spawn_vp(argv[1], argv, environ, attrp, ignore_error,
vroot_path);
if (childPid == -1) {
(void) fprintf(stderr,
gettext("Cannot load command `%s': %s\n"),
argv[1], errmsg(errno));
}
if (attrp != NULL)
(void) posix_spawnattr_destroy(attrp);
for (int i = 0; argv[i] != NULL; i++) {
retmem_mb(argv[i]);
}
return (childPid);
}
Boolean
await(Boolean ignore_error, Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs_p, int job_msg_id)
{
int status;
char *buffer;
int core_dumped;
int exit_status;
FILE *outfp;
pid_t pid;
struct stat stat_buff;
int termination_signal;
if (running_pid < 0) {
exit_status = 1;
termination_signal = 0;
core_dumped = 0;
} else {
while ((pid = wait(&status)) != running_pid) {
if (pid == -1) {
fatal_mksh(gettext("wait() failed: %s"),
errmsg(errno));
}
}
(void) fflush(stdout);
(void) fflush(stderr);
if (status == 0) {
#ifdef PRINT_EXIT_STATUS
warning_mksh("I'm in await(), and status is 0.");
#endif
return (succeeded);
}
#ifdef PRINT_EXIT_STATUS
warning_mksh("I'm in await(), and status is *NOT* 0.");
#endif
exit_status = WEXITSTATUS(status);
#ifdef PRINT_EXIT_STATUS
warning_mksh("I'm in await(), and exit_status is %d.",
exit_status);
#endif
termination_signal = WTERMSIG(status);
core_dumped = WCOREDUMP(status);
}
if (!silent_error) {
if (exit_status != 0) {
(void) fprintf(stdout,
gettext("*** Error code %d"),
exit_status);
} else {
(void) fprintf(stdout,
gettext("*** Signal %d"),
termination_signal);
if (core_dumped) {
(void) fprintf(stdout,
gettext(" - core dumped"));
}
}
if (ignore_error) {
(void) fprintf(stdout,
gettext(" (ignored)"));
}
(void) fprintf(stdout, "\n");
(void) fflush(stdout);
}
#ifdef PRINT_EXIT_STATUS
warning_mksh("I'm in await(), returning failed.");
#endif
return failed;
}
void
sh_command2string(String command, String destination)
{
FILE *fd;
int chr;
int status;
Boolean command_generated_output = false;
command->text.p = NULL;
WCSTOMBS(mbs_buffer, command->buffer.start);
if ((fd = popen(mbs_buffer, "r")) == NULL) {
WCSTOMBS(mbs_buffer, command->buffer.start);
fatal_mksh(gettext("Could not run command `%s' for :sh transformation"),
mbs_buffer);
}
while ((chr = getc(fd)) != EOF) {
if (chr == (int) newline_char) {
chr = (int) space_char;
}
command_generated_output = true;
append_char(chr, destination);
}
if (command_generated_output){
if ( *(destination->text.p-1) == (int) space_char) {
* (-- destination->text.p) = '\0';
}
} else {
*(destination->text.p) = '\0';
}
status = pclose(fd);
if (status != 0) {
WCSTOMBS(mbs_buffer, command->buffer.start);
fatal_mksh(gettext("The command `%s' returned status `%d'"),
mbs_buffer,
WEXITSTATUS(status));
}
}