Commit message: CVE 2018-6556: verify netns fd in lxc-user-nic

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
CVE: CVE-2018-6556
Vulnerability description: lxc-user-nic when asked to delete a network interface will unconditionally open a user provided path. This code path may be used by an unprivileged user to check for the existence of a path which they wouldn't otherwise be able to reach. It may also be used to trigger side effects by causing a (read-only) open of special kernel files (ptmx, proc, sys). Affected releases are LXC: 2.0 versions above and including 2.0.9; 3.0 versions above and including 3.0.0, prior to 3.0.2.

Project: lxc
File: utils.c

int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags)
{
	int ret;
	struct loop_info64 lo64;
	int fd_img = -1, fret = -1, fd_loop = -1;

	fd_loop = lxc_get_unused_loop_dev(loop_dev);
	if (fd_loop < 0) {
		if (fd_loop == -ENODEV)
			fd_loop = lxc_get_unused_loop_dev_legacy(loop_dev);
		else
			goto on_error;
	}

	fd_img = open(source, O_RDWR | O_CLOEXEC);
	if (fd_img < 0)
		goto on_error;

	ret = ioctl(fd_loop, LOOP_SET_FD, fd_img);
	if (ret < 0)
		goto on_error;

	memset(&lo64, 0, sizeof(lo64));
	lo64.lo_flags = flags;

	ret = ioctl(fd_loop, LOOP_SET_STATUS64, &lo64);
	if (ret < 0)
		goto on_error;

	fret = 0;

on_error:
	if (fd_img >= 0)
		close(fd_img);

	if (fret < 0 && fd_loop >= 0) {
		close(fd_loop);
		fd_loop = -1;
	}

	return fd_loop;
}

Project: lxc
File: utils.c

int is_dir(const char *path)
{
	struct stat statbuf;
	int ret;

	ret = stat(path, &statbuf);
	if (ret == 0 && S_ISDIR(statbuf.st_mode))
		return 1;

	return 0;
}

Project: lxc
File: utils.c

static int lxc_get_unused_loop_dev(char *name_loop)
{
	int loop_nr, ret;
	int fd_ctl = -1, fd_tmp = -1;

	fd_ctl = open("/dev/loop-control", O_RDWR | O_CLOEXEC);
	if (fd_ctl < 0)
		return -ENODEV;

	loop_nr = ioctl(fd_ctl, LOOP_CTL_GET_FREE);
	if (loop_nr < 0)
		goto on_error;

	ret = snprintf(name_loop, LO_NAME_SIZE, "/dev/loop%d", loop_nr);
	if (ret < 0 || ret >= LO_NAME_SIZE)
		goto on_error;

	fd_tmp = open(name_loop, O_RDWR | O_CLOEXEC);
	if (fd_tmp < 0)
		goto on_error;

on_error:
	close(fd_ctl);
	return fd_tmp;
}

Project: lxc
File: utils.c

int lxc_count_file_lines(const char *fn)
{
	FILE *f;
	char *line = NULL;
	size_t sz = 0;
	int n = 0;

	f = fopen_cloexec(fn, "r");
	if (!f)
		return -1;

	while (getline(&line, &sz, f) != -1) {
		n++;
	}

	free(line);
	fclose(f);
	return n;
}

Project: lxc
File: utils.c

bool is_shared_mountpoint(const char *path)
{
	char buf[LXC_LINELEN];
	FILE *f;
	int i;
	char *p, *p2;

	f = fopen("/proc/self/mountinfo", "r");
	if (!f)
		return 0;

	while (fgets(buf, LXC_LINELEN, f)) {
		for (p = buf, i = 0; p && i < 4; i++)
			p = strchr(p + 1, ' ');
		if (!p)
			continue;

		p2 = strchr(p + 1, ' ');
		if (!p2)
			continue;

		*p2 = '\0';
		if (strcmp(p + 1, path) == 0) {
			/* This is the path. Is it shared? */
			p = strchr(p2 + 1, ' ');
			if (p && strstr(p, "shared:")) {
				fclose(f);
				return true;
			}
		}
	}

	fclose(f);
	return false;
}

Project: lxc
File: lxc_user_nic.c

static bool may_access_netns(int pid)
{
	int ret;
	char s[200];
	uid_t ruid, suid, euid;
	bool may_access = false;

	ret = getresuid(&ruid, &euid, &suid);
	if (ret < 0) {
		usernic_error("Failed to retrieve real, effective, and saved "
			      "user IDs: %s\n",
			      strerror(errno));
		return false;
	}

	ret = setresuid(ruid, ruid, euid);
	if (ret < 0) {
		usernic_error("Failed to drop privilege by setting effective "
			      "user id and real user id to %d, and saved user "
			      "ID to %d: %s\n",
			      ruid, euid, strerror(errno));
		return false;
	}

	ret = snprintf(s, 200, "/proc/%d/ns/net", pid);
	if (ret < 0 || ret >= 200)
		return false;

	ret = access(s, R_OK);
	may_access = true;
	if (ret < 0)  {
		may_access = false;
		usernic_error("Uid %d may not access %s: %s\n", (int)ruid, s, strerror(errno));
	}

	ret = setresuid(ruid, euid, suid);
	if (ret < 0) {
		usernic_error("Failed to restore user id to %d, real user id "
			      "to %d, and saved user ID to %d: %s\n",
			      ruid, euid, suid, strerror(errno));
		may_access = false;
	}

	return may_access;
}

Project: lxc
File: utils.c

int lxc_set_death_signal(int signal)
{
	int ret;
	pid_t ppid;

	ret = prctl(PR_SET_PDEATHSIG, prctl_arg(signal), prctl_arg(0),
		    prctl_arg(0), prctl_arg(0));

	/* Check whether we have been orphaned. */
	ppid = (pid_t)syscall(SYS_getppid);
	if (ppid == 1) {
		pid_t self;

		self = lxc_raw_getpid();
		ret = kill(self, SIGKILL);
		if (ret < 0)
			return -1;
	}

	if (ret < 0) {
		SYSERROR("Failed to set PR_SET_PDEATHSIG to %d", signal);
		return -1;
	}

	return 0;
}

Project: lxc
File: utils.c

int lxc_safe_long(const char *numstr, long int *converted)
{
	char *err = NULL;
	signed long int sli;

	errno = 0;
	sli = strtol(numstr, &err, 0);
	if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
		return -ERANGE;

	if (errno != 0 && sli == 0)
		return -EINVAL;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	*converted = sli;
	return 0;
}

Project: lxc
File: lxc_user_nic.c

static int count_entries(char *buf, off_t len, char *name, char *net_type, char *net_link)
{
	int count = 0;
	bool owner = false;;
	char *buf_end;

	buf_end = &buf[len];
	while ((buf = find_line(buf, buf_end, name, net_type, net_link, NULL,
				&owner, &(bool){true}, &(bool){true}))) {
		if (owner)
			count++;
		buf = get_eol(buf, buf_end) + 1;
		if (buf >= buf_end)
			break;
	}

	return count;
}

Project: lxc
File: utils.c

void **lxc_append_null_to_array(void **array, size_t count)
{
	void **temp;

	/* Append NULL to the array */
	if (count) {
		temp = realloc(array, (count + 1) * sizeof(*array));
		if (!temp) {
			size_t i;
			for (i = 0; i < count; i++)
				free(array[i]);
			free(array);
			return NULL;
		}

		array = temp;
		array[count] = NULL;
	}

	return array;
}

Project: lxc
File: utils.c

int lxc_switch_uid_gid(uid_t uid, gid_t gid)
{
	if (setgid(gid) < 0) {
		SYSERROR("Failed to switch to gid %d.", gid);
		return -errno;
	}
	NOTICE("Switched to gid %d.", gid);

	if (setuid(uid) < 0) {
		SYSERROR("Failed to switch to uid %d.", uid);
		return -errno;
	}
	NOTICE("Switched to uid %d.", uid);

	return 0;
}

Project: lxc
File: utils.c

int open_devnull(void)
{
	int fd = open("/dev/null", O_RDWR);

	if (fd < 0)
		SYSERROR("Can't open /dev/null");

	return fd;
}

Project: lxc
File: lxc_user_nic.c

static bool create_db_dir(char *fnam)
{
	int ret;
	char *p;
	size_t len;

	len = strlen(fnam);
	p = alloca(len + 1);
	(void)strlcpy(p, fnam, len + 1);
	fnam = p;
	p = p + 1;

again:
	while (*p && *p != '/')
		p++;
	if (!*p)
		return true;

	*p = '\0';

	ret = mkdir(fnam, 0755);
	if (ret < 0 && errno != EEXIST) {
		usernic_error("Failed to create %s: %s\n", fnam,
			      strerror(errno));
		*p = '/';
		return false;
	}
	*(p++) = '/';

	goto again;
}

Project: lxc
File: utils.c

int lxc_safe_ulong(const char *numstr, unsigned long *converted)
{
	char *err = NULL;
	unsigned long int uli;

	while (isspace(*numstr))
		numstr++;

	if (*numstr == '-')
		return -EINVAL;

	errno = 0;
	uli = strtoul(numstr, &err, 0);
	if (errno == ERANGE && uli == ULONG_MAX)
		return -ERANGE;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	*converted = uli;
	return 0;
}

Project: lxc
File: utils.c

char *choose_init(const char *rootfs)
{
	char *retv = NULL;
	const char *empty = "",
		   *tmp;
	int ret, env_set = 0;

	if (!getenv("PATH")) {
		if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 0))
			SYSERROR("Failed to setenv");

		env_set = 1;
	}

	retv = on_path("init.lxc", rootfs);

	if (env_set) {
		if (unsetenv("PATH"))
			SYSERROR("Failed to unsetenv");
	}

	if (retv)
		return retv;

	retv = malloc(PATH_MAX);
	if (!retv)
		return NULL;

	if (rootfs)
		tmp = rootfs;
	else
		tmp = empty;

	ret = snprintf(retv, PATH_MAX, "%s/%s/%s", tmp, SBINDIR, "/init.lxc");
	if (ret < 0 || ret >= PATH_MAX) {
		ERROR("pathname too long");
		goto out1;
	}

	if (access(retv, X_OK) == 0)
		return retv;

	ret = snprintf(retv, PATH_MAX, "%s/%s/%s", tmp, LXCINITDIR, "/lxc/lxc-init");
	if (ret < 0 || ret >= PATH_MAX) {
		ERROR("pathname too long");
		goto out1;
	}

	if (access(retv, X_OK) == 0)
		return retv;

	ret = snprintf(retv, PATH_MAX, "%s/usr/lib/lxc/lxc-init", tmp);
	if (ret < 0 || ret >= PATH_MAX) {
		ERROR("pathname too long");
		goto out1;
	}

	if (access(retv, X_OK) == 0)
		return retv;

	ret = snprintf(retv, PATH_MAX, "%s/sbin/lxc-init", tmp);
	if (ret < 0 || ret >= PATH_MAX) {
		ERROR("pathname too long");
		goto out1;
	}

	if (access(retv, X_OK) == 0)
		return retv;

	/*
	 * Last resort, look for the statically compiled init.lxc which we
	 * hopefully bind-mounted in.
	 * If we are called during container setup, and we get to this point,
	 * then the init.lxc.static from the host will need to be bind-mounted
	 * in.  So we return NULL here to indicate that.
	 */
	if (rootfs)
		goto out1;

	ret = snprintf(retv, PATH_MAX, "/init.lxc.static");
	if (ret < 0 || ret >= PATH_MAX) {
		WARN("Nonsense - name /lxc.init.static too long");
		goto out1;
	}

	if (access(retv, X_OK) == 0)
		return retv;

out1:
	free(retv);
	return NULL;
}

Project: lxc
File: utils.c

int fd_cloexec(int fd, bool cloexec)
{
	int oflags, nflags;

	oflags = fcntl(fd, F_GETFD, 0);
	if (oflags < 0)
		return -errno;

	if (cloexec)
		nflags = oflags | FD_CLOEXEC;
	else
		nflags = oflags & ~FD_CLOEXEC;

	if (nflags == oflags)
		return 0;

	if (fcntl(fd, F_SETFD, nflags) < 0)
		return -errno;

	return 0;
}

Project: lxc
File: utils.c

