λ

swayaway

a drop-in replacement for swayidle(1)

settings

log.c

2791 bytes raw

#include "swayaway.h"

#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

static bool debug_enabled = false;

void log_init(bool debug)
{
	debug_enabled = debug;
}

void sway_log(const char *fmt, ...)
{
	time_t t = time(NULL);
	struct tm tm_buf;
	struct tm *tm_info = localtime_r(&t, &tm_buf);
	char ts[32];
	if (tm_info) {
		strftime(ts, sizeof(ts), "%F %T", tm_info);
	} else {
		strcpy(ts, "??????????");
	}

	va_list args;
	va_start(args, fmt);
	fprintf(stderr, "%s - ", ts);
	vfprintf(stderr, fmt, args);
	fprintf(stderr, "\n");
	va_end(args);
}

void sway_log_debug(const char *fmt, ...)
{
	if (!debug_enabled) {
		return;
	}
	time_t t = time(NULL);
	struct tm tm_buf;
	struct tm *tm_info = localtime_r(&t, &tm_buf);
	char ts[32];
	if (tm_info) {
		strftime(ts, sizeof(ts), "%F %T", tm_info);
	} else {
		strcpy(ts, "??????????");
	}
	va_list args;
	va_start(args, fmt);
	fprintf(stderr, "%s - [debug] ", ts);
	vfprintf(stderr, fmt, args);
	fprintf(stderr, "\n");
	va_end(args);
}

/* prepare the current process to run a user command: unblock signals that
 * were blocked for signalfd, restore default dispositions, and drop every fd
 * inherited from the daemon so long-running commands don't hold sockets
 * open. */
static void child_prepare_exec(void)
{
	sigset_t set;
	sigemptyset(&set);
	sigprocmask(SIG_SETMASK, &set, NULL);
	signal(SIGINT, SIG_DFL);
	signal(SIGTERM, SIG_DFL);
	signal(SIGUSR1, SIG_DFL);

	long max = sysconf(_SC_OPEN_MAX);
	if (max < 0 || max > 4096) {
		max = 4096;
	}
	for (long fd = 3; fd < max; fd++) {
		close((int)fd);
	}
}

/* run `cmd` through the shell ("sh -c")
 * with state.wait, double-fork and block until the command exits; otherwise
 * double-fork and return immediately, leaving the command running detached */
void cmd_exec(const char *cmd)
{
	if (!cmd) {
		return;
	}
	sway_log_debug("exec: %s", cmd);

	pid_t pid = fork();
	if (pid == 0) {
		if (!state.wait) {
			// intermediate child: fork the grandchild and exit
			pid = fork();
			if (pid > 0) {
				_exit(0);
			}
		}
		if (pid == 0) {
			child_prepare_exec();
			char *const argv[] = { "sh", "-c", (char *)cmd, NULL };
			execvp(argv[0], argv);
			sway_log("exec failed: %s", strerror(errno));
			_exit(1);
		}
		_exit(0);
	} else if (pid < 0) {
		sway_log("fork failed: %s", strerror(errno));
		return;
	}

	if (state.wait) {
		int status;
		sway_log_debug("waiting for command to finish");
		while (waitpid(pid, &status, 0) < 0) {
			if (errno != EINTR) {
				break;
			}
		}
		if (WIFEXITED(status)) {
			sway_log_debug("command exited: %d", WEXITSTATUS(status));
		}
	}
	// non-wait: reap the intermediate child
	else {
		waitpid(pid, NULL, 0);
	}
}