# HG changeset patch # User Paul Boddie # Date 1706127457 -3600 # Node ID b8942c43b1254c361a322bd0619f66fafaa682c6 # Parent 30117334a34f6343adea92eaa5bfe9abcaaf947b Added support for input pipes in initiated programs, extending the fsaccess program to allow programs to be connected together and for programs to consume files as standard input. Fixed the clip program to behave more like it should since it is useful for testing connected programs. diff -r 30117334a34f -r b8942c43b125 fsaccess/fsaccess.c --- a/fsaccess/fsaccess.c Wed Jan 24 21:12:41 2024 +0100 +++ b/fsaccess/fsaccess.c Wed Jan 24 21:17:37 2024 +0100 @@ -53,7 +53,9 @@ script Read operations from a script file (or stdin)\n\ Execution operations:\n\ \n\ + file Send a file to a program\n\ jobs Show initiated programs\n\ + pipe Connect a running program to a new program\n\ run Run a program from the filesystem\n\ wait Wait for a program to finish\n\ "; @@ -62,9 +64,11 @@ struct operation operations[] = { {"copy-in", copy_in}, + {"file", file_to_program}, {"jobs", show_programs}, {"ls", list_objects}, {"mkdir", make_dirs}, + {"pipe", pipe_to_program}, {"rm", remove_non_dirs}, {"rmdir", remove_dirs}, {"run", run_program}, diff -r 30117334a34f -r b8942c43b125 fsaccess/op_run.c --- a/fsaccess/op_run.c Wed Jan 24 21:12:41 2024 +0100 +++ b/fsaccess/op_run.c Wed Jan 24 21:17:37 2024 +0100 @@ -47,68 +47,131 @@ static file_t *readers[NUMBER_OF_JOBS] = {NULL}; static process_t *processes[NUMBER_OF_JOBS] = {NULL}; -static const char *programs[NUMBER_OF_JOBS] = {NULL}; +static char *programs[NUMBER_OF_JOBS] = {NULL}; static int next_job = 0; +/* Show output from a program. */ + +static void _show_output(file_t *reader) +{ + char buffer[TO_TRANSFER]; + offset_t nread; + + while ((nread = client_read(reader, buffer, TO_TRANSFER))) + fwrite(buffer, sizeof(char), nread, stdout); +} + /* Wait for a program to finish, showing its output. */ static int _wait_program(file_t *reader, process_t *process) { - char buffer[TO_TRANSFER]; - notify_flags_t flags; - notify_values_t values; + notifier_t *notifier = client_notifier_local(); + notifiable_t *notifiable; + int exitcode; long err; - /* Read until the pipe yields no more data. */ + /* Subscribe to reader and process notifications. */ + + if (reader != NULL) + { + err = client_subscribe(reader, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED, notifier); + + if (err) + { + printf("Could not subscribe to pipe notifications: %s\n", l4sys_errtostr(err)); + client_notifier_close(notifier); + return -1; + } + } + + err = notify_subscribe(process_notifiable(process), NOTIFY_TASK_ALL, notifier); + + if (err) + { + printf("Could not subscribe to process notifications: %s\n", l4sys_errtostr(err)); + client_unsubscribe(reader, notifier); + client_notifier_close(notifier); + return -1; + } + + /* Read from and write to pipes until the program terminates. */ while (1) { - offset_t nread = client_read(reader, buffer, TO_TRANSFER); + err = notify_wait_many(¬ifiable, notifier); + + if (err) + { + printf("Notification error: %s\n", l4sys_errtostr(err)); + + if (reader != NULL) + client_unsubscribe(reader, notifier); + + notify_unsubscribe(process_notifiable(process), notifier); + client_notifier_close(notifier); + return -1; + } - if (nread) - fwrite(buffer, sizeof(char), nread, stdout); - else + /* Handle input from the reader. */ + + if ((reader != NULL) && (file_notifications(reader) & (NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED))) + _show_output(reader); + + /* Handle process termination, obtaining the process state. */ + + if (process_terminated(notifiable)) + { + if (reader != NULL) + _show_output(reader); + + printf("End process (flags %" pFMTnotify_flags "x values: %ld, %ld)\n", + notifiable->notifications, notifiable->values.sig, notifiable->values.val); break; + } } - /* Close the pipe and obtain the process state. */ + exitcode = notifiable->values.val; - client_close(reader); + if (reader != NULL) + client_unsubscribe(reader, notifier); - err = process_wait(process, &flags, &values); + notify_unsubscribe(process_notifiable(process), notifier); - printf("End process (flags %" pFMTnotify_flags "x values: %ld, %ld)\n", flags, values.sig, values.val); + /* Close the process and pipe. */ + + client_notifier_close(notifier); + process_free(process); - if (err) - return -1; + if (reader != NULL) + client_close(reader); - return values.val; + return exitcode; } /* Run the given program. */ -int run_program(int argc, char *argv[]) +static int _run_program(int argc, char *argv[], file_t *input_reader) { process_t *process; - file_t *reader, *writer; + file_t *output_reader, *output_writer; int last_job; long err; /* Create a pipe for process output. */ - err = client_pipe(&reader, &writer, 0); + err = client_pipe(&output_reader, &output_writer, O_NONBLOCK); if (err) { - printf("Could not obtain pipe: %s\n", l4sys_errtostr(err)); + printf("Could not obtain pipe for output: %s\n", l4sys_errtostr(err)); return -1; } /* Start the process. */ - err = process_spawn(argc, (const char **) argv, writer, &process); + err = process_spawn(argc, (const char **) argv, input_reader, output_writer, &process); if (err) { @@ -118,15 +181,15 @@ printf("Finished program initiation.\n"); - /* Release the writing end of the pipe. */ + /* Release the relinquished end of the pipe. */ - client_close(writer); + client_close(output_writer); /* Record the output stream. */ last_job = next_job; - while (readers[next_job] != NULL) + while (processes[next_job] != NULL) { next_job++; @@ -136,16 +199,79 @@ /* Wait for the process to complete if no more job slots are available. */ if (next_job == last_job) - return _wait_program(reader, process); + return _wait_program(output_reader, process); } - readers[next_job] = reader; + readers[next_job] = output_reader; processes[next_job] = process; programs[next_job] = strdup(argv[0]); return 0; } + + +/* Run the given program, providing input from a file. */ + +int file_to_program(int argc, char *argv[]) +{ + file_t *reader; + + /* Obtain a file reader and run the program with this as its input reader. */ + + if (argc < 1) + return -1; + + reader = client_open(argv[0], O_RDONLY); + + if (!client_opened(reader)) + { + printf("Could not open file: %s\n", argv[0]); + return -1; + } + + return _run_program(argc - 1, &argv[1], reader); +} + +/* Run the given program, connecting input from another program. */ + +int pipe_to_program(int argc, char *argv[]) +{ + int job_number; + int exitcode; + + /* Obtain the job number for the output writer and run the program with this + as its input reader. */ + + if (argc < 1) + return -1; + + job_number = atoi(argv[0]); + + if (readers[job_number] == NULL) + { + printf("No output available for this job: %s\n", argv[0]); + return -1; + } + + exitcode = _run_program(argc - 1, &argv[1], readers[job_number]); + + /* Remove the job's reader to prevent direct access to it and to allow it to + be closed by the receiving program. */ + + client_close(readers[job_number]); + readers[job_number] = NULL; + + return exitcode; +} + +/* Run the given program. */ + +int run_program(int argc, char *argv[]) +{ + return _run_program(argc, argv, NULL); +} + /* Show initiated programs. */ int show_programs(int argc, char *argv[]) @@ -156,8 +282,9 @@ for (job_number = 0; job_number < NUMBER_OF_JOBS; job_number++) { - if (readers[job_number] != NULL) - printf("[%d] %s\n", job_number, programs[job_number]); + if (processes[job_number] != NULL) + printf("[%d] %s%s\n", job_number, programs[job_number], + readers[job_number] != NULL ? " [!]" : ""); } return 0; @@ -175,8 +302,11 @@ job_number = atoi(argv[0]); - if (readers[job_number] == NULL) + if (processes[job_number] == NULL) + { + printf("No such job: %s\n", argv[0]); return -1; + } exitcode = _wait_program(readers[job_number], processes[job_number]); diff -r 30117334a34f -r b8942c43b125 fsaccess/ops.h --- a/fsaccess/ops.h Wed Jan 24 21:12:41 2024 +0100 +++ b/fsaccess/ops.h Wed Jan 24 21:17:37 2024 +0100 @@ -46,8 +46,10 @@ int copy_in(int argc, char *argv[]); int copy_out(int argc, char *argv[]); +int file_to_program(int argc, char *argv[]); int list_objects(int argc, char *argv[]); int make_dirs(int argc, char *argv[]); +int pipe_to_program(int argc, char *argv[]); int remove_dirs(int argc, char *argv[]); int remove_non_dirs(int argc, char *argv[]); int run_program(int argc, char *argv[]); diff -r 30117334a34f -r b8942c43b125 libexec/include/exec/process_creating.h --- a/libexec/include/exec/process_creating.h Wed Jan 24 21:12:41 2024 +0100 +++ b/libexec/include/exec/process_creating.h Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * Support for executing code in new tasks and threads. * - * Copyright (C) 2022, 2023 Paul Boddie + * Copyright (C) 2022, 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -87,18 +87,18 @@ long start_region_mapper(l4_cap_idx_t pager); long start_program(l4_cap_idx_t monitor, int argc, const char *argv[], - l4_cap_idx_t writer); + l4_cap_idx_t reader, l4_cap_idx_t writer); - long _start(int argc, const char *argv[], l4_cap_idx_t writer, - l4_cap_idx_t process); + long _start(int argc, const char *argv[], l4_cap_idx_t reader, + l4_cap_idx_t writer, l4_cap_idx_t process); public: explicit ProcessCreating(const char *rm_filename, file_t *rm_file); virtual long init_process_monitor(l4_cap_idx_t *monitor); - virtual long start(int argc, const char *argv[], l4_cap_idx_t writer, - l4_cap_idx_t process); + virtual long start(int argc, const char *argv[], l4_cap_idx_t reader, + l4_cap_idx_t writer, l4_cap_idx_t process); }; /* vim: tabstop=2 expandtab shiftwidth=2 diff -r 30117334a34f -r b8942c43b125 libexec/include/exec/process_creator_context_resource.h --- a/libexec/include/exec/process_creator_context_resource.h Wed Jan 24 21:12:41 2024 +0100 +++ b/libexec/include/exec/process_creator_context_resource.h Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * Support for creating a new process. * - * Copyright (C) 2022, 2023 Paul Boddie + * Copyright (C) 2022, 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -56,7 +56,8 @@ /* Process creator context interface methods. */ - virtual long start(int argc, l4_cap_idx_t writer, l4_cap_idx_t *process); + virtual long start(int argc, l4_cap_idx_t reader, l4_cap_idx_t writer, + l4_cap_idx_t *process); /* Pager/dataspace methods. */ diff -r 30117334a34f -r b8942c43b125 libexec/include/exec/process_creator_resource.h --- a/libexec/include/exec/process_creator_resource.h Wed Jan 24 21:12:41 2024 +0100 +++ b/libexec/include/exec/process_creator_resource.h Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * Support for executing code in new tasks and threads. * - * Copyright (C) 2022, 2023 Paul Boddie + * Copyright (C) 2022, 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -51,8 +51,8 @@ virtual long init_process(l4_cap_idx_t *process); - virtual long start(int argc, const char *argv[], l4_cap_idx_t writer, - l4_cap_idx_t process); + virtual long start(int argc, const char *argv[], l4_cap_idx_t reader, + l4_cap_idx_t writer, l4_cap_idx_t process); /* Opener interface methods. */ diff -r 30117334a34f -r b8942c43b125 libexec/lib/src/process_creating.cc --- a/libexec/lib/src/process_creating.cc Wed Jan 24 21:12:41 2024 +0100 +++ b/libexec/lib/src/process_creating.cc Wed Jan 24 21:17:37 2024 +0100 @@ -284,7 +284,8 @@ thread. */ long ProcessCreating::start_program(l4_cap_idx_t monitor, int argc, - const char *argv[], l4_cap_idx_t writer) + const char *argv[], l4_cap_idx_t reader, + l4_cap_idx_t writer) { /* NOTE: Environment vector is currently not defined. */ @@ -317,14 +318,17 @@ l4_cap_idx_t fsserver_cap = _process.allocate_cap(); l4_cap_idx_t fsserver = l4re_env_get_cap(ENV_FILESYSTEM_SERVER_NAME); - /* Also reserve a capability for the writer. */ + /* Also reserve capabilities for the reader and writer. If the reader or + writer are invalid capabilities, these will not actually be transferred. */ + l4_cap_idx_t reader_cap = _process.allocate_cap(); l4_cap_idx_t writer_cap = _process.allocate_cap(); /* Define the capabilities to be mapped for the filesystem. */ struct ipc_mapped_cap program_mapped_caps[] = { {fsserver_cap, fsserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, + {reader_cap, reader, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, {writer_cap, writer, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS}, {L4_INVALID_CAP, L4_INVALID_CAP, 0, 0}, }; @@ -340,8 +344,15 @@ l4re_env API. Each capability index is assigned above when mapping the capability and encoded in the entry below. */ + if (l4_is_invalid_cap(reader)) + reader_cap = L4_INVALID_CAP; + + if (l4_is_invalid_cap(writer)) + writer_cap = L4_INVALID_CAP; + l4re_env_cap_entry_t program_init_caps[] = { l4re_env_cap_entry_t(ENV_FILESYSTEM_SERVER_NAME, fsserver_cap, L4_CAP_FPAGE_RWS), + l4re_env_cap_entry_t(ENV_INPUT_STREAM_NAME, reader_cap, L4_CAP_FPAGE_R), l4re_env_cap_entry_t(ENV_OUTPUT_STREAM_NAME, writer_cap, L4_CAP_FPAGE_W), l4re_env_cap_entry_t() }; @@ -368,12 +379,12 @@ } /* Start a new process for the payload indicated by the first of the given - program arguments, employing the given writer pipe, and returning a - reference to the process monitor as an object for interacting with the + program arguments, employing the given reader and writer pipes, and returning + a reference to the process monitor as an object for interacting with the process. */ -long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t writer, - l4_cap_idx_t process) +long ProcessCreating::_start(int argc, const char *argv[], l4_cap_idx_t reader, + l4_cap_idx_t writer, l4_cap_idx_t process) { /* Open the program file, handling any error conditions. If successfully opened, it will be closed when the process terminates. */ @@ -419,7 +430,7 @@ if (err) return err; - err = start_program(process, argc, argv, writer); + err = start_program(process, argc, argv, reader, writer); if (err) return err; @@ -444,15 +455,16 @@ /* Start the given program, notifying the process monitor upon any error. */ -long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t writer, - l4_cap_idx_t process) +long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t reader, + l4_cap_idx_t writer, l4_cap_idx_t process) { std::lock_guard guard(_lock); - long err = _start(argc, argv, writer, process); + long err = _start(argc, argv, reader, writer, process); - /* Discard the writer since it will not be used in this task. */ + /* Discard the reader and writer since they will not be used in this task. */ + ipc_cap_free_um(reader); ipc_cap_free_um(writer); /* Communicate the error using the signal value. */ diff -r 30117334a34f -r b8942c43b125 libexec/lib/src/process_creator_context_resource.cc --- a/libexec/lib/src/process_creator_context_resource.cc Wed Jan 24 21:12:41 2024 +0100 +++ b/libexec/lib/src/process_creator_context_resource.cc Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * A resource offering support for creating processes. * - * Copyright (C) 2023 Paul Boddie + * Copyright (C) 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -50,7 +50,8 @@ /* ProcessCreatorContext interface methods. */ -long ProcessCreatorContextResource::start(int argc, l4_cap_idx_t writer, +long ProcessCreatorContextResource::start(int argc, l4_cap_idx_t reader, + l4_cap_idx_t writer, l4_cap_idx_t *process) { /* Obtain the arguments by reading from the shared memory. */ @@ -58,6 +59,9 @@ const char *argv[argc]; offset_t pos = 0; + if (!argc) + return -L4_EINVAL; + for (int i = 0; i < argc; i++) { argv[i] = get_string(pos); @@ -89,7 +93,7 @@ reply, so a notification is sent via the process monitor instead by the process creator. */ - _creator->start(argc, argv, writer, *process); + _creator->start(argc, argv, reader, writer, *process); return IPC_MESSAGE_SENT; } diff -r 30117334a34f -r b8942c43b125 libexec/lib/src/process_creator_resource.cc --- a/libexec/lib/src/process_creator_resource.cc Wed Jan 24 21:12:41 2024 +0100 +++ b/libexec/lib/src/process_creator_resource.cc Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * A resource offering support for creating processes. * - * Copyright (C) 2023 Paul Boddie + * Copyright (C) 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -56,9 +56,10 @@ /* Start the new process, obtaining a reference to it. */ long ProcessCreatorResource::start(int argc, const char *argv[], - l4_cap_idx_t writer, l4_cap_idx_t process) + l4_cap_idx_t reader, l4_cap_idx_t writer, + l4_cap_idx_t process) { - return _creating.start(argc, argv, writer, process); + return _creating.start(argc, argv, reader, writer, process); } diff -r 30117334a34f -r b8942c43b125 libfsclient/include/fsclient/process.h --- a/libfsclient/include/fsclient/process.h Wed Jan 24 21:12:41 2024 +0100 +++ b/libfsclient/include/fsclient/process.h Wed Jan 24 21:17:37 2024 +0100 @@ -53,8 +53,8 @@ long process_error(process_t *process); void process_free(process_t *process); void process_init(process_t *process); -long process_spawn(int argc, const char *argv[], file_t *writer, process_t **process); -long process_start(process_t *process, int argc, const char *argv[], file_t *writer); +long process_spawn(int argc, const char *argv[], file_t *reader, file_t *writer, process_t **process); +long process_start(process_t *process, int argc, const char *argv[], file_t *reader, file_t *writer); long process_wait(process_t *process, notify_flags_t *flags, notify_values_t *values); /* Notification support. */ @@ -63,6 +63,8 @@ notify_flags_t process_notifications(process_t *process); notify_values_t process_notification_values(process_t *process); +int process_terminated(notifiable_t *notifiable); + long process_notify_wait_process(process_t *process, notifier_t *notifier); long process_notify_wait_processes(process_t **process, notifier_t *notifier); diff -r 30117334a34f -r b8942c43b125 libfsclient/lib/src/process.cc --- a/libfsclient/lib/src/process.cc Wed Jan 24 21:12:41 2024 +0100 +++ b/libfsclient/lib/src/process.cc Wed Jan 24 21:17:37 2024 +0100 @@ -34,17 +34,6 @@ -/* Utility functions. */ - -static bool _process_terminated(notifiable_t *notifiable) -{ - return ((notifiable->notifications & NOTIFY_TASK_SIGNAL) && - (notifiable->values.sig == 0)) || - (notifiable->notifications & NOTIFY_TASK_ERROR); -} - - - /* Create a new process object. */ process_t *process_new() @@ -111,15 +100,15 @@ /* A convenience function for creating and starting a process. */ -long process_spawn(int argc, const char *argv[], file_t *writer, - process_t **process) +long process_spawn(int argc, const char *argv[], file_t *reader, + file_t *writer, process_t **process) { *process = process_new(); /* Start the process with the given arguments. */ if (*process != NULL) - return process_start(*process, argc, argv, writer); + return process_start(*process, argc, argv, reader, writer); else return -L4_ENOMEM; } @@ -128,7 +117,7 @@ NOTE: This does not yet employ a pipe for the process's input stream. */ long process_start(process_t *process, int argc, const char *argv[], - file_t *writer) + file_t *reader, file_t *writer) { l4_cap_idx_t server = l4re_env_get_cap(ENV_PROCESS_SERVER_NAME); @@ -159,7 +148,9 @@ /* Start the process, obtaining a reference to it. */ - err = creator.start(argc, writer != NULL ? writer->ref : (l4_cap_idx_t) L4_INVALID_CAP, + err = creator.start(argc, + reader != NULL ? reader->ref : (l4_cap_idx_t) L4_INVALID_CAP, + writer != NULL ? writer->ref : (l4_cap_idx_t) L4_INVALID_CAP, &process->ref); /* Initialise the notifiable section of the structure. */ @@ -232,6 +223,15 @@ return notify_notification_values((notifiable_base_t *) process); } +/* Return whether a notification has indicated process termination. */ + +int process_terminated(notifiable_t *notifiable) +{ + return ((notifiable->notifications & NOTIFY_TASK_SIGNAL) && + (notifiable->values.sig == 0)) || + (notifiable->notifications & NOTIFY_TASK_ERROR); +} + /* Wait for a notification event on a process. */ long process_notify_wait_process(process_t *process, notifier_t *notifier) @@ -240,7 +240,7 @@ /* Unsubscribe if a termination notification has been received. */ - if (!err && _process_terminated(process_notifiable(process))) + if (!err && process_terminated(process_notifiable(process))) notify_unsubscribe(process_notifiable(process), notifier); return err; @@ -253,12 +253,13 @@ notifiable_t *notifiable; long err = notify_wait_many(¬ifiable, notifier); - *process = (process_t *) notifiable->base; - /* Unsubscribe if a termination notification has been received. */ - if (!err && _process_terminated(notifiable)) + if (!err && process_terminated(notifiable)) + { + *process = (process_t *) notifiable->base; notify_unsubscribe(notifiable, notifier); + } return err; } diff -r 30117334a34f -r b8942c43b125 libsystypes/idl/process_creator_context.idl --- a/libsystypes/idl/process_creator_context.idl Wed Jan 24 21:12:41 2024 +0100 +++ b/libsystypes/idl/process_creator_context.idl Wed Jan 24 21:17:37 2024 +0100 @@ -6,8 +6,9 @@ /* Start a process, using the given argument count to refer to the process arguments supplied via the dataspace, including the program itself. - A writer pipe capability is to be provided for the process's output, and - the process capability is returned. */ + A reader pipe capability and a writer pipe capability are to be provided + for the process's input and output respectively, and the process capability + is returned. */ - [opcode(30)] void start(in int argc, in cap writer, out cap process); + [opcode(30)] void start(in int argc, in cap reader, in cap writer, out cap process); }; diff -r 30117334a34f -r b8942c43b125 libsystypes/include/systypes/env.h --- a/libsystypes/include/systypes/env.h Wed Jan 24 21:12:41 2024 +0100 +++ b/libsystypes/include/systypes/env.h Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * Common environment definitions. * - * Copyright (C) 2023 Paul Boddie + * Copyright (C) 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -26,6 +26,7 @@ #define ENV_FILESYSTEM_SERVER_NAME "fsserver" #define ENV_PIPE_SERVER_NAME "pipeserver" #define ENV_PROCESS_SERVER_NAME "prserver" +#define ENV_INPUT_STREAM_NAME "stdin" #define ENV_OUTPUT_STREAM_NAME "stdout" EXTERN_C_END diff -r 30117334a34f -r b8942c43b125 test_files/programs/cat.c --- a/test_files/programs/cat.c Wed Jan 24 21:12:41 2024 +0100 +++ b/test_files/programs/cat.c Wed Jan 24 21:17:37 2024 +0100 @@ -54,7 +54,10 @@ return 1; while ((nread = client_read(file, buffer, TO_TRANSFER))) - client_write(output, buffer, nread); + { + if (!client_write(output, buffer, nread)) + break; + } client_close(file); diff -r 30117334a34f -r b8942c43b125 test_files/programs/clip.c --- a/test_files/programs/clip.c Wed Jan 24 21:12:41 2024 +0100 +++ b/test_files/programs/clip.c Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * Show lines from a file. * - * Copyright (C) 2022, 2023 Paul Boddie + * Copyright (C) 2022, 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -25,10 +25,21 @@ #include #include +/* NOTE: For inclusion in the C library: stream acquisition and access. */ + #include #include #include +static file_t *input, *output; +static char output_buffer[256]; + +#define _fprintf(file, s, ...) \ +( \ + sprintf(output_buffer, s, ##__VA_ARGS__), \ + client_write(file, output_buffer, strlen(output_buffer)) \ +) + /* Read a line from the file. */ @@ -37,74 +48,65 @@ { static char buf[256]; static offset_t current = 0, limit = 0; - char *newline = NULL; + static int next_lineno = 1; + char *newline; + + /* Advance to the next line if previously found. */ - do + if (next_lineno > *lineno) + *lineno = next_lineno; + + /* Obtain file content. */ + + if (!limit) { - /* Obtain file content. */ + limit = client_read(file, buf, 256); if (!limit) { - limit = client_read(file, buf, 256); + *start = NULL; + *end = NULL; + return 0; + } + } - if (!limit) - { - *start = NULL; - *end = NULL; - return 0; - } + /* Find newline. */ - current = 0; + newline = (char *) memchr(buf + current, (int) '\n', limit - current); + *start = (buf + current); + + /* Return final line fragment, potentially leaving more available text. */ - /* Start of line at start of buffer. */ + if (newline != NULL) + { + /* Advance the line number when reading again. */ + + next_lineno = (*lineno) + 1; + + /* Define the end of the line and the start of the next line. */ - if (newline != NULL) - { - current += 1; - *start = buf; - *end = (char *) memchr(buf + current, (int) '\n', limit - current); + *end = newline + 1; + current = *end - buf; - if (*end == NULL) - *end = buf + limit; - - return 1; - } + if (current >= limit) + { + current = 0; + limit = 0; } - /* Find newline. */ - - newline = (char *) memchr(buf + current, (int) '\n', limit - current); + return 1; + } - if (newline != NULL) - { - (*lineno)++; - current = newline - (char *) buf + 1; - - /* Start of line before end of buffer. */ - - if (current < limit) - { - *start = newline + 1; - *end = (char *) memchr(buf + current, (int) '\n', limit - current); + /* Or return mid-line fragment reaching the limit of the available text. */ - if (*end == NULL) - *end = buf + limit; - - return 1; - } - - /* Otherwise, reset the buffer to read the start of line. */ + else + { + *end = buf + limit; + current = 0; + limit = 0; - else - limit = 0; - } - - /* No newline: read more data. */ - - else - limit = 0; + return 1; } - while (1); } @@ -112,50 +114,56 @@ int main(int argc, char *argv[]) { file_t *file; - int i, startline, numlines; + int lineno, startline, numlines; char *start, *end; + /* NOTE: For inclusion in the C library: stream acquisition and access. */ + + input = client_get_stream(ENV_INPUT_STREAM_NAME, O_RDONLY); + output = client_get_stream(ENV_OUTPUT_STREAM_NAME, O_WRONLY); + if (argc < 4) return 1; - file = client_open(argv[1], O_RDONLY); + if (!strcmp(argv[1], "-")) + file = input; + else + file = client_open(argv[1], O_RDONLY); if (!client_opened(file)) { if (file != NULL) - printf("Error: %s\n", l4sys_errtostr(file->error)); + _fprintf(output, "Error: %s\n", l4sys_errtostr(file->error)); else - printf("Could not open file.\n"); + _fprintf(output, "Could not open file.\n"); - while (1); + client_flush(output); + return 1; } startline = atoi(argv[2]); numlines = atoi(argv[3]); - i = 1; - while (i < startline) + lineno = 1; + + while (lineno < startline + numlines) { - if (!readline(file, &i, &start, &end)) + if (!readline(file, &lineno, &start, &end)) { - printf("EOF error at line %d.\n", i); + _fprintf(output, "EOF error at line %d.\n", lineno); + client_flush(output); return 1; } + + if ((lineno >= startline) && (lineno < startline + numlines)) + client_write(output, start, end - start); } - while (i < startline + numlines) - { - fwrite(start, sizeof(char), end - start, stdout); - if (!readline(file, &i, &start, &end)) - { - printf("EOF error at line %d.\n", i); - return 1; - } - } + client_close(file); - fputs("\n\n", stdout); + /* NOTE: For inclusion in the C library: stream acquisition and access. */ - client_close(file); + client_flush(output); return 0; } diff -r 30117334a34f -r b8942c43b125 tests/dstest_exec.cc --- a/tests/dstest_exec.cc Wed Jan 24 21:12:41 2024 +0100 +++ b/tests/dstest_exec.cc Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * A test of executing code in new tasks and threads. * - * Copyright (C) 2022, 2023 Paul Boddie + * Copyright (C) 2022, 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -41,22 +41,31 @@ static long test_process(int argc, const char *argv[]) { process_t *process; - file_t *reader, *writer; + file_t *input_reader, *input_writer; + file_t *output_reader, *output_writer; long err; - /* Create a pipe for process output. */ + /* Create pipes for process input and output. */ - err = client_pipe(&reader, &writer, 0); + err = client_pipe(&input_reader, &input_writer, 0); if (err) { - printf("Could not obtain pipe: %s\n", l4sys_errtostr(err)); + printf("Could not obtain pipe for input: %s\n", l4sys_errtostr(err)); + return err; + } + + err = client_pipe(&output_reader, &output_writer, 0); + + if (err) + { + printf("Could not obtain pipe for output: %s\n", l4sys_errtostr(err)); return err; } /* Start the process. */ - err = process_spawn(argc, argv, writer, &process); + err = process_spawn(argc, argv, input_reader, output_writer, &process); if (err) { @@ -66,9 +75,10 @@ printf("Finished program initiation.\n"); - /* Release the writing end of the pipe. */ + /* Release the relinquished ends of the pipes. */ - client_close(writer); + client_close(input_reader); + client_close(output_writer); /* Read until the pipe yields no more data. */ @@ -76,7 +86,7 @@ while (1) { - offset_t nread = client_read(reader, buffer, TO_TRANSFER); + offset_t nread = client_read(output_reader, buffer, TO_TRANSFER); if (nread) fwrite(buffer, sizeof(char), nread, stdout); @@ -84,9 +94,10 @@ break; } - /* Close the pipe and obtain the process state. */ + /* Close the pipes and obtain the process state. */ - client_close(reader); + client_close(input_writer); + client_close(output_reader); notify_flags_t flags; notify_values_t values; diff -r 30117334a34f -r b8942c43b125 tests/dstest_exec_many.cc --- a/tests/dstest_exec_many.cc Wed Jan 24 21:12:41 2024 +0100 +++ b/tests/dstest_exec_many.cc Wed Jan 24 21:17:37 2024 +0100 @@ -1,7 +1,7 @@ /* * A test of executing code in new tasks and threads. * - * Copyright (C) 2022, 2023 Paul Boddie + * Copyright (C) 2022, 2023, 2024 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -49,7 +49,7 @@ process_t *process; - err = process_spawn(argc - 2, (const char **) argv + 2, NULL, &process); + err = process_spawn(argc - 2, (const char **) argv + 2, NULL, NULL, &process); if (err) {