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 * (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 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: */