A bunch of changes working towards eliminating all memory allocation after fork()

This commit is contained in:
ridiculousfish 2012-03-08 23:21:07 -08:00
parent ce859c9e92
commit d173bb6e0a
15 changed files with 251 additions and 129 deletions

View file

@ -2964,7 +2964,7 @@ static int builtin_fg( parser_t &parser, wchar_t **argv )
_( L"%ls: Can't put job %d, '%ls' to foreground because it is not under job control\n" ),
argv[0],
pid,
j->command_cstr() );
j->command_wcstr() );
builtin_print_help( parser, argv[0], stderr_buffer );
j=0;
}
@ -2978,7 +2978,7 @@ static int builtin_fg( parser_t &parser, wchar_t **argv )
append_format(stderr_buffer,
FG_MSG,
j->job_id,
j->command_cstr() );
j->command_wcstr() );
}
else
{
@ -2990,10 +2990,10 @@ static int builtin_fg( parser_t &parser, wchar_t **argv )
fwprintf( stderr,
FG_MSG,
j->job_id,
j->command_cstr() );
j->command_wcstr() );
}
wchar_t *ft = tok_first( j->command_cstr() );
wchar_t *ft = tok_first( j->command_wcstr() );
if( ft != 0 )
env_set( L"_", ft, ENV_EXPORT );
free(ft);
@ -3027,7 +3027,7 @@ static int send_to_bg( parser_t &parser, job_t *j, const wchar_t *name )
_( L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n" ),
L"bg",
j->job_id,
j->command_cstr() );
j->command_wcstr() );
builtin_print_help( parser, L"bg", stderr_buffer );
return STATUS_BUILTIN_ERROR;
}
@ -3036,7 +3036,7 @@ static int send_to_bg( parser_t &parser, job_t *j, const wchar_t *name )
append_format(stderr_buffer,
_(L"Send job %d '%ls' to background\n"),
j->job_id,
j->command_cstr() );
j->command_wcstr() );
}
make_first( j );
job_set_flag( j, JOB_FOREGROUND, 0 );

View file

@ -98,7 +98,7 @@ static void builtin_jobs_print( const job_t *j, int mode, int header )
#endif
stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running"));
stdout_buffer.append(L"\t");
stdout_buffer.append(j->command_cstr());
stdout_buffer.append(j->command_wcstr());
stdout_buffer.append(L"\t");
break;
}

View file

