You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
9.5 KiB
C
479 lines
9.5 KiB
C
/*
|
|
* binkleyforce -- unix FTN mailer project
|
|
*
|
|
* Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
|
|
*
|
|
* This program 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.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "confread.h"
|
|
#include "logger.h"
|
|
|
|
struct {
|
|
char *name;
|
|
int value;
|
|
} exec_options_names[] = {
|
|
{ "nowait", EXEC_OPT_NOWAIT },
|
|
{ "setsid", EXEC_OPT_SETSID },
|
|
{ "logout", EXEC_OPT_LOGOUT },
|
|
{ "useshell", EXEC_OPT_USESHELL },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
int exec_file_exist(const char *command)
|
|
{
|
|
const char *p;
|
|
|
|
ASSERT(command);
|
|
|
|
if( *command == '[' )
|
|
{
|
|
p = strchr(command + 1, ']');
|
|
if( !p )
|
|
return -1;
|
|
++p;
|
|
}
|
|
else
|
|
p = command;
|
|
|
|
if( !access(p, X_OK) )
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int exec_options_parse(char *str)
|
|
{
|
|
int yield = 0;
|
|
char *p;
|
|
char *n;
|
|
int i;
|
|
|
|
for( p = string_token(str, &n, ", \t", 0); p;
|
|
p = string_token(NULL, &n, ", \t", 0) )
|
|
{
|
|
for( i = 0; exec_options_names[i].name; i++ )
|
|
if( !strcasecmp(p, exec_options_names[i].name) )
|
|
{
|
|
yield |= exec_options_names[i].value;
|
|
break;
|
|
}
|
|
if( !exec_options_names[i].name )
|
|
log("unknown exec option '%s'", p);
|
|
}
|
|
|
|
return yield;
|
|
}
|
|
|
|
void exec_options_init(s_exec_options *eopt)
|
|
{
|
|
int i = 0;
|
|
|
|
memset(eopt, '\0', sizeof(s_exec_options));
|
|
eopt->umask = EXEC_DEFAULT_UMASK;
|
|
eopt->envp[0] = NULL;
|
|
|
|
while(mainenv[i]) {
|
|
DEB((D_FREE,"EXEC: Added ENV variable: %s\n", mainenv[i]));
|
|
eopt->envp[i] = xmalloc(strlen(mainenv[i]) + 2);
|
|
strcpy(eopt->envp[i],mainenv[i]);
|
|
eopt->envp[++i] = NULL;
|
|
}
|
|
}
|
|
|
|
void exec_options_deinit(s_exec_options *eopt)
|
|
{
|
|
int i;
|
|
|
|
if( eopt->command )
|
|
free(eopt->command);
|
|
|
|
for( i = 0; eopt->envp[i]; i++ )
|
|
free(eopt->envp[i]);
|
|
|
|
memset(eopt, '\0', sizeof(s_exec_options));
|
|
}
|
|
|
|
void exec_env_add(s_exec_options *eopt, const char *name, const char *value)
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; eopt->envp[i]; i++ ); /* Empty Loop */
|
|
|
|
if( i < EXEC_MAX_NUM_ENVS )
|
|
{
|
|
eopt->envp[i] = xmalloc(strlen(name) + strlen(value) + 2);
|
|
sprintf(eopt->envp[i], "%s=%s", name, value);
|
|
eopt->envp[i+1] = NULL;
|
|
}
|
|
}
|
|
|
|
void exec_options_set_command(s_exec_options *eopt, const char *command)
|
|
{
|
|
char *copy, *p;
|
|
|
|
if( *command == '[' )
|
|
{
|
|
copy = xstrcpy(command);
|
|
p = strchr(copy, ']');
|
|
if( !p )
|
|
eopt->command = copy;
|
|
else
|
|
{
|
|
*p++ = '\0';
|
|
eopt->command = xstrcpy(string_trimleft(p));
|
|
eopt->options = exec_options_parse(copy+1);
|
|
free(copy);
|
|
}
|
|
}
|
|
else
|
|
eopt->command = xstrcpy(command);
|
|
}
|
|
|
|
int exec_redirect_descriptor(int desc, const char *fname, int flags)
|
|
{
|
|
int fd;
|
|
|
|
(void)close(desc);
|
|
|
|
if( (fd = open(fname, flags, S_IRUSR|S_IWUSR)) == -1 )
|
|
{
|
|
logerr("exec error: cannot open \"%s\"", fname);
|
|
return -1;
|
|
}
|
|
|
|
if( fd != desc )
|
|
{
|
|
log("exec error: cannot open \"%s\" <--> %d (got %d)",
|
|
fname, desc, fd);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int exec_command(s_exec_options *eopt)
|
|
{
|
|
pid_t pid = 0;
|
|
int status = 0;
|
|
time_t starttime = 0;
|
|
time_t timer = 0;
|
|
char *tempfile = NULL;
|
|
struct sigaction new_chld;
|
|
struct sigaction old_chld;
|
|
sigset_t new_mask;
|
|
sigset_t old_mask;
|
|
|
|
if( eopt->command == NULL )
|
|
return -1;
|
|
|
|
if( eopt->options & EXEC_OPT_LOGOUT )
|
|
{
|
|
tempfile = file_gettmp();
|
|
if( !tempfile )
|
|
log("cannot generate temporary file name");
|
|
}
|
|
|
|
/* Block SIGCHLD */
|
|
sigemptyset(&new_mask);
|
|
sigaddset(&new_mask, SIGCHLD);
|
|
sigprocmask(SIG_BLOCK, &new_mask, NULL);
|
|
|
|
/* Set the default SIGCHLD handler */
|
|
new_chld.sa_handler = SIG_DFL;
|
|
sigaction(SIGCHLD, &new_chld, &old_chld);
|
|
|
|
if( (pid = fork()) == -1 )
|
|
{
|
|
logerr("exec error: cannot fork()");
|
|
return -1;
|
|
}
|
|
|
|
if( !pid )
|
|
{
|
|
if( exec_redirect_descriptor(0, "/dev/null", O_RDONLY) == -1 )
|
|
exit(128);
|
|
|
|
if( tempfile )
|
|
{
|
|
if( exec_redirect_descriptor(1, tempfile, O_WRONLY|O_CREAT|O_TRUNC) == -1
|
|
|| exec_redirect_descriptor(2, tempfile, O_WRONLY) == -1 )
|
|
exit(128);
|
|
}
|
|
else
|
|
{
|
|
if( exec_redirect_descriptor(1, "/dev/null", O_WRONLY) == -1
|
|
|| exec_redirect_descriptor(2, "/dev/null", O_WRONLY) == -1 )
|
|
exit(128);
|
|
}
|
|
|
|
if( eopt->options & EXEC_OPT_SETSID )
|
|
setsid();
|
|
|
|
if( eopt->options & EXEC_OPT_USESHELL )
|
|
execle("/bin/sh", "sh", "-c", eopt->command, NULL, eopt->envp);
|
|
else
|
|
{
|
|
char *argv[EXEC_MAX_NUM_ARGS+1];
|
|
int argc = string_parse_regular(argv, EXEC_MAX_NUM_ARGS,
|
|
eopt->command);
|
|
if( argc > 0 )
|
|
{
|
|
argv[argc] = NULL;
|
|
execve(argv[0], argv, eopt->envp);
|
|
}
|
|
/* eopt->command is corrupted now */
|
|
}
|
|
|
|
/* We get here only in case of errors */
|
|
logerr("cannot execute \"%s\"", eopt->command);
|
|
|
|
exit(128);
|
|
}
|
|
|
|
/*
|
|
* We are inside parent process, do:
|
|
*
|
|
* 1) if OUTMODE_LOGPIPE is used than log child process ouput
|
|
* 2) wait for child process to exit
|
|
*/
|
|
|
|
log("running \"%s\", PID %d",
|
|
string_printable(eopt->command), (int)pid);
|
|
|
|
if( !(eopt->options & EXEC_OPT_NOWAIT) )
|
|
{
|
|
starttime = time(NULL); /* Set process start time */
|
|
|
|
if( waitpid(pid, &status, 0) > 0 )
|
|
{
|
|
eopt->runtime = time(NULL) - starttime;
|
|
|
|
if( eopt->runtime < 0 )
|
|
eopt->runtime = 0;
|
|
|
|
/*
|
|
* Write process's output to the log file
|
|
*/
|
|
if( tempfile )
|
|
{
|
|
char buf[256];
|
|
FILE *fp = file_open(tempfile, "r");
|
|
|
|
if( !fp )
|
|
logerr("cannot open temporary file \"%s\"", tempfile);
|
|
else
|
|
{
|
|
while( fgets(buf, sizeof(buf), fp) )
|
|
{
|
|
string_chomp(buf);
|
|
log("[%d] %s", pid, string_printable(buf));
|
|
}
|
|
file_close(fp);
|
|
}
|
|
}
|
|
|
|
if( WIFEXITED(status) )
|
|
{
|
|
eopt->retc = WEXITSTATUS(status);
|
|
log("process %d exit with code %d (%d seconds)",
|
|
(int)pid, eopt->retc, eopt->runtime);
|
|
}
|
|
else if( WIFSIGNALED(status) )
|
|
{
|
|
eopt->retc = -1;
|
|
log("process %d terminated on signal %d (%d seconds)",
|
|
(int)pid, WTERMSIG(status), eopt->runtime);
|
|
}
|
|
else
|
|
{
|
|
eopt->retc = -1;
|
|
log("process %d return with unknown status (%d seconds)",
|
|
(int)pid, eopt->runtime);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eopt->retc = -1;
|
|
logerr("waitpid return error for PID %d", (int)pid);
|
|
}
|
|
}
|
|
|
|
if( tempfile )
|
|
{
|
|
if( unlink(tempfile) == -1 && errno != ENOENT )
|
|
logerr("cannot unlink temporary file \"%s\"", tempfile);
|
|
free(tempfile);
|
|
}
|
|
|
|
/* Restore the original SIGCHLD handler */
|
|
sigaction(SIGCHLD, &old_chld, NULL);
|
|
|
|
/* Unblock SIGCHLD */
|
|
sigprocmask(SIG_UNBLOCK, &new_mask, NULL);
|
|
|
|
return eopt->retc;
|
|
}
|
|
|
|
/*
|
|
* Return -1 if error occured while excuting command, other values
|
|
* are return codes of your command
|
|
*/
|
|
int xsystem(const char *command, const char *p_input, const char *p_output)
|
|
{
|
|
pid_t pid;
|
|
int status;
|
|
|
|
ASSERT(command != NULL);
|
|
|
|
DEB((D_INFO, "xsystem: command \"%s\", input \"%s\", output \"%s\"",
|
|
command, p_input, p_output));
|
|
|
|
switch(pid=fork()) {
|
|
case -1:
|
|
return(-1);
|
|
case 0:
|
|
if( p_input )
|
|
{
|
|
close(0);
|
|
if( open(p_input, O_RDONLY) != 0 )
|
|
{
|
|
logerr("can't open stdin \"%s\"", p_input);
|
|
exit(-1);
|
|
}
|
|
}
|
|
if( p_output )
|
|
{
|
|
close(1);
|
|
if( open(p_output, O_WRONLY|O_APPEND|O_CREAT, 0600) != 1 )
|
|
{
|
|
logerr("can't open stdout \"%s\"", p_output);
|
|
exit(-1);
|
|
}
|
|
}
|
|
if( p_output )
|
|
{
|
|
close(2);
|
|
if( open(p_output, O_WRONLY|O_APPEND|O_CREAT, 0600) != 2 )
|
|
{
|
|
logerr("can't open stderr \"%s\"", p_output);
|
|
exit(-1);
|
|
}
|
|
}
|
|
#ifdef SHELL
|
|
execl(SHELL, "sh", "-c", command, NULL);
|
|
#else
|
|
execl("/bin/sh", "sh", "-c", command, NULL);
|
|
#endif
|
|
exit( (errno == 0)?0:-1 );
|
|
}
|
|
|
|
if( waitpid(pid, &status, 0) == pid && WIFEXITED(status) )
|
|
{
|
|
return(WEXITSTATUS(status));
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
#ifndef HAVE_RENAME
|
|
int rename(const char *old_name, const char *new_name)
|
|
{
|
|
int rc;
|
|
|
|
ASSERT(old_name != NULL && new_name != NULL);
|
|
|
|
if( (rc = link(old_name, new_name)) == 0 )
|
|
{
|
|
if( (rc = unlink(old_name)) == -1 )
|
|
{ logerr("can't unlink file \"%s\"", old_name); }
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Get free space on device mounted at path $path */
|
|
/* ------------------------------------------------------------------------- */
|
|
#if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
|
|
size_t getfreespace(const char *path)
|
|
{
|
|
#ifdef HAVE_STATVFS
|
|
struct statvfs sfs;
|
|
#else
|
|
struct statfs sfs;
|
|
#endif
|
|
|
|
ASSERT(path != NULL);
|
|
|
|
#ifdef HAVE_STATVFS
|
|
if( statvfs(path, &sfs) == 0 )
|
|
#else
|
|
if( statfs(path, &sfs) == 0 )
|
|
#endif
|
|
{
|
|
return(sfs.f_bsize * sfs.f_bavail);
|
|
}
|
|
else
|
|
{
|
|
logerr("can't statfs \"%s\", assume enough space", path);
|
|
return(~0L);
|
|
}
|
|
}
|
|
#else
|
|
size_t getfreespace(const char *path)
|
|
{
|
|
ASSERT(path != NULL);
|
|
|
|
log("warning: fake getfreespace - assume enough space");
|
|
return(~0L);
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_SETPROCTITLE
|
|
|
|
/*
|
|
* clobber argv so ps will show what we're doing.
|
|
* (stolen from BSD ftpd where it was stolen from sendmail)
|
|
* warning, since this is usually started from inetd.conf, it
|
|
* often doesn't have much of an environment or arglist to overwrite.
|
|
*/
|
|
|
|
static char *cmdstr = NULL;
|
|
static char *cmdstrend = NULL;
|
|
|
|
void setargspace(char *argv[], char *envp[])
|
|
{
|
|
cmdstr = argv[0];
|
|
while( *envp ) envp++;
|
|
envp--;
|
|
cmdstrend = (*envp) + strlen(*envp);
|
|
}
|
|
|
|
void setproctitle(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char buf[256]; /* I hope it will be large enough */
|
|
char *d = cmdstr;
|
|
char *s = buf;
|
|
|
|
va_start(args, fmt);
|
|
vsprintf(buf, fmt, args);
|
|
va_end(args);
|
|
|
|
/* Make ps print our process name */
|
|
while( d < cmdstrend && *s ) { *d = *s; d++; s++; }
|
|
while( d < cmdstrend ) *d++ = ' ';
|
|
}
|
|
|
|
#endif /* HAVE_SETPROCTITLE */
|