Blame tools/fpgad/command_line.c

Packit 6639f8
// Copyright(c) 2018-2020, Intel Corporation
Packit 6639f8
//
Packit 6639f8
// Redistribution  and  use  in source  and  binary  forms,  with  or  without
Packit 6639f8
// modification, are permitted provided that the following conditions are met:
Packit 6639f8
//
Packit 6639f8
// * Redistributions of  source code  must retain the  above copyright notice,
Packit 6639f8
//   this list of conditions and the following disclaimer.
Packit 6639f8
// * Redistributions in binary form must reproduce the above copyright notice,
Packit 6639f8
//   this list of conditions and the following disclaimer in the documentation
Packit 6639f8
//   and/or other materials provided with the distribution.
Packit 6639f8
// * Neither the name  of Intel Corporation  nor the names of its contributors
Packit 6639f8
//   may be used to  endorse or promote  products derived  from this  software
Packit 6639f8
//   without specific prior written permission.
Packit 6639f8
//
Packit 6639f8
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
Packit 6639f8
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED TO,  THE
Packit 6639f8
// IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit 6639f8
// ARE DISCLAIMED.  IN NO EVENT  SHALL THE COPYRIGHT OWNER  OR CONTRIBUTORS BE
Packit 6639f8
// LIABLE  FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR
Packit 6639f8
// CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT LIMITED  TO,  PROCUREMENT  OF
Packit 6639f8
// SUBSTITUTE GOODS OR SERVICES;  LOSS OF USE,  DATA, OR PROFITS;  OR BUSINESS
Packit 6639f8
// INTERRUPTION)  HOWEVER CAUSED  AND ON ANY THEORY  OF LIABILITY,  WHETHER IN
Packit 6639f8
// CONTRACT,  STRICT LIABILITY,  OR TORT  (INCLUDING NEGLIGENCE  OR OTHERWISE)
Packit 6639f8
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  EVEN IF ADVISED OF THE
Packit 6639f8
// POSSIBILITY OF SUCH DAMAGE.
Packit 6639f8
Packit 6639f8
#define _GNU_SOURCE
Packit 6639f8
#ifdef HAVE_CONFIG_H
Packit 6639f8
#include <config.h>
Packit 6639f8
#endif // HAVE_CONFIG_H
Packit 6639f8
Packit 6639f8
#include <getopt.h>
Packit 6639f8
#include <fcntl.h>
Packit 6639f8
#include <sys/stat.h>
Packit 6639f8
#include <sys/types.h>
Packit 6639f8
#include <pwd.h>
Packit 6639f8
#include "command_line.h"
Packit 6639f8
#include "config_file.h"
Packit 6639f8
#include "monitored_device.h"
Packit 6639f8
Packit 6639f8
#ifdef LOG
Packit 6639f8
#undef LOG
Packit 6639f8
#endif
Packit 6639f8
#define LOG(format, ...) \
Packit 6639f8
log_printf("args: " format, ##__VA_ARGS__)
Packit 6639f8
Packit 6639f8
extern fpgad_supported_device default_supported_devices_table[];
Packit 6639f8
Packit 6639f8
#define OPT_STR ":hdl:p:s:n:c:v"
Packit 6639f8
Packit 6639f8
STATIC struct option longopts[] = {
Packit 6639f8
	{ "help",           no_argument,       NULL, 'h' },
Packit 6639f8
	{ "daemon",         no_argument,       NULL, 'd' },
Packit 6639f8
	{ "logfile",        required_argument, NULL, 'l' },
Packit 6639f8
	{ "pidfile",        required_argument, NULL, 'p' },
Packit 6639f8
	{ "socket",         required_argument, NULL, 's' },
Packit 6639f8
	{ "null-bitstream", required_argument, NULL, 'n' },
Packit 6639f8
	{ "config",         required_argument, NULL, 'c' },
Packit 6639f8
	{ "version",        no_argument,       NULL, 'v' },
Packit 6639f8
Packit 6639f8
	{ 0, 0, 0, 0 }
Packit 6639f8
};
Packit 6639f8
Packit 6639f8
#define DEFAULT_DIR_ROOT      "/var/lib/opae"
Packit 6639f8
#define DEFAULT_DIR_ROOT_SIZE 13
Packit 6639f8
#define DEFAULT_LOG           "fpgad.log"
Packit 6639f8
#define DEFAULT_PID           "fpgad.pid"
Packit 6639f8
#define DEFAULT_CFG           "fpgad.cfg"
Packit 6639f8
Packit 6639f8
void cmd_show_help(FILE *fptr)
Packit 6639f8
{
Packit 6639f8
	fprintf(fptr, "Usage: fpgad <options>\n");
Packit 6639f8
	fprintf(fptr, "\n");
Packit 6639f8
	fprintf(fptr, "\t-d,--daemon                 run as daemon process.\n");
Packit 6639f8
	fprintf(fptr, "\t-l,--logfile <file>         the log file for daemon mode [%s].\n", DEFAULT_LOG);
Packit 6639f8
	fprintf(fptr, "\t-p,--pidfile <file>         the pid file for daemon mode [%s].\n", DEFAULT_PID);
Packit 6639f8
	fprintf(fptr, "\t-s,--socket <sock>          the unix domain socket [/tmp/fpga_event_socket].\n");
Packit 6639f8
	fprintf(fptr, "\t-n,--null-bitstream <file>  NULL bitstream (for AP6 handling, may be\n"
Packit 6639f8
		      "\t                            given multiple times).\n");
Packit 6639f8
	fprintf(fptr, "\t-c,--config <file>          the configuration file [%s].\n", DEFAULT_CFG);
Packit 6639f8
	fprintf(fptr, "\t-v,--version                display the version and exit.\n");
Packit 6639f8
}
Packit 6639f8
Packit 6639f8
STATIC bool cmd_register_null_gbs(struct fpgad_config *c, char *null_gbs_path)
Packit 6639f8
{
Packit 6639f8
	char *canon_path = NULL;
Packit 6639f8
Packit 6639f8
	if (c->num_null_gbs < (sizeof(c->null_gbs) / sizeof(c->null_gbs[0]))) {
Packit 6639f8
		canon_path = canonicalize_file_name(null_gbs_path);
Packit 6639f8
Packit 6639f8
		if (canon_path) {
Packit 6639f8
Packit 6639f8
			memset(&c->null_gbs[c->num_null_gbs], 0,
Packit 6639f8
				 sizeof(opae_bitstream_info));
Packit 6639f8
Packit 6639f8
			if (opae_load_bitstream(canon_path,
Packit 6639f8
						&c->null_gbs[c->num_null_gbs])) {
Packit 6639f8
				LOG("failed to load NULL GBS \"%s\"\n", canon_path);
Packit 6639f8
				opae_unload_bitstream(&c->null_gbs[c->num_null_gbs]);
Packit 6639f8
				free(canon_path);
Packit 6639f8
				return false;
Packit 6639f8
			}
Packit 6639f8
Packit 6639f8
			c->num_null_gbs++;
Packit 6639f8
Packit 6639f8
			LOG("registering NULL bitstream \"%s\"\n", canon_path);
Packit 6639f8
Packit 6639f8
		} else {
Packit 6639f8
			LOG("error with NULL GBS argument: %s\n", strerror(errno));
Packit 6639f8
			return false;
Packit 6639f8
		}
Packit 6639f8
Packit 6639f8
	} else {
Packit 6639f8
		LOG("maximum number of NULL bitstreams exceeded. Ignoring -n option.\n");
Packit 6639f8
	}
Packit 6639f8
	return true;
Packit 6639f8
}
Packit 6639f8
Packit 6639f8
int cmd_parse_args(struct fpgad_config *c, int argc, char *argv[])
Packit 6639f8
{
Packit 6639f8
	int getopt_ret;
Packit 6639f8
	int option_index;
Packit 6639f8
	size_t len;
Packit 6639f8
Packit 6639f8
	while (-1 != (getopt_ret = getopt_long(argc, argv, OPT_STR, longopts, &option_index))) {
Packit 6639f8
		const char *tmp_optarg = optarg;
Packit 6639f8
Packit 6639f8
		if (optarg && ('=' == *tmp_optarg))
Packit 6639f8
			++tmp_optarg;
Packit 6639f8
Packit 6639f8
		if (!optarg && (optind < argc) &&
Packit 6639f8
			(NULL != argv[optind]) &&
Packit 6639f8
			('-' != argv[optind][0]))
Packit 6639f8
			tmp_optarg = argv[optind++];
Packit 6639f8
Packit 6639f8
		switch (getopt_ret) {
Packit 6639f8
		case 'h':
Packit 6639f8
			cmd_show_help(stdout);
Packit 6639f8
			return -2;
Packit 6639f8
			break;
Packit 6639f8
Packit 6639f8
		case 'd':
Packit 6639f8
			c->daemon = 1;
Packit 6639f8
			LOG("daemon requested\n");
Packit 6639f8
			break;
Packit 6639f8
Packit 6639f8
		case 'l':
Packit 6639f8
			if (tmp_optarg) {
Packit 6639f8
				len = strnlen(tmp_optarg, PATH_MAX - 1);
Packit 6639f8
				memcpy(c->logfile, tmp_optarg, len);
Packit 6639f8
				c->logfile[len] = '\0';
Packit 6639f8
			} else {
Packit 6639f8
				LOG("missing logfile parameter.\n");
Packit 6639f8
				return 1;
Packit 6639f8
			}
Packit 6639f8
			break;
Packit 6639f8
Packit 6639f8
		case 'p':
Packit 6639f8
			if (tmp_optarg) {
Packit 6639f8
				len = strnlen(tmp_optarg, PATH_MAX - 1);
Packit 6639f8
				memcpy(c->pidfile, tmp_optarg, len);
Packit 6639f8
				c->pidfile[len] = '\0';
Packit 6639f8
			} else {
Packit 6639f8
				LOG("missing pidfile parameter.\n");
Packit 6639f8
				return 1;
Packit 6639f8
			}
Packit 6639f8
			break;
Packit 6639f8
Packit 6639f8
		case 'n':
Packit 6639f8
			if (tmp_optarg) {
Packit 6639f8
				if (!cmd_register_null_gbs(c, (char *)tmp_optarg)) {
Packit 6639f8
					LOG("invalid null gbs path: \"%s\"\n", tmp_optarg);
Packit 6639f8
					return 1;
Packit 6639f8
				}
Packit 6639f8
			} else {
Packit 6639f8
				LOG("missing bitstream parameter.\n");
Packit 6639f8
				return 1;
Packit 6639f8
			}
Packit 6639f8
			break;
Packit 6639f8
Packit 6639f8
		case 's':
Packit 6639f8
			if (tmp_optarg) {
Packit 6639f8
				c->api_socket = tmp_optarg;
Packit 6639f8
				LOG("daemon socket is %s\n", c->api_socket);
Packit 6639f8
			} else {
Packit 6639f8
				LOG("missing socket parameter.\n");
Packit 6639f8
				return 1;
Packit 6639f8
			}
Packit 6639f8
			break;
Packit 6639f8
Packit 6639f8
		case 'c':
Packit 6639f8
			if (tmp_optarg) {
Packit 6639f8
				len = strnlen(tmp_optarg, PATH_MAX - 1);
Packit 6639f8
				memcpy(c->cfgfile, tmp_optarg, len);
Packit 6639f8
				c->cfgfile[len] = '\0';
Packit 6639f8
			} else {
Packit 6639f8
				LOG("missing cfgfile parameter.\n");
Packit 6639f8
				return 1;
Packit 6639f8
			}
Packit 6639f8
			break;
Packit 6639f8
Packit 6639f8
		case 'v':
Packit 6639f8
			fprintf(stdout, "fpgad %s %s%s\n",
Packit 6639f8
					OPAE_VERSION,
Packit 6639f8
					OPAE_GIT_COMMIT_HASH,
Packit 6639f8
					OPAE_GIT_SRC_TREE_DIRTY ? "*":"");
Packit 6639f8
			return -2;
Packit 6639f8
			break;
Packit 6639f8
Packit 6639f8
		case ':':
Packit 6639f8
			LOG("Missing option argument.\n");
Packit 6639f8
			return 1;
Packit 6639f8
Packit 6639f8
		case '?':
Packit 6639f8
			LOG("Invalid command option.\n");
Packit 6639f8
			return 1;
Packit 6639f8
Packit 6639f8
		default:
Packit 6639f8
			LOG("Invalid command option.\n");
Packit 6639f8
			return 1;
Packit 6639f8
		}
Packit 6639f8
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	return 0;
Packit 6639f8
}
Packit 6639f8
Packit 6639f8
int cmd_canonicalize_paths(struct fpgad_config *c)
Packit 6639f8
{
Packit 6639f8
	char *sub;
Packit 6639f8
	bool def;
Packit 6639f8
	mode_t mode;
Packit 6639f8
	struct stat stat_buf;
Packit 6639f8
	bool search = true;
Packit 6639f8
	char buf[PATH_MAX];
Packit 6639f8
	char *canon_path;
Packit 6639f8
	uid_t uid;
Packit 6639f8
	size_t len;
Packit 6639f8
Packit 6639f8
	uid = geteuid();
Packit 6639f8
Packit 6639f8
	if (!uid) {
Packit 6639f8
		// If we're being run as root, then use DEFAULT_DIR_ROOT
Packit 6639f8
		// as the working directory.
Packit 6639f8
		memcpy(c->directory, DEFAULT_DIR_ROOT, sizeof(DEFAULT_DIR_ROOT));
Packit 6639f8
		c->directory[sizeof(DEFAULT_DIR_ROOT)] = '\0';
Packit 6639f8
		mode = 0755;
Packit 6639f8
		c->filemode = 0026;
Packit 6639f8
	} else {
Packit 6639f8
		// We're not root. Try to use ${HOME}/.opae
Packit 6639f8
		struct passwd *passwd;
Packit 6639f8
Packit 6639f8
		passwd = getpwuid(uid);
Packit 6639f8
Packit 6639f8
		canon_path = canonicalize_file_name(passwd->pw_dir);
Packit 6639f8
Packit 6639f8
		if (canon_path) {
Packit 6639f8
			snprintf(c->directory, sizeof(c->directory),
Packit 6639f8
					"%s/.opae", canon_path);
Packit 6639f8
			free(canon_path);
Packit 6639f8
		} else {
Packit 6639f8
			// ${HOME} not found or invalid - use current dir.
Packit 6639f8
			if (getcwd(buf, sizeof(buf))) {
Packit 6639f8
				if (snprintf(c->directory, sizeof(c->directory),
Packit 6639f8
					     "%s/.opae", buf) < 0) {
Packit 6639f8
					len = strnlen("./.opae",
Packit 6639f8
						sizeof(c->directory) - 1);
Packit 6639f8
					memcpy(c->directory, "./.opae", len);
Packit 6639f8
					c->directory[len] = '\0';
Packit 6639f8
				}
Packit 6639f8
			} else {
Packit 6639f8
				// Current directory not found - use /
Packit 6639f8
				len = strnlen("/.opae", sizeof(c->directory) - 1);
Packit 6639f8
				memcpy(c->directory, "/.opae", len);
Packit 6639f8
				c->directory[len] = '\0';
Packit 6639f8
			}
Packit 6639f8
		}
Packit 6639f8
Packit 6639f8
		mode = 0775;
Packit 6639f8
		c->filemode = 0022;
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	if (cmd_path_is_symlink(c->directory)) {
Packit 6639f8
		LOG("Aborting - working directory contains a link: %s\n.",
Packit 6639f8
		    c->directory);
Packit 6639f8
		return 1;
Packit 6639f8
	}
Packit 6639f8
	LOG("daemon working directory is %s\n", c->directory);
Packit 6639f8
Packit 6639f8
	// Create the directory if it doesn't exist.
Packit 6639f8
	if (lstat(c->directory, &stat_buf) && (errno == ENOENT)) {
Packit 6639f8
		if (mkdir(c->directory, mode)) {
Packit 6639f8
			LOG("mkdir failed\n");
Packit 6639f8
			return 1;
Packit 6639f8
		}
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	// Verify logfile and pidfile do not contain ".."
Packit 6639f8
	// nor "/".
Packit 6639f8
	def = false;
Packit 6639f8
	sub = strstr(c->logfile, "..");
Packit 6639f8
	if (sub)
Packit 6639f8
		def = true;
Packit 6639f8
Packit 6639f8
	sub = strstr(c->logfile, "/");
Packit 6639f8
	if (sub)
Packit 6639f8
		def = true;
Packit 6639f8
Packit 6639f8
	if (def || (c->logfile[0] == '\0')) {
Packit 6639f8
		if (snprintf(c->logfile, sizeof(c->logfile),
Packit 6639f8
			     "%s/%s", c->directory, DEFAULT_LOG) < 0) {
Packit 6639f8
			len = strnlen("./" DEFAULT_LOG,
Packit 6639f8
					sizeof(c->logfile) - 1);
Packit 6639f8
			memcpy(c->logfile, "./" DEFAULT_LOG, len);
Packit 6639f8
			c->logfile[len] = '\0';
Packit 6639f8
		}
Packit 6639f8
	} else {
Packit 6639f8
		len = strnlen(c->logfile, sizeof(buf) - 1);
Packit 6639f8
		memcpy(buf, c->logfile, len);
Packit 6639f8
		buf[len] = '\0';
Packit 6639f8
Packit 6639f8
		if (snprintf(c->logfile, sizeof(c->logfile),
Packit 6639f8
			     "%s/%s", c->directory, buf) < 0) {
Packit 6639f8
			len = strnlen("./" DEFAULT_LOG,
Packit 6639f8
					sizeof(c->logfile) - 1);
Packit 6639f8
			memcpy(c->logfile, "./" DEFAULT_LOG, len);
Packit 6639f8
			c->logfile[len] = '\0';
Packit 6639f8
		}
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	if (cmd_path_is_symlink(c->logfile)) {
Packit 6639f8
		LOG("Aborting - log file path contains a link: %s\n.",
Packit 6639f8
		    c->logfile);
Packit 6639f8
		return 1;
Packit 6639f8
	}
Packit 6639f8
	LOG("daemon log file is %s\n", c->logfile);
Packit 6639f8
Packit 6639f8
	def = false;
Packit 6639f8
	sub = strstr(c->pidfile, "..");
Packit 6639f8
	if (sub)
Packit 6639f8
		def = true;
Packit 6639f8
Packit 6639f8
	sub = strstr(c->pidfile, "/");
Packit 6639f8
	if (sub)
Packit 6639f8
		def = true;
Packit 6639f8
Packit 6639f8
	if (def || (c->pidfile[0] == '\0')) {
Packit 6639f8
Packit 6639f8
		if (snprintf(c->pidfile, sizeof(c->pidfile),
Packit 6639f8
			     "%s/%s", c->directory, DEFAULT_PID) < 0) {
Packit 6639f8
			len = strnlen("./" DEFAULT_PID,
Packit 6639f8
					sizeof(c->pidfile) - 1);
Packit 6639f8
			memcpy(c->pidfile, "./" DEFAULT_PID, len);
Packit 6639f8
			c->pidfile[len] = '\0';
Packit 6639f8
		}
Packit 6639f8
Packit 6639f8
	} else {
Packit 6639f8
		len = strnlen(c->pidfile, sizeof(buf) - 1);
Packit 6639f8
		memcpy(buf, c->pidfile, len);
Packit 6639f8
		buf[len] = '\0';
Packit 6639f8
Packit 6639f8
		if (snprintf(c->pidfile, sizeof(c->pidfile),
Packit 6639f8
			     "%s/%s", c->directory, buf) < 0) {
Packit 6639f8
			len = strnlen("./" DEFAULT_PID,
Packit 6639f8
					sizeof(c->pidfile) - 1);
Packit 6639f8
			memcpy(c->pidfile, "./" DEFAULT_PID, len);
Packit 6639f8
			c->pidfile[len] = '\0';
Packit 6639f8
		}
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	if (cmd_path_is_symlink(c->pidfile)) {
Packit 6639f8
		LOG("Aborting - pid file path contains a link: %s\n.",
Packit 6639f8
		    c->pidfile);
Packit 6639f8
		return 1;
Packit 6639f8
	}
Packit 6639f8
	LOG("daemon pid file is %s\n", c->pidfile);
Packit 6639f8
Packit 6639f8
	// Verify cfgfile doesn't contain ".."
Packit 6639f8
	def = false;
Packit 6639f8
	sub = strstr(c->cfgfile, "..");
Packit 6639f8
	if (sub)
Packit 6639f8
		def = true;
Packit 6639f8
Packit 6639f8
	if (def || (c->cfgfile[0] == '\0')) {
Packit 6639f8
		search = true;
Packit 6639f8
	} else {
Packit 6639f8
		canon_path = canonicalize_file_name(c->cfgfile);
Packit 6639f8
		if (canon_path) {
Packit 6639f8
Packit 6639f8
			if (!cmd_path_is_symlink(c->cfgfile)) {
Packit 6639f8
Packit 6639f8
				len = strnlen(canon_path,
Packit 6639f8
					      sizeof(c->cfgfile) - 1);
Packit 6639f8
				memcpy(c->cfgfile,
Packit 6639f8
					  canon_path,
Packit 6639f8
					  len);
Packit 6639f8
				c->cfgfile[len] = '\0';
Packit 6639f8
Packit 6639f8
				if (!cfg_load_config(c)) {
Packit 6639f8
					LOG("daemon cfg file is %s\n",
Packit 6639f8
					    c->cfgfile);
Packit 6639f8
					search = false; // found and loaded it
Packit 6639f8
				}
Packit 6639f8
Packit 6639f8
			}
Packit 6639f8
Packit 6639f8
			free(canon_path);
Packit 6639f8
		}
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	if (search) {
Packit 6639f8
		c->cfgfile[0] = '\0';
Packit 6639f8
		if (cfg_find_config_file(c))
Packit 6639f8
			LOG("failed to find config file.\n");
Packit 6639f8
		else {
Packit 6639f8
			if (cfg_load_config(c))
Packit 6639f8
				LOG("failed to load config file %s\n",
Packit 6639f8
				    c->cfgfile);
Packit 6639f8
			else
Packit 6639f8
				LOG("daemon cfg file is %s\n", c->cfgfile);
Packit 6639f8
		}
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	if (!c->supported_devices) {
Packit 6639f8
		LOG("using default configuration.\n");
Packit 6639f8
		c->cfgfile[0] = '\0';
Packit 6639f8
		c->supported_devices = default_supported_devices_table;
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	return 0;
Packit 6639f8
}
Packit 6639f8
Packit 6639f8
void cmd_destroy(struct fpgad_config *c)
Packit 6639f8
{
Packit 6639f8
	unsigned i;
Packit 6639f8
Packit 6639f8
	if (c->daemon)
Packit 6639f8
		unlink(c->pidfile);
Packit 6639f8
Packit 6639f8
	for (i = 0 ; i < c->num_null_gbs ; ++i) {
Packit 6639f8
		if (c->null_gbs[i].filename)
Packit 6639f8
			free((char *)c->null_gbs[i].filename);
Packit 6639f8
		opae_unload_bitstream(&c->null_gbs[i]);
Packit 6639f8
	}
Packit 6639f8
	c->num_null_gbs = 0;
Packit 6639f8
Packit 6639f8
	if (c->supported_devices &&
Packit 6639f8
	    (c->supported_devices != default_supported_devices_table)) {
Packit 6639f8
Packit 6639f8
		for (i = 0 ; c->supported_devices[i].library_path ; ++i) {
Packit 6639f8
			fpgad_supported_device *d = &c->supported_devices[i];
Packit 6639f8
			if (d->library_path)
Packit 6639f8
				free((void *)d->library_path);
Packit 6639f8
			if (d->config)
Packit 6639f8
				free((void *)d->config);
Packit 6639f8
		}
Packit 6639f8
Packit 6639f8
		free(c->supported_devices);
Packit 6639f8
	}
Packit 6639f8
	c->supported_devices = NULL;
Packit 6639f8
}
Packit 6639f8
Packit 6639f8
bool cmd_path_is_symlink(const char *path)
Packit 6639f8
{
Packit 6639f8
	char component[PATH_MAX];
Packit 6639f8
	struct stat stat_buf;
Packit 6639f8
	size_t len;
Packit 6639f8
	char *pslash;
Packit 6639f8
Packit 6639f8
	len = strnlen(path, PATH_MAX - 1);
Packit 6639f8
	if (!len) // empty path
Packit 6639f8
		return false;
Packit 6639f8
Packit 6639f8
	memcpy(component, path, len);
Packit 6639f8
	component[len] = '\0';
Packit 6639f8
Packit 6639f8
	if (component[0] == '/') {
Packit 6639f8
		// absolute path
Packit 6639f8
Packit 6639f8
		pslash = realpath(path, component);
Packit 6639f8
Packit 6639f8
		if (strcmp(component, path))
Packit 6639f8
			return true;
Packit 6639f8
Packit 6639f8
Packit 6639f8
	} else {
Packit 6639f8
		// relative path
Packit 6639f8
Packit 6639f8
		pslash = strrchr(component, '/');
Packit 6639f8
Packit 6639f8
		while (pslash) {
Packit 6639f8
Packit 6639f8
			if (fstatat(AT_FDCWD, component,
Packit 6639f8
				    &stat_buf, AT_SYMLINK_NOFOLLOW)) {
Packit 6639f8
				return false;
Packit 6639f8
			}
Packit 6639f8
Packit 6639f8
			if (S_ISLNK(stat_buf.st_mode))
Packit 6639f8
				return true;
Packit 6639f8
Packit 6639f8
			*pslash = '\0';
Packit 6639f8
			pslash = strrchr(component, '/');
Packit 6639f8
		}
Packit 6639f8
Packit 6639f8
		if (fstatat(AT_FDCWD, component,
Packit 6639f8
			    &stat_buf, AT_SYMLINK_NOFOLLOW)) {
Packit 6639f8
			return false;
Packit 6639f8
		}
Packit 6639f8
Packit 6639f8
		if (S_ISLNK(stat_buf.st_mode))
Packit 6639f8
			return true;
Packit 6639f8
Packit 6639f8
	}
Packit 6639f8
Packit 6639f8
	return false;
Packit 6639f8
}