diff --git a/builtin.cpp b/builtin.cpp index f16c065e4..57c53d793 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -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 ); diff --git a/builtin_jobs.cpp b/builtin_jobs.cpp index 957b54c66..904d62c79 100644 --- a/builtin_jobs.cpp +++ b/builtin_jobs.cpp @@ -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; } diff --git a/common.cpp b/common.cpp index 20840c4ad..bf47fabca 100644 --- a/common.cpp +++ b/common.cpp @@ -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)); diff --git a/common.h b/common.h index 7f943ac16..4c24afdbb 100644 --- a/common.h +++ b/common.h @@ -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 to a null_terminated_array_t */ null_terminated_array_t convert_wide_array_to_narrow(const null_terminated_array_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 */ diff --git a/event.cpp b/event.cpp index 4a9f9222b..a36b729ff 100644 --- a/event.cpp +++ b/event.cpp @@ -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 ); diff --git a/exec.cpp b/exec.cpp index 54ee809ae..111cb50b5 100644 --- a/exec.cpp +++ b/exec.cpp @@ -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 ) diff --git a/exec.h b/exec.h index 5d882a002..4fa4b10c6 100644 --- a/exec.h +++ b/exec.h @@ -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 ); diff --git a/expand.cpp b/expand.cpp index 4b2011954..047d02e11 100644 --- a/expand.cpp +++ b/expand.cpp @@ -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 ) { diff --git a/io.cpp b/io.cpp index 08bdf5d56..d238e1439 100644 --- a/io.cpp +++ b/io.cpp @@ -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 ); diff --git a/io.h b/io.h index 58fc1f883..df1cef9d6 100644 --- a/io.h +++ b/io.h @@ -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); @@ -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 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 diff --git a/parser.cpp b/parser.cpp index ac01b3715..5774f3274 100644 --- a/parser.cpp +++ b/parser.cpp @@ -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; } diff --git a/postfork.cpp b/postfork.cpp index 523a78164..cf9c931f6 100644 --- a/postfork.cpp +++ b/postfork.cpp @@ -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; diff --git a/proc.cpp b/proc.cpp index 7ad35bd4b..17a5fd9b9 100644 --- a/proc.cpp +++ b/proc.cpp @@ -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(); diff --git a/proc.h b/proc.h index 06757cae6..45de45a18 100644 --- a/proc.h +++ b/proc.h @@ -130,7 +130,15 @@ class process_t private: null_terminated_array_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 &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. diff --git a/wutil.cpp b/wutil.cpp index 7e4289316..da6649160 100644 --- a/wutil.cpp +++ b/wutil.cpp @@ -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); }