return PyLong_FromLong(cmp);
}
-static int __Pakfire_logging_callback(struct pakfire* pakfire, void* data,
+static int Pakfire_execute_output_callback(struct pakfire* pakfire, void* data,
int priority, const char* line, size_t length) {
PyObject* callback = (PyObject*)data;
int r = 0;
"command",
"environ",
"bind",
- "logging_callback",
+ "callback",
"nice",
- "return_output",
NULL
};
int flags = 0;
int r;
PyObject* ret = NULL;
- char* output = NULL;
PyObject* command = NULL;
PyObject* environ = NULL;
PyObject* bind = NULL;
- PyObject* logging_callback = NULL;
+ PyObject* callback = NULL;
int nice = 0;
- int return_output = 0;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOip", kwlist, &command, &environ,
- &bind, &logging_callback, &nice, &return_output))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOi", kwlist, &command, &environ,
+ &bind, &callback, &nice))
return NULL;
// Check if command is a list
goto ERROR;
}
+ // Check callback
+ if (callback && !PyCallable_Check(callback)) {
+ PyErr_SetString(PyExc_TypeError, "callback must be callable\n");
+ goto ERROR;
+ }
+
// Create jail
r = pakfire_jail_create(&jail, self->pakfire, flags);
if (r) {
goto ERROR;
}
- // Set logging callback
- if (logging_callback) {
- if (!PyCallable_Check(logging_callback)) {
- PyErr_SetString(PyExc_TypeError, "logging_callback must be callable\n");
- goto ERROR;
- }
-
- pakfire_jail_set_log_callback(jail, __Pakfire_logging_callback, logging_callback);
+ // Check callback
+ if (callback && !PyCallable_Check(callback)) {
+ PyErr_SetString(PyExc_TypeError, "callback must be callable\n");
+ goto ERROR;
}
// Set nice
}
// Execute command
- r = pakfire_jail_exec(jail, argv, (return_output) ? &output : NULL);
+ r = pakfire_jail_exec_communicate(jail, argv,
+ NULL, Pakfire_execute_output_callback, callback);
// If the return code was negative, we had some internal error
if (r < 0) {
// The process has exited successfully
- // Did the user request the output?
- if (return_output) {
- // Return the buffer as bytes
- if (output)
- ret = PyBytes_FromString(output);
- }
-
- // Otherwise just return None
- if (!ret) {
- ret = Py_None;
- Py_INCREF(ret);
- }
+ // Return None
+ ret = Py_None;
+ Py_INCREF(ret);
ERROR:
if (argv)
free(argv);
- if (output)
- free(output);
-
- if (jail) {
- // Dereference the logging callback
- // It might happen that the jail is not being freed because something else is
- // holding a reference to it. We will however lose the reference to the logging
- // function here which is why we reset it.
- pakfire_jail_set_log_callback(jail, NULL, NULL);
-
+ if (jail)
pakfire_jail_unref(jail);
- }
return ret;
}
// Environment
char* env[ENVIRON_SIZE];
- // Logging
- pakfire_jail_log_callback log_callback;
- void* log_data;
-
// Mountpoints
struct pakfire_jail_mountpoint mountpoints[MAX_MOUNTPOINTS];
unsigned int num_mountpoints;
// Log pipes
struct pakfire_jail_pipes {
+ int stdin[2];
int stdout[2];
int stderr[2];
int log_DEBUG[2];
} pipes;
+ // Communicate
+ struct pakfire_jail_communicate {
+ pakfire_jail_communicate_in in;
+ pakfire_jail_communicate_out out;
+ void* data;
+ } communicate;
+
// Log buffers
struct pakfire_jail_buffers {
struct pakfire_log_buffer stdout;
DEBUG(j->pakfire, "Allocated new jail at %p\n", j);
- // Set default log callback
- r = pakfire_jail_set_log_callback(j, pakfire_jail_default_log_callback, NULL);
- if (r)
- goto ERROR;
-
// Set default environment
for (const struct environ* e = ENV; e->key; e++) {
r = pakfire_jail_set_env(j, e->key, e->val);
return 0;
}
-// Logging
-
-PAKFIRE_EXPORT int pakfire_jail_set_log_callback(struct pakfire_jail* jail,
- pakfire_jail_log_callback callback, void* data) {
- jail->log_callback = callback;
- jail->log_data = data;
-
- return 0;
-}
-
/*
This function replaces any logging in the child process.
*/
static int pakfire_jail_handle_log(struct pakfire_jail* jail,
struct pakfire_jail_exec* ctx, int priority, int fd,
- struct pakfire_log_buffer* buffer, pakfire_jail_log_callback callback, void* data) {
+ struct pakfire_log_buffer* buffer, pakfire_jail_communicate_out callback, void* data) {
char line[BUFFER_SIZE + 1];
// Fill up buffer from fd
int fd = events[i].data.fd;
struct pakfire_log_buffer* buffer = NULL;
- pakfire_jail_log_callback callback = NULL;
+ pakfire_jail_communicate_out callback = NULL;
void* data = NULL;
int priority;
buffer = &ctx->buffers.stdout;
priority = LOG_INFO;
- callback = jail->log_callback;
- data = jail->log_data;
+ callback = ctx->communicate.out;
+ data = ctx->communicate.data;
} else if (fd == stderr) {
buffer = &ctx->buffers.stderr;
priority = LOG_ERR;
- callback = jail->log_callback;
- data = jail->log_data;
+ callback = ctx->communicate.out;
+ data = ctx->communicate.data;
} else {
DEBUG(jail->pakfire, "Received invalid file descriptor %d\n", fd);
char** output = (char**)data;
int r;
- // Append everything from stdout to a buffer
- if (priority == LOG_INFO) {
+ // Append everything from stdout to a buffer
+ if (output && priority == LOG_INFO) {
r = asprintf(output, "%s%s", (output && *output) ? *output : "", line);
if (r < 0)
return 1;
return r;
}
+static int pakfire_jail_stream_stdin(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
+ int r;
+
+ // Nothing to do if there is no stdin callback set
+ if (!ctx->communicate.in) {
+ DEBUG(jail->pakfire, "Callback for standard input is not set\n");
+ return 0;
+ }
+
+ int* fd = &ctx->pipes.stdin[1];
+
+ DEBUG(jail->pakfire, "Streaming standard input...\n");
+
+ // Calling the callback
+ r = ctx->communicate.in(jail->pakfire, ctx->communicate.data, *fd);
+
+ DEBUG(jail->pakfire, "Standard input callback finished: %d\n", r);
+
+ // Close the file descriptor when we are done
+ close(*fd);
+ *fd = 0;
+
+ return r;
+}
+
/*
Performs the initialisation that needs to happen in the parent part
*/
if (r)
return r;
+ // Stream standard input
+ r = pakfire_jail_stream_stdin(jail, ctx);
+ if (r)
+ return r;
+
return 0;
}
DEBUG(jail->pakfire, "Launched child process in jail with PID %d\n", pid);
- // Log argv
- for (unsigned int i = 0; argv[i]; i++)
- DEBUG(jail->pakfire, " argv[%d] = %s\n", i, argv[i]);
-
// Wait for the parent to finish initialization
r = pakfire_jail_wait_for_signal(jail, ctx->completed_fd);
if (r)
close(ctx->pipes.log_DEBUG[0]);
#endif /* ENABLE_DEBUG */
+ // Connect standard input
+ if (ctx->pipes.stdin[0]) {
+ r = dup2(ctx->pipes.stdin[0], STDIN_FILENO);
+ if (r < 0) {
+ ERROR(jail->pakfire, "Could not connect fd %d to stdin: %m\n",
+ ctx->pipes.stdin[0]);
+
+ return 1;
+ }
+ }
+
// Connect standard output and error
if (ctx->pipes.stdout[1] && ctx->pipes.stderr[1]) {
r = dup2(ctx->pipes.stdout[1], STDOUT_FILENO);
}
// Close the pipe (as we have moved the original file descriptors)
+ pakfire_jail_close_pipe(jail, ctx->pipes.stdin);
pakfire_jail_close_pipe(jail, ctx->pipes.stdout);
pakfire_jail_close_pipe(jail, ctx->pipes.stderr);
}
if (r)
return r;
+ DEBUG(jail->pakfire, "Child process initialization done\n");
+ DEBUG(jail->pakfire, "Launching command:\n");
+
+ // Log argv
+ for (unsigned int i = 0; argv[i]; i++)
+ DEBUG(jail->pakfire, " argv[%d] = %s\n", i, argv[i]);
+
// exec() command
r = execvpe(argv[0], (char**)argv, jail->env);
if (r < 0)
// Run a command in the jail
static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
- const int interactive) {
+ const int interactive,
+ pakfire_jail_communicate_in communicate_in,
+ pakfire_jail_communicate_out communicate_out,
+ void* data) {
int exit = -1;
int r;
return -1;
}
+ // Send any output to the default logger if no callback is set
+ if (!communicate_out)
+ communicate_out = pakfire_jail_default_log_callback;
+
// Initialize context for this call
struct pakfire_jail_exec ctx = {
.pipes = {
- .stdout = { 0, 0 },
- .stderr = { 0, 0 },
+ .stdin = { 0, 0 },
+ .stdout = { 0, 0 },
+ .stderr = { 0, 0 },
+ },
+
+ .communicate = {
+ .in = communicate_in,
+ .out = communicate_out,
+ .data = data,
},
};
// Create pipes to communicate with child process if we are not running interactively
if (!interactive) {
+ // stdin (only if callback is set)
+ if (ctx.communicate.in) {
+ r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdin, 0);
+ if (r)
+ goto ERROR;
+ }
+
// stdout
r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdout, 0);
if (r)
}
// Close any file descriptors
+ pakfire_jail_close_pipe(jail, ctx.pipes.stdin);
pakfire_jail_close_pipe(jail, ctx.pipes.stdout);
pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
if (ctx.pidfd)
return exit;
}
+PAKFIRE_EXPORT int pakfire_jail_exec_communicate(
+ struct pakfire_jail* jail,
+ const char* argv[],
+ pakfire_jail_communicate_in callback_in,
+ pakfire_jail_communicate_out callback_out,
+ void* data) {
+ return __pakfire_jail_exec(jail, argv, 0, callback_in, callback_out, data);
+}
+
PAKFIRE_EXPORT int pakfire_jail_exec(struct pakfire_jail* jail,
const char* argv[], char** output) {
int r;
- // Store logging callback
- pakfire_jail_log_callback log_callback = jail->log_callback;
- void* log_data = jail->log_data;
-
- // Capture output if requested by user
- if (output)
- pakfire_jail_set_log_callback(jail, pakfire_jail_capture_stdout, output);
-
// Run exec()
- r = __pakfire_jail_exec(jail, argv, 0);
-
- // Restore log callback
- pakfire_jail_set_log_callback(jail, log_callback, log_data);
+ r = __pakfire_jail_exec(jail, argv, 0,
+ NULL, pakfire_jail_capture_stdout, output);
return r;
}
if (r)
return r;
- return __pakfire_jail_exec(jail, argv, 1);
+ return __pakfire_jail_exec(jail, argv, 1, NULL, NULL, NULL);
}
PAKFIRE_EXPORT int pakfire_jail_exec_script(struct pakfire_jail* jail,