callisto/lprocess.c
Jeremy Baxter b5d61a25cb rename the statically linked strlcpy to strbcpy
stands for "string bounds copy". Some systems like macOS include their
very own version of strlcpy and testing for that would require some
bloat like gnu automake so I'm just gonna rename it to prevent any
future conflicts. I only test on Linux and OpenBSD anyway. ;)
2024-01-25 19:47:57 +13:00

372 lines
7.2 KiB
C

/***
* Processes, signals, and signal handlers.
*
* @module process
*/
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <lua.h>
#include <lauxlib.h>
#include "callisto.h"
#include "util.h"
#define PID_MAX 8 /* rounded to the nearest even number */
#define PROCESS_MAX 256
/* signals */
static const int signals[] = {
SIGHUP,
SIGINT,
SIGQUIT,
SIGILL,
SIGTRAP,
SIGABRT,
SIGIOT,
SIGFPE,
SIGKILL,
SIGBUS,
SIGSEGV,
SIGSYS,
SIGPIPE,
SIGALRM,
SIGTERM,
SIGURG,
SIGSTOP,
SIGTSTP,
SIGCONT,
SIGCHLD,
SIGTTIN,
SIGTTOU,
#ifdef SIGSTKFL
SIGSTKFL,
#endif
SIGIO,
SIGXCPU,
SIGXFSZ,
#ifdef SIGVTALR
SIGVTALR,
#elif defined(SIGVTALRM)
SIGVTALRM,
#endif
SIGPROF,
#ifdef SIGWINC
SIGWINC,
#elif defined(SIGWINCH)
SIGWINCH,
#endif
#ifdef SIGINFO
SIGINFO,
#endif
#ifdef SIGPOLL
SIGPOLL,
#endif
#ifdef SIGPWR
SIGPWR,
#endif
SIGUSR1,
SIGUSR2,
-1 /* end */
};
static const char *sigstrs[] = {
[SIGHUP] = "SIGHUP",
[SIGINT] = "SIGINT",
[SIGQUIT] = "SIGQUIT",
[SIGILL] = "SIGILL",
[SIGTRAP] = "SIGTRAP",
[SIGABRT] = "SIGABRT",
[SIGIOT] = "SIGIOT",
[SIGFPE] = "SIGFPE",
[SIGKILL] = "SIGKILL",
[SIGBUS] = "SIGBUS",
[SIGSEGV] = "SIGSEGV",
[SIGSYS] = "SIGSYS",
[SIGPIPE] = "SIGPIPE",
[SIGALRM] = "SIGALRM",
[SIGTERM] = "SIGTERM",
[SIGURG] = "SIGURG",
[SIGSTOP] = "SIGSTOP",
[SIGTSTP] = "SIGTSTP",
[SIGCONT] = "SIGCONT",
[SIGCHLD] = "SIGCHLD",
[SIGTTIN] = "SIGTTIN",
[SIGTTOU] = "SIGTTOU",
#ifdef SIGSTKFL
[SIGSTKFL] = "SIGSTKFL",
#endif
[SIGIO] = "SIGIO",
[SIGXCPU] = "SIGXCPU",
[SIGXFSZ] = "SIGXFSZ",
#ifdef SIGVTALR
[SIGVTALR] = "SIGVTALR",
#elif defined(SIGVTALRM)
[SIGVTALRM] = "SIGVTALRM",
#endif
[SIGPROF] = "SIGPROF",
#ifdef SIGWINC
[SIGWINC] = "SIGWINC",
#elif defined(SIGWINCH)
[SIGWINCH] = "SIGWINCH",
#endif
#ifdef SIGINFO
[SIGINFO] = "SIGINFO",
#endif
#ifdef SIGPOLL
[SIGPOLL] = "SIGPOLL",
#endif
#ifdef SIGPWR
[SIGPWR] = "SIGPWR",
#endif
[SIGUSR1] = "SIGUSR1",
[SIGUSR2] = "SIGUSR2"
};
/***
* Returns the PID of the current process.
*
* @function pid
* @usage local pid = process.pid()
*/
static int
process_pid(lua_State *L)
{
lua_pushinteger(L, getpid());
return 1;
}
/***
* Returns the PID (process ID) of the given process,
* or nil if the process could not be found.
*
* Depends on the nonportable userspace utility `pgrep`.
* Can be found in the procps-ng package on Linux usually
* included in most distributions, or as part of the base
* system on OpenBSD, NetBSD and FreeBSD.
*
* @function pidof
* @usage process.pidof("init")
* @tparam string process The name of the process to look up.
*/
static int
process_pidof(lua_State *L)
{
const char *process; /* parameter 1 (string) */
char command[PROCESS_MAX]; /* pgrep command buffer */
char *buffer; /* pgrep reading buffer */
int pexit; /* pgrep exit code */
long pid; /* pid to return to Lua */
size_t pidmax; /* length passed to getline */
ssize_t ret; /* getline return value */
FILE *p; /* pgrep reading stream */
process = luaL_checkstring(L, 1);
/* construct pgrep command */
memset(command, 0, PROCESS_MAX * sizeof(char));
strbcat(command, "pgrep '", PROCESS_MAX);
strbcat(command, process, PROCESS_MAX);
strbcat(command, "' | sed 1q", PROCESS_MAX);
p = popen(command, "r");
buffer = malloc(PID_MAX * sizeof(char *));
pidmax = PID_MAX;
/* read line from pgrep */
ret = getline(&buffer, &pidmax, p);
pexit = pclose(p);
if (ret == -1 || pexit != 0) { /* did getline or pgrep fail? */
lua_pushnil(L);
return 1;
}
/* convert it to an integer and push */
pid = strtol(buffer, NULL, 10);
lua_pushinteger(L, pid);
free(buffer);
return 1;
}
#define REG_SIGC "callisto!process:sigc"
static int
initsignals(lua_State *L)
{
int sigc;
/* get the registry entry containing the sig count */
lua_getfield(L, LUA_REGISTRYINDEX, REG_SIGC);
/* if the registry entry isn't a number greater than 0... */
if (!lua_isnoneornil(L, -1)) {
if (lua_type(L, -1) != LUA_TNUMBER || lua_tointeger(L, -1) <= 0) {
luaL_error(L, "registry index for signal count is invalid");
return 0;
}
}
sigc = 0;
while (signals[sigc] != -1)
sigc++;
lua_pushinteger(L, sigc + 1);
lua_setfield(L, LUA_REGISTRYINDEX, REG_SIGC);
return 1;
}
static int
strtosig(const char *sig, int sigc)
{
int i, j;
j = 0;
for (i = signals[j]; i < sigc; j++) {
i = signals[j];
if (strcasecmp(sigstrs[i], sig) == 0)
return i; /* signal found */
}
return -1; /* invalid signal */
}
/***
* Returns the given signal as an integer.
*
* This signal value is a platform-dependent value;
* do not attempt to use it portably across different platforms.
*
* @function signum
* @usage local sigkill = process.signum("SIGKILL")
* @tparam string signal The signal to look up.
*/
static int
process_signum(lua_State *L)
{
char *sigstr;
int sig, sigc;
sigstr = strdup(luaL_checkstring(L, 1));
if (!initsignals(L))
return 0;
lua_getfield(L, LUA_REGISTRYINDEX, REG_SIGC);
sigc = lua_tointeger(L, -1);
sig = strtosig(sigstr, sigc);
free(sigstr);
if (sig != -1) { /* valid signal? */
lua_pushinteger(L, sig); /* return signal */
return 1;
}
return luaL_error(L, "no such signal");
}
static int
sigsend(lua_State *L, pid_t pid, const char *sigstr)
{
int ret, sig, sigc;
if (!initsignals(L))
return 0;
lua_getfield(L, LUA_REGISTRYINDEX, REG_SIGC);
sigc = lua_tointeger(L, -1);
sig = strtosig(sigstr, sigc);
if (sig != -1) /* valid signal? */
ret = kill(pid, sig);
else
return luaL_error(L, "no such signal");
if (ret == 0) { /* check for success */
lua_pushboolean(L, 1);
return 1;
}
return lfail(L);
}
#undef REG_SIGC
/***
* Sends the given signal to the process with the given PID.
*
* The *signal* parameter is a string containing the name
* of the desired signal to send (e.g. SIGKILL).
*
* @function send
* @usage
local pid = process.pid("sh")
process.send(pid, "SIGTERM")
* @tparam integer pid The PID of the process.
* @tparam string signal The signal to send.
*/
static int
process_send(lua_State *L)
{
pid_t pid; /* parameter 1 (integer) */
const char *sig; /* parameter 2 (string) */
pid = luaL_checkinteger(L, 1);
sig = luaL_checkstring(L, 2);
return sigsend(L, pid, sig);
}
/***
* Kills the process with the given PID.
*
* Equivalent to `process.send(pid, "SIGKILL")`.
*
* @function kill
* @tparam integer pid The PID of the process.
*/
static int
process_kill(lua_State *L)
{
pid_t pid; /* parameter 1 (integer) */
pid = luaL_checkinteger(L, 1);
return sigsend(L, pid, "SIGKILL");
}
/***
* Terminates the process with the given PID.
*
* Equivalent to `process.send(pid, "SIGTERM")`.
*
* @function terminate
* @tparam integer pid The PID of the process.
*/
static int
process_terminate(lua_State *L)
{
pid_t pid; /* parameter 1 (integer) */
pid = luaL_checkinteger(L, 1);
return sigsend(L, pid, "SIGTERM");
}
static const luaL_Reg proclib[] = {
{"kill", process_kill},
{"pid", process_pid},
{"pidof", process_pidof},
{"send", process_send},
{"signum", process_signum},
{"terminate", process_terminate},
{NULL, NULL}
};
int
luaopen_process(lua_State *L)
{
luaL_newlib(L, proclib);
return 0;
}