bool has_fs_type(const char *path, fs_type_magic magic_val)
{
	bool has_type;
	int ret;
	struct statfs sb;

	ret = statfs(path, &sb);
	if (ret < 0)
		return false;

	has_type = is_fs_type(&sb, magic_val);
	if (!has_type && magic_val == RAMFS_MAGIC)
		WARN("When the ramfs it a tmpfs statfs() might report tmpfs");

	return has_type;
}

Project: lxc
File: utils.c

static int open_if_safe(int dirfd, const char *nextpath)
{
	int newfd = openat(dirfd, nextpath, O_RDONLY | O_NOFOLLOW);
	if (newfd >= 0) /* Was not a symlink, all good. */
		return newfd;

	if (errno == ELOOP)
		return newfd;

	if (errno == EPERM || errno == EACCES) {
		/* We're not root (cause we got EPERM) so try opening with
		 * O_PATH.
		 */
		newfd = openat(dirfd, nextpath, O_PATH | O_NOFOLLOW);
		if (newfd >= 0) {
			/* O_PATH will return an fd for symlinks. We know
			 * nextpath wasn't a symlink at last openat, so if fd is
			 * now a link, then something * fishy is going on.
			 */
			int ret = check_symlink(newfd);
			if (ret < 0) {
				close(newfd);
				newfd = ret;
			}
		}
	}

	return newfd;
}

Project: lxc
File: utils.c

int set_stdfds(int fd)
{
	int ret;

	if (fd < 0)
		return -1;

	ret = dup2(fd, STDIN_FILENO);
	if (ret < 0)
		return -1;

	ret = dup2(fd, STDOUT_FILENO);
	if (ret < 0)
		return -1;

	ret = dup2(fd, STDERR_FILENO);
	if (ret < 0)
		return -1;

	return 0;
}

Project: lxc
File: utils.c

int recursive_destroy(char *dirname)
{
	int ret;
	struct dirent *direntp;
	DIR *dir;
	int r = 0;

	dir = opendir(dirname);
	if (!dir)
		return -1;

	while ((direntp = readdir(dir))) {
		char *pathname;
		struct stat mystat;

		if (!strcmp(direntp->d_name, ".") ||
		    !strcmp(direntp->d_name, ".."))
			continue;

		pathname = must_make_path(dirname, direntp->d_name, NULL);

		ret = lstat(pathname, &mystat);
		if (ret < 0) {
			if (!r)
				WARN("Failed to stat \"%s\"", pathname);

			r = -1;
			goto next;
		}

		if (!S_ISDIR(mystat.st_mode))
			goto next;

		ret = recursive_destroy(pathname);
		if (ret < 0)
			r = -1;

	next:
		free(pathname);
	}

	ret = rmdir(dirname);
	if (ret < 0) {
		if (!r)
			SYSWARN("Failed to delete \"%s\"", dirname);

		r = -1;
	}

	ret = closedir(dir);
	if (ret < 0) {
		if (!r)
			SYSWARN("Failed to delete \"%s\"", dirname);

		r = -1;
	}

	return r;
}

Project: lxc
File: utils.c

uid_t get_ns_uid(uid_t orig)
{
	char *line = NULL;
	size_t sz = 0;
	uid_t nsid, hostid, range;
	FILE *f = fopen("/proc/self/uid_map", "r");
	if (!f)
		return 0;

	while (getline(&line, &sz, f) != -1) {
		if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3)
			continue;

		if (hostid <= orig && hostid + range > orig) {
			nsid += orig - hostid;
			goto found;
		}
	}

	nsid = 0;

found:
	fclose(f);
	free(line);
	return nsid;
}

Project: lxc
File: utils.c

static bool complete_word(char ***result, char *start, char *end, size_t *cap, size_t *cnt)
{
	int r;

	r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16);
	if (r < 0)
		return false;

	(*result)[*cnt] = strndup(start, end - start);
	if (!(*result)[*cnt])
		return false;

	(*cnt)++;

	return true;
}

Project: lxc
File: utils.c

static int lxc_append_null_to_list(void ***list)
{
	int newentry = 0;
	void **tmp;

	if (*list)
		for (; (*list)[newentry]; newentry++) {
			;
		}

	tmp = realloc(*list, (newentry + 2) * sizeof(void **));
	if (!tmp)
		return -1;

	*list = tmp;
	(*list)[newentry + 1] = NULL;

	return newentry;
}

Project: lxc
File: utils.c

const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip)
{
	return (const char**)lxc_va_arg_list_to_argv(ap, skip, 0);
}

Project: lxc
File: utils.c

int null_stdfds(void)
{
	int ret = -1;
	int fd;

	fd = open_devnull();
	if (fd >= 0) {
		ret = set_stdfds(fd);
		close(fd);
	}

	return ret;
}

Project: lxc
File: utils.c

int lxc_safe_uint(const char *numstr, unsigned int *converted)
{
	char *err = NULL;
	unsigned long int uli;

	while (isspace(*numstr))
		numstr++;

	if (*numstr == '-')
		return -EINVAL;

	errno = 0;
	uli = strtoul(numstr, &err, 0);
	if (errno == ERANGE && uli == ULONG_MAX)
		return -ERANGE;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	if (uli > UINT_MAX)
		return -ERANGE;

	*converted = (unsigned int)uli;
	return 0;
}

Project: lxc
File: utils.c

bool task_blocks_signal(pid_t pid, int signal)
{
	int ret;
	char status[__PROC_STATUS_LEN];
	FILE *f;
	uint64_t sigblk = 0, one = 1;
	size_t n = 0;
	bool bret = false;
	char *line = NULL;

	ret = snprintf(status, __PROC_STATUS_LEN, "/proc/%d/status", pid);
	if (ret < 0 || ret >= __PROC_STATUS_LEN)
		return bret;

	f = fopen(status, "r");
	if (!f)
		return bret;

	while (getline(&line, &n, f) != -1) {
		char *numstr;

		if (strncmp(line, "SigBlk:", 7))
			continue;

		numstr = lxc_trim_whitespace_in_place(line + 7);
		ret = lxc_safe_uint64(numstr, &sigblk, 16);
		if (ret < 0)
			goto out;

		break;
	}

	if (sigblk & (one << (signal - 1)))
		bret = true;

out:
	free(line);
	fclose(f);
	return bret;
}

Project: lxc
File: lxc_user_nic.c

static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid,
			      char *intype, char *br, int allowed, char **cnic)
{
	int ret;
	size_t slen;
	char *newline, *owner;
	char nicname[IFNAMSIZ];
	struct stat sb;
	struct alloted_s *n;
	int count = 0;
	char *buf = NULL;

	for (n = names; n != NULL; n = n->next)
		cull_entries(fd, n->name, intype, br, NULL, NULL);

	if (allowed == 0)
		return NULL;

	owner = names->name;

	ret = fstat(fd, &sb);
	if (ret < 0) {
		usernic_error("Failed to fstat: %s\n", strerror(errno));
		return NULL;
	}

	if (sb.st_size > 0) {
		buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE,
				  MAP_SHARED, fd, 0);
		if (buf == MAP_FAILED) {
			usernic_error("Failed to establish shared memory "
				      "mapping: %s\n", strerror(errno));
			return NULL;
		}

		owner = NULL;
		for (n = names; n != NULL; n = n->next) {
			count = count_entries(buf, sb.st_size, n->name, intype, br);
			if (count >= n->allowed)
				continue;

			owner = n->name;
			break;
		}

		lxc_strmunmap(buf, sb.st_size);
	}

	if (owner == NULL)
		return NULL;

	ret = snprintf(nicname, sizeof(nicname), "vethXXXXXX");
	if (ret < 0 || (size_t)ret >= sizeof(nicname))
		return NULL;

	if (!lxc_mkifname(nicname))
		return NULL;

	ret = create_nic(nicname, br, pid, cnic);
	if (ret < 0) {
		usernic_error("%s", "Failed to create new nic\n");
		return NULL;
	}

	/* strlen(owner)
	 * +
	 * " "
	 * +
	 * strlen(intype)
	 * +
	 * " "
	 * +
	 * strlen(br)
	 * +
	 * " "
	 * +
	 * strlen(nicname)
	 * +
	 * \n
	 * +
	 * \0
	 */
	slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(nicname) + 4;
	newline = malloc(slen + 1);
	if (!newline) {
		free(newline);
		usernic_error("Failed allocate memory: %s\n", strerror(errno));
		return NULL;
	}

	ret = snprintf(newline, slen + 1, "%s %s %s %s\n", owner, intype, br, nicname);
	if (ret < 0 || (size_t)ret >= (slen + 1)) {
		if (lxc_netdev_delete_by_name(nicname) != 0)
			usernic_error("Error unlinking %s\n", nicname);
		free(newline);
		return NULL;
	}

	/* Note that the file needs to be truncated to the size **without** the
	 * \0 byte! Files are not \0-terminated!
	 */
	ret = ftruncate(fd, sb.st_size + slen);
	if (ret < 0)
		usernic_error("Failed to truncate file: %s\n", strerror(errno));

	buf = lxc_strmmap(NULL, sb.st_size + slen, PROT_READ | PROT_WRITE,
			  MAP_SHARED, fd, 0);
	if (buf == MAP_FAILED) {
		usernic_error("Failed to establish shared memory mapping: %s\n",
			      strerror(errno));
		if (lxc_netdev_delete_by_name(nicname) != 0)
			usernic_error("Error unlinking %s\n", nicname);
		free(newline);
		return NULL;
	}

	/* Note that the memory needs to be moved in the buffer **without** the
	 * \0 byte! Files are not \0-terminated!
	 */
	memmove(buf + sb.st_size, newline, slen);
	free(newline);
	lxc_strmunmap(buf, sb.st_size + slen);

	return strdup(nicname);
}

Project: lxc
File: lxc_user_nic.c

static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
				     int *container_veth_ifidx)
{
	int ret;
	pid_t pid_self;
	uid_t ruid, suid, euid;
	char ifname[IFNAMSIZ];
	char *string_ret = NULL, *name = NULL;
	int fd = -1, ifindex = -1, ofd = -1;

	pid_self = lxc_raw_getpid();
	ofd = lxc_preserve_ns(pid_self, "net");
	if (ofd < 0) {
		usernic_error("Failed opening network namespace path for %d", pid_self);
		return NULL;
	}

	fd = lxc_preserve_ns(pid, "net");
	if (fd < 0) {
		usernic_error("Failed opening network namespace path for %d", pid);
		goto do_partial_cleanup;
	}

	ret = getresuid(&ruid, &euid, &suid);
	if (ret < 0) {
		usernic_error("Failed to retrieve real, effective, and saved "
			      "user IDs: %s\n",
			      strerror(errno));
		goto do_partial_cleanup;
	}

	ret = setns(fd, CLONE_NEWNET);
	close(fd);
	fd = -1;
	if (ret < 0) {
		usernic_error("Failed to setns() to the network namespace of "
			      "the container with PID %d: %s\n",
			      pid, strerror(errno));
		goto do_partial_cleanup;
	}

	ret = setresuid(ruid, ruid, 0);
	if (ret < 0) {
		usernic_error("Failed to drop privilege by setting effective "
			      "user id and real user id to %d, and saved user "
			      "ID to 0: %s\n",
			      ruid, strerror(errno));
		/* It's ok to jump to do_full_cleanup here since setresuid()
		 * will succeed when trying to set real, effective, and saved to
		 * values they currently have.
		 */
		goto do_full_cleanup;
	}

	/* Check if old interface exists. */
	ifindex = if_nametoindex(oldname);
	if (!ifindex) {
		usernic_error("Failed to get netdev index: %s\n", strerror(errno));
		goto do_full_cleanup;
	}

	/* When the IFLA_IFNAME attribute is passed something like "<prefix>%d"
	 * netlink will replace the format specifier with an appropriate index.
	 * So we pass "eth%d".
	 */
	if (newname)
		name = newname;
	else
		name = "eth%d";

	ret = lxc_netdev_rename_by_name(oldname, name);
	name = NULL;
	if (ret < 0) {
		usernic_error("Error %d renaming netdev %s to %s in container\n",
			      ret, oldname, newname ? newname : "eth%d");
		goto do_full_cleanup;
	}

	/* Retrieve new name for interface. */
	if (!if_indextoname(ifindex, ifname)) {
		usernic_error("Failed to get new netdev name: %s\n", strerror(errno));
		goto do_full_cleanup;
	}

	/* Allocation failure for strdup() is checked below. */
	name = strdup(ifname);
	string_ret = name;
	*container_veth_ifidx = ifindex;

do_full_cleanup:
	ret = setresuid(ruid, euid, suid);
	if (ret < 0) {
		usernic_error("Failed to restore privilege by setting "
			      "effective user id to %d, real user id to %d, "
			      "and saved user ID to %d: %s\n", ruid, euid, suid,
			      strerror(errno));

		string_ret = NULL;
	}

	ret = setns(ofd, CLONE_NEWNET);
	if (ret < 0) {
		usernic_error("Failed to setns() to original network namespace "
			      "of PID %d: %s\n", ofd, strerror(errno));

		string_ret = NULL;
	}

do_partial_cleanup:
	if (fd >= 0)
		close(fd);

	if (!string_ret && name)
		free(name);

	close(ofd);

	return string_ret;
}

