1.1 --- a/fsaccess/fsaccess.c Wed Jan 24 21:12:41 2024 +0100 1.2 +++ b/fsaccess/fsaccess.c Wed Jan 24 21:17:37 2024 +0100 1.3 @@ -53,7 +53,9 @@ 1.4 script Read operations from a script file (or stdin)\n\ 1.5 Execution operations:\n\ 1.6 \n\ 1.7 + file Send a file to a program\n\ 1.8 jobs Show initiated programs\n\ 1.9 + pipe Connect a running program to a new program\n\ 1.10 run Run a program from the filesystem\n\ 1.11 wait Wait for a program to finish\n\ 1.12 "; 1.13 @@ -62,9 +64,11 @@ 1.14 1.15 struct operation operations[] = { 1.16 {"copy-in", copy_in}, 1.17 + {"file", file_to_program}, 1.18 {"jobs", show_programs}, 1.19 {"ls", list_objects}, 1.20 {"mkdir", make_dirs}, 1.21 + {"pipe", pipe_to_program}, 1.22 {"rm", remove_non_dirs}, 1.23 {"rmdir", remove_dirs}, 1.24 {"run", run_program},
2.1 --- a/fsaccess/op_run.c Wed Jan 24 21:12:41 2024 +0100 2.2 +++ b/fsaccess/op_run.c Wed Jan 24 21:17:37 2024 +0100 2.3 @@ -47,68 +47,131 @@ 2.4 2.5 static file_t *readers[NUMBER_OF_JOBS] = {NULL}; 2.6 static process_t *processes[NUMBER_OF_JOBS] = {NULL}; 2.7 -static const char *programs[NUMBER_OF_JOBS] = {NULL}; 2.8 +static char *programs[NUMBER_OF_JOBS] = {NULL}; 2.9 static int next_job = 0; 2.10 2.11 2.12 2.13 +/* Show output from a program. */ 2.14 + 2.15 +static void _show_output(file_t *reader) 2.16 +{ 2.17 + char buffer[TO_TRANSFER]; 2.18 + offset_t nread; 2.19 + 2.20 + while ((nread = client_read(reader, buffer, TO_TRANSFER))) 2.21 + fwrite(buffer, sizeof(char), nread, stdout); 2.22 +} 2.23 + 2.24 /* Wait for a program to finish, showing its output. */ 2.25 2.26 static int _wait_program(file_t *reader, process_t *process) 2.27 { 2.28 - char buffer[TO_TRANSFER]; 2.29 - notify_flags_t flags; 2.30 - notify_values_t values; 2.31 + notifier_t *notifier = client_notifier_local(); 2.32 + notifiable_t *notifiable; 2.33 + int exitcode; 2.34 long err; 2.35 2.36 - /* Read until the pipe yields no more data. */ 2.37 + /* Subscribe to reader and process notifications. */ 2.38 + 2.39 + if (reader != NULL) 2.40 + { 2.41 + err = client_subscribe(reader, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED, notifier); 2.42 + 2.43 + if (err) 2.44 + { 2.45 + printf("Could not subscribe to pipe notifications: %s\n", l4sys_errtostr(err)); 2.46 + client_notifier_close(notifier); 2.47 + return -1; 2.48 + } 2.49 + } 2.50 + 2.51 + err = notify_subscribe(process_notifiable(process), NOTIFY_TASK_ALL, notifier); 2.52 + 2.53 + if (err) 2.54 + { 2.55 + printf("Could not subscribe to process notifications: %s\n", l4sys_errtostr(err)); 2.56 + client_unsubscribe(reader, notifier); 2.57 + client_notifier_close(notifier); 2.58 + return -1; 2.59 + } 2.60 + 2.61 + /* Read from and write to pipes until the program terminates. */ 2.62 2.63 while (1) 2.64 { 2.65 - offset_t nread = client_read(reader, buffer, TO_TRANSFER); 2.66 + err = notify_wait_many(¬ifiable, notifier); 2.67 + 2.68 + if (err) 2.69 + { 2.70 + printf("Notification error: %s\n", l4sys_errtostr(err)); 2.71 + 2.72 + if (reader != NULL) 2.73 + client_unsubscribe(reader, notifier); 2.74 + 2.75 + notify_unsubscribe(process_notifiable(process), notifier); 2.76 + client_notifier_close(notifier); 2.77 + return -1; 2.78 + } 2.79 2.80 - if (nread) 2.81 - fwrite(buffer, sizeof(char), nread, stdout); 2.82 - else 2.83 + /* Handle input from the reader. */ 2.84 + 2.85 + if ((reader != NULL) && (file_notifications(reader) & (NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED))) 2.86 + _show_output(reader); 2.87 + 2.88 + /* Handle process termination, obtaining the process state. */ 2.89 + 2.90 + if (process_terminated(notifiable)) 2.91 + { 2.92 + if (reader != NULL) 2.93 + _show_output(reader); 2.94 + 2.95 + printf("End process (flags %" pFMTnotify_flags "x values: %ld, %ld)\n", 2.96 + notifiable->notifications, notifiable->values.sig, notifiable->values.val); 2.97 break; 2.98 + } 2.99 } 2.100 2.101 - /* Close the pipe and obtain the process state. */ 2.102 + exitcode = notifiable->values.val; 2.103 2.104 - client_close(reader); 2.105 + if (reader != NULL) 2.106 + client_unsubscribe(reader, notifier); 2.107 2.108 - err = process_wait(process, &flags, &values); 2.109 + notify_unsubscribe(process_notifiable(process), notifier); 2.110 2.111 - printf("End process (flags %" pFMTnotify_flags "x values: %ld, %ld)\n", flags, values.sig, values.val); 2.112 + /* Close the process and pipe. */ 2.113 + 2.114 + client_notifier_close(notifier); 2.115 + process_free(process); 2.116 2.117 - if (err) 2.118 - return -1; 2.119 + if (reader != NULL) 2.120 + client_close(reader); 2.121 2.122 - return values.val; 2.123 + return exitcode; 2.124 } 2.125 2.126 /* Run the given program. */ 2.127 2.128 -int run_program(int argc, char *argv[]) 2.129 +static int _run_program(int argc, char *argv[], file_t *input_reader) 2.130 { 2.131 process_t *process; 2.132 - file_t *reader, *writer; 2.133 + file_t *output_reader, *output_writer; 2.134 int last_job; 2.135 long err; 2.136 2.137 /* Create a pipe for process output. */ 2.138 2.139 - err = client_pipe(&reader, &writer, 0); 2.140 + err = client_pipe(&output_reader, &output_writer, O_NONBLOCK); 2.141 2.142 if (err) 2.143 { 2.144 - printf("Could not obtain pipe: %s\n", l4sys_errtostr(err)); 2.145 + printf("Could not obtain pipe for output: %s\n", l4sys_errtostr(err)); 2.146 return -1; 2.147 } 2.148 2.149 /* Start the process. */ 2.150 2.151 - err = process_spawn(argc, (const char **) argv, writer, &process); 2.152 + err = process_spawn(argc, (const char **) argv, input_reader, output_writer, &process); 2.153 2.154 if (err) 2.155 { 2.156 @@ -118,15 +181,15 @@ 2.157 2.158 printf("Finished program initiation.\n"); 2.159 2.160 - /* Release the writing end of the pipe. */ 2.161 + /* Release the relinquished end of the pipe. */ 2.162 2.163 - client_close(writer); 2.164 + client_close(output_writer); 2.165 2.166 /* Record the output stream. */ 2.167 2.168 last_job = next_job; 2.169 2.170 - while (readers[next_job] != NULL) 2.171 + while (processes[next_job] != NULL) 2.172 { 2.173 next_job++; 2.174 2.175 @@ -136,16 +199,79 @@ 2.176 /* Wait for the process to complete if no more job slots are available. */ 2.177 2.178 if (next_job == last_job) 2.179 - return _wait_program(reader, process); 2.180 + return _wait_program(output_reader, process); 2.181 } 2.182 2.183 - readers[next_job] = reader; 2.184 + readers[next_job] = output_reader; 2.185 processes[next_job] = process; 2.186 programs[next_job] = strdup(argv[0]); 2.187 2.188 return 0; 2.189 } 2.190 2.191 + 2.192 + 2.193 +/* Run the given program, providing input from a file. */ 2.194 + 2.195 +int file_to_program(int argc, char *argv[]) 2.196 +{ 2.197 + file_t *reader; 2.198 + 2.199 + /* Obtain a file reader and run the program with this as its input reader. */ 2.200 + 2.201 + if (argc < 1) 2.202 + return -1; 2.203 + 2.204 + reader = client_open(argv[0], O_RDONLY); 2.205 + 2.206 + if (!client_opened(reader)) 2.207 + { 2.208 + printf("Could not open file: %s\n", argv[0]); 2.209 + return -1; 2.210 + } 2.211 + 2.212 + return _run_program(argc - 1, &argv[1], reader); 2.213 +} 2.214 + 2.215 +/* Run the given program, connecting input from another program. */ 2.216 + 2.217 +int pipe_to_program(int argc, char *argv[]) 2.218 +{ 2.219 + int job_number; 2.220 + int exitcode; 2.221 + 2.222 + /* Obtain the job number for the output writer and run the program with this 2.223 + as its input reader. */ 2.224 + 2.225 + if (argc < 1) 2.226 + return -1; 2.227 + 2.228 + job_number = atoi(argv[0]); 2.229 + 2.230 + if (readers[job_number] == NULL) 2.231 + { 2.232 + printf("No output available for this job: %s\n", argv[0]); 2.233 + return -1; 2.234 + } 2.235 + 2.236 + exitcode = _run_program(argc - 1, &argv[1], readers[job_number]); 2.237 + 2.238 + /* Remove the job's reader to prevent direct access to it and to allow it to 2.239 + be closed by the receiving program. */ 2.240 + 2.241 + client_close(readers[job_number]); 2.242 + readers[job_number] = NULL; 2.243 + 2.244 + return exitcode; 2.245 +} 2.246 + 2.247 +/* Run the given program. */ 2.248 + 2.249 +int run_program(int argc, char *argv[]) 2.250 +{ 2.251 + return _run_program(argc, argv, NULL); 2.252 +} 2.253 + 2.254 /* Show initiated programs. */ 2.255 2.256 int show_programs(int argc, char *argv[]) 2.257 @@ -156,8 +282,9 @@ 2.258 2.259 for (job_number = 0; job_number < NUMBER_OF_JOBS; job_number++) 2.260 { 2.261 - if (readers[job_number] != NULL) 2.262 - printf("[%d] %s\n", job_number, programs[job_number]); 2.263 + if (processes[job_number] != NULL) 2.264 + printf("[%d] %s%s\n", job_number, programs[job_number], 2.265 + readers[job_number] != NULL ? " [!]" : ""); 2.266 } 2.267 2.268 return 0; 2.269 @@ -175,8 +302,11 @@ 2.270 2.271 job_number = atoi(argv[0]); 2.272 2.273 - if (readers[job_number] == NULL) 2.274 + if (processes[job_number] == NULL) 2.275 + { 2.276 + printf("No such job: %s\n", argv[0]); 2.277 return -1; 2.278 + } 2.279 2.280 exitcode = _wait_program(readers[job_number], processes[job_number]); 2.281
3.1 --- a/fsaccess/ops.h Wed Jan 24 21:12:41 2024 +0100 3.2 +++ b/fsaccess/ops.h Wed Jan 24 21:17:37 2024 +0100 3.3 @@ -46,8 +46,10 @@ 3.4 3.5 int copy_in(int argc, char *argv[]); 3.6 int copy_out(int argc, char *argv[]); 3.7 +int file_to_program(int argc, char *argv[]); 3.8 int list_objects(int argc, char *argv[]); 3.9 int make_dirs(int argc, char *argv[]); 3.10 +int pipe_to_program(int argc, char *argv[]); 3.11 int remove_dirs(int argc, char *argv[]); 3.12 int remove_non_dirs(int argc, char *argv[]); 3.13 int run_program(int argc, char *argv[]);
4.1 --- a/libexec/include/exec/process_creating.h Wed Jan 24 21:12:41 2024 +0100 4.2 +++ b/libexec/include/exec/process_creating.h Wed Jan 24 21:17:37 2024 +0100 4.3 @@ -1,7 +1,7 @@ 4.4 /* 4.5 * Support for executing code in new tasks and threads. 4.6 * 4.7 - * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk> 4.8 + * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 4.9 * 4.10 * This program is free software; you can redistribute it and/or 4.11 * modify it under the terms of the GNU General Public License as 4.12 @@ -87,18 +87,18 @@ 4.13 long start_region_mapper(l4_cap_idx_t pager); 4.14 4.15 long start_program(l4_cap_idx_t monitor, int argc, const char *argv[], 4.16 - l4_cap_idx_t writer); 4.17 + l4_cap_idx_t reader, l4_cap_idx_t writer); 4.18 4.19 - long _start(int argc, const char *argv[], l4_cap_idx_t writer, 4.20 - l4_cap_idx_t process); 4.21 + long _start(int argc, const char *argv[], l4_cap_idx_t reader, 4.22 + l4_cap_idx_t writer, l4_cap_idx_t process); 4.23 4.24 public: 4.25 explicit ProcessCreating(const char *rm_filename, file_t *rm_file); 4.26 4.27 virtual long init_process_monitor(l4_cap_idx_t *monitor); 4.28 4.29 - virtual long start(int argc, const char *argv[], l4_cap_idx_t writer, 4.30 - l4_cap_idx_t process); 4.31 + virtual long start(int argc, const char *argv[], l4_cap_idx_t reader, 4.32 + l4_cap_idx_t writer, l4_cap_idx_t process); 4.33 }; 4.34 4.35 /* vim: tabstop=2 expandtab shiftwidth=2
5.1 --- a/libexec/include/exec/process_creator_context_resource.h Wed Jan 24 21:12:41 2024 +0100 5.2 +++ b/libexec/include/exec/process_creator_context_resource.h Wed Jan 24 21:17:37 2024 +0100 5.3 @@ -1,7 +1,7 @@ 5.4 /* 5.5 * Support for creating a new process. 5.6 * 5.7 - * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk> 5.8 + * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 5.9 * 5.10 * This program is free software; you can redistribute it and/or 5.11 * modify it under the terms of the GNU General Public License as 5.12 @@ -56,7 +56,8 @@ 5.13 5.14 /* Process creator context interface methods. */ 5.15 5.16 - virtual long start(int argc, l4_cap_idx_t writer, l4_cap_idx_t *process); 5.17 + virtual long start(int argc, l4_cap_idx_t reader, l4_cap_idx_t writer, 5.18 + l4_cap_idx_t *process); 5.19 5.20 /* Pager/dataspace methods. */ 5.21
6.1 --- a/libexec/include/exec/process_creator_resource.h Wed Jan 24 21:12:41 2024 +0100 6.2 +++ b/libexec/include/exec/process_creator_resource.h Wed Jan 24 21:17:37 2024 +0100 6.3 @@ -1,7 +1,7 @@ 6.4 /* 6.5 * Support for executing code in new tasks and threads. 6.6 * 6.7 - * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk> 6.8 + * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 6.9 * 6.10 * This program is free software; you can redistribute it and/or 6.11 * modify it under the terms of the GNU General Public License as 6.12 @@ -51,8 +51,8 @@ 6.13 6.14 virtual long init_process(l4_cap_idx_t *process); 6.15 6.16 - virtual long start(int argc, const char *argv[], l4_cap_idx_t writer, 6.17 - l4_cap_idx_t process); 6.18 + virtual long start(int argc, const char *argv[], l4_cap_idx_t reader, 6.19 + l4_cap_idx_t writer, l4_cap_idx_t process); 6.20 6.21 /* Opener interface methods. */ 6.22
7.1 --- a/libexec/lib/src/process_creating.cc Wed Jan 24 21:12:41 2024 +0100 7.2 +++ b/libexec/lib/src/process_creating.cc Wed Jan 24 21:17:37 2024 +0100 7.3 @@ -284,7 +284,8 @@ 7.4 thread. */ 7.5 7.6 long ProcessCreating::start_program(l4_cap_idx_t monitor, int argc, 7.7 - const char *argv[], l4_cap_idx_t writer) 7.8 + const char *argv[], l4_cap_idx_t reader, 7.9 + l4_cap_idx_t writer) 7.10 { 7.11 /* NOTE: Environment vector is currently not defined. */ 7.12 7.13 @@ -317,14 +318,17 @@ 7.14 l4_cap_idx_t fsserver_cap = _process.allocate_cap(); 7.15 l4_cap_idx_t fsserver = l4re_env_get_cap(ENV_FILESYSTEM_SERVER_NAME); 7.16 7.17 - /* Also reserve a capability for the writer. */ 7.18 + /* Also reserve capabilities for the reader and writer. If the reader or 7.19 + writer are invalid capabilities, these will not actually be transferred. */ 7.20 7.21 + l4_cap_idx_t reader_cap = _process.allocate_cap(); 7.22 l4_cap_idx_t writer_cap = _process.allocate_cap(); 7.23 7.24 /* Define the capabilities to be mapped for the filesystem. */ 7.25 7.26 struct ipc_mapped_cap program_mapped_caps[] = { 7.27 {fsserver_cap, fsserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 7.28 + {reader_cap, reader, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 7.29 {writer_cap, writer, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, 7.30 {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}, 7.31 }; 7.32 @@ -340,8 +344,15 @@ 7.33 l4re_env API. Each capability index is assigned above when mapping the 7.34 capability and encoded in the entry below. */ 7.35 7.36 + if (l4_is_invalid_cap(reader)) 7.37 + reader_cap = L4_INVALID_CAP; 7.38 + 7.39 + if (l4_is_invalid_cap(writer)) 7.40 + writer_cap = L4_INVALID_CAP; 7.41 + 7.42 l4re_env_cap_entry_t program_init_caps[] = { 7.43 l4re_env_cap_entry_t(ENV_FILESYSTEM_SERVER_NAME, fsserver_cap, L4_CAP_FPAGE_RWS), 7.44 + l4re_env_cap_entry_t(ENV_INPUT_STREAM_NAME, reader_cap, L4_CAP_FPAGE_R), 7.45 l4re_env_cap_entry_t(ENV_OUTPUT_STREAM_NAME, writer_cap, L4_CAP_FPAGE_W), 7.46 l4re_env_cap_entry_t() 7.47 }; 7.48 @@ -368,12 +379,12 @@ 7.49 } 7.50 7.51 /* Start a new process for the payload indicated by the first of the given 7.52 - program arguments, employing the given writer pipe, and returning a 7.53 - reference to the process monitor as an object for interacting with the 7.54 + program arguments, employing the given reader and writer pipes, and returning 7.55 + a reference to the process monitor as an object for interacting with the 7.56 process. */ 7.57 7.58 -long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t writer, 7.59 - l4_cap_idx_t process) 7.60 +long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t reader, 7.61 + l4_cap_idx_t writer, l4_cap_idx_t process) 7.62 { 7.63 /* Open the program file, handling any error conditions. If successfully 7.64 opened, it will be closed when the process terminates. */ 7.65 @@ -419,7 +430,7 @@ 7.66 if (err) 7.67 return err; 7.68 7.69 - err = start_program(process, argc, argv, writer); 7.70 + err = start_program(process, argc, argv, reader, writer); 7.71 if (err) 7.72 return err; 7.73 7.74 @@ -444,15 +455,16 @@ 7.75 7.76 /* Start the given program, notifying the process monitor upon any error. */ 7.77 7.78 -long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t writer, 7.79 - l4_cap_idx_t process) 7.80 +long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t reader, 7.81 + l4_cap_idx_t writer, l4_cap_idx_t process) 7.82 { 7.83 std::lock_guard<std::mutex> guard(_lock); 7.84 7.85 - long err = _start(argc, argv, writer, process); 7.86 + long err = _start(argc, argv, reader, writer, process); 7.87 7.88 - /* Discard the writer since it will not be used in this task. */ 7.89 + /* Discard the reader and writer since they will not be used in this task. */ 7.90 7.91 + ipc_cap_free_um(reader); 7.92 ipc_cap_free_um(writer); 7.93 7.94 /* Communicate the error using the signal value. */
8.1 --- a/libexec/lib/src/process_creator_context_resource.cc Wed Jan 24 21:12:41 2024 +0100 8.2 +++ b/libexec/lib/src/process_creator_context_resource.cc Wed Jan 24 21:17:37 2024 +0100 8.3 @@ -1,7 +1,7 @@ 8.4 /* 8.5 * A resource offering support for creating processes. 8.6 * 8.7 - * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk> 8.8 + * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk> 8.9 * 8.10 * This program is free software; you can redistribute it and/or 8.11 * modify it under the terms of the GNU General Public License as 8.12 @@ -50,7 +50,8 @@ 8.13 8.14 /* ProcessCreatorContext interface methods. */ 8.15 8.16 -long ProcessCreatorContextResource::start(int argc, l4_cap_idx_t writer, 8.17 +long ProcessCreatorContextResource::start(int argc, l4_cap_idx_t reader, 8.18 + l4_cap_idx_t writer, 8.19 l4_cap_idx_t *process) 8.20 { 8.21 /* Obtain the arguments by reading from the shared memory. */ 8.22 @@ -58,6 +59,9 @@ 8.23 const char *argv[argc]; 8.24 offset_t pos = 0; 8.25 8.26 + if (!argc) 8.27 + return -L4_EINVAL; 8.28 + 8.29 for (int i = 0; i < argc; i++) 8.30 { 8.31 argv[i] = get_string(pos); 8.32 @@ -89,7 +93,7 @@ 8.33 reply, so a notification is sent via the process monitor instead by the 8.34 process creator. */ 8.35 8.36 - _creator->start(argc, argv, writer, *process); 8.37 + _creator->start(argc, argv, reader, writer, *process); 8.38 8.39 return IPC_MESSAGE_SENT; 8.40 }
9.1 --- a/libexec/lib/src/process_creator_resource.cc Wed Jan 24 21:12:41 2024 +0100 9.2 +++ b/libexec/lib/src/process_creator_resource.cc Wed Jan 24 21:17:37 2024 +0100 9.3 @@ -1,7 +1,7 @@ 9.4 /* 9.5 * A resource offering support for creating processes. 9.6 * 9.7 - * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk> 9.8 + * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk> 9.9 * 9.10 * This program is free software; you can redistribute it and/or 9.11 * modify it under the terms of the GNU General Public License as 9.12 @@ -56,9 +56,10 @@ 9.13 /* Start the new process, obtaining a reference to it. */ 9.14 9.15 long ProcessCreatorResource::start(int argc, const char *argv[], 9.16 - l4_cap_idx_t writer, l4_cap_idx_t process) 9.17 + l4_cap_idx_t reader, l4_cap_idx_t writer, 9.18 + l4_cap_idx_t process) 9.19 { 9.20 - return _creating.start(argc, argv, writer, process); 9.21 + return _creating.start(argc, argv, reader, writer, process); 9.22 } 9.23 9.24
10.1 --- a/libfsclient/include/fsclient/process.h Wed Jan 24 21:12:41 2024 +0100 10.2 +++ b/libfsclient/include/fsclient/process.h Wed Jan 24 21:17:37 2024 +0100 10.3 @@ -53,8 +53,8 @@ 10.4 long process_error(process_t *process); 10.5 void process_free(process_t *process); 10.6 void process_init(process_t *process); 10.7 -long process_spawn(int argc, const char *argv[], file_t *writer, process_t **process); 10.8 -long process_start(process_t *process, int argc, const char *argv[], file_t *writer); 10.9 +long process_spawn(int argc, const char *argv[], file_t *reader, file_t *writer, process_t **process); 10.10 +long process_start(process_t *process, int argc, const char *argv[], file_t *reader, file_t *writer); 10.11 long process_wait(process_t *process, notify_flags_t *flags, notify_values_t *values); 10.12 10.13 /* Notification support. */ 10.14 @@ -63,6 +63,8 @@ 10.15 notify_flags_t process_notifications(process_t *process); 10.16 notify_values_t process_notification_values(process_t *process); 10.17 10.18 +int process_terminated(notifiable_t *notifiable); 10.19 + 10.20 long process_notify_wait_process(process_t *process, notifier_t *notifier); 10.21 long process_notify_wait_processes(process_t **process, notifier_t *notifier); 10.22
11.1 --- a/libfsclient/lib/src/process.cc Wed Jan 24 21:12:41 2024 +0100 11.2 +++ b/libfsclient/lib/src/process.cc Wed Jan 24 21:17:37 2024 +0100 11.3 @@ -34,17 +34,6 @@ 11.4 11.5 11.6 11.7 -/* Utility functions. */ 11.8 - 11.9 -static bool _process_terminated(notifiable_t *notifiable) 11.10 -{ 11.11 - return ((notifiable->notifications & NOTIFY_TASK_SIGNAL) && 11.12 - (notifiable->values.sig == 0)) || 11.13 - (notifiable->notifications & NOTIFY_TASK_ERROR); 11.14 -} 11.15 - 11.16 - 11.17 - 11.18 /* Create a new process object. */ 11.19 11.20 process_t *process_new() 11.21 @@ -111,15 +100,15 @@ 11.22 11.23 /* A convenience function for creating and starting a process. */ 11.24 11.25 -long process_spawn(int argc, const char *argv[], file_t *writer, 11.26 - process_t **process) 11.27 +long process_spawn(int argc, const char *argv[], file_t *reader, 11.28 + file_t *writer, process_t **process) 11.29 { 11.30 *process = process_new(); 11.31 11.32 /* Start the process with the given arguments. */ 11.33 11.34 if (*process != NULL) 11.35 - return process_start(*process, argc, argv, writer); 11.36 + return process_start(*process, argc, argv, reader, writer); 11.37 else 11.38 return -L4_ENOMEM; 11.39 } 11.40 @@ -128,7 +117,7 @@ 11.41 NOTE: This does not yet employ a pipe for the process's input stream. */ 11.42 11.43 long process_start(process_t *process, int argc, const char *argv[], 11.44 - file_t *writer) 11.45 + file_t *reader, file_t *writer) 11.46 { 11.47 l4_cap_idx_t server = l4re_env_get_cap(ENV_PROCESS_SERVER_NAME); 11.48 11.49 @@ -159,7 +148,9 @@ 11.50 11.51 /* Start the process, obtaining a reference to it. */ 11.52 11.53 - err = creator.start(argc, writer != NULL ? writer->ref : (l4_cap_idx_t) L4_INVALID_CAP, 11.54 + err = creator.start(argc, 11.55 + reader != NULL ? reader->ref : (l4_cap_idx_t) L4_INVALID_CAP, 11.56 + writer != NULL ? writer->ref : (l4_cap_idx_t) L4_INVALID_CAP, 11.57 &process->ref); 11.58 11.59 /* Initialise the notifiable section of the structure. */ 11.60 @@ -232,6 +223,15 @@ 11.61 return notify_notification_values((notifiable_base_t *) process); 11.62 } 11.63 11.64 +/* Return whether a notification has indicated process termination. */ 11.65 + 11.66 +int process_terminated(notifiable_t *notifiable) 11.67 +{ 11.68 + return ((notifiable->notifications & NOTIFY_TASK_SIGNAL) && 11.69 + (notifiable->values.sig == 0)) || 11.70 + (notifiable->notifications & NOTIFY_TASK_ERROR); 11.71 +} 11.72 + 11.73 /* Wait for a notification event on a process. */ 11.74 11.75 long process_notify_wait_process(process_t *process, notifier_t *notifier) 11.76 @@ -240,7 +240,7 @@ 11.77 11.78 /* Unsubscribe if a termination notification has been received. */ 11.79 11.80 - if (!err && _process_terminated(process_notifiable(process))) 11.81 + if (!err && process_terminated(process_notifiable(process))) 11.82 notify_unsubscribe(process_notifiable(process), notifier); 11.83 11.84 return err; 11.85 @@ -253,12 +253,13 @@ 11.86 notifiable_t *notifiable; 11.87 long err = notify_wait_many(¬ifiable, notifier); 11.88 11.89 - *process = (process_t *) notifiable->base; 11.90 - 11.91 /* Unsubscribe if a termination notification has been received. */ 11.92 11.93 - if (!err && _process_terminated(notifiable)) 11.94 + if (!err && process_terminated(notifiable)) 11.95 + { 11.96 + *process = (process_t *) notifiable->base; 11.97 notify_unsubscribe(notifiable, notifier); 11.98 + } 11.99 11.100 return err; 11.101 }
12.1 --- a/libsystypes/idl/process_creator_context.idl Wed Jan 24 21:12:41 2024 +0100 12.2 +++ b/libsystypes/idl/process_creator_context.idl Wed Jan 24 21:17:37 2024 +0100 12.3 @@ -6,8 +6,9 @@ 12.4 /* Start a process, using the given argument count to refer to the process 12.5 arguments supplied via the dataspace, including the program itself. 12.6 12.7 - A writer pipe capability is to be provided for the process's output, and 12.8 - the process capability is returned. */ 12.9 + A reader pipe capability and a writer pipe capability are to be provided 12.10 + for the process's input and output respectively, and the process capability 12.11 + is returned. */ 12.12 12.13 - [opcode(30)] void start(in int argc, in cap writer, out cap process); 12.14 + [opcode(30)] void start(in int argc, in cap reader, in cap writer, out cap process); 12.15 };
13.1 --- a/libsystypes/include/systypes/env.h Wed Jan 24 21:12:41 2024 +0100 13.2 +++ b/libsystypes/include/systypes/env.h Wed Jan 24 21:17:37 2024 +0100 13.3 @@ -1,7 +1,7 @@ 13.4 /* 13.5 * Common environment definitions. 13.6 * 13.7 - * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk> 13.8 + * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk> 13.9 * 13.10 * This program is free software; you can redistribute it and/or 13.11 * modify it under the terms of the GNU General Public License as 13.12 @@ -26,6 +26,7 @@ 13.13 #define ENV_FILESYSTEM_SERVER_NAME "fsserver" 13.14 #define ENV_PIPE_SERVER_NAME "pipeserver" 13.15 #define ENV_PROCESS_SERVER_NAME "prserver" 13.16 +#define ENV_INPUT_STREAM_NAME "stdin" 13.17 #define ENV_OUTPUT_STREAM_NAME "stdout" 13.18 13.19 EXTERN_C_END
14.1 --- a/test_files/programs/cat.c Wed Jan 24 21:12:41 2024 +0100 14.2 +++ b/test_files/programs/cat.c Wed Jan 24 21:17:37 2024 +0100 14.3 @@ -54,7 +54,10 @@ 14.4 return 1; 14.5 14.6 while ((nread = client_read(file, buffer, TO_TRANSFER))) 14.7 - client_write(output, buffer, nread); 14.8 + { 14.9 + if (!client_write(output, buffer, nread)) 14.10 + break; 14.11 + } 14.12 14.13 client_close(file); 14.14
15.1 --- a/test_files/programs/clip.c Wed Jan 24 21:12:41 2024 +0100 15.2 +++ b/test_files/programs/clip.c Wed Jan 24 21:17:37 2024 +0100 15.3 @@ -1,7 +1,7 @@ 15.4 /* 15.5 * Show lines from a file. 15.6 * 15.7 - * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk> 15.8 + * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 15.9 * 15.10 * This program is free software; you can redistribute it and/or 15.11 * modify it under the terms of the GNU General Public License as 15.12 @@ -25,10 +25,21 @@ 15.13 #include <stdlib.h> 15.14 #include <string.h> 15.15 15.16 +/* NOTE: For inclusion in the C library: stream acquisition and access. */ 15.17 + 15.18 #include <fsclient/client.h> 15.19 #include <systypes/env.h> 15.20 #include <systypes/fcntl.h> 15.21 15.22 +static file_t *input, *output; 15.23 +static char output_buffer[256]; 15.24 + 15.25 +#define _fprintf(file, s, ...) \ 15.26 +( \ 15.27 + sprintf(output_buffer, s, ##__VA_ARGS__), \ 15.28 + client_write(file, output_buffer, strlen(output_buffer)) \ 15.29 +) 15.30 + 15.31 15.32 15.33 /* Read a line from the file. */ 15.34 @@ -37,74 +48,65 @@ 15.35 { 15.36 static char buf[256]; 15.37 static offset_t current = 0, limit = 0; 15.38 - char *newline = NULL; 15.39 + static int next_lineno = 1; 15.40 + char *newline; 15.41 + 15.42 + /* Advance to the next line if previously found. */ 15.43 15.44 - do 15.45 + if (next_lineno > *lineno) 15.46 + *lineno = next_lineno; 15.47 + 15.48 + /* Obtain file content. */ 15.49 + 15.50 + if (!limit) 15.51 { 15.52 - /* Obtain file content. */ 15.53 + limit = client_read(file, buf, 256); 15.54 15.55 if (!limit) 15.56 { 15.57 - limit = client_read(file, buf, 256); 15.58 + *start = NULL; 15.59 + *end = NULL; 15.60 + return 0; 15.61 + } 15.62 + } 15.63 15.64 - if (!limit) 15.65 - { 15.66 - *start = NULL; 15.67 - *end = NULL; 15.68 - return 0; 15.69 - } 15.70 + /* Find newline. */ 15.71 15.72 - current = 0; 15.73 + newline = (char *) memchr(buf + current, (int) '\n', limit - current); 15.74 + *start = (buf + current); 15.75 + 15.76 + /* Return final line fragment, potentially leaving more available text. */ 15.77 15.78 - /* Start of line at start of buffer. */ 15.79 + if (newline != NULL) 15.80 + { 15.81 + /* Advance the line number when reading again. */ 15.82 + 15.83 + next_lineno = (*lineno) + 1; 15.84 + 15.85 + /* Define the end of the line and the start of the next line. */ 15.86 15.87 - if (newline != NULL) 15.88 - { 15.89 - current += 1; 15.90 - *start = buf; 15.91 - *end = (char *) memchr(buf + current, (int) '\n', limit - current); 15.92 + *end = newline + 1; 15.93 + current = *end - buf; 15.94 15.95 - if (*end == NULL) 15.96 - *end = buf + limit; 15.97 - 15.98 - return 1; 15.99 - } 15.100 + if (current >= limit) 15.101 + { 15.102 + current = 0; 15.103 + limit = 0; 15.104 } 15.105 15.106 - /* Find newline. */ 15.107 - 15.108 - newline = (char *) memchr(buf + current, (int) '\n', limit - current); 15.109 + return 1; 15.110 + } 15.111 15.112 - if (newline != NULL) 15.113 - { 15.114 - (*lineno)++; 15.115 - current = newline - (char *) buf + 1; 15.116 - 15.117 - /* Start of line before end of buffer. */ 15.118 - 15.119 - if (current < limit) 15.120 - { 15.121 - *start = newline + 1; 15.122 - *end = (char *) memchr(buf + current, (int) '\n', limit - current); 15.123 + /* Or return mid-line fragment reaching the limit of the available text. */ 15.124 15.125 - if (*end == NULL) 15.126 - *end = buf + limit; 15.127 - 15.128 - return 1; 15.129 - } 15.130 - 15.131 - /* Otherwise, reset the buffer to read the start of line. */ 15.132 + else 15.133 + { 15.134 + *end = buf + limit; 15.135 + current = 0; 15.136 + limit = 0; 15.137 15.138 - else 15.139 - limit = 0; 15.140 - } 15.141 - 15.142 - /* No newline: read more data. */ 15.143 - 15.144 - else 15.145 - limit = 0; 15.146 + return 1; 15.147 } 15.148 - while (1); 15.149 } 15.150 15.151 15.152 @@ -112,50 +114,56 @@ 15.153 int main(int argc, char *argv[]) 15.154 { 15.155 file_t *file; 15.156 - int i, startline, numlines; 15.157 + int lineno, startline, numlines; 15.158 char *start, *end; 15.159 15.160 + /* NOTE: For inclusion in the C library: stream acquisition and access. */ 15.161 + 15.162 + input = client_get_stream(ENV_INPUT_STREAM_NAME, O_RDONLY); 15.163 + output = client_get_stream(ENV_OUTPUT_STREAM_NAME, O_WRONLY); 15.164 + 15.165 if (argc < 4) 15.166 return 1; 15.167 15.168 - file = client_open(argv[1], O_RDONLY); 15.169 + if (!strcmp(argv[1], "-")) 15.170 + file = input; 15.171 + else 15.172 + file = client_open(argv[1], O_RDONLY); 15.173 15.174 if (!client_opened(file)) 15.175 { 15.176 if (file != NULL) 15.177 - printf("Error: %s\n", l4sys_errtostr(file->error)); 15.178 + _fprintf(output, "Error: %s\n", l4sys_errtostr(file->error)); 15.179 else 15.180 - printf("Could not open file.\n"); 15.181 + _fprintf(output, "Could not open file.\n"); 15.182 15.183 - while (1); 15.184 + client_flush(output); 15.185 + return 1; 15.186 } 15.187 15.188 startline = atoi(argv[2]); 15.189 numlines = atoi(argv[3]); 15.190 15.191 - i = 1; 15.192 - while (i < startline) 15.193 + lineno = 1; 15.194 + 15.195 + while (lineno < startline + numlines) 15.196 { 15.197 - if (!readline(file, &i, &start, &end)) 15.198 + if (!readline(file, &lineno, &start, &end)) 15.199 { 15.200 - printf("EOF error at line %d.\n", i); 15.201 + _fprintf(output, "EOF error at line %d.\n", lineno); 15.202 + client_flush(output); 15.203 return 1; 15.204 } 15.205 + 15.206 + if ((lineno >= startline) && (lineno < startline + numlines)) 15.207 + client_write(output, start, end - start); 15.208 } 15.209 15.210 - while (i < startline + numlines) 15.211 - { 15.212 - fwrite(start, sizeof(char), end - start, stdout); 15.213 - if (!readline(file, &i, &start, &end)) 15.214 - { 15.215 - printf("EOF error at line %d.\n", i); 15.216 - return 1; 15.217 - } 15.218 - } 15.219 + client_close(file); 15.220 15.221 - fputs("\n\n", stdout); 15.222 + /* NOTE: For inclusion in the C library: stream acquisition and access. */ 15.223 15.224 - client_close(file); 15.225 + client_flush(output); 15.226 15.227 return 0; 15.228 }
16.1 --- a/tests/dstest_exec.cc Wed Jan 24 21:12:41 2024 +0100 16.2 +++ b/tests/dstest_exec.cc Wed Jan 24 21:17:37 2024 +0100 16.3 @@ -1,7 +1,7 @@ 16.4 /* 16.5 * A test of executing code in new tasks and threads. 16.6 * 16.7 - * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk> 16.8 + * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 16.9 * 16.10 * This program is free software; you can redistribute it and/or 16.11 * modify it under the terms of the GNU General Public License as 16.12 @@ -41,22 +41,31 @@ 16.13 static long test_process(int argc, const char *argv[]) 16.14 { 16.15 process_t *process; 16.16 - file_t *reader, *writer; 16.17 + file_t *input_reader, *input_writer; 16.18 + file_t *output_reader, *output_writer; 16.19 long err; 16.20 16.21 - /* Create a pipe for process output. */ 16.22 + /* Create pipes for process input and output. */ 16.23 16.24 - err = client_pipe(&reader, &writer, 0); 16.25 + err = client_pipe(&input_reader, &input_writer, 0); 16.26 16.27 if (err) 16.28 { 16.29 - printf("Could not obtain pipe: %s\n", l4sys_errtostr(err)); 16.30 + printf("Could not obtain pipe for input: %s\n", l4sys_errtostr(err)); 16.31 + return err; 16.32 + } 16.33 + 16.34 + err = client_pipe(&output_reader, &output_writer, 0); 16.35 + 16.36 + if (err) 16.37 + { 16.38 + printf("Could not obtain pipe for output: %s\n", l4sys_errtostr(err)); 16.39 return err; 16.40 } 16.41 16.42 /* Start the process. */ 16.43 16.44 - err = process_spawn(argc, argv, writer, &process); 16.45 + err = process_spawn(argc, argv, input_reader, output_writer, &process); 16.46 16.47 if (err) 16.48 { 16.49 @@ -66,9 +75,10 @@ 16.50 16.51 printf("Finished program initiation.\n"); 16.52 16.53 - /* Release the writing end of the pipe. */ 16.54 + /* Release the relinquished ends of the pipes. */ 16.55 16.56 - client_close(writer); 16.57 + client_close(input_reader); 16.58 + client_close(output_writer); 16.59 16.60 /* Read until the pipe yields no more data. */ 16.61 16.62 @@ -76,7 +86,7 @@ 16.63 16.64 while (1) 16.65 { 16.66 - offset_t nread = client_read(reader, buffer, TO_TRANSFER); 16.67 + offset_t nread = client_read(output_reader, buffer, TO_TRANSFER); 16.68 16.69 if (nread) 16.70 fwrite(buffer, sizeof(char), nread, stdout); 16.71 @@ -84,9 +94,10 @@ 16.72 break; 16.73 } 16.74 16.75 - /* Close the pipe and obtain the process state. */ 16.76 + /* Close the pipes and obtain the process state. */ 16.77 16.78 - client_close(reader); 16.79 + client_close(input_writer); 16.80 + client_close(output_reader); 16.81 16.82 notify_flags_t flags; 16.83 notify_values_t values;
17.1 --- a/tests/dstest_exec_many.cc Wed Jan 24 21:12:41 2024 +0100 17.2 +++ b/tests/dstest_exec_many.cc Wed Jan 24 21:17:37 2024 +0100 17.3 @@ -1,7 +1,7 @@ 17.4 /* 17.5 * A test of executing code in new tasks and threads. 17.6 * 17.7 - * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk> 17.8 + * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 17.9 * 17.10 * This program is free software; you can redistribute it and/or 17.11 * modify it under the terms of the GNU General Public License as 17.12 @@ -49,7 +49,7 @@ 17.13 17.14 process_t *process; 17.15 17.16 - err = process_spawn(argc - 2, (const char **) argv + 2, NULL, &process); 17.17 + err = process_spawn(argc - 2, (const char **) argv + 2, NULL, NULL, &process); 17.18 17.19 if (err) 17.20 {