Get output of `posix_spawn`
Solution 1
Here's a minimal example of modifying file descriptors of a spawned process, saved as foo.c
:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <spawn.h>
int main(int argc, char* argv[], char *env[])
{
int ret;
pid_t child_pid;
posix_spawn_file_actions_t child_fd_actions;
if (ret = posix_spawn_file_actions_init (&child_fd_actions))
perror ("posix_spawn_file_actions_init"), exit(ret);
if (ret = posix_spawn_file_actions_addopen (&child_fd_actions, 1, "/tmp/foo-log",
O_WRONLY | O_CREAT | O_TRUNC, 0644))
perror ("posix_spawn_file_actions_addopen"), exit(ret);
if (ret = posix_spawn_file_actions_adddup2 (&child_fd_actions, 1, 2))
perror ("posix_spawn_file_actions_adddup2"), exit(ret);
if (ret = posix_spawnp (&child_pid, "date", &child_fd_actions, NULL, argv, env))
perror ("posix_spawn"), exit(ret);
}
What does it do?
- The third parameter of
posix_spwan
is a pointer of typeposix_spawn_file_actions_t
(one you have given asNULL
).posix_spawn
will open, close or duplicate file descriptors inherited from the calling process as specified by theposix_spawn_file_actions_t
object. - So we start with a
posix_spawn_file_actions_t
object (chiild_fd_actions
), and initialize it withposix_spawn_file_actions_init()
. - Now, the
posix_spawn_file_actions_{addopen,addclose,addup2}
functions can be used to open, close or duplicate file descriptors (after theopen(3)
,close(3)
anddup2(3)
functions) respectively. - So we
posix_spawn_file_actions_addopen
a file at/tmp/foo-log
to file descriptor1
(aka stdout). - Then we
posix_spawn_file_actions_adddup2
fd2
(akastderr
) to fd 1. - Note that nothing has been opened or duped yet. The last two functions simply changed the
child_fd_actions
object to note that these actions are to be taken. - And finally we use
posix_spawn
with thechild_fd_actions
object.
Testing it out:
$ make foo
cc foo.c -o foo
$ ./foo
$ cat /tmp/foo-log
Sun Jan 3 03:48:17 IST 2016
$ ./foo +'%F %R'
$ cat /tmp/foo-log
2016-01-03 03:48
$ ./foo -d 'foo'
$ cat /tmp/foo-log
./foo: invalid date ‘foo’
As you can see, both stdout and stderr of the spawned process went to /tmp/foo-log
.
Solution 2
Yes, you can. Defining the right list of posix spawn file actions definitely is the way to go.
Example:
#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <stdio.h>
#include <string.h>
#define CHECK_ERROR(R, MSG) do { if (R) { fprintf(stderr, "%s: %s\n",
(MSG), strerror(R)); return 1; } } while (0)
extern char **environ;
int main(int argc, char **argv)
{
if (argc < 3) {
fprintf(stderr, "Call: %s OUTFILE COMMAND [ARG]...\n", argv[0]);
return 2;
}
const char *out_filename = argv[1];
char **child_argv = argv+2;
posix_spawn_file_actions_t as;
int r = posix_spawn_file_actions_init(&as);
CHECK_ERROR(r, "actions init");
r = posix_spawn_file_actions_addopen(&as, 1, out_filename,
O_CREAT | O_TRUNC | O_WRONLY, 0644);
CHECK_ERROR(r, "addopen");
r = posix_spawn_file_actions_adddup2(&as, 1, 2);
CHECK_ERROR(r, "adddup2");
pid_t child_pid;
r = posix_spawnp(&child_pid, child_argv[0], &as, NULL,
child_argv, environ);
CHECK_ERROR(r, "spawnp");
r = posix_spawn_file_actions_destroy(&as);
CHECK_ERROR(r, "actions destroy");
return 0;
}
Compile and test:
$ cc -Wall -g -o spawnp spawnp.c
$ ./spawnp log date -I
$ cat log
2018-11-03
$ ./a.out log dat
spawnp: No such file or directory
Note that the posix_spawn
functions don't set errno, instead, unlike most other UNIX functions, they return an error code. Thus, we can't use perror()
but have to use something like strerror()
.
We use two spawn file actions: addopen and addup2. The addopen is similar to a normal open()
but you also specify a file descriptor which is automatically closed if already open (here 1, i.e. stdout). The addup2 has similar effects to dup2()
, i.e. the target file descriptor (here 2, i.e. stderr) is atomically closed before 1 is duplicated to 2. Those actions are only executed in the child created by posix_spawn
, i.e. right before it execs the specified command.
Like fork()
, posix_spawn()
and posix_spawnp()
immediately return to the parent. Thus, we have to use waitid()
or waitpid()
to explicitly wait on child_pid
's termination.
Related videos on Youtube
nbubis
Updated on September 18, 2022Comments
-
nbubis over 1 year
So I can run a process in Unix / Linux using POSIX, but is there some way I can store / redirect both the STDOUT and STDERR of the process to a file? The
spawn.h
header contains a deceleration ofposix_spawn_file_actions_adddup2
which looks relevant, but I'm not sure quite how to use it.The process spawn:
posix_spawn(&processID, (char *)"myprocess", NULL, NULL, args, environ);
The output storage:
...?
-
Admin over 8 yearsThe third paramater of
posix_spwan
is a pointer of typeposix_spawn_file_actions_t
(one you have given asNULL
).posix_spawn
will open, close or duplicate file descriptors inherited from the calling process as specified by theposix_spawn_file_actions_t
object. Theposix_spawn_file_actions_{addclose,adddup2}
functions are used to indicate what happens to which fd. -
Admin over 8 years@muru - Do you think you could add a working example? I understood the interaction between the functions is done by a "file action", but it's not clear how exactly this fits together, or where the fd location is defined.
-
-
maxschlepzig over 5 yearsNote that
posix_spawn*
don't set errno. Thus, you can't useperror()
. Use something likefprintf(stderr, "...: %s\n", strerror(ret))
instead. Also, the main function is missing areturn 0
statement. -
Joelio about 2 yearsIn this case do you need to close the fd also?