/*
 * Copyright 2001, 2002, 2003, 2025 Adam Sampson <ats@offog.org>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "iolib.h"
#include "freedt.h"
#include "config.h"

void warn(const char *msg) {
	format(fd_err, "@c: @c\n", progname, msg);
}

void warn2(const char *msg1, const char *msg2) {
	format(fd_err, "@c: @c: @c\n", progname, msg1, msg2);
}

void die(const char *msg) {
	warn(msg);
	exit(111);
}

void die2(const char *msg1, const char *msg2) {
	warn2(msg1, msg2);
	exit(111);
}

static void show_version() {
	format(fd_out, "@c from freedt version " VERSION "\n", progname);
}

void version() {
	show_version();
	exit(1);
}

void help() {
	show_version();
	format(fd_out, "\n@c"
		"-V        Show version information\n"
		"-?        Show usage information\n", proghelp);
	exit(1);
}

void get_default_args(int argc, char **argv) {
	while (1) {
		int c = getopt(argc, argv, "+V?");
		if (c == -1)
			break;

		switch (c) {
		case 'V':
			version();
		default:
			help();
		}
	}
}

void setuidgidroot() {
	char *root, *uid, *gid;

	root = getenv("ROOT");
	if (root != NULL) {
		if (chroot(root) < 0)
			die2(root, "unable to chroot");
		if (chdir("/") < 0)
			die2(root, "unable to chdir");
	}

	gid = getenv("GID");
	if (gid != NULL) {
		gid_t g = atoi(gid);

		if (setgid(g) < 0)
			die("unable to setgid");
		if (setgroups(1, &g) < 0)
			die("unable to setgroups");
	}

	uid = getenv("UID");
	if (uid != NULL) {
		uid_t u = atoi(uid);

		if (setuid(u) < 0)
			die("unable to setuid");
	}
}

void change_fd_flags(int fd, int add, int remove) {
	int flags = fcntl(fd, F_GETFL);
	if (flags < 0)
		die("unable to read FD flags");

	flags |= add;
	flags &= ~remove;

	if (fcntl(fd, F_SETFL, flags) < 0)
		die("unable to set FD flags");
}

void set_fd_cloexec(int fd) {
	if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
		die("unable to set FD_CLOEXEC");
}

void reliable_sleep(int seconds) {
	while (seconds > 0)
		seconds = sleep(seconds);
}

int lock_fd(int fd, int block) {
	struct flock lock;
	int rc;

	lock.l_type = F_WRLCK;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;

	do {
		rc = fcntl(fd, block ? F_SETLKW : F_SETLK, &lock);
	} while (rc < 0 && errno == EINTR);
	return rc;
}