Project: lxc
File: utils.c

static char *get_nextpath(char *path, int *offsetp, int fulllen)
{
	int offset = *offsetp;

	if (offset >= fulllen)
		return NULL;

	while (path[offset] != '\0' && offset < fulllen)
		offset++;

	while (path[offset] == '\0' && offset < fulllen)
		offset++;

	*offsetp = offset;
	return (offset < fulllen) ? &path[offset] : NULL;
}

Project: lxc
File: utils.c

static bool is_subdir(const char *subdir, const char *dir, size_t len)
{
	size_t subdirlen = strlen(subdir);

	if (subdirlen < len)
		return false;

	if (strncmp(subdir, dir, len) != 0)
		return false;

	if (dir[len-1] == '/')
		return true;

	if (subdir[len] == '/' || subdirlen == len)
		return true;

	return false;
}

Project: lxc
File: utils.c

char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack)
{
	ssize_t len = -1, saved_len = -1;
	char *result = NULL;
	size_t replacement_len = strlen(replacement);
	size_t needle_len = strlen(needle);

	/* should be executed exactly twice */
	while (len == -1 || result == NULL) {
		char *p;
		char *last_p;
		ssize_t part_len;

		if (len != -1) {
			result = calloc(1, len + 1);
			if (!result)
				return NULL;

			saved_len = len;
		}

		len = 0;

		for (last_p = (char *)haystack, p = strstr(last_p, needle); p; last_p = p, p = strstr(last_p, needle)) {
			part_len = (ssize_t)(p - last_p);
			if (result && part_len > 0)
				memcpy(&result[len], last_p, part_len);

			len += part_len;

			if (result && replacement_len > 0)
				memcpy(&result[len], replacement, replacement_len);

			len += replacement_len;
			p += needle_len;
		}

		part_len = strlen(last_p);
		if (result && part_len > 0)
			memcpy(&result[len], last_p, part_len);

		len += part_len;
	}

	/* make sure we did the same thing twice,
	 * once for calculating length, the other
	 * time for copying data */
	if (saved_len != len) {
		free(result);
		return NULL;
	}

	/* make sure we didn't overwrite any buffer,
	 * due to calloc the string should be 0-terminated */
	if (result[len] != '\0') {
		free(result);
		return NULL;
	}

	return result;
}

Project: lxc
File: lxc_user_nic.c

static int get_alloted(char *me, char *intype, char *link,
		       struct alloted_s **alloted)
{
	int n, ret;
	char name[100], type[100], br[100];
	char **groups;
	FILE *fin;

	int count = 0;
	size_t len = 0;
	char *line = NULL;

	fin = fopen(LXC_USERNIC_CONF, "r");
	if (!fin) {
		usernic_error("Failed to open \"%s\": %s\n", LXC_USERNIC_CONF,
			      strerror(errno));
		return -1;
	}

	groups = get_groupnames();
	while ((getline(&line, &len, fin)) != -1) {
		ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name,
			     type, br, &n);
		if (ret != 4)
			continue;

		if (strlen(name) == 0)
			continue;

		if (strcmp(name, me)) {
			if (name[0] != '@')
				continue;

			if (!name_is_in_groupnames(name + 1, groups))
				continue;
		}

		if (strcmp(type, intype))
			continue;

		if (strcmp(link, br))
			continue;

		/* Found the user or group with the appropriate settings,
		 * therefore finish the search. What to do if there are more
		 * than one applicable lines? not specified in the docs. Since
		 * getline is implemented with realloc, we don't need to free
		 * line until exiting func.
		 *
		 * If append_alloted returns NULL, e.g. due to a malloc error,
		 * we set count to 0 and break the loop, allowing cleanup and
		 * then exiting from main().
		 */
		if (!append_alloted(alloted, name, n)) {
			count = 0;
			break;
		}
		count += n;
	}

	free_groupnames(groups);
	fclose(fin);
	free(line);

	/* Now return the total number of nics that this user can create. */
	return count;
}

Project: lxc
File: utils.c

bool lxc_nic_exists(char *nic)
{
#define __LXC_SYS_CLASS_NET_LEN 15 + IFNAMSIZ + 1
	char path[__LXC_SYS_CLASS_NET_LEN];
	int ret;
	struct stat sb;

	if (!strcmp(nic, "none"))
		return true;

	ret = snprintf(path, __LXC_SYS_CLASS_NET_LEN, "/sys/class/net/%s", nic);
	if (ret < 0 || (size_t)ret >= __LXC_SYS_CLASS_NET_LEN)
		return false;

	ret = stat(path, &sb);
	if (ret < 0)
		return false;

	return true;
}

Project: lxc
File: utils.c

char *must_concat(const char *first, ...)
{
	va_list args;
	char *cur, *dest;
	size_t cur_len, it_len;

	dest = must_copy_string(first);
	cur_len = it_len = strlen(first);

	va_start(args, first);
	while ((cur = va_arg(args, char *)) != NULL) {
		it_len = strlen(cur);

		dest = must_realloc(dest, cur_len + it_len + 1);

		(void)memcpy(dest + cur_len, cur, it_len);
		cur_len += it_len;
	}
	va_end(args);

	dest[cur_len] = 0;
	return dest;
}

Project: lxc
File: utils.c

extern int lxc_rmdir_onedev(const char *path, const char *exclude)
{
	struct stat mystat;
	bool onedev = true;

	if (is_native_overlayfs(path))
		onedev = false;

	if (lstat(path, &mystat) < 0) {
		if (errno == ENOENT)
			return 0;

		ERROR("Failed to stat %s", path);
		return -1;
	}

	return _recursive_rmdir(path, mystat.st_dev, exclude, 0, onedev);
}

Project: lxc
File: utils.c

int lxc_pclose(struct lxc_popen_FILE *fp)
{
	pid_t wait_pid;
	int wstatus = 0;

	if (!fp)
		return -1;

	do {
		wait_pid = waitpid(fp->child_pid, &wstatus, 0);
	} while (wait_pid < 0 && errno == EINTR);

	fclose(fp->f);
	free(fp);

	if (wait_pid < 0)
		return -1;

	return wstatus;
}

Project: lxc
File: utils.c

bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val)
{
	return (fs->f_type == (fs_type_magic)magic_val);
}

Project: lxc
File: utils.c

int lxc_unstack_mountpoint(const char *path, bool lazy)
{
	int ret;
	int umounts = 0;

pop_stack:
	ret = umount2(path, lazy ? MNT_DETACH : 0);
	if (ret < 0) {
		/* We consider anything else than EINVAL deadly to prevent going
		 * into an infinite loop. (The other alternative is constantly
		 * parsing /proc/self/mountinfo which is yucky and probably
		 * racy.)
		 */
		if (errno != EINVAL)
			return -errno;
	} else {
		/* Just stop counting when this happens. That'd just be so
		 * stupid that we won't even bother trying to report back the
		 * correct value anymore.
		 */
		if (umounts != INT_MAX)
			umounts++;

		/* We succeeded in umounting. Make sure that there's no other
		 * mountpoint stacked underneath.
		 */
		goto pop_stack;
	}

	return umounts;
}

Project: lxc
File: utils.c

static bool is_native_overlayfs(const char *path)
{
	if (has_fs_type(path, OVERLAY_SUPER_MAGIC) ||
	    has_fs_type(path, OVERLAYFS_SUPER_MAGIC))
		return true;

	return false;
}

Project: lxc
File: lxc_user_nic.c

static char **get_groupnames(void)
{
	int ngroups;
	gid_t *group_ids;
	int ret, i;
	char **groupnames;
	struct group grent;
	struct group *grentp = NULL;
	char *buf;
	size_t bufsize;

	ngroups = getgroups(0, NULL);
	if (ngroups < 0) {
		usernic_error("Failed to get number of groups the user "
			      "belongs to: %s\n", strerror(errno));
		return NULL;
	}
	if (ngroups == 0)
		return NULL;

	group_ids = malloc(sizeof(gid_t) * ngroups);
	if (!group_ids) {
		usernic_error("Failed to allocate memory while getting groups "
			      "the user belongs to: %s\n",
			      strerror(errno));
		return NULL;
	}

	ret = getgroups(ngroups, group_ids);
	if (ret < 0) {
		free(group_ids);
		usernic_error("Failed to get process groups: %s\n",
			      strerror(errno));
		return NULL;
	}

	groupnames = malloc(sizeof(char *) * (ngroups + 1));
	if (!groupnames) {
		free(group_ids);
		usernic_error("Failed to allocate memory while getting group "
			      "names: %s\n",
			      strerror(errno));
		return NULL;
	}

	memset(groupnames, 0, sizeof(char *) * (ngroups + 1));

	bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
	if (bufsize == -1)
		bufsize = 1024;

	buf = malloc(bufsize);
	if (!buf) {
		free(group_ids);
		free_groupnames(groupnames);
		usernic_error("Failed to allocate memory while getting group "
			      "names: %s\n",
			      strerror(errno));
		return NULL;
	}

	for (i = 0; i < ngroups; i++) {
		ret = getgrgid_r(group_ids[i], &grent, buf, bufsize, &grentp);
		if (!grentp) {
			if (ret == 0)
				usernic_error("%s", "Could not find matched group record\n");

			usernic_error("Failed to get group name: %s(%u)\n",
			      strerror(errno), group_ids[i]);
			free(buf);
			free(group_ids);
			free_groupnames(groupnames);
			return NULL;
		}

		groupnames[i] = strdup(grent.gr_name);
		if (!groupnames[i]) {
			usernic_error("Failed to copy group name \"%s\"",
				      grent.gr_name);
			free(buf);
			free(group_ids);
			free_groupnames(groupnames);
			return NULL;
		}
	}

	free(buf);
	free(group_ids);

	return groupnames;
}

Project: lxc
File: utils.c

bool lxc_string_in_array(const char *needle, const char **haystack)
{
	for (; haystack && *haystack; haystack++)
		if (!strcmp(needle, *haystack))
			return true;

	return false;
}

Project: lxc
File: utils.c

int randseed(bool srand_it)
{
	/*
	   srand pre-seed function based on /dev/urandom
	   */
	unsigned int seed = time(NULL) + getpid();

	FILE *f;
	f = fopen("/dev/urandom", "r");
	if (f) {
		int ret = fread(&seed, sizeof(seed), 1, f);
		if (ret != 1)
			SYSDEBUG("unable to fread /dev/urandom, fallback to time+pid rand seed");

		fclose(f);
	}

	if (srand_it)
		srand(seed);

	return seed;
}

Project: lxc
File: utils.c

int print_to_file(const char *file, const char *content)
{
	FILE *f;
	int ret = 0;

	f = fopen(file, "w");
	if (!f)
		return -1;

	if (fprintf(f, "%s", content) != strlen(content))
		ret = -1;

	fclose(f);
	return ret;
}

Project: lxc
File: lxc_user_nic.c

static void free_alloted(struct alloted_s **head)
{
	struct alloted_s *cur;

	if (!head)
		return;

	cur = *head;
	while (cur) {
		cur = cur->next;
		free((*head)->name);
		free(*head);
		*head = cur;
	}
}

