8 #include "vis-subprocess.h"
11 /* Pool of information about currently running subprocesses */
12 static Process
*process_pool
;
15 * Adds new empty process information structure to the process pool and
17 * @return a new Process instance
19 static Process
*new_process_in_pool(void) {
20 Process
*newprocess
= malloc(sizeof(Process
));
24 newprocess
->next
= process_pool
;
25 process_pool
= newprocess
;
30 * Removes the subprocess information from the pool, sets invalidator to NULL
31 * and frees resources.
32 * @param a reference to the process to be removed
33 * @return the next process in the pool
35 static Process
*destroy_process(Process
*target
) {
36 if (target
->outfd
!= -1) {
39 if (target
->errfd
!= -1) {
42 if (target
->inpfd
!= -1) {
45 /* marking stream as closed for lua */
46 if (target
->invalidator
) {
47 *(target
->invalidator
) = NULL
;
49 Process
*next
= target
->next
;
57 * Starts new subprocess by passing the `command` to the shell and
58 * returns the subprocess information structure, containing file descriptors
60 * Also stores the subprocess information to the internal pool to track
61 * its status and responses.
62 * @param name a string that contains a unique name for the subprocess.
63 * This name will be passed to the PROCESS_RESPONSE event handler
64 * to distinguish running subprocesses.
65 * @param command a command to be executed to spawn a process
66 * @param invalidator a pointer to the pointer which shows that the subprocess
67 * is invalid when set to NULL. When the subprocess dies, it is set to NULL.
68 * If a caller sets the pointer to NULL the subprocess will be killed on the
69 * next main loop iteration.
71 Process
*vis_process_communicate(Vis
*vis
, const char *name
,
72 const char *command
, Invalidator
**invalidator
) {
73 int pin
[2], pout
[2], perr
[2];
74 pid_t pid
= (pid_t
)-1;
75 if (pipe(perr
) == -1) {
78 if (pipe(pout
) == -1) {
81 if (pipe(pin
) == -1) {
86 vis_info_show(vis
, "fork failed: %s", strerror(errno
));
87 } else if (pid
== 0) { /* child process */
88 sigset_t sigterm_mask
;
89 sigemptyset(&sigterm_mask
);
90 sigaddset(&sigterm_mask
, SIGTERM
);
91 if (sigprocmask(SIG_UNBLOCK
, &sigterm_mask
, NULL
) == -1) {
92 fprintf(stderr
, "failed to reset signal mask");
95 dup2(pin
[0], STDIN_FILENO
);
96 dup2(pout
[1], STDOUT_FILENO
);
97 dup2(perr
[1], STDERR_FILENO
);
98 } else { /* main process */
99 Process
*new = new_process_in_pool();
101 vis_info_show(vis
, "Cannot create process: %s", strerror(errno
));
104 new->name
= strdup(name
);
106 vis_info_show(vis
, "Cannot copy process name: %s", strerror(errno
));
107 /* pop top element (which is `new`) from the pool */
108 process_pool
= destroy_process(process_pool
);
111 new->outfd
= pout
[0];
112 new->errfd
= perr
[0];
115 new->invalidator
= invalidator
;
130 if (pid
== 0) { /* start command in child process */
131 execlp(vis
->shell
, vis
->shell
, "-c", command
, (char*)NULL
);
132 fprintf(stderr
, "exec failed: %s(%d)\n", strerror(errno
), errno
);
135 vis_info_show(vis
, "process creation failed: %s", strerror(errno
));
141 * Adds file descriptors of currently running subprocesses to the `readfds`
142 * to track their readiness and returns maximum file descriptor value
143 * to pass it to the `pselect` call
144 * @param readfds the structure for `pselect` call to fill
145 * @return maximum file descriptor number in the readfds structure
147 int vis_process_before_tick(fd_set
*readfds
) {
149 for (Process
**pointer
= &process_pool
; *pointer
; pointer
= &((*pointer
)->next
)) {
150 Process
*current
= *pointer
;
151 if (current
->outfd
!= -1) {
152 FD_SET(current
->outfd
, readfds
);
153 maxfd
= maxfd
< current
->outfd
? current
->outfd
: maxfd
;
155 if (current
->errfd
!= -1) {
156 FD_SET(current
->errfd
, readfds
);
157 maxfd
= maxfd
< current
->errfd
? current
->errfd
: maxfd
;
164 * Reads data from the given subprocess file descriptor `fd` and fires
165 * the PROCESS_RESPONSE event in Lua with given subprocess `name`,
166 * `rtype` and the read data as arguments.
167 * @param fd the file descriptor to read data from
168 * @param name a name of the subprocess
169 * @param rtype a type of file descriptor where the new data is found
171 static void read_and_fire(Vis
* vis
, int fd
, const char *name
, ResponseType rtype
) {
172 static char buffer
[PIPE_BUF
];
173 size_t obtained
= read(fd
, &buffer
, PIPE_BUF
-1);
175 vis_lua_process_response(vis
, name
, buffer
, obtained
, rtype
);
180 * Checks if `readfds` contains file descriptors of subprocesses from
181 * the pool. If so, it reads their data and fires corresponding events.
182 * Also checks if each subprocess from the pool is dead or needs to be
183 * killed then raises an event or kills it if necessary.
184 * @param readfds the structure for `pselect` call with file descriptors
186 void vis_process_tick(Vis
*vis
, fd_set
*readfds
) {
187 for (Process
**pointer
= &process_pool
; *pointer
; ) {
188 Process
*current
= *pointer
;
189 if (current
->outfd
!= -1 && FD_ISSET(current
->outfd
, readfds
)) {
190 read_and_fire(vis
, current
->outfd
, current
->name
, STDOUT
);
192 if (current
->errfd
!= -1 && FD_ISSET(current
->errfd
, readfds
)) {
193 read_and_fire(vis
, current
->errfd
, current
->name
, STDERR
);
196 pid_t wpid
= waitpid(current
->pid
, &status
, WNOHANG
);
198 vis_message_show(vis
, strerror(errno
));
199 } else if (wpid
== current
->pid
) {
201 } else if (!*(current
->invalidator
)) {
202 goto kill_and_destroy
;
204 pointer
= ¤t
->next
;
207 kill(current
->pid
, SIGTERM
);
208 waitpid(current
->pid
, &status
, 0);
210 if (WIFSIGNALED(status
)) {
211 vis_lua_process_response(vis
, current
->name
, NULL
, WTERMSIG(status
), SIGNAL
);
213 vis_lua_process_response(vis
, current
->name
, NULL
, WEXITSTATUS(status
), EXIT
);
215 /* update our iteration pointer */
216 *pointer
= destroy_process(current
);