Skip to content

Commit

Permalink
Properly implement a message channel for scripts
Browse files Browse the repository at this point in the history
For shell scripts a PKG_MSGFD variable is defined for each scripts
representing the file descriptor of that message channel

echo "something" >&${PKG_MSGFD}

will append messages from script to pkg-messages

For lua script: print_msg will now use the same mechanism as shell
scripts
  • Loading branch information
bapt committed Jan 17, 2020
1 parent 2fab660 commit 767fbd7
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 3 deletions.
40 changes: 38 additions & 2 deletions libpkg/lua_scripts.c
Expand Up @@ -34,6 +34,7 @@
#include <sys/wait.h>

#include <errno.h>
#include <poll.h>
#include <utstring.h>
#include <lauxlib.h>
#include <lualib.h>
Expand Down Expand Up @@ -84,9 +85,11 @@ static int
lua_print_msg(lua_State *L)
{
const char* str = luaL_checkstring(L, 1);
lua_getglobal(L, "msgfd");
int fd = lua_tointeger(L, -1);

dprintf(fd, "%s\n", str);

pkg_emit_message(str);
pkg_emit_message("\n");
return (0);
}

Expand Down Expand Up @@ -126,6 +129,11 @@ pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type)
struct procctl_reaper_status info;
struct procctl_reaper_kill killemall;
#endif
struct pollfd pfd;
int cur_pipe[2];
char *line = NULL;
FILE *f;
ssize_t linecap = 0;

if (pkg->lua_scripts[type] == NULL)
return (EPKG_OK);
Expand All @@ -140,16 +148,23 @@ pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type)
#endif

LL_FOREACH(pkg->lua_scripts[type], lscript) {
if (get_socketpair(cur_pipe) == -1) {
pkg_emit_errno("pkg_run_script", "socketpair");
goto cleanup;
}
pid_t pid = fork();
if (pid > 0) {
static const luaL_Reg pkg_lib[] = {
{ "print_msg", lua_print_msg },
{ "prefixed_path", lua_prefix_path },
{ NULL, NULL },
};
close(cur_pipe[0]);
lua_State *L = luaL_newstate();
luaL_openlibs( L );
lua_atpanic(L, (lua_CFunction)stack_dump );
lua_pushinteger(L, cur_pipe[1]);
lua_setglobal(L, "msgfd");
lua_pushlightuserdata(L, pkg);
lua_setglobal(L, "package");
lua_pushliteral(L, "PREFIX");
Expand All @@ -175,6 +190,26 @@ pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type)
goto cleanup;
}

close(cur_pipe[1]);
memset(&pfd, 0, sizeof(pfd));
pfd.fd = cur_pipe[0];
pfd.events = POLLIN | POLLERR | POLLHUP;
f = fdopen(pfd.fd, "r");
for (;;) {
if (poll(&pfd, 1, -1) == -1) {
if (errno == EINTR)
continue;
else
goto cleanup;
}
if (pfd.revents & (POLLERR|POLLHUP))
break;
if (getline(&line, &linecap, f) > 0) {
pkg_emit_message(line);
}
}
fclose(f);

while (waitpid(pid, &pstat, 0) == -1) {
if (errno != EINTR) {
ret = EPKG_FATAL;
Expand Down Expand Up @@ -208,6 +243,7 @@ pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type)
}
procctl(P_PID, mypid, PROC_REAP_RELEASE, NULL);
#endif
free(line);

return (ret);
}
Expand Down
45 changes: 44 additions & 1 deletion libpkg/scripts.c
Expand Up @@ -37,6 +37,7 @@
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <poll.h>
#include <spawn.h>
#include <stdlib.h>
#include <limits.h>
Expand All @@ -58,7 +59,7 @@ pkg_script_run(struct pkg * const pkg, pkg_script type)
pid_t pid;
const char *script_cmd_p;
const char *argv[4];
char **ep;
char **ep, *buf;
int ret = EPKG_OK;
int fd = -1;
int stdin_pipe[2] = {-1, -1};
Expand All @@ -68,12 +69,17 @@ pkg_script_run(struct pkg * const pkg, pkg_script type)
ssize_t bytes_written;
size_t script_cmd_len;
long argmax;
int cur_pipe[2];
#ifdef PROC_REAP_KILL
bool do_reap;
pid_t mypid;
struct procctl_reaper_status info;
struct procctl_reaper_kill killemall;
#endif
struct pollfd pfd;
ssize_t linecap = 0;
char *line = NULL;
FILE *f;