Project: lxc
File: utils.c

int lxc_safe_int(const char *numstr, int *converted)
{
	char *err = NULL;
	signed long int sli;

	errno = 0;
	sli = strtol(numstr, &err, 0);
	if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
		return -ERANGE;

	if (errno != 0 && sli == 0)
		return -EINVAL;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	if (sli > INT_MAX || sli < INT_MIN)
		return -ERANGE;

	*converted = (int)sli;
	return 0;
}

Project: lxc
File: utils.c

int lxc_grow_array(void ***array, size_t *capacity, size_t new_size, size_t capacity_increment)
{
	size_t new_capacity;
	void **new_array;

	/* first time around, catch some trivial mistakes of the user
	 * only initializing one of these */
	if (!*array || !*capacity) {
		*array = NULL;
		*capacity = 0;
	}

	new_capacity = *capacity;
	while (new_size + 1 > new_capacity)
		new_capacity += capacity_increment;

	if (new_capacity != *capacity) {
		/* we have to reallocate */
		new_array = realloc(*array, new_capacity * sizeof(void *));
		if (!new_array)
			return -1;

		memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *));
		*array = new_array;
		*capacity = new_capacity;
	}

	/* array has sufficient elements */
	return 0;
}

Project: lxc
File: utils.c

static int check_symlink(int fd)
{
	struct stat sb;
	int ret;

	ret = fstat(fd, &sb);
	if (ret < 0)
		return -ENOENT;

	if (S_ISLNK(sb.st_mode))
		return -ELOOP;

	return 0;
}

Project: lxc
File: utils.c

void remove_trailing_newlines(char *l)
{
	char *p = l;

	while (*p)
		p++;

	while (--p >= l && *p == '\n')
		*p = '\0';
}

Project: lxc
File: lxc_user_nic.c

