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" ), _( L"%ls: Can't put job %d, '%ls' to foreground because it is not under job control\n" ),
argv[0], argv[0],
pid, pid,
j->command_cstr() ); j->command_wcstr() );
builtin_print_help( parser, argv[0], stderr_buffer ); builtin_print_help( parser, argv[0], stderr_buffer );
j=0; j=0;
} }
@ -2978,7 +2978,7 @@ static int builtin_fg( parser_t &parser, wchar_t **argv )
append_format(stderr_buffer, append_format(stderr_buffer,
FG_MSG, FG_MSG,
j->job_id, j->job_id,
j->command_cstr() ); j->command_wcstr() );
} }
else else
{ {
@ -2990,10 +2990,10 @@ static int builtin_fg( parser_t &parser, wchar_t **argv )
fwprintf( stderr, fwprintf( stderr,
FG_MSG, FG_MSG,
j->job_id, 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 ) if( ft != 0 )
env_set( L"_", ft, ENV_EXPORT ); env_set( L"_", ft, ENV_EXPORT );
free(ft); 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"%ls: Can't put job %d, '%ls' to background because it is not under job control\n" ),
L"bg", L"bg",
j->job_id, j->job_id,
j->command_cstr() ); j->command_wcstr() );
builtin_print_help( parser, L"bg", stderr_buffer ); builtin_print_help( parser, L"bg", stderr_buffer );
return STATUS_BUILTIN_ERROR; 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, append_format(stderr_buffer,
_(L"Send job %d '%ls' to background\n"), _(L"Send job %d '%ls' to background\n"),
j->job_id, j->job_id,
j->command_cstr() ); j->command_wcstr() );
} }
make_first( j ); make_first( j );
job_set_flag( j, JOB_FOREGROUND, 0 ); 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 #endif
stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running")); stdout_buffer.append(job_is_stopped(j)?_(L"stopped"):_(L"running"));
stdout_buffer.append(L"\t"); stdout_buffer.append(L"\t");
stdout_buffer.append(j->command_cstr()); stdout_buffer.append(j->command_wcstr());
stdout_buffer.append(L"\t"); stdout_buffer.append(L"\t");
break; break;
} }

View file