@ -299,14 +299,31 @@ wchar_t *str2wcs_internal( const char *in, wchar_t *out )
char *wcs2str( const wchar_t *in )
{
char *out;
out = (char *)malloc( MAX_UTF8_BYTES*wcslen(in)+1 );
if( !out )
{
DIE_MEM();
}
if (! in)
return NULL;
char *out;
size_t desired_size = MAX_UTF8_BYTES*wcslen(in)+1;
char local_buff[512];
if (desired_size <= sizeof local_buff / sizeof *local_buff) {
// convert into local buff, then use strdup() so we don't waste malloc'd space
char *result = wcs2str_internal(in, local_buff);
if (result) {
// It converted into the local buffer, so copy it
result = strdup(result);
if (! result) {
DIE_MEM();
}
}
return result;
} else {
// here we fall into the bad case of allocating a buffer probably much larger than necessary
out = (char *)malloc( MAX_UTF8_BYTES*wcslen(in)+1 );
if (!out) {
DIE_MEM();
}
return wcs2str_internal( in, out );
}
return wcs2str_internal( in, out );
}
@ -727,8 +744,9 @@ void debug( int level, const wchar_t *msg, ... )
errno = errno_old;
}
void debug_safe(int level, const char *msg, const char *param1, const char *param2, const char *param3)
void debug_safe(int level, const char *msg, const char *param1, const char *param2, const char *param3, const char *param4, const char *param5, const char *param6, const char *param7, const char *param8, const char *param9, const char *param10, const char *param11, const char *param12)
{
const char * const params[] = {param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12};
if (! msg)
return;
@ -748,12 +766,8 @@ void debug_safe(int level, const char *msg, const char *param1, const char *para
if (end[0] == '%' && end[1] == 's') {
/* Handle a format string */
const char *format = NULL;
switch (param_idx++) {
case 0: format = param1; break;
case 1: format = param2; break;
case 2: format = param3; break;
}
assert(param_idx < sizeof params / sizeof *params);
const char *format = params[param_idx++];
if (! format)
format = "(null)";
write(STDERR_FILENO, format, strlen(format));

View file

@ -288,7 +288,7 @@ wcstring format_size(long long sz);
void format_size_safe(char buff[128], unsigned long long sz);
/** Our crappier versions of debug which is guaranteed to not allocate any memory, or do anything other than call write(). This is useful after a call to fork() with threads. */
void debug_safe(int level, const char *msg, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL);
void debug_safe(int level, const char *msg, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL, const char *param5 = NULL, const char *param6 = NULL, const char *param7 = NULL, const char *param8 = NULL, const char *param9 = NULL, const char *param10 = NULL, const char *param11 = NULL, const char *param12 = NULL);
/** Writes out a long safely */
void format_long_safe(char buff[128], long val);
@ -446,6 +446,28 @@ class null_terminated_array_t {
/* Helper function to convert from a null_terminated_array_t<wchar_t> to a null_terminated_array_t<char_t> */
null_terminated_array_t<char> convert_wide_array_to_narrow(const null_terminated_array_t<wchar_t> &arr);
/* Helper class to cache a narrow version of a wcstring in a malloc'd buffer, so that we can read it after fork() */
class narrow_string_rep_t {
private:
const char *str;
public:
~narrow_string_rep_t() {
free((void *)str);
}
narrow_string_rep_t() : str(NULL) {}
void set(const wcstring &s) {
free((void *)str);
str = wcs2str(s.c_str());
}
const char *get() const {
return str;
}
};
bool is_forked_child();
/* Basic scoped lock class */

View file

@ -192,7 +192,7 @@ wcstring event_get_desc( const event_t *e )
{
job_t *j = job_get_from_pid( -e->param1.pid );
if( j )
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_cstr() );
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr() );
else
result = format_string(_(L"exit handler for job with process group %d"), -e->param1.pid );
}
@ -203,7 +203,7 @@ wcstring event_get_desc( const event_t *e )
{
job_t *j = job_get( e->param1.job_id );
if( j )
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_cstr() );
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id, j->command_wcstr() );
else
result = format_string(_(L"exit handler for job with job id %d"), j->job_id );

View file

@ -65,7 +65,7 @@
/**
file redirection error message
*/
#define FILE_ERROR _( L"An error occurred while redirecting file '%ls'" )
#define FILE_ERROR _( L"An error occurred while redirecting file '%s'" )
/**
Base open mode to pass to calls to open
@ -448,11 +448,11 @@ static io_data_t *io_transmogrify( io_data_t * in )
{
int fd;
if( (fd=wopen( in->filename, in->param2.flags, OPEN_MASK ) )==-1 )
if( (fd=open( in->filename_cstr, in->param2.flags, OPEN_MASK ) )==-1 )
{
debug( 1,
FILE_ERROR,
in->filename.c_str() );
in->filename_cstr );
wperror( L"open" );
return NULL;
@ -574,7 +574,7 @@ void exec( parser_t &parser, job_t *j )
sigemptyset( &chldset );
sigaddset( &chldset, SIGCHLD );
debug( 4, L"Exec job '%ls' with id %d", j->command_cstr(), j->job_id );
debug( 4, L"Exec job '%ls' with id %d", j->command_wcstr(), j->job_id );
if( parser.block_io )
{
@ -689,6 +689,9 @@ void exec( parser_t &parser, job_t *j )
if( needs_keepalive )
{
/* Call fork. No need to wait for threads since our use is confined and simple. */
if (g_log_forks) {
printf("Executing keepalive fork for '%ls'\n", j->command_wcstr());
}
keepalive.pid = execute_fork(false);
if( keepalive.pid == 0 )
{
@ -880,13 +883,13 @@ void exec( parser_t &parser, job_t *j )
case IO_FILE:
{
/* Do not set CLO_EXEC because child needs access */
builtin_stdin=wopen( in->filename,
builtin_stdin=open( in->filename_cstr,
in->param2.flags, OPEN_MASK );
if( builtin_stdin == -1 )
{
debug( 1,
FILE_ERROR,
in->filename.c_str() );
in->filename_cstr );
wperror( L"open" );
}
else
@ -1045,6 +1048,9 @@ void exec( parser_t &parser, job_t *j )
if( io_buffer->out_buffer_size() > 0 )
{
/* We don't have to drain threads here because our child process is simple */
if (g_log_forks) {
printf("Executing fork for internal block or function for '%ls'\n", p->argv0());
}
pid = execute_fork(false);
if( pid == 0 )
{
@ -1093,6 +1099,10 @@ void exec( parser_t &parser, job_t *j )
const char *buffer = input_redirect->out_buffer_ptr();
size_t count = input_redirect->out_buffer_size();
/* We don't have to drain threads here because our child process is simple */
if (g_log_forks) {
printf("Executing fork for internal buffer for '%ls'\n", p->argv0() ? p->argv0() : L"(null)");
}
pid = execute_fork(false);
if( pid == 0 )
{
@ -1148,7 +1158,7 @@ void exec( parser_t &parser, job_t *j )
*/
io_data_t *io = io_get( j->io, 1 );
int buffer_stdout = io && io->io_mode == IO_BUFFER;
bool buffer_stdout = io && io->io_mode == IO_BUFFER;
if( ( get_stderr_buffer().empty() ) &&
( !p->next ) &&
@ -1160,9 +1170,9 @@ void exec( parser_t &parser, job_t *j )
skip_fork = 1;
}
for( io = j->io; io; io=io->next )
for( io_data_t *tmp_io = j->io; tmp_io != NULL; tmp_io=tmp_io->next )
{
if( io->io_mode == IO_FILE && io->filename != L"/dev/null")
if( tmp_io->io_mode == IO_FILE && strcmp(tmp_io->filename_cstr, "/dev/null") != 0)
{
skip_fork = 0;
}
@ -1173,7 +1183,7 @@ void exec( parser_t &parser, job_t *j )
p->completed=1;
if( p->next == 0 )
{
debug( 3, L"Set status of %ls to %d using short circut", j->command_cstr(), p->status );
debug( 3, L"Set status of %ls to %d using short circut", j->command_wcstr(), p->status );
int status = p->status;
proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):status );
@ -1189,6 +1199,10 @@ void exec( parser_t &parser, job_t *j )
fflush(stdout);
fflush(stderr);
if (g_log_forks) {
printf("Executing fork for internal builtin for '%ls' (io is %p)\n", p->argv0(), io);
io_print(io);
}
pid = execute_fork(false);
if( pid == 0 )
{
@ -1240,9 +1254,6 @@ void exec( parser_t &parser, job_t *j )
const wchar_t *reader_current_filename();
if (g_log_forks) {
printf("forking for '%s' in '%ls'\n", actual_cmd, reader_current_filename());
if (std::string(actual_cmd) == "/usr/bin/getopt") {
puts("wat");
}
}
pid = execute_fork(true /* must drain threads */);
if( pid == 0 )

4
exec.h
View file

@ -59,8 +59,8 @@ __warn_unused int exec_subshell(const wcstring &cmd );
/**
Loops over close until thesyscall was run without beeing
interrupted. Thenremoves the fd from the open_fds list.
Loops over close until the syscall was run without being
interrupted. Then removes the fd from the open_fds list.
*/
void exec_close( int fd );

View file

@ -348,14 +348,14 @@ static int find_process( const wchar_t *proc,
while ((j = jobs.next()))
{
wchar_t jid[16];
if( j->command.size() == 0 )
if( j->command_is_empty() )
continue;
swprintf( jid, 16, L"%d", j->job_id );
if( wcsncmp( proc, jid, wcslen(proc ) )==0 )
{
wcstring desc_buff = format_string(COMPLETE_JOB_DESC_VAL, j->command_cstr());
wcstring desc_buff = format_string(COMPLETE_JOB_DESC_VAL, j->command_wcstr());
completion_allocate( out,
jid+wcslen(proc),
desc_buff,
@ -375,7 +375,7 @@ static int find_process( const wchar_t *proc,
if( jid > 0 && !errno && !*end )
{
j = job_get( jid );
if( (j != 0) && (j->command_cstr() != 0 ) )
if( (j != 0) && (j->command_wcstr() != 0 ) )
{
{
@ -395,15 +395,15 @@ static int find_process( const wchar_t *proc,
{
int offset;
if( j->command_cstr() == 0 )
if( j->command_wcstr() == 0 )
continue;
if( match_pid( j->command_cstr(), proc, flags, &offset ) )
if( match_pid( j->command_wcstr(), proc, flags, &offset ) )
{
if( flags & ACCEPT_INCOMPLETE )
{
completion_allocate( out,
j->command_cstr() + offset + wcslen(proc),
j->command_wcstr() + offset + wcslen(proc),
COMPLETE_JOB_DESC,
0 );
}
@ -425,7 +425,7 @@ static int find_process( const wchar_t *proc,
while ((j = jobs.next()))
{
process_t *p;
if( j->command.size() == 0 )
if( j->command_is_empty() )
continue;
for( p=j->first_process; p; p=p->next )
{

2
io.cpp
View file

@ -197,7 +197,7 @@ io_data_t *io_duplicate( io_data_t *l )
io_data_t *io_get( io_data_t *io, int fd )
{
if( io == 0 )
if( io == NULL )
return 0;
io_data_t *res = io_get( io->next, fd );

32
io.h
View file

@ -40,8 +40,6 @@ public:
int old_fd;
} param1;
/** Filename IO_FILE */
wcstring filename;
/** Second type-specific paramter for redirection */
union
@ -52,6 +50,15 @@ public:
int close_old;
} param2;
/** Filename IO_FILE. malloc'd. This needs to be used after fork, so don't use wcstring here. */
const char *filename_cstr;
/** Convenience to set filename_cstr via wcstring */
void set_filename(const wcstring &str) {
free((void *)filename_cstr);
filename_cstr = wcs2str(str.c_str());
}
/** Function to create the output buffer */
void out_buffer_create() {
out_buffer.reset(new std::vector<char>);
@ -81,11 +88,26 @@ public:
/** Pointer to the next IO redirection */
io_data_t *next;
io_data_t() : next(NULL)
io_data_t() : filename_cstr(NULL), next(NULL)
{
}
/* Note: we have a default copy constructor */
io_data_t(const io_data_t &rhs) :
out_buffer(rhs.out_buffer),
io_mode(rhs.io_mode),
fd(rhs.fd),
param1(rhs.param1),
param2(rhs.param2),
filename_cstr(rhs.filename_cstr ? strdup(rhs.filename_cstr) : NULL),
is_input(rhs.is_input),
next(rhs.next)
{
}
~io_data_t() {
free((void *)filename_cstr);
}
};
@ -118,7 +140,7 @@ void io_buffer_destroy( io_data_t *io_buffer );
/**
Create a IO_BUFFER type io redirection, complete with a pipe and a
buffer_t for output. The default file descriptor used is 1 for
vector<char> for output. The default file descriptor used is 1 for
output buffering and 0 for input buffering.
\param is_input set this parameter to zero if the buffer should be

View file

@ -1538,25 +1538,25 @@ void parser_t::parse_job_argument_list( process_t *p,
case TOK_REDIRECT_APPEND:
new_io->io_mode = IO_FILE;
new_io->param2.flags = O_CREAT | O_APPEND | O_WRONLY;
new_io->filename = target;
new_io->set_filename(target);
break;
case TOK_REDIRECT_OUT:
new_io->io_mode = IO_FILE;
new_io->param2.flags = O_CREAT | O_WRONLY | O_TRUNC;
new_io->filename = target;
new_io->set_filename(target);
break;
case TOK_REDIRECT_NOCLOB:
new_io->io_mode = IO_FILE;
new_io->param2.flags = O_CREAT | O_EXCL | O_WRONLY;
new_io->filename = target;
new_io->set_filename(target);
break;
case TOK_REDIRECT_IN:
new_io->io_mode = IO_FILE;
new_io->param2.flags = O_RDONLY;
new_io->filename = target;
new_io->set_filename(target);
break;
case TOK_REDIRECT_FD:
@ -2311,15 +2311,15 @@ void parser_t::eval_job( tokenizer *tok )
if( newline )
stop_pos = mini( stop_pos, newline - tok_string(tok) );
j->command = wcstring(tok_string(tok)+start_pos, stop_pos-start_pos);
j->set_command(wcstring(tok_string(tok)+start_pos, stop_pos-start_pos));
}
else
j->command = L"";
j->set_command(L"");
if( profile )
{
t2 = get_time();
profile_item->cmd = wcsdup( j->command_cstr() );
profile_item->cmd = wcsdup( j->command_wcstr() );
profile_item->skipped=current_block->skip;
}

View file

@ -20,17 +20,27 @@
#define OPEN_MASK 0666
/** fork error message */
#define FORK_ERROR _( L"Could not create child process - exiting" )
#define FORK_ERROR "Could not create child process - exiting"
/** file redirection clobbering error message */
#define NOCLOB_ERROR _( L"The file '%ls' already exists" )
#define NOCLOB_ERROR "The file '%s' already exists"
/** file redirection error message */
#define FILE_ERROR _( L"An error occurred while redirecting file '%ls'" )
#define FILE_ERROR "An error occurred while redirecting file '%s'"
/** file descriptor redirection error message */
#define FD_ERROR _( L"An error occurred while redirecting file descriptor %d" )
#define FD_ERROR "An error occurred while redirecting file descriptor %s"
/** pipe error */
#define LOCAL_PIPE_ERROR "An error occurred while setting up pipe"
/* Cover for debug_safe that can take an int. The format string should expect a %s */
static void debug_safe_int(int level, const char *format, int val)
{
char buff[128];
format_long_safe(buff, val);
debug_safe(level, format, buff);
}
// PCA These calls to debug are rather sketchy because they may allocate memory. Fortunately they only occur if an error occurs.
int set_child_group( job_t *j, process_t *p, int print_errors )
@ -48,15 +58,25 @@ int set_child_group( job_t *j, process_t *p, int print_errors )
{
if( getpgid( p->pid) != j->pgid && print_errors )
{
char pid_buff[128];
char job_id_buff[128];
char getpgid_buff[128];
char job_pgid_buff[128];
format_long_safe(pid_buff, p->pid);
format_long_safe(job_id_buff, j->job_id);
format_long_safe(getpgid_buff, getpgid( p->pid));
format_long_safe(job_pgid_buff, j->pgid);
// PCA FIXME This is sketchy to do in a forked child because it may allocate memory. This needs to call only safe functions.
debug( 1,
_( L"Could not send process %d, '%ls' in job %d, '%ls' from group %d to group %d" ),
p->pid,
p->argv0(),
j->job_id,
debug_safe( 1,
"Could not send process %s, '%s' in job %s, '%s' from group %s to group %s",
pid_buff,
p->argv0_cstr(),
job_id_buff,
j->command_cstr(),
getpgid( p->pid),
j->pgid );
getpgid_buff,
job_pgid_buff );
wperror( L"setpgid" );
res = -1;
@ -72,9 +92,9 @@ int set_child_group( job_t *j, process_t *p, int print_errors )
{
if( tcsetpgrp (0, j->pgid) && print_errors )
{
debug( 1, _( L"Could not send job %d ('%ls') to foreground" ),
j->job_id,
j->command_cstr() );
char job_id_buff[128];
format_long_safe(job_id_buff, j->job_id);
debug_safe( 1, "Could not send job %s ('%s') to foreground", job_id_buff, j->command_cstr() );
wperror( L"tcsetpgrp" );
res = -1;
}
@ -102,9 +122,7 @@ static void free_fd( io_data_t *io, int fd )
{
if( errno != EINTR )
{
debug( 1,
FD_ERROR,
fd );
debug_safe_int( 1, FD_ERROR, fd );
wperror( L"dup" );
FATAL_EXIT();
}
@ -161,7 +179,7 @@ static int handle_child_io( io_data_t *io )
{
if( close(io->fd) )
{
debug( 0, _(L"Failed to close file descriptor %d"), io->fd );
debug_safe_int( 0, "Failed to close file descriptor %s", io->fd );
wperror( L"close" );
}
break;
@ -170,23 +188,18 @@ static int handle_child_io( io_data_t *io )
case IO_FILE:
{
// Here we definitely do not want to set CLO_EXEC because our child needs access
if( (tmp=wopen( io->filename,
if( (tmp=open( io->filename_cstr,
io->param2.flags, OPEN_MASK ) )==-1 )
{
if( ( io->param2.flags & O_EXCL ) &&
( errno ==EEXIST ) )
{
debug( 1,
NOCLOB_ERROR,
io->filename.c_str() );
debug_safe( 1, NOCLOB_ERROR, io->filename_cstr );
}
else
{
debug( 1,
FILE_ERROR,
io->filename.c_str() );
wperror( L"open" );
debug_safe( 1, FILE_ERROR, io->filename_cstr );
perror( "open" );
}
return -1;
@ -201,10 +214,8 @@ static int handle_child_io( io_data_t *io )
if(dup2( tmp, io->fd ) == -1 )
{
debug( 1,
FD_ERROR,
io->fd );
wperror( L"dup2" );
debug_safe_int( 1, FD_ERROR, io->fd );
perror( "dup2" );
return -1;
}
exec_close( tmp );
@ -222,9 +233,7 @@ static int handle_child_io( io_data_t *io )
if( dup2( io->param1.old_fd, io->fd ) == -1 )
{
debug( 1,
FD_ERROR,
io->fd );
debug_safe_int( 1, FD_ERROR, io->fd );
wperror( L"dup2" );
return -1;
}
@ -248,8 +257,8 @@ static int handle_child_io( io_data_t *io )
*/
if( dup2( io->param1.pipe_fd[write_pipe], io->fd ) != io->fd )
{
debug( 1, PIPE_ERROR );
wperror( L"dup2" );
debug_safe( 1, LOCAL_PIPE_ERROR );
perror( "dup2" );
return -1;
}
@ -275,24 +284,24 @@ static int handle_child_io( io_data_t *io )
int setup_child_process( job_t *j, process_t *p )
{
int res=0;
bool ok=true;
if( p )
{
res = set_child_group( j, p, 1 );
ok = (0 == set_child_group( j, p, 1 ));
}
if( !res )
if( ok )
{
res = handle_child_io( j->io );
if( p != 0 && res )
ok = (0 == handle_child_io( j->io ));
if( p != 0 && ! ok )
{
exit_without_destructors( 1 );
}
}
/* Set the handling for job control signals back to the default. */
if( !res )
if( ok )
{
signal_reset_handlers();
}
@ -300,7 +309,7 @@ int setup_child_process( job_t *j, process_t *p )
/* Remove all signal blocks */
signal_unblock();
return res;
return ok ? 0 : -1;
}
@ -353,7 +362,7 @@ pid_t execute_fork(bool wait_for_threads_to_die)
}
}
debug( 0, FORK_ERROR );
debug_safe( 0, FORK_ERROR );
wperror (L"fork");
FATAL_EXIT();
return 0;

View file

@ -184,7 +184,7 @@ void proc_destroy()
while( ! jobs.empty() )
{
job_t *job = jobs.front();
debug( 2, L"freeing leaked job %ls", job->command_cstr() );
debug( 2, L"freeing leaked job %ls", job->command_wcstr() );
job_free( job );
}
}
@ -528,7 +528,7 @@ void job_handle_signal ( int signal, siginfo_t *info, void *con )
static void format_job_info( const job_t *j, const wchar_t *status )
{
fwprintf (stdout, L"\r" );
fwprintf (stdout, _( L"Job %d, \'%ls\' has %ls" ), j->job_id, j->command_cstr(), status);
fwprintf (stdout, _( L"Job %d, \'%ls\' has %ls" ), j->job_id, j->command_wcstr(), status);
fflush( stdout );
tputs(clr_eol,1,&writeb);
fwprintf (stdout, L"\n" );
@ -613,7 +613,7 @@ int job_reap( bool interactive )
_( L"%ls: Job %d, \'%ls\' terminated by signal %ls (%ls)" ),
program_name,
j->job_id,
j->command_cstr(),
j->command_wcstr(),
sig2wcs(WTERMSIG(p->status)),
signal_get_desc( WTERMSIG(p->status) ) );
else
@ -623,7 +623,7 @@ int job_reap( bool interactive )
p->pid,
p->argv0(),
j->job_id,
j->command_cstr(),
j->command_wcstr(),
sig2wcs(WTERMSIG(p->status)),
signal_get_desc( WTERMSIG(p->status) ) );
tputs(clr_eol,1,&writeb);
@ -857,7 +857,7 @@ static void read_try( job_t *j )
if( buff )
{
debug( 3, L"proc::read_try('%ls')\n", j->command_cstr() );
debug( 3, L"proc::read_try('%ls')\n", j->command_wcstr() );
while(1)
{
char b[BUFFER_SIZE];
@ -905,7 +905,7 @@ static int terminal_give_to_job( job_t *j, int cont )
debug( 1,
_( L"Could not send job %d ('%ls') to foreground" ),
j->job_id,
j->command_cstr() );
j->command_wcstr() );
wperror( L"tcsetpgrp" );
return 0;
}
@ -917,7 +917,7 @@ static int terminal_give_to_job( job_t *j, int cont )
debug( 1,
_( L"Could not send job %d ('%ls') to foreground" ),
j->job_id,
j->command_cstr() );
j->command_wcstr() );
wperror( L"tcsetattr" );
return 0;
}
@ -977,7 +977,7 @@ void job_continue (job_t *j, int cont)
L"Continue job %d, gid %d (%ls), %ls, %ls",
j->job_id,
j->pgid,
j->command_cstr(),
j->command_wcstr(),
job_is_completed( j )?L"COMPLETED":L"UNCOMPLETED",
is_interactive?L"INTERACTIVE":L"NON-INTERACTIVE" );
@ -1188,8 +1188,8 @@ void proc_sanity_check()
{
debug( 0,
_( L"More than one job in foreground: job 1: '%ls' job 2: '%ls'"),
fg_job->command_cstr(),
j->command_cstr() );
fg_job->command_wcstr(),
j->command_wcstr() );
sanity_lose();
}
fg_job = j;
@ -1207,7 +1207,7 @@ void proc_sanity_check()
{
debug( 0,
_( L"Job '%ls', process '%ls' has inconsistent state \'stopped\'=%d" ),
j->command_cstr(),
j->command_wcstr(),
p->argv0(),
p->stopped );
sanity_lose();
@ -1217,7 +1217,7 @@ void proc_sanity_check()
{
debug( 0,
_( L"Job '%ls', process '%ls' has inconsistent state \'completed\'=%d" ),
j->command_cstr(),
j->command_wcstr(),
p->argv0(),
p->completed );
sanity_lose();

68
proc.h
View file

@ -130,7 +130,15 @@ class process_t
private:
null_terminated_array_t<wchar_t> argv_array;
/* narrow copy of argv0 so we don't have to convert after fork */
narrow_string_rep_t argv0_narrow;
/* No copying */
process_t(const process_t &rhs) { }
void operator=(const process_t &rhs) { }
public:
process_t() :
@ -168,17 +176,32 @@ class process_t
/** Sets argv */
void set_argv(const wcstring_list_t &argv) { argv_array.set(argv); }
void set_argv(const wcstring_list_t &argv) {
argv_array.set(argv);
argv0_narrow.set(argv.empty() ? L"" : argv[0]);
}
/** Returns argv */
const wchar_t * const *get_argv(void) const { return argv_array.get(); }
const null_terminated_array_t<wchar_t> &get_argv_array(void) const { return argv_array; }
/** Returns argv[0] */
const wchar_t *argv0(void) const { return argv_array.get()[0]; }
/** Returns argv[idx] */
const wchar_t *argv(size_t idx) const { return argv_array.get()[idx]; }
const wchar_t *argv(size_t idx) const {
const wchar_t * const *argv = argv_array.get();
assert(argv != NULL);
return argv[idx];
}
/** Returns argv[0], or NULL */
const wchar_t *argv0(void) const {
const wchar_t * const *argv = argv_array.get();
return argv ? argv[0] : NULL;
}
/** Returns argv[0] as a char * */
const char *argv0_cstr(void) const {
return argv0_narrow.get();
}
/** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. malloc'd! */
const wchar_t *actual_cmd;
@ -283,6 +306,20 @@ void release_job_id(job_id_t jobid);
class job_t
{
/**
The original command which led to the creation of this
job. It is used for displaying messages about job status
on the terminal.
*/
wcstring command;
/* narrow copy so we don't have to convert after fork */
narrow_string_rep_t command_narrow;
/* No copying */
job_t(const job_t &rhs) : job_id(0) { }
void operator=(const job_t &) { }
public:
job_t(job_id_t jobid) :
@ -307,16 +344,21 @@ class job_t
}
release_job_id(job_id);
}
/**
The original command which led to the creation of this
job. It is used for displaying messages about job status
on the terminal.
*/
wcstring command;
/** Returns whether the command is empty. */
bool command_is_empty() const { return command.empty(); }
const wchar_t *command_cstr() const { return command.c_str(); }
/** Returns the command as a wchar_t *. */
const wchar_t *command_wcstr() const { return command.c_str(); }
/** Returns the command as a char *. */
const char *command_cstr() const { return command_narrow.get(); }
/** Sets the command */
void set_command(const wcstring &cmd) {
command = cmd;
command_narrow.set(cmd);
}
/**
A linked list of all the processes in this job. We are responsible for deleting this when we are deallocated.

View file

@ -194,6 +194,7 @@ bool set_cloexec(int fd) {
static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec)
{
ASSERT_IS_NOT_FORKED_CHILD();
cstring tmp = wcs2string(pathname);
/* Prefer to use O_CLOEXEC. It has to both be defined and nonzero */
#ifdef O_CLOEXEC
@ -214,6 +215,7 @@ int wopen(const wcstring &pathname, int flags, mode_t mode)
{
// off the main thread, always use wopen_cloexec
ASSERT_IS_MAIN_THREAD();
ASSERT_IS_NOT_FORKED_CHILD();
return wopen_internal(pathname, flags, mode, false);
}