int main(int argc, char *argv[])
{
	int fd, n, pid, request, ret;
	char *me, *newname;
	struct user_nic_args args;
	int container_veth_ifidx = -1, host_veth_ifidx = -1, netns_fd = -1;
	char *cnic = NULL, *nicname = NULL;
	struct alloted_s *alloted = NULL;

	if (argc < 7 || argc > 8) {
		usage(argv[0], true);
		exit(EXIT_FAILURE);
	}

	memset(&args, 0, sizeof(struct user_nic_args));
	args.cmd = argv[1];
	args.lxc_path = argv[2];
	args.lxc_name = argv[3];
	args.pid = argv[4];
	args.type = argv[5];
	args.link = argv[6];
	if (argc >= 8)
		args.veth_name = argv[7];

	if (!strcmp(args.cmd, "create")) {
		request = LXC_USERNIC_CREATE;
	} else if (!strcmp(args.cmd, "delete")) {
		request = LXC_USERNIC_DELETE;
	} else {
		usage(argv[0], true);
		exit(EXIT_FAILURE);
	}

	/* Set a sane env, because we are setuid-root. */
	ret = clearenv();
	if (ret) {
		usernic_error("%s", "Failed to clear environment\n");
		exit(EXIT_FAILURE);
	}

	ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
	if (ret < 0) {
		usernic_error("%s", "Failed to set PATH, exiting\n");
		exit(EXIT_FAILURE);
	}

	me = get_username();
	if (!me) {
		usernic_error("%s", "Failed to get username\n");
		exit(EXIT_FAILURE);
	}

	if (request == LXC_USERNIC_CREATE) {
		ret = lxc_safe_int(args.pid, &pid);
		if (ret < 0) {
			usernic_error("Could not read pid: %s\n", args.pid);
			exit(EXIT_FAILURE);
		}
	} else if (request == LXC_USERNIC_DELETE) {
		char opath[LXC_PROC_PID_FD_LEN];

		/* Open the path with O_PATH which will not trigger an actual
		 * open(). Don't report an errno to the caller to not leak
		 * information whether the path exists or not.
		 * When stracing setuid is stripped so this is not a concern
		 * either.
		 */
		netns_fd = open(args.pid, O_PATH | O_CLOEXEC);
		if (netns_fd < 0) {
			usernic_error("Failed to open \"%s\"\n", args.pid);
			exit(EXIT_FAILURE);
		}

		if (!fhas_fs_type(netns_fd, NSFS_MAGIC)) {
			usernic_error("Path \"%s\" does not refer to a network namespace path\n", args.pid);
			close(netns_fd);
			exit(EXIT_FAILURE);
		}

		ret = snprintf(opath, sizeof(opath), "/proc/self/fd/%d", netns_fd);
		if (ret < 0 || (size_t)ret >= sizeof(opath)) {
			close(netns_fd);
			exit(EXIT_FAILURE);
		}

		/* Now get an fd that we can use in setns() calls. */
		ret = open(opath, O_RDONLY | O_CLOEXEC);
		if (ret < 0) {
			usernic_error("Failed to open \"%s\": %s\n", args.pid, strerror(errno));
			close(netns_fd);
			exit(EXIT_FAILURE);
		}
		close(netns_fd);
		netns_fd = ret;
	}

	if (!create_db_dir(LXC_USERNIC_DB)) {
		usernic_error("%s", "Failed to create directory for db file\n");
		if (netns_fd >= 0)
			close(netns_fd);
		exit(EXIT_FAILURE);
	}

	fd = open_and_lock(LXC_USERNIC_DB);
	if (fd < 0) {
		usernic_error("Failed to lock %s\n", LXC_USERNIC_DB);
		if (netns_fd >= 0)
			close(netns_fd);
		exit(EXIT_FAILURE);
	}

	if (request == LXC_USERNIC_CREATE) {
		if (!may_access_netns(pid)) {
			usernic_error("User %s may not modify netns for pid %d\n", me, pid);
			exit(EXIT_FAILURE);
		}
	} else if (request == LXC_USERNIC_DELETE) {
		bool has_priv;
		has_priv = is_privileged_over_netns(netns_fd);
		close(netns_fd);
		if (!has_priv) {
			usernic_error("%s", "Process is not privileged over "
					    "network namespace\n");
			exit(EXIT_FAILURE);
		}
	}

	n = get_alloted(me, args.type, args.link, &alloted);
	free(me);

	if (request == LXC_USERNIC_DELETE) {
		int ret;
		struct alloted_s *it;
		bool found_nicname = false;

		if (!is_ovs_bridge(args.link)) {
			usernic_error("%s", "Deletion of non ovs type network "
					    "devices not implemented\n");
			close(fd);
			free_alloted(&alloted);
			exit(EXIT_FAILURE);
		}

		/* Check whether the network device we are supposed to delete
		 * exists in the db. If it doesn't we will not delete it as we
		 * need to assume the network device is not under our control.
		 * As a side effect we also clear any invalid entries from the
		 * database.
		 */
		for (it = alloted; it; it = it->next)
			cull_entries(fd, it->name, args.type, args.link,
				     args.veth_name, &found_nicname);
		close(fd);
		free_alloted(&alloted);

		if (!found_nicname) {
			usernic_error("Caller is not allowed to delete network "
				      "device \"%s\"\n", args.veth_name);
			exit(EXIT_FAILURE);
		}

		ret = lxc_ovs_delete_port(args.link, args.veth_name);
		if (ret < 0) {
			usernic_error("Failed to remove port \"%s\" from "
				      "openvswitch bridge \"%s\"",
				      args.veth_name, args.link);
			exit(EXIT_FAILURE);
		}

		exit(EXIT_SUCCESS);
	}
	if (n > 0)
		nicname = get_nic_if_avail(fd, alloted, pid, args.type,
					   args.link, n, &cnic);

	close(fd);
	free_alloted(&alloted);
	if (!nicname) {
		usernic_error("%s", "Quota reached\n");
		exit(EXIT_FAILURE);
	}

	/* Now rename the link. */
	newname = lxc_secure_rename_in_ns(pid, cnic, args.veth_name,
					  &container_veth_ifidx);
	if (!newname) {
		usernic_error("%s", "Failed to rename the link\n");
		ret = lxc_netdev_delete_by_name(cnic);
		if (ret < 0)
			usernic_error("Failed to delete \"%s\"\n", cnic);
		free(nicname);
		exit(EXIT_FAILURE);
	}

	host_veth_ifidx = if_nametoindex(nicname);
	if (!host_veth_ifidx) {
		free(newname);
		free(nicname);
		usernic_error("Failed to get netdev index: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}

	/* Write names of veth pairs and their ifindeces to stout:
	 * (e.g. eth0:731:veth9MT2L4:730)
	 */
	fprintf(stdout, "%s:%d:%s:%d\n", newname, container_veth_ifidx, nicname,
		host_veth_ifidx);
	free(newname);
	free(nicname);
	exit(EXIT_SUCCESS);
}

Project: lxc
File: lxc_user_nic.c

static int get_mtu(char *name)
{
	int idx;

	idx = if_nametoindex(name);
	if (idx < 0)
		return -1;
	return netdev_get_mtu(idx);
}

Project: lxc
File: utils.c

char *get_template_path(const char *t)
{
	int ret, len;
	char *tpath;

	if (t[0] == '/' && access(t, X_OK) == 0) {
		tpath = strdup(t);
		return tpath;
	}

	len = strlen(LXCTEMPLATEDIR) + strlen(t) + strlen("/lxc-") + 1;

	tpath = malloc(len);
	if (!tpath)
		return NULL;

	ret = snprintf(tpath, len, "%s/lxc-%s", LXCTEMPLATEDIR, t);
	if (ret < 0 || ret >= len) {
		free(tpath);
		return NULL;
	}

	if (access(tpath, X_OK) < 0) {
		SYSERROR("bad template: %s", t);
		free(tpath);
		return NULL;
	}

	return tpath;
}

Project: lxc
File: utils.c

char **lxc_string_split_quoted(char *string)
{
	char *nextword = string, *p, state;
	char **result = NULL;
	size_t result_capacity = 0;
	size_t result_count = 0;

	if (!string || !*string)
		return calloc(1, sizeof(char *));

	// TODO I'm *not* handling escaped quote
	state = ' ';
	for (p = string; *p; p++) {
		switch(state) {
		case ' ':
			if (isspace(*p))
				continue;
			else if (*p == '"' || *p == '\'') {
				nextword = p;
				state = *p;
				continue;
			}
			nextword = p;
			state = 'a';
			continue;
		case 'a':
			if (isspace(*p)) {
				complete_word(&result, nextword, p, &result_capacity, &result_count);
				state = ' ';
				continue;
			}
			continue;
		case '"':
		case '\'':
			if (*p == state) {
				complete_word(&result, nextword+1, p, &result_capacity, &result_count);
				state = ' ';
				continue;
			}
			continue;
		}
	}

	if (state == 'a')
		complete_word(&result, nextword, p, &result_capacity, &result_count);

	return realloc(result, (result_count + 1) * sizeof(char *));
}

Project: lxc
File: utils.c

extern int mkdir_p(const char *dir, mode_t mode)
{
	const char *tmp = dir;
	const char *orig = dir;
	char *makeme;

	do {
		dir = tmp + strspn(tmp, "/");
		tmp = dir + strcspn(dir, "/");

		makeme = strndup(orig, dir - orig);
		if (*makeme) {
			if (mkdir(makeme, mode) && errno != EEXIST) {
				SYSERROR("failed to create directory '%s'", makeme);
				free(makeme);
				return -1;
			}
		}
		free(makeme);
	} while(tmp != dir);

	return 0;
}

Project: lxc
File: lxc_user_nic.c

static bool is_privileged_over_netns(int netns_fd)
{
	int ret;
	pid_t pid_self;
	uid_t euid, ruid, suid;
	bool bret = false;
	int ofd = -1;

	pid_self = lxc_raw_getpid();
	ofd = lxc_preserve_ns(pid_self, "net");
	if (ofd < 0) {
		usernic_error("Failed opening network namespace path for %d", pid_self);
		return false;
	}

	ret = getresuid(&ruid, &euid, &suid);
	if (ret < 0) {
		usernic_error("Failed to retrieve real, effective, and saved "
			      "user IDs: %s\n",
			      strerror(errno));
		goto do_partial_cleanup;
	}

	ret = setns(netns_fd, CLONE_NEWNET);
	if (ret < 0) {
		usernic_error("Failed to setns() to network namespace %s\n",
			      strerror(errno));
		goto do_partial_cleanup;
	}

	ret = setresuid(ruid, ruid, 0);
	if (ret < 0) {
		usernic_error("Failed to drop privilege by setting effective "
			      "user id and real user id to %d, and saved user "
			      "ID to 0: %s\n",
			      ruid, strerror(errno));
		/* It's ok to jump to do_full_cleanup here since setresuid()
		 * will succeed when trying to set real, effective, and saved to
		 * values they currently have.
		 */
		goto do_full_cleanup;
	}

	/* Test whether we are privileged over the network namespace. To do this
	 * we try to delete the loopback interface which is not possible. If we
	 * are privileged over the network namespace we will get ENOTSUP. If we
	 * are not privileged over the network namespace we will get EPERM.
	 */
	ret = lxc_netdev_delete_by_name("lo");
	if (ret == -ENOTSUP)
		bret = true;

do_full_cleanup:
	ret = setresuid(ruid, euid, suid);
	if (ret < 0) {
		usernic_error("Failed to restore privilege by setting "
			      "effective user id to %d, real user id to %d, "
			      "and saved user ID to %d: %s\n", ruid, euid, suid,
			      strerror(errno));

		bret = false;
	}

	ret = setns(ofd, CLONE_NEWNET);
	if (ret < 0) {
		usernic_error("Failed to setns() to original network namespace "
			      "of PID %d: %s\n", ofd, strerror(errno));

		bret = false;
	}

do_partial_cleanup:

	close(ofd);
	return bret;
}

Project: lxc
File: lxc_user_nic.c

static int open_and_lock(char *path)
{
	int fd, ret;
	struct flock lk;

	fd = open(path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
	if (fd < 0) {
		usernic_error("Failed to open \"%s\": %s\n", path,
			      strerror(errno));
		return -1;
	}

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

	ret = fcntl(fd, F_SETLKW, &lk);
	if (ret < 0) {
		usernic_error("Failed to lock \"%s\": %s\n", path,
			      strerror(errno));
		close(fd);
		return -1;
	}

	return fd;
}

Project: lxc
File: utils.c

char *lxc_deslashify(const char *path)
{
	char *dup, *p;
	char **parts = NULL;
	size_t n, len;

	dup = strdup(path);
	if (!dup)
		return NULL;

	parts = lxc_normalize_path(dup);
	if (!parts) {
		free(dup);
		return NULL;
	}

	/* We'll end up here if path == "///" or path == "". */
	if (!*parts) {
		len = strlen(dup);
		if (!len) {
			lxc_free_array((void **)parts, free);
			return dup;
		}

		n = strcspn(dup, "/");
		if (n == len) {
			free(dup);
			lxc_free_array((void **)parts, free);

			p = strdup("/");
			if (!p)
				return NULL;

			return p;
		}
	}

	p = lxc_string_join("/", (const char **)parts, *dup == '/');
	free(dup);
	lxc_free_array((void **)parts, free);
	return p;
}

Project: lxc
File: utils.c

static int lxc_get_unused_loop_dev_legacy(char *loop_name)
{
	struct dirent *dp;
	struct loop_info64 lo64;
	DIR *dir;
	int dfd = -1, fd = -1, ret = -1;

	dir = opendir("/dev");
	if (!dir)
		return -1;

	while ((dp = readdir(dir))) {
		if (strncmp(dp->d_name, "loop", 4) != 0)
			continue;

		dfd = dirfd(dir);
		if (dfd < 0)
			continue;

		fd = openat(dfd, dp->d_name, O_RDWR);
		if (fd < 0)
			continue;

		ret = ioctl(fd, LOOP_GET_STATUS64, &lo64);
		if (ret < 0) {
			if (ioctl(fd, LOOP_GET_STATUS64, &lo64) == 0 ||
			    errno != ENXIO) {
				close(fd);
				fd = -1;
				continue;
			}
		}

		ret = snprintf(loop_name, LO_NAME_SIZE, "/dev/%s", dp->d_name);
		if (ret < 0 || ret >= LO_NAME_SIZE) {
			close(fd);
			fd = -1;
			continue;
		}

		break;
	}

	closedir(dir);

	if (fd < 0)
		return -1;

	return fd;
}

Project: lxc
File: utils.c

int lxc_safe_uint64(const char *numstr, uint64_t *converted, int base)
{
	char *err = NULL;
	uint64_t u;

	while (isspace(*numstr))
		numstr++;

	if (*numstr == '-')
		return -EINVAL;

	errno = 0;
	u = strtoull(numstr, &err, base);
	if (errno == ERANGE && u == ULLONG_MAX)
		return -ERANGE;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	*converted = u;
	return 0;
}

Project: lxc
File: utils.c

char *on_path(const char *cmd, const char *rootfs) {
	char *entry = NULL, *path = NULL;
	char cmdpath[MAXPATHLEN];
	int ret;

	path = getenv("PATH");
	if (!path)
		return NULL;

	path = strdup(path);
	if (!path)
		return NULL;

	lxc_iterate_parts(entry, path, ":") {
		if (rootfs)
			ret = snprintf(cmdpath, MAXPATHLEN, "%s/%s/%s", rootfs, entry, cmd);
		else
			ret = snprintf(cmdpath, MAXPATHLEN, "%s/%s", entry, cmd);
		if (ret < 0 || ret >= MAXPATHLEN)
			continue;

		if (access(cmdpath, X_OK) == 0) {
			free(path);
			return strdup(cmdpath);
		}
	}

	free(path);
	return NULL;
}

Project: lxc
File: utils.c

int detect_shared_rootfs(void)
{
	if (is_shared_mountpoint("/"))
		return 1;
	return 0;
}

Project: lxc
File: utils.c

int lxc_make_tmpfile(char *template, bool rm)
{
	int fd, ret;
	mode_t msk;

	msk = umask(0022);
	fd = mkstemp(template);
	umask(msk);
	if (fd < 0)
		return -1;

	if (!rm)
		return fd;

	ret = unlink(template);
	if (ret < 0) {
		close(fd);
		return -1;
	}

	return fd;
}

Project: lxc
File: utils.c

int lxc_setgroups(int size, gid_t list[])
{
	if (setgroups(size, list) < 0) {
		SYSERROR("Failed to setgroups().");
		return -errno;
	}
	NOTICE("Dropped additional groups.");

	return 0;
}

Project: lxc
File: utils.c

char *get_rundir()
{
	char *rundir;
	const char *homedir;
	struct stat sb;

	if (stat(RUNTIME_PATH, &sb) < 0)
		return NULL;

	if (geteuid() == sb.st_uid || getegid() == sb.st_gid) {
		rundir = strdup(RUNTIME_PATH);
		return rundir;
	}

	rundir = getenv("XDG_RUNTIME_DIR");
	if (rundir) {
		rundir = strdup(rundir);
		return rundir;
	}

	INFO("XDG_RUNTIME_DIR isn't set in the environment.");
	homedir = getenv("HOME");
	if (!homedir) {
		ERROR("HOME isn't set in the environment.");
		return NULL;
	}

	rundir = malloc(sizeof(char) * (17 + strlen(homedir)));
	if (!rundir)
		return NULL;

	sprintf(rundir, "%s/.cache/lxc/run/", homedir);

	return rundir;
}

Project: lxc
File: utils.c

ssize_t lxc_read_nointr_expect(int fd, void *buf, size_t count, const void *expected_buf)
{
	ssize_t ret;

	ret = lxc_read_nointr(fd, buf, count);
	if (ret <= 0)
		return ret;

	if ((size_t)ret != count)
		return -1;

	if (expected_buf && memcmp(buf, expected_buf, count) != 0) {
		errno = EINVAL;
		return -1;
	}

	return ret;
}

Project: lxc
File: utils.c

struct lxc_popen_FILE *lxc_popen(const char *command)
{
	int ret;
	int pipe_fds[2];
	pid_t child_pid;
	struct lxc_popen_FILE *fp = NULL;

	ret = pipe2(pipe_fds, O_CLOEXEC);
	if (ret < 0)
		return NULL;

	child_pid = fork();
	if (child_pid < 0)
		goto on_error;

	if (!child_pid) {
		sigset_t mask;

		close(pipe_fds[0]);

		/* duplicate stdout */
		if (pipe_fds[1] != STDOUT_FILENO)
			ret = dup2(pipe_fds[1], STDOUT_FILENO);
		else
			ret = fcntl(pipe_fds[1], F_SETFD, 0);
		if (ret < 0) {
			close(pipe_fds[1]);
			_exit(EXIT_FAILURE);
		}

		/* duplicate stderr */
		if (pipe_fds[1] != STDERR_FILENO)
			ret = dup2(pipe_fds[1], STDERR_FILENO);
		else
			ret = fcntl(pipe_fds[1], F_SETFD, 0);
		close(pipe_fds[1]);
		if (ret < 0)
			_exit(EXIT_FAILURE);

		/* unblock all signals */
		ret = sigfillset(&mask);
		if (ret < 0)
			_exit(EXIT_FAILURE);

		ret = pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
		if (ret < 0)
			_exit(EXIT_FAILURE);

		execl("/bin/sh", "sh", "-c", command, (char *)NULL);
		_exit(127);
	}

	close(pipe_fds[1]);
	pipe_fds[1] = -1;

	fp = malloc(sizeof(*fp));
	if (!fp)
		goto on_error;

	memset(fp, 0, sizeof(*fp));

	fp->child_pid = child_pid;
	fp->pipe = pipe_fds[0];

	/* From now on, closing fp->f will also close fp->pipe. So only ever
	 * call fclose(fp->f).
	 */
	fp->f = fdopen(pipe_fds[0], "r");
	if (!fp->f)
		goto on_error;

	return fp;

on_error:
	/* We can only close pipe_fds[0] if fdopen() didn't succeed or wasn't
	 * called yet. Otherwise the fd belongs to the file opened by fdopen()
	 * since it isn't dup()ed.
	 */
	if (fp && !fp->f && pipe_fds[0] >= 0)
		close(pipe_fds[0]);

	if (pipe_fds[1] >= 0)
		close(pipe_fds[1]);

	if (fp && fp->f)
		fclose(fp->f);

	if (fp)
		free(fp);

	return NULL;
}

Project: lxc
File: utils.c

int lxc_write_to_file(const char *filename, const void *buf, size_t count,
		      bool add_newline, mode_t mode)
{
	int fd, saved_errno;
	ssize_t ret;

	fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
	if (fd < 0)
		return -1;

	ret = lxc_write_nointr(fd, buf, count);
	if (ret < 0)
		goto out_error;

	if ((size_t)ret != count)
		goto out_error;

	if (add_newline) {
		ret = lxc_write_nointr(fd, "\n", 1);
		if (ret != 1)
			goto out_error;
	}

	close(fd);
	return 0;

out_error:
	saved_errno = errno;
	close(fd);
	errno = saved_errno;
	return -1;
}

Project: lxc
File: utils.c

char *lxc_append_paths(const char *first, const char *second)
{
	int ret;
	size_t len;
	char *result = NULL;
	const char *pattern = "%s%s";

	len = strlen(first) + strlen(second) + 1;
	if (second[0] != '/') {
		len += 1;
		pattern = "%s/%s";
	}

	result = calloc(1, len);
	if (!result)
		return NULL;

	ret = snprintf(result, len, pattern, first, second);
	if (ret < 0 || (size_t)ret >= len) {
		free(result);
		return NULL;
	}

	return result;
}

Project: lxc
File: utils.c

char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix)
{
	char *result;
	char **p;
	size_t sep_len = strlen(sep);
	size_t result_len = use_as_prefix * sep_len;
	size_t buf_len;

	/* calculate new string length */
	for (p = (char **)parts; *p; p++)
		result_len += (p > (char **)parts) * sep_len + strlen(*p);

	buf_len = result_len + 1;
	result = calloc(buf_len, 1);
	if (!result)
		return NULL;

	if (use_as_prefix)
		(void)strlcpy(result, sep, buf_len);

	for (p = (char **)parts; *p; p++) {
		if (p > (char **)parts)
			(void)strlcat(result, sep, buf_len);

		(void)strlcat(result, *p, buf_len);
	}

	return result;
}

Project: lxc
File: utils.c

int lxc_safe_long_long(const char *numstr, long long int *converted)
{
	char *err = NULL;
	signed long long int sli;

	errno = 0;
	sli = strtoll(numstr, &err, 0);
	if (errno == ERANGE && (sli == LLONG_MAX || sli == LLONG_MIN))
		return -ERANGE;

	if (errno != 0 && sli == 0)
		return -EINVAL;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	*converted = sli;
	return 0;
}

Project: lxc
File: utils.c

int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args)
{
	pid_t child;
	int ret, fret, pipefd[2];
	ssize_t bytes;

	/* Make sure our callers do not receive uninitialized memory. */
	if (buf_size > 0 && buf)
		buf[0] = '\0';

	if (pipe(pipefd) < 0) {
		SYSERROR("failed to create pipe");
		return -1;
	}

	child = lxc_raw_clone(0);
	if (child < 0) {
		close(pipefd[0]);
		close(pipefd[1]);
		SYSERROR("failed to create new process");
		return -1;
	}

	if (child == 0) {
		/* Close the read-end of the pipe. */
		close(pipefd[0]);

		/* Redirect std{err,out} to write-end of the
		 * pipe.
		 */
		ret = dup2(pipefd[1], STDOUT_FILENO);
		if (ret >= 0)
			ret = dup2(pipefd[1], STDERR_FILENO);

		/* Close the write-end of the pipe. */
		close(pipefd[1]);

		if (ret < 0) {
			SYSERROR("failed to duplicate std{err,out} file descriptor");
			_exit(EXIT_FAILURE);
		}

		/* Does not return. */
		child_fn(args);
		ERROR("failed to exec command");
		_exit(EXIT_FAILURE);
	}

	/* close the write-end of the pipe */
	close(pipefd[1]);

	if (buf && buf_size > 0) {
		bytes = lxc_read_nointr(pipefd[0], buf, buf_size - 1);
		if (bytes > 0)
			buf[bytes - 1] = '\0';
	}

	fret = wait_for_pid(child);
	/* close the read-end of the pipe */
	close(pipefd[0]);

	return fret;
}

Project: lxc
File: utils.c

int parse_byte_size_string(const char *s, int64_t *converted)
{
	int ret, suffix_len;
	long long int conv;
	int64_t mltpl, overflow;
	char *end;
	char dup[LXC_NUMSTRLEN64 + 2];
	char suffix[3] = {0};

	if (!s || !strcmp(s, ""))
		return -EINVAL;

	end = stpncpy(dup, s, sizeof(dup) - 1);
	if (*end != '\0')
		return -EINVAL;

	if (isdigit(*(end - 1)))
		suffix_len = 0;
	else if (isalpha(*(end - 1)))
		suffix_len = 1;
	else
		return -EINVAL;

	if (suffix_len > 0 && (end - 2) == dup && !isdigit(*(end - 2)))
		return -EINVAL;

	if (suffix_len > 0 && isalpha(*(end - 2)))
		suffix_len++;

	if (suffix_len > 0) {
		memcpy(suffix, end - suffix_len, suffix_len);
		*(suffix + suffix_len) = '\0';
		*(end - suffix_len) = '\0';
	}
	dup[lxc_char_right_gc(dup, strlen(dup))] = '\0';

	ret = lxc_safe_long_long(dup, &conv);
	if (ret < 0)
		return -ret;

	if (suffix_len != 2) {
		*converted = conv;
		return 0;
	}

	if (strcasecmp(suffix, "KB") == 0)
		mltpl = 1024;
	else if (strcasecmp(suffix, "MB") == 0)
		mltpl = 1024 * 1024;
	else if (strcasecmp(suffix, "GB") == 0)
		mltpl = 1024 * 1024 * 1024;
	else
		return -EINVAL;

	overflow = conv * mltpl;
	if (conv != 0 && (overflow / conv) != mltpl)
		return -ERANGE;

	*converted = overflow;
	return 0;
}

Project: lxc
File: utils.c

static int _recursive_rmdir(const char *dirname, dev_t pdev,
			    const char *exclude, int level, bool onedev)
{
	struct dirent *direntp;
	DIR *dir;
	int ret, failed=0;
	char pathname[MAXPATHLEN];
	bool hadexclude = false;

	dir = opendir(dirname);
	if (!dir) {
		ERROR("failed to open %s", dirname);
		return -1;
	}

	while ((direntp = readdir(dir))) {
		struct stat mystat;
		int rc;

		if (!strcmp(direntp->d_name, ".") ||
		    !strcmp(direntp->d_name, ".."))
			continue;

		rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name);
		if (rc < 0 || rc >= MAXPATHLEN) {
			ERROR("pathname too long");
			failed=1;
			continue;
		}

		if (!level && exclude && !strcmp(direntp->d_name, exclude)) {
			ret = rmdir(pathname);
			if (ret < 0) {
				switch(errno) {
				case ENOTEMPTY:
					INFO("Not deleting snapshot %s", pathname);
					hadexclude = true;
					break;
				case ENOTDIR:
					ret = unlink(pathname);
					if (ret)
						INFO("Failed to remove %s", pathname);
					break;
				default:
					SYSERROR("Failed to rmdir %s", pathname);
					failed = 1;
					break;
				}
			}
			continue;
		}

		ret = lstat(pathname, &mystat);
		if (ret) {
			ERROR("Failed to stat %s", pathname);
			failed = 1;
			continue;
		}

		if (onedev && mystat.st_dev != pdev) {
			/* TODO should we be checking /proc/self/mountinfo for
			 * pathname and not doing this if found? */
			if (btrfs_try_remove_subvol(pathname))
				INFO("Removed btrfs subvolume at %s\n", pathname);
			continue;
		}

		if (S_ISDIR(mystat.st_mode)) {
			if (_recursive_rmdir(pathname, pdev, exclude, level+1, onedev) < 0)
				failed=1;
		} else {
			if (unlink(pathname) < 0) {
				SYSERROR("Failed to delete %s", pathname);
				failed=1;
			}
		}
	}

	if (rmdir(dirname) < 0 && !btrfs_try_remove_subvol(dirname) && !hadexclude) {
		ERROR("Failed to delete %s", dirname);
		failed=1;
	}

	ret = closedir(dir);
	if (ret) {
		ERROR("Failed to close directory %s", dirname);
		failed=1;
	}

	return failed ? -1 : 0;
}