@ -299,14 +299,31 @@ wchar_t *str2wcs_internal( const char *in, wchar_t *out )
char *wcs2str( const wchar_t *in ) char *wcs2str( const wchar_t *in )
{ {
char *out; if (! in)
return NULL;
out = (char *)malloc( MAX_UTF8_BYTES*wcslen(in)+1 ); char *out;
size_t desired_size = MAX_UTF8_BYTES*wcslen(in)+1;
if( !out ) char local_buff[512];
{ if (desired_size <= sizeof local_buff / sizeof *local_buff) {
DIE_MEM(); // 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 ); return wcs2str_internal( in, out );
} }
@ -727,8 +744,9 @@ void debug( int level, const wchar_t *msg, ... )
errno = errno_old; 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) if (! msg)
return; 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') { if (end[0] == '%' && end[1] == 's') {
/* Handle a format string */ /* Handle a format string */
const char *format = NULL; assert(param_idx < sizeof params / sizeof *params);
switch (param_idx++) { const char *format = params[param_idx++];
case 0: format = param1; break;
case 1: format = param2; break;
case 2: format = param3; break;
}
if (! format) if (! format)
format = "(null)"; format = "(null)";
write(STDERR_FILENO, format, strlen(format)); 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); 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. */ /** 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 */ /** Writes out a long safely */
void format_long_safe(char buff[128], long val); 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> */ /* 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); 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(); bool is_forked_child();
/* Basic scoped lock class */ /* 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 ); job_t *j = job_get_from_pid( -e->param1.pid );
if( j ) 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 else
result = format_string(_(L"exit handler for job with process group %d"), -e->param1.pid ); 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 ); job_t *j = job_get( e->param1.job_id );
if( j ) 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 else
result = format_string(_(L"exit handler for job with job id %d"), j->job_id ); 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 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 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; 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, debug( 1,
FILE_ERROR, FILE_ERROR,
in->filename.c_str() ); in->filename_cstr );
wperror( L"open" ); wperror( L"open" );
return NULL; return NULL;
@ -574,7 +574,7 @@ void exec( parser_t &parser, job_t *j )
sigemptyset( &chldset ); sigemptyset( &chldset );
sigaddset( &chldset, SIGCHLD ); 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 ) if( parser.block_io )
{ {
@ -689,6 +689,9 @@ void exec( parser_t &parser, job_t *j )
if( needs_keepalive ) if( needs_keepalive )
{ {
/* Call fork. No need to wait for threads since our use is confined and simple. */ /* 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); keepalive.pid = execute_fork(false);
if( keepalive.pid == 0 ) if( keepalive.pid == 0 )
{ {
@ -880,13 +883,13 @@ void exec( parser_t &parser, job_t *j )
case IO_FILE: case IO_FILE:
{ {
/* Do not set CLO_EXEC because child needs access */ /* 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 ); in->param2.flags, OPEN_MASK );
if( builtin_stdin == -1 ) if( builtin_stdin == -1 )
{ {
debug( 1, debug( 1,
FILE_ERROR, FILE_ERROR,
in->filename.c_str() ); in->filename_cstr );
wperror( L"open" ); wperror( L"open" );
} }
else else
@ -1045,6 +1048,9 @@ void exec( parser_t &parser, job_t *j )
if( io_buffer->out_buffer_size() > 0 ) if( io_buffer->out_buffer_size() > 0 )
{ {
/* We don't have to drain threads here because our child process is simple */ /* 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); pid = execute_fork(false);
if( pid == 0 ) if( pid == 0 )
{ {
@ -1093,6 +1099,10 @@ void exec( parser_t &parser, job_t *j )
const char *buffer = input_redirect->out_buffer_ptr(); const char *buffer = input_redirect->out_buffer_ptr();
size_t count = input_redirect->out_buffer_size(); 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); pid = execute_fork(false);
if( pid == 0 ) if( pid == 0 )
{ {
@ -1148,7 +1158,7 @@ void exec( parser_t &parser, job_t *j )
*/ */
io_data_t *io = io_get( j->io, 1 ); 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() ) && if( ( get_stderr_buffer().empty() ) &&
( !p->next ) && ( !p->next ) &&
@ -1160,9 +1170,9 @@ void exec( parser_t &parser, job_t *j )
skip_fork = 1; 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; skip_fork = 0;
} }
@ -1173,7 +1183,7 @@ void exec( parser_t &parser, job_t *j )
p->completed=1; p->completed=1;
if( p->next == 0 ) 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; int status = p->status;
proc_set_last_status( job_get_flag( j, JOB_NEGATE )?(!status):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(stdout);
fflush(stderr); 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); pid = execute_fork(false);
if( pid == 0 ) if( pid == 0 )
{ {
@ -1240,9 +1254,6 @@ void exec( parser_t &parser, job_t *j )
const wchar_t *reader_current_filename(); const wchar_t *reader_current_filename();
if (g_log_forks) { if (g_log_forks) {
printf("forking for '%s' in '%ls'\n", actual_cmd, reader_current_filename()); 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 */); pid = execute_fork(true /* must drain threads */);
if( pid == 0 ) 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 Loops over close until the syscall was run without being
interrupted. Thenremoves the fd from the open_fds list. interrupted. Then removes the fd from the open_fds list.
*/ */
void exec_close( int fd ); void exec_close( int fd );

View file

@ -348,14 +348,14 @@ static int find_process( const wchar_t *proc,
while ((j = jobs.next())) while ((j = jobs.next()))
{ {
wchar_t jid[16]; wchar_t jid[16];
if( j->command.size() == 0 ) if( j->command_is_empty() )
continue; continue;
swprintf( jid, 16, L"%d", j->job_id ); swprintf( jid, 16, L"%d", j->job_id );
if( wcsncmp( proc, jid, wcslen(proc ) )==0 ) 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, completion_allocate( out,
jid+wcslen(proc), jid+wcslen(proc),
desc_buff, desc_buff,
@ -375,7 +375,7 @@ static int find_process( const wchar_t *proc,
if( jid > 0 && !errno && !*end ) if( jid > 0 && !errno && !*end )
{ {
j = job_get( jid ); 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; int offset;
if( j->command_cstr() == 0 ) if( j->command_wcstr() == 0 )
continue; continue;
if( match_pid( j->command_cstr(), proc, flags, &offset ) ) if( match_pid( j->command_wcstr(), proc, flags, &offset ) )
{ {
if( flags & ACCEPT_INCOMPLETE ) if( flags & ACCEPT_INCOMPLETE )
{ {
completion_allocate( out, completion_allocate( out,
j->command_cstr() + offset + wcslen(proc), j->command_wcstr() + offset + wcslen(proc),
COMPLETE_JOB_DESC, COMPLETE_JOB_DESC,
0 ); 0 );
} }
@ -425,7 +425,7 @@ static int find_process( const wchar_t *proc,
while ((j = jobs.next())) while ((j = jobs.next()))
{ {
process_t *p; process_t *p;
if( j->command.size() == 0 ) if( j->command_is_empty() )
continue; continue;
for( p=j->first_process; p; p=p->next ) 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 ) io_data_t *io_get( io_data_t *io, int fd )
{ {
if( io == 0 ) if( io == NULL )
return 0; return 0;
io_data_t *res = io_get( io->next, fd ); io_data_t *res = io_get( io->next, fd );

32
io.h
View file

@ -40,8 +40,6 @@ public:
int old_fd; int old_fd;
} param1; } param1;
/** Filename IO_FILE */
wcstring filename;
/** Second type-specific paramter for redirection */ /** Second type-specific paramter for redirection */
union union
@ -52,6 +50,15 @@ public:
int close_old; int close_old;
} param2; } 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 */ /** Function to create the output buffer */
void out_buffer_create() { void out_buffer_create() {
out_buffer.reset(new std::vector<char>); out_buffer.reset(new std::vector<char>);
@ -81,11 +88,26 @@ public:
/** Pointer to the next IO redirection */ /** Pointer to the next IO redirection */
io_data_t *next; 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 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. output buffering and 0 for input buffering.
\param is_input set this parameter to zero if the buffer should be \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: case TOK_REDIRECT_APPEND:
new_io->io_mode = IO_FILE; new_io->io_mode = IO_FILE;
new_io->param2.flags = O_CREAT | O_APPEND | O_WRONLY; new_io->param2.flags = O_CREAT | O_APPEND | O_WRONLY;
new_io->filename = target; new_io->set_filename(target);
break; break;
case TOK_REDIRECT_OUT: case TOK_REDIRECT_OUT:
new_io->io_mode = IO_FILE; new_io->io_mode = IO_FILE;
new_io->param2.flags = O_CREAT | O_WRONLY | O_TRUNC; new_io->param2.flags = O_CREAT | O_WRONLY | O_TRUNC;
new_io->filename = target; new_io->set_filename(target);
break; break;
case TOK_REDIRECT_NOCLOB: case TOK_REDIRECT_NOCLOB:
new_io->io_mode = IO_FILE; new_io->io_mode = IO_FILE;
new_io->param2.flags = O_CREAT | O_EXCL | O_WRONLY; new_io->param2.flags = O_CREAT | O_EXCL | O_WRONLY;
new_io->filename = target; new_io->set_filename(target);
break; break;
case TOK_REDIRECT_IN: case TOK_REDIRECT_IN:
new_io->io_mode = IO_FILE; new_io->io_mode = IO_FILE;
new_io->param2.flags = O_RDONLY; new_io->param2.flags = O_RDONLY;
new_io->filename = target; new_io->set_filename(target);
break; break;
case TOK_REDIRECT_FD: case TOK_REDIRECT_FD:
@ -2311,15 +2311,15 @@ void parser_t::eval_job( tokenizer *tok )
if( newline ) if( newline )
stop_pos = mini( stop_pos, newline - tok_string(tok) ); 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 else
j->command = L""; j->set_command(L"");
if( profile ) if( profile )
{ {
t2 = get_time(); t2 = get_time();
profile_item->cmd = wcsdup( j->command_cstr() ); profile_item->cmd = wcsdup( j->command_wcstr() );
profile_item->skipped=current_block->skip; profile_item->skipped=current_block->skip;
} }

View file

@ -20,17 +20,27 @@
#define OPEN_MASK 0666 #define OPEN_MASK 0666
/** fork error message */ /** 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 */ /** 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 */ /** 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 */ /** 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. // 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 ) 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 ) 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. // 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, debug_safe( 1,
_( L"Could not send process %d, '%ls' in job %d, '%ls' from group %d to group %d" ), "Could not send process %s, '%s' in job %s, '%s' from group %s to group %s",
p->pid, pid_buff,
p->argv0(), p->argv0_cstr(),
j->job_id, job_id_buff,
j->command_cstr(), j->command_cstr(),
getpgid( p->pid), getpgid_buff,
j->pgid ); job_pgid_buff );
wperror( L"setpgid" ); wperror( L"setpgid" );
res = -1; 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 ) if( tcsetpgrp (0, j->pgid) && print_errors )
{ {
debug( 1, _( L"Could not send job %d ('%ls') to foreground" ), char job_id_buff[128];
j->job_id, format_long_safe(job_id_buff, j->job_id);
j->command_cstr() ); debug_safe( 1, "Could not send job %s ('%s') to foreground", job_id_buff, j->command_cstr() );
wperror( L"tcsetpgrp" ); wperror( L"tcsetpgrp" );
res = -1; res = -1;
} }
@ -102,9 +122,7 @@ static void free_fd( io_data_t *io, int fd )
{ {
if( errno != EINTR ) if( errno != EINTR )
{ {
debug( 1, debug_safe_int( 1, FD_ERROR, fd );
FD_ERROR,
fd );
wperror( L"dup" ); wperror( L"dup" );
FATAL_EXIT(); FATAL_EXIT();
} }
@ -161,7 +179,7 @@ static int handle_child_io( io_data_t *io )
{ {
if( close(io->fd) ) 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" ); wperror( L"close" );
} }
break; break;
@ -170,23 +188,18 @@ static int handle_child_io( io_data_t *io )
case IO_FILE: case IO_FILE:
{ {
// Here we definitely do not want to set CLO_EXEC because our child needs access // 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 ) io->param2.flags, OPEN_MASK ) )==-1 )
{ {
if( ( io->param2.flags & O_EXCL ) && if( ( io->param2.flags & O_EXCL ) &&
( errno ==EEXIST ) ) ( errno ==EEXIST ) )
{ {
debug( 1, debug_safe( 1, NOCLOB_ERROR, io->filename_cstr );
NOCLOB_ERROR,
io->filename.c_str() );
} }
else else
{ {
debug( 1, debug_safe( 1, FILE_ERROR, io->filename_cstr );
FILE_ERROR, perror( "open" );
io->filename.c_str() );
wperror( L"open" );
} }
return -1; return -1;
@ -201,10 +214,8 @@ static int handle_child_io( io_data_t *io )
if(dup2( tmp, io->fd ) == -1 ) if(dup2( tmp, io->fd ) == -1 )
{ {
debug( 1, debug_safe_int( 1, FD_ERROR, io->fd );
FD_ERROR, perror( "dup2" );
io->fd );
wperror( L"dup2" );
return -1; return -1;
} }
exec_close( tmp ); 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 ) if( dup2( io->param1.old_fd, io->fd ) == -1 )
{ {
debug( 1, debug_safe_int( 1, FD_ERROR, io->fd );
FD_ERROR,
io->fd );
wperror( L"dup2" ); wperror( L"dup2" );
return -1; 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 ) if( dup2( io->param1.pipe_fd[write_pipe], io->fd ) != io->fd )
{ {
debug( 1, PIPE_ERROR ); debug_safe( 1, LOCAL_PIPE_ERROR );
wperror( L"dup2" ); perror( "dup2" );
return -1; 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 setup_child_process( job_t *j, process_t *p )
{ {
int res=0; bool ok=true;
if( p ) 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 ); ok = (0 == handle_child_io( j->io ));
if( p != 0 && res ) if( p != 0 && ! ok )
{ {
exit_without_destructors( 1 ); exit_without_destructors( 1 );
} }
} }
/* Set the handling for job control signals back to the default. */ /* Set the handling for job control signals back to the default. */
if( !res ) if( ok )
{ {
signal_reset_handlers(); signal_reset_handlers();
} }
@ -300,7 +309,7 @@ int setup_child_process( job_t *j, process_t *p )
/* Remove all signal blocks */ /* Remove all signal blocks */
signal_unblock(); 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"); wperror (L"fork");
FATAL_EXIT(); FATAL_EXIT();
return 0; return 0;

View file

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

68
proc.h
View file

@ -130,7 +130,15 @@ class process_t
private: private:
null_terminated_array_t<wchar_t> argv_array; 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: public:
process_t() : process_t() :
@ -168,17 +176,32 @@ class process_t
/** Sets argv */ /** 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 */ /** Returns argv */
const wchar_t * const *get_argv(void) const { return argv_array.get(); } 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; } 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] */ /** 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! */ /** actual command to pass to exec in case of EXTERNAL or INTERNAL_EXEC. malloc'd! */
const wchar_t *actual_cmd; const wchar_t *actual_cmd;
@ -283,6 +306,20 @@ void release_job_id(job_id_t jobid);
class job_t 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: public:
job_t(job_id_t jobid) : job_t(job_id_t jobid) :
@ -307,16 +344,21 @@ class job_t
} }
release_job_id(job_id); release_job_id(job_id);
} }
/** /** Returns whether the command is empty. */
The original command which led to the creation of this bool command_is_empty() const { return command.empty(); }
job. It is used for displaying messages about job status
on the terminal.
*/
wcstring command;
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. 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) static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec)
{ {
ASSERT_IS_NOT_FORKED_CHILD();
cstring tmp = wcs2string(pathname); cstring tmp = wcs2string(pathname);
/* Prefer to use O_CLOEXEC. It has to both be defined and nonzero */ /* Prefer to use O_CLOEXEC. It has to both be defined and nonzero */
#ifdef O_CLOEXEC #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 // off the main thread, always use wopen_cloexec
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
ASSERT_IS_NOT_FORKED_CHILD();
return wopen_internal(pathname, flags, mode, false); return wopen_internal(pathname, flags, mode, false);
} }