static char rcsid[] = "@(#)$Id: syscall.c,v 1.19 2006/04/09 07:37:06 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.19 $ $State: Exp $
*
* Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI>
* (was hurtta+elm@ozone.FMI.FI)
******************************************************************************
*
* Some code based on ../src/syscall.c. It have following copyright:
*
* The Elm Mail System
*
* Copyright (c) 1988-1992 USENET Community Trust
* Copyright (c) 1986,1987 Dave Taylor
*****************************************************************************/
#include "headers.h"
#include "s_elm.h"
DEBUG_VAR(Debug,__FILE__,"system");
#include <errno.h>
static void no_SR_print_status P_((struct run_state *rs,int sig,
int exit_code));
static void no_SR_print_status(rs,sig,exit_code)
struct run_state *rs;
int sig;
int exit_code;
{
DPRINT(Debug,5,(&Debug, "No print_status_hook ...\n"));
}
static int no_SR_RawState P_((void));
static int no_SR_RawState()
{
DPRINT(Debug,5,(&Debug, "No RawState_hook ...\n"));
return 0;
}
static void no_SR_Raw P_((int state));
static void no_SR_Raw(state)
int state;
{
DPRINT(Debug,5,(&Debug, "No Raw_hook ...\n"));
}
static void no_SR_tty_init P_((void));
static void no_SR_tty_init()
{
DPRINT(Debug,5,(&Debug, "No run_tty_init_hook ...\n"));
}
static void no_SR_ClearScreen P_((void));
static void no_SR_ClearScreen()
{
DPRINT(Debug,5,(&Debug, "No ClearScreen_hook ...\n"));
}
static char *us2s P_((unsigned char *str));
static char *us2s(str)
unsigned char *str;
{
return (char *)str;
}
static void no_SR_PutLineS P_((struct string *S));
static void no_SR_PutLineS(S)
struct string *S;
{
struct string * R1 = convert_string(display_charset,S,1);
char * store1;
DPRINT(Debug,5,(&Debug, "No PutLineS_hook ...\n"));
store1 = us2s(stream_from_string(R1,0,NULL));
fprintf(stderr,"%s",store1);
free(store1);
free_string(&R1);
}
static SR_print_status * print_status_hook = no_SR_print_status;
static SR_RawState * RawState_hook = no_SR_RawState;
static SR_Raw * Raw_hook = no_SR_Raw;
static SR_tty_init * run_tty_init_hook = no_SR_tty_init;
static SR_ClearScreen * ClearScreen_hook = no_SR_ClearScreen;
static SR_PutLineS * PutLineS_hook = no_SR_PutLineS;
static SR_print_status * print_status_cooked_hook = no_SR_print_status;
void set_start_run_hooks(print_status_func,raw_query_func,raw_set_func,
run_tty_init,clear_screen_func,put_line_function,
print_status_cooked_func)
SR_print_status * print_status_func;
SR_RawState * raw_query_func;
SR_Raw * raw_set_func;
SR_tty_init * run_tty_init;
SR_ClearScreen * clear_screen_func;
SR_PutLineS * put_line_function;
SR_print_status * print_status_cooked_func;
{
print_status_hook = print_status_func;
RawState_hook = raw_query_func;
Raw_hook = raw_set_func;
run_tty_init_hook = run_tty_init;
ClearScreen_hook = clear_screen_func;
PutLineS_hook = put_line_function;
print_status_cooked_hook = print_status_cooked_func;
}
int set_child_signals(options)
int options;
{
/*
* Program to exec may or may not be able to handle
* interrupt, quit, hangup and stop signals.
*/
if (options&SY_ENAB_SIGINT)
options |= SY_ENAB_SIGHUP;
(void) signal(SIGHUP, (options&SY_ENAB_SIGHUP) ? SIG_DFL : SIG_IGN);
(void) signal(SIGINT, (options&SY_ENAB_SIGINT) ? SIG_DFL : SIG_IGN);
(void) signal(SIGQUIT, (options&SY_ENAB_SIGINT) ? SIG_DFL : SIG_IGN);
#ifdef SIGTSTP
(void) signal(SIGTSTP, (options&SY_ENAB_SIGHUP) ? SIG_DFL : SIG_IGN);
(void) signal(SIGCONT, (options&SY_ENAB_SIGHUP) ? SIG_DFL : SIG_IGN);
#endif
return 0;
}
int set_child_env(options)
int options;
{
/* Optionally override the MM_CHARSET environment variable. */
if (options&SY_ENV_METAMAIL) {
char * A = display_charset->MIME_name;
char mm[] = "MM_CHARSET=";
char mm1[] = "MAILCAPS=";
if (A) {
int size = sizeof(mm) + strlen(A);
/* \0 character is included in size returned by sizeof */
char *p = malloc(size);
if (p) {
elm_sfprintf(p, size,
FRM("%s%s"), mm , A );
if (0 == putenv(p)) {
DPRINT(Debug,5,(&Debug, "Child: added %s\n",p));
}
}
}
A = give_dt_path_as_str(&metamail_mailcaps,"metamail-mailcaps");
if (A) {
int size = sizeof(mm1) + strlen(A);
/* \0 character is included in size returned by sizeof */
char *p = malloc(size);
if (p) {
elm_sfprintf(p, size,
FRM("%s%s"), mm1 , A );
if (0 == putenv(p)) {
DPRINT(Debug,5,(&Debug, "Child: added %s\n",p));
}
}
}
/* Used 'h' command */
if (!elm_filter) {
static char mm2[] = "KEYHEADS=*";
static char mm3[] = "KEYIGNHEADS=";
if (0 == putenv(mm2)) {
DPRINT(Debug,5,(&Debug, "Child: added %s\n",mm2));
}
if (0 == putenv(mm3)) {
DPRINT(Debug,5,(&Debug, "Child: added %s\n",mm3));
}
}
}
/* Optionally override the SHELL environment variable. */
if (options&SY_ENV_SHELL) {
static char sheq[] = "SHELL=";
char * sh = "/bin/sh";
int size;
char *p;
if (0 != (options & SY_USER_SHELL)) {
char * s = give_dt_estr_as_str(&shell_e,"shell");
if (s)
sh = s;
}
size = sizeof(sheq) + strlen(sh);
p = malloc(size);
if (p) {
elm_sfprintf(p, size,
FRM("%s%s"), sheq, sh);
if (0 == putenv(p)) {
DPRINT(Debug,5,(&Debug, "Child: added %s\n",p));
}
}
}
return 0;
}
#ifdef BACKGROUD_PROCESSES /* We assume POSIX in here */
VOLATILE int handle_sigchld = 0; /* got SIGCHLD */
static struct process_list {
union any_fd fd;
char * message;
struct run_state state_information;
end_handler *handler;
struct process_list * next;
} * my_processes = NULL;
static void got_sigchld P_((int sig));
static void got_sigchld (sig)
int sig;
{
SIGDPRINT(Debug,2,(&Debug,
"got_sigchld: Setting handle_sigchld\n"));
handle_sigchld = 1;
}
void sigchld_handler() {
DPRINT(Debug,2,(&Debug,
"sigchld_handler --> ENTER (not on signal)\n"));
do {
struct process_list *tmp = my_processes, *prev = NULL;
handle_sigchld = 0;
while (tmp) {
struct process_list * this = tmp;
int exit_code;
int ret = run_already_done(&(tmp->state_information),&exit_code);
if (ret != 0) {
tmp->handler(tmp->fd,tmp->message,&(tmp->state_information),
ret,exit_code);
if (prev)
prev -> next = tmp -> next;
else
my_processes = tmp -> next;
DPRINT(Debug,2,(&Debug,
"sigchld_handler: deleting %d from list: %s\n",
tmp->state_information.pid, tmp->message));
free(tmp->message);
tmp = tmp -> next;
free(this);
continue;
}
prev = tmp;
tmp = tmp -> next;
}
} while (handle_sigchld);
DPRINT(Debug,2,(&Debug,
"sigchld_handler --> LEAVE\n"));
}
void init_backgroud_handling () {
/* We use POSIX sigaction here,
* so that we not depend different semantic
* between SYSV and BSD signal(SIGCHLD, ...)
*/
struct sigaction new_child;
new_child.sa_flags = 0;
#ifdef SA_INTERRUPT
new_child.sa_flags |= SA_INTERRUPT; /* SunOS? */
#endif
new_child.sa_handler = got_sigchld;
sigemptyset(& (new_child.sa_mask));
if (-1 == sigaction(SIGCHLD,&new_child,NULL)) {
int err = errno;
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSigaction,"sigaction: %s"),
error_description(err));
exit (1);
}
}
int maybe_background (rs,exit_code,fd,title,func)
struct run_state *rs;
int *exit_code;
union any_fd fd;
char *title;
end_handler *func;
{
int ret;
struct process_list *listptr;
if (!title)
title = "NO NAME";
if (rs->raw == ON && RawState_hook() == OFF) {
ret = wait_end (rs,exit_code);
if (ret != 0)
return ret;
}
listptr = safe_malloc(sizeof (struct process_list));
rs -> listptr = listptr;
listptr -> fd = fd;
listptr -> message = safe_strdup(title);
listptr -> state_information = *rs;
listptr -> handler = func;
listptr -> next = my_processes;
my_processes = listptr;
return 0;
}
#endif
static void raw_exit P_((struct run_state *rs));
static void raw_exit(rs)
struct run_state *rs;
{
if (rs->raw == ON && RawState_hook() == OFF) {
DPRINT(Debug,4,(&Debug,
"raw_exit: setting RAW on\n"));
Raw_hook(ON|NO_TITE);
} else {
DPRINT(Debug,4,(&Debug,
"raw_exit: no state change\n"));
}
}
int run_already_done (rs,exit_code)
struct run_state *rs;
int *exit_code;
{
S__ status;
#ifdef HASWAITPID
int w;
/* waitpid is on POSIX */
*exit_code = -1;
w = waitpid(rs->pid,&status,WNOHANG);
if (w == 0) {
DPRINT(Debug,2,(&Debug,
"run_already_done=%d (w=%d)\n",0,w));
return 0;
}
if (w == -1) {
rs->save_errno = errno;
DPRINT(Debug,2,(&Debug,
"run_already_done: errno=%d\n", rs->save_errno));
if (rs->save_errno == EINTR) {
DPRINT(Debug,2,(&Debug,
"run_already_done=%d (w=%d) [EINTR]\n",0,w));
return 0;
}
}
raw_exit(rs);
if(rs->pfd) {
DPRINT(Debug,4,(&Debug,
"run_already_done: closing rs->pfd=%p\n",
rs->pfd));
fclose(rs->pfd);
rs->pfd = NULL;
}
if (w == rs->pid) {
int sig = convert_status(status,exit_code);
DPRINT(Debug,2,(&Debug,
"run_already_done: exit_code=%d, sig=%d\n",
*exit_code,sig));
print_status_hook(rs,sig,*exit_code) ;
if (sig) {
DPRINT(Debug,2,(&Debug,
"run_already_done=%d\n",-sig));
return -sig;
}
}
DPRINT(Debug,2,(&Debug,
"run_already_done=%d\n",w != -1));
return w != -1;
#else
DPRINT(Debug,2,(&Debug,
"run_already_done=%d (no HASWAITPID)\n",0));
return 0;
#endif
}
int wait_end (rs,exit_code)
struct run_state *rs;
int *exit_code;
{
int w;
S__ status;
*exit_code = -1;
while ((w = my_wait(rs->pid,&status)) != rs->pid)
if (w == -1 && errno != EINTR)
break;
if (w == -1) {
rs->save_errno = errno;
DPRINT(Debug,2,(&Debug,
"wait_end: errno=%d\n", rs->save_errno));
}
raw_exit(rs);
if(rs->pfd) {
DPRINT(Debug,4,(&Debug,
"wait_end: closing rs->pfd=%p\n",
rs->pfd));
fclose(rs->pfd);
rs->pfd = NULL;
}
if (w == rs->pid) {
int sig = convert_status(status,exit_code);
DPRINT(Debug,2,(&Debug,
"wait_end: exit_code=%d, sig=%d\n",
*exit_code,sig));
print_status_hook(rs,sig,*exit_code);
if (sig) {
DPRINT(Debug,2,(&Debug,
"wait_end=%d\n",-sig));
return -sig;
}
}
DPRINT(Debug,2,(&Debug,
"wait_end=%d\n",w != -1));
return w != -1;
}
CONST char ** join_argv(argv1,argv2)
char * argv1[];
char * argv2[];
{
int count1;
int count2;
CONST char ** res;
int i;
for (count1 = 0; argv1[count1]; count1++);
for (count2 = 0; argv2[count2]; count2++);
res = safe_malloc((count1 + count2 + 1)* sizeof (char *));
for (i = 0; i < count1; i++)
res[i] = argv1[i];
for (i = 0; i < count2; i++)
res[i + count1] = argv2[i];
res[count1 + count2] = NULL;
return res;
}
void sr_call_ClearScreen()
{
ClearScreen_hook();
}
void sr_call_Raw(x)
int x;
{
Raw_hook(x);
}
int sr_call_RawState()
{
int x = RawState_hook();
return x;
}
/* copied from curses.c */
void sr_call_Write_to_screen (
#if ANSI_C
CONST char *format, CONST char *msg, ...
#else
format, msg, va_alist
#endif
)
#if !ANSI_C
CONST char *format;
CONST char *msg;
va_dcl
#endif
{
va_list vl;
struct string *text;
Va_start(vl, msg); /* defined in defs.h */
/** This routine writes to the screen at the current location.
when done, it increments lines & columns accordingly by
looking for "\n" sequences... **/
text = elm_smessage(0,format,msg,vl);
PutLineS_hook(text);
free_string(&text);
va_end(vl);
}
void call_print_status_cooked(rs,sig,exit_code)
struct run_state *rs;
int sig;
int exit_code;
{
print_status_cooked_hook(rs,sig,exit_code);
}
int start_run(rs, options, argv, infd, outfd)
struct run_state *rs;
int options;
CONST char * argv[];
int infd, outfd;
{
int count;
int pfd[2], opipe[2];
static int fd = -1;
int notty = (options & SY_NOTTY) != 0;
int outpipe = (options & SY_RUN_STATE_OPIPE) != 0;
int errpipe = (options & SY_RUN_STATE_EPIPE) != 0;
DPRINT(Debug,2,(&Debug,
"start_run: [0] %s\n", argv[0]));
for (count = 1; argv[count]; count++) {
DPRINT(Debug,2,(&Debug,
" [%d] %s\n",
count,argv[count]));
}
DPRINT(Debug,2,(&Debug,
" infd=%d, outfd=%d\n",infd,outfd));
if (fd == -1)
fd = open("/dev/null",O_RDWR);
DPRINT(Debug,2,(&Debug,
" fd=%d \n", fd));
/* flush any pending output */
fflush(stdout);
rs->save_errno = 0;
rs->raw = RawState_hook();
rs->options = options;
#ifdef BACKGROUD_PROCESSES
rs->listptr = NULL;
#endif
rs->pfd = NULL;
if (!notty) {
run_tty_init_hook();
if (rs->raw == ON) {
if (options & SY_CLRWAIT) {
ClearScreen_hook();
}
DPRINT(Debug,4,(&Debug,
"start_run: setting RAW off\n"));
Raw_hook(OFF|NO_TITE);
}
if (options & SY_CLRWAIT) {
printf("Executing: %s ...\n\n",argv[0]);
}
}
if (outpipe || errpipe) {
if (-1 == pipe(opipe)) {
rs->save_errno = errno;
raw_exit(rs);
return 0;
}
}
if (pipe(pfd) == -1) {
rs->save_errno = errno;
if (outpipe || errpipe) {
close(opipe[0]);
close(opipe[1]);
}
raw_exit(rs);
return 0;
}
#ifdef FD_CLOEXEC
fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
#else
fcntl(pfd[0], F_SETFD, 1);
fcntl(pfd[1], F_SETFD, 1);
#endif
rs->pid = fork();
if (rs->pid == -1) {
rs->save_errno = errno;
if (outpipe || errpipe) {
close(opipe[0]);
close(opipe[1]);
}
close(pfd[0]);
close(pfd[1]);
raw_exit(rs);
return 0;
}
else if (rs->pid == 0) {
close(pfd[0]);
if (outpipe || errpipe)
close(opipe[0]);
/*
* Set group and user back to their original values.
* Note that group must be set first.
*/
if (-1 == setgid(groupid)) {
int err = errno;
fprintf(stderr,"start_run: setgid(%d) FAILED: %s\n",
groupid,error_description(err));
fflush(stderr);
write(pfd[1],(void *)&err,sizeof err); _exit(127);
}
if (-1 == setuid(userid)) {
int err = errno;
fprintf(stderr,"start_run: setruid(%d) FAILED: %s\n",
userid,error_description(err));
fflush(stderr);
write(pfd[1],(void *)&err,sizeof err); _exit(127);
}
set_child_signals(options);
set_child_env(options);
if (options & SY_RUN_STATE_ENV) {
int i;
for (i = 0; rs->ext_env[i]; i++) {
if (0 == putenv(rs->ext_env[i])) {
DPRINT(Debug,5,(&Debug,
"Child: added %s\n",
rs->ext_env[i]));
}
}
}
if (options & SY_RUN_STATE_INIT) {
rs->ext_init(rs);
}
if (outpipe || errpipe) {
if (outpipe) {
if (opipe[1] != 1) {
if (-1 == dup2(opipe[1],1)) {
write(pfd[1],(void *)&errno,sizeof errno); _exit(127);
}
}
}
if (errpipe) {
if (opipe[1] != 2) {
if (-1 == dup2(opipe[1],2)) {
write(pfd[1],(void *)&errno,sizeof errno); _exit(127);
}
}
}
if (opipe[1] != 1 && opipe[1] != 2) {
close(opipe[1]);
}
}
if (notty) {
if ((infd != 0 && -1 == dup2(fd,0)) ||
(!outpipe && outfd != 1 && -1 == dup2(fd,1)) ||
(!errpipe && outfd != 2 && -1 == dup2(fd,2))) {
write(pfd[1],(void *)&errno,sizeof errno); _exit(127);
}
#ifdef SIGTTSTP
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
#endif
}
if (infd >= 0) {
if (infd != 0 && -1 == dup2(infd,0)) {
write(pfd[1],(void *)&errno,sizeof errno); _exit(127);
}
}
if (outfd >= 0) {
if (outfd != 1 && -1 == dup2(outfd,1)) {
write(pfd[1],(void *)&errno,sizeof errno); _exit(127);
}
}
/* ... lose const */
execvp(argv[0],(char **)argv);
write(pfd[1],(void *)&errno,sizeof errno); _exit(127);
}
else {
int code, rd;
close(pfd[1]);
DPRINT(Debug,4,(&Debug,
"start_run: child pid=%d\n", rs->pid));
rd = read(pfd[0],(void *)&code, sizeof code);
close(pfd[0]);
if (outpipe || errpipe)
close(opipe[1]);
if (rd > 0) {
int exitcode;
if (outpipe || errpipe)
close(opipe[0]);
wait_end(rs,&exitcode);
if (rd == sizeof code)
rs->save_errno = code;
else
rs->save_errno = 0;
return 0;
}
if (outpipe || errpipe) {
rs->pfd = fdopen(opipe[0],"r");
if (!rs->pfd) {
DPRINT(Debug,4,(&Debug,
"start_run: Failed open rs->pfd (fd=%d)\n",
opipe[0]));
close(opipe[0]);
} else {
DPRINT(Debug,4,(&Debug,
"start_run: opened rs->pfd=%p (fd=%d)\n",
rs->pfd,opipe[0]));
}
}
return 1;
}
raw_exit(rs);
return 0;
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1