Project: lxc
File: utils.c

ssize_t lxc_write_nointr(int fd, const void *buf, size_t count)
{
	ssize_t ret;
again:
	ret = write(fd, buf, count);
	if (ret < 0 && errno == EINTR)
		goto again;

	return ret;
}

Project: lxc
File: utils.c

char **lxc_string_split_and_trim(const char *string, char _sep)
{
	char *token, *str;
	char sep[2] = { _sep, '\0' };
	char **result = NULL;
	size_t result_capacity = 0;
	size_t result_count = 0;
	int r, saved_errno;
	size_t i = 0;
	size_t len;

	if (!string)
		return calloc(1, sizeof(char *));

	len = strlen(string);
	str = alloca(len + 1);
	(void)strlcpy(str, string, len + 1);

	lxc_iterate_parts(token, str, sep) {
		while (token[0] == ' ' || token[0] == '\t')
			token++;

		i = strlen(token);
		while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) {
			token[i - 1] = '\0';
			i--;
		}

		r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
		if (r < 0)
			goto error_out;

		result[result_count] = strdup(token);
		if (!result[result_count])
			goto error_out;

		result_count++;
	}

	/* if we allocated too much, reduce it */
	return realloc(result, (result_count + 1) * sizeof(char *));

error_out:
	saved_errno = errno;
	lxc_free_array((void **)result, free);
	errno = saved_errno;
	return NULL;
}

Project: lxc
File: utils.c

static int open_without_symlink(const char *target, const char *prefix_skip)
{
	int curlen = 0, dirfd, fulllen, i;
	char *dup = NULL;

	fulllen = strlen(target);

	/* make sure prefix-skip makes sense */
	if (prefix_skip && strlen(prefix_skip) > 0) {
		curlen = strlen(prefix_skip);
		if (!is_subdir(target, prefix_skip, curlen)) {
			ERROR("WHOA there - target '%s' didn't start with prefix '%s'",
				target, prefix_skip);
			return -EINVAL;
		}

		/*
		 * get_nextpath() expects the curlen argument to be
		 * on a  (turned into \0) / or before it, so decrement
		 * curlen to make sure that happens
		 */
		if (curlen)
			curlen--;
	} else {
		prefix_skip = "/";
		curlen = 0;
	}

	/* Make a copy of target which we can hack up, and tokenize it */
	if ((dup = strdup(target)) == NULL) {
		SYSERROR("Out of memory checking for symbolic link");
		return -ENOMEM;
	}

	for (i = 0; i < fulllen; i++) {
		if (dup[i] == '/')
			dup[i] = '\0';
	}

	dirfd = open(prefix_skip, O_RDONLY);
	if (dirfd < 0)
		goto out;

	while (1) {
		int newfd, saved_errno;
		char *nextpath;

		if ((nextpath = get_nextpath(dup, &curlen, fulllen)) == NULL)
			goto out;

		newfd = open_if_safe(dirfd, nextpath);
		saved_errno = errno;
		close(dirfd);

		dirfd = newfd;
		if (newfd < 0) {
			errno = saved_errno;
			if (errno == ELOOP)
				SYSERROR("%s in %s was a symbolic link!", nextpath, target);

			goto out;
		}
	}

out:
	free(dup);
	return dirfd;
}

Project: lxc
File: utils.c

static void gnutls_lxc_init(void)
{
	gnutls_global_init();
}

Project: lxc
File: lxc_user_nic.c

static char *get_eow(char *s, char *e)
{
	while ((s < e) && *s && !isblank(*s) && (*s != '\n'))
		s++;
	return s;
}

Project: lxc
File: utils.c

char *must_make_path(const char *first, ...)
{
	va_list args;
	char *cur, *dest;
	size_t full_len = strlen(first);
	size_t buf_len;

	dest = must_copy_string(first);

	va_start(args, first);
	while ((cur = va_arg(args, char *)) != NULL) {
		full_len += strlen(cur);
		if (cur[0] != '/')
			full_len++;

		buf_len = full_len + 1;
		dest = must_realloc(dest, buf_len);

		if (cur[0] != '/')
			(void)strlcat(dest, "/", buf_len);
		(void)strlcat(dest, cur, buf_len);
	}
	va_end(args);

	return dest;
}

Project: lxc
File: lxc_user_nic.c

static bool cull_entries(int fd, char *name, char *net_type, char *net_link,
			 char *net_dev, bool *found_nicname)
{
	int i, ret;
	char *buf, *buf_end, *buf_start;
	struct stat sb;
	int n = 0;
	bool found, keep;
	struct entry_line *entry_lines = NULL;

	ret = fstat(fd, &sb);
	if (ret < 0) {
		usernic_error("Failed to fstat: %s\n", strerror(errno));
		return false;
	}

	if (!sb.st_size)
		return false;

	buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (buf == MAP_FAILED) {
		usernic_error("Failed to establish shared memory mapping: %s\n",
			      strerror(errno));
		return false;
	}

	buf_start = buf;
	buf_end = buf + sb.st_size;
	while ((buf_start = find_line(buf_start, buf_end, name, net_type,
				      net_link, net_dev, &(bool){true}, &found,
				      &keep))) {
		struct entry_line *newe;

		newe = realloc(entry_lines, sizeof(*entry_lines) * (n + 1));
		if (!newe) {
			free(entry_lines);
			lxc_strmunmap(buf, sb.st_size);
			return false;
		}

		if (found)
			*found_nicname = true;

		entry_lines = newe;
		entry_lines[n].start = buf_start;
		entry_lines[n].len = get_eol(buf_start, buf_end) - entry_lines[n].start;
		entry_lines[n].keep = keep;
		n++;

		buf_start += entry_lines[n - 1].len + 1;
		if (buf_start >= buf_end)
			break;
	}

	buf_start = buf;
	for (i = 0; i < n; i++) {
		if (!entry_lines[i].keep)
			continue;

		memcpy(buf_start, entry_lines[i].start, entry_lines[i].len);
		buf_start += entry_lines[i].len;
		*buf_start = '\n';
		buf_start++;
	}
	free(entry_lines);

	ret = ftruncate(fd, buf_start - buf);
	lxc_strmunmap(buf, sb.st_size);
	if (ret < 0)
		usernic_error("Failed to set new file size: %s\n",
			      strerror(errno));

	return true;
}