struct {
const char * const arg;
Expand Down Expand Up @@ -142,10 +148,21 @@ pkg_script_run(struct pkg * const pkg, pkg_script type)

pkg_debug(3, "Scripts: executing\n--- BEGIN ---\n%s\nScripts: --- END ---", utstring_body(script_cmd));
posix_spawn_file_actions_init(&action);
if (get_socketpair(cur_pipe) == -1) {
pkg_emit_errno("pkg_run_script", "socketpair");
goto cleanup;
}

asprintf(&buf, "%d", cur_pipe[1]);
setenv("PKG_MSGFD", buf, 1);
free(buf);

if (utstring_len(script_cmd) > argmax) {
if (pipe(stdin_pipe) < 0) {
ret = EPKG_FATAL;
posix_spawn_file_actions_destroy(&action);
close(cur_pipe[0]);
close(cur_pipe[1]);
goto cleanup;
}

Expand All @@ -164,6 +181,8 @@ pkg_script_run(struct pkg * const pkg, pkg_script type)
pkg_errno("Cannot open %s", "/dev/null");
ret = EPKG_FATAL;
posix_spawn_file_actions_destroy(&action);
close(cur_pipe[0]);
close(cur_pipe[1]);
goto cleanup;
}
posix_spawn_file_actions_adddup2(&action,
Expand All @@ -183,6 +202,8 @@ pkg_script_run(struct pkg * const pkg, pkg_script type)
errno = error;
pkg_errno("Cannot runscript %s", map[i].arg);
posix_spawn_file_actions_destroy(&action);
close(cur_pipe[0]);
close(cur_pipe[1]);
goto cleanup;
}
posix_spawn_file_actions_destroy(&action);
Expand All @@ -208,6 +229,27 @@ pkg_script_run(struct pkg * const pkg, pkg_script type)

unsetenv("PKG_PREFIX");

close(cur_pipe[1]);
memset(&pfd, 0, sizeof(pfd));
pfd.fd = cur_pipe[0];
pfd.events = POLLIN | POLLERR | POLLHUP;

f = fdopen(pfd.fd, "r");
for (;;) {
if (poll(&pfd, 1, -1) == -1) {
if (errno == EINTR)
continue;
else
goto cleanup;
}
if (pfd.revents & (POLLERR|POLLHUP))
break;
if (getline(&line, &linecap, f) > 0) {
pkg_emit_message(line);
}
}
fclose(f);

while (waitpid(pid, &pstat, 0) == -1) {
if (errno != EINTR)
goto cleanup;
Expand All @@ -226,6 +268,7 @@ pkg_script_run(struct pkg * const pkg, pkg_script type)

cleanup:

free(line);
utstring_free(script_cmd);
if (stdin_pipe[0] != -1)
close(stdin_pipe[0]);
Expand Down

2 comments on commit 767fbd7

@sobomax
Copy link

@sobomax sobomax commented on 767fbd7 Apr 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This, unfortunately, creates some additional complication due to the fact that FD4 is now propagated into all descendants that the script creates and pkg would not exit until the last of them exits, even if the script itself has finished long time ago. As such, any runaway process would just make pkg hang indefinitely, which was not the case before. Yes, it's not nice to have a runaway processes but also true that ShitHappens[tm] and it would just create constant headache for people running automated build farms. Potential workaround perhaps is to have a timeout on poll() and mix it with calls to waitpid(WNOHANG) so that either condition would break the loop.

@sobomax
Copy link

@sobomax sobomax commented on 767fbd7 Apr 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.