initial commit
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) [2026] [Randy Jordan]
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# bi_popen
|
||||||
|
|
||||||
|
## Description
|
||||||
|
`bi_popen`
|
||||||
|
|
||||||
|
Creates two pipes, forks, and runs the given command. One pipe is
|
||||||
|
connected between the given *in and the standard input stream of the child;
|
||||||
|
the other pipe is connected between the given *out and the standard output
|
||||||
|
stream of the child.
|
||||||
|
|
||||||
|
Returns the pid of the child on success, -1 otherwise. On error, errno
|
||||||
|
will be set accordingly.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
* [Features](#features)
|
||||||
|
* [Todos](#todos)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Acknowledgments](#acknowledgments)
|
||||||
|
* [License](#license)
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
## Todos
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
FILE* in = NULL;
|
||||||
|
FILE* out = NULL;
|
||||||
|
char* line = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
const int pid = bi_popen("/bin/bash", &in, &out);
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("bi_popen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(in, "date\n");
|
||||||
|
getline(&line, &size, out);
|
||||||
|
printf("-> %s", line);
|
||||||
|
|
||||||
|
// Since in this case we can tell the child to terminate, we'll do so
|
||||||
|
// and wait for it to terminate before we close down.
|
||||||
|
fprintf(in, "exit\n");
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
|
||||||
|
fclose(in);
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
[Unix Stack Exchange](https://unix.stackexchange.com/questions/606861/programming-communicating-with-chess-engine-stockfish-fifos-bash-redirecti)<br>
|
||||||
|
## License
|
||||||
|
This project is licensed under the MIT License - see the [MIT License](LICENSE.md) file for details.
|
||||||
|
|
||||||
+214
@@ -0,0 +1,214 @@
|
|||||||
|
/* - | Copyright / About | ----------------------------------------------------
|
||||||
|
Copyright (c) 2026 Randy Jordan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Creates two pipes, forks, and runs the given command. One pipe is
|
||||||
|
connected between the given *in and the standard input stream of the child;
|
||||||
|
the other pipe is connected between the given *out and the standard output
|
||||||
|
stream of the child.
|
||||||
|
|
||||||
|
Returns the pid of the child on success, -1 otherwise. On error, errno
|
||||||
|
will be set accordingly.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
#define DEBUG
|
||||||
|
#include "bi_popen.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
static const int READ_END = 0;
|
||||||
|
static const int WRITE_END = 1;
|
||||||
|
static const int INVALID_FD = -1;
|
||||||
|
|
||||||
|
/* The front fell of, which is quite unusual. */
|
||||||
|
static int
|
||||||
|
bi_popen_bail(FILE** const in, FILE** const out, int child[2], int parent[2])
|
||||||
|
{
|
||||||
|
/* Save and log the error */
|
||||||
|
const int old_errno = errno;
|
||||||
|
fprintf(stderr, "bi_popen: %s\n", strerror(old_errno));
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
|
/* Close the file pointers */
|
||||||
|
if (*in != NULL) {
|
||||||
|
fclose(*in);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*out != NULL) {
|
||||||
|
fclose(*out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close all parent and child file descriptors */
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
if (child[i] != INVALID_FD) {
|
||||||
|
close(child[i]);
|
||||||
|
}
|
||||||
|
if (parent[i] != INVALID_FD) {
|
||||||
|
close(parent[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = old_errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup pipe redirection, and call exec process. */
|
||||||
|
static int
|
||||||
|
bi_popen_child(FILE** const in,
|
||||||
|
FILE** const out,
|
||||||
|
int child[2],
|
||||||
|
int parent[2],
|
||||||
|
const char* command)
|
||||||
|
{
|
||||||
|
/* Redirect child's stdin to the read end of to_child. */
|
||||||
|
if (dup2(child[READ_END], STDIN_FILENO) < 0) {
|
||||||
|
perror("dup2");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/* Close original pipe fds in the child after duplicating. */
|
||||||
|
close(child[READ_END]);
|
||||||
|
close(child[WRITE_END]);
|
||||||
|
|
||||||
|
/* Redirect child's stdout to the write end of to_parent */
|
||||||
|
if (dup2(parent[WRITE_END], STDOUT_FILENO) < 0) {
|
||||||
|
perror("dup2");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/* Close original pipe fds in the child after duplicating. */
|
||||||
|
close(parent[READ_END]);
|
||||||
|
close(parent[WRITE_END]);
|
||||||
|
|
||||||
|
/* Replace the child process image with the requested command. */
|
||||||
|
execlp(command, command, NULL);
|
||||||
|
|
||||||
|
/* If execlp returns, it failed. */
|
||||||
|
perror("execlp");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert remaining fd's to streams and return pid_t to wait */
|
||||||
|
static int
|
||||||
|
bi_popen_parent(FILE** const in,
|
||||||
|
FILE** const out,
|
||||||
|
int child[2],
|
||||||
|
int parent[2],
|
||||||
|
pid_t pid)
|
||||||
|
{
|
||||||
|
/* Parent process: Close ends that the parent does not use. */
|
||||||
|
close(child[READ_END]);
|
||||||
|
close(parent[WRITE_END]);
|
||||||
|
parent[WRITE_END] = INVALID_FD;
|
||||||
|
child[READ_END] = INVALID_FD;
|
||||||
|
|
||||||
|
/* Convert remaining pipe fds into stdio streams */
|
||||||
|
*out = fdopen(parent[READ_END], "r");
|
||||||
|
if (*out == NULL) {
|
||||||
|
return bi_popen_bail(in, out, child, parent);
|
||||||
|
}
|
||||||
|
parent[READ_END] = INVALID_FD;
|
||||||
|
|
||||||
|
*in = fdopen(child[WRITE_END], "w");
|
||||||
|
if (*in == NULL) {
|
||||||
|
return bi_popen_bail(in, out, child, parent);
|
||||||
|
}
|
||||||
|
child[WRITE_END] = INVALID_FD;
|
||||||
|
|
||||||
|
/* Make writes to *in unbuffered (so data is sent immediately) */
|
||||||
|
setvbuf(*in, NULL, _IONBF, BUFSIZ);
|
||||||
|
|
||||||
|
/* Success: return the child's pid, so the caller can use to wait. */
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
bi_popen(const char* const command, FILE** const in, FILE** const out)
|
||||||
|
{
|
||||||
|
/* Initialize file descriptors and pointers for safe cleanup. */
|
||||||
|
int to_child[2] = { INVALID_FD, INVALID_FD };
|
||||||
|
int to_parent[2] = { INVALID_FD, INVALID_FD };
|
||||||
|
*in = NULL;
|
||||||
|
*out = NULL;
|
||||||
|
|
||||||
|
/* Validate inputs. */
|
||||||
|
if (command == NULL || in == NULL || out == NULL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return bi_popen_bail(in, out, to_child, to_parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create pipe for sending data to the child (stdin of child). */
|
||||||
|
if (pipe(to_child) < 0) {
|
||||||
|
return bi_popen_bail(in, out, to_child, to_parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create pipe for recieving data from the child (stdout of child). */
|
||||||
|
if (pipe(to_parent) < 0) {
|
||||||
|
return bi_popen_bail(in, out, to_child, to_parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fork the process and have the child setup stdio redirection and exec */
|
||||||
|
const pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
return bi_popen_bail(in, out, to_child, to_parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid == 0) { // Child Process - execlp shouldn't return .
|
||||||
|
return bi_popen_child(in, out, to_child, to_parent, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parent process, convert the fd's to streams and return pid to wait */
|
||||||
|
return bi_popen_parent(in, out, to_child, to_parent, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
FILE* in = NULL;
|
||||||
|
FILE* out = NULL;
|
||||||
|
char* line = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
const int pid = bi_popen("/bin/bash", &in, &out);
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("bi_popen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(in, "date\n");
|
||||||
|
getline(&line, &size, out);
|
||||||
|
printf("-> %s", line);
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
// Since in this case we can tell the child to terminate, we'll do so
|
||||||
|
// and wait for it to terminate before we close down.
|
||||||
|
fprintf(in, "exit\n");
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
|
||||||
|
fclose(in);
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/* - | Copyright / About | ----------------------------------------------------
|
||||||
|
Copyright (c) 2026 Randy Jordan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Creates two pipes, forks, and runs the given command. One pipe is
|
||||||
|
connected between the given *in and the standard input stream of the child;
|
||||||
|
the other pipe is connected between the given *out and the standard output
|
||||||
|
stream of the child.
|
||||||
|
|
||||||
|
Returns the pid of the child on success, -1 otherwise. On error, errno
|
||||||
|
will be set accordingly.
|
||||||
|
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
#ifndef BI_POPEN_H
|
||||||
|
#define BI_POPEN_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
extern pid_t
|
||||||
|
bi_popen(const char* const command, FILE** const in, FILE** const out);
|
||||||
|
|
||||||
|
#endif // bi_popen.h
|
||||||
Reference in New Issue
Block a user