Project: lxc
File: utils.c

int safe_mount(const char *src, const char *dest, const char *fstype,
		unsigned long flags, const void *data, const char *rootfs)
{
	int destfd, ret, saved_errno;
	/* Only needs enough for /proc/self/fd/<fd>. */
	char srcbuf[50], destbuf[50];
	int srcfd = -1;
	const char *mntsrc = src;

	if (!rootfs)
		rootfs = "";

	/* todo - allow symlinks for relative paths if 'allowsymlinks' option is passed */
	if (flags & MS_BIND && src && src[0] != '/') {
		INFO("this is a relative bind mount");

		srcfd = open_without_symlink(src, NULL);
		if (srcfd < 0)
			return srcfd;

		ret = snprintf(srcbuf, 50, "/proc/self/fd/%d", srcfd);
		if (ret < 0 || ret > 50) {
			close(srcfd);
			ERROR("Out of memory");
			return -EINVAL;
		}
		mntsrc = srcbuf;
	}

	destfd = open_without_symlink(dest, rootfs);
	if (destfd < 0) {
		if (srcfd != -1) {
			saved_errno = errno;
			close(srcfd);
			errno = saved_errno;
		}

		return destfd;
	}

	ret = snprintf(destbuf, 50, "/proc/self/fd/%d", destfd);
	if (ret < 0 || ret > 50) {
		if (srcfd != -1)
			close(srcfd);

		close(destfd);
		ERROR("Out of memory");
		return -EINVAL;
	}

	ret = mount(mntsrc, destbuf, fstype, flags, data);
	saved_errno = errno;
	if (srcfd != -1)
		close(srcfd);

	close(destfd);
	if (ret < 0) {
		errno = saved_errno;
		SYSERROR("Failed to mount %s onto %s", src ? src : "(null)", dest);
		return ret;
	}

	return 0;
}

Project: lxc
File: utils.c

bool cgns_supported(void)
{
	return file_exists("/proc/self/ns/cgroup");
}

Project: lxc
File: utils.c

bool file_exists(const char *f)
{
	struct stat statbuf;

	return stat(f, &statbuf) == 0;
}

Project: lxc
File: utils.c

int lxc_preserve_ns(const int pid, const char *ns)
{
	int ret;
/* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */
#define __NS_PATH_LEN 50
	char path[__NS_PATH_LEN];

	/* This way we can use this function to also check whether namespaces
	 * are supported by the kernel by passing in the NULL or the empty
	 * string.
	 */
	ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid,
		       !ns || strcmp(ns, "") == 0 ? "" : "/",
		       !ns || strcmp(ns, "") == 0 ? "" : ns);
	if (ret < 0 || (size_t)ret >= __NS_PATH_LEN) {
		errno = EFBIG;
		return -1;
	}

	return open(path, O_RDONLY | O_CLOEXEC);
}

Project: lxc
File: lxc_user_nic.c

static char *find_line(char *buf_start, char *buf_end, char *name,
		       char *net_type, char *net_link, char *net_dev,
		       bool *owner, bool *found, bool *keep)
{
	char *end_of_line, *end_of_word, *line;

	while (buf_start < buf_end) {
		size_t len;
		char netdev_name[IFNAMSIZ];

		*found = false;
		*keep = true;
		*owner = false;

		end_of_line = get_eol(buf_start, buf_end);
		if (end_of_line >= buf_end)
			return NULL;

		line = buf_start;
		if (*buf_start == '#')
			goto next;

		while ((buf_start < buf_end) && isblank(*buf_start))
			buf_start++;

		/* Check whether the line contains the caller's name. */
		end_of_word = get_eow(buf_start, buf_end);
		/* corrupt db */
		if (!end_of_word)
			return NULL;

		if (strncmp(buf_start, name, strlen(name)))
			*found = false;

		*owner = true;

		buf_start = end_of_word + 1;
		while ((buf_start < buf_end) && isblank(*buf_start))
			buf_start++;

		/* Check whether line is of the right network type. */
		end_of_word = get_eow(buf_start, buf_end);
		/* corrupt db */
		if (!end_of_word)
			return NULL;

		if (strncmp(buf_start, net_type, strlen(net_type)))
			*found = false;

		buf_start = end_of_word + 1;
		while ((buf_start < buf_end) && isblank(*buf_start))
			buf_start++;

		/* Check whether line is contains the right link. */
		end_of_word = get_eow(buf_start, buf_end);
		/* corrupt db */
		if (!end_of_word)
			return NULL;

		if (strncmp(buf_start, net_link, strlen(net_link)))
			*found = false;

		buf_start = end_of_word + 1;
		while ((buf_start < buf_end) && isblank(*buf_start))
			buf_start++;

		/* Check whether line contains the right network device. */
		end_of_word = get_eow(buf_start, buf_end);
		/* corrupt db */
		if (!end_of_word)
			return NULL;

		len = end_of_word - buf_start;
		/* corrupt db */
		if (len >= IFNAMSIZ)
			return NULL;

		memcpy(netdev_name, buf_start, len);
		netdev_name[len] = '\0';
		*keep = lxc_nic_exists(netdev_name);

		if (net_dev && !strcmp(netdev_name, net_dev))
			*found = true;

		return line;

	next:
		buf_start = end_of_line + 1;
	}

	return NULL;
}

Project: lxc
File: utils.c

extern int get_u16(unsigned short *val, const char *arg, int base)
{
	unsigned long res;
	char *ptr;

	if (!arg || !*arg)
		return -1;

	errno = 0;
	res = strtoul(arg, &ptr, base);
	if (!ptr || ptr == arg || *ptr || res > 0xFFFF || errno != 0)
		return -1;

	*val = res;

	return 0;
}

Project: lxc
File: utils.c

int lxc_wait_for_pid_status(pid_t pid)
{
	int status, ret;

again:
	ret = waitpid(pid, &status, 0);
	if (ret == -1) {
		if (errno == EINTR)
			goto again;

		return -1;
	}

	if (ret != pid)
		goto again;

	return status;
}

Project: lxc
File: utils.c

char **lxc_string_split(const char *string, char _sep)
{
	char *token, *str;
	char sep[2] = {_sep, '\0'};
	char **tmp = NULL, **result = NULL;
	size_t result_capacity = 0;
	size_t result_count = 0;
	int r, saved_errno;
	size_t len;

	if (!string)
		return calloc(1, sizeof(char *));

	len = strlen(string);
	str = alloca(len + 1);
	(void)strlcpy(str, string, len + 1);

	lxc_iterate_parts(token, str, sep) {
		r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
		if (r < 0)
			goto error_out;

		result[result_count] = strdup(token);
		if (!result[result_count])
			goto error_out;

		result_count++;
	}

	/* if we allocated too much, reduce it */
	tmp = realloc(result, (result_count + 1) * sizeof(char *));
	if (!tmp)
		goto error_out;

	result = tmp;

	/* Make sure we don't return uninitialized memory. */
	if (result_count == 0)
		*result = NULL;

	return result;

error_out:
	saved_errno = errno;
	lxc_free_array((void **)result, free);
	errno = saved_errno;
	return NULL;
}

Project: lxc
File: utils.c

int lxc_mount_proc_if_needed(const char *rootfs)
{
	char path[MAXPATHLEN];
	int link_to_pid, linklen, mypid, ret;
	char link[LXC_NUMSTRLEN64] = {0};

	ret = snprintf(path, MAXPATHLEN, "%s/proc/self", rootfs);
	if (ret < 0 || ret >= MAXPATHLEN) {
		SYSERROR("proc path name too long");
		return -1;
	}

	linklen = readlink(path, link, LXC_NUMSTRLEN64);

	ret = snprintf(path, MAXPATHLEN, "%s/proc", rootfs);
	if (ret < 0 || ret >= MAXPATHLEN) {
		SYSERROR("proc path name too long");
		return -1;
	}

	/* /proc not mounted */
	if (linklen < 0) {
		if (mkdir(path, 0755) && errno != EEXIST)
			return -1;

		goto domount;
	} else if (linklen >= LXC_NUMSTRLEN64) {
		link[linklen - 1] = '\0';
		ERROR("readlink returned truncated content: \"%s\"", link);
		return -1;
	}

	mypid = lxc_raw_getpid();
	INFO("I am %d, /proc/self points to \"%s\"", mypid, link);

	if (lxc_safe_int(link, &link_to_pid) < 0)
		return -1;

	/* correct procfs is already mounted */
	if (link_to_pid == mypid)
		return 0;

	ret = umount2(path, MNT_DETACH);
	if (ret < 0)
		WARN("failed to umount \"%s\" with MNT_DETACH", path);

domount:
	/* rootfs is NULL */
	if (!strcmp(rootfs, ""))
		ret = mount("proc", path, "proc", 0, NULL);
	else
		ret = safe_mount("proc", path, "proc", 0, NULL, rootfs);
	if (ret < 0)
		return -1;

	INFO("mounted /proc in container for security transition");
	return 1;
}

Project: lxc
File: lxc_user_nic.c

static char *get_eol(char *s, char *e)
{
	while ((s < e) && *s && (*s != '\n'))
		s++;
	return s;
}

Project: lxc
File: lxc_user_nic.c

static int instantiate_veth(char *veth1, char *veth2)
{
	int ret;

	ret = lxc_veth_create(veth1, veth2);
	if (ret < 0) {
		usernic_error("Failed to create %s-%s : %s.\n", veth1, veth2,
			      strerror(-ret));
		return -1;
	}

	/* Changing the high byte of the mac address to 0xfe, the bridge
	 * interface will always keep the host's mac address and not take the
	 * mac address of a container. */
	ret = setup_private_host_hw_addr(veth1);
	if (ret < 0)
		usernic_error("Failed to change mac address of host interface "
			      "%s : %s\n", veth1, strerror(-ret));

	return netdev_set_flag(veth1, IFF_UP);
}

Project: lxc
File: utils.c

bool dir_exists(const char *path)
{
	struct stat sb;
	int ret;

	ret = stat(path, &sb);
	if (ret < 0)
		/* Could be something other than eexist, just say "no". */
		return false;

	return S_ISDIR(sb.st_mode);
}

Project: lxc
File: utils.c

bool switch_to_ns(pid_t pid, const char *ns) {
	int fd, ret;
	char nspath[MAXPATHLEN];

	/* Switch to new ns */
	ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns);
	if (ret < 0 || ret >= MAXPATHLEN)
		return false;

	fd = open(nspath, O_RDONLY);
	if (fd < 0) {
		SYSERROR("Failed to open %s", nspath);
		return false;
	}

	ret = setns(fd, 0);
	if (ret) {
		SYSERROR("Failed to set process %d to %s of %d.", pid, ns, fd);
		close(fd);
		return false;
	}

	close(fd);
	return true;
}

Project: lxc
File: utils.c

char *must_copy_string(const char *entry)
{
	char *ret;

	if (!entry)
		return NULL;

	do {
		ret = strdup(entry);
	} while (!ret);

	return ret;
}

Project: lxc
File: utils.c

void lxc_free_array(void **array, lxc_free_fn element_free_fn)
{
	void **p;

	for (p = array; p && *p; p++)
		element_free_fn(*p);

	free((void*)array);
}

Project: lxc
File: utils.c

int lxc_append_string(char ***list, char *entry)
{
	char *copy;
	int newentry;

	newentry = lxc_append_null_to_list((void ***)list);
	if (newentry < 0)
		return -1;

	copy = strdup(entry);
	if (!copy)
		return -1;

	(*list)[newentry] = copy;

	return 0;
}

Project: lxc
File: utils.c

bool lxc_string_in_list(const char *needle, const char *haystack, char _sep)
{
	char *token, *str;
	char sep[2] = { _sep, '\0' };
	size_t len;

	if (!haystack || !needle)
		return 0;

	len = strlen(haystack);
	str = alloca(len + 1);
	(void)strlcpy(str, haystack, len + 1);

	lxc_iterate_parts(token, str, sep)
		if (strcmp(needle, token) == 0)
			return 1;

	return 0;
}

