1.1 --- a/fsaccess/op_run.c Sun Jun 02 23:15:39 2024 +0200 1.2 +++ b/fsaccess/op_run.c Tue Jun 04 18:12:02 2024 +0200 1.3 @@ -46,6 +46,7 @@ 1.4 #define NUMBER_OF_JOBS 32 1.5 1.6 static file_t *readers[NUMBER_OF_JOBS] = {NULL}; 1.7 +static file_t *errors[NUMBER_OF_JOBS] = {NULL}; 1.8 static process_t *processes[NUMBER_OF_JOBS] = {NULL}; 1.9 static char *programs[NUMBER_OF_JOBS] = {NULL}; 1.10 static int next_job = 0; 1.11 @@ -72,6 +73,7 @@ 1.12 static void _remove_program(int job_number) 1.13 { 1.14 readers[job_number] = NULL; 1.15 + errors[job_number] = NULL; 1.16 processes[job_number] = NULL; 1.17 1.18 free(programs[job_number]); 1.19 @@ -124,8 +126,11 @@ 1.20 1.21 /* Wait for a program to finish, showing its output. */ 1.22 1.23 -static int _wait_program(file_t *reader, process_t *process) 1.24 +static int _wait_program(int job_number) 1.25 { 1.26 + file_t *reader = readers[job_number]; 1.27 + file_t *error = errors[job_number]; 1.28 + process_t *process = processes[job_number]; 1.29 notifier_t *notifier = client_notifier_task(); 1.30 notifiable_t *notifiable; 1.31 int exitcode; 1.32 @@ -144,12 +149,33 @@ 1.33 } 1.34 } 1.35 1.36 + if (error != NULL) 1.37 + { 1.38 + err = client_subscribe(error, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED, notifier); 1.39 + 1.40 + if (err) 1.41 + { 1.42 + printf("Could not subscribe to pipe notifications: %s\n", l4sys_errtostr(err)); 1.43 + 1.44 + if (reader != NULL) 1.45 + client_unsubscribe(reader, notifier); 1.46 + 1.47 + return -1; 1.48 + } 1.49 + } 1.50 + 1.51 err = notify_subscribe(process_notifiable(process), NOTIFY_TASK_ALL, notifier); 1.52 1.53 if (err) 1.54 { 1.55 printf("Could not subscribe to process notifications: %s\n", l4sys_errtostr(err)); 1.56 - client_unsubscribe(reader, notifier); 1.57 + 1.58 + if (reader != NULL) 1.59 + client_unsubscribe(reader, notifier); 1.60 + 1.61 + if (error != NULL) 1.62 + client_unsubscribe(error, notifier); 1.63 + 1.64 return -1; 1.65 } 1.66 1.67 @@ -166,15 +192,21 @@ 1.68 if (reader != NULL) 1.69 client_unsubscribe(reader, notifier); 1.70 1.71 + if (error != NULL) 1.72 + client_unsubscribe(error, notifier); 1.73 + 1.74 notify_unsubscribe(process_notifiable(process), notifier); 1.75 return -1; 1.76 } 1.77 1.78 - /* Handle input from the reader. */ 1.79 + /* Handle input from the readers. */ 1.80 1.81 if ((reader != NULL) && (file_notifications(reader) & (NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED))) 1.82 _show_output(reader); 1.83 1.84 + if ((error != NULL) && (file_notifications(error) & (NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED))) 1.85 + _show_output(error); 1.86 + 1.87 /* Handle process termination, obtaining the process state. */ 1.88 1.89 if (process_terminated(notifiable)) 1.90 @@ -182,6 +214,9 @@ 1.91 if ((reader != NULL) && ((process_t *) notifiable->base == process)) 1.92 _show_output(reader); 1.93 1.94 + if ((error != NULL) && ((process_t *) notifiable->base == process)) 1.95 + _show_output(error); 1.96 + 1.97 _show_status(notifiable); 1.98 1.99 /* If explicitly waiting for this process, remove it from the job list and 1.100 @@ -200,15 +235,21 @@ 1.101 if (reader != NULL) 1.102 client_unsubscribe(reader, notifier); 1.103 1.104 + if (error != NULL) 1.105 + client_unsubscribe(error, notifier); 1.106 + 1.107 notify_unsubscribe(process_notifiable(process), notifier); 1.108 1.109 - /* Close the process and pipe. */ 1.110 + /* Close the process and pipes. */ 1.111 1.112 process_free(process); 1.113 1.114 if (reader != NULL) 1.115 client_close(reader); 1.116 1.117 + if (error != NULL) 1.118 + client_close(error); 1.119 + 1.120 return exitcode; 1.121 } 1.122 1.123 @@ -218,6 +259,7 @@ 1.124 { 1.125 process_t *process; 1.126 file_t *output_reader, *output_writer; 1.127 + file_t *error_reader, *error_writer; 1.128 int last_job; 1.129 long err; 1.130 1.131 @@ -241,7 +283,7 @@ 1.132 } 1.133 } 1.134 1.135 - /* Create a pipe for process output. */ 1.136 + /* Create pipes for process output and errors. */ 1.137 1.138 err = client_pipe(&output_reader, &output_writer, O_NONBLOCK); 1.139 1.140 @@ -251,9 +293,18 @@ 1.141 return -1; 1.142 } 1.143 1.144 + err = client_pipe(&error_reader, &error_writer, O_NONBLOCK); 1.145 + 1.146 + if (err) 1.147 + { 1.148 + printf("Could not obtain pipe for errors: %s\n", l4sys_errtostr(err)); 1.149 + return -1; 1.150 + } 1.151 + 1.152 /* Start the process. */ 1.153 1.154 - err = process_spawn(argc, (const char **) argv, input_reader, output_writer, &process); 1.155 + err = process_spawn(argc, (const char **) argv, input_reader, output_writer, 1.156 + error_writer, &process); 1.157 1.158 if (err) 1.159 { 1.160 @@ -262,13 +313,15 @@ 1.161 return -1; 1.162 } 1.163 1.164 - /* Release the relinquished end of the output pipe. */ 1.165 + /* Release the relinquished end of the output and error pipes. */ 1.166 1.167 client_close(output_writer); 1.168 + client_close(error_writer); 1.169 1.170 /* Record the output stream, process and command details. */ 1.171 1.172 readers[next_job] = output_reader; 1.173 + errors[next_job] = error_reader; 1.174 processes[next_job] = process; 1.175 programs[next_job] = strdup(argv[0]); 1.176 1.177 @@ -353,7 +406,7 @@ 1.178 if (exitcode) 1.179 return exitcode; 1.180 1.181 - return _wait_program(readers[next_job], processes[next_job]); 1.182 + return _wait_program(next_job); 1.183 } 1.184 1.185 /* Show initiated programs. */ 1.186 @@ -405,7 +458,7 @@ 1.187 return -1; 1.188 } 1.189 1.190 - return _wait_program(readers[job_number], processes[job_number]); 1.191 + return _wait_program(job_number); 1.192 } 1.193 1.194 /* vim: tabstop=2 expandtab shiftwidth=2
2.1 --- a/libexec/include/exec/process_creating.h Sun Jun 02 23:15:39 2024 +0200 2.2 +++ b/libexec/include/exec/process_creating.h Tue Jun 04 18:12:02 2024 +0200 2.3 @@ -87,10 +87,11 @@ 2.4 long start_region_mapper(l4_cap_idx_t pager); 2.5 2.6 long start_program(l4_cap_idx_t monitor, int argc, const char *argv[], 2.7 - l4_cap_idx_t reader, l4_cap_idx_t writer); 2.8 + l4_cap_idx_t reader, l4_cap_idx_t writer, 2.9 + l4_cap_idx_t error); 2.10 2.11 long _start(int argc, const char *argv[], l4_cap_idx_t reader, 2.12 - l4_cap_idx_t writer, l4_cap_idx_t process); 2.13 + l4_cap_idx_t writer, l4_cap_idx_t error, l4_cap_idx_t process); 2.14 2.15 public: 2.16 explicit ProcessCreating(const char *rm_filename, file_t *rm_file); 2.17 @@ -98,7 +99,8 @@ 2.18 virtual long init_process_monitor(l4_cap_idx_t *monitor); 2.19 2.20 virtual long start(int argc, const char *argv[], l4_cap_idx_t reader, 2.21 - l4_cap_idx_t writer, l4_cap_idx_t process); 2.22 + l4_cap_idx_t writer, l4_cap_idx_t error, 2.23 + l4_cap_idx_t process); 2.24 }; 2.25 2.26 /* vim: tabstop=2 expandtab shiftwidth=2
3.1 --- a/libexec/include/exec/process_creator_context_resource.h Sun Jun 02 23:15:39 2024 +0200 3.2 +++ b/libexec/include/exec/process_creator_context_resource.h Tue Jun 04 18:12:02 2024 +0200 3.3 @@ -57,7 +57,7 @@ 3.4 /* Process creator context interface methods. */ 3.5 3.6 virtual long start(int argc, l4_cap_idx_t reader, l4_cap_idx_t writer, 3.7 - l4_cap_idx_t *process); 3.8 + l4_cap_idx_t error, l4_cap_idx_t *process); 3.9 3.10 /* Pager/dataspace methods. */ 3.11
4.1 --- a/libexec/include/exec/process_creator_resource.h Sun Jun 02 23:15:39 2024 +0200 4.2 +++ b/libexec/include/exec/process_creator_resource.h Tue Jun 04 18:12:02 2024 +0200 4.3 @@ -52,7 +52,8 @@ 4.4 virtual long init_process(l4_cap_idx_t *process); 4.5 4.6 virtual long start(int argc, const char *argv[], l4_cap_idx_t reader, 4.7 - l4_cap_idx_t writer, l4_cap_idx_t process); 4.8 + l4_cap_idx_t writer, l4_cap_idx_t error, 4.9 + l4_cap_idx_t process); 4.10 4.11 /* Opener interface methods. */ 4.12
5.1 --- a/libexec/lib/src/process_creating.cc Sun Jun 02 23:15:39 2024 +0200 5.2 +++ b/libexec/lib/src/process_creating.cc Tue Jun 04 18:12:02 2024 +0200 5.3 @@ -294,7 +294,7 @@ 5.4 5.5 long ProcessCreating::start_program(l4_cap_idx_t monitor, int argc, 5.6 const char *argv[], l4_cap_idx_t reader, 5.7 - l4_cap_idx_t writer) 5.8 + l4_cap_idx_t writer, l4_cap_idx_t error) 5.9 { 5.10 /* NOTE: Environment vector is currently not defined. */ 5.11 5.12 @@ -337,11 +337,12 @@ 5.13 l4_cap_idx_t pipeserver_cap = _process.allocate_cap(); 5.14 l4_cap_idx_t pipeserver = l4re_env_get_cap(ENV_PIPE_SERVER_NAME); 5.15 5.16 - /* Also reserve capabilities for the reader and writer. If the reader or 5.17 - writer are invalid capabilities, these will not actually be transferred. */ 5.18 + /* Also reserve capabilities for the reader and writers. If the reader or 5.19 + writers are invalid capabilities, these will not actually be transferred. */ 5.20 5.21 l4_cap_idx_t reader_cap = _process.allocate_cap(); 5.22 l4_cap_idx_t writer_cap = _process.allocate_cap(); 5.23 + l4_cap_idx_t error_cap = _process.allocate_cap(); 5.24 5.25 /* Define the capabilities to be mapped for the filesystem. */ 5.26 5.27 @@ -351,6 +352,7 @@ 5.28 {pipeserver_cap, pipeserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 5.29 {reader_cap, reader, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 5.30 {writer_cap, writer, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 5.31 + {error_cap, error, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 5.32 {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}, 5.33 }; 5.34 5.35 @@ -371,12 +373,16 @@ 5.36 if (l4_is_invalid_cap(writer)) 5.37 writer_cap = L4_INVALID_CAP; 5.38 5.39 + if (l4_is_invalid_cap(error)) 5.40 + error_cap = L4_INVALID_CAP; 5.41 + 5.42 l4re_env_cap_entry_t program_init_caps[] = { 5.43 l4re_env_cap_entry_t(ENV_FILESYSTEM_SERVER_NAME, fsserver_cap, L4_CAP_FPAGE_RWS), 5.44 l4re_env_cap_entry_t(ENV_PROCESS_SERVER_NAME, prserver_cap, L4_CAP_FPAGE_RWS), 5.45 l4re_env_cap_entry_t(ENV_PIPE_SERVER_NAME, pipeserver_cap, L4_CAP_FPAGE_RWS), 5.46 l4re_env_cap_entry_t(ENV_INPUT_STREAM_NAME, reader_cap, L4_CAP_FPAGE_R), 5.47 l4re_env_cap_entry_t(ENV_OUTPUT_STREAM_NAME, writer_cap, L4_CAP_FPAGE_W), 5.48 + l4re_env_cap_entry_t(ENV_ERROR_STREAM_NAME, error_cap, L4_CAP_FPAGE_W), 5.49 l4re_env_cap_entry_t() 5.50 }; 5.51 5.52 @@ -407,7 +413,8 @@ 5.53 process. */ 5.54 5.55 long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t reader, 5.56 - l4_cap_idx_t writer, l4_cap_idx_t process) 5.57 + l4_cap_idx_t writer, l4_cap_idx_t error, 5.58 + l4_cap_idx_t process) 5.59 { 5.60 /* Open the program file, handling any error conditions. If successfully 5.61 opened, it will be closed when the process terminates. */ 5.62 @@ -453,7 +460,7 @@ 5.63 if (err) 5.64 return err; 5.65 5.66 - err = start_program(process, argc, argv, reader, writer); 5.67 + err = start_program(process, argc, argv, reader, writer, error); 5.68 if (err) 5.69 return err; 5.70 5.71 @@ -479,16 +486,18 @@ 5.72 /* Start the given program, notifying the process monitor upon any error. */ 5.73 5.74 long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t reader, 5.75 - l4_cap_idx_t writer, l4_cap_idx_t process) 5.76 + l4_cap_idx_t writer, l4_cap_idx_t error, 5.77 + l4_cap_idx_t process) 5.78 { 5.79 std::lock_guard<std::mutex> guard(_lock); 5.80 5.81 - long err = _start(argc, argv, reader, writer, process); 5.82 + long err = _start(argc, argv, reader, writer, error, process); 5.83 5.84 /* Discard the reader and writer since they will not be used in this task. */ 5.85 5.86 ipc_cap_free_um(reader); 5.87 ipc_cap_free_um(writer); 5.88 + ipc_cap_free_um(error); 5.89 5.90 /* Communicate the error using the signal value. */ 5.91
6.1 --- a/libexec/lib/src/process_creator_context_resource.cc Sun Jun 02 23:15:39 2024 +0200 6.2 +++ b/libexec/lib/src/process_creator_context_resource.cc Tue Jun 04 18:12:02 2024 +0200 6.3 @@ -52,6 +52,7 @@ 6.4 6.5 long ProcessCreatorContextResource::start(int argc, l4_cap_idx_t reader, 6.6 l4_cap_idx_t writer, 6.7 + l4_cap_idx_t error, 6.8 l4_cap_idx_t *process) 6.9 { 6.10 /* Obtain the arguments by reading from the shared memory. */ 6.11 @@ -69,6 +70,8 @@ 6.12 if (argv[i] != NULL) 6.13 pos += strlen(argv[i]) + 1; 6.14 6.15 + /* NOTE: Debugging output. */ 6.16 + 6.17 printf("argv[%d] = %s\n", i, argv[i]); 6.18 } 6.19 6.20 @@ -93,7 +96,7 @@ 6.21 reply, so a notification is sent via the process monitor instead by the 6.22 process creator. */ 6.23 6.24 - _creator->start(argc, argv, reader, writer, *process); 6.25 + _creator->start(argc, argv, reader, writer, error, *process); 6.26 6.27 return IPC_MESSAGE_SENT; 6.28 }
7.1 --- a/libexec/lib/src/process_creator_resource.cc Sun Jun 02 23:15:39 2024 +0200 7.2 +++ b/libexec/lib/src/process_creator_resource.cc Tue Jun 04 18:12:02 2024 +0200 7.3 @@ -57,9 +57,9 @@ 7.4 7.5 long ProcessCreatorResource::start(int argc, const char *argv[], 7.6 l4_cap_idx_t reader, l4_cap_idx_t writer, 7.7 - l4_cap_idx_t process) 7.8 + l4_cap_idx_t error, l4_cap_idx_t process) 7.9 { 7.10 - return _creating.start(argc, argv, reader, writer, process); 7.11 + return _creating.start(argc, argv, reader, writer, error, process); 7.12 } 7.13 7.14
8.1 --- a/libfsclient/include/fsclient/process.h Sun Jun 02 23:15:39 2024 +0200 8.2 +++ b/libfsclient/include/fsclient/process.h Tue Jun 04 18:12:02 2024 +0200 8.3 @@ -53,9 +53,12 @@ 8.4 long process_error(process_t *process); 8.5 void process_free(process_t *process); 8.6 void process_init(process_t *process); 8.7 -long process_spawn(int argc, const char *argv[], file_t *reader, file_t *writer, process_t **process); 8.8 -long process_start(process_t *process, int argc, const char *argv[], file_t *reader, file_t *writer); 8.9 -long process_wait(process_t *process, notify_flags_t *flags, notify_values_t *values); 8.10 +long process_spawn(int argc, const char *argv[], file_t *reader, file_t *writer, 8.11 + file_t *error, process_t **process); 8.12 +long process_start(process_t *process, int argc, const char *argv[], 8.13 + file_t *reader, file_t *writer, file_t *error); 8.14 +long process_wait(process_t *process, notify_flags_t *flags, 8.15 + notify_values_t *values); 8.16 8.17 /* Notification support. */ 8.18
9.1 --- a/libfsclient/lib/src/process.cc Sun Jun 02 23:15:39 2024 +0200 9.2 +++ b/libfsclient/lib/src/process.cc Tue Jun 04 18:12:02 2024 +0200 9.3 @@ -101,23 +101,22 @@ 9.4 /* A convenience function for creating and starting a process. */ 9.5 9.6 long process_spawn(int argc, const char *argv[], file_t *reader, 9.7 - file_t *writer, process_t **process) 9.8 + file_t *writer, file_t *error, process_t **process) 9.9 { 9.10 *process = process_new(); 9.11 9.12 /* Start the process with the given arguments. */ 9.13 9.14 if (*process != NULL) 9.15 - return process_start(*process, argc, argv, reader, writer); 9.16 + return process_start(*process, argc, argv, reader, writer, error); 9.17 else 9.18 return -L4_ENOMEM; 9.19 } 9.20 9.21 -/* Start a process using the given arguments. 9.22 - NOTE: This does not yet employ a pipe for the process's input stream. */ 9.23 +/* Start a process using the given arguments. */ 9.24 9.25 long process_start(process_t *process, int argc, const char *argv[], 9.26 - file_t *reader, file_t *writer) 9.27 + file_t *reader, file_t *writer, file_t *error) 9.28 { 9.29 l4_cap_idx_t server = l4re_env_get_cap(ENV_PROCESS_SERVER_NAME); 9.30 9.31 @@ -151,6 +150,7 @@ 9.32 err = creator.start(argc, 9.33 reader != NULL ? reader->ref : (l4_cap_idx_t) L4_INVALID_CAP, 9.34 writer != NULL ? writer->ref : (l4_cap_idx_t) L4_INVALID_CAP, 9.35 + error != NULL ? error->ref : (l4_cap_idx_t) L4_INVALID_CAP, 9.36 &process->ref); 9.37 9.38 /* Initialise the notifiable section of the structure. */
10.1 --- a/libsystypes/idl/process_creator_context.idl Sun Jun 02 23:15:39 2024 +0200 10.2 +++ b/libsystypes/idl/process_creator_context.idl Tue Jun 04 18:12:02 2024 +0200 10.3 @@ -6,9 +6,10 @@ 10.4 /* Start a process, using the given argument count to refer to the process 10.5 arguments supplied via the dataspace, including the program itself. 10.6 10.7 - A reader pipe capability and a writer pipe capability are to be provided 10.8 - for the process's input and output respectively, and the process capability 10.9 - is returned. */ 10.10 + A reader pipe capability, a writer pipe capability and an error (writer) 10.11 + pipe capability are to be provided for the process's input, output and 10.12 + error streams respectively, and the process capability is returned. */ 10.13 10.14 - [opcode(30)] void start(in int argc, in cap reader, in cap writer, out cap process); 10.15 + [opcode(30)] void start(in int argc, in cap reader, in cap writer, 10.16 + in cap error, out cap process); 10.17 };
11.1 --- a/test_files/programs/clip.c Sun Jun 02 23:15:39 2024 +0200 11.2 +++ b/test_files/programs/clip.c Tue Jun 04 18:12:02 2024 +0200 11.3 @@ -110,7 +110,7 @@ 11.4 11.5 if (file == NULL) 11.6 { 11.7 - fprintf(stdout, "Could not open file.\n"); 11.8 + fprintf(stderr, "Could not open file.\n"); 11.9 return 1; 11.10 } 11.11 11.12 @@ -133,7 +133,7 @@ 11.13 11.14 /* Otherwise, indicate that end-of-file occurred. */ 11.15 11.16 - fprintf(stdout, "EOF error at line %d.\n", lineno); 11.17 + fprintf(stderr, "EOF error at line %d.\n", lineno); 11.18 return 1; 11.19 } 11.20
12.1 --- a/test_files/programs/ls.c Sun Jun 02 23:15:39 2024 +0200 12.2 +++ b/test_files/programs/ls.c Tue Jun 04 18:12:02 2024 +0200 12.3 @@ -117,7 +117,7 @@ 12.4 /* NOTE: Should write via a standard error stream provided in the same 12.5 way as the output stream. */ 12.6 12.7 - fprintf(stdout, "Failed to list object: %s\n", path); 12.8 + fprintf(stderr, "Failed to list object: %s\n", path); 12.9 return 1; 12.10 } 12.11
13.1 --- a/tests/dstest_exec.cc Sun Jun 02 23:15:39 2024 +0200 13.2 +++ b/tests/dstest_exec.cc Tue Jun 04 18:12:02 2024 +0200 13.3 @@ -65,7 +65,7 @@ 13.4 13.5 /* Start the process. */ 13.6 13.7 - err = process_spawn(argc, argv, input_reader, output_writer, &process); 13.8 + err = process_spawn(argc, argv, input_reader, output_writer, NULL, &process); 13.9 13.10 if (err) 13.11 {
14.1 --- a/tests/dstest_exec_many.cc Sun Jun 02 23:15:39 2024 +0200 14.2 +++ b/tests/dstest_exec_many.cc Tue Jun 04 18:12:02 2024 +0200 14.3 @@ -49,7 +49,7 @@ 14.4 14.5 process_t *process; 14.6 14.7 - err = process_spawn(argc - 2, (const char **) argv + 2, NULL, NULL, &process); 14.8 + err = process_spawn(argc - 2, (const char **) argv + 2, NULL, NULL, NULL, &process); 14.9 14.10 if (err) 14.11 {