L4Re/departure

test_files/programs/test_popenv.c

695:617cfedf27be
5 months ago Paul Boddie Test end-of-file state on stdin to determine whether to close the input pipe. libc_newlib
     1 /*     2  * Run a program using the supplied arguments.     3  *     4  * Copyright (C) 2024 Paul Boddie <paul@boddie.org.uk>     5  *     6  * This program is free software; you can redistribute it and/or     7  * modify it under the terms of the GNU General Public License as     8  * published by the Free Software Foundation; either version 2 of     9  * the License, or (at your option) any later version.    10  *    11  * This program is distributed in the hope that it will be useful,    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    14  * GNU General Public License for more details.    15  *    16  * You should have received a copy of the GNU General Public License    17  * along with this program; if not, write to the Free Software    18  * Foundation, Inc., 51 Franklin Street, Fifth Floor,    19  * Boston, MA  02110-1301, USA    20  */    21     22 #include <errno.h>    23 #include <fcntl.h>    24 #include <port.h>    25 #include <process.h>    26 #include <stdio.h>    27 #include <sys/types.h>    28     29     30     31 /* Transfer size for communication. */    32     33 static const offset_t TO_TRANSFER = 1024;    34     35 /* Transfer data between streams. */    36     37 static void transfer(FILE *input, FILE *output, char *buffer, int *start, size_t *remaining)    38 {    39     size_t nwritten;    40     41     /* Transfer any previously read data before trying to read more. */    42     43     while (*remaining || (*start = 0, *remaining = fread(buffer, sizeof(char), TO_TRANSFER, input)))    44     {    45         /* Write the data, stopping if no data can be written. */    46     47         nwritten = fwrite(buffer + *start, sizeof(char), *remaining, output);    48     49         if (!nwritten)    50             return;    51     52         /* Record the remaining amount. */    53     54         *remaining -= nwritten;    55         *start += nwritten;    56     }    57 }    58     59     60     61 /* List objects in the filesystem image. */    62     63 int main(int argc, char *argv[])    64 {    65     FILE *input, *output, *error;    66     pid_t pid;    67     port_t port;    68     event_source_t source;    69     event_values_t result;    70     71     /* Transfer buffers and status. */    72     73     char input_buffer[TO_TRANSFER];    74     char output_buffer[TO_TRANSFER];    75     char error_buffer[TO_TRANSFER];    76     int input_start = 0, output_start = 0, error_start = 0;    77     size_t input_remaining = 0, output_remaining = 0, error_remaining = 0;    78     79     if (argc < 2)    80     {    81         printf("Usage: test_popenv <program> <argument>...\n");    82         return 1;    83     }    84     85     printf("Running: %s\n", argv[1]);    86     87     pid = popenv(argc - 1, (const char **) argv + 1, &input, &output, &error);    88     89     if (pid == -1)    90     {    91         printf("Error: %d\n", errno);    92         return 1;    93     }    94     95     printf("Process: %d\n", pid);    96     97     /* Prevent stdin from blocking to be able to read as much data as is    98        available and to monitor events on it manually. */    99    100     if (stdin != NULL)   101         fcntl(fileno(stdin), F_SETFL, O_NONBLOCK | fcntl(fileno(stdin), F_GETFL));   102    103     /* Monitor the streams and the process. */   104    105     port = port_create();   106    107     port_subscribe(port, port_process_source(pid), PORT_PROCESS_ALL);   108    109     if (stdin != NULL)   110     {   111         port_subscribe(port, port_file_source(fileno(stdin)), PORT_INPUT | PORT_CLOSE);   112         port_subscribe(port, port_file_source(fileno(input)), PORT_OUTPUT | PORT_CLOSE);   113     }   114    115     port_subscribe(port, port_file_source(fileno(output)), PORT_INPUT | PORT_CLOSE);   116    117     if (error != NULL)   118         port_subscribe(port, port_file_source(fileno(error)), PORT_INPUT | PORT_CLOSE);   119    120     /* Wait for events on the streams and the process. */   121    122     while (1)   123     {   124         if (port_wait(port, &source))   125         {   126             printf("Error waiting for notifications.\n");   127             return 1;   128         }   129    130         /* Handle input to this process and its transfer to the created   131            process. */   132    133         if ((stdin != NULL) && (input != NULL))   134         {   135             /* Attempt to transfer input. */   136    137             if ((source == port_file_source(fileno(stdin))) ||   138                 input_remaining || (source == port_file_source(fileno(input))))   139                 transfer(stdin, input, input_buffer, &input_start, &input_remaining);   140    141             /* If no input remains and stdin has been closed, close the input   142                pipe. */   143    144             if (!input_remaining && (feof(stdin) ||   145                  port_source_event_flags(port_file_source(fileno(stdin))) & PORT_CLOSE))   146             {   147                 fclose(input);   148                 input = NULL;   149             }   150         }   151    152         /* Handle output from the process. */   153    154         if (output_remaining || (source == port_file_source(fileno(output))))   155             transfer(output, stdout, output_buffer, &output_start, &output_remaining);   156    157         if ((error != NULL) && (error_remaining || (source == port_file_source(fileno(error)))))   158             transfer(error, stderr, error_buffer, &error_start, &error_remaining);   159    160         /* Handle process termination. */   161    162         else if (source == port_process_source(pid))   163         {   164             transfer(output, stdout, output_buffer, &output_start, &output_remaining);   165    166             if (error != NULL)   167                 transfer(error, stderr, error_buffer, &error_start, &error_remaining);   168    169             result = port_source_event_values(source);   170             break;   171         }   172     }   173    174     /* Close any open pipes. */   175    176     if (input != NULL)   177         fclose(input);   178    179     fclose(output);   180    181     if (error != NULL)   182         fclose(error);   183    184     printf("Result: %ld\n", result.val);   185     return 0;   186 }   187    188 /* vim: tabstop=4 expandtab shiftwidth=4   189 */