Project: lxc
File: utils.c

void *must_realloc(void *orig, size_t sz)
{
	void *ret;

	do {
		ret = realloc(orig, sz);
	} while (!ret);

	return ret;
}

Project: lxc
File: utils.c

int lxc_read_from_file(const char *filename, void *buf, size_t count)
{
	int fd = -1, saved_errno;
	ssize_t ret;

	fd = open(filename, O_RDONLY | O_CLOEXEC);
	if (fd < 0)
		return -1;

	if (!buf || !count) {
		char buf2[100];
		size_t count2 = 0;

		while ((ret = lxc_read_nointr(fd, buf2, 100)) > 0)
			count2 += ret;

		if (ret >= 0)
			ret = count2;
	} else {
		memset(buf, 0, count);
		ret = lxc_read_nointr(fd, buf, count);
	}

	if (ret < 0)
		SYSERROR("Read %s", filename);

	saved_errno = errno;
	close(fd);
	errno = saved_errno;
	return ret;
}

Project: lxc
File: lxc_user_nic.c

static void usage(char *me, bool fail)
{
	fprintf(stderr, "Usage: %s create {lxcpath} {name} {pid} {type} "
			"{bridge} {nicname}\n", me);
	fprintf(stderr, "Usage: %s delete {lxcpath} {name} "
			"{/proc/<pid>/ns/net} {type} {bridge} {nicname}\n", me);
	fprintf(stderr, "{nicname} is the name to use inside the container\n");

	if (fail)
		exit(EXIT_FAILURE);

	exit(EXIT_SUCCESS);
}

Project: lxc
File: utils.c

int wait_for_pid(pid_t pid)
{
	int status, ret;

again:
	ret = waitpid(pid, &status, 0);
	if (ret == -1) {
		if (errno == EINTR)
			goto again;

		return -1;
	}

	if (ret != pid)
		goto again;

	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
		return -1;

	return 0;
}

Project: lxc
File: utils.c

char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup)
{
	va_list ap2;
	size_t count = 1 + skip;
	char **result;

	/* first determine size of argument list, we don't want to reallocate
	 * constantly...
	 */
	va_copy(ap2, ap);
	while (1) {
		char* arg = va_arg(ap2, char*);
		if (!arg)
			break;
		count++;
	}
	va_end(ap2);

	result = calloc(count, sizeof(char*));
	if (!result)
		return NULL;

	count = skip;
	while (1) {
		char* arg = va_arg(ap, char*);
		if (!arg)
			break;
		arg = do_strdup ? strdup(arg) : arg;
		if (!arg)
			goto oom;
		result[count++] = arg;
	}

	/* calloc has already set last element to NULL*/
	return result;

oom:
	free(result);
	return NULL;
}

Project: lxc
File: lxc_user_nic.c

static int create_nic(char *nic, char *br, int pid, char **cnic)
{
	char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ];
	int mtu, ret;

	ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic);
	if (ret < 0 || ret >= IFNAMSIZ) {
		usernic_error("%s", "Could not create nic name\n");
		return -1;
	}

	ret = snprintf(veth2buf, IFNAMSIZ, "%sp", veth1buf);
	if (ret < 0 || ret >= IFNAMSIZ) {
		usernic_error("%s\n", "Could not create nic name");
		return -1;
	}
	/* create the nics */
	ret = instantiate_veth(veth1buf, veth2buf);
	if (ret < 0) {
		usernic_error("%s", "Error creating veth tunnel\n");
		return -1;
	}

	if (strcmp(br, "none")) {
		/* copy the bridge's mtu to both ends */
		mtu = get_mtu(br);
		if (mtu > 0) {
			ret = lxc_netdev_set_mtu(veth1buf, mtu);
			if (ret < 0) {
				usernic_error("Failed to set mtu to %d on %s\n",
					      mtu, veth1buf);
				goto out_del;
			}

			ret = lxc_netdev_set_mtu(veth2buf, mtu);
			if (ret < 0) {
				usernic_error("Failed to set mtu to %d on %s\n",
					      mtu, veth2buf);
				goto out_del;
			}
		}

		/* attach veth1 to bridge */
		ret = lxc_bridge_attach(br, veth1buf);
		if (ret < 0) {
			usernic_error("Error attaching %s to %s\n", veth1buf, br);
			goto out_del;
		}
	}

	/* pass veth2 to target netns */
	ret = lxc_netdev_move_by_name(veth2buf, pid, NULL);
	if (ret < 0) {
		usernic_error("Error moving %s to network namespace of %d\n",
			      veth2buf, pid);
		goto out_del;
	}

	*cnic = strdup(veth2buf);
	if (!*cnic) {
		usernic_error("Failed to copy string \"%s\"\n", veth2buf);
		return -1;
	}

	return 0;

out_del:
	lxc_netdev_delete_by_name(veth1buf);
	return -1;
}

Project: lxc
File: utils.c

int sha1sum_file(char *fnam, unsigned char *digest)
{
	char *buf;
	int ret;
	FILE *f;
	long flen;

	if (!fnam)
		return -1;

	f = fopen_cloexec(fnam, "r");
	if (!f) {
		SYSERROR("Error opening template");
		return -1;
	}

	if (fseek(f, 0, SEEK_END) < 0) {
		SYSERROR("Error seeking to end of template");
		fclose(f);
		return -1;
	}

	if ((flen = ftell(f)) < 0) {
		SYSERROR("Error telling size of template");
		fclose(f);
		return -1;
	}

	if (fseek(f, 0, SEEK_SET) < 0) {
		SYSERROR("Error seeking to start of template");
		fclose(f);
		return -1;
	}

	if ((buf = malloc(flen+1)) == NULL) {
		SYSERROR("Out of memory");
		fclose(f);
		return -1;
	}

	if (fread(buf, 1, flen, f) != flen) {
		SYSERROR("Failure reading template");
		free(buf);
		fclose(f);
		return -1;
	}

	if (fclose(f) < 0) {
		SYSERROR("Failre closing template");
		free(buf);
		return -1;
	}

	buf[flen] = '\0';
	ret = gnutls_hash_fast(GNUTLS_DIG_SHA1, buf, flen, (void *)digest);
	free(buf);
	return ret;
}

Project: lxc
File: utils.c

uint64_t lxc_find_next_power2(uint64_t n)
{
	/* 0 is not valid input. We return 0 to the caller since 0 is not a
	 * valid power of two.
	 */
	if (n == 0)
		return 0;

	if (!(n & (n - 1)))
		return n;

	while (n & (n - 1))
		n = n & (n - 1);

	n = n << 1;
	return n;
}

Project: lxc
File: lxc_user_nic.c

static struct alloted_s *append_alloted(struct alloted_s **head, char *name,
					int n)
{
	struct alloted_s *cur, *al;

	if (!head || !name) {
		/* Sanity check. Parameters should not be null. */
		usernic_error("%s\n", "Unexpected NULL argument");
		return NULL;
	}

	al = malloc(sizeof(struct alloted_s));
	if (!al) {
		usernic_error("Failed to allocate memory: %s\n",
			      strerror(errno));
		return NULL;
	}

	al->name = strdup(name);
	if (!al->name) {
		free(al);
		return NULL;
	}

	al->allowed = n;
	al->next = NULL;

	if (!*head) {
		*head = al;
		return al;
	}

	cur = *head;
	while (cur->next)
		cur = cur->next;
	cur->next = al;

	return al;
}

Project: lxc
File: lxc_user_nic.c

static bool name_is_in_groupnames(char *name, char **groupnames)
{
	while (groupnames) {
		if (!strcmp(name, *groupnames))
			return true;
		groupnames++;
	}
	return false;
}

Project: lxc
File: utils.c

ssize_t lxc_read_nointr(int fd, void *buf, size_t count)
{
	ssize_t ret;
again:
	ret = read(fd, buf, count);
	if (ret < 0 && errno == EINTR)
		goto again;

	return ret;
}

Project: lxc
File: utils.c

uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval)
{
	unsigned char *bp;

	for(bp = buf; bp < (unsigned char *)buf + len; bp++)
	{
		/* xor the bottom with the current octet */
		hval ^= (uint64_t)*bp;

		/* gcc optimised:
		 * multiply by the 64 bit FNV magic prime mod 2^64
		 */
		hval += (hval << 1) + (hval << 4) + (hval << 5) +
			(hval << 7) + (hval << 8) + (hval << 40);
	}

	return hval;
}

Project: lxc
File: utils.c

bool fhas_fs_type(int fd, fs_type_magic magic_val)
{
	int ret;
	struct statfs sb;

	ret = fstatfs(fd, &sb);
	if (ret < 0)
		return false;

	return is_fs_type(&sb, magic_val);
}

Project: lxc
File: utils.c

bool detect_ramfs_rootfs(void)
{
	FILE *f;
	char *p, *p2;
	char *line = NULL;
	size_t len = 0;
	int i;

	f = fopen("/proc/self/mountinfo", "r");
	if (!f)
		return false;

	while (getline(&line, &len, f) != -1) {
		for (p = line, i = 0; p && i < 4; i++)
			p = strchr(p + 1, ' ');
		if (!p)
			continue;

		p2 = strchr(p + 1, ' ');
		if (!p2)
			continue;

		*p2 = '\0';
		if (strcmp(p + 1, "/") == 0) {
			/* This is '/'. Is it the ramfs? */
			p = strchr(p2 + 1, '-');
			if (p && strncmp(p, "- rootfs rootfs ", 16) == 0) {
				free(line);
				fclose(f);
				INFO("Rootfs is located on ramfs");
				return true;
			}
		}
	}

	free(line);
	fclose(f);
	return false;
}

Project: lxc
File: utils.c

char **lxc_normalize_path(const char *path)
{
	char **components;
	char **p;
	size_t components_len = 0;
	size_t pos = 0;

	components = lxc_string_split(path, '/');
	if (!components)
		return NULL;

	for (p = components; *p; p++)
		components_len++;

	/* resolve '.' and '..' */
	for (pos = 0; pos < components_len; ) {
		if (!strcmp(components[pos], ".") || (!strcmp(components[pos], "..") && pos == 0)) {
			/* eat this element */
			free(components[pos]);
			memmove(&components[pos], &components[pos+1], sizeof(char *) * (components_len - pos));
			components_len--;
		} else if (!strcmp(components[pos], "..")) {
			/* eat this and the previous element */
			free(components[pos - 1]);
			free(components[pos]);
			memmove(&components[pos-1], &components[pos+1], sizeof(char *) * (components_len - pos));
			components_len -= 2;
			pos--;
		} else {
			pos++;
		}
	}

	return components;
}

Project: lxc
File: utils.c

char *must_append_path(char *first, ...)
{
	char *cur;
	size_t full_len;
	va_list args;
	char *dest = first;
	size_t buf_len;

	full_len = strlen(first);
	va_start(args, first);
	while ((cur = va_arg(args, char *)) != NULL) {
		full_len += strlen(cur);
		if (cur[0] != '/')
			full_len++;

		buf_len = full_len + 1;
		dest = must_realloc(dest, buf_len);

		if (cur[0] != '/')
			(void)strlcat(dest, "/", buf_len);
		(void)strlcat(dest, cur, buf_len);
	}
	va_end(args);

	return dest;
}

Project: lxc
File: utils.c

size_t lxc_array_len(void **array)
{
	void **p;
	size_t result = 0;

	for (p = array; p && *p; p++)
		result++;

	return result;
}

Project: lxc
File: lxc_user_nic.c

static char *get_username(void)
{
	struct passwd pwent;
	struct passwd *pwentp = NULL;
	char *buf;
	char *username;
	size_t bufsize;
	int ret;

	bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
	if (bufsize == -1)
		bufsize = 1024;

	buf = malloc(bufsize);
	if (!buf)
		return NULL;

	ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp);
	if (!pwentp) {
		if (ret == 0)
			usernic_error("%s", "Could not find matched password record\n");

		usernic_error("Failed to get username: %s(%u)\n", strerror(errno), getuid());
		free(buf);
		return NULL;
	}

	username = strdup(pwent.pw_name);
	free(buf);

	return username;
}