lxc-4.0.2/0000755061062106075000000000000013646120504007315 500000000000000lxc-4.0.2/lxc.pc.in0000644061062106075000000000056313646120451010761 00000000000000bindir=@BINDIR@ libdir=@LIBDIR@ localstatedir=@LOCALSTATEDIR@ includedir=@INCLUDEDIR@ rootfsmountdir=@LXCROOTFSMOUNT@ Name: lxc Description: linux container tools Version: @PACKAGE_VERSION@ URL: http://linuxcontainers.org Libs: -L${libdir} -llxc -lutil Libs.private: @CAP_LIBS@ @SECCOMP_LIBS@ @OPENSSL_LIBS@ @SELINUX_LIBS@ @PAM_LIBS@ @DLOG_LIBS@ Cflags: -I${includedir} lxc-4.0.2/lxc.spec0000644061062106075000000002304413646120474010710 00000000000000# # lxc: linux Container library # # (C) Copyright IBM Corp. 2007, 2008 # # Authors: # Daniel Lezcano # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Set with_systemd on distros that use it, so we can install the service # file, otherwise the sysvinit script will be installed %if 0%{?fedora} >= 14 || 0%{?rhel} >= 7 || 0%{?suse_version} >= 1210 %global with_systemd 1 %define init_script systemd # # BuildRequires systemd-units on fedora and rhel %if 0%{?fedora} >= 14 || 0%{?rhel} >= 7 BuildRequires: systemd-units %endif # # BuildRequires systemd on openSUSE and SUSE %if 0%{?suse_version} >= 1210 BuildRequires: systemd %endif %else %global with_systemd 0 %define init_script sysvinit %endif # Must use /var/run for runtime_path on older releases or dnsmasq in the # lxc-net script will not be able to write its pid in /run (selinux denial) %if 0%{?fedora} < 15 || 0%{?rhel} < 7 %define _with_runtime_path --with-runtime-path=/var/run %endif # RPM needs alpha/beta/rc in Release: not Version: to ensure smooth # package upgrades from alpha->beta->rc->release. For more info see: # http://fedoraproject.org/wiki/Packaging%3aNamingGuidelines#NonNumericRelease %if "x" != "x" %global beta_rel %global beta_dot .%{beta_rel} %else %global norm_rel 1 %endif Name: lxc Version: 4.0.2 Release: %{?beta_rel:0.1.%{beta_rel}}%{?!beta_rel:%{norm_rel}}%{?dist} URL: http://linuxcontainers.org Source: http://linuxcontainers.org/downloads/%{name}-%{version}%{?beta_dot}.tar.gz Summary: Linux Containers userspace tools Group: Applications/System License: LGPLv2+ BuildRoot: %{_tmppath}/%{name}-%{version}-build Requires: openssl rsync dnsmasq bridge-utils Requires: %{name}-libs = %{version}-%{release} Requires(pre): /usr/sbin/useradd Requires(postun): /usr/sbin/userdel %if 0%{?fedora} < 15 || 0%{?rhel} < 7 Requires: libcgroup %endif # Note for Suse. The "docbook2X" BuildRequires does properly # match docbook2x on Suse in a case insensitive manner BuildRequires: libcap libcap-devel docbook2X graphviz libxslt pkgconfig # # Additional packages for openSUSE and SUSE # %if 0%{?suse_version} >= 1210 PreReq: permissions BuildRequires: libapparmor-devel linux-glibc-devel lsb-release docbook-utils # # libseccomp-devel only needed on i386/i586/i686 and X86_64 # %ifarch %ix86 x86_64 BuildRequires: libseccomp-devel %endif %endif # # Additional package for Tizen # %if %{defined tizen_version} BuildRequires: pkgconfig(dlog) %endif %description Containers are insulated areas inside a system, which have their own namespace for filesystem, network, PID, IPC, CPU and memory allocation and which can be created using the Control Group and Namespace features included in the Linux kernel. This package provides the lxc-* tools, which can be used to start a single daemon in a container, or to boot an entire "containerized" system, and to manage and debug your containers. %package libs Summary: Shared library files for %{name} Group: System Environment/Libraries %description libs The %{name}-libs package contains libraries for running %{name} applications. %package devel Summary: Development library for %{name} Group: Development/Libraries Requires: %{name} = %{version}-%{release}, pkgconfig %description devel The %{name}-devel package contains header files and library needed for development of the Linux containers. %prep %setup -q -n %{name}-%{version}%{?beta_dot} %build #Dont use pkgconfig to get bash completion dir and use backwards compatible location. export bashcompdir=%{_sysconfdir}/bash_completion.d PATH=$PATH:/usr/sbin:/sbin %configure $args \ %if "x%{_unitdir}" != "x" --with-systemdsystemunitdir=%{_unitdir} \ %endif %{?_with_runtime_path} \ --disable-rpath \ --with-init-script=%{init_script} make %{?_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} find %{buildroot} -type f -name '*.la' -exec rm -f {} ';' %clean rm -rf %{buildroot} %pre # Ensure that lxc-dnsmasq uid & gid gets correctly allocated if getent passwd lxc-dnsmasq >/dev/null 2>&1 ; then : ; else \ /usr/sbin/useradd -M -r -s /sbin/nologin \ -c "LXC Networking Service" -d %_localstatedir/%name lxc-dnsmasq 2> /dev/null \ || exit 1 fi %post # This test should trigger a network configure on a new install. if [ ! -d /usr/local/etc/default ] then mkdir -p /usr/local/etc/default fi if [ ! -f /usr/local/etc/default/lxc-net ] || ! grep -q 'USE_LXC_BRIDGE=' /usr/local/etc/default/lxc-net then # Grab a random 10net subnet. Need to add test logic... while [ true ] do SUBNET=10.$(($RANDOM % 256)).$(($RANDOM % 256)) if ! ip -4 route ls | grep -q "^$SUBNET" then break fi done cat > /usr/local/etc/default/lxc-net </config) for any containers # already created using the default config to reflect the new bridge # name. # If you have the dnsmasq daemon installed, you'll also have to update # /etc/dnsmasq.d/lxc and restart the system wide dnsmasq daemon. LXC_BRIDGE="lxcbr0" LXC_BRIDGE_MAC="00:16:3e:00:00:00" LXC_ADDR="$SUBNET.1" LXC_NETMASK="255.255.255.0" LXC_NETWORK="$SUBNET.0/24" LXC_DHCP_RANGE="$SUBNET.2,$SUBNET.254" LXC_DHCP_MAX="253" # Uncomment the next line if you'd like to use a conf-file for the lxcbr0 # dnsmasq. For instance, you can use 'dhcp-host=mail1,10.0.3.100' to have # container 'mail1' always get ip address 10.0.3.100. #LXC_DHCP_CONFILE=/etc/lxc/dnsmasq.conf # Uncomment the next line if you want lxcbr0's dnsmasq to resolve the .lxc # domain. You can then add "server=/lxc/10.0.3.1' (or your actual $LXC_ADDR) # to /etc/dnsmasq.conf, after which 'container1.lxc' will resolve on your # host. #LXC_DOMAIN="lxc" EOF fi %postun /usr/sbin/userdel lxc-dnsmasq > /dev/null 2>&1 || : %post libs -p /sbin/ldconfig %postun libs -p /sbin/ldconfig %files %defattr(-,root,root) %{_bindir}/* # openSUSE/SUSE %if 0%{?suse_version} >= 1210 %dir %{_sysconfdir}/apparmor.d %dir %{_sysconfdir}/apparmor.d/abstractions %dir %{_sysconfdir}/apparmor.d/abstractions/%{name} %config %{_sysconfdir}/apparmor.d/abstractions/%{name}/container-base %config %{_sysconfdir}/apparmor.d/abstractions/%{name}/start-container %config %{_sysconfdir}/apparmor.d/%{name}-containers %dir %{_sysconfdir}/apparmor.d/%{name} %config %{_sysconfdir}/apparmor.d/%{name}/%{name}-default %config %{_sysconfdir}/apparmor.d/%{name}/%{name}-default-with-mounting %config %{_sysconfdir}/apparmor.d/%{name}/%{name}-default-with-nesting %config %{_sysconfdir}/apparmor.d/usr.bin.%{name}-start %endif %{_mandir}/man1/lxc* %{_mandir}/man5/lxc* %{_mandir}/man7/lxc* # not openSUSE/SUSE %if %{undefined suse_version} %{_mandir}/ja/man1/lxc* %{_mandir}/ja/man5/lxc* %{_mandir}/ja/man7/lxc* %{_mandir}/ko/man1/lxc* %{_mandir}/ko/man5/lxc* %{_mandir}/ko/man7/lxc* %endif %{_datadir}/doc/* %{_datadir}/lxc/* %{_sysconfdir}/bash_completion.d %config(noreplace) %{_sysconfdir}/lxc/* %config(noreplace) %{_sysconfdir}/sysconfig/* %if %{with_systemd} %{_unitdir}/lxc-net.service %{_unitdir}/lxc.service %{_unitdir}/lxc@.service %else %{_sysconfdir}/rc.d/init.d/lxc %{_sysconfdir}/rc.d/init.d/lxc-net %endif %files libs %defattr(-,root,root) %{_sbindir}/* %{_libdir}/*.so.* %{_libdir}/*.a %{_libdir}/%{name} %{_localstatedir}/* %{_libexecdir}/%{name}/hooks/unmount-namespace %{_libexecdir}/%{name}/lxc-apparmor-load %{_libexecdir}/%{name}/lxc-monitord %attr(4111,root,root) %{_libexecdir}/%{name}/lxc-user-nic %if %{with_systemd} %attr(555,root,root) %{_libexecdir}/%{name}/lxc-net %attr(555,root,root) %{_libexecdir}/%{name}/lxc-containers %endif %files devel %defattr(-,root,root) %{_includedir}/%{name}/* %{_libdir}/*.so %{_libdir}/pkgconfig/* %changelog * Tue Oct 22 2013 Dwight Engen - 1.0.0-0.1.alpha2 - fix some rpmlint warnings/errors - split lua bits into separate package * Mon Sep 10 2012 Dwight Engen - 0.8.0 - fix lxc-init moved to libexec - .pc moved to _libdir - package template files /usr/share/lxc/templates * Thu Sep 8 2011 Greg Kurz - 0.7.5.1 - fix installed files for rpmbuild - introduce lxc-libs package * Fri Jul 23 2010 Daniel Lezcano - 0.7.2 - set attribute for installed files - fix libraries installation * Tue Mar 24 2009 Daniel Lezcano - 0.6.1 - Removed capability setting, let the user to do that through "lxc-setcap" * Mon Feb 16 2009 Daniel Lezcano - 0.6.0 - Added more capabilities to the executables * Sun Jan 25 2009 Daniel Lezcano - 0.6.0 - Reduced spec file * Sun Aug 3 2008 Daniel Lezcano - 0.1.0 - Initial RPM release. # Local variables: # mode: shell-script # sh-shell: rpm # end: lxc-4.0.2/src/0000755061062106075000000000000013646120503010103 500000000000000lxc-4.0.2/src/include/0000755061062106075000000000000013646120503011526 500000000000000lxc-4.0.2/src/include/getline.h0000644061062106075000000000306513646120451013254 00000000000000/* * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _GETLINE_H #define _GETLINE_H #include extern ssize_t getline(char **outbuf, size_t *outsize, FILE *fp); #endif lxc-4.0.2/src/include/strlcat.h0000644061062106075000000000167413646120451013305 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This function has been copied from musl. */ #ifndef _STRLCAT_H #define _STRLCAT_H #include extern size_t strlcat(char *src, const char *append, size_t len); #endif lxc-4.0.2/src/include/prlimit.c0000644061062106075000000000510713646120451013277 00000000000000/* * Copyright (C) 2008 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include /* __le64, __l32 ... */ #include #include #include #include #include #include #if defined(__LP64__) #error This code is only needed on 32-bit systems! #endif #define RLIM64_INFINITY (~0ULL) typedef uint64_t u64; // There is no prlimit system call, so we need to use prlimit64. int prlimit(pid_t pid, int resource, const struct rlimit *n32, struct rlimit *o32) { struct rlimit64 n64; if (n32 != NULL) { n64.rlim_cur = (n32->rlim_cur == RLIM_INFINITY) ? RLIM64_INFINITY : n32->rlim_cur; n64.rlim_max = (n32->rlim_max == RLIM_INFINITY) ? RLIM64_INFINITY : n32->rlim_max; } struct rlimit64 o64; int result = prlimit64( pid, resource, (n32 != NULL) ? (const struct rlimit64 *)&n64 : NULL, (o32 != NULL) ? &o64 : NULL); if (result != -1 && o32 != NULL) { o32->rlim_cur = (o64.rlim_cur == RLIM64_INFINITY) ? RLIM_INFINITY : o64.rlim_cur; o32->rlim_max = (o64.rlim_max == RLIM64_INFINITY) ? RLIM_INFINITY : o64.rlim_max; } return result; } lxc-4.0.2/src/include/openpty.h0000644061062106075000000000246313646120451013324 00000000000000/* * openpty: glibc implementation * * Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. * * Authors: * Zack Weinberg , 1998. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENPTY_H #define _OPENPTY_H #include #include /* Create pseudo tty master slave pair with NAME and set terminal attributes according to TERMP and WINP and return handles for both ends in AMASTER and ASLAVE. */ extern int openpty (int *__amaster, int *__aslave, char *__name, const struct termios *__termp, const struct winsize *__winp); #endif lxc-4.0.2/src/include/strlcpy.c0000644061062106075000000000204013646120451013310 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This function has been copied from musl. */ #include size_t strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); if (size) { size_t len = (ret >= size) ? size - 1 : ret; memcpy(dest, src, len); dest[len] = '\0'; } return ret; } lxc-4.0.2/src/include/netns_ifaddrs.c0000644061062106075000000003101713646120451014441 00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "nl.h" #include "macro.h" #include "netns_ifaddrs.h" #ifndef NETNS_RTA #define NETNS_RTA(r) \ ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg)))) #endif #define IFADDRS_HASH_SIZE 64 #define __NETLINK_ALIGN(len) (((len) + 3) & ~3) #define __NLMSG_OK(nlh, end) \ ((char *)(end) - (char *)(nlh) >= sizeof(struct nlmsghdr)) #define __NLMSG_NEXT(nlh) \ (struct nlmsghdr *)((char *)(nlh) + __NETLINK_ALIGN((nlh)->nlmsg_len)) #define __NLMSG_DATA(nlh) ((void *)((char *)(nlh) + sizeof(struct nlmsghdr))) #define __NLMSG_DATAEND(nlh) ((char *)(nlh) + (nlh)->nlmsg_len) #define __NLMSG_RTA(nlh, len) \ ((void *)((char *)(nlh) + sizeof(struct nlmsghdr) + \ __NETLINK_ALIGN(len))) #define __RTA_DATALEN(rta) ((rta)->rta_len - sizeof(struct rtattr)) #define __RTA_NEXT(rta) \ (struct rtattr *)((char *)(rta) + __NETLINK_ALIGN((rta)->rta_len)) #define __RTA_OK(nlh, end) \ ((char *)(end) - (char *)(rta) >= sizeof(struct rtattr)) #define __NLMSG_RTAOK(rta, nlh) __RTA_OK(rta, __NLMSG_DATAEND(nlh)) #define __IN6_IS_ADDR_LINKLOCAL(a) \ ((((uint8_t *)(a))[0]) == 0xfe && (((uint8_t *)(a))[1] & 0xc0) == 0x80) #define __IN6_IS_ADDR_MC_LINKLOCAL(a) \ (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *)(a))[1] & 0xf) == 0x2)) #define __RTA_DATA(rta) ((void *)((char *)(rta) + sizeof(struct rtattr))) /* getifaddrs() reports hardware addresses with PF_PACKET that implies struct * sockaddr_ll. But e.g. Infiniband socket address length is longer than * sockaddr_ll.ssl_addr[8] can hold. Use this hack struct to extend ssl_addr - * callers should be able to still use it. */ struct sockaddr_ll_hack { unsigned short sll_family, sll_protocol; int sll_ifindex; unsigned short sll_hatype; unsigned char sll_pkttype, sll_halen; unsigned char sll_addr[24]; }; union sockany { struct sockaddr sa; struct sockaddr_ll_hack ll; struct sockaddr_in v4; struct sockaddr_in6 v6; }; struct ifaddrs_storage { struct netns_ifaddrs ifa; struct ifaddrs_storage *hash_next; union sockany addr, netmask, ifu; unsigned int index; char name[IFNAMSIZ + 1]; }; struct ifaddrs_ctx { struct ifaddrs_storage *first; struct ifaddrs_storage *last; struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE]; }; static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex) { uint8_t *dst; size_t len; switch (af) { case AF_INET: dst = (uint8_t *)&sa->v4.sin_addr; len = 4; break; case AF_INET6: dst = (uint8_t *)&sa->v6.sin6_addr; len = 16; if (__IN6_IS_ADDR_LINKLOCAL(addr) || __IN6_IS_ADDR_MC_LINKLOCAL(addr)) sa->v6.sin6_scope_id = ifindex; break; default: return; } if (addrlen < len) return; sa->sa.sa_family = af; memcpy(dst, addr, len); *r = &sa->sa; } static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen) { uint8_t addr[16] = {0}; int i; if ((size_t)prefixlen > 8 * sizeof(addr)) prefixlen = 8 * sizeof(addr); i = prefixlen / 8; memset(addr, 0xff, i); if ((size_t)i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8)); copy_addr(r, af, sa, addr, sizeof(addr), 0); } static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype) { if (addrlen > sizeof(sa->ll.sll_addr)) return; sa->ll.sll_family = AF_PACKET; sa->ll.sll_ifindex = ifindex; sa->ll.sll_hatype = hatype; sa->ll.sll_halen = addrlen; memcpy(sa->ll.sll_addr, addr, addrlen); *r = &sa->sa; } static int nl_msg_to_ifaddr(void *pctx, bool *netnsid_aware, struct nlmsghdr *h) { struct ifaddrs_storage *ifs, *ifs0; struct rtattr *rta; int stats_len = 0; struct ifinfomsg *ifi = __NLMSG_DATA(h); struct ifaddrmsg *ifa = __NLMSG_DATA(h); struct ifaddrs_ctx *ctx = pctx; if (h->nlmsg_type == RTM_NEWLINK) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h); rta = __RTA_NEXT(rta)) { #if HAVE_STRUCT_RTNL_LINK_STATS64 if (rta->rta_type != IFLA_STATS64) #else if (rta->rta_type != IFLA_STATS) #endif continue; stats_len = __RTA_DATALEN(rta); break; } #pragma GCC diagnostic pop } else { for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next) if (ifs0->index == ifa->ifa_index) break; if (!ifs0) return 0; } ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len); if (!ifs) { errno = ENOMEM; return -1; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" if (h->nlmsg_type == RTM_NEWLINK) { ifs->index = ifi->ifi_index; ifs->ifa.ifa_ifindex = ifi->ifi_index; ifs->ifa.ifa_flags = ifi->ifi_flags; for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h); rta = __RTA_NEXT(rta)) { switch (rta->rta_type) { case IFLA_IFNAME: if (__RTA_DATALEN(rta) < sizeof(ifs->name)) { memcpy(ifs->name, __RTA_DATA(rta), __RTA_DATALEN(rta)); ifs->ifa.ifa_name = ifs->name; } break; case IFLA_ADDRESS: copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, __RTA_DATA(rta), __RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type); break; case IFLA_BROADCAST: copy_lladdr(&ifs->ifa.__ifa_broadaddr, &ifs->ifu, __RTA_DATA(rta), __RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type); break; #if HAVE_STRUCT_RTNL_LINK_STATS64 case IFLA_STATS64: ifs->ifa.ifa_stats_type = IFLA_STATS64; #else case IFLA_STATS: ifs->ifa.ifa_stats_type = IFLA_STATS; #endif memcpy(&ifs->ifa.ifa_stats, __RTA_DATA(rta), __RTA_DATALEN(rta)); break; case IFLA_MTU: memcpy(&ifs->ifa.ifa_mtu, __RTA_DATA(rta), sizeof(int)); break; case IFLA_TARGET_NETNSID: *netnsid_aware = true; break; } } if (ifs->ifa.ifa_name) { unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE; ifs->hash_next = ctx->hash[bucket]; ctx->hash[bucket] = ifs; } } else { ifs->ifa.ifa_name = ifs0->ifa.ifa_name; ifs->ifa.ifa_mtu = ifs0->ifa.ifa_mtu; ifs->ifa.ifa_ifindex = ifs0->ifa.ifa_ifindex; ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags; for (rta = __NLMSG_RTA(h, sizeof(*ifa)); __NLMSG_RTAOK(rta, h); rta = __RTA_NEXT(rta)) { switch (rta->rta_type) { case IFA_ADDRESS: /* If ifa_addr is already set we, received an * IFA_LOCAL before so treat this as * destination address. */ if (ifs->ifa.ifa_addr) copy_addr(&ifs->ifa.__ifa_dstaddr, ifa->ifa_family, &ifs->ifu, __RTA_DATA(rta), __RTA_DATALEN(rta), ifa->ifa_index); else copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, __RTA_DATA(rta), __RTA_DATALEN(rta), ifa->ifa_index); break; case IFA_BROADCAST: copy_addr(&ifs->ifa.__ifa_broadaddr, ifa->ifa_family, &ifs->ifu, __RTA_DATA(rta), __RTA_DATALEN(rta), ifa->ifa_index); break; case IFA_LOCAL: /* If ifa_addr is set and we get IFA_LOCAL, * assume we have a point-to-point network. * Move address to correct field. */ if (ifs->ifa.ifa_addr) { ifs->ifu = ifs->addr; ifs->ifa.__ifa_dstaddr = &ifs->ifu.sa; memset(&ifs->addr, 0, sizeof(ifs->addr)); } copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, __RTA_DATA(rta), __RTA_DATALEN(rta), ifa->ifa_index); break; case IFA_LABEL: if (__RTA_DATALEN(rta) < sizeof(ifs->name)) { memcpy(ifs->name, __RTA_DATA(rta), __RTA_DATALEN(rta)); ifs->ifa.ifa_name = ifs->name; } break; case IFA_TARGET_NETNSID: *netnsid_aware = true; break; } } if (ifs->ifa.ifa_addr) { gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen); ifs->ifa.ifa_prefixlen = ifa->ifa_prefixlen; } } #pragma GCC diagnostic pop if (ifs->ifa.ifa_name) { if (!ctx->first) ctx->first = ifs; if (ctx->last) ctx->last->ifa.ifa_next = &ifs->ifa; ctx->last = ifs; } else { free(ifs); } return 0; } static int __ifaddrs_netlink_send(int fd, struct nlmsghdr *nlmsghdr) { int ret; struct sockaddr_nl nladdr; struct iovec iov = { .iov_base = nlmsghdr, .iov_len = nlmsghdr->nlmsg_len, }; struct msghdr msg = { .msg_name = &nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1, }; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; ret = sendmsg(fd, &msg, MSG_NOSIGNAL); if (ret < 0) return -1; return ret; } static int __ifaddrs_netlink_recv(int fd, unsigned int seq, int type, int af, __s32 netns_id, bool *netnsid_aware, int (*cb)(void *ctx, bool *netnsid_aware, struct nlmsghdr *h), void *ctx) { int r, property, ret; char *buf; struct nlmsghdr *hdr; struct ifinfomsg *ifi_msg; struct ifaddrmsg *ifa_msg; union { uint8_t buf[8192]; struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; struct nlmsghdr reply; } u; char getlink_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) + __NETLINK_ALIGN(sizeof(struct ifinfomsg)) + __NETLINK_ALIGN(1024)] = {0}; char getaddr_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) + __NETLINK_ALIGN(sizeof(struct ifaddrmsg)) + __NETLINK_ALIGN(1024)] = {0}; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" if (type == RTM_GETLINK) { buf = getlink_buf; hdr = (struct nlmsghdr *)buf; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi_msg)); ifi_msg = (struct ifinfomsg *)__NLMSG_DATA(hdr); ifi_msg->ifi_family = af; property = IFLA_TARGET_NETNSID; } else if (type == RTM_GETADDR) { buf = getaddr_buf; hdr = (struct nlmsghdr *)buf; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa_msg)); ifa_msg = (struct ifaddrmsg *)__NLMSG_DATA(hdr); ifa_msg->ifa_family = af; property = IFA_TARGET_NETNSID; } else { errno = EINVAL; return -1; } #pragma GCC diagnostic pop hdr->nlmsg_type = type; hdr->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; hdr->nlmsg_pid = 0; hdr->nlmsg_seq = seq; if (netns_id >= 0) addattr(hdr, 1024, property, &netns_id, sizeof(netns_id)); r = __ifaddrs_netlink_send(fd, hdr); if (r < 0) return -1; for (;;) { r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT); if (r <= 0) return -1; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" for (hdr = &u.reply; __NLMSG_OK(hdr, (void *)&u.buf[r]); hdr = __NLMSG_NEXT(hdr)) { if (hdr->nlmsg_type == NLMSG_DONE) return 0; if (hdr->nlmsg_type == NLMSG_ERROR) { errno = EINVAL; return -1; } ret = cb(ctx, netnsid_aware, hdr); if (ret) return ret; } #pragma GCC diagnostic pop } } static int __rtnl_enumerate(int link_af, int addr_af, __s32 netns_id, bool *netnsid_aware, int (*cb)(void *ctx, bool *netnsid_aware, struct nlmsghdr *h), void *ctx) { int fd, r, saved_errno; bool getaddr_netnsid_aware = false, getlink_netnsid_aware = false; fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); if (fd < 0) return -1; r = setsockopt(fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &(int){1}, sizeof(int)); if (r < 0 && netns_id >= 0) { close(fd); *netnsid_aware = false; return -1; } r = __ifaddrs_netlink_recv(fd, 1, RTM_GETLINK, link_af, netns_id, &getlink_netnsid_aware, cb, ctx); if (!r) r = __ifaddrs_netlink_recv(fd, 2, RTM_GETADDR, addr_af, netns_id, &getaddr_netnsid_aware, cb, ctx); saved_errno = errno; close(fd); errno = saved_errno; if (getaddr_netnsid_aware && getlink_netnsid_aware) *netnsid_aware = true; else *netnsid_aware = false; return r; } void netns_freeifaddrs(struct netns_ifaddrs *ifp) { struct netns_ifaddrs *n; while (ifp) { n = ifp->ifa_next; free(ifp); ifp = n; } } int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id, bool *netnsid_aware) { int r, saved_errno; struct ifaddrs_ctx _ctx; struct ifaddrs_ctx *ctx = &_ctx; memset(ctx, 0, sizeof *ctx); r = __rtnl_enumerate(AF_UNSPEC, AF_UNSPEC, netns_id, netnsid_aware, nl_msg_to_ifaddr, ctx); saved_errno = errno; if (r < 0) netns_freeifaddrs(&ctx->first->ifa); else *ifap = &ctx->first->ifa; errno = saved_errno; return r; } lxc-4.0.2/src/include/getgrgid_r.c0000644061062106075000000002300513646120451013731 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This function has been copied from musl. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #define LOGIN_NAME_MAX 256 #define NSCDVERSION 2 #define GETPWBYNAME 0 #define GETPWBYUID 1 #define GETGRBYNAME 2 #define GETGRBYGID 3 #define GETINITGR 15 #define REQVERSION 0 #define REQTYPE 1 #define REQKEYLEN 2 #define REQ_LEN 3 #define PWVERSION 0 #define PWFOUND 1 #define PWNAMELEN 2 #define PWPASSWDLEN 3 #define PWUID 4 #define PWGID 5 #define PWGECOSLEN 6 #define PWDIRLEN 7 #define PWSHELLLEN 8 #define PW_LEN 9 #define GRVERSION 0 #define GRFOUND 1 #define GRNAMELEN 2 #define GRPASSWDLEN 3 #define GRGID 4 #define GRMEMCNT 5 #define GR_LEN 6 #define INITGRVERSION 0 #define INITGRFOUND 1 #define INITGRNGRPS 2 #define INITGR_LEN 3 #define FIX(x) (gr->gr_##x = gr->gr_##x - line + buf) static unsigned atou(char **s) { unsigned x; for (x = 0; **s - '0' < 10U; ++*s) x = 10 * x + (**s - '0'); return x; } static int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem, struct group **res) { ssize_t l; char *s, *mems; size_t i; int rv = 0; #ifdef HAVE_PTHREAD_SETCANCELSTATE int cs; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); #endif for (;;) { if ((l = getline(line, size, f)) < 0) { rv = ferror(f) ? errno : 0; free(*line); *line = 0; gr = 0; goto end; } line[0][l - 1] = 0; s = line[0]; gr->gr_name = s++; if (!(s = strchr(s, ':'))) continue; *s++ = 0; gr->gr_passwd = s; if (!(s = strchr(s, ':'))) continue; *s++ = 0; gr->gr_gid = atou(&s); if (*s != ':') continue; *s++ = 0; mems = s; break; } for (*nmem = !!*s; *s; s++) if (*s == ',') ++*nmem; free(*mem); *mem = calloc(sizeof(char *), *nmem + 1); if (!*mem) { rv = errno; free(*line); *line = 0; gr = 0; goto end; } if (*mems) { mem[0][0] = mems; for (s = mems, i = 0; *s; s++) if (*s == ',') *s++ = 0, mem[0][++i] = s; mem[0][++i] = 0; } else { mem[0][0] = 0; } gr->gr_mem = *mem; end: #ifdef HAVE_PTHREAD_SETCANCELSTATE pthread_setcancelstate(cs, 0); #endif *res = gr; if (rv) errno = rv; return rv; } static char *itoa(char *p, uint32_t x) { // number of digits in a uint32_t + NUL p += 11; *--p = 0; do { *--p = '0' + x % 10; x /= 10; } while (x); return p; } static const struct { short sun_family; char sun_path[21]; } addr = {AF_UNIX, "/var/run/nscd/socket"}; static FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap) { size_t i; int fd; FILE *f = 0; int32_t req_buf[REQ_LEN] = {NSCDVERSION, req, strnlen(key, LOGIN_NAME_MAX) + 1}; struct msghdr msg = {.msg_iov = (struct iovec[]){{&req_buf, sizeof(req_buf)}, {(char *)key, strlen(key) + 1}}, .msg_iovlen = 2}; int errno_save = errno; *swap = 0; retry: memset(buf, 0, len); buf[0] = NSCDVERSION; fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) return NULL; if (!(f = fdopen(fd, "r"))) { close(fd); return 0; } if (req_buf[2] > LOGIN_NAME_MAX) return f; if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { /* If there isn't a running nscd we simulate a "not found" * result and the caller is responsible for calling * fclose on the (unconnected) socket. The value of * errno must be left unchanged in this case. */ if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) { errno = errno_save; return f; } goto error; } if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0) goto error; if (!fread(buf, len, 1, f)) { /* If the VERSION entry mismatches nscd will disconnect. The * most likely cause is that the endianness mismatched. So, we * byteswap and try once more. (if we already swapped, just * fail out) */ if (ferror(f)) goto error; if (!*swap) { fclose(f); for (i = 0; i < sizeof(req_buf) / sizeof(req_buf[0]); i++) { req_buf[i] = bswap_32(req_buf[i]); } *swap = 1; goto retry; } else { errno = EIO; goto error; } } if (*swap) { for (i = 0; i < len / sizeof(buf[0]); i++) { buf[i] = bswap_32(buf[i]); } } /* The first entry in every nscd response is the version number. This * really shouldn't happen, and is evidence of some form of malformed * response. */ if (buf[0] != NSCDVERSION) { errno = EIO; goto error; } return f; error: fclose(f); return 0; } static int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t *size, char ***mem, size_t *nmem, struct group **res) { FILE *f; int rv = 0; #ifdef HAVE_PTHREAD_SETCANCELSTATE int cs; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); #endif *res = 0; f = fopen("/etc/group", "rbe"); if (!f) { rv = errno; goto done; } while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) { if ((name && !strcmp(name, (*res)->gr_name)) || (!name && (*res)->gr_gid == gid)) { break; } } fclose(f); if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) { int32_t req = name ? GETGRBYNAME : GETGRBYGID; int32_t i; const char *key; int32_t groupbuf[GR_LEN] = {0}; size_t len = 0; size_t grlist_len = 0; char gidbuf[11] = {0}; int swap = 0; char *ptr; if (name) { key = name; } else { if (gid < 0 || gid > UINT32_MAX) { rv = 0; goto done; } key = itoa(gidbuf, gid); } f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap); if (!f) { rv = errno; goto done; } if (!groupbuf[GRFOUND]) { rv = 0; goto cleanup_f; } if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) { rv = EIO; goto cleanup_f; } if ((int64_t)groupbuf[GRNAMELEN] > (int64_t)(SIZE_MAX - groupbuf[GRPASSWDLEN])) { rv = ENOMEM; goto cleanup_f; } len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN]; for (i = 0; i < groupbuf[GRMEMCNT]; i++) { uint32_t name_len; if (fread(&name_len, sizeof name_len, 1, f) < 1) { rv = ferror(f) ? errno : EIO; goto cleanup_f; } if (swap) { name_len = bswap_32(name_len); } if (name_len > SIZE_MAX - grlist_len || name_len > SIZE_MAX - len) { rv = ENOMEM; goto cleanup_f; } len += name_len; grlist_len += name_len; } if (len > *size || !*buf) { char *tmp = realloc(*buf, len); if (!tmp) { rv = errno; goto cleanup_f; } *buf = tmp; *size = len; } if (!fread(*buf, len, 1, f)) { rv = ferror(f) ? errno : EIO; goto cleanup_f; } if (((size_t)(groupbuf[GRMEMCNT] + 1)) > *nmem) { if (((size_t)(groupbuf[GRMEMCNT] + 1)) > (SIZE_MAX / sizeof(char *))) { rv = ENOMEM; goto cleanup_f; } char **tmp = realloc(*mem, (groupbuf[GRMEMCNT] + 1) * sizeof(char *)); if (!tmp) { rv = errno; goto cleanup_f; } *mem = tmp; *nmem = groupbuf[GRMEMCNT] + 1; } if (groupbuf[GRMEMCNT]) { mem[0][0] = *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN]; for (ptr = mem[0][0], i = 0; ptr != mem[0][0] + grlist_len; ptr++) if (!*ptr) mem[0][++i] = ptr + 1; mem[0][i] = 0; if (i != groupbuf[GRMEMCNT]) { rv = EIO; goto cleanup_f; } } else { mem[0][0] = 0; } gr->gr_name = *buf; gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN]; gr->gr_gid = groupbuf[GRGID]; gr->gr_mem = *mem; if (gr->gr_passwd[-1] || gr->gr_passwd[groupbuf[GRPASSWDLEN] - 1]) { rv = EIO; goto cleanup_f; } if ((name && strcmp(name, gr->gr_name)) || (!name && gid != gr->gr_gid)) { rv = EIO; goto cleanup_f; } *res = gr; cleanup_f: fclose(f); goto done; } done: #ifdef HAVE_PTHREAD_SETCANCELSTATE pthread_setcancelstate(cs, 0); #endif if (rv) errno = rv; return rv; } static int getgr_r(const char *name, gid_t gid, struct group *gr, char *buf, size_t size, struct group **res) { char *line = 0; size_t len = 0; char **mem = 0; size_t nmem = 0; int rv = 0; size_t i; #ifdef HAVE_PTHREAD_SETCANCELSTATE int cs; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); #endif rv = __getgr_a(name, gid, gr, &line, &len, &mem, &nmem, res); if (*res && size < len + (nmem + 1) * sizeof(char *) + 32) { *res = 0; rv = ERANGE; } if (*res) { buf += (16 - (uintptr_t)buf) % 16; gr->gr_mem = (void *)buf; buf += (nmem + 1) * sizeof(char *); memcpy(buf, line, len); FIX(name); FIX(passwd); for (i = 0; mem[i]; i++) gr->gr_mem[i] = mem[i] - line + buf; gr->gr_mem[i] = 0; } free(mem); free(line); #ifdef HAVE_PTHREAD_SETCANCELSTATE pthread_setcancelstate(cs, 0); #endif if (rv) errno = rv; return rv; } int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size, struct group **res) { return getgr_r(0, gid, gr, buf, size, res); } lxc-4.0.2/src/include/lxcmntent.c0000644061062106075000000001211113646120451013624 00000000000000/* Utilities for reading/writing fstab, mtab, etc. * Copyright (C) 1995-2000, 2001, 2002, 2003, 2006 * Free Software Foundation, Inc. * This file is part of the GNU C Library. * * The GNU C Library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * The GNU C Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include "../lxc/macro.h" /* Since the values in a line are separated by spaces, a name cannot * contain a space. Therefore some programs encode spaces in names * by the strings "\040". We undo the encoding when reading an entry. * The decoding happens in place. */ static char *decode_name(char *buf) { char *rp = buf; char *wp = buf; do { if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '4' && rp[3] == '0') { /* \040 is a SPACE. */ *wp++ = ' '; rp += 3; } else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '1') { /* \011 is a TAB. */ *wp++ = '\t'; rp += 3; } else if (rp[0] == '\\' && rp[1] == '0' && rp[2] == '1' && rp[3] == '2') { /* \012 is a NEWLINE. */ *wp++ = '\n'; rp += 3; } else if (rp[0] == '\\' && rp[1] == '\\') { /* We have to escape \\ to be able to represent all characters. */ *wp++ = '\\'; rp += 1; } else if (rp[0] == '\\' && rp[1] == '1' && rp[2] == '3' && rp[3] == '4') { /* \134 is also \\. */ *wp++ = '\\'; rp += 3; } else { *wp++ = *rp; } } while (*rp++ != '\0'); return buf; } /* Read one mount table entry from STREAM. Returns a pointer to storage * reused on the next call, or null for EOF or error (use feof/ferror to check). */ struct mntent *getmntent_r(FILE *stream, struct mntent *mp, char *buffer, int bufsiz) { char *cp; char *head; do { char *end_ptr; if (!fgets(buffer, bufsiz, stream)) return NULL; end_ptr = strchr(buffer, '\n'); if (end_ptr != NULL) { /* chop newline */ *end_ptr = '\0'; } else { /* Not the whole line was read. Do it now but forget it. */ char tmp[1024] = {0}; while (fgets(tmp, sizeof tmp, stream)) if (strchr(tmp, '\n') != NULL) break; } head = buffer + strspn(buffer, " \t"); /* skip empty lines and comment lines: */ } while (head[0] == '\0' || head[0] == '#'); cp = strsep(&head, " \t"); mp->mnt_fsname = cp ? decode_name(cp) : (char *)""; if (head) head += strspn(head, " \t"); cp = strsep(&head, " \t"); mp->mnt_dir = cp ? decode_name(cp) : (char *)""; if (head) head += strspn(head, " \t"); cp = strsep(&head, " \t"); mp->mnt_type = cp ? decode_name(cp) : (char *)""; if (head) head += strspn(head, " \t"); cp = strsep(&head, " \t"); mp->mnt_opts = cp ? decode_name(cp) : (char *)""; if (head) { int ret = sscanf(head, " %d %d ", &mp->mnt_freq, &mp->mnt_passno); switch (ret) { case 0: mp->mnt_freq = 0; case 1: mp->mnt_passno = 0; case 2: break; } } else { mp->mnt_freq = 0; } return mp; } struct mntent *getmntent(FILE *stream) { static struct mntent m; static char *getmntent_buffer; if (!getmntent_buffer) { getmntent_buffer = (char *)malloc(LXC_MAX_BUFFER); if (!getmntent_buffer) return NULL; } return getmntent_r(stream, &m, getmntent_buffer, LXC_MAX_BUFFER); } /* Prepare to begin reading and/or writing mount table entries from the * beginning of FILE. MODE is as for `fopen'. */ FILE *setmntent(const char *file, const char *mode) { /* Extend the mode parameter with "c" to disable cancellation in the * I/O functions and "e" to set FD_CLOEXEC. */ size_t modelen = strlen(mode); char newmode[256]; if (modelen >= (sizeof(newmode) - 3)) { errno = -EFBIG; return NULL; } memcpy(newmode, mode, modelen); memcpy(newmode + modelen, "ce", 3); return fopen(file, newmode); } /* Close a stream opened with `setmntent'. */ int endmntent(FILE *stream) { /* SunOS 4.x allows for NULL stream */ if (stream) fclose(stream); /* SunOS 4.x says to always return 1 */ return 1; } /* Search MNT->mnt_opts for an option matching OPT. * Returns the address of the substring, or null if none found. */ char *hasmntopt(const struct mntent *mnt, const char *opt) { const size_t optlen = strlen(opt); char *rest = mnt->mnt_opts, *p; while ((p = strstr(rest, opt))) { if ((p == rest || p[-1] == ',') && (p[optlen] == '\0' || p[optlen] == '=' || p[optlen] == ',')) return p; rest = strchr(p, ','); if (!rest) break; ++rest; } return NULL; } lxc-4.0.2/src/include/fexecve.h0000644061062106075000000000200713646120451013245 00000000000000/* liblxcapi * * Copyright © 2019 Christian Brauner . * Copyright © 2019 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _LXC_FEXECVE_H #define _LXC_FEXECVE_H #include extern int fexecve(int fd, char *const argv[], char *const envp[]); #endif /* _LXC_FEXECVE_H */ lxc-4.0.2/src/include/prlimit.h0000644061062106075000000000325113646120451013302 00000000000000/* * Copyright (C) 2008 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _PRLIMIT_H #define _PRLIMIT_H #include #include #define RLIM_SAVED_CUR RLIM_INFINITY #define RLIM_SAVED_MAX RLIM_INFINITY int prlimit(pid_t, int, const struct rlimit*, struct rlimit*); int prlimit64(pid_t, int, const struct rlimit64*, struct rlimit64*); #endif lxc-4.0.2/src/include/openpty.c0000644061062106075000000000366413646120451013323 00000000000000 /* * openpty: glibc implementation * * Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. * * Authors: * Zack Weinberg , 1998. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _XOPEN_SOURCE /* See feature_test_macros(7) */ #include #include #include #include #include #include #include #include #include #define _PATH_DEVPTMX "/dev/ptmx" int openpty (int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp) { char buf[PATH_MAX]; int master, slave; master = open(_PATH_DEVPTMX, O_RDWR); if (master == -1) return -1; if (grantpt(master)) goto fail; if (unlockpt(master)) goto fail; if (ptsname_r(master, buf, sizeof buf)) goto fail; slave = open(buf, O_RDWR | O_NOCTTY); if (slave == -1) goto fail; /* XXX Should we ignore errors here? */ if (termp) tcsetattr(slave, TCSAFLUSH, termp); if (winp) ioctl(slave, TIOCSWINSZ, winp); *amaster = master; *aslave = slave; if (name != NULL) strcpy(name, buf); return 0; fail: close(master); return -1; } lxc-4.0.2/src/include/getline.c0000644061062106075000000000415213646120451013245 00000000000000/* * Copyright (c) 2006 SPARTA, Inc. * All rights reserved. * * This software was developed by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include /* * Emulate glibc getline() via BSD fgetln(). * Note that outsize is not changed unless memory is allocated. */ ssize_t getline(char **outbuf, size_t *outsize, FILE *fp) { size_t len; char *buf; buf = fgetln(fp, &len); if (buf == NULL) return (-1); /* Assumes realloc() accepts NULL for ptr (C99) */ if (*outbuf == NULL || *outsize < len + 1) { void *tmp = realloc(*outbuf, len + 1); if (tmp == NULL) return (-1); *outbuf = tmp; *outsize = len + 1; } memcpy(*outbuf, buf, len); (*outbuf)[len] = '\0'; return (len); } lxc-4.0.2/src/include/strlcat.c0000644061062106075000000000221613646120451013271 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This function has been copied from musl. */ #include #include #include #ifndef HAVE_STRLCPY #include "strlcpy.h" #endif size_t strlcat(char *src, const char *append, size_t len) { size_t src_len; src_len = strnlen(src, len); if (src_len == len) return src_len + strlen(append); return src_len + strlcpy(src + src_len, append, len - src_len); } lxc-4.0.2/src/include/fexecve.c0000644061062106075000000000302213646120451013236 00000000000000/* liblxcapi * * Copyright © 2019 Christian Brauner . * Copyright © 2019 Canonical Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include "config.h" #include "macro.h" #include "raw_syscalls.h" int fexecve(int fd, char *const argv[], char *const envp[]) { char procfd[LXC_PROC_PID_FD_LEN]; int ret; if (fd < 0 || !argv || !envp) { errno = EINVAL; return -1; } #ifdef __NR_execveat lxc_raw_execveat(fd, "", argv, envp, AT_EMPTY_PATH); if (errno != ENOSYS) return -1; #endif ret = snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", fd); if (ret < 0 || (size_t)ret >= sizeof(procfd)) { errno = ENAMETOOLONG; return -1; } execve(procfd, argv, envp); return -1; } lxc-4.0.2/src/include/netns_ifaddrs.h0000644061062106075000000000235513646120451014451 00000000000000#ifndef _LXC_NETNS_IFADDRS_H #define _LXC_NETNS_IFADDRS_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include "netns_ifaddrs.h" struct netns_ifaddrs { struct netns_ifaddrs *ifa_next; /* Can - but shouldn't be - NULL. */ char *ifa_name; /* This field is not present struct ifaddrs. */ int ifa_ifindex; unsigned ifa_flags; /* This field is not present struct ifaddrs. */ int ifa_mtu; /* This field is not present struct ifaddrs. */ int ifa_prefixlen; struct sockaddr *ifa_addr; struct sockaddr *ifa_netmask; union { struct sockaddr *ifu_broadaddr; struct sockaddr *ifu_dstaddr; } ifa_ifu; /* These fields are not present struct ifaddrs. */ int ifa_stats_type; #if HAVE_STRUCT_RTNL_LINK_STATS64 struct rtnl_link_stats64 ifa_stats; #else struct rtnl_link_stats ifa_stats; #endif }; #define __ifa_broadaddr ifa_ifu.ifu_broadaddr #define __ifa_dstaddr ifa_ifu.ifu_dstaddr extern void netns_freeifaddrs(struct netns_ifaddrs *); extern int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id, bool *netnsid_aware); #ifdef __cplusplus } #endif #endif /* _LXC_NETNS_IFADDRS_H */ lxc-4.0.2/src/include/getgrgid_r.h0000644061062106075000000000205413646120451013737 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This function has been copied from musl. */ #ifndef _GETGRGID_R_H #define _GETGRGID_R_H #include #include #include extern int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size, struct group **res); #endif /* _GETGRGID_R_H */ lxc-4.0.2/src/include/lxcmntent.h0000644061062106075000000000306413646120451013640 00000000000000/* Utilities for reading/writing fstab, mtab, etc. Copyright (C) 1995-2000, 2001, 2002, 2003, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _LXCMNTENT_H #define _LXCMNTENT_H #if IS_BIONIC struct mntent { char* mnt_fsname; char* mnt_dir; char* mnt_type; char* mnt_opts; int mnt_freq; int mnt_passno; }; extern struct mntent *getmntent (FILE *stream); extern struct mntent *getmntent_r (FILE *stream, struct mntent *mp, char *buffer, int bufsiz); #endif #if !defined(HAVE_SETMNTENT) || IS_BIONIC FILE *setmntent (const char *file, const char *mode); #endif #if !defined(HAVE_ENDMNTENT) || IS_BIONIC int endmntent (FILE *stream); #endif #if !defined(HAVE_HASMNTOPT) || IS_BIONIC extern char *hasmntopt (const struct mntent *mnt, const char *opt); #endif #endif lxc-4.0.2/src/include/strlcpy.h0000644061062106075000000000165713646120451013332 00000000000000/* liblxcapi * * Copyright © 2018 Christian Brauner . * Copyright © 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This function has been copied from musl. */ #ifndef _STRLCPY_H #define _STRLCPY_H #include extern size_t strlcpy(char *, const char *, size_t); #endif lxc-4.0.2/src/Makefile.in0000644061062106075000000005072613646120463012107 00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/config/acinclude.m4 \ $(top_srcdir)/config/ax_check_compile_flag.m4 \ $(top_srcdir)/config/ax_check_link_flag.m4 \ $(top_srcdir)/config/ax_pthread.m4 \ $(top_srcdir)/config/libtool.m4 \ $(top_srcdir)/config/ltoptions.m4 \ $(top_srcdir)/config/ltsugar.m4 \ $(top_srcdir)/config/ltversion.m4 \ $(top_srcdir)/config/lt~obsolete.m4 \ $(top_srcdir)/config/tls.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ APPARMOR_CACHE_DIR = @APPARMOR_CACHE_DIR@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BINDIR = @BINDIR@ CAP_LIBS = @CAP_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DATADIR = @DATADIR@ DEFAULT_CGROUP_PATTERN = @DEFAULT_CGROUP_PATTERN@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DLOG_CFLAGS = @DLOG_CFLAGS@ DLOG_LIBS = @DLOG_LIBS@ DOCDIR = @DOCDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ HAVE_DOXYGEN = @HAVE_DOXYGEN@ INCLUDEDIR = @INCLUDEDIR@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBDIR = @LIBDIR@ LIBEXECDIR = @LIBEXECDIR@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIPO = @LIPO@ LN_S = @LN_S@ LOCALSTATEDIR = @LOCALSTATEDIR@ LOGPATH = @LOGPATH@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LXCBINHOOKDIR = @LXCBINHOOKDIR@ LXCHOOKDIR = @LXCHOOKDIR@ LXCINITDIR = @LXCINITDIR@ LXCPATH = @LXCPATH@ LXCROOTFSMOUNT = @LXCROOTFSMOUNT@ LXCTEMPLATECONFIG = @LXCTEMPLATECONFIG@ LXCTEMPLATEDIR = @LXCTEMPLATEDIR@ LXC_ABI = @LXC_ABI@ LXC_ABI_MAJOR = @LXC_ABI_MAJOR@ LXC_ABI_MICRO = @LXC_ABI_MICRO@ LXC_ABI_MINOR = @LXC_ABI_MINOR@ LXC_DEFAULT_CONFIG = @LXC_DEFAULT_CONFIG@ LXC_DEVEL = @LXC_DEVEL@ LXC_DISTRO_SYSCONF = @LXC_DISTRO_SYSCONF@ LXC_GENERATE_DATE = @LXC_GENERATE_DATE@ LXC_GLOBAL_CONF = @LXC_GLOBAL_CONF@ LXC_USERNIC_CONF = @LXC_USERNIC_CONF@ LXC_USERNIC_DB = @LXC_USERNIC_DB@ LXC_VERSION = @LXC_VERSION@ LXC_VERSION_BASE = @LXC_VERSION_BASE@ LXC_VERSION_BETA = @LXC_VERSION_BETA@ LXC_VERSION_MAJOR = @LXC_VERSION_MAJOR@ LXC_VERSION_MICRO = @LXC_VERSION_MICRO@ LXC_VERSION_MINOR = @LXC_VERSION_MINOR@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OPENSSL_LIBS = @OPENSSL_LIBS@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PAM_CFLAGS = @PAM_CFLAGS@ PAM_LIBS = @PAM_LIBS@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PREFIX = @PREFIX@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ RANLIB = @RANLIB@ RUNTIME_PATH = @RUNTIME_PATH@ SBINDIR = @SBINDIR@ SECCOMP_CFLAGS = @SECCOMP_CFLAGS@ SECCOMP_LIBS = @SECCOMP_LIBS@ SED = @SED@ SELINUX_LIBS = @SELINUX_LIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SYSCONFDIR = @SYSCONFDIR@ SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bashcompdir = @bashcompdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ db2xman = @db2xman@ docdir = @docdir@ docdtd = @docdtd@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pamdir = @pamdir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = lxc tests all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status src/config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile config.h installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) all install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool cscopelist-am ctags \ ctags-am distclean distclean-generic distclean-hdr \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: lxc-4.0.2/src/Makefile.am0000644061062106075000000000002413646120451012055 00000000000000SUBDIRS = lxc tests lxc-4.0.2/src/config.h.in0000644061062106075000000002030313646120461012047 00000000000000/* src/config.h.in. Generated from configure.ac by autoheader. */ /* "Prefix for shared files." */ #undef DATADIR /* build for use with Coverity */ #undef ENABLE_COVERITY_BUILD /* Rexec liblxc as memfd */ #undef ENFORCE_MEMFD_REXEC /* enforce thread-safety otherwise fail the build */ #undef ENFORCE_THREAD_SAFETY /* Define to 1 if you have the `confstr' function. */ #undef HAVE_CONFSTR /* Define to 1 if you have the declaration of `PR_CAPBSET_DROP', and to 0 if you don't. */ #undef HAVE_DECL_PR_CAPBSET_DROP /* Define to 1 if you have the declaration of `PR_GET_NO_NEW_PRIVS', and to 0 if you don't. */ #undef HAVE_DECL_PR_GET_NO_NEW_PRIVS /* Define to 1 if you have the declaration of `PR_SET_NO_NEW_PRIVS', and to 0 if you don't. */ #undef HAVE_DECL_PR_SET_NO_NEW_PRIVS /* Define to 1 if you have the declaration of `seccomp_notify_fd', and to 0 if you don't. */ #undef HAVE_DECL_SECCOMP_NOTIFY_FD /* Define to 1 if you have the declaration of `seccomp_syscall_resolve_name_arch', and to 0 if you don't. */ #undef HAVE_DECL_SECCOMP_SYSCALL_RESOLVE_NAME_ARCH /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #undef HAVE_DECL_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the `endmntent' function. */ #undef HAVE_ENDMNTENT /* Define to 1 if you have the `faccessat' function. */ #undef HAVE_FACCESSAT /* Define to 1 if you have the `fgetln' function. */ #undef HAVE_FGETLN /* Define to 1 if you have the `fmemopen' function. */ #undef HAVE_FMEMOPEN /* Define to 1 if you have the `getgrgid_r' function. */ #undef HAVE_GETGRGID_R /* Define to 1 if you have the `getline' function. */ #undef HAVE_GETLINE /* Define to 1 if you have the `getsubopt' function. */ #undef HAVE_GETSUBOPT /* Define to 1 if you have the `gettid' function. */ #undef HAVE_GETTID /* Define to 1 if you have the `hasmntopt' function. */ #undef HAVE_HASMNTOPT /* Have ifaddrs.h */ #undef HAVE_IFADDRS_H /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `keyctl' function. */ #undef HAVE_KEYCTL /* Define to 1 if you have the `cap' library (-lcap). */ #undef HAVE_LIBCAP /* Define to 1 if you have the `dlog' library (-ldlog). */ #undef HAVE_LIBDLOG /* Define to 1 if you have the `pthread' library (-lpthread). */ #undef HAVE_LIBPTHREAD /* Define to 1 if you have the `seccomp' library (-lseccomp). */ #undef HAVE_LIBSECCOMP /* Define to 1 if you have the `util' library (-lutil). */ #undef HAVE_LIBUTIL /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_BPF_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_GENETLINK_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_NETLINK_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_UNISTD_H /* Define to 1 if you have the `memfd_create' function. */ #undef HAVE_MEMFD_CREATE /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Have %m format */ #undef HAVE_M_FORMAT /* Define to 1 if you have the `openpty' function. */ #undef HAVE_OPENPTY /* Define to 1 if you have the `pivot_root' function. */ #undef HAVE_PIVOT_ROOT /* Define to 1 if you have the `prlimit' function. */ #undef HAVE_PRLIMIT /* Define to 1 if you have the `prlimit64' function. */ #undef HAVE_PRLIMIT64 /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Have PTHREAD_PRIO_INHERIT. */ #undef HAVE_PTHREAD_PRIO_INHERIT /* Define to 1 if you have the `pthread_setcancelstate' function. */ #undef HAVE_PTHREAD_SETCANCELSTATE /* Define to 1 if you have the header file. */ #undef HAVE_PTY_H /* Define to 1 if you have the `rand_r' function. */ #undef HAVE_RAND_R /* Define to 1 if the system has the type `scmp_filter_ctx'. */ #undef HAVE_SCMP_FILTER_CTX /* Define to 1 if you have the `sethostname' function. */ #undef HAVE_SETHOSTNAME /* Define to 1 if you have the `setmntent' function. */ #undef HAVE_SETMNTENT /* Define to 1 if you have the `setns' function. */ #undef HAVE_SETNS /* Have static libcap */ #undef HAVE_STATIC_LIBCAP /* Define to 1 if you have the `statvfs' function. */ #undef HAVE_STATVFS /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strerror_r' function. */ #undef HAVE_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcat' function. */ #undef HAVE_STRLCAT /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if the system has the type `struct bpf_cgroup_dev_ctx'. */ #undef HAVE_STRUCT_BPF_CGROUP_DEV_CTX /* Define to 1 if the system has the type `struct rtnl_link_stats64'. */ #undef HAVE_STRUCT_RTNL_LINK_STATS64 /* Define to 1 if the system has the type `struct seccomp_notif_sizes'. */ #undef HAVE_STRUCT_SECCOMP_NOTIF_SIZES /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MEMFD_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PERSONALITY_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_RESOURCE_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SIGNALFD_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIMERFD_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define if the compiler supports __thread */ #undef HAVE_TLS /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `unshare' function. */ #undef HAVE_UNSHARE /* Define to 1 if you have the `utmpxname' function. */ #undef HAVE_UTMPXNAME /* Define to 1 if you have the header file. */ #undef HAVE_UTMPX_H /* bionic libc */ #undef IS_BIONIC /* Have cap_get_file */ #undef LIBCAP_SUPPORTS_FILE_CAPABILITIES /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Define to 1 if `major', `minor', and `makedev' are declared in . */ #undef MAJOR_IN_MKDEV /* Define to 1 if `major', `minor', and `makedev' are declared in . */ #undef MAJOR_IN_SYSMACROS /* Enabling mutex debugging */ #undef MUTEX_DEBUGGING /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if strerror_r returns char *. */ #undef STRERROR_R_CHAR_P /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif /* Version number of package */ #undef VERSION /* Define to 1 if on MINIX. */ #undef _MINIX /* Define to 2 if the system does not provide POSIX.1 features except with this defined. */ #undef _POSIX_1_SOURCE /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE /* Define to the compiler TLS keyword */ #undef thread_local lxc-4.0.2/src/lxc/0000755061062106075000000000000013646120503010671 500000000000000lxc-4.0.2/src/lxc/freezer.c0000644061062106075000000000471013646120451012423 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include "cgroups/cgroup.h" #include "cgroups/cgroup_utils.h" #include "commands.h" #include "config.h" #include "error.h" #include "log.h" #include "lxc.h" #include "monitor.h" #include "state.h" #include "string_utils.h" lxc_log_define(freezer, lxc); static void notify_state_listeners(const char *name, const char *lxcpath, lxc_state_t state) { (void)lxc_cmd_serve_state_clients(name, lxcpath, state); (void)lxc_monitor_send_state(name, state, lxcpath); } static int do_freeze_thaw(bool freeze, struct lxc_conf *conf, const char *name, const char *lxcpath) { call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL; lxc_state_t new_state = freeze ? FROZEN : THAWED; int ret; const char *state; size_t state_len; state = lxc_state2str(new_state); state_len = strlen(state); cgroup_ops = cgroup_init(conf); if (!cgroup_ops) return -1; ret = cgroup_ops->set(cgroup_ops, "freezer.state", state, name, lxcpath); if (ret < 0) return log_error(-1, "Failed to %s %s", freeze ? "freeze" : "unfreeze", name); for (;;) { char cur_state[MAX_STATE_LENGTH] = ""; ret = cgroup_ops->get(cgroup_ops, "freezer.state", cur_state, sizeof(cur_state), name, lxcpath); if (ret < 0) return log_error(-1, "Failed to get freezer state of %s", name); cur_state[lxc_char_right_gc(cur_state, strlen(cur_state))] = '\0'; ret = strncmp(cur_state, state, state_len); if (ret == 0) { notify_state_listeners(name, lxcpath, new_state); return 0; } sleep(1); } return 0; } int lxc_freeze(struct lxc_conf *conf, const char *name, const char *lxcpath) { int ret; notify_state_listeners(name, lxcpath, FREEZING); if (unified_cgroup_hierarchy() > 0) ret = lxc_cmd_freeze(name, lxcpath, -1); else ret = do_freeze_thaw(true, conf, name, lxcpath); notify_state_listeners(name, lxcpath, !ret ? FROZEN : RUNNING); return ret; } int lxc_unfreeze(struct lxc_conf *conf, const char *name, const char *lxcpath) { int ret; notify_state_listeners(name, lxcpath, THAWED); if (unified_cgroup_hierarchy() > 0) ret = lxc_cmd_unfreeze(name, lxcpath, -1); else ret = do_freeze_thaw(false, conf, name, lxcpath); notify_state_listeners(name, lxcpath, !ret ? RUNNING : FROZEN); return ret; } lxc-4.0.2/src/lxc/rtnl.h0000644061062106075000000000455213646120451011751 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_RTNL_H #define __LXC_RTNL_H /* * Use this as a good size to allocate route netlink messages */ #define RTNLMSG_GOOD_SIZE NLMSG_GOOD_SIZE #define RTNLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + RTNL_HDRLEN)) /* * struct genl_handler : the structure which store the netlink handler * and the family number * * @nlh: the netlink socket handler */ struct rtnl_handler { struct nl_handler nlh; }; /* * struct rtnlmsg : the struct containing the route netlink message * format * * @nlmsghdr: a netlink message header * @rtnlmsghdr: a route netlink message header pointer * */ struct rtnlmsg { struct nlmsghdr nlmsghdr; }; /* * rtnetlink_open : open a route netlink socket * * @handler: a struct rtnl_handler pointer * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_open(struct rtnl_handler *handler); /* * genetlink_close : close a route netlink socket * * @handler: the handler of the socket to be closed */ extern void rtnetlink_close(struct rtnl_handler *handler); /* * rtnetlink_rcv : receive a route netlink socket, it is up * to the caller to manage the allocation of the route netlink message * * @handler: the handler of the route netlink socket * @rtnlmsg: the pointer to a route netlink message pre-allocated * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_rcv(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg); /* * rtnetlink_send : send a route netlink socket, it is up * to the caller to manage the allocation of the route netlink message * * @handler: the handler of the route netlink socket * @rtnlmsg: the pointer to a netlink message pre-allocated * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_send(struct rtnl_handler *handler, struct rtnlmsg *rtnlmsg); struct genlmsg *genlmsg_alloc(size_t size); extern void rtnlmsg_free(struct rtnlmsg *rtnlmsg); /* * rtnetlink_transaction : send and receive a route netlink message in one shot * * @handler: the handler of the route netlink socket * @request: a route netlink message containing the request to be sent * @answer: a pre-allocated route netlink message to receive the response * * Returns 0 on success, < 0 otherwise */ extern int rtnetlink_transaction(struct rtnl_handler *handler, struct rtnlmsg *request, struct rtnlmsg *answer); #endif /* __LXC_RTNL_H */ lxc-4.0.2/src/lxc/network.h0000644061062106075000000002237113646120451012462 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_NETWORK_H #define __LXC_NETWORK_H #include #include #include #include #include #include #include "list.h" struct lxc_conf; struct lxc_handler; struct lxc_netdev; enum { LXC_NET_EMPTY, LXC_NET_VETH, LXC_NET_MACVLAN, LXC_NET_IPVLAN, LXC_NET_PHYS, LXC_NET_VLAN, LXC_NET_NONE, LXC_NET_MAXCONFTYPE, }; /* * Defines the structure to configure an ipv4 address * @address : ipv4 address * @broadcast : ipv4 broadcast address * @mask : network mask */ struct lxc_inetdev { struct in_addr addr; struct in_addr bcast; unsigned int prefix; }; struct lxc_route { struct in_addr addr; }; /* * Defines the structure to configure an ipv6 address * @flags : set the address up * @address : ipv6 address * @broadcast : ipv6 broadcast address * @mask : network mask */ struct lxc_inet6dev { struct in6_addr addr; struct in6_addr mcast; struct in6_addr acast; unsigned int prefix; }; struct lxc_route6 { struct in6_addr addr; }; /* Contains information about the host side veth device. * @pair : Name of the host side veth device. * If the user requested that the host veth device be created with a * specific names this field will be set. If this field is set @veth1 * is not set. * @veth1 : Name of the host side veth device. * If the user did not request that the host veth device be created * with a specific name this field will be set. If this field is set * @pair is not set. * @ifindex : Ifindex of the network device. */ struct ifla_veth { char pair[IFNAMSIZ]; char veth1[IFNAMSIZ]; int ifindex; struct lxc_list ipv4_routes; struct lxc_list ipv6_routes; int mode; /* bridge, router */ }; struct ifla_vlan { unsigned int flags; unsigned int fmask; unsigned short vid; unsigned short pad; }; struct ifla_macvlan { int mode; /* private, vepa, bridge, passthru */ }; struct ifla_ipvlan { int mode; /* l3, l3s, l2 */ int isolation; /* bridge, private, vepa */ }; /* Contains information about the physical network device as seen from the host. * @ifindex : The ifindex of the physical network device in the host's network * namespace. */ struct ifla_phys { int ifindex; int mtu; }; union netdev_p { struct ifla_macvlan macvlan_attr; struct ifla_ipvlan ipvlan_attr; struct ifla_phys phys_attr; struct ifla_veth veth_attr; struct ifla_vlan vlan_attr; }; /* * Defines a structure to configure a network device * @idx : network counter * @ifindex : ifindex of the network device * Note that this is the ifindex of the network device in * the container's network namespace. If the network device * consists of a pair of network devices (e.g. veth pairs * attached to a network bridge) then this index cannot be * used to identify or modify the host veth device. See * struct ifla_veth for the host side information. * @type : network type (veth, macvlan, vlan, ...) * @flags : flag of the network device (IFF_UP, ... ) * @link : lxc.net.[i].link, name of bridge or host iface to attach * if any * @name : lxc.net.[i].name, name of iface on the container side * @created_name : the name with which this interface got created before * being renamed to final_name. * Currenly only used for veth devices. * @hwaddr : mac address * @mtu : maximum transmission unit * @priv : information specific to the specificed network type * Note that this is a union so whether accessing a struct * is possible is dependent on the network type. * @ipv4 : a list of ipv4 addresses to be set on the network device * @ipv6 : a list of ipv6 addresses to be set on the network device * @ipv4_gateway_auto : whether the ipv4 gateway is to be automatically gathered * from the associated @link * @ipv4_gateway_dev : whether the ipv4 gateway is to be set as a device route * @ipv4_gateway : ipv4 gateway * @ipv6_gateway_auto : whether the ipv6 gateway is to be automatically gathered * from the associated @link * @ipv6_gateway_dev : whether the ipv6 gateway is to be set as a device route * @ipv6_gateway : ipv6 gateway * @upscript : a script filename to be executed during interface * configuration * @downscript : a script filename to be executed during interface * destruction */ struct lxc_netdev { ssize_t idx; int ifindex; int type; int flags; char link[IFNAMSIZ]; bool l2proxy; char name[IFNAMSIZ]; char created_name[IFNAMSIZ]; char *hwaddr; char *mtu; union netdev_p priv; struct lxc_list ipv4; struct lxc_list ipv6; bool ipv4_gateway_auto; bool ipv4_gateway_dev; struct in_addr *ipv4_gateway; bool ipv6_gateway_auto; bool ipv6_gateway_dev; struct in6_addr *ipv6_gateway; char *upscript; char *downscript; }; /* Convert a string mac address to a socket structure. */ extern int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr); /* Move a device between namespaces. */ extern int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char *ifname); extern int lxc_netdev_move_by_name(const char *ifname, pid_t pid, const char *newname); /* Delete a network device. */ extern int lxc_netdev_delete_by_name(const char *name); extern int lxc_netdev_delete_by_index(int ifindex); /* Change the device name. */ extern int lxc_netdev_rename_by_name(const char *oldname, const char *newname); extern int lxc_netdev_rename_by_index(int ifindex, const char *newname); extern int netdev_set_flag(const char *name, int flag); /* Set the device network up or down. */ extern int lxc_netdev_isup(const char *name); extern int lxc_netdev_up(const char *name); extern int lxc_netdev_down(const char *name); /* Change the mtu size for the specified device. */ extern int lxc_netdev_set_mtu(const char *name, int mtu); /* Create a virtual network devices. */ extern int lxc_veth_create(const char *name1, const char *name2, pid_t pid, unsigned int mtu); extern int lxc_macvlan_create(const char *master, const char *name, int mode); extern int lxc_vlan_create(const char *master, const char *name, unsigned short vid); /* Set ip address. */ extern int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr, struct in6_addr *mcast, struct in6_addr *acast, int prefix); extern int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr, struct in_addr *bcast, int prefix); /* Get ip address. */ extern int lxc_ipv4_addr_get(int ifindex, struct in_addr **res); extern int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res); /* Set default route. */ extern int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw); extern int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw); /* Attach an interface to the bridge. */ extern int lxc_bridge_attach(const char *bridge, const char *ifname); extern int lxc_ovs_delete_port(const char *bridge, const char *nic); extern bool is_ovs_bridge(const char *bridge); /* Create default gateway. */ extern int lxc_route_create_default(const char *addr, const char *ifname, int gateway); /* Delete default gateway. */ extern int lxc_route_delete_default(const char *addr, const char *ifname, int gateway); /* Activate neighbor proxying. */ extern int lxc_neigh_proxy_on(const char *name, int family); /* Disable neighbor proxying. */ extern int lxc_neigh_proxy_off(const char *name, int family); /* Activate IP forwarding. */ extern int lxc_ip_forwarding_on(const char *name, int family); /* Disable IP forwarding. */ extern int lxc_ip_forwarding_off(const char *name, int family); /* * Generate a new unique network interface name. * * Allows for 62^n unique combinations. */ extern char *lxc_ifname_alnum_case_sensitive(char *template); extern const char *lxc_net_type_to_str(int type); extern int setup_private_host_hw_addr(char *veth1); extern int netdev_get_mtu(int ifindex); extern int lxc_network_move_created_netdev_priv(struct lxc_handler *handler); extern void lxc_delete_network(struct lxc_handler *handler); extern int lxc_find_gateway_addresses(struct lxc_handler *handler); extern int lxc_requests_empty_network(struct lxc_handler *handler); extern int lxc_restore_phys_nics_to_netns(struct lxc_handler *handler); extern int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf, struct lxc_list *network); extern int lxc_network_send_to_child(struct lxc_handler *handler); extern int lxc_network_recv_from_parent(struct lxc_handler *handler); extern int lxc_network_send_name_and_ifindex_to_parent(struct lxc_handler *handler); extern int lxc_network_recv_name_and_ifindex_from_child(struct lxc_handler *handler); extern int lxc_netns_set_nsid(int netns_fd); extern int lxc_netns_get_nsid(__s32 fd); extern int lxc_create_network(struct lxc_handler *handler); extern char *is_wlan(const char *ifname); extern int lxc_netdev_move_wlan(char *physname, const char *ifname, pid_t pid, const char *newname); #endif /* __LXC_NETWORK_H */ lxc-4.0.2/src/lxc/criu.c0000644061062106075000000010067013646120451011725 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include "cgroup.h" #include "commands.h" #include "conf.h" #include "config.h" #include "criu.h" #include "log.h" #include "lxc.h" #include "lxclock.h" #include "network.h" #include "storage.h" #include "syscall_wrappers.h" #include "utils.h" #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #define CRIU_VERSION "2.0" #define CRIU_GITID_VERSION "2.0" #define CRIU_GITID_PATCHLEVEL 0 #define CRIU_IN_FLIGHT_SUPPORT "2.4" #define CRIU_EXTERNAL_NOT_VETH "2.8" lxc_log_define(criu, lxc); struct criu_opts { /* the thing to hook to stdout and stderr for logging */ int pipefd; /* The type of criu invocation, one of "dump" or "restore" */ char *action; /* the user-provided migrate options relevant to this action */ struct migrate_opts *user; /* The container to dump */ struct lxc_container *c; /* dump: stop the container or not after dumping? */ char tty_id[32]; /* the criu tty id for /dev/console, i.e. "tty[${rdev}:${dev}]" */ /* restore: the file to write the init process' pid into */ struct lxc_handler *handler; int console_fd; /* The path that is bind mounted from /dev/console, if any. We don't * want to use `--ext-mount-map auto`'s result here because the pts * device may have a different path (e.g. if the pty number is * different) on the target host. NULL if lxc.console.path = "none". */ char *console_name; /* The detected version of criu */ char *criu_version; }; static int load_tty_major_minor(char *directory, char *output, int len) { FILE *f; char path[PATH_MAX]; int ret; ret = snprintf(path, sizeof(path), "%s/tty.info", directory); if (ret < 0 || ret >= sizeof(path)) { ERROR("snprintf'd too many characters: %d", ret); return -1; } f = fopen(path, "re"); if (!f) { /* This means we're coming from a liblxc which didn't export * the tty info. In this case they had to have lxc.console.path * = * none, so there's no problem restoring. */ if (errno == ENOENT) return 0; SYSERROR("couldn't open %s", path); return -1; } if (!fgets(output, len, f)) { fclose(f); SYSERROR("couldn't read %s", path); return -1; } fclose(f); return 0; } static int cmp_version(const char *v1, const char *v2) { int ret; int oct_v1[3], oct_v2[3]; memset(oct_v1, -1, sizeof(oct_v1)); memset(oct_v2, -1, sizeof(oct_v2)); ret = sscanf(v1, "%d.%d.%d", &oct_v1[0], &oct_v1[1], &oct_v1[2]); if (ret < 1) return -1; ret = sscanf(v2, "%d.%d.%d", &oct_v2[0], &oct_v2[1], &oct_v2[2]); if (ret < 1) return -1; /* Major version is greater. */ if (oct_v1[0] > oct_v2[0]) return 1; if (oct_v1[0] < oct_v2[0]) return -1; /* Minor number is greater.*/ if (oct_v1[1] > oct_v2[1]) return 1; if (oct_v1[1] < oct_v2[1]) return -1; /* Patch number is greater. */ if (oct_v1[2] > oct_v2[2]) return 1; /* Patch numbers are equal. */ if (oct_v1[2] == oct_v2[2]) return 0; return -1; } static void exec_criu(struct cgroup_ops *cgroup_ops, struct lxc_conf *conf, struct criu_opts *opts) { char **argv, log[PATH_MAX]; int static_args = 23, argc = 0, i, ret; int netnr = 0; struct lxc_list *it; FILE *mnts; struct mntent mntent; char buf[4096], ttys[32]; size_t pos; /* If we are currently in a cgroup /foo/bar, and the container is in a * cgroup /lxc/foo, lxcfs will give us an ENOENT if some task in the * container has an open fd that points to one of the cgroup files * (systemd always opens its "root" cgroup). So, let's escape to the * /actual/ root cgroup so that lxcfs thinks criu has enough rights to * see all cgroups. */ if (!cgroup_ops->escape(cgroup_ops, conf)) { ERROR("failed to escape cgroups"); return; } /* The command line always looks like: * criu $(action) --tcp-established --file-locks --link-remap \ * --manage-cgroups=full --action-script foo.sh -D $(directory) \ * -o $(directory)/$(action).log --ext-mount-map auto * --enable-external-sharing --enable-external-masters * --enable-fs hugetlbfs --enable-fs tracefs --ext-mount-map console:/dev/pts/n * +1 for final NULL */ if (strcmp(opts->action, "dump") == 0 || strcmp(opts->action, "pre-dump") == 0) { /* -t pid --freeze-cgroup /lxc/ct */ static_args += 4; /* --prev-images-dir */ if (opts->user->predump_dir) static_args += 2; /* --page-server --address
--port */ if (opts->user->pageserver_address && opts->user->pageserver_port) static_args += 5; /* --leave-running (only for final dump) */ if (strcmp(opts->action, "dump") == 0 && !opts->user->stop) static_args++; /* --external tty[88,4] */ if (opts->tty_id[0]) static_args += 2; /* --force-irmap */ if (!opts->user->preserves_inodes) static_args++; /* --ghost-limit 1024 */ if (opts->user->ghost_limit) static_args += 2; } else if (strcmp(opts->action, "restore") == 0) { /* --root $(lxc_mount_point) --restore-detached * --restore-sibling * --lsm-profile apparmor:whatever */ static_args += 6; ttys[0] = 0; if (load_tty_major_minor(opts->user->directory, ttys, sizeof(ttys))) return; /* --inherit-fd fd[%d]:tty[%s] */ if (ttys[0]) static_args += 2; } else { return; } if (cgroup_ops->num_hierarchies(cgroup_ops) > 0) static_args += 2 * cgroup_ops->num_hierarchies(cgroup_ops); if (opts->user->verbose) static_args++; if (opts->user->action_script) static_args += 2; static_args += 2 * lxc_list_len(&opts->c->lxc_conf->mount_list); ret = snprintf(log, PATH_MAX, "%s/%s.log", opts->user->directory, opts->action); if (ret < 0 || ret >= PATH_MAX) { ERROR("logfile name too long"); return; } argv = malloc(static_args * sizeof(*argv)); if (!argv) return; memset(argv, 0, static_args * sizeof(*argv)); #define DECLARE_ARG(arg) \ do { \ if (arg == NULL) { \ ERROR("Got NULL argument for criu"); \ goto err; \ } \ argv[argc++] = strdup(arg); \ if (!argv[argc-1]) \ goto err; \ } while (0) argv[argc++] = on_path("criu", NULL); if (!argv[argc-1]) { ERROR("Couldn't find criu binary"); goto err; } DECLARE_ARG(opts->action); DECLARE_ARG("--tcp-established"); DECLARE_ARG("--file-locks"); DECLARE_ARG("--link-remap"); DECLARE_ARG("--manage-cgroups=full"); DECLARE_ARG("--ext-mount-map"); DECLARE_ARG("auto"); DECLARE_ARG("--enable-external-sharing"); DECLARE_ARG("--enable-external-masters"); DECLARE_ARG("--enable-fs"); DECLARE_ARG("hugetlbfs"); DECLARE_ARG("--enable-fs"); DECLARE_ARG("tracefs"); DECLARE_ARG("-D"); DECLARE_ARG(opts->user->directory); DECLARE_ARG("-o"); DECLARE_ARG(log); for (i = 0; i < cgroup_ops->num_hierarchies(cgroup_ops); i++) { char **controllers = NULL, *fullname; char *path, *tmp; if (!cgroup_ops->get_hierarchies(cgroup_ops, i, &controllers)) { ERROR("failed to get hierarchy %d", i); goto err; } /* if we are in a dump, we have to ask the monitor process what * the right cgroup is. if this is a restore, we can just use * the handler the restore task created. */ if (!strcmp(opts->action, "dump") || !strcmp(opts->action, "pre-dump")) { path = lxc_cmd_get_cgroup_path(opts->c->name, opts->c->config_path, controllers[0]); if (!path) { ERROR("failed to get cgroup path for %s", controllers[0]); goto err; } } else { const char *p; p = cgroup_ops->get_cgroup(cgroup_ops, controllers[0]); if (!p) { ERROR("failed to get cgroup path for %s", controllers[0]); goto err; } path = strdup(p); if (!path) { ERROR("strdup failed"); goto err; } } tmp = lxc_deslashify(path); if (!tmp) { ERROR("Failed to remove extraneous slashes from \"%s\"", path); free(path); goto err; } free(path); path = tmp; fullname = lxc_string_join(",", (const char **) controllers, false); if (!fullname) { ERROR("failed to join controllers"); free(path); goto err; } ret = sprintf(buf, "%s:%s", fullname, path); free(path); free(fullname); if (ret < 0 || ret >= sizeof(buf)) { ERROR("sprintf of cgroup root arg failed"); goto err; } DECLARE_ARG("--cgroup-root"); DECLARE_ARG(buf); } if (opts->user->verbose) DECLARE_ARG("-v4"); if (opts->user->action_script) { DECLARE_ARG("--action-script"); DECLARE_ARG(opts->user->action_script); } mnts = make_anonymous_mount_file(&opts->c->lxc_conf->mount_list, opts->c->lxc_conf->lsm_aa_allow_nesting); if (!mnts) goto err; while (getmntent_r(mnts, &mntent, buf, sizeof(buf))) { unsigned long flags = 0; char *mntdata = NULL; char arg[2 * PATH_MAX + 2]; if (parse_mntopts(mntent.mnt_opts, &flags, &mntdata) < 0) goto err; free(mntdata); /* only add --ext-mount-map for actual bind mounts */ if (!(flags & MS_BIND)) continue; if (strcmp(opts->action, "dump") == 0) ret = snprintf(arg, sizeof(arg), "/%s:%s", mntent.mnt_dir, mntent.mnt_dir); else ret = snprintf(arg, sizeof(arg), "%s:%s", mntent.mnt_dir, mntent.mnt_fsname); if (ret < 0 || ret >= sizeof(arg)) { fclose(mnts); ERROR("snprintf failed"); goto err; } DECLARE_ARG("--ext-mount-map"); DECLARE_ARG(arg); } fclose(mnts); if (strcmp(opts->action, "dump") == 0 || strcmp(opts->action, "pre-dump") == 0) { char pid[32], *freezer_relative; if (sprintf(pid, "%d", opts->c->init_pid(opts->c)) < 0) goto err; DECLARE_ARG("-t"); DECLARE_ARG(pid); freezer_relative = lxc_cmd_get_cgroup_path(opts->c->name, opts->c->config_path, "freezer"); if (!freezer_relative) { ERROR("failed getting freezer path"); goto err; } ret = snprintf(log, sizeof(log), "/sys/fs/cgroup/freezer/%s", freezer_relative); if (ret < 0 || ret >= sizeof(log)) goto err; if (!opts->user->disable_skip_in_flight && strcmp(opts->criu_version, CRIU_IN_FLIGHT_SUPPORT) >= 0) DECLARE_ARG("--skip-in-flight"); DECLARE_ARG("--freeze-cgroup"); DECLARE_ARG(log); if (opts->tty_id[0]) { DECLARE_ARG("--ext-mount-map"); DECLARE_ARG("/dev/console:console"); DECLARE_ARG("--external"); DECLARE_ARG(opts->tty_id); } if (opts->user->predump_dir) { DECLARE_ARG("--prev-images-dir"); DECLARE_ARG(opts->user->predump_dir); DECLARE_ARG("--track-mem"); } if (opts->user->pageserver_address && opts->user->pageserver_port) { DECLARE_ARG("--page-server"); DECLARE_ARG("--address"); DECLARE_ARG(opts->user->pageserver_address); DECLARE_ARG("--port"); DECLARE_ARG(opts->user->pageserver_port); } if (!opts->user->preserves_inodes) DECLARE_ARG("--force-irmap"); if (opts->user->ghost_limit) { char ghost_limit[32]; ret = sprintf(ghost_limit, "%"PRIu64, opts->user->ghost_limit); if (ret < 0 || ret >= sizeof(ghost_limit)) { ERROR("failed to print ghost limit %"PRIu64, opts->user->ghost_limit); goto err; } DECLARE_ARG("--ghost-limit"); DECLARE_ARG(ghost_limit); } /* only for final dump */ if (strcmp(opts->action, "dump") == 0 && !opts->user->stop) DECLARE_ARG("--leave-running"); } else if (strcmp(opts->action, "restore") == 0) { void *m; int additional; struct lxc_conf *lxc_conf = opts->c->lxc_conf; DECLARE_ARG("--root"); DECLARE_ARG(opts->c->lxc_conf->rootfs.mount); DECLARE_ARG("--restore-detached"); DECLARE_ARG("--restore-sibling"); if (ttys[0]) { if (opts->console_fd < 0) { ERROR("lxc.console.path configured on source host but not target"); goto err; } ret = snprintf(buf, sizeof(buf), "fd[%d]:%s", opts->console_fd, ttys); if (ret < 0 || ret >= sizeof(buf)) goto err; DECLARE_ARG("--inherit-fd"); DECLARE_ARG(buf); } if (opts->console_name) { if (snprintf(buf, sizeof(buf), "console:%s", opts->console_name) < 0) { SYSERROR("sprintf'd too many bytes"); } DECLARE_ARG("--ext-mount-map"); DECLARE_ARG(buf); } if (lxc_conf->lsm_aa_profile || lxc_conf->lsm_se_context) { if (lxc_conf->lsm_aa_profile) ret = snprintf(buf, sizeof(buf), "apparmor:%s", lxc_conf->lsm_aa_profile); else ret = snprintf(buf, sizeof(buf), "selinux:%s", lxc_conf->lsm_se_context); if (ret < 0 || ret >= sizeof(buf)) goto err; DECLARE_ARG("--lsm-profile"); DECLARE_ARG(buf); } additional = lxc_list_len(&opts->c->lxc_conf->network) * 2; m = realloc(argv, (argc + additional + 1) * sizeof(*argv)); if (!m) goto err; argv = m; lxc_list_for_each(it, &opts->c->lxc_conf->network) { size_t retlen; char eth[128], *veth; struct lxc_netdev *n = it->elem; bool external_not_veth; if (cmp_version(opts->criu_version, CRIU_EXTERNAL_NOT_VETH) >= 0) { /* Since criu version 2.8 the usage of --veth-pair * has been deprecated: * git tag --contains f2037e6d3445fc400 * v2.8 */ external_not_veth = true; } else { external_not_veth = false; } if (n->name[0] != '\0') { retlen = strlcpy(eth, n->name, sizeof(eth)); if (retlen >= sizeof(eth)) goto err; } else { ret = snprintf(eth, sizeof(eth), "eth%d", netnr); if (ret < 0 || ret >= sizeof(eth)) goto err; } switch (n->type) { case LXC_NET_VETH: veth = n->priv.veth_attr.pair; if (veth[0] == '\0') veth = n->priv.veth_attr.veth1; if (n->link[0] != '\0') { if (external_not_veth) ret = snprintf(buf, sizeof(buf), "veth[%s]:%s@%s", eth, veth, n->link); else ret = snprintf(buf, sizeof(buf), "%s=%s@%s", eth, veth, n->link); } else { if (external_not_veth) ret = snprintf(buf, sizeof(buf), "veth[%s]:%s", eth, veth); else ret = snprintf(buf, sizeof(buf), "%s=%s", eth, veth); } if (ret < 0 || ret >= sizeof(buf)) goto err; break; case LXC_NET_MACVLAN: if (n->link[0] == '\0') { ERROR("no host interface for macvlan %s", n->name); goto err; } ret = snprintf(buf, sizeof(buf), "macvlan[%s]:%s", eth, n->link); if (ret < 0 || ret >= sizeof(buf)) goto err; break; case LXC_NET_NONE: case LXC_NET_EMPTY: break; default: /* we have screened for this earlier... */ ERROR("unexpected network type %d", n->type); goto err; } if (external_not_veth) DECLARE_ARG("--external"); else DECLARE_ARG("--veth-pair"); DECLARE_ARG(buf); netnr++; } } argv[argc] = NULL; buf[0] = 0; pos = 0; for (i = 0; argv[i]; i++) { ret = snprintf(buf + pos, sizeof(buf) - pos, "%s ", argv[i]); if (ret < 0 || ret >= sizeof(buf) - pos) goto err; else pos += ret; } INFO("execing: %s", buf); /* before criu inits its log, it sometimes prints things to stdout/err; * let's be sure we capture that. */ if (dup2(opts->pipefd, STDOUT_FILENO) < 0) { SYSERROR("dup2 stdout failed"); goto err; } if (dup2(opts->pipefd, STDERR_FILENO) < 0) { SYSERROR("dup2 stderr failed"); goto err; } close(opts->pipefd); #undef DECLARE_ARG execv(argv[0], argv); err: for (i = 0; argv[i]; i++) free(argv[i]); free(argv); } /* * Function to check if the checks activated in 'features_to_check' are * available with the current architecture/kernel/criu combination. * * Parameter features_to_check is a bit mask of all features that should be * checked (see feature check defines in lxc/lxccontainer.h). * * If the return value is true, all requested features are supported. If * the return value is false the features_to_check parameter is updated * to reflect which features are available. '0' means no feature but * also that something went totally wrong. * * Some of the code flow of criu_version_ok() is duplicated and maybe it * is a good candidate for refactoring. */ bool __criu_check_feature(uint64_t *features_to_check) { pid_t pid; uint64_t current_bit = 0; int ret; uint64_t features = *features_to_check; /* Feature checking is currently always like * criu check --feature */ char *args[] = { "criu", "check", "--feature", NULL, NULL }; if ((features & ~FEATURE_MEM_TRACK & ~FEATURE_LAZY_PAGES) != 0) { /* There are feature bits activated we do not understand. * Refusing to answer at all */ *features_to_check = 0; return false; } while (current_bit < (sizeof(uint64_t) * 8 - 1)) { /* only test requested features */ if (!(features & (1ULL << current_bit))) { /* skip this */ current_bit++; continue; } pid = fork(); if (pid < 0) { SYSERROR("fork() failed"); *features_to_check = 0; return false; } if (pid == 0) { if ((1ULL << current_bit) == FEATURE_MEM_TRACK) /* This is needed for pre-dump support, which * enables pre-copy migration. */ args[3] = "mem_dirty_track"; else if ((1ULL << current_bit) == FEATURE_LAZY_PAGES) /* CRIU has two checks for userfaultfd support. * * The simpler check is only for 'uffd'. If the * kernel supports userfaultfd without noncoop * then only process can be lazily restored * which do not fork. With 'uffd-noncoop' * it is also possible to lazily restore processes * which do fork. For a container runtime like * LXC checking only for 'uffd' makes not much sense. */ args[3] = "uffd-noncoop"; else _exit(EXIT_FAILURE); null_stdfds(); execvp("criu", args); SYSERROR("Failed to exec \"criu\""); _exit(EXIT_FAILURE); } ret = wait_for_pid(pid); if (ret == -1) { /* It is not known why CRIU failed. Either * CRIU is not available, the feature check * does not exist or the feature is not * supported. */ INFO("feature not supported"); /* Clear not supported feature bit */ features &= ~(1ULL << current_bit); } current_bit++; /* no more checks requested; exit check loop */ if (!(features & ~((1ULL << current_bit)-1))) break; } if (features != *features_to_check) { *features_to_check = features; return false; } return true; } /* * Check to see if the criu version is recent enough for all the features we * use. This version allows either CRIU_VERSION or (CRIU_GITID_VERSION and * CRIU_GITID_PATCHLEVEL) to work, enabling users building from git to c/r * things potentially before a version is released with a particular feature. * * The intent is that when criu development slows down, we can drop this, but * for now we shouldn't attempt to c/r with versions that we know won't work. * * Note: If version != NULL criu_version() stores the detected criu version in * version. Allocates memory for version which must be freed by caller. */ static bool criu_version_ok(char **version) { int pipes[2]; pid_t pid; if (pipe(pipes) < 0) { SYSERROR("pipe() failed"); return false; } pid = fork(); if (pid < 0) { SYSERROR("fork() failed"); return false; } if (pid == 0) { char *args[] = { "criu", "--version", NULL }; char *path; close(pipes[0]); close(STDERR_FILENO); if (dup2(pipes[1], STDOUT_FILENO) < 0) _exit(EXIT_FAILURE); path = on_path("criu", NULL); if (!path) _exit(EXIT_FAILURE); execv(path, args); _exit(EXIT_FAILURE); } else { FILE *f; char *tmp; int patch; close(pipes[1]); if (wait_for_pid(pid) < 0) { close(pipes[0]); SYSERROR("execing criu failed, is it installed?"); return false; } f = fdopen(pipes[0], "re"); if (!f) { close(pipes[0]); return false; } tmp = malloc(1024); if (!tmp) { fclose(f); return false; } if (fscanf(f, "Version: %1023[^\n]s", tmp) != 1) goto version_error; if (fgetc(f) != '\n') goto version_error; if (strcmp(tmp, CRIU_VERSION) >= 0) goto version_match; if (fscanf(f, "GitID: v%1023[^-]s", tmp) != 1) goto version_error; if (fgetc(f) != '-') goto version_error; if (fscanf(f, "%d", &patch) != 1) goto version_error; if (strcmp(tmp, CRIU_GITID_VERSION) < 0) goto version_error; if (patch < CRIU_GITID_PATCHLEVEL) goto version_error; version_match: fclose(f); if (!version) free(tmp); else *version = tmp; return true; version_error: fclose(f); free(tmp); ERROR("must have criu " CRIU_VERSION " or greater to checkpoint/restore"); return false; } } /* Check and make sure the container has a configuration that we know CRIU can * dump. */ static bool criu_ok(struct lxc_container *c, char **criu_version) { struct lxc_list *it; if (geteuid()) { ERROR("Must be root to checkpoint"); return false; } if (!criu_version_ok(criu_version)) return false; /* We only know how to restore containers with veth networks. */ lxc_list_for_each(it, &c->lxc_conf->network) { struct lxc_netdev *n = it->elem; switch(n->type) { case LXC_NET_VETH: case LXC_NET_NONE: case LXC_NET_EMPTY: case LXC_NET_MACVLAN: break; default: ERROR("Found un-dumpable network: %s (%s)", lxc_net_type_to_str(n->type), n->name); if (criu_version) { free(*criu_version); *criu_version = NULL; } return false; } } return true; } static bool restore_net_info(struct lxc_container *c) { int ret; struct lxc_list *it; bool has_error = true; if (container_mem_lock(c)) return false; lxc_list_for_each(it, &c->lxc_conf->network) { struct lxc_netdev *netdev = it->elem; char template[IFNAMSIZ]; if (netdev->type != LXC_NET_VETH) continue; ret = snprintf(template, sizeof(template), "vethXXXXXX"); if (ret < 0 || ret >= sizeof(template)) goto out_unlock; if (netdev->priv.veth_attr.pair[0] == '\0' && netdev->priv.veth_attr.veth1[0] == '\0') { if (!lxc_ifname_alnum_case_sensitive(template)) goto out_unlock; (void)strlcpy(netdev->priv.veth_attr.veth1, template, IFNAMSIZ); } } has_error = false; out_unlock: container_mem_unlock(c); return !has_error; } /* do_restore never returns, the calling process is used as the monitor process. * do_restore calls _exit() if it fails. */ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_opts *opts, char *criu_version) { int fd, ret; pid_t pid; struct lxc_handler *handler; int status = 0; int pipes[2] = {-1, -1}; struct cgroup_ops *cgroup_ops; /* Try to detach from the current controlling tty if it exists. * Otherwise, lxc_init (via lxc_console) will attach the container's * console output to the current tty, which is probably not what any * library user wants, and if they do, they can just manually configure * it :) */ fd = open("/dev/tty", O_RDWR); if (fd >= 0) { if (ioctl(fd, TIOCNOTTY, NULL) < 0) SYSERROR("couldn't detach from tty"); close(fd); } handler = lxc_init_handler(c->name, c->lxc_conf, c->config_path, false); if (!handler) goto out; if (lxc_init(c->name, handler) < 0) goto out; cgroup_ops = cgroup_init(c->lxc_conf); if (!cgroup_ops) goto out_fini_handler; handler->cgroup_ops = cgroup_ops; if (!cgroup_ops->payload_create(cgroup_ops, handler)) { ERROR("failed creating groups"); goto out_fini_handler; } if (!restore_net_info(c)) { ERROR("failed restoring network info"); goto out_fini_handler; } ret = resolve_clone_flags(handler); if (ret < 0) { SYSERROR("Unsupported clone flag specified"); goto out_fini_handler; } if (pipe2(pipes, O_CLOEXEC) < 0) { SYSERROR("pipe() failed"); goto out_fini_handler; } pid = fork(); if (pid < 0) goto out_fini_handler; if (pid == 0) { struct criu_opts os; struct lxc_rootfs *rootfs; int flags; close(status_pipe); status_pipe = -1; close(pipes[0]); pipes[0] = -1; if (unshare(CLONE_NEWNS)) goto out_fini_handler; /* CRIU needs the lxc root bind mounted so that it is the root of some * mount. */ rootfs = &c->lxc_conf->rootfs; if (rootfs_is_blockdev(c->lxc_conf)) { if (lxc_setup_rootfs_prepare_root(c->lxc_conf, c->name, c->config_path) < 0) goto out_fini_handler; } else { if (mkdir(rootfs->mount, 0755) < 0 && errno != EEXIST) goto out_fini_handler; if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { SYSERROR("remount / to private failed"); goto out_fini_handler; } if (mount(rootfs->path, rootfs->mount, NULL, MS_BIND, NULL) < 0) { (void)rmdir(rootfs->mount); goto out_fini_handler; } } os.pipefd = pipes[1]; os.action = "restore"; os.user = opts; os.c = c; os.console_fd = c->lxc_conf->console.slave; os.criu_version = criu_version; os.handler = handler; if (os.console_fd >= 0) { /* Twiddle the FD_CLOEXEC bit. We want to pass this FD to criu * via --inherit-fd, so we don't want it to close. */ flags = fcntl(os.console_fd, F_GETFD); if (flags < 0) { SYSERROR("F_GETFD failed: %d", os.console_fd); goto out_fini_handler; } flags &= ~FD_CLOEXEC; if (fcntl(os.console_fd, F_SETFD, flags) < 0) { SYSERROR("F_SETFD failed"); goto out_fini_handler; } } os.console_name = c->lxc_conf->console.name; /* exec_criu() returning is an error */ exec_criu(cgroup_ops, c->lxc_conf, &os); umount(rootfs->mount); (void)rmdir(rootfs->mount); goto out_fini_handler; } else { char title[2048]; close(pipes[1]); pipes[1] = -1; pid_t w = waitpid(pid, &status, 0); if (w == -1) { SYSERROR("waitpid"); goto out_fini_handler; } if (WIFEXITED(status)) { char buf[4096]; if (WEXITSTATUS(status)) { int n; n = lxc_read_nointr(pipes[0], buf, sizeof(buf)); if (n < 0) { SYSERROR("failed reading from criu stderr"); goto out_fini_handler; } if (n == sizeof(buf)) n--; buf[n] = 0; ERROR("criu process exited %d, output:\n%s", WEXITSTATUS(status), buf); goto out_fini_handler; } else { ret = snprintf(buf, sizeof(buf), "/proc/self/task/%lu/children", (unsigned long)syscall(__NR_gettid)); if (ret < 0 || ret >= sizeof(buf)) { ERROR("snprintf'd too many characters: %d", ret); goto out_fini_handler; } FILE *f = fopen(buf, "re"); if (!f) { SYSERROR("couldn't read restore's children file %s", buf); goto out_fini_handler; } ret = fscanf(f, "%d", (int*) &handler->pid); fclose(f); if (ret != 1) { ERROR("reading restore pid failed"); goto out_fini_handler; } if (lxc_set_state(c->name, handler, RUNNING)) { ERROR("error setting running state after restore"); goto out_fini_handler; } } } else { ERROR("CRIU was killed with signal %d", WTERMSIG(status)); goto out_fini_handler; } close(pipes[0]); ret = lxc_write_nointr(status_pipe, &status, sizeof(status)); close(status_pipe); status_pipe = -1; if (sizeof(status) != ret) { SYSERROR("failed to write all of status"); goto out_fini_handler; } /* * See comment in lxcapi_start; we don't care if these * fail because it's just a beauty thing. We just * assign the return here to silence potential. */ ret = snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); if (ret < 0 || (size_t)ret >= sizeof(title)) INFO("Setting truncated process name"); ret = setproctitle(title); if (ret < 0) INFO("Failed to set process name"); ret = lxc_poll(c->name, handler); if (ret) lxc_abort(handler); lxc_end(handler); _exit(ret); } out_fini_handler: if (pipes[0] >= 0) close(pipes[0]); if (pipes[1] >= 0) close(pipes[1]); lxc_end(handler); out: if (status_pipe >= 0) { /* ensure getting here was a failure, e.g. if we failed to * parse the child pid or something, even after a successful * restore */ if (!status) status = 1; if (lxc_write_nointr(status_pipe, &status, sizeof(status)) != sizeof(status)) SYSERROR("writing status failed"); close(status_pipe); } _exit(EXIT_FAILURE); } static int save_tty_major_minor(char *directory, struct lxc_container *c, char *tty_id, int len) { FILE *f; char path[PATH_MAX]; int ret; struct stat sb; if (c->lxc_conf->console.path && !strcmp(c->lxc_conf->console.path, "none")) { tty_id[0] = 0; return 0; } ret = snprintf(path, sizeof(path), "/proc/%d/root/dev/console", c->init_pid(c)); if (ret < 0 || ret >= sizeof(path)) { ERROR("snprintf'd too many characters: %d", ret); return -1; } ret = stat(path, &sb); if (ret < 0) { SYSERROR("stat of %s failed", path); return -1; } ret = snprintf(path, sizeof(path), "%s/tty.info", directory); if (ret < 0 || ret >= sizeof(path)) { ERROR("snprintf'd too many characters: %d", ret); return -1; } ret = snprintf(tty_id, len, "tty[%llx:%llx]", (long long unsigned) sb.st_rdev, (long long unsigned) sb.st_dev); if (ret < 0 || ret >= sizeof(path)) { ERROR("snprintf'd too many characters: %d", ret); return -1; } f = fopen(path, "we"); if (!f) { SYSERROR("failed to open %s", path); return -1; } ret = fprintf(f, "%s", tty_id); fclose(f); if (ret < 0) SYSERROR("failed to write to %s", path); return ret; } /* do one of either predump or a regular dump */ static bool do_dump(struct lxc_container *c, char *mode, struct migrate_opts *opts) { int ret; pid_t pid; int criuout[2]; char *criu_version = NULL; if (!criu_ok(c, &criu_version)) return false; ret = pipe(criuout); if (ret < 0) { SYSERROR("pipe() failed"); free(criu_version); return false; } if (mkdir_p(opts->directory, 0700) < 0) goto fail; pid = fork(); if (pid < 0) { SYSERROR("fork failed"); goto fail; } if (pid == 0) { struct criu_opts os; struct cgroup_ops *cgroup_ops; close(criuout[0]); cgroup_ops = cgroup_init(c->lxc_conf); if (!cgroup_ops) { ERROR("failed to cgroup_init()"); _exit(EXIT_FAILURE); } os.pipefd = criuout[1]; os.action = mode; os.user = opts; os.c = c; os.console_name = c->lxc_conf->console.path; os.criu_version = criu_version; os.handler = NULL; ret = save_tty_major_minor(opts->directory, c, os.tty_id, sizeof(os.tty_id)); if (ret < 0) { free(criu_version); _exit(EXIT_FAILURE); } /* exec_criu() returning is an error */ exec_criu(cgroup_ops, c->lxc_conf, &os); free(criu_version); _exit(EXIT_FAILURE); } else { int status; ssize_t n; char buf[4096]; close(criuout[1]); pid_t w = waitpid(pid, &status, 0); if (w == -1) { SYSERROR("waitpid"); close(criuout[0]); free(criu_version); return false; } n = lxc_read_nointr(criuout[0], buf, sizeof(buf)); close(criuout[0]); if (n < 0) { SYSERROR("read"); n = 0; } if (n == sizeof(buf)) buf[n-1] = 0; else buf[n] = 0; if (WIFEXITED(status)) { if (WEXITSTATUS(status)) { ERROR("dump failed with %d", WEXITSTATUS(status)); ret = false; } else { ret = true; } } else if (WIFSIGNALED(status)) { ERROR("dump signaled with %d", WTERMSIG(status)); ret = false; } else { ERROR("unknown dump exit %d", status); ret = false; } if (!ret) ERROR("criu output: %s", buf); free(criu_version); return ret; } fail: close(criuout[0]); close(criuout[1]); (void)rmdir(opts->directory); free(criu_version); return false; } bool __criu_pre_dump(struct lxc_container *c, struct migrate_opts *opts) { return do_dump(c, "pre-dump", opts); } bool __criu_dump(struct lxc_container *c, struct migrate_opts *opts) { char path[PATH_MAX]; int ret; ret = snprintf(path, sizeof(path), "%s/inventory.img", opts->directory); if (ret < 0 || ret >= sizeof(path)) return false; if (access(path, F_OK) == 0) { ERROR("please use a fresh directory for the dump directory"); return false; } return do_dump(c, "dump", opts); } bool __criu_restore(struct lxc_container *c, struct migrate_opts *opts) { pid_t pid; int status, nread; int pipefd[2]; char *criu_version = NULL; if (geteuid()) { ERROR("Must be root to restore"); return false; } if (pipe(pipefd)) { ERROR("failed to create pipe"); return false; } if (!criu_ok(c, &criu_version)) { close(pipefd[0]); close(pipefd[1]); return false; } pid = fork(); if (pid < 0) { close(pipefd[0]); close(pipefd[1]); free(criu_version); return false; } if (pid == 0) { close(pipefd[0]); /* this never returns */ do_restore(c, pipefd[1], opts, criu_version); } close(pipefd[1]); free(criu_version); nread = lxc_read_nointr(pipefd[0], &status, sizeof(status)); close(pipefd[0]); if (sizeof(status) != nread) { ERROR("reading status from pipe failed"); goto err_wait; } /* If the criu process was killed or exited nonzero, wait() for the * handler, since the restore process died. Otherwise, we don't need to * wait, since the child becomes the monitor process. */ if (!WIFEXITED(status) || WEXITSTATUS(status)) goto err_wait; return true; err_wait: if (wait_for_pid(pid)) ERROR("restore process died"); return false; } lxc-4.0.2/src/lxc/start.h0000644061062106075000000001137213646120451012125 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_START_H #define __LXC_START_H #include #include #include #include #include #include "conf.h" #include "namespace.h" #include "state.h" struct lxc_handler { /* Record the clone for namespaces flags that the container requested. * * @ns_clone_flags * - All clone flags that were requested. * * @ns_on_clone_flags * - The clone flags for namespaces to actually use when calling * lxc_clone(): After the container has started ns_on_clone_flags will * list the clone flags that were unshare()ed rather then clone()ed * because of ordering requirements (e.g. e.g. CLONE_NEWNET and * CLONE_NEWUSER) or implementation details. * * @ns_keep_flags; * - The clone flags for the namespaces that the container will inherit * from the parent. They are not recorded in the handler itself but * are present in the container's config. * * @ns_share_flags; * - The clone flags for the namespaces that the container will share * with another process. They are not recorded in the handler itself * but are present in the container's config. */ struct /* lxc_ns */ { int ns_clone_flags; int ns_on_clone_flags; }; /* File descriptor to pin the rootfs for privileged containers. */ int pinfd; /* Signal file descriptor. */ int sigfd; /* List of file descriptors referring to the namespaces of the * container. Note that these are not necessarily identical to * the "clone_flags" handler field in case namespace inheritance is * requested. */ int nsfd[LXC_NS_MAX]; /* Abstract unix domain SOCK_DGRAM socketpair to pass arbitrary data * between child and parent. */ int data_sock[2]; /* The socketpair() fds used to wait on successful daemonized startup. */ int state_socket_pair[2]; /* Socketpair to synchronize processes during container creation. */ int sync_sock[2]; /* Pointer to the name of the container. Do not free! */ const char *name; /* Pointer to the path the container. Do not free! */ const char *lxcpath; /* Whether the container's startup process euid is 0. */ bool am_root; /* Indicates whether should we close std{in,out,err} on start. */ bool daemonize; /* The child's pid. */ pid_t pid; /* The child's pidfd. */ int pidfd; /* The grandfather's pid when double-forking. */ pid_t transient_pid; /* The monitor's pid. */ pid_t monitor_pid; int monitor_status_fd; /* Whether the child has already exited. */ bool init_died; /* The signal mask prior to setting up the signal file descriptor. */ sigset_t oldmask; /* The container's in-memory configuration. */ struct lxc_conf *conf; /* A set of operations to be performed at various stages of the * container's life. */ struct lxc_operations *ops; /* This holds the cgroup information. Note that the data here is * specific to the cgroup driver used. */ void *cgroup_data; /* Data to be passed to handler ops. */ void *data; /* Current state of the container. */ lxc_state_t state; /* The exit status of the container; not defined unless ->init_died == * true. */ int exit_status; struct cgroup_ops *cgroup_ops; }; struct execute_args { char *init_path; int init_fd; char *const *argv; int quiet; }; struct lxc_operations { int (*start)(struct lxc_handler *, void *); int (*post_start)(struct lxc_handler *, void *); }; extern int lxc_poll(const char *name, struct lxc_handler *handler); extern int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state); extern int lxc_serve_state_clients(const char *name, struct lxc_handler *handler, lxc_state_t state); extern void lxc_abort(struct lxc_handler *handler); extern struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf, const char *lxcpath, bool daemonize); extern void lxc_zero_handler(struct lxc_handler *handler); extern void lxc_free_handler(struct lxc_handler *handler); extern int lxc_init(const char *name, struct lxc_handler *handler); extern void lxc_end(struct lxc_handler *handler); /* lxc_check_inherited: Check for any open file descriptors and close them if * requested. * @param[in] conf The container's configuration. * @param[in] closeall Whether we should close all open file descriptors. * @param[in] fds_to_ignore Array of file descriptors to ignore. * @param[in] len_fds Length of fds_to_ignore array. */ extern int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int *fds_to_ignore, size_t len_fds); extern int __lxc_start(struct lxc_handler *, struct lxc_operations *, void *, const char *, bool, int *); extern int resolve_clone_flags(struct lxc_handler *handler); #endif lxc-4.0.2/src/lxc/monitor.c0000644061062106075000000002132713646120451012453 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "config.h" #include "error.h" #include "log.h" #include "lxclock.h" #include "macro.h" #include "memory_utils.h" #include "monitor.h" #include "state.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(monitor, lxc); /* routines used by monitor publishers (containers) */ int lxc_monitor_fifo_name(const char *lxcpath, char *fifo_path, size_t fifo_path_sz, int do_mkdirp) { int ret; char *rundir; rundir = get_rundir(); if (!rundir) return -1; if (do_mkdirp) { ret = snprintf(fifo_path, fifo_path_sz, "%s/lxc/%s", rundir, lxcpath); if (ret < 0 || (size_t)ret >= fifo_path_sz) { ERROR("rundir/lxcpath (%s/%s) too long for monitor fifo", rundir, lxcpath); free(rundir); return -1; } ret = mkdir_p(fifo_path, 0755); if (ret < 0) { ERROR("Unable to create monitor fifo directory %s", fifo_path); free(rundir); return ret; } } ret = snprintf(fifo_path, fifo_path_sz, "%s/lxc/%s/monitor-fifo", rundir, lxcpath); if (ret < 0 || (size_t)ret >= fifo_path_sz) { ERROR("rundir/lxcpath (%s/%s) too long for monitor fifo", rundir, lxcpath); free(rundir); return -1; } free(rundir); return 0; } static void lxc_monitor_fifo_send(struct lxc_msg *msg, const char *lxcpath) { int fd,ret; char fifo_path[PATH_MAX]; BUILD_BUG_ON(sizeof(*msg) > PIPE_BUF); /* write not guaranteed atomic */ ret = lxc_monitor_fifo_name(lxcpath, fifo_path, sizeof(fifo_path), 0); if (ret < 0) return; /* Open the fifo nonblock in case the monitor is dead, we don't want the * open to wait for a reader since it may never come. */ fd = open(fifo_path, O_WRONLY | O_NONBLOCK); if (fd < 0) { /* It is normal for this open() to fail with ENXIO when there is * no monitor running, so we don't log it. */ if (errno == ENXIO || errno == ENOENT) return; SYSWARN("Failed to open fifo to send message"); return; } if (fcntl(fd, F_SETFL, O_WRONLY) < 0) { close(fd); return; } ret = lxc_write_nointr(fd, msg, sizeof(*msg)); if (ret != sizeof(*msg)) { close(fd); SYSERROR("Failed to write to monitor fifo \"%s\"", fifo_path); return; } close(fd); } void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxcpath) { struct lxc_msg msg = {.type = lxc_msg_state, .value = state}; (void)strlcpy(msg.name, name, sizeof(msg.name)); lxc_monitor_fifo_send(&msg, lxcpath); } void lxc_monitor_send_exit_code(const char *name, int exit_code, const char *lxcpath) { struct lxc_msg msg = {.type = lxc_msg_exit_code, .value = exit_code}; (void)strlcpy(msg.name, name, sizeof(msg.name)); lxc_monitor_fifo_send(&msg, lxcpath); } /* routines used by monitor subscribers (lxc-monitor) */ int lxc_monitor_close(int fd) { return close(fd); } /* Enforces \0-termination for the abstract unix socket. This is not required * but allows us to print it out. * * Older version of liblxc only allowed for 105 bytes to be used for the * abstract unix domain socket name because the code for our abstract unix * socket handling performed invalid checks. Since we \0-terminate we could now * have a maximum of 106 chars. But to not break backwards compatibility we keep * the limit at 105. */ int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr) { __do_free char *path = NULL; size_t len; int ret; uint64_t hash; /* addr.sun_path is only 108 bytes, so we hash the full name and * then append as much of the name as we can fit. */ memset(addr, 0, sizeof(*addr)); addr->sun_family = AF_UNIX; /* strlen("lxc/") + strlen("/monitor-sock") + 1 = 18 */ len = strlen(lxcpath) + 18; path = must_realloc(NULL, len); ret = snprintf(path, len, "lxc/%s/monitor-sock", lxcpath); if (ret < 0 || (size_t)ret >= len) { ERROR("Failed to create name for monitor socket"); return -1; } /* Note: snprintf() will \0-terminate addr->sun_path on the 106th byte * and so the abstract socket name has 105 "meaningful" characters. This * is absolutely intentional. For further info read the comment for this * function above! */ len = sizeof(addr->sun_path) - 1; hash = fnv_64a_buf(path, ret, FNV1A_64_INIT); ret = snprintf(addr->sun_path, len, "@lxc/%016" PRIx64 "/%s", hash, lxcpath); if (ret < 0) { ERROR("Failed to create hashed name for monitor socket"); goto on_error; } else if ((size_t)ret >= len) { errno = ENAMETOOLONG; SYSERROR("The name of monitor socket too long (%d bytes)", ret); goto on_error; } /* replace @ with \0 */ addr->sun_path[0] = '\0'; INFO("Using monitor socket name \"%s\" (length of socket name %zu must be <= %zu)", &addr->sun_path[1], strlen(&addr->sun_path[1]), sizeof(addr->sun_path) - 3); return 0; on_error: return -1; } int lxc_monitor_open(const char *lxcpath) { struct sockaddr_un addr; int fd; size_t retry; int backoff_ms[] = {10, 50, 100}; if (lxc_monitor_sock_name(lxcpath, &addr) < 0) return -1; DEBUG("Opening monitor socket %s with len %zu", &addr.sun_path[1], strlen(&addr.sun_path[1])); for (retry = 0; retry < sizeof(backoff_ms) / sizeof(backoff_ms[0]); retry++) { fd = lxc_abstract_unix_connect(addr.sun_path); if (fd != -1 || errno != ECONNREFUSED) break; SYSERROR("Failed to connect to monitor socket. Retrying in %d ms", backoff_ms[retry]); usleep(backoff_ms[retry] * 1000); } if (fd < 0) { SYSERROR("Failed to connect to monitor socket"); return -1; } return fd; } int lxc_monitor_read_fdset(struct pollfd *fds, nfds_t nfds, struct lxc_msg *msg, int timeout) { long i; int ret; ret = poll(fds, nfds, timeout * 1000); if (ret == -1) return -1; else if (ret == 0) return -2; /* timed out */ /* Only read from the first ready fd, the others will remain ready for * when this routine is called again. */ for (i = 0; i < nfds; i++) { if (fds[i].revents != 0) { fds[i].revents = 0; ret = recv(fds[i].fd, msg, sizeof(*msg), 0); if (ret <= 0) { SYSERROR("Failed to receive message. Did monitord die?"); return -1; } return ret; } } SYSERROR("No ready fd found"); return -1; } int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout) { struct pollfd fds; fds.fd = fd; fds.events = POLLIN | POLLPRI; fds.revents = 0; return lxc_monitor_read_fdset(&fds, 1, msg, timeout); } int lxc_monitor_read(int fd, struct lxc_msg *msg) { return lxc_monitor_read_timeout(fd, msg, -1); } #define LXC_MONITORD_PATH LIBEXECDIR "/lxc/lxc-monitord" /* Used to spawn a monitord either on startup of a daemon container, or when * lxc-monitor starts. */ int lxc_monitord_spawn(const char *lxcpath) { int ret; int pipefd[2]; char pipefd_str[INTTYPE_TO_STRLEN(int)]; pid_t pid1, pid2; char *const args[] = { LXC_MONITORD_PATH, (char *)lxcpath, pipefd_str, NULL, }; /* double fork to avoid zombies when monitord exits */ pid1 = fork(); if (pid1 < 0) { SYSERROR("Failed to fork()"); return -1; } if (pid1) { DEBUG("Going to wait for pid %d", pid1); if (waitpid(pid1, NULL, 0) != pid1) return -1; DEBUG("Finished waiting on pid %d", pid1); return 0; } if (pipe(pipefd) < 0) { SYSERROR("Failed to create pipe"); _exit(EXIT_FAILURE); } pid2 = fork(); if (pid2 < 0) { SYSERROR("Failed to fork()"); _exit(EXIT_FAILURE); } if (pid2) { DEBUG("Trying to sync with child process"); char c; /* Wait for daemon to create socket. */ close(pipefd[1]); /* Sync with child, we're ignoring the return from read * because regardless if it works or not, either way we've * synced with the child process. the if-empty-statement * construct is to quiet the warn-unused-result warning. */ if (lxc_read_nointr(pipefd[0], &c, 1)) ; close(pipefd[0]); DEBUG("Successfully synced with child process"); _exit(EXIT_SUCCESS); } if (setsid() < 0) { SYSERROR("Failed to setsid()"); _exit(EXIT_FAILURE); } lxc_check_inherited(NULL, true, &pipefd[1], 1); if (null_stdfds() < 0) { SYSERROR("Failed to dup2() standard file descriptors to /dev/null"); _exit(EXIT_FAILURE); } close(pipefd[0]); ret = snprintf(pipefd_str, sizeof(pipefd_str), "%d", pipefd[1]); if (ret < 0 || ret >= sizeof(pipefd_str)) { ERROR("Failed to create pid argument to pass to monitord"); _exit(EXIT_FAILURE); } DEBUG("Using pipe file descriptor %d for monitord", pipefd[1]); execvp(args[0], args); SYSERROR("Failed to exec lxc-monitord"); _exit(EXIT_FAILURE); } lxc-4.0.2/src/lxc/attach_options.h0000644061062106075000000001242213646120451014004 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_ATTACH_OPTIONS_H #define __LXC_ATTACH_OPTIONS_H #include #ifdef __cplusplus extern "C" { #endif /*! * LXC environment policy. */ typedef enum lxc_attach_env_policy_t { LXC_ATTACH_KEEP_ENV, /*!< Retain the environment */ LXC_ATTACH_CLEAR_ENV /*!< Clear the environment */ } lxc_attach_env_policy_t; enum { /* The following are on by default: */ LXC_ATTACH_MOVE_TO_CGROUP = 0x00000001, /*!< Move to cgroup */ LXC_ATTACH_DROP_CAPABILITIES = 0x00000002, /*!< Drop capabilities */ LXC_ATTACH_SET_PERSONALITY = 0x00000004, /*!< Set personality */ LXC_ATTACH_LSM_EXEC = 0x00000008, /*!< Execute under a Linux Security Module */ /* The following are off by default: */ LXC_ATTACH_REMOUNT_PROC_SYS = 0x00010000, /*!< Remount /proc filesystem */ LXC_ATTACH_LSM_NOW = 0x00020000, /*!< FIXME: unknown */ /* Set PR_SET_NO_NEW_PRIVS to block execve() gainable privileges. */ LXC_ATTACH_NO_NEW_PRIVS = 0x00040000, /*!< PR_SET_NO_NEW_PRIVS */ LXC_ATTACH_TERMINAL = 0x00080000, /*!< Allocate new terminal for attached process. */ /* We have 16 bits for things that are on by default and 16 bits that * are off by default, that should be sufficient to keep binary * compatibility for a while */ LXC_ATTACH_DEFAULT = 0x0000FFFF /*!< Mask of flags to apply by default */ }; /*! All Linux Security Module flags */ #define LXC_ATTACH_LSM (LXC_ATTACH_LSM_EXEC | LXC_ATTACH_LSM_NOW) /*! LXC attach function type. * * Function to run in container. * * \param payload \ref lxc_attach_command_t to run. * * \return Function should return \c 0 on success, and any other value to denote failure. */ typedef int (*lxc_attach_exec_t)(void* payload); /*! * LXC attach options for \ref lxc_container \c attach(). */ typedef struct lxc_attach_options_t { /*! Any combination of LXC_ATTACH_* flags */ int attach_flags; /*! The namespaces to attach to (CLONE_NEW... flags) */ int namespaces; /*! Initial personality (\c -1 to autodetect). * \warning This may be ignored if lxc is compiled without personality * support) */ long personality; /*! Initial current directory, use \c NULL to use cwd. * If the current directory does not exist in the container, the root * directory will be used instead because of kernel defaults. */ char* initial_cwd; /*! The user-id to run as. * * \note Set to \c -1 for default behaviour (init uid for userns * containers or \c 0 (super-user) if detection fails). */ uid_t uid; /*! The group-id to run as. * * \note Set to \c -1 for default behaviour (init gid for userns * containers or \c 0 (super-user) if detection fails). */ gid_t gid; /*! Environment policy */ lxc_attach_env_policy_t env_policy; /*! Extra environment variables to set in the container environment */ char** extra_env_vars; /*! Names of environment variables in existing environment to retain * in container environment. */ char** extra_keep_env; /**@{*/ /*! File descriptors for stdin, stdout and stderr, * \c dup2() will be used before calling exec_function, * (assuming not \c 0, \c 1 and \c 2 are specified) and the * original fds are closed before passing control * over. Any \c O_CLOEXEC flag will be removed after * that. */ int stdin_fd; /*!< stdin file descriptor */ int stdout_fd; /*!< stdout file descriptor */ int stderr_fd; /*!< stderr file descriptor */ /**@}*/ /*! File descriptor to log output. */ int log_fd; } lxc_attach_options_t; /*! Default attach options to use */ #define LXC_ATTACH_OPTIONS_DEFAULT \ { \ /* .attach_flags = */ LXC_ATTACH_DEFAULT, \ /* .namespaces = */ -1, \ /* .personality = */ -1, \ /* .initial_cwd = */ NULL, \ /* .uid = */ (uid_t)-1, \ /* .gid = */ (gid_t)-1, \ /* .env_policy = */ LXC_ATTACH_KEEP_ENV, \ /* .extra_env_vars = */ NULL, \ /* .extra_keep_env = */ NULL, \ /* .stdin_fd = */ 0, \ /* .stdout_fd = */ 1, \ /* .stderr_fd = */ 2, \ /* .log_fd = */ -EBADF, \ } /*! * Representation of a command to run in a container. */ typedef struct lxc_attach_command_t { char* program; /*!< The program to run (passed to execvp) */ char** argv; /*!< The argv pointer of that program, including the program itself in argv[0] */ } lxc_attach_command_t; /*! * \brief Run a command in the container. * * \param payload \ref lxc_attach_command_t to run. * * \return \c -1 on error, exit code of lxc_attach_command_t program on success. */ extern int lxc_attach_run_command(void* payload); /*! * \brief Run a shell command in the container. * * \param payload Not used. * * \return Exit code of shell. */ extern int lxc_attach_run_shell(void* payload); #ifdef __cplusplus } #endif #endif lxc-4.0.2/src/lxc/api_extensions.h0000644061062106075000000000172313646120451014017 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_API_EXTENSIONS_H #define __LXC_API_EXTENSIONS_H #include #include #include "config.h" /* * api_extensions is the list of all API extensions in the order they were * added. The following kind of changes come with a new extensions: - New public functions - New configuration key - New valid values for a configuration key */ static char *api_extensions[] = { "lxc_log", "lxc_config_item_is_supported", "console_log", "reboot2", "mount_injection", "cgroup_relative", "mount_injection_file", "seccomp_allow_nesting", "seccomp_notify", "network_veth_routes", "network_ipvlan", "network_l2proxy", "network_gateway_device_route", "network_phys_macvlan_mtu", "network_veth_router", #ifdef HAVE_STRUCT_BPF_CGROUP_DEV_CTX "cgroup2_devices", #endif "cgroup2", }; static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions); #endif /* __LXC_API_EXTENSIONS_H */ lxc-4.0.2/src/lxc/state.c0000644061062106075000000000435313646120451012104 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "cgroup.h" #include "commands.h" #include "commands_utils.h" #include "config.h" #include "log.h" #include "lxc.h" #include "monitor.h" #include "start.h" #include "utils.h" lxc_log_define(state, lxc); static const char *const strstate[] = { "STOPPED", "STARTING", "RUNNING", "STOPPING", "ABORTING", "FREEZING", "FROZEN", "THAWED", }; const char *lxc_state2str(lxc_state_t state) { if (state < STOPPED || state > MAX_STATE - 1) return NULL; return strstate[state]; } lxc_state_t lxc_str2state(const char *state) { size_t len; lxc_state_t i; len = sizeof(strstate)/sizeof(strstate[0]); for (i = 0; i < len; i++) if (!strcmp(strstate[i], state)) return i; ERROR("invalid state '%s'", state); return -1; } lxc_state_t lxc_getstate(const char *name, const char *lxcpath) { return lxc_cmd_get_state(name, lxcpath); } static int fillwaitedstates(const char *strstates, lxc_state_t *states) { char *token; char *strstates_dup; int state; strstates_dup = strdup(strstates); if (!strstates_dup) return -1; lxc_iterate_parts(token, strstates_dup, "|") { state = lxc_str2state(token); if (state < 0) { free(strstates_dup); return -1; } states[state] = 1; } free(strstates_dup); return 0; } int lxc_wait(const char *lxcname, const char *states, int timeout, const char *lxcpath) { int state = -1; lxc_state_t s[MAX_STATE] = {0}; if (fillwaitedstates(states, s)) return -1; for (;;) { struct timespec onesec = { .tv_sec = 1, .tv_nsec = 0, }; state = lxc_cmd_sock_get_state(lxcname, lxcpath, s, timeout); if (state >= 0) break; if (errno != ECONNREFUSED) { SYSERROR("Failed to receive state from monitor"); return -1; } if (timeout > 0) timeout--; if (timeout == 0) return -1; (void)nanosleep(&onesec, NULL); } TRACE("Retrieved state of container %s", lxc_state2str(state)); if (!s[state]) return -1; return 0; } lxc-4.0.2/src/lxc/log.h0000644061062106075000000004165313646120451011556 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_LOG_H #define __LXC_LOG_H #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include "conf.h" #include "config.h" #ifndef O_CLOEXEC #define O_CLOEXEC 02000000 #endif #ifndef F_DUPFD_CLOEXEC #define F_DUPFD_CLOEXEC 1030 #endif #define LXC_LOG_PREFIX_SIZE 32 #define LXC_LOG_BUFFER_SIZE 4096 /* predefined lxc log priorities. */ enum lxc_loglevel { LXC_LOG_LEVEL_TRACE, LXC_LOG_LEVEL_DEBUG, LXC_LOG_LEVEL_INFO, LXC_LOG_LEVEL_NOTICE, LXC_LOG_LEVEL_WARN, LXC_LOG_LEVEL_ERROR, LXC_LOG_LEVEL_CRIT, LXC_LOG_LEVEL_ALERT, LXC_LOG_LEVEL_FATAL, LXC_LOG_LEVEL_NOTSET, }; /* location information of the logging event */ struct lxc_log_locinfo { const char *file; const char *func; int line; }; #define LXC_LOG_LOCINFO_INIT \ { .file = __FILE__, .func = __func__, .line = __LINE__ } /* brief logging event object */ struct lxc_log_event { const char *category; int priority; struct timespec timestamp; struct lxc_log_locinfo *locinfo; const char *fmt; va_list *vap; }; /* log appender object */ struct lxc_log_appender { const char *name; int (*append)(const struct lxc_log_appender *, struct lxc_log_event *); /* * appenders can be stacked */ struct lxc_log_appender *next; }; /* log category object */ struct lxc_log_category { const char *name; int priority; struct lxc_log_appender *appender; const struct lxc_log_category *parent; }; #ifndef NO_LXC_CONF extern int lxc_log_use_global_fd; #endif /* * Returns true if the chained priority is equal to or higher than * given priority. */ static inline int lxc_log_priority_is_enabled(const struct lxc_log_category *category, int priority) { while (category->priority == LXC_LOG_LEVEL_NOTSET && category->parent) category = category->parent; int cmp_prio = category->priority; #ifndef NO_LXC_CONF if (!lxc_log_use_global_fd && current_config && current_config->loglevel != LXC_LOG_LEVEL_NOTSET) cmp_prio = current_config->loglevel; #endif return priority >= cmp_prio; } /* * converts a priority to a literal string */ static inline const char *lxc_log_priority_to_string(int priority) { switch (priority) { case LXC_LOG_LEVEL_TRACE: return "TRACE"; case LXC_LOG_LEVEL_DEBUG: return "DEBUG"; case LXC_LOG_LEVEL_INFO: return "INFO"; case LXC_LOG_LEVEL_NOTICE: return "NOTICE"; case LXC_LOG_LEVEL_WARN: return "WARN"; case LXC_LOG_LEVEL_ERROR: return "ERROR"; case LXC_LOG_LEVEL_CRIT: return "CRIT"; case LXC_LOG_LEVEL_ALERT: return "ALERT"; case LXC_LOG_LEVEL_FATAL: return "FATAL"; } return "NOTSET"; } static inline const char *lxc_syslog_priority_to_string(int priority) { switch (priority) { case LOG_DAEMON: return "daemon"; case LOG_LOCAL0: return "local0"; case LOG_LOCAL1: return "local1"; case LOG_LOCAL2: return "local2"; case LOG_LOCAL3: return "local3"; case LOG_LOCAL4: return "local4"; case LOG_LOCAL5: return "local5"; case LOG_LOCAL6: return "local6"; case LOG_LOCAL7: return "local7"; } return "NOTSET"; } /* * converts a literal priority to an int */ static inline int lxc_log_priority_to_int(const char *name) { if (strcasecmp("TRACE", name) == 0) return LXC_LOG_LEVEL_TRACE; if (strcasecmp("DEBUG", name) == 0) return LXC_LOG_LEVEL_DEBUG; if (strcasecmp("INFO", name) == 0) return LXC_LOG_LEVEL_INFO; if (strcasecmp("NOTICE", name) == 0) return LXC_LOG_LEVEL_NOTICE; if (strcasecmp("WARN", name) == 0) return LXC_LOG_LEVEL_WARN; if (strcasecmp("ERROR", name) == 0) return LXC_LOG_LEVEL_ERROR; if (strcasecmp("CRIT", name) == 0) return LXC_LOG_LEVEL_CRIT; if (strcasecmp("ALERT", name) == 0) return LXC_LOG_LEVEL_ALERT; if (strcasecmp("FATAL", name) == 0) return LXC_LOG_LEVEL_FATAL; return LXC_LOG_LEVEL_NOTSET; } static inline int lxc_syslog_priority_to_int(const char *name) { if (strcasecmp("daemon", name) == 0) return LOG_DAEMON; if (strcasecmp("local0", name) == 0) return LOG_LOCAL0; if (strcasecmp("local1", name) == 0) return LOG_LOCAL1; if (strcasecmp("local2", name) == 0) return LOG_LOCAL2; if (strcasecmp("local3", name) == 0) return LOG_LOCAL3; if (strcasecmp("local4", name) == 0) return LOG_LOCAL4; if (strcasecmp("local5", name) == 0) return LOG_LOCAL5; if (strcasecmp("local6", name) == 0) return LOG_LOCAL6; if (strcasecmp("local7", name) == 0) return LOG_LOCAL7; return -EINVAL; } static inline void __lxc_log_append(const struct lxc_log_appender *appender, struct lxc_log_event *event) { va_list va; va_list *va_keep = event->vap; while (appender) { va_copy(va, *va_keep); event->vap = &va; appender->append(appender, event); appender = appender->next; va_end(va); } } static inline void __lxc_log(const struct lxc_log_category *category, struct lxc_log_event *event) { while (category) { __lxc_log_append(category->appender, event); category = category->parent; } } /* * Helper macro to define log functions. */ #define lxc_log_priority_define(acategory, LEVEL) \ \ __lxc_unused __attribute__ ((format (printf, 2, 3))) \ static inline void LXC_##LEVEL(struct lxc_log_locinfo *, const char *, ...); \ \ __lxc_unused static inline void LXC_##LEVEL(struct lxc_log_locinfo* locinfo, \ const char* format, ...) \ { \ if (lxc_log_priority_is_enabled(acategory, LXC_LOG_LEVEL_##LEVEL)) { \ va_list va_ref; \ int saved_errno; \ struct lxc_log_event evt = { \ .category = (acategory)->name, \ .priority = LXC_LOG_LEVEL_##LEVEL, \ .fmt = format, \ .locinfo = locinfo \ }; \ \ /* clock_gettime() is explicitly marked as MT-Safe \ * without restrictions. So let's use it for our \ * logging stamps. \ */ \ saved_errno = errno; \ (void)clock_gettime(CLOCK_REALTIME, &evt.timestamp); \ \ va_start(va_ref, format); \ evt.vap = &va_ref; \ __lxc_log(acategory, &evt); \ va_end(va_ref); \ errno = saved_errno; \ } \ } /* * Helper macro to define and use static categories. */ #define lxc_log_category_define(name, parent) \ extern struct lxc_log_category lxc_log_category_##parent; \ struct lxc_log_category lxc_log_category_##name = { \ #name, \ LXC_LOG_LEVEL_NOTSET, \ NULL, \ &lxc_log_category_##parent \ }; #define lxc_log_define(name, parent) \ lxc_log_category_define(name, parent) \ \ lxc_log_priority_define(&lxc_log_category_##name, TRACE) \ lxc_log_priority_define(&lxc_log_category_##name, DEBUG) \ lxc_log_priority_define(&lxc_log_category_##name, INFO) \ lxc_log_priority_define(&lxc_log_category_##name, NOTICE) \ lxc_log_priority_define(&lxc_log_category_##name, WARN) \ lxc_log_priority_define(&lxc_log_category_##name, ERROR) \ lxc_log_priority_define(&lxc_log_category_##name, CRIT) \ lxc_log_priority_define(&lxc_log_category_##name, ALERT) \ lxc_log_priority_define(&lxc_log_category_##name, FATAL) #define lxc_log_category_priority(name) \ (lxc_log_priority_to_string(lxc_log_category_##name.priority)) /* * Helper macro to define errno string. */ #if HAVE_STRERROR_R #ifndef HAVE_DECL_STRERROR_R #ifdef STRERROR_R_CHAR_P char *strerror_r(int errnum, char *buf, size_t buflen); #else int strerror_r(int errnum, char *buf, size_t buflen); #endif #endif #ifdef STRERROR_R_CHAR_P #define lxc_log_strerror_r \ char errno_buf[PATH_MAX / 2] = {"Failed to get errno string"}; \ char *ptr = NULL; \ { \ int __saved_errno = errno; \ ptr = strerror_r(errno, errno_buf, sizeof(errno_buf)); \ errno = __saved_errno; \ if (!ptr) \ ptr = errno_buf; \ } #else #define lxc_log_strerror_r \ char errno_buf[PATH_MAX / 2] = {"Failed to get errno string"}; \ char *ptr = errno_buf; \ { \ int __saved_errno = errno; \ (void)strerror_r(errno, errno_buf, sizeof(errno_buf)); \ errno = __saved_errno; \ } #endif #elif ENFORCE_THREAD_SAFETY #error ENFORCE_THREAD_SAFETY was set but cannot be guaranteed #else #define lxc_log_strerror_r \ char *ptr = NULL; \ { \ ptr = strerror(errno); \ } #endif /* * top categories */ #define TRACE(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_TRACE(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define DEBUG(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_DEBUG(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define INFO(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_INFO(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define NOTICE(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_NOTICE(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define WARN(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_WARN(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define ERROR(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_ERROR(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define CRIT(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_CRIT(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define ALERT(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_ALERT(&locinfo, format, ##__VA_ARGS__); \ } while (0) #define FATAL(format, ...) do { \ struct lxc_log_locinfo locinfo = LXC_LOG_LOCINFO_INIT; \ LXC_FATAL(&locinfo, format, ##__VA_ARGS__); \ } while (0) #if HAVE_M_FORMAT && !ENABLE_COVERITY_BUILD #define SYSTRACE(format, ...) \ TRACE("%m - " format, ##__VA_ARGS__) #else #define SYSTRACE(format, ...) \ do { \ lxc_log_strerror_r; \ TRACE("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #endif #if HAVE_M_FORMAT && !ENABLE_COVERITY_BUILD #define SYSDEBUG(format, ...) \ DEBUG("%m - " format, ##__VA_ARGS__) #else #define SYSDEBUG(format, ...) \ do { \ lxc_log_strerror_r; \ DEBUG("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #endif #if HAVE_M_FORMAT && !ENABLE_COVERITY_BUILD #define SYSINFO(format, ...) \ INFO("%m - " format, ##__VA_ARGS__) #else #define SYSINFO(format, ...) \ do { \ lxc_log_strerror_r; \ INFO("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #endif #if HAVE_M_FORMAT && !ENABLE_COVERITY_BUILD #define SYSNOTICE(format, ...) \ NOTICE("%m - " format, ##__VA_ARGS__) #else #define SYSNOTICE(format, ...) \ do { \ lxc_log_strerror_r; \ NOTICE("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #endif #if HAVE_M_FORMAT && !ENABLE_COVERITY_BUILD #define SYSWARN(format, ...) \ WARN("%m - " format, ##__VA_ARGS__) #else #define SYSWARN(format, ...) \ do { \ lxc_log_strerror_r; \ WARN("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #endif #if HAVE_M_FORMAT && !ENABLE_COVERITY_BUILD #define SYSERROR(format, ...) \ ERROR("%m - " format, ##__VA_ARGS__) #else #define SYSERROR(format, ...) \ do { \ lxc_log_strerror_r; \ ERROR("%s - " format, ptr, ##__VA_ARGS__); \ } while (0) #endif #if HAVE_M_FORMAT && !ENABLE_COVERITY_BUILD #define CMD_SYSERROR(format, ...) \ fprintf(stderr, "%s: %d: %s - %m - " format "\n", __FILE__, __LINE__, \ __func__, ##__VA_ARGS__); #else #define CMD_SYSERROR(format, ...) \ do { \ lxc_log_strerror_r; \ fprintf(stderr, "%s: %d: %s - %s - " format "\n", __FILE__, \ __LINE__, __func__, ptr, ##__VA_ARGS__); \ } while (0) #endif #if HAVE_M_FORMAT && !ENABLE_COVERITY_BUILD #define CMD_SYSINFO(format, ...) \ printf("%s: %d: %s - %m - " format "\n", __FILE__, __LINE__, __func__, \ ##__VA_ARGS__); #else #define CMD_SYSINFO(format, ...) \ do { \ lxc_log_strerror_r; \ printf("%s: %d: %s - %s - " format "\n", __FILE__, __LINE__, \ __func__, ptr, ##__VA_ARGS__); \ } while (0) #endif #define log_error_errno(__ret__, __errno__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ errno = (__errno__); \ SYSERROR(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_error(__ret__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ ERROR(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_trace_errno(__ret__, __errno__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ errno = __errno__; \ SYSTRACE(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_trace(__ret__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ TRACE(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_warn_errno(__ret__, __errno__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ errno = __errno__; \ SYSWARN(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_warn(__ret__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ WARN(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_debug_errno(__ret__, __errno__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ errno = __errno__; \ SYSDEBUG(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_debug(__ret__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ DEBUG(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_info_errno(__ret__, __errno__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ errno = __errno__; \ SYSINFO(format, ##__VA_ARGS__); \ __internal_ret__; \ }) #define log_info(__ret__, format, ...) \ ({ \ typeof(__ret__) __internal_ret__ = (__ret__); \ INFO(format, ##__VA_ARGS__); \ __internal_ret__; \ }) extern int lxc_log_fd; extern int lxc_log_syslog(int facility); extern void lxc_log_enable_syslog(void); extern int lxc_log_set_level(int *dest, int level); extern int lxc_log_get_level(void); extern bool lxc_log_has_valid_level(void); extern int lxc_log_set_file(int *fd, const char *fname); extern const char *lxc_log_get_file(void); extern void lxc_log_set_prefix(const char *prefix); extern const char *lxc_log_get_prefix(void); extern void lxc_log_options_no_override(void); #endif lxc-4.0.2/src/lxc/nl.h0000644061062106075000000001654013646120451011403 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_NL_H #define __LXC_NL_H #include #include "memory_utils.h" /* * Use this as a good size to allocate generic netlink messages */ #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif #define NLMSG_GOOD_SIZE (2*PAGE_SIZE) #define NLMSG_TAIL(nmsg) ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) #define NLA_NEXT_ATTR(attr) ((void *)((char *)attr) + NLA_ALIGN(attr->nla_len)) /* * struct nl_handler : the handler for netlink sockets, this structure * is used all along the netlink socket life cycle to specify the * netlink socket to be used. * * @fd: the file descriptor of the netlink socket * @seq: the sequence number of the netlink messages * @local: the bind address * @peer: the peer address */ struct nl_handler { int fd; int seq; struct sockaddr_nl local; struct sockaddr_nl peer; }; /* * struct nlmsg : the netlink message structure. This message is to be used to * be allocated with netlink_alloc. * * @nlmsghdr: a pointer to a netlink message header * @cap: capacity of the netlink message, this is the initially allocated size * and later operations (e.g. reserve and put) can not exceed this limit. */ struct nlmsg { struct nlmsghdr *nlmsghdr; ssize_t cap; }; /* * netlink_open : open a netlink socket, the function will * fill the handler with the right value * * @handler: a netlink handler to be used all along the netlink * socket life cycle * @protocol: specify the protocol to be used when opening the * netlink socket * * Return 0 on success, < 0 otherwise */ int netlink_open(struct nl_handler *handler, int protocol); /* * netlink_close : close a netlink socket, after this call, * the handler is no longer valid * * @handler: a handler to the netlink socket */ void netlink_close(struct nl_handler *handler); define_cleanup_function(struct nl_handler *, netlink_close); /* * netlink_rcv : receive a netlink message from the kernel. * It is up to the caller to manage the allocation of the * netlink message * * @handler: a handler to the netlink socket * @nlmsg: a netlink message * * Returns 0 on success, < 0 otherwise */ int netlink_rcv(struct nl_handler *handler, struct nlmsg *nlmsg); int __netlink_recv(struct nl_handler *handler, struct nlmsghdr *nlmsg); /* * netlink_send: send a netlink message to the kernel. It is up * to the caller to manage the allocate of the netlink message * * @handler: a handler to the netlink socket * @nlmsg: a netlink message * * Returns 0 on success, < 0 otherwise */ int netlink_send(struct nl_handler *handler, struct nlmsg *nlmsg); int __netlink_send(struct nl_handler *handler, struct nlmsghdr *nlmsg); /* * netlink_transaction: send a request to the kernel and read the response. * This is useful for transactional protocol. It is up to the caller * to manage the allocation of the netlink message. * * @handler: a handler to a opened netlink socket * @request: a netlink message pointer containing the request * @answer: a netlink message pointer to receive the result * * Returns 0 on success, < 0 otherwise */ int netlink_transaction(struct nl_handler *handler, struct nlmsg *request, struct nlmsg *answer); int __netlink_transaction(struct nl_handler *handler, struct nlmsghdr *request, struct nlmsghdr *answer); /* * nla_put_string: copy a null terminated string to a netlink message * attribute * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the string * @string: a null terminated string to be copied to the netlink message * * Returns 0 on success, < 0 otherwise */ int nla_put_string(struct nlmsg *nlmsg, int attr, const char *string); /* * nla_put_buffer: copy a buffer with a specified size to a netlink * message attribute * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the string * @data: a pointer to a buffer * @size: the size of the buffer * * Returns 0 on success, < 0 otherwise */ int nla_put_buffer(struct nlmsg *nlmsg, int attr, const void *data, size_t size); /* * nla_put_u32: copy an integer to a netlink message attribute * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the integer * @string: an integer to be copied to the netlink message * * Returns 0 on success, < 0 otherwise */ int nla_put_u32(struct nlmsg *nlmsg, int attr, int value); /* * nla_put_u16: copy an integer to a netlink message attribute * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the unsigned 16-bit value * @value: 16-bit attribute data value to be copied to the netlink message * * Returns 0 on success, < 0 otherwise */ int nla_put_u16(struct nlmsg *nlmsg, int attr, unsigned short value); /* * nla_put_attr: add an attribute name to a netlink * * @nlmsg: the netlink message to be filled * @attr: the attribute name of the integer * * Returns 0 on success, < 0 otherwise */ int nla_put_attr(struct nlmsg *nlmsg, int attr); /* * nla_begin_nested: begin the nesting attribute * * @nlmsg: the netlink message to be filled * @attr: the netsted attribute name * * Returns current nested pointer to be reused * to nla_end_nested. */ struct rtattr *nla_begin_nested(struct nlmsg *nlmsg, int attr); /* * nla_end_nested: end the nesting attribute * * @nlmsg: the netlink message * @nested: the nested pointer * * Returns the current */ void nla_end_nested(struct nlmsg *nlmsg, struct rtattr *attr); /* * nlmsg_allocate : allocate a netlink message. The netlink format message * is a header, a padding, a payload and a padding again. * When a netlink message is allocated, the size specify the * payload we want. So the real size of the allocated message * is sizeof(header) + sizeof(padding) + payloadsize + sizeof(padding), * in other words, the function will allocate more than specified. When * the buffer is allocated, the content is zeroed. * The function will also fill the field nlmsg_len with NLMSG_HDRLEN. * If the allocation must be for the specified size, just use malloc. * * @size: the capacity of the payload to be allocated * * Returns a pointer to the newly allocated netlink message, NULL otherwise */ struct nlmsg *nlmsg_alloc(size_t size); /* * nlmsg_alloc_reserve: like nlmsg_alloc(), but reserve the whole payload * after allocated, that is, the field nlmsg_len be set to the capacity * of nlmsg. Often used to allocate a message for the reply. * * @size: the capacity of the payload to be allocated. */ struct nlmsg *nlmsg_alloc_reserve(size_t size); /* * Reserve room for additional data at the tail of a netlink message * * @nlmsg: the netlink message * @len: length of additional data to reserve room for * * Returns a pointer to newly reserved room or NULL */ void *nlmsg_reserve(struct nlmsg *nlmsg, size_t len); /* * nlmsg_free : free a previously allocate message * * @nlmsg: the netlink message to be freed */ void nlmsg_free(struct nlmsg *nlmsg); define_cleanup_function(struct nlmsg *, nlmsg_free); /* * nlmsg_data : returns a pointer to the data contained in the netlink message * * @nlmsg : the netlink message to get the data * * Returns a pointer to the netlink data or NULL if there is no data */ void *nlmsg_data(struct nlmsg *nlmsg); extern int addattr(struct nlmsghdr *n, size_t maxlen, int type, const void *data, size_t alen); #endif lxc-4.0.2/src/lxc/Makefile.am0000644061062106075000000003076013646120451012655 00000000000000# SPDX-License-Identifier: LGPL-2.1+ pkginclude_HEADERS = attach_options.h \ lxccontainer.h \ version.h noinst_HEADERS = api_extensions.h \ attach.h \ caps.h \ cgroups/cgroup.h \ cgroups/cgroup_utils.h \ cgroups/cgroup2_devices.h \ compiler.h \ conf.h \ confile.h \ confile_utils.h \ criu.h \ error.h \ file_utils.h \ ../include/netns_ifaddrs.h \ initutils.h \ list.h \ log.h \ lxc.h \ lxclock.h \ macro.h \ memory_utils.h \ monitor.h \ namespace.h \ raw_syscalls.h \ rexec.h \ start.h \ state.h \ storage/btrfs.h \ storage/dir.h \ storage/loop.h \ storage/lvm.h \ storage/nbd.h \ storage/overlay.h \ storage/rbd.h \ storage/rsync.h \ storage/storage.h \ storage/storage_utils.h \ storage/zfs.h \ string_utils.h \ syscall_numbers.h \ syscall_wrappers.h \ terminal.h \ ../tests/lxctest.h \ tools/arguments.h \ storage/storage_utils.h \ utils.h \ uuid.h if IS_BIONIC noinst_HEADERS += ../include/fexecve.h \ ../include/lxcmntent.h \ ../include/openpty.h endif if !HAVE_PRLIMIT if HAVE_PRLIMIT64 noinst_HEADERS += ../include/prlimit.h endif endif if !HAVE_GETLINE if HAVE_FGETLN noinst_HEADERS += ../include/getline.h endif endif if !HAVE_GETSUBOPT noinst_HEADERS += tools/include/getsubopt.h endif if !HAVE_GETGRGID_R noinst_HEADERS += ../include/getgrgid_r.h endif sodir=$(libdir) LSM_SOURCES = lsm/lsm.c \ lsm/lsm.h \ lsm/nop.c if ENABLE_APPARMOR LSM_SOURCES += lsm/apparmor.c endif if ENABLE_SELINUX LSM_SOURCES += lsm/selinux.c endif lib_LTLIBRARIES = liblxc.la liblxc_la_SOURCES = af_unix.c af_unix.h \ api_extensions.h \ attach.c attach.h \ caps.c caps.h \ cgroups/cgfsng.c \ cgroups/cgroup.c cgroups/cgroup.h \ cgroups/cgroup2_devices.c cgroups/cgroup2_devices.h \ cgroups/cgroup_utils.c cgroups/cgroup_utils.h \ compiler.h \ commands.c commands.h \ commands_utils.c commands_utils.h \ conf.c conf.h \ confile.c confile.h \ confile_utils.c confile_utils.h \ criu.c criu.h \ error.c error.h \ execute.c \ freezer.c \ file_utils.c file_utils.h \ ../include/netns_ifaddrs.c ../include/netns_ifaddrs.h \ initutils.c initutils.h \ list.h \ log.c log.h \ lxc.h \ lxccontainer.c lxccontainer.h \ lxclock.c lxclock.h \ lxcseccomp.h \ macro.h \ memory_utils.h \ mainloop.c mainloop.h \ namespace.c namespace.h \ nl.c nl.h \ network.c network.h \ monitor.c monitor.h \ parse.c parse.h \ raw_syscalls.c raw_syscalls.h \ ringbuf.c ringbuf.h \ rtnl.c rtnl.h \ state.c state.h \ start.c start.h \ storage/btrfs.c storage/btrfs.h \ storage/dir.c storage/dir.h \ storage/loop.c storage/loop.h \ storage/lvm.c storage/lvm.h \ storage/nbd.c storage/nbd.h \ storage/overlay.c storage/overlay.h \ storage/rbd.c storage/rbd.h \ storage/rsync.c storage/rsync.h \ storage/storage.c storage/storage.h \ storage/storage_utils.c storage/storage_utils.h \ storage/zfs.c storage/zfs.h \ string_utils.c string_utils.h \ sync.c sync.h \ syscall_numbers.h \ syscall_wrappers.h \ terminal.c \ utils.c utils.h \ uuid.c uuid.h \ version.h \ $(LSM_SOURCES) if IS_BIONIC liblxc_la_SOURCES += ../include/fexecve.c ../include/fexecve.h \ ../include/lxcmntent.c ../include/lxcmntent.h \ ../include/openpty.c ../include/openpty.h endif if !HAVE_GETGRGID_R liblxc_la_SOURCES += ../include/getgrgid_r.c ../include/getgrgid_r.h endif if !HAVE_GETLINE if HAVE_FGETLN liblxc_la_SOURCES += ../include/getline.c ../include/getline.h endif endif if !HAVE_PRLIMIT if HAVE_PRLIMIT64 liblxc_la_SOURCES += ../include/prlimit.c ../include/prlimit.h endif endif if ENABLE_SECCOMP liblxc_la_SOURCES += seccomp.c endif if !HAVE_STRLCPY liblxc_la_SOURCES += ../include/strlcpy.c ../include/strlcpy.h endif if !HAVE_STRLCAT liblxc_la_SOURCES += ../include/strlcat.c ../include/strlcat.h endif if ENFORCE_MEMFD_REXEC liblxc_la_SOURCES += rexec.c rexec.h endif AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \ -DLXCINITDIR=\"$(LXCINITDIR)\" \ -DLIBEXECDIR=\"$(LIBEXECDIR)\" \ -DLXCTEMPLATEDIR=\"$(LXCTEMPLATEDIR)\" \ -DLXCTEMPLATECONFIG=\"$(LXCTEMPLATECONFIG)\" \ -DLOGPATH=\"$(LOGPATH)\" \ -DLXC_DEFAULT_CONFIG=\"$(LXC_DEFAULT_CONFIG)\" \ -DLXC_USERNIC_DB=\"$(LXC_USERNIC_DB)\" \ -DLXC_USERNIC_CONF=\"$(LXC_USERNIC_CONF)\" \ -DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \ -DRUNTIME_PATH=\"$(RUNTIME_PATH)\" \ -DSBINDIR=\"$(SBINDIR)\" \ -DAPPARMOR_CACHE_DIR=\"$(APPARMOR_CACHE_DIR)\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/lxc \ -I $(top_srcdir)/src/lxc/storage \ -I $(top_srcdir)/src/lxc/cgroups if ENABLE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR endif if ENABLE_OPENSSL AM_CFLAGS += -DHAVE_OPENSSL endif if ENABLE_SECCOMP AM_CFLAGS += -DHAVE_SECCOMP \ $(SECCOMP_CFLAGS) endif if ENABLE_SELINUX AM_CFLAGS += -DHAVE_SELINUX endif if ENABLE_DLOG AM_CFLAGS += -DHAVE_DLOG \ $(DLOG_CFLAGS) endif if USE_CONFIGPATH_LOGS AM_CFLAGS += -DUSE_CONFIGPATH_LOGS endif # build the shared library liblxc_la_CFLAGS = -fPIC \ -DPIC \ $(AM_CFLAGS) \ $(LIBLXC_SANITIZER) \ -pthread if ENABLE_ASAN liblxc_la_CFLAGS += -fsanitize=address \ -fno-omit-frame-pointer endif if ENABLE_UBSAN liblxc_la_CFLAGS += -fsanitize=undefined endif liblxc_la_LDFLAGS = -pthread \ -Wl,-no-undefined \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) \ -version-info @LXC_ABI_MAJOR@ liblxc_la_LIBADD = $(CAP_LIBS) \ $(OPENSSL_LIBS) \ $(SELINUX_LIBS) \ $(SECCOMP_LIBS) \ $(DLOG_LIBS) bin_SCRIPTS= if ENABLE_COMMANDS bin_SCRIPTS += cmd/lxc-checkconfig \ cmd/lxc-update-config endif if ENABLE_TOOLS bin_PROGRAMS = lxc-attach \ lxc-autostart \ lxc-cgroup \ lxc-checkpoint \ lxc-copy \ lxc-config \ lxc-console \ lxc-create \ lxc-destroy \ lxc-device \ lxc-execute \ lxc-freeze \ lxc-info \ lxc-ls \ lxc-monitor \ lxc-snapshot \ lxc-start \ lxc-stop \ lxc-top \ lxc-unfreeze \ lxc-unshare \ lxc-wait endif if ENABLE_COMMANDS if ENABLE_TOOLS bin_PROGRAMS += lxc-usernsexec else bin_PROGRAMS = lxc-usernsexec endif sbin_PROGRAMS = init.lxc pkglibexec_PROGRAMS = lxc-monitord \ lxc-user-nic endif AM_LDFLAGS = -Wl,-E if ENABLE_RPATH AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir) endif LDADD = liblxc.la \ @CAP_LIBS@ \ @OPENSSL_LIBS@ \ @SECCOMP_LIBS@ \ @SELINUX_LIBS@ \ @DLOG_LIBS@ if ENABLE_TOOLS lxc_attach_SOURCES = tools/lxc_attach.c \ rexec.c rexec.h \ tools/arguments.c tools/arguments.h lxc_autostart_SOURCES = tools/lxc_autostart.c \ tools/arguments.c tools/arguments.h lxc_cgroup_SOURCES = tools/lxc_cgroup.c \ tools/arguments.c tools/arguments.h lxc_config_SOURCES = tools/lxc_config.c \ tools/arguments.c tools/arguments.h lxc_console_SOURCES = tools/lxc_console.c \ tools/arguments.c tools/arguments.h lxc_destroy_SOURCES = tools/lxc_destroy.c \ tools/arguments.c tools/arguments.h lxc_device_SOURCES = tools/lxc_device.c \ tools/arguments.c tools/arguments.h lxc_execute_SOURCES = tools/lxc_execute.c \ tools/arguments.c tools/arguments.h lxc_freeze_SOURCES = tools/lxc_freeze.c \ tools/arguments.c tools/arguments.h lxc_info_SOURCES = tools/lxc_info.c \ tools/arguments.c tools/arguments.h lxc_monitor_SOURCES = tools/lxc_monitor.c \ macro.h \ tools/arguments.c tools/arguments.h lxc_ls_SOURCES = tools/lxc_ls.c \ tools/arguments.c tools/arguments.h lxc_copy_SOURCES = tools/lxc_copy.c \ tools/arguments.c tools/arguments.h \ storage/storage_utils.c storage/storage_utils.h lxc_start_SOURCES = tools/lxc_start.c \ tools/arguments.c tools/arguments.h lxc_stop_SOURCES = tools/lxc_stop.c \ tools/arguments.c tools/arguments.h lxc_top_SOURCES = tools/lxc_top.c \ tools/arguments.c tools/arguments.h lxc_unfreeze_SOURCES = tools/lxc_unfreeze.c \ tools/arguments.c tools/arguments.h lxc_unshare_SOURCES = tools/lxc_unshare.c \ syscall_numbers.h \ syscall_wrappers.h \ tools/arguments.c tools/arguments.h lxc_wait_SOURCES = tools/lxc_wait.c \ tools/arguments.c tools/arguments.h lxc_create_SOURCES = tools/lxc_create.c \ tools/arguments.c tools/arguments.h \ storage/storage_utils.c storage/storage_utils.h lxc_snapshot_SOURCES = tools/lxc_snapshot.c \ tools/arguments.c tools/arguments.h lxc_checkpoint_SOURCES = tools/lxc_checkpoint.c \ tools/arguments.c tools/arguments.h endif if ENABLE_COMMANDS # Binaries shipping with liblxc init_lxc_SOURCES = cmd/lxc_init.c \ compiler.h \ error.h \ initutils.c initutils.h \ memory_utils.h \ parse.c parse.h \ raw_syscalls.c raw_syscalls.h \ syscall_numbers.h \ string_utils.c string_utils.h init_lxc_LDFLAGS = -pthread lxc_monitord_SOURCES = cmd/lxc_monitord.c \ af_unix.c af_unix.h \ log.c log.h \ mainloop.c mainloop.h \ monitor.c monitor.h \ raw_syscalls.c raw_syscalls.h \ syscall_numbers.h \ utils.c utils.h lxc_user_nic_SOURCES = cmd/lxc_user_nic.c \ ../include/netns_ifaddrs.c ../include/netns_ifaddrs.h \ log.c log.h \ memory_utils.h \ network.c network.h \ parse.c parse.h \ raw_syscalls.c raw_syscalls.h \ syscall_numbers.h \ file_utils.c file_utils.h \ string_utils.c string_utils.h \ syscall_wrappers.h lxc_usernsexec_SOURCES = cmd/lxc_usernsexec.c \ conf.c conf.h \ file_utils.c file_utils.h \ list.h \ log.c log.h \ macro.h \ memory_utils.h \ string_utils.c string_utils.h \ syscall_wrappers.h \ utils.c utils.h endif if ENABLE_TOOLS if !HAVE_GETSUBOPT lxc_copy_SOURCES += tools/include/getsubopt.c tools/include/getsubopt.h endif endif if ENABLE_COMMANDS if HAVE_STATIC_LIBCAP sbin_PROGRAMS += init.lxc.static init_lxc_static_SOURCES = cmd/lxc_init.c \ caps.c caps.h \ error.c error.h \ initutils.c initutils.h \ file_utils.c file_utils.h \ log.c log.h \ macro.h \ memory_utils.h \ namespace.c namespace.h \ string_utils.c string_utils.h if !HAVE_GETLINE if HAVE_FGETLN init_lxc_static_SOURCES += ../include/getline.c ../include/getline.h endif endif if !HAVE_STRLCPY init_lxc_static_SOURCES += ../include/strlcpy.c ../include/strlcpy.h endif if !HAVE_STRLCAT init_lxc_static_SOURCES += ../include/strlcat.c ../include/strlcat.h endif init_lxc_static_LDFLAGS = -all-static -pthread init_lxc_static_LDADD = @CAP_LIBS@ init_lxc_static_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF endif endif if ENABLE_PAM if HAVE_PAM pam_LTLIBRARIES = pam_cgfs.la pam_cgfs_la_SOURCES = pam/pam_cgfs.c \ file_utils.c file_utils.h \ macro.h \ memory_utils.h \ string_utils.c string_utils.h if !HAVE_STRLCAT pam_cgfs_la_SOURCES += ../include/strlcat.c ../include/strlcat.h endif if !HAVE_STRLCPY pam_cgfs_la_SOURCES += ../include/strlcpy.c ../include/strlcpy.h endif pam_cgfs_la_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF pam_cgfs_la_LIBADD = $(AM_LIBS) \ $(PAM_LIBS) \ $(DLOG_LIBS) \ -L$(top_srcdir) pam_cgfs_la_LDFLAGS = $(AM_LDFLAGS) \ -avoid-version \ -module \ -shared \ -Wl,-no-undefined endif endif install-exec-local: install-libLTLIBRARIES mkdir -p $(DESTDIR)$(datadir)/lxc install -c -m 644 lxc.functions $(DESTDIR)$(datadir)/lxc mv $(shell readlink -f $(DESTDIR)$(libdir)/liblxc.so) $(DESTDIR)$(libdir)/liblxc.so.@LXC_ABI@ rm -f $(DESTDIR)$(libdir)/liblxc.so $(DESTDIR)$(libdir)/liblxc.so.1 cd $(DESTDIR)$(libdir); \ ln -sf liblxc.so.@LXC_ABI@ liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)); \ ln -sf liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) liblxc.so if ENABLE_COMMANDS install-exec-hook: chmod u+s $(DESTDIR)$(libexecdir)/lxc/lxc-user-nic endif uninstall-local: $(RM) $(DESTDIR)$(libdir)/liblxc.so* $(RM) $(DESTDIR)$(libdir)/liblxc.a if ENABLE_PAM if HAVE_PAM $(RM) $(DESTDIR)$(pamdir)/pam_cgfs.so* install-data-hook: install-pamLTLIBRARIES $(RM) "$(DESTDIR)$(pamdir)/pam_cgfs.la" $(RM) "$(DESTDIR)$(pamdir)/pam_cgfs.a" endif endif lxc-4.0.2/src/lxc/af_unix.c0000644061062106075000000002023313646120451012410 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "macro.h" #include "memory_utils.h" #include "raw_syscalls.h" #include "utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(af_unix, lxc); static ssize_t lxc_abstract_unix_set_sockaddr(struct sockaddr_un *addr, const char *path) { size_t len; if (!addr || !path) return ret_errno(EINVAL); /* Clear address structure */ memset(addr, 0, sizeof(*addr)); addr->sun_family = AF_UNIX; len = strlen(&path[1]); /* do not enforce \0-termination */ if (len >= INT_MAX || len >= sizeof(addr->sun_path)) return ret_errno(ENAMETOOLONG); /* do not enforce \0-termination */ memcpy(&addr->sun_path[1], &path[1], len); return len; } int lxc_abstract_unix_open(const char *path, int type, int flags) { __do_close int fd = -EBADF; int ret; ssize_t len; struct sockaddr_un addr; fd = socket(PF_UNIX, type | SOCK_CLOEXEC, 0); if (fd < 0) return -1; if (!path) return move_fd(fd); len = lxc_abstract_unix_set_sockaddr(&addr, path); if (len < 0) return -1; ret = bind(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1); if (ret < 0) return -1; if (type == SOCK_STREAM) { ret = listen(fd, 100); if (ret < 0) return -1; } return move_fd(fd); } void lxc_abstract_unix_close(int fd) { close(fd); } int lxc_abstract_unix_connect(const char *path) { __do_close int fd = -EBADF; int ret; ssize_t len; struct sockaddr_un addr; fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) return -1; len = lxc_abstract_unix_set_sockaddr(&addr, path); if (len < 0) return -1; ret = connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1); if (ret < 0) return -1; return move_fd(fd); } int lxc_abstract_unix_send_fds_iov(int fd, int *sendfds, int num_sendfds, struct iovec *iov, size_t iovlen) { __do_free char *cmsgbuf = NULL; int ret; struct msghdr msg; struct cmsghdr *cmsg = NULL; size_t cmsgbufsize = CMSG_SPACE(num_sendfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); cmsgbuf = malloc(cmsgbufsize); if (!cmsgbuf) { errno = ENOMEM; return -1; } msg.msg_control = cmsgbuf; msg.msg_controllen = cmsgbufsize; cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(num_sendfds * sizeof(int)); msg.msg_controllen = cmsg->cmsg_len; memcpy(CMSG_DATA(cmsg), sendfds, num_sendfds * sizeof(int)); msg.msg_iov = iov; msg.msg_iovlen = iovlen; do { ret = sendmsg(fd, &msg, MSG_NOSIGNAL); } while (ret < 0 && errno == EINTR); return ret; } int lxc_abstract_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, size_t size) { char buf[1] = {0}; struct iovec iov = { .iov_base = data ? data : buf, .iov_len = data ? size : sizeof(buf), }; return lxc_abstract_unix_send_fds_iov(fd, sendfds, num_sendfds, &iov, 1); } int lxc_unix_send_fds(int fd, int *sendfds, int num_sendfds, void *data, size_t size) { return lxc_abstract_unix_send_fds(fd, sendfds, num_sendfds, data, size); } static int lxc_abstract_unix_recv_fds_iov(int fd, int *recvfds, int num_recvfds, struct iovec *iov, size_t iovlen) { __do_free char *cmsgbuf = NULL; int ret; struct msghdr msg; size_t cmsgbufsize = CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(num_recvfds * sizeof(int)); memset(&msg, 0, sizeof(msg)); cmsgbuf = malloc(cmsgbufsize); if (!cmsgbuf) return ret_errno(ENOMEM); msg.msg_control = cmsgbuf; msg.msg_controllen = cmsgbufsize; msg.msg_iov = iov; msg.msg_iovlen = iovlen; do { ret = recvmsg(fd, &msg, MSG_CMSG_CLOEXEC); } while (ret < 0 && errno == EINTR); if (ret < 0 || ret == 0) return ret; /* * If SO_PASSCRED is set we will always get a ucred message. */ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_type != SCM_RIGHTS) continue; memset(recvfds, -1, num_recvfds * sizeof(int)); if (cmsg && cmsg->cmsg_len == CMSG_LEN(num_recvfds * sizeof(int)) && cmsg->cmsg_level == SOL_SOCKET) memcpy(recvfds, CMSG_DATA(cmsg), num_recvfds * sizeof(int)); break; } return ret; } int lxc_abstract_unix_recv_fds(int fd, int *recvfds, int num_recvfds, void *data, size_t size) { char buf[1] = {0}; struct iovec iov = { .iov_base = data ? data : buf, .iov_len = data ? size : sizeof(buf), }; return lxc_abstract_unix_recv_fds_iov(fd, recvfds, num_recvfds, &iov, 1); } int lxc_abstract_unix_send_credential(int fd, void *data, size_t size) { struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred = { .pid = lxc_raw_getpid(), .uid = getuid(), .gid = getgid(), }; char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; char buf[1] = {0}; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred)); msg.msg_name = NULL; msg.msg_namelen = 0; iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; return sendmsg(fd, &msg, MSG_NOSIGNAL); } int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size) { struct msghdr msg = {0}; struct iovec iov; struct cmsghdr *cmsg; struct ucred cred; int ret; char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0}; char buf[1] = {0}; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); iov.iov_base = data ? data : buf; iov.iov_len = data ? size : sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = recvmsg(fd, &msg, 0); if (ret <= 0) return ret; cmsg = CMSG_FIRSTHDR(&msg); if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { memcpy(&cred, CMSG_DATA(cmsg), sizeof(cred)); if (cred.uid && (cred.uid != getuid() || cred.gid != getgid())) return log_error_errno(-1, EACCES, "Message denied for '%d/%d'", cred.uid, cred.gid); } return ret; } int lxc_unix_sockaddr(struct sockaddr_un *ret, const char *path) { size_t len; len = strlen(path); if (len == 0) return ret_set_errno(-1, EINVAL); if (path[0] != '/' && path[0] != '@') return ret_set_errno(-1, EINVAL); if (path[1] == '\0') return ret_set_errno(-1, EINVAL); if (len + 1 > sizeof(ret->sun_path)) return ret_set_errno(-1, EINVAL); *ret = (struct sockaddr_un){ .sun_family = AF_UNIX, }; if (path[0] == '@') { memcpy(ret->sun_path + 1, path + 1, len); return (int)(offsetof(struct sockaddr_un, sun_path) + len); } memcpy(ret->sun_path, path, len + 1); return (int)(offsetof(struct sockaddr_un, sun_path) + len + 1); } int lxc_unix_connect_type(struct sockaddr_un *addr, int type) { __do_close int fd = -EBADF; int ret; ssize_t len; fd = socket(AF_UNIX, type | SOCK_CLOEXEC, 0); if (fd < 0) return log_error_errno(-1, errno, "Failed to open new AF_UNIX socket"); if (addr->sun_path[0] == '\0') len = strlen(&addr->sun_path[1]); else len = strlen(&addr->sun_path[0]); ret = connect(fd, (struct sockaddr *)addr, offsetof(struct sockaddr_un, sun_path) + len); if (ret < 0) return log_error_errno(-1, errno, "Failed to bind new AF_UNIX socket"); return move_fd(fd); } int lxc_unix_connect(struct sockaddr_un *addr, int type) { return lxc_unix_connect_type(addr, SOCK_STREAM); } int lxc_socket_set_timeout(int fd, int rcv_timeout, int snd_timeout) { struct timeval out = {0}; int ret; out.tv_sec = snd_timeout; ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&out, sizeof(out)); if (ret < 0) return -1; out.tv_sec = rcv_timeout; ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&out, sizeof(out)); if (ret < 0) return -1; return 0; } lxc-4.0.2/src/lxc/memory_utils.h0000644061062106075000000000340013646120451013511 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_MEMORY_UTILS_H #define __LXC_MEMORY_UTILS_H #include #include #include #include #include #include #include "macro.h" #define define_cleanup_function(type, cleaner) \ static inline void cleaner##_function(type *ptr) \ { \ if (*ptr) \ cleaner(*ptr); \ } #define call_cleaner(cleaner) __attribute__((__cleanup__(cleaner##_function))) #define close_prot_errno_disarm(fd) \ if (fd >= 0) { \ int _e_ = errno; \ close(fd); \ errno = _e_; \ fd = -EBADF; \ } static inline void close_prot_errno_disarm_function(int *fd) { close_prot_errno_disarm(*fd); } #define __do_close call_cleaner(close_prot_errno_disarm) define_cleanup_function(FILE *, fclose); #define __do_fclose call_cleaner(fclose) define_cleanup_function(DIR *, closedir); #define __do_closedir call_cleaner(closedir) #define free_disarm(ptr) \ ({ \ free(ptr); \ ptr = NULL; \ }) static inline void free_disarm_function(void *ptr) { free_disarm(*(void **)ptr); } #define __do_free call_cleaner(free_disarm) static inline void free_string_list(char **list) { if (list) { for (int i = 0; list[i]; i++) free(list[i]); free_disarm(list); } } define_cleanup_function(char **, free_string_list); #define __do_free_string_list call_cleaner(free_string_list) static inline void *memdup(const void *data, size_t len) { void *copy = NULL; copy = len ? malloc(len) : NULL; return copy ? memcpy(copy, data, len) : NULL; } #define zalloc(__size__) (calloc(1, __size__)) #endif /* __LXC_MEMORY_UTILS_H */ lxc-4.0.2/src/lxc/commands_utils.c0000644061062106075000000001153013646120451014000 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include #include "af_unix.h" #include "commands.h" #include "commands_utils.h" #include "config.h" #include "file_utils.h" #include "initutils.h" #include "log.h" #include "lxclock.h" #include "memory_utils.h" #include "monitor.h" #include "state.h" #include "utils.h" lxc_log_define(commands_utils, lxc); int lxc_cmd_sock_rcv_state(int state_client_fd, int timeout) { int ret; struct lxc_msg msg; struct timeval out; if (timeout >= 0) { memset(&out, 0, sizeof(out)); out.tv_sec = timeout; ret = setsockopt(state_client_fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&out, sizeof(out)); if (ret < 0) return log_error_errno(-1, errno, "Failed to set %ds timeout on container state socket", timeout); } memset(&msg, 0, sizeof(msg)); ret = lxc_recv_nointr(state_client_fd, &msg, sizeof(msg), 0); if (ret < 0) return log_error_errno(-1, errno, "Failed to receive message"); return log_trace(msg.value, "Received state %s from state client %d", lxc_state2str(msg.value), state_client_fd); } /* Register a new state client and retrieve state from command socket. */ int lxc_cmd_sock_get_state(const char *name, const char *lxcpath, lxc_state_t states[MAX_STATE], int timeout) { __do_close int state_client_fd = -EBADF; int ret; ret = lxc_cmd_add_state_client(name, lxcpath, states, &state_client_fd); if (ret < 0) return ret_errno(EINVAL); if (ret < MAX_STATE) return ret; if (state_client_fd < 0) return ret_errno(EBADF); return lxc_cmd_sock_rcv_state(state_client_fd, timeout); } int lxc_make_abstract_socket_name(char *path, size_t pathlen, const char *lxcname, const char *lxcpath, const char *hashed_sock_name, const char *suffix) { __do_free char *tmppath = NULL; const char *name; char *offset; size_t len; size_t tmplen; uint64_t hash; int ret; if (!path) return -1; offset = &path[1]; /* -2 here because this is an abstract unix socket so it needs a * leading \0, and we null terminate, so it needs a trailing \0. * Although null termination isn't required by the API, we do it anyway * because we print the sockname out sometimes. */ len = pathlen - 2; name = lxcname; if (!name) name = ""; if (hashed_sock_name != NULL) { ret = snprintf(offset, len, "lxc/%s/%s", hashed_sock_name, suffix); if (ret < 0 || (size_t)ret >= len) return log_error_errno(-1, errno, "Failed to create abstract socket name"); return 0; } if (!lxcpath) { lxcpath = lxc_global_config_value("lxc.lxcpath"); if (!lxcpath) return log_error(-1, "Failed to allocate memory"); } ret = snprintf(offset, len, "%s/%s/%s", lxcpath, name, suffix); if (ret < 0) return log_error_errno(-1, errno, "Failed to create abstract socket name"); /* * ret >= len. This means lxcpath and name are too long. We need to * hash both. */ if (ret >= len) { tmplen = strlen(name) + strlen(lxcpath) + 2; tmppath = must_realloc(NULL, tmplen); ret = snprintf(tmppath, tmplen, "%s/%s", lxcpath, name); if (ret < 0 || (size_t)ret >= tmplen) return log_error_errno(-1, errno, "Failed to create abstract socket name"); hash = fnv_64a_buf(tmppath, ret, FNV1A_64_INIT); ret = snprintf(offset, len, "lxc/%016" PRIx64 "/%s", hash, suffix); if (ret < 0 || (size_t)ret >= len) return log_error_errno(-1, errno, "Failed to create abstract socket name"); } return 0; } int lxc_cmd_connect(const char *name, const char *lxcpath, const char *hashed_sock_name, const char *suffix) { int ret, client_fd; char path[LXC_AUDS_ADDR_LEN] = {0}; ret = lxc_make_abstract_socket_name(path, sizeof(path), name, lxcpath, hashed_sock_name, suffix); if (ret < 0) return -1; /* Get new client fd. */ client_fd = lxc_abstract_unix_connect(path); if (client_fd < 0) return -1; return client_fd; } int lxc_add_state_client(int state_client_fd, struct lxc_handler *handler, lxc_state_t states[MAX_STATE]) { __do_free struct lxc_state_client *newclient = NULL; __do_free struct lxc_list *tmplist = NULL; int state; newclient = malloc(sizeof(*newclient)); if (!newclient) return -ENOMEM; /* copy requested states */ memcpy(newclient->states, states, sizeof(newclient->states)); newclient->clientfd = state_client_fd; tmplist = malloc(sizeof(*tmplist)); if (!tmplist) return -ENOMEM; state = handler->state; if (states[state] != 1) { lxc_list_add_elem(tmplist, newclient); lxc_list_add_tail(&handler->conf->state_clients, tmplist); } else { return state; } move_ptr(newclient); move_ptr(tmplist); TRACE("Added state client fd %d to state client list", state_client_fd); return MAX_STATE; } lxc-4.0.2/src/lxc/lxclock.c0000644061062106075000000001435113646120451012422 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include "config.h" #include "log.h" #include "lxclock.h" #include "utils.h" #ifdef MUTEX_DEBUGGING #include #endif #define MAX_STACKDEPTH 25 lxc_log_define(lxclock, lxc); #ifdef MUTEX_DEBUGGING static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; static inline void dump_stacktrace(void) { void *array[MAX_STACKDEPTH]; size_t size; char **strings; size_t i; size = backtrace(array, MAX_STACKDEPTH); strings = backtrace_symbols(array, size); /* Using fprintf here as our logging module is not thread safe. */ fprintf(stderr, "\tObtained %zu stack frames\n", size); for (i = 0; i < size; i++) fprintf(stderr, "\t\t%s\n", strings[i]); free(strings); } #else static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; static inline void dump_stacktrace(void) {;} #endif static void lock_mutex(pthread_mutex_t *l) { int ret; ret = pthread_mutex_lock(l); if (ret != 0) { SYSERROR("Failed to acquire mutex"); dump_stacktrace(); _exit(EXIT_FAILURE); } } static void unlock_mutex(pthread_mutex_t *l) { int ret; ret = pthread_mutex_unlock(l); if (ret != 0) { SYSERROR("Failed to release mutex"); dump_stacktrace(); _exit(EXIT_FAILURE); } } static char *lxclock_name(const char *p, const char *n) { int ret; size_t len; char *dest, *rundir; /* lockfile will be: * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root * or * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root */ /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */ len = STRLITERALLEN("/lxc/lock/") + strlen(n) + strlen(p) + 3; rundir = get_rundir(); if (!rundir) return NULL; len += strlen(rundir); dest = malloc(len); if (!dest) { free(rundir); return NULL; } ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p); if (ret < 0 || (size_t)ret >= len) { free(dest); free(rundir); return NULL; } ret = mkdir_p(dest, 0755); if (ret < 0) { free(dest); free(rundir); return NULL; } ret = snprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n); free(rundir); if (ret < 0 || (size_t)ret >= len) { free(dest); return NULL; } return dest; } static sem_t *lxc_new_unnamed_sem(void) { int ret; sem_t *s; s = malloc(sizeof(*s)); if (!s) return NULL; ret = sem_init(s, 0, 1); if (ret < 0) { free(s); return NULL; } return s; } struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name) { struct lxc_lock *l; l = malloc(sizeof(*l)); if (!l) goto on_error; if (!name) { l->type = LXC_LOCK_ANON_SEM; l->u.sem = lxc_new_unnamed_sem(); if (!l->u.sem) { free(l); l = NULL; } goto on_error; } l->type = LXC_LOCK_FLOCK; l->u.f.fname = lxclock_name(lxcpath, name); if (!l->u.f.fname) { if (!name) free(l->u.sem); free(l); l = NULL; goto on_error; } l->u.f.fd = -1; on_error: return l; } int lxclock(struct lxc_lock *l, int timeout) { struct flock lk; int ret = -1, saved_errno = errno; switch(l->type) { case LXC_LOCK_ANON_SEM: if (!timeout) { ret = sem_wait(l->u.sem); if (ret < 0) saved_errno = errno; } else { struct timespec ts; ret = clock_gettime(CLOCK_REALTIME, &ts); if (ret < 0) { ret = -2; goto on_error; } ts.tv_sec += timeout; ret = sem_timedwait(l->u.sem, &ts); if (ret < 0) saved_errno = errno; } break; case LXC_LOCK_FLOCK: ret = -2; if (timeout) { ERROR("Timeouts are not supported with file locks"); goto on_error; } if (!l->u.f.fname) { ERROR("No filename set for file lock"); goto on_error; } if (l->u.f.fd == -1) { l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR); if (l->u.f.fd == -1) { SYSERROR("Failed to open \"%s\"", l->u.f.fname); saved_errno = errno; goto on_error; } } memset(&lk, 0, sizeof(struct flock)); lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk); if (ret < 0) { if (errno == EINVAL) ret = flock(l->u.f.fd, LOCK_EX); saved_errno = errno; } break; } on_error: errno = saved_errno; return ret; } int lxcunlock(struct lxc_lock *l) { struct flock lk; int ret = 0, saved_errno = errno; switch (l->type) { case LXC_LOCK_ANON_SEM: if (!l->u.sem) { ret = -2; } else { ret = sem_post(l->u.sem); saved_errno = errno; } break; case LXC_LOCK_FLOCK: if (l->u.f.fd != -1) { memset(&lk, 0, sizeof(struct flock)); lk.l_type = F_UNLCK; lk.l_whence = SEEK_SET; ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk); if (ret < 0) { if (errno == EINVAL) ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB); saved_errno = errno; } close(l->u.f.fd); l->u.f.fd = -1; } else { ret = -2; } break; } errno = saved_errno; return ret; } /* * lxc_putlock() is only called when a container_new() fails, * or during container_put(), which is already guaranteed to * only be done by one task. * So the only exclusion we need to provide here is for regular * thread safety (i.e. file descriptor table changes). */ void lxc_putlock(struct lxc_lock *l) { if (!l) return; switch(l->type) { case LXC_LOCK_ANON_SEM: if (l->u.sem) { sem_destroy(l->u.sem); free(l->u.sem); l->u.sem = NULL; } break; case LXC_LOCK_FLOCK: if (l->u.f.fd != -1) { close(l->u.f.fd); l->u.f.fd = -1; } free(l->u.f.fname); l->u.f.fname = NULL; break; } free(l); } void process_lock(void) { lock_mutex(&thread_mutex); } void process_unlock(void) { unlock_mutex(&thread_mutex); } int container_mem_lock(struct lxc_container *c) { return lxclock(c->privlock, 0); } void container_mem_unlock(struct lxc_container *c) { lxcunlock(c->privlock); } int container_disk_lock(struct lxc_container *c) { int ret; ret = lxclock(c->privlock, 0); if (ret < 0) return ret; ret = lxclock(c->slock, 0); if (ret < 0) { lxcunlock(c->privlock); return ret; } return 0; } void container_disk_unlock(struct lxc_container *c) { lxcunlock(c->slock); lxcunlock(c->privlock); } lxc-4.0.2/src/lxc/string_utils.c0000644061062106075000000004376313646120451013522 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "lxclock.h" #include "macro.h" #include "memory_utils.h" #include "namespace.h" #include "parse.h" #include "string_utils.h" #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif #ifndef HAVE_STRLCAT #include "include/strlcat.h" #endif 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); for (;;) { char *arg = va_arg(ap2, char *); if (!arg) break; count++; } va_end(ap2); result = calloc(count, sizeof(char *)); if (!result) return NULL; count = skip; for (;;) { 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; } 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); } 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; } bool lxc_string_in_array(const char *needle, const char **haystack) { for (; haystack && *haystack; haystack++) if (!strcmp(needle, *haystack)) return true; return false; } 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; } 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; } 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; } char *lxc_append_paths(const char *first, const char *second) { int ret; size_t len; char *result = NULL; int pattern_type = 0; len = strlen(first) + strlen(second) + 1; if (second[0] != '/') { len += 1; pattern_type = 1; } result = calloc(1, len); if (!result) return NULL; if (pattern_type == 0) ret = snprintf(result, len, "%s%s", first, second); else ret = snprintf(result, len, "%s/%s", first, second); if (ret < 0 || (size_t)ret >= len) { free(result); return NULL; } return result; } bool lxc_string_in_list(const char *needle, const char *haystack, char _sep) { __do_free char *str = NULL; char *token; char sep[2] = { _sep, '\0' }; if (!haystack || !needle) return 0; str = must_copy_string(haystack); lxc_iterate_parts(token, str, sep) if (strcmp(needle, token) == 0) return 1; return 0; } char **lxc_string_split(const char *string, char _sep) { __do_free char *str = NULL; char *token; char sep[2] = {_sep, '\0'}; char **tmp = NULL, **result = NULL; size_t result_capacity = 0; size_t result_count = 0; int r, saved_errno; if (!string) return calloc(1, sizeof(char *)); str = must_copy_string(string); 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; } 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; } /* * Given a a string 'one two "three four"', split into three words, * one, two, and "three four" */ 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 *)); } char **lxc_string_split_and_trim(const char *string, char _sep) { __do_free char *str = NULL; char *token; 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; if (!string) return calloc(1, sizeof(char *)); str = must_copy_string(string); 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; } 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); } 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; } size_t lxc_array_len(void **array) { void **p; size_t result = 0; for (p = array; p && *p; p++) result++; return result; } 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; } 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; } 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; } 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; } 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; } 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 == UINT64_MAX) return -ERANGE; if (err == numstr || *err != '\0') return -EINVAL; *converted = u; return 0; } 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; } 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; } 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; } char *must_concat(size_t *len, 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'; if (len) *len = cur_len; return dest; } char *must_make_path(const char *first, ...) { va_list args; char *cur, *dest; size_t full_len = strlen(first); size_t buf_len; size_t cur_len; dest = must_copy_string(first); cur_len = full_len; va_start(args, first); while ((cur = va_arg(args, char *)) != NULL) { buf_len = strlen(cur); full_len += buf_len; if (cur[0] != '/') full_len++; dest = must_realloc(dest, full_len + 1); if (cur[0] != '/') { memcpy(dest + cur_len, "/", 1); cur_len++; } memcpy(dest + cur_len, cur, buf_len); cur_len += buf_len; } va_end(args); dest[cur_len] = '\0'; return dest; } char *must_append_path(char *first, ...) { char *cur; size_t full_len; va_list args; char *dest = first; size_t buf_len; size_t cur_len; full_len = strlen(first); cur_len = full_len; va_start(args, first); while ((cur = va_arg(args, char *)) != NULL) { buf_len = strlen(cur); full_len += buf_len; if (cur[0] != '/') full_len++; dest = must_realloc(dest, full_len + 1); if (cur[0] != '/') { memcpy(dest + cur_len, "/", 1); cur_len++; } memcpy(dest + cur_len, cur, buf_len); cur_len += buf_len; } va_end(args); dest[cur_len] = '\0'; return dest; } char *must_copy_string(const char *entry) { char *ret; if (!entry) return NULL; do { ret = strdup(entry); } while (!ret); return ret; } void *must_realloc(void *orig, size_t sz) { void *ret; do { ret = realloc(orig, sz); } while (!ret); return ret; } 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[INTTYPE_TO_STRLEN(int64_t)]; 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; } void remove_trailing_newlines(char *l) { char *p = l; while (*p) p++; while (--p >= l && *p == '\n') *p = '\0'; } int lxc_char_left_gc(const char *buffer, size_t len) { size_t i; for (i = 0; i < len; i++) { if (buffer[i] == ' ' || buffer[i] == '\t') continue; return i; } return 0; } int lxc_char_right_gc(const char *buffer, size_t len) { int i; for (i = len - 1; i >= 0; i--) { if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n' || buffer[i] == '\0') continue; return i + 1; } return 0; } char *lxc_trim_whitespace_in_place(char *buffer) { buffer += lxc_char_left_gc(buffer, strlen(buffer)); buffer[lxc_char_right_gc(buffer, strlen(buffer))] = '\0'; return buffer; } int lxc_is_line_empty(const char *line) { int i; size_t len = strlen(line); for (i = 0; i < len; i++) if (line[i] != ' ' && line[i] != '\t' && line[i] != '\n' && line[i] != '\r' && line[i] != '\f' && line[i] != '\0') return 0; return 1; } void remove_trailing_slashes(char *p) { int l = strlen(p); while (--l >= 0 && (p[l] == '/' || p[l] == '\n')) p[l] = '\0'; } lxc-4.0.2/src/lxc/lxc.functions.in0000644061062106075000000000047213646120451013743 00000000000000# SPDX-License-Identifier: LGPL-2.1+ # This file contains helpers for the various lxc shell scripts globalconf=@LXC_GLOBAL_CONF@ bindir=@BINDIR@ templatedir=@LXCTEMPLATEDIR@ lxcinitdir=@LXCINITDIR@ lxc_path=`lxc-config lxc.lxcpath` lxc_vg=`lxc-config lxc.bdev.lvm.vg` lxc_zfsroot=`lxc-config lxc.bdev.zfs.root` lxc-4.0.2/src/lxc/confile_utils.h0000644061062106075000000000656013646120451013632 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_CONFILE_UTILS_H #define __LXC_CONFILE_UTILS_H #include #include "conf.h" #include "confile_utils.h" #define strprint(str, inlen, ...) \ do { \ if (str) \ len = snprintf(str, inlen, ##__VA_ARGS__); \ else \ len = snprintf((char *){""}, 0, ##__VA_ARGS__); \ if (len < 0) { \ SYSERROR("failed to create string"); \ return -1; \ }; \ fulllen += len; \ if (inlen > 0) { \ if (str) \ str += len; \ inlen -= len; \ if (inlen < 0) \ inlen = 0; \ } \ } while (0); extern int parse_idmaps(const char *idmap, char *type, unsigned long *nsid, unsigned long *hostid, unsigned long *range); extern bool lxc_config_value_empty(const char *value); extern struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail); extern struct lxc_netdev * lxc_get_netdev_by_idx(struct lxc_conf *conf, unsigned int idx, bool allocate); extern void lxc_log_configured_netdevs(const struct lxc_conf *conf); extern bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx); extern void lxc_free_networks(struct lxc_list *networks); extern int lxc_veth_mode_to_flag(int *mode, const char *value); extern int lxc_macvlan_mode_to_flag(int *mode, const char *value); extern char *lxc_macvlan_flag_to_mode(int mode); extern int lxc_ipvlan_mode_to_flag(int *mode, const char *value); extern char *lxc_ipvlan_flag_to_mode(int mode); extern int lxc_ipvlan_isolation_to_flag(int *mode, const char *value); extern char *lxc_ipvlan_flag_to_isolation(int mode); extern int set_config_string_item(char **conf_item, const char *value); extern int set_config_string_item_max(char **conf_item, const char *value, size_t max); extern int set_config_path_item(char **conf_item, const char *value); extern int set_config_bool_item(bool *conf_item, const char *value, bool empty_conf_action); extern int config_ip_prefix(struct in_addr *addr); extern int network_ifname(char *valuep, const char *value, size_t size); extern void rand_complete_hwaddr(char *hwaddr); extern bool lxc_config_net_is_hwaddr(const char *line); extern bool new_hwaddr(char *hwaddr); extern int lxc_get_conf_str(char *retv, int inlen, const char *value); extern int lxc_get_conf_bool(struct lxc_conf *c, char *retv, int inlen, bool v); extern int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v); extern int lxc_get_conf_size_t(struct lxc_conf *c, char *retv, int inlen, size_t v); extern int lxc_get_conf_uint64(struct lxc_conf *c, char *retv, int inlen, uint64_t v); extern int lxc_inherit_namespace(const char *lxcname_or_pid, const char *lxcpath, const char *namespace); extern int sig_parse(const char *signame); #endif /* __LXC_CONFILE_UTILS_H */ lxc-4.0.2/src/lxc/terminal.h0000644061062106075000000002000513646120451012574 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_TERMINAL_H #define __LXC_TERMINAL_H #include #include #include "list.h" #include "macro.h" #include "ringbuf.h" struct lxc_container; struct lxc_conf; struct lxc_epoll_descr; struct lxc_terminal_info { /* the path name of the slave side */ char name[PATH_MAX]; /* the file descriptor of the master */ int master; /* the file descriptor of the slave */ int slave; /* whether the terminal is currently used */ int busy; }; struct lxc_terminal_state { struct lxc_list node; int stdinfd; int stdoutfd; int masterfd; /* Escape sequence to use for exiting the terminal. A single char can * be specified. The terminal can then exited by doing: Ctrl + * specified_char + q. This field is checked by * lxc_terminal_stdin_cb(). Set to -1 to disable exiting the terminal * via a escape sequence. */ int escape; /* Used internally by lxc_terminal_stdin_cb() to check whether an * escape sequence has been received. */ int saw_escape; /* File descriptor that accepts signals. If set to -1 no signal handler * could be installed. This also means that the sigset_t oldmask member * is meaningless. */ int sigfd; sigset_t oldmask; }; struct lxc_terminal { int slave; int master; int peer; struct lxc_terminal_info proxy; struct lxc_epoll_descr *descr; char *path; char name[PATH_MAX]; struct termios *tios; struct lxc_terminal_state *tty_state; struct /* lxc_terminal_log */ { /* size of the log file */ uint64_t log_size; /* path to the log file */ char *log_path; /* fd to the log file */ int log_fd; /* whether the log file will be rotated */ unsigned int log_rotate; }; struct /* lxc_terminal_ringbuf */ { /* size of the ringbuffer */ uint64_t buffer_size; /* the in-memory ringbuffer */ struct lxc_ringbuf ringbuf; }; }; /** * lxc_terminal_allocate: allocate the console or a tty * * @conf : the configuration of the container to allocate from * @sockfd : the socket fd whose remote side when closed, will be an * indication that the console or tty is no longer in use * @ttyreq : the tty requested to be opened, -1 for any, 0 for the console */ extern int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttynum); /** * Create a new terminal: * - calls openpty() to allocate a master/slave pair * - sets the FD_CLOEXEC flag on the master/slave fds * - allocates either the current controlling terminal (default) or a user * specified terminal as proxy for the newly created master/slave pair * - sets up SIGWINCH handler, winsz, and new terminal settings * (Handlers for SIGWINCH and I/O are not registered in a mainloop.) */ extern int lxc_terminal_create(struct lxc_terminal *console); /** * lxc_terminal_setup: Create a new terminal. * - In addition to lxc_terminal_create() also sets up logging. */ extern int lxc_terminal_setup(struct lxc_conf *); /** * Delete a terminal created via lxc_terminal_create() or lxc_terminal_setup(): * Note, registered handlers are not automatically deleted. */ extern void lxc_terminal_delete(struct lxc_terminal *); /** * lxc_terminal_free: mark the terminal as unallocated and free any resources * allocated by lxc_terminal_allocate(). * * @conf : the configuration of the container whose tty was closed * @fd : the socket fd whose remote side was closed, which indicated * the terminal is no longer in use. this is used to match * which terminal is being freed. */ extern void lxc_terminal_free(struct lxc_conf *conf, int fd); /** * Register terminal event handlers in an open mainloop. */ extern int lxc_terminal_mainloop_add(struct lxc_epoll_descr *, struct lxc_terminal *); /** * Handle SIGWINCH events on the allocated terminals. */ extern void lxc_terminal_sigwinch(int sig); /** * Connect to one of the ttys given to the container via lxc.tty.max. * - allocates either the current controlling terminal (default) or a user specified * terminal as proxy terminal for the containers tty * - sets up SIGWINCH handler, winsz, and new terminal settings * - opens mainloop * - registers SIGWINCH, I/O handlers in the mainloop * - performs all necessary cleanup operations */ extern int lxc_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape); /** * Allocate one of the tty given to the container via lxc.tty.max. Returns an * open fd to the allocated tty. * Set ttynum to -1 to allocate the first available tty, or to a value within * the range specified by lxc.tty.max to allocate a specific tty. */ extern int lxc_terminal_getfd(struct lxc_container *c, int *ttynum, int *masterfd); /** * Make fd a duplicate of the standard file descriptors. The fd is made a * duplicate of a specific standard file descriptor iff the standard file * descriptor refers to a terminal. */ extern int lxc_terminal_set_stdfds(int fd); /** * Handler for events on the stdin fd of the terminal. To be registered via the * corresponding functions declared and defined in mainloop.{c,h} or * lxc_terminal_mainloop_add(). * This function exits the loop cleanly when an EPOLLHUP event is received. */ extern int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr); /** * Handler for events on the master fd of the terminal. To be registered via * the corresponding functions declared and defined in mainloop.{c,h} or * lxc_terminal_mainloop_add(). * This function exits the loop cleanly when an EPOLLHUP event is received. */ extern int lxc_terminal_master_cb(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr); /** * Setup new terminal properties. The old terminal settings are stored in * oldtios. */ extern int lxc_setup_tios(int fd, struct termios *oldtios); /** * lxc_terminal_winsz: propagate winsz from one terminal to another * * @srcfd * - terminal to get size from (typically a slave pty) * @dstfd * - terminal to set size on (typically a master pty) */ extern void lxc_terminal_winsz(int srcfd, int dstfd); /* * lxc_terminal_signal_init: install signal handler * * @srcfd * - src for winsz in SIGWINCH handler * @dstfd * - dst for winsz in SIGWINCH handler * * Returns lxc_terminal_state structure on success or NULL on failure. The * sigfd member of the returned lxc_terminal_state can be * select()/poll()ed/epoll()ed on (i.e. added to a mainloop) for signals. * * Must be called with process_lock held to protect the lxc_ttys list, or from * a non-threaded context. * * Note that the signal handler isn't installed as a classic asynchronous * handler, rather signalfd(2) is used so that we can handle the signal when * we're ready for it. This avoids deadlocks since a signal handler (ie * lxc_terminal_sigwinch()) would need to take the thread mutex to prevent * lxc_ttys list corruption, but using the fd we can provide the tty_state * needed to the callback (lxc_terminal_signalfd_cb()). * * This function allocates memory. It is up to the caller to free it. */ extern struct lxc_terminal_state *lxc_terminal_signal_init(int srcfd, int dstfd); /** * Handler for signal events. To be registered via the corresponding functions * declared and defined in mainloop.{c,h} or lxc_terminal_mainloop_add(). */ extern int lxc_terminal_signalfd_cb(int fd, uint32_t events, void *cbdata, struct lxc_epoll_descr *descr); extern int lxc_terminal_write_ringbuffer(struct lxc_terminal *terminal); extern int lxc_terminal_create_log_file(struct lxc_terminal *terminal); extern int lxc_terminal_io_cb(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr); extern int lxc_make_controlling_terminal(int fd); extern int lxc_terminal_prepare_login(int fd); extern void lxc_terminal_conf_free(struct lxc_terminal *terminal); extern void lxc_terminal_info_init(struct lxc_terminal_info *terminal); extern void lxc_terminal_init(struct lxc_terminal *terminal); extern int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal); #endif /* __LXC_TERMINAL_H */ lxc-4.0.2/src/lxc/conf.h0000644061062106075000000003404313646120451011715 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_CONF_H #define __LXC_CONF_H #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include "compiler.h" #include "config.h" #include "list.h" #include "lxcseccomp.h" #include "ringbuf.h" #include "start.h" #include "terminal.h" #if HAVE_SYS_RESOURCE_H #include #endif #if HAVE_SCMP_FILTER_CTX typedef void * scmp_filter_ctx; #endif /* worth moving to configure.ac? */ #define subuidfile "/etc/subuid" #define subgidfile "/etc/subgid" /* * Defines a generic struct to configure the control group. It is up to the * programmer to specify the right subsystem. * @subsystem : the targeted subsystem * @value : the value to set * @version : The version of the cgroup filesystem on which the controller * resides. * * @controllers : The controllers to use for this container. * @dir : The name of the directory containing the container's cgroup. * Not that this is a per-container setting. */ struct lxc_cgroup { union { /* information about a specific controller */ struct /* controller */ { int version; char *subsystem; char *value; }; /* meta information about cgroup configuration */ struct /* meta */ { char *controllers; char *dir; bool relative; }; }; }; #if !HAVE_SYS_RESOURCE_H #define RLIM_INFINITY ((unsigned long)-1) struct rlimit { unsigned long rlim_cur; unsigned long rlim_max; }; #endif /* * Defines a structure to configure resource limits to set via setrlimit(). * @resource : the resource name in lowercase without the RLIMIT_ prefix * @limit : the limit to set */ struct lxc_limit { char *resource; struct rlimit limit; }; enum idtype { ID_TYPE_UID, ID_TYPE_GID }; /* * Defines a structure to configure kernel parameters at runtime. * @key : the kernel parameters will be configured without the "lxc.sysctl" prefix * @value : the value to set */ struct lxc_sysctl { char *key; char *value; }; /* * Defines a structure to configure proc filesystem at runtime. * @filename : the proc filesystem will be configured without the "lxc.proc" prefix * @value : the value to set */ struct lxc_proc { char *filename; char *value; }; /* * id_map is an id map entry. Form in confile is: * lxc.idmap = u 0 9800 100 * lxc.idmap = u 1000 9900 100 * lxc.idmap = g 0 9800 100 * lxc.idmap = g 1000 9900 100 * meaning the container can use uids and gids 0-99 and 1000-1099, * with [ug]id 0 mapping to [ug]id 9800 on the host, and [ug]id 1000 to * [ug]id 9900 on the host. */ struct id_map { enum idtype idtype; unsigned long hostid, nsid, range; }; /* Defines the number of tty configured and contains the * instantiated ptys * @max = number of configured ttys */ struct lxc_tty_info { size_t max; char *dir; char *tty_names; struct lxc_terminal_info *tty; }; /* Defines a structure to store the rootfs location, the * optionals pivot_root, rootfs mount paths * @path : the rootfs source (directory or device) * @mount : where it is mounted * @bev_type : optional backing store type * @options : mount options * @mountflags : the portion of @options that are flags * @data : the portion of @options that are not flags * @managed : whether it is managed by LXC */ struct lxc_rootfs { char *path; char *mount; char *bdev_type; char *options; unsigned long mountflags; char *data; bool managed; }; /* * Automatic mounts for LXC to perform inside the container */ enum { LXC_AUTO_PROC_RW = 0x001, /* /proc read-write */ LXC_AUTO_PROC_MIXED = 0x002, /* /proc/sys and /proc/sysrq-trigger read-only */ LXC_AUTO_PROC_MASK = 0x003, LXC_AUTO_SYS_RW = 0x004, /* /sys */ LXC_AUTO_SYS_RO = 0x008, /* /sys read-only */ LXC_AUTO_SYS_MIXED = 0x00C, /* /sys read-only and /sys/class/net read-write */ LXC_AUTO_SYS_MASK = 0x00C, LXC_AUTO_CGROUP_RO = 0x010, /* /sys/fs/cgroup (partial mount, read-only) */ LXC_AUTO_CGROUP_RW = 0x020, /* /sys/fs/cgroup (partial mount, read-write) */ LXC_AUTO_CGROUP_MIXED = 0x030, /* /sys/fs/cgroup (partial mount, paths r/o, cgroup r/w) */ LXC_AUTO_CGROUP_FULL_RO = 0x040, /* /sys/fs/cgroup (full mount, read-only) */ LXC_AUTO_CGROUP_FULL_RW = 0x050, /* /sys/fs/cgroup (full mount, read-write) */ LXC_AUTO_CGROUP_FULL_MIXED = 0x060, /* /sys/fs/cgroup (full mount, parent r/o, own r/w) */ /* * These are defined in such a way as to retain binary compatibility * with earlier versions of this code. If the previous mask is applied, * both of these will default back to the _MIXED variants, which is * safe. */ LXC_AUTO_CGROUP_NOSPEC = 0x0B0, /* /sys/fs/cgroup (partial mount, r/w or mixed, depending on caps) */ LXC_AUTO_CGROUP_FULL_NOSPEC = 0x0E0, /* /sys/fs/cgroup (full mount, r/w or mixed, depending on caps) */ LXC_AUTO_CGROUP_FORCE = 0x100, /* mount cgroups even when cgroup namespaces are supported */ LXC_AUTO_CGROUP_MASK = 0x1F0, /* all known cgroup options, doe not contain LXC_AUTO_CGROUP_FORCE */ LXC_AUTO_SHMOUNTS = 0x200, /* shared mount point */ LXC_AUTO_SHMOUNTS_MASK = 0x200, /* shared mount point mask */ LXC_AUTO_ALL_MASK = 0x1FF, /* all known settings */ }; enum lxchooks { LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV, LXCHOOK_START, LXCHOOK_STOP, LXCHOOK_POSTSTOP, LXCHOOK_CLONE, LXCHOOK_DESTROY, LXCHOOK_START_HOST, NUM_LXC_HOOKS }; extern char *lxchook_names[NUM_LXC_HOOKS]; struct lxc_state_client { int clientfd; lxc_state_t states[MAX_STATE]; }; enum { LXC_BPF_DEVICE_CGROUP_LOCAL_RULE = -1, LXC_BPF_DEVICE_CGROUP_WHITELIST = 0, LXC_BPF_DEVICE_CGROUP_BLACKLIST = 1, }; struct device_item { char type; int major; int minor; char access[4]; int allow; /* * LXC_BPF_DEVICE_CGROUP_LOCAL_RULE -> no global rule * LXC_BPF_DEVICE_CGROUP_WHITELIST -> whitelist (deny all) * LXC_BPF_DEVICE_CGROUP_BLACKLIST -> blacklist (allow all) */ int global_rule; }; struct lxc_conf { /* Pointer to the name of the container. Do not free! */ const char *name; bool is_execute; int reboot; signed long personality; struct utsname *utsname; struct { struct lxc_list cgroup; struct lxc_list cgroup2; struct bpf_program *cgroup2_devices; /* This should be reimplemented as a hashmap. */ struct lxc_list devices; }; struct { struct lxc_list id_map; /* * Pointer to the idmap entry for the container's root uid in * the id_map list. Do not free! */ const struct id_map *root_nsuid_map; /* * Pointer to the idmap entry for the container's root gid in * the id_map list. Do not free! */ const struct id_map *root_nsgid_map; }; struct lxc_list network; struct { char *fstab; int auto_mounts; struct lxc_list mount_list; }; struct lxc_list caps; struct lxc_list keepcaps; /* /dev/tty devices */ struct lxc_tty_info ttys; /* /dev/console device */ struct lxc_terminal console; /* maximum pty devices allowed by devpts mount */ size_t pty_max; /* set to true when rootfs has been setup */ bool rootfs_setup; struct lxc_rootfs rootfs; bool close_all_fds; struct { unsigned int hooks_version; struct lxc_list hooks[NUM_LXC_HOOKS]; }; char *lsm_aa_profile; char *lsm_aa_profile_computed; bool lsm_aa_profile_created; unsigned int lsm_aa_allow_nesting; unsigned int lsm_aa_allow_incomplete; struct lxc_list lsm_aa_raw; char *lsm_se_context; char *lsm_se_keyring_context; bool keyring_disable_session; bool tmp_umount_proc; struct lxc_seccomp seccomp; int maincmd_fd; unsigned int autodev; /* if 1, mount and fill a /dev at start */ int autodevtmpfssize; /* size of the /dev tmpfs */ int haltsignal; /* signal used to halt container */ int rebootsignal; /* signal used to reboot container */ int stopsignal; /* signal used to hard stop container */ char *rcfile; /* Copy of the top level rcfile we read */ /* Logfile and loglevel can be set in a container config file. Those * function as defaults. The defaults can be overridden by command line. * However we don't want the command line specified values to be saved * on c->save_config(). So we store the config file specified values * here. */ char *logfile; /* the logfile as specified in config */ int loglevel; /* loglevel as specified in config (if any) */ int logfd; unsigned int start_auto; unsigned int start_delay; int start_order; struct lxc_list groups; int nbd_idx; /* unshare the mount namespace in the monitor */ unsigned int monitor_unshare; unsigned int monitor_signal_pdeath; /* list of included files */ struct lxc_list includes; /* config entries which are not "lxc.*" are aliens */ struct lxc_list aliens; /* list of environment variables we'll add to the container when * started */ struct lxc_list environment; /* text representation of the config file */ char *unexpanded_config; size_t unexpanded_len; size_t unexpanded_alloced; /* default command for lxc-execute */ char *execute_cmd; /* init command */ char *init_cmd; /* if running in a new user namespace, the UID/GID that init and COMMAND * should run under when using lxc-execute */ uid_t init_uid; gid_t init_gid; /* indicator if the container will be destroyed on shutdown */ unsigned int ephemeral; /* The facility to pass to syslog. Let's users establish as what type of * program liblxc is supposed to write to the syslog. */ char *syslog; /* Whether PR_SET_NO_NEW_PRIVS will be set for the container. */ bool no_new_privs; /* RLIMIT_* limits */ struct lxc_list limits; /* Contains generic info about the cgroup configuration for this * container. Note that struct lxc_cgroup contains a union. It is only * valid to access the members of the anonymous "meta" struct within * that union. */ struct lxc_cgroup cgroup_meta; struct { int ns_clone; int ns_keep; char *ns_share[LXC_NS_MAX]; }; /* init working directory */ char *init_cwd; /* A list of clients registered to be informed about a container state. */ struct lxc_list state_clients; /* sysctls */ struct lxc_list sysctls; /* procs */ struct lxc_list procs; struct shmount { /* Absolute path to the shared mount point on the host */ char *path_host; /* Absolute path (in the container) to the shared mount point */ char *path_cont; } shmount; }; extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size); #ifdef HAVE_TLS extern thread_local struct lxc_conf *current_config; #else extern struct lxc_conf *current_config; #endif extern int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, char *argv[]); extern int detect_shared_rootfs(void); extern struct lxc_conf *lxc_conf_init(void); extern void lxc_conf_free(struct lxc_conf *conf); extern int pin_rootfs(const char *rootfs); extern int lxc_map_ids(struct lxc_list *idmap, pid_t pid); extern int lxc_create_tty(const char *name, struct lxc_conf *conf); extern void lxc_delete_tty(struct lxc_tty_info *ttys); extern int lxc_clear_config_caps(struct lxc_conf *c); extern int lxc_clear_config_keepcaps(struct lxc_conf *c); extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version); extern int lxc_clear_mount_entries(struct lxc_conf *c); extern int lxc_clear_automounts(struct lxc_conf *c); extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); extern int lxc_clear_idmaps(struct lxc_conf *c); extern int lxc_clear_groups(struct lxc_conf *c); extern int lxc_clear_environment(struct lxc_conf *c); extern int lxc_clear_limits(struct lxc_conf *c, const char *key); extern int lxc_delete_autodev(struct lxc_handler *handler); extern int lxc_clear_autodev_tmpfs_size(struct lxc_conf *c); extern void lxc_clear_includes(struct lxc_conf *conf); extern int lxc_setup_rootfs_prepare_root(struct lxc_conf *conf, const char *name, const char *lxcpath); extern int lxc_setup(struct lxc_handler *handler); extern int lxc_setup_parent(struct lxc_handler *handler); extern int setup_resource_limits(struct lxc_list *limits, pid_t pid); extern int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype); extern int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype); extern int chown_mapped_root(const char *path, const struct lxc_conf *conf); extern int userns_exec_1(const struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name); extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data, const char *fn_name); extern int parse_mntopts(const char *mntopts, unsigned long *mntflags, char **mntdata); extern int parse_propagationopts(const char *mntopts, unsigned long *pflags); extern void tmp_proc_unmount(struct lxc_conf *lxc_conf); extern void remount_all_slave(void); extern void suggest_default_idmap(void); extern FILE *make_anonymous_mount_file(struct lxc_list *mount, bool include_nesting_helpers); extern struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings); extern unsigned long add_required_remount_flags(const char *s, const char *d, unsigned long flags); extern int run_script(const char *name, const char *section, const char *script, ...); extern int run_script_argv(const char *name, unsigned int hook_version, const char *section, const char *script, const char *hookname, char **argsin); extern int in_caplist(int cap, struct lxc_list *caps); extern int setup_sysctl_parameters(struct lxc_list *sysctls); extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key); extern int setup_proc_filesystem(struct lxc_list *procs, pid_t pid); extern int lxc_clear_procs(struct lxc_conf *c, const char *key); extern int lxc_clear_apparmor_raw(struct lxc_conf *c); extern int lxc_clear_namespace(struct lxc_conf *c); extern int userns_exec_minimal(const struct lxc_conf *conf, int (*fn_parent)(void *), void *fn_parent_data, int (*fn_child)(void *), void *fn_child_data); #endif /* __LXC_CONF_H */ lxc-4.0.2/src/lxc/error.c0000644061062106075000000000152213646120451012110 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #include #include #include #include "error.h" #include "log.h" lxc_log_define(error, lxc); /*---------------------------------------------------------------------------*/ /* lxc_error_set_and_log * function is here to convert * the reported status to an exit code as detailed here: * * 0-126 exit code of the application * 128+n signal n received by the application * 255 lxc error */ extern int lxc_error_set_and_log(int pid, int status) { int ret = 0; if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret) INFO("Child <%d> ended on error (%d)", pid, ret); } if (WIFSIGNALED(status)) { int signal = WTERMSIG(status); INFO("Child <%d> ended on signal (%d)", pid, signal); ret = 128 + signal; } return ret; } lxc-4.0.2/src/lxc/parse.c0000644061062106075000000000731613646120451012100 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include "config.h" #include "file_utils.h" #include "log.h" #include "macro.h" #include "parse.h" #include "syscall_wrappers.h" #include "utils.h" lxc_log_define(parse, lxc); void *lxc_strmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { void *tmp = NULL, *overlap = NULL; /* We establish an anonymous mapping that is one byte larger than the * underlying file. The pages handed to us are zero filled. */ tmp = mmap(addr, length + 1, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (tmp == MAP_FAILED) return tmp; /* Now we establish a fixed-address mapping starting at the address we * received from our anonymous mapping and replace all bytes excluding * the additional \0-byte with the file. This allows us to use normal * string-handling functions. */ overlap = mmap(tmp, length, prot, MAP_FIXED | flags, fd, offset); if (overlap == MAP_FAILED) munmap(tmp, length + 1); return overlap; } int lxc_strmunmap(void *addr, size_t length) { return munmap(addr, length + 1); } int lxc_file_for_each_line_mmap(const char *file, lxc_file_cb callback, void *data) { int saved_errno; ssize_t ret = -1, bytes_sent; char *line; int fd = -1, memfd = -1; char *buf = NULL; memfd = memfd_create(".lxc_config_file", MFD_CLOEXEC); if (memfd < 0) { char template[] = P_tmpdir "/.lxc_config_file_XXXXXX"; if (errno != ENOSYS) { SYSERROR("Failed to create memory file"); goto on_error; } TRACE("Failed to create in-memory file. Falling back to " "temporary file"); memfd = lxc_make_tmpfile(template, true); if (memfd < 0) { SYSERROR("Failed to create temporary file \"%s\"", template); goto on_error; } } fd = open(file, O_RDONLY | O_CLOEXEC); if (fd < 0) { SYSERROR("Failed to open file \"%s\"", file); goto on_error; } /* sendfile() handles up to 2GB. No config file should be that big. */ bytes_sent = lxc_sendfile_nointr(memfd, fd, NULL, LXC_SENDFILE_MAX); if (bytes_sent < 0) { SYSERROR("Failed to sendfile \"%s\"", file); goto on_error; } ret = lxc_write_nointr(memfd, "\0", 1); if (ret < 0) { SYSERROR("Failed to append zero byte"); goto on_error; } bytes_sent++; ret = lseek(memfd, 0, SEEK_SET); if (ret < 0) { SYSERROR("Failed to lseek"); goto on_error; } ret = -1; buf = mmap(NULL, bytes_sent, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, memfd, 0); if (buf == MAP_FAILED) { buf = NULL; SYSERROR("Failed to mmap"); goto on_error; } ret = 0; lxc_iterate_parts(line, buf, "\r\n\0") { ret = callback(line, data); if (ret) { /* Callback rv > 0 means stop here callback rv < 0 means * error. */ if (ret < 0) ERROR("Failed to parse config file \"%s\" at " "line \"%s\"", file, line); break; } } on_error: saved_errno = errno; if (fd >= 0) close(fd); if (memfd >= 0) close(memfd); if (buf && munmap(buf, bytes_sent)) { SYSERROR("Failed to unmap"); if (ret == 0) ret = -1; } errno = saved_errno; return ret; } int lxc_file_for_each_line(const char *file, lxc_file_cb callback, void *data) { __do_fclose FILE *f = NULL; __do_free char *line = NULL; int err = 0; size_t len = 0; f = fopen(file, "re"); if (!f) { SYSERROR("Failed to open \"%s\"", file); return -1; } while (getline(&line, &len, f) != -1) { err = callback(line, data); if (err) { /* Callback rv > 0 means stop here callback rv < 0 means * error. */ if (err < 0) ERROR("Failed to parse config: \"%s\"", line); break; } } return err; } lxc-4.0.2/src/lxc/raw_syscalls.h0000644061062106075000000000523413646120451013476 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_RAW_SYSCALL_H #define __LXC_RAW_SYSCALL_H #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include /* clone */ #ifndef CLONE_PIDFD #define CLONE_PIDFD 0x00001000 #endif /* waitid */ #ifndef P_PIDFD #define P_PIDFD 3 #endif /* * lxc_raw_clone() - create a new process * * - fork() behavior: * This function returns 0 in the child and > 0 in the parent. * * - copy-on-write: * This function does not allocate a new stack and relies on copy-on-write * semantics. * * - supports subset of ClONE_* flags: * lxc_raw_clone() intentionally only supports a subset of the flags available * to the actual system call. Please refer to the implementation what flags * cannot be used. Also, please don't assume that just because a flag isn't * explicitly checked for as being unsupported that it is supported. If in * doubt or not sufficiently familiar with process creation in the kernel and * interactions with libcs this function should be used. * * - no pthread_atfork() handlers: * This function circumvents - as much as this this is possible - any libc * wrappers and thus does not run any pthread_atfork() handlers. Make sure * that this is safe to do in the context you are trying to call this * function. * * - must call lxc_raw_getpid(): * The child must use lxc_raw_getpid() to retrieve its pid. */ extern pid_t lxc_raw_clone(unsigned long flags, int *pidfd); /* * lxc_raw_clone_cb() - create a new process * * - non-fork() behavior: * Function does return pid of the child or -1 on error. Pass in a callback * function via the "fn" argument that gets executed in the child process. * The "args" argument is passed to "fn". * * All other comments that apply to lxc_raw_clone() apply to lxc_raw_clone_cb() * as well. */ extern pid_t lxc_raw_clone_cb(int (*fn)(void *), void *args, unsigned long flags, int *pidfd); extern int lxc_raw_execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags); /* * Because of older glibc's pid cache (up to 2.25) whenever clone() is called * the child must must retrieve it's own pid via lxc_raw_getpid(). */ static inline pid_t lxc_raw_getpid(void) { return (pid_t)syscall(SYS_getpid); } static inline pid_t lxc_raw_gettid(void) { #if __NR_gettid > 0 return syscall(__NR_gettid); #else return lxc_raw_getpid(); #endif } extern int lxc_raw_pidfd_send_signal(int pidfd, int sig, siginfo_t *info, unsigned int flags); #endif /* __LXC_RAW_SYSCALL_H */ lxc-4.0.2/src/lxc/compiler.h0000644061062106075000000000227613646120451012605 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_COMPILER_H #define __LXC_COMPILER_H #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include "config.h" #ifndef thread_local #if __STDC_VERSION__ >= 201112L && \ !(defined(__STDC_NO_THREADS__) || \ (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) #define thread_local _Thread_local #else #define thread_local __thread #endif #endif #ifndef __fallthrough #define __fallthrough /* fall through */ #endif #ifndef __noreturn # if __STDC_VERSION__ >= 201112L # if !IS_BIONIC # define __noreturn _Noreturn # else # define __noreturn __attribute__((__noreturn__)) # endif # elif IS_BIONIC # define __noreturn __attribute__((__noreturn__)) # else # define __noreturn __attribute__((noreturn)) # endif #endif #ifndef __hot # define __hot __attribute__((hot)) #endif #ifndef __returns_twice #define __returns_twice __attribute__((returns_twice)) #endif /* This attribute is required to silence clang warnings */ #if defined(__GNUC__) #define __lxc_unused __attribute__ ((unused)) #else #define __lxc_unused #endif /* Indicates taking ownership */ #define __owns #define __cgfsng_ops #endif /* __LXC_COMPILER_H */ lxc-4.0.2/src/lxc/attach.c0000644061062106075000000011433313646120451012230 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "af_unix.h" #include "attach.h" #include "caps.h" #include "cgroup.h" #include "commands.h" #include "conf.h" #include "config.h" #include "confile.h" #include "log.h" #include "lsm/lsm.h" #include "lxclock.h" #include "lxcseccomp.h" #include "macro.h" #include "mainloop.h" #include "memory_utils.h" #include "namespace.h" #include "raw_syscalls.h" #include "syscall_wrappers.h" #include "terminal.h" #include "utils.h" #if HAVE_SYS_PERSONALITY_H #include #endif lxc_log_define(attach, lxc); /* Define default options if no options are supplied by the user. */ static lxc_attach_options_t attach_static_default_options = LXC_ATTACH_OPTIONS_DEFAULT; static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) { __do_free char *line = NULL; __do_fclose FILE *proc_file = NULL; __do_free struct lxc_proc_context_info *info = NULL; int ret; bool found; char proc_fn[LXC_PROC_STATUS_LEN]; size_t line_bufsz = 0; /* Read capabilities. */ ret = snprintf(proc_fn, LXC_PROC_STATUS_LEN, "/proc/%d/status", pid); if (ret < 0 || ret >= LXC_PROC_STATUS_LEN) return NULL; proc_file = fopen(proc_fn, "re"); if (!proc_file) return log_error_errno(NULL, errno, "Failed to open %s", proc_fn); info = calloc(1, sizeof(*info)); if (!info) return NULL; found = false; while (getline(&line, &line_bufsz, proc_file) != -1) { ret = sscanf(line, "CapBnd: %llx", &info->capability_mask); if (ret != EOF && ret == 1) { found = true; break; } } if (!found) return log_error_errno(NULL, ENOENT, "Failed to read capability bounding set from %s", proc_fn); info->lsm_label = lsm_process_label_get(pid); info->ns_inherited = 0; for (int i = 0; i < LXC_NS_MAX; i++) info->ns_fd[i] = -EBADF; return move_ptr(info); } static inline void lxc_proc_close_ns_fd(struct lxc_proc_context_info *ctx) { for (int i = 0; i < LXC_NS_MAX; i++) close_prot_errno_disarm(ctx->ns_fd[i]); } static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx) { free(ctx->lsm_label); ctx->lsm_label = NULL; if (ctx->container) { lxc_container_put(ctx->container); ctx->container = NULL; } lxc_proc_close_ns_fd(ctx); free(ctx); } /** * in_same_namespace - Check whether two processes are in the same namespace. * @pid1 - PID of the first process. * @pid2 - PID of the second process. * @ns - Name of the namespace to check. Must correspond to one of the names * for the namespaces as shown in /proc/ns_fd[i] < 0) continue; ret = setns(ctx->ns_fd[i], ns_info[i].clone_flag); if (ret < 0) return log_error_errno(-1, errno, "Failed to attach to %s namespace of %d", ns_info[i].proc_name, pid); DEBUG("Attached to %s namespace of %d", ns_info[i].proc_name, pid); } return 0; } int lxc_attach_remount_sys_proc(void) { int ret; ret = unshare(CLONE_NEWNS); if (ret < 0) return log_error_errno(-1, errno, "Failed to unshare mount namespace"); if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) { SYSERROR("Failed to make / rslave"); ERROR("Continuing..."); } } /* Assume /proc is always mounted, so remount it. */ ret = umount2("/proc", MNT_DETACH); if (ret < 0) return log_error_errno(-1, errno, "Failed to unmount /proc"); ret = mount("none", "/proc", "proc", 0, NULL); if (ret < 0) return log_error_errno(-1, errno, "Failed to remount /proc"); /* * Try to umount /sys. If it's not a mount point, we'll get EINVAL, then * we ignore it because it may not have been mounted in the first place. */ ret = umount2("/sys", MNT_DETACH); if (ret < 0 && errno != EINVAL) return log_error_errno(-1, errno, "Failed to unmount /sys"); /* Remount it. */ if (ret == 0 && mount("none", "/sys", "sysfs", 0, NULL)) return log_error_errno(-1, errno, "Failed to remount /sys"); return 0; } static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) { int last_cap; last_cap = lxc_caps_last_cap(); for (int cap = 0; cap <= last_cap; cap++) { if (ctx->capability_mask & (1LL << cap)) continue; if (prctl(PR_CAPBSET_DROP, prctl_arg(cap), prctl_arg(0), prctl_arg(0), prctl_arg(0))) return log_error_errno(-1, errno, "Failed to drop capability %d", cap); TRACE("Dropped capability %d", cap); } return 0; } static int lxc_attach_set_environment(struct lxc_proc_context_info *init_ctx, enum lxc_attach_env_policy_t policy, char **extra_env, char **extra_keep) { int ret; struct lxc_list *iterator; if (policy == LXC_ATTACH_CLEAR_ENV) { int path_kept = 0; char **extra_keep_store = NULL; if (extra_keep) { size_t count, i; for (count = 0; extra_keep[count]; count++) ; extra_keep_store = calloc(count, sizeof(char *)); if (!extra_keep_store) return -1; for (i = 0; i < count; i++) { char *v = getenv(extra_keep[i]); if (v) { extra_keep_store[i] = strdup(v); if (!extra_keep_store[i]) { while (i > 0) free(extra_keep_store[--i]); free(extra_keep_store); return -1; } if (strcmp(extra_keep[i], "PATH") == 0) path_kept = 1; } } } if (clearenv()) { if (extra_keep_store) { char **p; for (p = extra_keep_store; *p; p++) free(*p); free(extra_keep_store); } return log_error(-1, "Failed to clear environment"); } if (extra_keep_store) { size_t i; for (i = 0; extra_keep[i]; i++) { if (extra_keep_store[i]) { ret = setenv(extra_keep[i], extra_keep_store[i], 1); if (ret < 0) SYSWARN("Failed to set environment variable"); } free(extra_keep_store[i]); } free(extra_keep_store); } /* Always set a default path; shells and execlp tend to be fine * without it, but there is a disturbing number of C programs * out there that just assume that getenv("PATH") is never NULL * and then die a painful segfault death. */ if (!path_kept) { ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); if (ret < 0) SYSWARN("Failed to set environment variable"); } } ret = putenv("container=lxc"); if (ret < 0) return log_warn(-1, "Failed to set environment variable"); /* Set container environment variables.*/ if (init_ctx && init_ctx->container && init_ctx->container->lxc_conf) { lxc_list_for_each(iterator, &init_ctx->container->lxc_conf->environment) { char *env_tmp; env_tmp = strdup((char *)iterator->elem); if (!env_tmp) return -1; ret = putenv(env_tmp); if (ret < 0) return log_error_errno(-1, errno, "Failed to set environment variable: %s", (char *)iterator->elem); } } /* Set extra environment variables. */ if (extra_env) { for (; *extra_env; extra_env++) { char *p; /* We just assume the user knows what they are doing, so * we don't do any checks. */ p = strdup(*extra_env); if (!p) return -1; ret = putenv(p); if (ret < 0) SYSWARN("Failed to set environment variable"); } } return 0; } static char *lxc_attach_getpwshell(uid_t uid) { __do_free char *line = NULL, *result = NULL; __do_fclose FILE *pipe_f = NULL; int fd, ret; pid_t pid; int pipes[2]; bool found = false; size_t line_bufsz = 0; /* We need to fork off a process that runs the getent program, and we * need to capture its output, so we use a pipe for that purpose. */ ret = pipe2(pipes, O_CLOEXEC); if (ret < 0) return NULL; pid = fork(); if (pid < 0) { close(pipes[0]); close(pipes[1]); return NULL; } if (!pid) { char uid_buf[32]; char *arguments[] = { "getent", "passwd", uid_buf, NULL }; close(pipes[0]); /* We want to capture stdout. */ ret = dup2(pipes[1], STDOUT_FILENO); close(pipes[1]); if (ret < 0) _exit(EXIT_FAILURE); /* Get rid of stdin/stderr, so we try to associate it with * /dev/null. */ fd = open_devnull(); if (fd < 0) { close(STDIN_FILENO); close(STDERR_FILENO); } else { (void)dup3(fd, STDIN_FILENO, O_CLOEXEC); (void)dup3(fd, STDERR_FILENO, O_CLOEXEC); close(fd); } /* Finish argument list. */ ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long)uid); if (ret <= 0 || ret >= sizeof(uid_buf)) _exit(EXIT_FAILURE); /* Try to run getent program. */ (void)execvp("getent", arguments); _exit(EXIT_FAILURE); } close(pipes[1]); pipe_f = fdopen(pipes[0], "re"); if (!pipe_f) { close(pipes[0]); goto reap_child; } /* Transfer ownership of pipes[0] to pipe_f. */ move_fd(pipes[0]); while (getline(&line, &line_bufsz, pipe_f) != -1) { int i; long value; char *token; char *endptr = NULL, *saveptr = NULL; /* If we already found something, just continue to read * until the pipe doesn't deliver any more data, but * don't modify the existing data structure. */ if (found) continue; if (!line) continue; /* Trim line on the right hand side. */ for (i = strlen(line); i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i) line[i - 1] = '\0'; /* Split into tokens: first: user name. */ token = strtok_r(line, ":", &saveptr); if (!token) continue; /* next: dummy password field */ token = strtok_r(NULL, ":", &saveptr); if (!token) continue; /* next: user id */ token = strtok_r(NULL, ":", &saveptr); value = token ? strtol(token, &endptr, 10) : 0; if (!token || !endptr || *endptr || value == LONG_MIN || value == LONG_MAX) continue; /* dummy sanity check: user id matches */ if ((uid_t)value != uid) continue; /* skip fields: gid, gecos, dir, go to next field 'shell' */ for (i = 0; i < 4; i++) { token = strtok_r(NULL, ":", &saveptr); if (!token) continue; } if (!token) continue; free_disarm(result); result = strdup(token); /* Sanity check that there are no fields after that. */ token = strtok_r(NULL, ":", &saveptr); if (token) continue; found = true; } reap_child: ret = wait_for_pid(pid); if (ret < 0) return NULL; if (!found) return NULL; return move_ptr(result); } static void lxc_attach_get_init_uidgid(uid_t *init_uid, gid_t *init_gid) { __do_free char *line = NULL; __do_fclose FILE *proc_file = NULL; char proc_fn[LXC_PROC_STATUS_LEN]; int ret; size_t line_bufsz = 0; long value = -1; uid_t uid = LXC_INVALID_UID; gid_t gid = LXC_INVALID_GID; ret = snprintf(proc_fn, LXC_PROC_STATUS_LEN, "/proc/%d/status", 1); if (ret < 0 || ret >= LXC_PROC_STATUS_LEN) return; proc_file = fopen(proc_fn, "re"); if (!proc_file) return; while (getline(&line, &line_bufsz, proc_file) != -1) { /* Format is: real, effective, saved set user, fs we only care * about real uid. */ ret = sscanf(line, "Uid: %ld", &value); if (ret != EOF && ret == 1) { uid = (uid_t)value; } else { ret = sscanf(line, "Gid: %ld", &value); if (ret != EOF && ret == 1) gid = (gid_t)value; } if (uid != LXC_INVALID_UID && gid != LXC_INVALID_GID) break; } /* Only override arguments if we found something. */ if (uid != LXC_INVALID_UID) *init_uid = uid; if (gid != LXC_INVALID_GID) *init_gid = gid; /* TODO: we should also parse supplementary groups and use * setgroups() to set them. */ } static bool fetch_seccomp(struct lxc_container *c, lxc_attach_options_t *options) { __do_free char *path = NULL; int ret; bool bret; if (!(options->namespaces & CLONE_NEWNS) || !(options->attach_flags & LXC_ATTACH_LSM)) { free_disarm(c->lxc_conf->seccomp.seccomp); return true; } /* Remove current setting. */ if (!c->set_config_item(c, "lxc.seccomp.profile", "") && !c->set_config_item(c, "lxc.seccomp", "")) return false; /* Fetch the current profile path over the cmd interface. */ path = c->get_running_config_item(c, "lxc.seccomp.profile"); if (!path) { INFO("Failed to retrieve lxc.seccomp.profile"); path = c->get_running_config_item(c, "lxc.seccomp"); if (!path) return log_info(true, "Failed to retrieve lxc.seccomp"); } /* Copy the value into the new lxc_conf. */ bret = c->set_config_item(c, "lxc.seccomp.profile", path); if (!bret) return false; /* Attempt to parse the resulting config. */ ret = lxc_read_seccomp_config(c->lxc_conf); if (ret < 0) return log_error(false, "Failed to retrieve seccomp policy"); return log_info(true, "Retrieved seccomp policy"); } static bool no_new_privs(struct lxc_container *c, lxc_attach_options_t *options) { __do_free char *val = NULL; /* Remove current setting. */ if (!c->set_config_item(c, "lxc.no_new_privs", "")) return log_info(false, "Failed to unset lxc.no_new_privs"); /* Retrieve currently active setting. */ val = c->get_running_config_item(c, "lxc.no_new_privs"); if (!val) return log_info(false, "Failed to retrieve lxc.no_new_privs"); /* Set currently active setting. */ return c->set_config_item(c, "lxc.no_new_privs", val); } static signed long get_personality(const char *name, const char *lxcpath) { __do_free char *p = NULL; p = lxc_cmd_get_config_item(name, "lxc.arch", lxcpath); if (!p) return -1; return lxc_config_parse_arch(p); } struct attach_clone_payload { int ipc_socket; int terminal_slave_fd; lxc_attach_options_t *options; struct lxc_proc_context_info *init_ctx; lxc_attach_exec_t exec_function; void *exec_payload; }; static void lxc_put_attach_clone_payload(struct attach_clone_payload *p) { close_prot_errno_disarm(p->ipc_socket); close_prot_errno_disarm(p->terminal_slave_fd); if (p->init_ctx) { lxc_proc_put_context_info(p->init_ctx); p->init_ctx = NULL; } } static int attach_child_main(struct attach_clone_payload *payload) { int lsm_fd, ret; uid_t new_uid; gid_t new_gid; uid_t ns_root_uid = 0; gid_t ns_root_gid = 0; lxc_attach_options_t* options = payload->options; struct lxc_proc_context_info* init_ctx = payload->init_ctx; bool needs_lsm = (options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label; /* A description of the purpose of this functionality is provided in the * lxc-attach(1) manual page. We have to remount here and not in the * parent process, otherwise /proc may not properly reflect the new pid * namespace. */ if (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) { ret = lxc_attach_remount_sys_proc(); if (ret < 0) goto on_error; TRACE("Remounted \"/proc\" and \"/sys\""); } /* Now perform additional attachments. */ #if HAVE_SYS_PERSONALITY_H if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) { long new_personality; if (options->personality < 0) new_personality = init_ctx->personality; else new_personality = options->personality; ret = personality(new_personality); if (ret < 0) goto on_error; TRACE("Set new personality"); } #endif if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) { ret = lxc_attach_drop_privs(init_ctx); if (ret < 0) goto on_error; TRACE("Dropped capabilities"); } /* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL) * if you want this to be a no-op). */ ret = lxc_attach_set_environment(init_ctx, options->env_policy, options->extra_env_vars, options->extra_keep_env); if (ret < 0) goto on_error; TRACE("Set up environment"); /* This remark only affects fully unprivileged containers: * Receive fd for LSM security module before we set{g,u}id(). The reason * is that on set{g,u}id() the kernel will a) make us undumpable and b) * we will change our effective uid. This means our effective uid will * be different from the effective uid of the process that created us * which means that this processs no longer has capabilities in our * namespace including CAP_SYS_PTRACE. This means we will not be able to * read and /proc/ files for the process anymore when /proc is * mounted with hidepid={1,2}. So let's get the lsm label fd before the * set{g,u}id(). */ if (needs_lsm) { ret = lxc_abstract_unix_recv_fds(payload->ipc_socket, &lsm_fd, 1, NULL, 0); if (ret <= 0) { if (ret < 0) SYSERROR("Failed to receive lsm label fd"); goto on_error; } TRACE("Received LSM label file descriptor %d from parent", lsm_fd); } if (options->stdin_fd > 0 && isatty(options->stdin_fd)) { ret = lxc_make_controlling_terminal(options->stdin_fd); if (ret < 0) goto on_error; } if (!lxc_setgroups(0, NULL) && errno != EPERM) goto on_error; if (options->namespaces & CLONE_NEWUSER) { /* Check whether nsuid 0 has a mapping. */ ns_root_uid = get_ns_uid(0); /* Check whether nsgid 0 has a mapping. */ ns_root_gid = get_ns_gid(0); /* If there's no mapping for nsuid 0 try to retrieve the nsuid * init was started with. */ if (ns_root_uid == LXC_INVALID_UID) lxc_attach_get_init_uidgid(&ns_root_uid, &ns_root_gid); if (ns_root_uid == LXC_INVALID_UID) goto on_error; if (!lxc_switch_uid_gid(ns_root_uid, ns_root_gid)) goto on_error; } /* Set {u,g}id. */ if (options->uid != LXC_INVALID_UID) new_uid = options->uid; else new_uid = ns_root_uid; if (options->gid != LXC_INVALID_GID) new_gid = options->gid; else new_gid = ns_root_gid; if ((init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->no_new_privs) || (options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) { ret = prctl(PR_SET_NO_NEW_PRIVS, prctl_arg(1), prctl_arg(0), prctl_arg(0), prctl_arg(0)); if (ret < 0) goto on_error; TRACE("Set PR_SET_NO_NEW_PRIVS"); } if (needs_lsm) { bool on_exec; /* Change into our new LSM profile. */ on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; ret = lsm_process_label_set_at(lsm_fd, init_ctx->lsm_label, on_exec); close(lsm_fd); if (ret < 0) goto on_error; TRACE("Set %s LSM label to \"%s\"", lsm_name(), init_ctx->lsm_label); } if (init_ctx->container && init_ctx->container->lxc_conf && init_ctx->container->lxc_conf->seccomp.seccomp) { struct lxc_conf *conf = init_ctx->container->lxc_conf; ret = lxc_seccomp_load(conf); if (ret < 0) goto on_error; TRACE("Loaded seccomp profile"); ret = lxc_seccomp_send_notifier_fd(&conf->seccomp, payload->ipc_socket); if (ret < 0) goto on_error; } close(payload->ipc_socket); payload->ipc_socket = -EBADF; lxc_proc_put_context_info(init_ctx); payload->init_ctx = NULL; /* The following is done after the communication socket is shut down. * That way, all errors that might (though unlikely) occur up until this * point will have their messages printed to the original stderr (if * logging is so configured) and not the fd the user supplied, if any. */ /* Fd handling for stdin, stdout and stderr; ignore errors here, user * may want to make sure the fds are closed, for example. */ if (options->stdin_fd >= 0 && options->stdin_fd != STDIN_FILENO) (void)dup2(options->stdin_fd, STDIN_FILENO); if (options->stdout_fd >= 0 && options->stdout_fd != STDOUT_FILENO) (void)dup2(options->stdout_fd, STDOUT_FILENO); if (options->stderr_fd >= 0 && options->stderr_fd != STDERR_FILENO) (void)dup2(options->stderr_fd, STDERR_FILENO); /* close the old fds */ if (options->stdin_fd > STDERR_FILENO) close(options->stdin_fd); if (options->stdout_fd > STDERR_FILENO) close(options->stdout_fd); if (options->stderr_fd > STDERR_FILENO) close(options->stderr_fd); /* * Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also * here, ignore errors. */ for (int fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) { ret = fd_cloexec(fd, false); if (ret < 0) { SYSERROR("Failed to clear FD_CLOEXEC from file descriptor %d", fd); goto on_error; } } if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = lxc_terminal_prepare_login(payload->terminal_slave_fd); if (ret < 0) { SYSERROR("Failed to prepare terminal file descriptor %d", payload->terminal_slave_fd); goto on_error; } TRACE("Prepared terminal file descriptor %d", payload->terminal_slave_fd); } /* Avoid unnecessary syscalls. */ if (new_uid == ns_root_uid) new_uid = LXC_INVALID_UID; if (new_gid == ns_root_gid) new_gid = LXC_INVALID_GID; /* Make sure that the processes STDIO is correctly owned by the user that we are switching to */ ret = fix_stdio_permissions(new_uid); if (ret) WARN("Failed to ajust stdio permissions"); if (!lxc_switch_uid_gid(new_uid, new_gid)) goto on_error; /* We're done, so we can now do whatever the user intended us to do. */ _exit(payload->exec_function(payload->exec_payload)); on_error: lxc_put_attach_clone_payload(payload); _exit(EXIT_FAILURE); } static int lxc_attach_terminal(struct lxc_conf *conf, struct lxc_terminal *terminal) { int ret; lxc_terminal_init(terminal); ret = lxc_terminal_create(terminal); if (ret < 0) return log_error(-1, "Failed to create terminal"); /* Shift ttys to container. */ ret = lxc_terminal_map_ids(conf, terminal); if (ret < 0) { ERROR("Failed to chown terminal"); goto on_error; } return 0; on_error: lxc_terminal_delete(terminal); lxc_terminal_conf_free(terminal); return -1; } static int lxc_attach_terminal_mainloop_init(struct lxc_terminal *terminal, struct lxc_epoll_descr *descr) { int ret; ret = lxc_mainloop_open(descr); if (ret < 0) return log_error(-1, "Failed to create mainloop"); ret = lxc_terminal_mainloop_add(descr, terminal); if (ret < 0) { lxc_mainloop_close(descr); return log_error(-1, "Failed to add handlers to mainloop"); } return 0; } static inline void lxc_attach_terminal_close_master(struct lxc_terminal *terminal) { close_prot_errno_disarm(terminal->master); } static inline void lxc_attach_terminal_close_slave(struct lxc_terminal *terminal) { close_prot_errno_disarm(terminal->slave); } static inline void lxc_attach_terminal_close_peer(struct lxc_terminal *terminal) { close_prot_errno_disarm(terminal->peer); } static inline void lxc_attach_terminal_close_log(struct lxc_terminal *terminal) { close_prot_errno_disarm(terminal->log_fd); } int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process) { int i, ret, status; int ipc_sockets[2]; char *cwd, *new_cwd; signed long personality; pid_t attached_pid, init_pid, pid; struct lxc_proc_context_info *init_ctx; struct lxc_terminal terminal; struct lxc_conf *conf; char *name, *lxcpath; struct attach_clone_payload payload = {0}; ret = access("/proc/self/ns", X_OK); if (ret) return log_error_errno(-1, errno, "Does this kernel version support namespaces?"); if (!container) return ret_set_errno(-1, EINVAL); if (!lxc_container_get(container)) return ret_set_errno(-1, EINVAL); name = container->name; lxcpath = container->config_path; if (!options) options = &attach_static_default_options; init_pid = lxc_cmd_get_init_pid(name, lxcpath); if (init_pid < 0) { lxc_container_put(container); return log_error(-1, "Failed to get init pid"); } init_ctx = lxc_proc_get_context_info(init_pid); if (!init_ctx) { ERROR("Failed to get context of init process: %ld", (long)init_pid); lxc_container_put(container); return -1; } init_ctx->container = container; personality = get_personality(name, lxcpath); if (init_ctx->personality < 0) { ERROR("Failed to get personality of the container"); lxc_proc_put_context_info(init_ctx); return -1; } init_ctx->personality = personality; if (!init_ctx->container->lxc_conf) { init_ctx->container->lxc_conf = lxc_conf_init(); if (!init_ctx->container->lxc_conf) { lxc_proc_put_context_info(init_ctx); return -1; } } conf = init_ctx->container->lxc_conf; if (!conf) return log_error_errno(-EINVAL, EINVAL, "Missing container confifg"); if (!fetch_seccomp(init_ctx->container, options)) WARN("Failed to get seccomp policy"); if (!no_new_privs(init_ctx->container, options)) WARN("Could not determine whether PR_SET_NO_NEW_PRIVS is set"); cwd = getcwd(NULL, 0); /* Determine which namespaces the container was created with * by asking lxc-start, if necessary. */ if (options->namespaces == -1) { options->namespaces = lxc_cmd_get_clone_flags(name, lxcpath); /* call failed */ if (options->namespaces == -1) { ERROR("Failed to automatically determine the " "namespaces which the container uses"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } for (i = 0; i < LXC_NS_MAX; i++) { if (ns_info[i].clone_flag & CLONE_NEWCGROUP) if (!(options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) || !cgns_supported()) continue; if (ns_info[i].clone_flag & options->namespaces) continue; init_ctx->ns_inherited |= ns_info[i].clone_flag; } } pid = lxc_raw_getpid(); for (i = 0; i < LXC_NS_MAX; i++) { int j; if (options->namespaces & ns_info[i].clone_flag) init_ctx->ns_fd[i] = lxc_preserve_ns(init_pid, ns_info[i].proc_name); else if (init_ctx->ns_inherited & ns_info[i].clone_flag) init_ctx->ns_fd[i] = in_same_namespace(pid, init_pid, ns_info[i].proc_name); else continue; if (init_ctx->ns_fd[i] >= 0) continue; if (init_ctx->ns_fd[i] == -EINVAL) { DEBUG("Inheriting %s namespace from %d", ns_info[i].proc_name, pid); init_ctx->ns_inherited &= ~ns_info[i].clone_flag; continue; } /* We failed to preserve the namespace. */ SYSERROR("Failed to attach to %s namespace of %d", ns_info[i].proc_name, pid); /* Close all already opened file descriptors before we return an * error, so we don't leak them. */ for (j = 0; j < i; j++) close(init_ctx->ns_fd[j]); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = lxc_attach_terminal(conf, &terminal); if (ret < 0) { ERROR("Failed to setup new terminal"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } terminal.log_fd = options->log_fd; } else { lxc_terminal_init(&terminal); } /* Create a socket pair for IPC communication; set SOCK_CLOEXEC in order * to make sure we don't irritate other threads that want to fork+exec * away * * IMPORTANT: if the initial process is multithreaded and another call * just fork()s away without exec'ing directly after, the socket fd will * exist in the forked process from the other thread and any close() in * our own child process will not really cause the socket to close * properly, potentially causing the parent to hang. * * For this reason, while IPC is still active, we have to use shutdown() * if the child exits prematurely in order to signal that the socket is * closed and cannot assume that the child exiting will automatically do * that. * * IPC mechanism: (X is receiver) * initial process intermediate attached * X <--- send pid of * attached proc, * then exit * send 0 ------------------------------------> X * [do initialization] * X <------------------------------------ send 1 * [add to cgroup, ...] * send 2 ------------------------------------> X * [set LXC_ATTACH_NO_NEW_PRIVS] * X <------------------------------------ send 3 * [open LSM label fd] * send 4 ------------------------------------> X * [set LSM label] * close socket close socket * run program */ ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); if (ret < 0) { SYSERROR("Could not set up required IPC mechanism for attaching"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } /* Create intermediate subprocess, two reasons: * 1. We can't setns() in the child itself, since we want to make * sure we are properly attached to the pidns. * 2. Also, the initial thread has to put the attached process * into the cgroup, which we can only do if we didn't already * setns() (otherwise, user namespaces will hate us). */ pid = fork(); if (pid < 0) { SYSERROR("Failed to create first subprocess"); free(cwd); lxc_proc_put_context_info(init_ctx); return -1; } if (pid) { int ret_parent = -1; pid_t to_cleanup_pid = pid; struct lxc_epoll_descr descr = {0}; /* close unneeded file descriptors */ close(ipc_sockets[1]); free(cwd); lxc_proc_close_ns_fd(init_ctx); if (options->attach_flags & LXC_ATTACH_TERMINAL) lxc_attach_terminal_close_slave(&terminal); /* Attach to cgroup, if requested. */ if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { /* * If this is the unified hierarchy cgroup_attach() is * enough. */ ret = cgroup_attach(conf, name, lxcpath, pid); if (ret) { call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL; cgroup_ops = cgroup_init(conf); if (!cgroup_ops) goto on_error; if (!cgroup_ops->attach(cgroup_ops, conf, name, lxcpath, pid)) goto on_error; } TRACE("Moved intermediate process %d into container's cgroups", pid); } /* Setup /proc limits */ if (!lxc_list_empty(&conf->procs)) { ret = setup_proc_filesystem(&conf->procs, pid); if (ret < 0) goto on_error; } /* Setup resource limits */ if (!lxc_list_empty(&conf->limits)) { ret = setup_resource_limits(&conf->limits, pid); if (ret < 0) goto on_error; } if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = lxc_attach_terminal_mainloop_init(&terminal, &descr); if (ret < 0) goto on_error; TRACE("Initialized terminal mainloop"); } /* Let the child process know to go ahead. */ status = 0; ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status)); if (ret != sizeof(status)) goto close_mainloop; TRACE("Told intermediate process to start initializing"); /* Get pid of attached process from intermediate process. */ ret = lxc_read_nointr(ipc_sockets[0], &attached_pid, sizeof(attached_pid)); if (ret != sizeof(attached_pid)) goto close_mainloop; TRACE("Received pid %d of attached process in parent pid namespace", attached_pid); /* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */ if (options->stdin_fd == 0) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); } /* Reap intermediate process. */ ret = wait_for_pid(pid); if (ret < 0) goto close_mainloop; TRACE("Intermediate process %d exited", pid); /* We will always have to reap the attached process now. */ to_cleanup_pid = attached_pid; /* Open LSM fd and send it to child. */ if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { int labelfd; bool on_exec; ret = -1; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? true : false; labelfd = lsm_process_label_fd_get(attached_pid, on_exec); if (labelfd < 0) goto close_mainloop; TRACE("Opened LSM label file descriptor %d", labelfd); /* Send child fd of the LSM security module to write to. */ ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0); if (ret <= 0) { if (ret < 0) SYSERROR("Failed to send lsm label fd"); close(labelfd); goto close_mainloop; } close(labelfd); TRACE("Sent LSM label file descriptor %d to child", labelfd); } if (conf->seccomp.seccomp) { ret = lxc_seccomp_recv_notifier_fd(&conf->seccomp, ipc_sockets[0]); if (ret < 0) goto close_mainloop; ret = lxc_seccomp_add_notifier(name, lxcpath, &conf->seccomp); if (ret < 0) goto close_mainloop; } /* We're done, the child process should now execute whatever it * is that the user requested. The parent can now track it with * waitpid() or similar. */ *attached_process = attached_pid; /* Now shut down communication with child, we're done. */ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); ipc_sockets[0] = -1; ret_parent = 0; to_cleanup_pid = -1; if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = lxc_mainloop(&descr, -1); if (ret < 0) { ret_parent = -1; to_cleanup_pid = attached_pid; } } close_mainloop: if (options->attach_flags & LXC_ATTACH_TERMINAL) lxc_mainloop_close(&descr); on_error: if (ipc_sockets[0] >= 0) { shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); } if (to_cleanup_pid > 0) (void)wait_for_pid(to_cleanup_pid); if (options->attach_flags & LXC_ATTACH_TERMINAL) { lxc_terminal_delete(&terminal); lxc_terminal_conf_free(&terminal); } lxc_proc_put_context_info(init_ctx); return ret_parent; } /* close unneeded file descriptors */ close_prot_errno_disarm(ipc_sockets[0]); if (options->attach_flags & LXC_ATTACH_TERMINAL) { lxc_attach_terminal_close_master(&terminal); lxc_attach_terminal_close_peer(&terminal); lxc_attach_terminal_close_log(&terminal); } /* Wait for the parent to have setup cgroups. */ ret = lxc_read_nointr(ipc_sockets[1], &status, sizeof(status)); if (ret != sizeof(status)) { shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); _exit(EXIT_FAILURE); } TRACE("Intermediate process starting to initialize"); /* Attach now, create another subprocess later, since pid namespaces * only really affect the children of the current process. */ ret = lxc_attach_to_ns(init_pid, init_ctx); if (ret < 0) { ERROR("Failed to enter namespaces"); shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); _exit(EXIT_FAILURE); } /* close namespace file descriptors */ lxc_proc_close_ns_fd(init_ctx); /* Attach succeeded, try to cwd. */ if (options->initial_cwd) new_cwd = options->initial_cwd; else new_cwd = cwd; if (new_cwd) { ret = chdir(new_cwd); if (ret < 0) WARN("Could not change directory to \"%s\"", new_cwd); } free(cwd); /* Create attached process. */ payload.ipc_socket = ipc_sockets[1]; payload.options = options; payload.init_ctx = init_ctx; payload.terminal_slave_fd = terminal.slave; payload.exec_function = exec_function; payload.exec_payload = exec_payload; pid = lxc_raw_clone(CLONE_PARENT, NULL); if (pid < 0) { SYSERROR("Failed to clone attached process"); shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); _exit(EXIT_FAILURE); } if (pid == 0) { if (options->attach_flags & LXC_ATTACH_TERMINAL) { ret = pthread_sigmask(SIG_SETMASK, &terminal.tty_state->oldmask, NULL); if (ret < 0) { SYSERROR("Failed to reset signal mask"); _exit(EXIT_FAILURE); } } ret = attach_child_main(&payload); if (ret < 0) ERROR("Failed to exec"); _exit(EXIT_FAILURE); } if (options->attach_flags & LXC_ATTACH_TERMINAL) lxc_attach_terminal_close_slave(&terminal); /* Tell grandparent the pid of the pid of the newly created child. */ ret = lxc_write_nointr(ipc_sockets[1], &pid, sizeof(pid)); if (ret != sizeof(pid)) { /* If this really happens here, this is very unfortunate, since * the parent will not know the pid of the attached process and * will not be able to wait for it (and we won't either due to * CLONE_PARENT) so the parent won't be able to reap it and the * attached process will remain a zombie. */ shutdown(ipc_sockets[1], SHUT_RDWR); lxc_proc_put_context_info(init_ctx); _exit(EXIT_FAILURE); } TRACE("Sending pid %d of attached process", pid); /* The rest is in the hands of the initial and the attached process. */ lxc_proc_put_context_info(init_ctx); _exit(EXIT_SUCCESS); } int lxc_attach_run_command(void *payload) { int ret = -1; lxc_attach_command_t *cmd = payload; ret = execvp(cmd->program, cmd->argv); if (ret < 0) { switch (errno) { case ENOEXEC: ret = 126; break; case ENOENT: ret = 127; break; } } return log_error_errno(ret, errno, "Failed to exec \"%s\"", cmd->program); } int lxc_attach_run_shell(void* payload) { __do_free char *buf = NULL; uid_t uid; struct passwd pwent; struct passwd *pwentp = NULL; char *user_shell; size_t bufsize; int ret; /* Ignore payload parameter. */ (void)payload; uid = getuid(); bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 1024; buf = malloc(bufsize); if (buf) { ret = getpwuid_r(uid, &pwent, buf, bufsize, &pwentp); if (!pwentp) { if (ret == 0) WARN("Could not find matched password record"); WARN("Failed to get password record - %u", uid); } } /* This probably happens because of incompatible nss implementations in * host and container (remember, this code is still using the host's * glibc but our mount namespace is in the container) we may try to get * the information by spawning a [getent passwd uid] process and parsing * the result. */ if (!pwentp) user_shell = lxc_attach_getpwshell(uid); else user_shell = pwent.pw_shell; if (user_shell) execlp(user_shell, user_shell, (char *)NULL); /* Executed if either no passwd entry or execvp fails, we will fall back * on /bin/sh as a default shell. */ execlp("/bin/sh", "/bin/sh", (char *)NULL); SYSERROR("Failed to execute shell"); if (!pwentp) free(user_shell); return -1; } lxc-4.0.2/src/lxc/commands.h0000644061062106075000000001037613646120451012574 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef __LXC_COMMANDS_H #define __LXC_COMMANDS_H #include #include #include #include "lxccontainer.h" #include "macro.h" #include "state.h" /* * Value command callbacks should return when they want the client fd to be * cleaned up by the main loop. This is most certainly what you want unless you * have specific reasons to keep the file descriptor alive. */ #define LXC_CMD_REAP_CLIENT_FD 1 typedef enum { LXC_CMD_CONSOLE, LXC_CMD_TERMINAL_WINCH, LXC_CMD_STOP, LXC_CMD_GET_STATE, LXC_CMD_GET_INIT_PID, LXC_CMD_GET_CLONE_FLAGS, LXC_CMD_GET_CGROUP, LXC_CMD_GET_CONFIG_ITEM, LXC_CMD_GET_NAME, LXC_CMD_GET_LXCPATH, LXC_CMD_ADD_STATE_CLIENT, LXC_CMD_CONSOLE_LOG, LXC_CMD_SERVE_STATE_CLIENTS, LXC_CMD_SECCOMP_NOTIFY_ADD_LISTENER, LXC_CMD_ADD_BPF_DEVICE_CGROUP, LXC_CMD_FREEZE, LXC_CMD_UNFREEZE, LXC_CMD_GET_CGROUP2_FD, LXC_CMD_GET_INIT_PIDFD, LXC_CMD_MAX, } lxc_cmd_t; struct lxc_cmd_req { lxc_cmd_t cmd; int datalen; const void *data; }; struct lxc_cmd_rsp { int ret; /* 0 on success, -errno on failure */ int datalen; void *data; }; struct lxc_cmd_rr { struct lxc_cmd_req req; struct lxc_cmd_rsp rsp; }; struct lxc_cmd_console_rsp_data { int masterfd; int ttynum; }; struct lxc_cmd_console_log { bool clear; bool read; uint64_t read_max; bool write_logfile; }; extern int lxc_cmd_terminal_winch(const char *name, const char *lxcpath); extern int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath); /* * Get the 'real' cgroup path (as seen in /proc/self/cgroup) for a container * for a particular subsystem */ extern char *lxc_cmd_get_cgroup_path(const char *name, const char *lxcpath, const char *subsystem); extern int lxc_cmd_get_clone_flags(const char *name, const char *lxcpath); extern char *lxc_cmd_get_config_item(const char *name, const char *item, const char *lxcpath); extern char *lxc_cmd_get_name(const char *hashed_sock); extern char *lxc_cmd_get_lxcpath(const char *hashed_sock); extern pid_t lxc_cmd_get_init_pid(const char *name, const char *lxcpath); extern int lxc_cmd_get_init_pidfd(const char *name, const char *lxcpath); extern int lxc_cmd_get_state(const char *name, const char *lxcpath); extern int lxc_cmd_stop(const char *name, const char *lxcpath); /* lxc_cmd_add_state_client Register a new state client fd in the container's * in-memory handler. * * @param[in] name Name of container to connect to. * @param[in] lxcpath The lxcpath in which the container is running. * @param[in] states The states to wait for. * @param[out] state_client_fd The state client fd from which the state can be * received. * @return Return < 0 on error * == MAX_STATE when state needs to retrieved * via socket fd * < MAX_STATE current container state */ extern int lxc_cmd_add_state_client(const char *name, const char *lxcpath, lxc_state_t states[MAX_STATE], int *state_client_fd); extern int lxc_cmd_serve_state_clients(const char *name, const char *lxcpath, lxc_state_t state); struct lxc_epoll_descr; struct lxc_handler; extern int lxc_cmd_init(const char *name, const char *lxcpath, const char *suffix); extern int lxc_cmd_mainloop_add(const char *name, struct lxc_epoll_descr *descr, struct lxc_handler *handler); extern int lxc_try_cmd(const char *name, const char *lxcpath); extern int lxc_cmd_console_log(const char *name, const char *lxcpath, struct lxc_console_log *log); extern int lxc_cmd_seccomp_notify_add_listener(const char *name, const char *lxcpath, int fd, /* unused */ unsigned int command, /* unused */ unsigned int flags); struct device_item; extern int lxc_cmd_add_bpf_device_cgroup(const char *name, const char *lxcpath, struct device_item *device); extern int lxc_cmd_freeze(const char *name, const char *lxcpath, int timeout); extern int lxc_cmd_unfreeze(const char *name, const char *lxcpath, int timeout); extern int lxc_cmd_get_cgroup2_fd(const char *name, const char *lxcpath); #endif /* __commands_h */ lxc-4.0.2/src/lxc/lxccontainer.c0000644061062106075000000037615513646120451013471 00000000000000/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/netns_ifaddrs.h" #include "af_unix.h" #include "api_extensions.h" #include "attach.h" #include "cgroup.h" #include "macro.h" #include "commands.h" #include "commands_utils.h" #include "conf.h" #include "config.h" #include "confile.h" #include "confile_utils.h" #include "criu.h" #include "error.h" #include "initutils.h" #include "log.h" #include "lxc.h" #include "lxccontainer.h" #include "lxclock.h" #include "memory_utils.h" #include "monitor.h" #include "namespace.h" #include "network.h" #include "parse.h" #include "raw_syscalls.h" #include "start.h" #include "state.h" #include "storage.h" #include "storage/btrfs.h" #include "storage/overlay.h" #include "storage_utils.h" #include "sync.h" #include "syscall_wrappers.h" #include "terminal.h" #include "utils.h" #include "version.h" #if HAVE_OPENSSL #include #endif /* major()/minor() */ #ifdef MAJOR_IN_MKDEV #include #endif #if IS_BIONIC #include <../include/lxcmntent.h> #else #include #endif #ifndef HAVE_STRLCPY #include "include/strlcpy.h" #endif lxc_log_define(lxccontainer, lxc); static bool do_lxcapi_destroy(struct lxc_container *c); static const char *lxcapi_get_config_path(struct lxc_container *c); #define do_lxcapi_get_config_path(c) lxcapi_get_config_path(c) static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v); static bool container_destroy(struct lxc_container *c, struct lxc_storage *storage); static bool get_snappath_dir(struct lxc_container *c, char *snappath); static bool lxcapi_snapshot_destroy_all(struct lxc_container *c); static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file); static bool config_file_exists(const char *lxcpath, const char *cname) { __do_free char *fname = NULL; int ret; size_t len; /* $lxcpath + '/' + $cname + '/config' + \0 */ len = strlen(lxcpath) + 1 + strlen(cname) + 1 + strlen(LXC_CONFIG_FNAME) + 1; fname = must_realloc(NULL, len); ret = snprintf(fname, len, "%s/%s/%s", lxcpath, cname, LXC_CONFIG_FNAME); if (ret < 0 || (size_t)ret >= len) return false; return file_exists(fname); } /* * A few functions to help detect when a container creation failed. If a * container creation was killed partway through, then trying to actually start * that container could harm the host. We detect this by creating a 'partial' * file under the container directory, and keeping an advisory lock. When * container creation completes, we remove that file. When we load or try to * start a container, if we find that file, without a flock, we remove the * container. */ enum { LXC_CREATE_FAILED = -1, LXC_CREATE_SUCCESS = 0, LXC_CREATE_ONGOING = 1, LXC_CREATE_INCOMPLETE = 2, }; static int ongoing_create(struct lxc_container *c) { __do_close int fd = -EBADF; __do_free char *path = NULL; struct flock lk = {0}; int ret; size_t len; len = strlen(c->config_path) + 1 + strlen(c->name) + 1 + strlen(LXC_PARTIAL_FNAME) + 1; path = must_realloc(NULL, len); ret = snprintf(path, len, "%s/%s/%s", c->config_path, c->name, LXC_PARTIAL_FNAME); if (ret < 0 || (size_t)ret >= len) return LXC_CREATE_FAILED; fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) { if (errno != ENOENT) return LXC_CREATE_FAILED; return LXC_CREATE_SUCCESS; } lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; /* * F_OFD_GETLK requires that l_pid be set to 0 otherwise the kernel * will EINVAL us. */ lk.l_pid = 0; ret = fcntl(fd, F_OFD_GETLK, &lk); if (ret < 0 && errno == EINVAL) { ret = flock(fd, LOCK_EX | LOCK_NB); if (ret < 0 && errno == EWOULDBLOCK) ret = 0; } /* F_OFD_GETLK will not send us back a pid so don't check it. */ if (ret == 0) /* Create is still ongoing. */ return LXC_CREATE_ONGOING; /* Create completed but partial is still there. */ return LXC_CREATE_INCOMPLETE; } static int create_partial(struct lxc_container *c) { __do_free char *path = NULL; int fd, ret; size_t len; struct flock lk = {0}; /* $lxcpath + '/' + $name + '/partial' + \0 */ len = strlen(c->config_path) + 1 + strlen(c->name) + 1 + strlen(LXC_PARTIAL_FNAME) + 1; path = must_realloc(NULL, len); ret = snprintf(path, len, "%s/%s/%s", c->config_path, c->name, LXC_PARTIAL_FNAME); if (ret < 0 || (size_t)ret >= len) return -1; fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0000); if (fd < 0) return -1; lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; ret = fcntl(fd, F_OFD_SETLKW, &lk); if (ret < 0) { if (errno == EINVAL) { ret = flock(fd, LOCK_EX); if (ret == 0) return fd; } SYSERROR("Failed to lock partial file %s", path); close(fd); return -1; } return fd; } static void remove_partial(struct lxc_container *c, int fd) { __do_free char *path = NULL; int ret; size_t len; close(fd); /* $lxcpath + '/' + $name + '/partial' + \0 */ len = strlen(c->config_path) + 1 + strlen(c->name) + 1 + strlen(LXC_PARTIAL_FNAME) + 1; path = must_realloc(NULL, len); ret = snprintf(path, len, "%s/%s/%s", c->config_path, c->name, LXC_PARTIAL_FNAME); if (ret < 0 || (size_t)ret >= len) return; ret = unlink(path); if (ret < 0) SYSERROR("Failed to remove partial file %s", path); } /* LOCKING * 1. container_mem_lock(c) protects the struct lxc_container from multiple threads. * 2. container_disk_lock(c) protects the on-disk container data - in particular the * container configuration file. * The container_disk_lock also takes the container_mem_lock. * 3. thread_mutex protects process data (ex: fd table) from multiple threads. * NOTHING mutexes two independent programs with their own struct * lxc_container for the same c->name, between API calls. For instance, * c->config_read(); c->start(); Between those calls, data on disk * could change (which shouldn't bother the caller unless for instance * the rootfs get moved). c->config_read(); update; c->config_write(); * Two such updaters could race. The callers should therefore check their * results. Trying to prevent that would necessarily expose us to deadlocks * due to hung callers. So I prefer to keep the locks only within our own * functions, not across functions. * * If you're going to clone while holding a lxccontainer, increment * c->numthreads (under privlock) before forking. When deleting, * decrement numthreads under privlock, then if it hits 0 you can delete. * Do not ever use a lxccontainer whose numthreads you did not bump. */ static void lxc_container_free(struct lxc_container *c) { if (!c) return; free(c->configfile); c->configfile = NULL; free(c->error_string); c->error_string = NULL; if (c->slock) { lxc_putlock(c->slock); c->slock = NULL; } if (c->privlock) { lxc_putlock(c->privlock); c->privlock = NULL; } free(c->name); c->name = NULL; if (c->lxc_conf) { lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; } free(c->config_path); c->config_path = NULL; free(c); } /* Consider the following case: * * |====================================================================| * | freer | racing get()er | * |====================================================================| * | lxc_container_put() | lxc_container_get() | * | \ lxclock(c->privlock) | c->numthreads < 1? (no) | * | \ c->numthreads = 0 | \ lxclock(c->privlock) -> waits | * | \ lxcunlock() | \ | * | \ lxc_container_free() | \ lxclock() returns | * | | \ c->numthreads < 1 -> return 0 | * | \ \ (free stuff) | | * | \ \ sem_destroy(privlock) | | * |_______________________________|____________________________________| * * When the get()er checks numthreads the first time, one of the following * is true: * 1. freer has set numthreads = 0. get() returns 0 * 2. freer is between lxclock and setting numthreads to 0. get()er will * sem_wait on privlock, get lxclock after freer() drops it, then see * numthreads is 0 and exit without touching lxclock again.. * 3. freer has not yet locked privlock. If get()er runs first, then put()er * will see --numthreads = 1 and not call lxc_container_free(). */ int lxc_container_get(struct lxc_container *c) { if (!c) return 0; /* If someone else has already started freeing the container, don't try * to take the lock, which may be invalid. */ if (c->numthreads < 1) return 0; if (container_mem_lock(c)) return 0; /* Bail without trying to unlock, bc the privlock is now probably in * freed memory. */ if (c->numthreads < 1) return 0; c->numthreads++; container_mem_unlock(c); return 1; } int lxc_container_put(struct lxc_container *c) { if (!c) return -1; if (container_mem_lock(c)) return -1; c->numthreads--; if (c->numthreads < 1) { container_mem_unlock(c); lxc_container_free(c); return 1; } container_mem_unlock(c); return 0; } static bool do_lxcapi_is_defined(struct lxc_container *c) { int statret; struct stat statbuf; bool ret = false; if (!c) return false; if (container_mem_lock(c)) return false; if (!c->configfile) goto on_error; statret = stat(c->configfile, &statbuf); if (statret != 0) goto on_error; ret = true; on_error: container_mem_unlock(c); return ret; } #define WRAP_API(rettype, fnname) \ static rettype fnname(struct lxc_container *c) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_1(rettype, fnname, t1) \ static rettype fnname(struct lxc_container *c, t1 a1) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_2(rettype, fnname, t1, t2) \ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1, a2); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_3(rettype, fnname, t1, t2, t3) \ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1, a2, a3); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } #define WRAP_API_6(rettype, fnname, t1, t2, t3, t4, t5, t6) \ static rettype fnname(struct lxc_container *c, t1 a1, t2 a2, t3 a3, \ t4 a4, t5 a5, t6 a6) \ { \ rettype ret; \ bool reset_config = false; \ \ if (!current_config && c && c->lxc_conf) { \ current_config = c->lxc_conf; \ reset_config = true; \ } \ \ ret = do_##fnname(c, a1, a2, a3, a4, a5, a6); \ if (reset_config) \ current_config = NULL; \ \ return ret; \ } WRAP_API(bool, lxcapi_is_defined) static const char *do_lxcapi_state(struct lxc_container *c) { lxc_state_t s; if (!c) return NULL; s = lxc_getstate(c->name, c->config_path); return lxc_state2str(s); } WRAP_API(const char *, lxcapi_state) static bool is_stopped(struct lxc_container *c) { lxc_state_t s; s = lxc_getstate(c->name, c->config_path); return (s == STOPPED); } static bool do_lxcapi_is_running(struct lxc_container *c) { if (!c) return false; return !is_stopped(c); } WRAP_API(bool, lxcapi_is_running) static bool do_lxcapi_freeze(struct lxc_container *c) { lxc_state_t s; if (!c || !c->lxc_conf) return false; s = lxc_getstate(c->name, c->config_path); if (s != FROZEN) return lxc_freeze(c->lxc_conf, c->name, c->config_path) == 0; return true; } WRAP_API(bool, lxcapi_freeze) static bool do_lxcapi_unfreeze(struct lxc_container *c) { lxc_state_t s; if (!c || !c->lxc_conf) return false; s = lxc_getstate(c->name, c->config_path); if (s == FROZEN) return lxc_unfreeze(c->lxc_conf, c->name, c->config_path) == 0; return true; } WRAP_API(bool, lxcapi_unfreeze) static int do_lxcapi_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd) { if (!c) return -1; return lxc_terminal_getfd(c, ttynum, masterfd); } WRAP_API_2(int, lxcapi_console_getfd, int *, int *) static int lxcapi_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape) { int ret; if (!c) return -1; current_config = c->lxc_conf; ret = lxc_console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape); current_config = NULL; return ret; } static int do_lxcapi_console_log(struct lxc_container *c, struct lxc_console_log *log) { int ret; if (!c) return -EINVAL; ret = lxc_cmd_console_log(c->name, do_lxcapi_get_config_path(c), log); if (ret < 0) { if (ret == -ENODATA) NOTICE("The console log is empty"); else if (ret == -EFAULT) NOTICE("The container does not keep a console log"); else if (ret == -ENOENT) NOTICE("The container does not keep a console log file"); else if (ret == -EIO) NOTICE("Failed to write console log to log file"); else ERROR("Failed to retrieve console log"); } return ret; } WRAP_API_1(int, lxcapi_console_log, struct lxc_console_log *) static pid_t do_lxcapi_init_pid(struct lxc_container *c) { if (!c) return -1; return lxc_cmd_get_init_pid(c->name, c->config_path); } WRAP_API(pid_t, lxcapi_init_pid) static int do_lxcapi_init_pidfd(struct lxc_container *c) { if (!c) return ret_errno(EBADF); return lxc_cmd_get_init_pidfd(c->name, c->config_path); } WRAP_API(int, lxcapi_init_pidfd) static bool load_config_locked(struct lxc_container *c, const char *fname) { if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); if (!c->lxc_conf) return false; if (lxc_config_read(fname, c->lxc_conf, false) != 0) return false; c->lxc_conf->name = c->name; return true; } static bool do_lxcapi_load_config(struct lxc_container *c, const char *alt_file) { int lret; const char *fname; bool need_disklock = false, ret = false; if (!c) return false; fname = c->configfile; if (alt_file) fname = alt_file; if (!fname) return false; /* If we're reading something other than the container's config, we only * need to lock the in-memory container. If loading the container's * config file, take the disk lock. */ if (strcmp(fname, c->configfile) == 0) need_disklock = true; if (need_disklock) lret = container_disk_lock(c); else lret = container_mem_lock(c); if (lret) return false; ret = load_config_locked(c, fname); if (need_disklock) container_disk_unlock(c); else container_mem_unlock(c); return ret; } WRAP_API_1(bool, lxcapi_load_config, const char *) static bool do_lxcapi_want_daemonize(struct lxc_container *c, bool state) { if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; c->daemonize = state; container_mem_unlock(c); return true; } WRAP_API_1(bool, lxcapi_want_daemonize, bool) static bool do_lxcapi_want_close_all_fds(struct lxc_container *c, bool state) { if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; c->lxc_conf->close_all_fds = state; container_mem_unlock(c); return true; } WRAP_API_1(bool, lxcapi_want_close_all_fds, bool) static bool do_lxcapi_wait(struct lxc_container *c, const char *state, int timeout) { int ret; if (!c) return false; ret = lxc_wait(c->name, state, timeout, c->config_path); return ret == 0; } WRAP_API_2(bool, lxcapi_wait, const char *, int) static bool am_single_threaded(void) { __do_closedir DIR *dir = NULL; struct dirent *direntp; int count = 0; dir = opendir("/proc/self/task"); if (!dir) return false; while ((direntp = readdir(dir))) { if (strcmp(direntp->d_name, ".") == 0) continue; if (strcmp(direntp->d_name, "..") == 0) continue; count++; if (count > 1) break; } return count == 1; } static void push_arg(char ***argp, char *arg, int *nargs) { char *copy; char **argv; copy = must_copy_string(arg); do { argv = realloc(*argp, (*nargs + 2) * sizeof(char *)); } while (!argv); *argp = argv; argv[*nargs] = copy; (*nargs)++; argv[*nargs] = NULL; } static char **split_init_cmd(const char *incmd) { __do_free char *copy = NULL; char *p; char **argv; int nargs = 0; if (!incmd) return NULL; copy = must_copy_string(incmd); do { argv = malloc(sizeof(char *)); } while (!argv); argv[0] = NULL; lxc_iterate_parts (p, copy, " ") push_arg(&argv, p, &nargs); if (nargs == 0) { free(argv); return NULL; } return argv; } static void free_init_cmd(char **argv) { int i = 0; if (!argv) return; while (argv[i]) free(argv[i++]); free(argv); } static int lxc_rcv_status(int state_socket) { int ret; int state = -1; again: /* Receive container state. */ ret = lxc_abstract_unix_rcv_credential(state_socket, &state, sizeof(int)); if (ret <= 0) { if (errno != EINTR) return -1; TRACE("Caught EINTR; retrying"); goto again; } return state; } static bool wait_on_daemonized_start(struct lxc_handler *handler, int pid) { int ret, state; /* The first child is going to fork() again and then exits. So we reap * the first child here. */ ret = wait_for_pid(pid); if (ret < 0) DEBUG("Failed waiting on first child %d", pid); else DEBUG("First child %d exited", pid); /* Close write end of the socket pair. */ close(handler->state_socket_pair[1]); handler->state_socket_pair[1] = -1; state = lxc_rcv_status(handler->state_socket_pair[0]); /* Close read end of the socket pair. */ close(handler->state_socket_pair[0]); handler->state_socket_pair[0] = -1; if (state < 0) { SYSERROR("Failed to receive the container state"); return false; } /* If we receive anything else then running we know that the container * failed to start. */ if (state != RUNNING) { ERROR("Received container state \"%s\" instead of \"RUNNING\"", lxc_state2str(state)); return false; } TRACE("Container is in \"RUNNING\" state"); return true; } static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const argv[]) { int ret; struct lxc_handler *handler; struct lxc_conf *conf; char *default_args[] = { "/sbin/init", NULL, }; char **init_cmd = NULL; int keepfds[3] = {-1, -1, -1}; /* container does exist */ if (!c) return false; /* If anything fails before we set error_num, we want an error in there. */ c->error_num = 1; /* Container has not been setup. */ if (!c->lxc_conf) return false; ret = ongoing_create(c); switch (ret) { case LXC_CREATE_FAILED: ERROR("Failed checking for incomplete container creation"); return false; case LXC_CREATE_ONGOING: ERROR("Ongoing container creation detected"); return false; case LXC_CREATE_INCOMPLETE: ERROR("Failed to create container"); do_lxcapi_destroy(c); return false; } if (container_mem_lock(c)) return false; conf = c->lxc_conf; /* initialize handler */ handler = lxc_init_handler(c->name, conf, c->config_path, c->daemonize); container_mem_unlock(c); if (!handler) return false; if (!argv) { if (useinit && conf->execute_cmd) argv = init_cmd = split_init_cmd(conf->execute_cmd); else argv = init_cmd = split_init_cmd(conf->init_cmd); } /* ... otherwise use default_args. */ if (!argv) { if (useinit) { ERROR("No valid init detected"); lxc_free_handler(handler); return false; } argv = default_args; } /* I'm not sure what locks we want here.Any? Is liblxc's locking enough * here to protect the on disk container? We don't want to exclude * things like lxc_info while the container is running. */ if (c->daemonize) { bool started; char title[2048]; pid_t pid_first, pid_second; pid_first = fork(); if (pid_first < 0) { free_init_cmd(init_cmd); lxc_free_handler(handler); return false; } /* first parent */ if (pid_first != 0) { /* Set to NULL because we don't want father unlink * the PID file, child will do the free and unlink. */ c->pidfile = NULL; /* Wait for container to tell us whether it started * successfully. */ started = wait_on_daemonized_start(handler, pid_first); free_init_cmd(init_cmd); lxc_free_handler(handler); return started; } /* first child */ /* We don't really care if this doesn't print all the * characters. All that it means is that the proctitle will be * ugly. Similarly, we also don't care if setproctitle() fails. */ ret = snprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); if (ret > 0) { ret = setproctitle(title); if (ret < 0) INFO("Failed to set process title to %s", title); else INFO("Set process title to %s", title); } /* We fork() a second time to be reparented to init. Like * POSIX's daemon() function we change to "/" and redirect * std{in,out,err} to /dev/null. */ pid_second = fork(); if (pid_second < 0) { SYSERROR("Failed to fork first child process"); _exit(EXIT_FAILURE); } /* second parent */ if (pid_second != 0) { free_init_cmd(init_cmd); lxc_free_handler(handler); _exit(EXIT_SUCCESS); } /* second child */ /* change to / directory */ ret = chdir("/"); if (ret < 0) { SYSERROR("Failed to change to \"/\" directory"); _exit(EXIT_FAILURE); } keepfds[0] = handler->conf->maincmd_fd; keepfds[1] = handler->state_socket_pair[0]; keepfds[2] = handler->state_socket_pair[1]; ret = lxc_check_inherited(conf, true, keepfds, sizeof(keepfds) / sizeof(keepfds[0])); if (ret < 0) _exit(EXIT_FAILURE); /* redirect std{in,out,err} to /dev/null */ ret = null_stdfds(); if (ret < 0) { ERROR("Failed to redirect std{in,out,err} to /dev/null"); _exit(EXIT_FAILURE); } /* become session leader */ ret = setsid(); if (ret < 0) TRACE("Process %d is already process group leader", lxc_raw_getpid()); } else if (!am_single_threaded()) { ERROR("Cannot start non-daemonized container when threaded"); free_init_cmd(init_cmd); lxc_free_handler(handler); return false; } /* We need to write PID file after daemonize, so we always write the * right PID. */ if (c->pidfile) { int w; char pidstr[INTTYPE_TO_STRLEN(pid_t)]; w = snprintf(pidstr, sizeof(pidstr), "%d", lxc_raw_getpid()); if (w < 0 || (size_t)w >= sizeof(pidstr)) { free_init_cmd(init_cmd); lxc_free_handler(handler); SYSERROR("Failed to write monitor pid to \"%s\"", c->pidfile); if (c->daemonize) _exit(EXIT_FAILURE); return false; } ret = lxc_write_to_file(c->pidfile, pidstr, w, false, 0600); if (ret < 0) { free_init_cmd(init_cmd); lxc_free_handler(handler); SYSERROR("Failed to write monitor pid to \"%s\"", c->pidfile); if (c->daemonize) _exit(EXIT_FAILURE); return false; } } conf->reboot = REBOOT_NONE; /* Unshare the mount namespace if requested */ if (conf->monitor_unshare) { ret = unshare(CLONE_NEWNS); if (ret < 0) { SYSERROR("Failed to unshare mount namespace"); lxc_free_handler(handler); ret = 1; goto on_error; } ret = mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL); if (ret < 0) { SYSERROR("Failed to make / rslave at startup"); lxc_free_handler(handler); ret = 1; goto on_error; } } reboot: if (conf->reboot == REBOOT_INIT) { /* initialize handler */ handler = lxc_init_handler(c->name, conf, c->config_path, c->daemonize); if (!handler) { ret = 1; goto on_error; } } keepfds[0] = handler->conf->maincmd_fd; keepfds[1] = handler->state_socket_pair[0]; keepfds[2] = handler->state_socket_pair[1]; ret = lxc_check_inherited(conf, c->daemonize, keepfds, sizeof(keepfds) / sizeof(keepfds[0])); if (ret < 0) { lxc_free_handler(handler); ret = 1; goto on_error; } if (useinit) ret = lxc_execute(c->name, argv, 1, handler, c->config_path, c->daemonize, &c->error_num); else ret = lxc_start(argv, handler, c->config_path, c->daemonize, &c->error_num); if (conf->reboot == REBOOT_REQ) { INFO("Container requested reboot"); conf->reboot = REBOOT_INIT; goto reboot; } on_error: if (c->pidfile) { unlink(c->pidfile); free(c->pidfile); c->pidfile = NULL; } free_init_cmd(init_cmd); if (c->daemonize && ret != 0) _exit(EXIT_FAILURE); else if (c->daemonize) _exit(EXIT_SUCCESS); if (ret != 0) return false; return true; } static bool lxcapi_start(struct lxc_container *c, int useinit, char *const argv[]) { bool ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_start(c, useinit, argv); current_config = NULL; return ret; } /* Note, there MUST be an ending NULL. */ static bool lxcapi_startl(struct lxc_container *c, int useinit, ...) { va_list ap; char **inargs = NULL; bool bret = false; /* container exists */ if (!c) return false; current_config = c->lxc_conf; va_start(ap, useinit); inargs = lxc_va_arg_list_to_argv(ap, 0, 1); va_end(ap); if (!inargs) goto on_error; /* pass NULL if no arguments were supplied */ bret = do_lxcapi_start(c, useinit, *inargs ? inargs : NULL); on_error: if (inargs) { char **arg; for (arg = inargs; *arg; arg++) free(*arg); free(inargs); } current_config = NULL; return bret; } static bool do_lxcapi_stop(struct lxc_container *c) { int ret; if (!c) return false; ret = lxc_cmd_stop(c->name, c->config_path); return ret == 0; } WRAP_API(bool, lxcapi_stop) static int do_create_container_dir(const char *path, struct lxc_conf *conf) { __do_free char *p = NULL; int lasterr; int ret = -1; mode_t mask = umask(0002); ret = mkdir(path, 0770); lasterr = errno; umask(mask); errno = lasterr; if (ret) { if (errno != EEXIST) return -1; ret = 0; } p = must_copy_string(path); if (!lxc_list_empty(&conf->id_map)) { ret = chown_mapped_root(p, conf); if (ret < 0) ret = -1; } return ret; } /* Create the standard expected container dir. */ static bool create_container_dir(struct lxc_container *c) { int ret; size_t len; char *s; len = strlen(c->config_path) + strlen(c->name) + 2; s = malloc(len); if (!s) return false; ret = snprintf(s, len, "%s/%s", c->config_path, c->name); if (ret < 0 || (size_t)ret >= len) { free(s); return false; } ret = do_create_container_dir(s, c->lxc_conf); free(s); return ret == 0; } /* do_storage_create: thin wrapper around storage_create(). Like * storage_create(), it returns a mounted bdev on success, NULL on error. */ static struct lxc_storage *do_storage_create(struct lxc_container *c, const char *type, struct bdev_specs *specs) { __do_free char *dest = NULL; int ret; size_t len; struct lxc_storage *bdev; /* rootfs.path or lxcpath/lxcname/rootfs */ if (c->lxc_conf->rootfs.path && (access(c->lxc_conf->rootfs.path, F_OK) == 0)) { const char *rpath = c->lxc_conf->rootfs.path; len = strlen(rpath) + 1; dest = must_realloc(NULL, len); ret = snprintf(dest, len, "%s", rpath); } else { const char *lxcpath = do_lxcapi_get_config_path(c); len = strlen(c->name) + 1 + strlen(lxcpath) + 1 + strlen(LXC_ROOTFS_DNAME) + 1; dest = must_realloc(NULL, len); ret = snprintf(dest, len, "%s/%s/%s", lxcpath, c->name, LXC_ROOTFS_DNAME); } if (ret < 0 || (size_t)ret >= len) return NULL; bdev = storage_create(dest, type, c->name, specs, c->lxc_conf); if (!bdev) { ERROR("Failed to create \"%s\" storage", type); return NULL; } if (!c->set_config_item(c, "lxc.rootfs.path", bdev->src)) { ERROR("Failed to set \"lxc.rootfs.path = %s\"", bdev->src); storage_put(bdev); return NULL; } /* If we are not root, chown the rootfs dir to root in the target user * namespace. */ if (am_guest_unpriv() || !lxc_list_empty(&c->lxc_conf->id_map)) { ret = chown_mapped_root(bdev->dest, c->lxc_conf); if (ret < 0) { ERROR("Error chowning \"%s\" to container root", bdev->dest); suggest_default_idmap(); storage_put(bdev); return NULL; } } return bdev; } /* Strip path and return name of file for argv[0] passed to execvp */ static char *lxctemplatefilename(char *tpath) { char *p; p = tpath + strlen(tpath) - 1; while ( (p-1) >= tpath && *(p-1) != '/') p--; return p; } static bool create_run_template(struct lxc_container *c, char *tpath, bool need_null_stdfds, char *const argv[]) { int ret; pid_t pid; if (!tpath) return true; pid = fork(); if (pid < 0) { SYSERROR("Failed to fork task for container creation template"); return false; } if (pid == 0) { /* child */ int i, len; char *namearg, *patharg, *rootfsarg; char **newargv; int nargs = 0; struct lxc_storage *bdev = NULL; struct lxc_conf *conf = c->lxc_conf; uid_t euid; if (need_null_stdfds) { ret = null_stdfds(); if (ret < 0) _exit(EXIT_FAILURE); } bdev = storage_init(c->lxc_conf); if (!bdev) { ERROR("Failed to initialize storage"); _exit(EXIT_FAILURE); } euid = geteuid(); if (euid == 0) { ret = unshare(CLONE_NEWNS); if (ret < 0) { ERROR("Failed to unshare CLONE_NEWNS"); _exit(EXIT_FAILURE); } ret = detect_shared_rootfs(); if (ret == 1) { ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL); if (ret < 0) { SYSERROR("Failed to make \"/\" rslave"); ERROR("Continuing..."); } } } if (strcmp(bdev->type, "dir") != 0 && strcmp(bdev->type, "btrfs") != 0) { if (euid != 0) { ERROR("Unprivileged users can only create " "btrfs and directory-backed containers"); _exit(EXIT_FAILURE); } if (strcmp(bdev->type, "overlay") == 0 || strcmp(bdev->type, "overlayfs") == 0) { /* If we create an overlay container we need to * rsync the contents into * //rootfs. * However, the overlay mount function will * mount * //delta0 * over * //rootfs * which means we would rsync the rootfs into * the delta directory. That doesn't make sense * since the delta directory only exists to * record the differences to * //rootfs. So * let's simply bind-mount here and then rsync * directly into * //rootfs. */ char *src; src = ovl_get_rootfs(bdev->src, &(size_t){0}); if (!src) { ERROR("Failed to get rootfs"); _exit(EXIT_FAILURE); } ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC, NULL); if (ret < 0) { ERROR("Failed to mount rootfs"); _exit(EXIT_FAILURE); } } else { ret = bdev->ops->mount(bdev); if (ret < 0) { ERROR("Failed to mount rootfs"); _exit(EXIT_FAILURE); } } } else { /* TODO come up with a better way here! */ const char *src; free(bdev->dest); src = lxc_storage_get_path(bdev->src, bdev->type); bdev->dest = strdup(src); } /* Create our new array, pre-pend the template name and base * args. */ if (argv) for (nargs = 0; argv[nargs]; nargs++) ; /* template, path, rootfs and name args */ nargs += 4; newargv = malloc(nargs * sizeof(*newargv)); if (!newargv) _exit(EXIT_FAILURE); newargv[0] = lxctemplatefilename(tpath); /* --path */ len = strlen(c->config_path) + strlen(c->name) + strlen("--path=") + 2; patharg = malloc(len); if (!patharg) _exit(EXIT_FAILURE); ret = snprintf(patharg, len, "--path=%s/%s", c->config_path, c->name); if (ret < 0 || ret >= len) _exit(EXIT_FAILURE); newargv[1] = patharg; /* --name */ len = strlen("--name=") + strlen(c->name) + 1; namearg = malloc(len); if (!namearg) _exit(EXIT_FAILURE); ret = snprintf(namearg, len, "--name=%s", c->name); if (ret < 0 || ret >= len) _exit(EXIT_FAILURE); newargv[2] = namearg; /* --rootfs */ len = strlen("--rootfs=") + 1 + strlen(bdev->dest); rootfsarg = malloc(len); if (!rootfsarg) _exit(EXIT_FAILURE); ret = snprintf(rootfsarg, len, "--rootfs=%s", bdev->dest); if (ret < 0 || ret >= len) _exit(EXIT_FAILURE); newargv[3] = rootfsarg; /* add passed-in args */ if (argv) for (i = 4; i < nargs; i++) newargv[i] = argv[i - 4]; /* add trailing NULL */ nargs++; newargv = realloc(newargv, nargs * sizeof(*newargv)); if (!newargv) _exit(EXIT_FAILURE); newargv[nargs - 1] = NULL; /* If we're running the template in a mapped userns, then we * prepend the template command with: lxc-usernsexec <-m map1> * ... <-m mapn> -- and we append "--mapped-uid x", where x is * the mapped uid for our geteuid() */ if (!lxc_list_empty(&conf->id_map)) { int extraargs, hostuid_mapped, hostgid_mapped; char **n2; char txtuid[20], txtgid[20]; struct lxc_list *it; struct id_map *map; int n2args = 1; n2 = malloc(n2args * sizeof(*n2)); if (!n2) _exit(EXIT_FAILURE); newargv[0] = tpath; tpath = "lxc-usernsexec"; n2[0] = "lxc-usernsexec"; lxc_list_for_each(it, &conf->id_map) { map = it->elem; n2args += 2; n2 = realloc(n2, n2args * sizeof(char *)); if (!n2) _exit(EXIT_FAILURE); n2[n2args - 2] = "-m"; n2[n2args - 1] = malloc(200); if (!n2[n2args - 1]) _exit(EXIT_FAILURE); ret = snprintf(n2[n2args - 1], 200, "%c:%lu:%lu:%lu", map->idtype == ID_TYPE_UID ? 'u' : 'g', map->nsid, map->hostid, map->range); if (ret < 0 || ret >= 200) _exit(EXIT_FAILURE); } hostuid_mapped = mapped_hostid(geteuid(), conf, ID_TYPE_UID); extraargs = hostuid_mapped >= 0 ? 1 : 3; n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *)); if (!n2) _exit(EXIT_FAILURE); if (hostuid_mapped < 0) { hostuid_mapped = find_unmapped_nsid(conf, ID_TYPE_UID); n2[n2args++] = "-m"; if (hostuid_mapped < 0) { ERROR("Failed to find free uid to map"); _exit(EXIT_FAILURE); } n2[n2args++] = malloc(200); if (!n2[n2args - 1]) { SYSERROR("out of memory"); _exit(EXIT_FAILURE); } ret = snprintf(n2[n2args - 1], 200, "u:%d:%d:1", hostuid_mapped, geteuid()); if (ret < 0 || ret >= 200) _exit(EXIT_FAILURE); } hostgid_mapped = mapped_hostid(getegid(), conf, ID_TYPE_GID); extraargs = hostgid_mapped >= 0 ? 1 : 3; n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *)); if (!n2) _exit(EXIT_FAILURE); if (hostgid_mapped < 0) { hostgid_mapped = find_unmapped_nsid(conf, ID_TYPE_GID); n2[n2args++] = "-m"; if (hostgid_mapped < 0) { ERROR("Failed to find free gid to map"); _exit(EXIT_FAILURE); } n2[n2args++] = malloc(200); if (!n2[n2args - 1]) { SYSERROR("out of memory"); _exit(EXIT_FAILURE); } ret = snprintf(n2[n2args - 1], 200, "g:%d:%d:1", hostgid_mapped, getegid()); if (ret < 0 || ret >= 200) _exit(EXIT_FAILURE); } n2[n2args++] = "--"; for (i = 0; i < nargs; i++) n2[i + n2args] = newargv[i]; n2args += nargs; /* Finally add "--mapped-uid $uid" to tell template what * to chown cached images to. */ n2args += 4; n2 = realloc(n2, n2args * sizeof(char *)); if (!n2) _exit(EXIT_FAILURE); /* note n2[n2args-1] is NULL */ n2[n2args - 5] = "--mapped-uid"; ret = snprintf(txtuid, 20, "%d", hostuid_mapped); if (ret < 0 || ret >= 20) { free(newargv); free(n2); _exit(EXIT_FAILURE); } n2[n2args - 4] = txtuid; n2[n2args - 3] = "--mapped-gid"; ret = snprintf(txtgid, 20, "%d", hostgid_mapped); if (ret < 0 || ret >= 20) { free(newargv); free(n2); _exit(EXIT_FAILURE); } n2[n2args - 2] = txtgid; n2[n2args - 1] = NULL; free(newargv); newargv = n2; } execvp(tpath, newargv); SYSERROR("Failed to execute template %s", tpath); _exit(EXIT_FAILURE); } ret = wait_for_pid(pid); if (ret != 0) { ERROR("Failed to create container from template"); return false; } return true; } static bool prepend_lxc_header(char *path, const char *t, char *const argv[]) { long flen; size_t len; char *contents; FILE *f; int ret = -1; #if HAVE_OPENSSL int i; unsigned int md_len = 0; unsigned char md_value[EVP_MAX_MD_SIZE]; char *tpath; #endif f = fopen(path, "re"); if (f == NULL) return false; ret = fseek(f, 0, SEEK_END); if (ret < 0) goto out_error; ret = -1; flen = ftell(f); if (flen < 0) goto out_error; ret = fseek(f, 0, SEEK_SET); if (ret < 0) goto out_error; ret = fseek(f, 0, SEEK_SET); if (ret < 0) goto out_error; ret = -1; contents = malloc(flen + 1); if (!contents) goto out_error; len = fread(contents, 1, flen, f); if (len != flen) goto out_free_contents; contents[flen] = '\0'; ret = fclose(f); f = NULL; if (ret < 0) goto out_free_contents; #if HAVE_OPENSSL tpath = get_template_path(t); if (!tpath) { ERROR("Invalid template \"%s\" specified", t); goto out_free_contents; } ret = sha1sum_file(tpath, md_value, &md_len); if (ret < 0) { ERROR("Failed to get sha1sum of %s", tpath); free(tpath); goto out_free_contents; } free(tpath); #endif f = fopen(path, "we"); if (f == NULL) { SYSERROR("Reopening config for writing"); free(contents); return false; } fprintf(f, "# Template used to create this container: %s\n", t); if (argv) { fprintf(f, "# Parameters passed to the template:"); while (*argv) { fprintf(f, " %s", *argv); argv++; } fprintf(f, "\n"); } #if HAVE_OPENSSL fprintf(f, "# Template script checksum (SHA-1): "); for (i=0; ilxc_conf) return; lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; } #define do_lxcapi_clear_config(c) lxcapi_clear_config(c) /* * lxcapi_create: * create a container with the given parameters. * @c: container to be created. It has the lxcpath, name, and a starting * configuration already set * @t: the template to execute to instantiate the root filesystem and * adjust the configuration. * @bdevtype: backing store type to use. If NULL, dir will be used. * @specs: additional parameters for the backing store, i.e. LVM vg to * use. * * @argv: the arguments to pass to the template, terminated by NULL. If no * arguments, you can just pass NULL. */ static bool do_lxcapi_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]) { int partial_fd; mode_t mask; pid_t pid; bool ret = false, rootfs_managed = true; char *tpath = NULL; if (!c) return false; if (t) { tpath = get_template_path(t); if (!tpath) { ERROR("Unknown template \"%s\"", t); goto out; } } /* If a template is passed in, and the rootfs already is defined in the * container config and exists, then the caller is trying to create an * existing container. Return an error, but do NOT delete the container. */ if (do_lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0 && tpath) { ERROR("Container \"%s\" already exists in \"%s\"", c->name, c->config_path); goto free_tpath; } if (!c->lxc_conf) { if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) { ERROR("Error loading default configuration file %s", lxc_global_config_value("lxc.default_config")); goto free_tpath; } } if (!create_container_dir(c)) goto free_tpath; if (c->lxc_conf->rootfs.path) rootfs_managed = false; /* If both template and rootfs.path are set, template is setup as * rootfs.path. The container is already created if we have a config and * rootfs.path is accessible */ if (!c->lxc_conf->rootfs.path && !tpath) { /* No template passed in and rootfs does not exist. */ if (!c->save_config(c, NULL)) { ERROR("Failed to save initial config for \"%s\"", c->name); goto out; } ret = true; goto out; } /* Rootfs passed into configuration, but does not exist. */ if (c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) != 0) goto out; if (do_lxcapi_is_defined(c) && c->lxc_conf->rootfs.path && !tpath) { /* Rootfs already existed, user just wanted to save the loaded * configuration. */ if (!c->save_config(c, NULL)) ERROR("Failed to save initial config for \"%s\"", c->name); ret = true; goto out; } /* Mark that this container is being created */ partial_fd = create_partial(c); if (partial_fd < 0) goto out; /* No need to get disk lock bc we have the partial lock. */ mask = umask(0022); /* Create the storage. * Note we can't do this in the same task as we use to execute the * template because of the way zfs works. * After you 'zfs create', zfs mounts the fs only in the initial * namespace. */ pid = fork(); if (pid < 0) { SYSERROR("Failed to fork task for container creation template"); goto out_unlock; } if (pid == 0) { /* child */ struct lxc_storage *bdev = NULL; bdev = do_storage_create(c, bdevtype, specs); if (!bdev) { ERROR("Failed to create %s storage for %s", bdevtype ? bdevtype : "(none)", c->name); _exit(EXIT_FAILURE); } /* Save config file again to store the new rootfs location. */ if (!do_lxcapi_save_config(c, NULL)) { ERROR("Failed to save initial config for %s", c->name); /* Parent task won't see the storage driver in the * config so we delete it. */ bdev->ops->umount(bdev); bdev->ops->destroy(bdev); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } if (wait_for_pid(pid) != 0) goto out_unlock; /* Reload config to get the rootfs. */ lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL; if (!load_config_locked(c, c->configfile)) goto out_unlock; if (!create_run_template(c, tpath, !!(flags & LXC_CREATE_QUIET), argv)) goto out_unlock; /* Now clear out the lxc_conf we have, reload from the created * container. */ do_lxcapi_clear_config(c); if (t) { if (!prepend_lxc_header(c->configfile, tpath, argv)) { ERROR("Failed to prepend header to config file"); goto out_unlock; } } ret = load_config_locked(c, c->configfile); out_unlock: umask(mask); remove_partial(c, partial_fd); out: if (!ret) { bool reset_managed = c->lxc_conf->rootfs.managed; /* * Ensure that we don't destroy storage we didn't create * ourselves. */ if (!rootfs_managed) c->lxc_conf->rootfs.managed = false; container_destroy(c, NULL); c->lxc_conf->rootfs.managed = reset_managed; } free_tpath: free(tpath); return ret; } static bool lxcapi_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char *const argv[]) { bool ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_create(c, t, bdevtype, specs, flags, argv); current_config = NULL; return ret; } static bool do_lxcapi_reboot(struct lxc_container *c) { __do_close int pidfd = -EBADF; pid_t pid = -1; int ret; int rebootsignal = SIGINT; if (!c) return false; if (!do_lxcapi_is_running(c)) return false; pidfd = do_lxcapi_init_pidfd(c); if (pidfd < 0) { pid = do_lxcapi_init_pid(c); if (pid <= 0) return false; } if (c->lxc_conf && c->lxc_conf->rebootsignal) rebootsignal = c->lxc_conf->rebootsignal; if (pidfd >= 0) ret = lxc_raw_pidfd_send_signal(pidfd, rebootsignal, NULL, 0); else ret = kill(pid, rebootsignal); if (ret < 0) return log_warn(false, "Failed to send signal %d to pid %d", rebootsignal, pid); return true; } WRAP_API(bool, lxcapi_reboot) static bool do_lxcapi_reboot2(struct lxc_container *c, int timeout) { __do_close int pidfd = -EBADF, state_client_fd = -EBADF; int rebootsignal = SIGINT; pid_t pid = -1; lxc_state_t states[MAX_STATE] = {0}; int killret, ret; if (!c) return false; if (!do_lxcapi_is_running(c)) return true; pidfd = do_lxcapi_init_pidfd(c); if (pidfd < 0) { pid = do_lxcapi_init_pid(c); if (pid <= 0) return true; } if (c->lxc_conf && c->lxc_conf->rebootsignal) rebootsignal = c->lxc_conf->rebootsignal; /* Add a new state client before sending the shutdown signal so that we * don't miss a state. */ if (timeout != 0) { states[RUNNING] = 2; ret = lxc_cmd_add_state_client(c->name, c->config_path, states, &state_client_fd); if (ret < 0) return false; if (state_client_fd < 0) return false; if (ret == RUNNING) return true; if (ret < MAX_STATE) return false; } /* Send reboot signal to container. */ if (pidfd >= 0) killret = lxc_raw_pidfd_send_signal(pidfd, rebootsignal, NULL, 0); else killret = kill(pid, rebootsignal); if (killret < 0) return log_warn(false, "Failed to send signal %d to pid %d", rebootsignal, pid); TRACE("Sent signal %d to pid %d", rebootsignal, pid); if (timeout == 0) return true; ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); if (ret < 0) return false; TRACE("Received state \"%s\"", lxc_state2str(ret)); if (ret != RUNNING) return false; return true; } WRAP_API_1(bool, lxcapi_reboot2, int) static bool do_lxcapi_shutdown(struct lxc_container *c, int timeout) { __do_close int pidfd = -EBADF, state_client_fd = -EBADF; int haltsignal = SIGPWR; pid_t pid = -1; lxc_state_t states[MAX_STATE] = {0}; int killret, ret; if (!c) return false; if (!do_lxcapi_is_running(c)) return true; pidfd = do_lxcapi_init_pidfd(c); pid = do_lxcapi_init_pid(c); if (pid <= 0) return true; /* Detect whether we should send SIGRTMIN + 3 (e.g. systemd). */ if (c->lxc_conf && c->lxc_conf->haltsignal) haltsignal = c->lxc_conf->haltsignal; else if (task_blocks_signal(pid, (SIGRTMIN + 3))) haltsignal = (SIGRTMIN + 3); /* * Add a new state client before sending the shutdown signal so * that we don't miss a state. */ if (timeout != 0) { states[STOPPED] = 1; ret = lxc_cmd_add_state_client(c->name, c->config_path, states, &state_client_fd); if (ret < 0) return false; if (state_client_fd < 0) return false; if (ret == STOPPED) return true; if (ret < MAX_STATE) return false; if (pidfd >= 0) { struct pollfd pidfd_poll = { .events = POLLIN, .fd = pidfd, }; killret = lxc_raw_pidfd_send_signal(pidfd, haltsignal, NULL, 0); if (killret < 0) return log_warn(false, "Failed to send signal %d to pidfd %d", haltsignal, pidfd); TRACE("Sent signal %d to pidfd %d", haltsignal, pidfd); /* * No need for going through all of the state server * complications anymore. We can just poll on pidfds. :) */ if (timeout != 0) { ret = poll(&pidfd_poll, 1, timeout * 1000); if (ret < 0 || !(pidfd_poll.revents & POLLIN)) return false; TRACE("Pidfd polling detected container exit"); } } else { killret = kill(pid, haltsignal); if (killret < 0) return log_warn(false, "Failed to send signal %d to pid %d", haltsignal, pid); TRACE("Sent signal %d to pid %d", haltsignal, pid); } } if (timeout == 0) return true; ret = lxc_cmd_sock_rcv_state(state_client_fd, timeout); if (ret < 0) return false; TRACE("Received state \"%s\"", lxc_state2str(ret)); if (ret != STOPPED) return false; return true; } WRAP_API_1(bool, lxcapi_shutdown, int) static bool lxcapi_createl(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, ...) { bool bret = false; char **args = NULL; va_list ap; if (!c) return false; current_config = c->lxc_conf; /* * since we're going to wait for create to finish, I don't think we * need to get a copy of the arguments. */ va_start(ap, flags); args = lxc_va_arg_list_to_argv(ap, 0, 0); va_end(ap); if (!args) { ERROR("Failed to allocate memory"); goto out; } bret = do_lxcapi_create(c, t, bdevtype, specs, flags, args); out: free(args); current_config = NULL; return bret; } static void do_clear_unexp_config_line(struct lxc_conf *conf, const char *key) { if (!strcmp(key, "lxc.cgroup")) return clear_unexp_config_line(conf, key, true); if (!strcmp(key, "lxc.network")) return clear_unexp_config_line(conf, key, true); if (!strcmp(key, "lxc.net")) return clear_unexp_config_line(conf, key, true); /* Clear a network with a specific index. */ if (!strncmp(key, "lxc.net.", 8)) { int ret; const char *idx; idx = key + 8; ret = lxc_safe_uint(idx, &(unsigned int){0}); if (!ret) return clear_unexp_config_line(conf, key, true); } if (!strcmp(key, "lxc.hook")) return clear_unexp_config_line(conf, key, true); return clear_unexp_config_line(conf, key, false); } static bool do_lxcapi_clear_config_item(struct lxc_container *c, const char *key) { int ret = 1; struct lxc_config_t *config; if (!c || !c->lxc_conf) return false; if (container_mem_lock(c)) return false; config = lxc_get_config(key); /* Verify that the config key exists and that it has a callback * implemented. */ if (config && config->clr) ret = config->clr(key, c->lxc_conf, NULL); if (!ret) do_clear_unexp_config_line(c->lxc_conf, key); container_mem_unlock(c); return ret == 0; } WRAP_API_1(bool, lxcapi_clear_config_item, const char *) static inline bool enter_net_ns(struct lxc_container *c) { pid_t pid = do_lxcapi_init_pid(c); if (pid < 0) return false; if ((geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) && (access("/proc/self/ns/user", F_OK) == 0)) if (!switch_to_ns(pid, "user")) return false; return switch_to_ns(pid, "net"); } /* Used by qsort and bsearch functions for comparing names. */ static inline int string_cmp(char **first, char **second) { return strcmp(*first, *second); } /* Used by qsort and bsearch functions for comparing container names. */ static inline int container_cmp(struct lxc_container **first, struct lxc_container **second) { return strcmp((*first)->name, (*second)->name); } static bool add_to_array(char ***names, char *cname, int pos) { char **newnames = realloc(*names, (pos+1) * sizeof(char *)); if (!newnames) { ERROR("Out of memory"); return false; } *names = newnames; newnames[pos] = strdup(cname); if (!newnames[pos]) return false; /* Sort the array as we will use binary search on it. */ qsort(newnames, pos + 1, sizeof(char *), (int (*)(const void *, const void *))string_cmp); return true; } static bool add_to_clist(struct lxc_container ***list, struct lxc_container *c, int pos, bool sort) { struct lxc_container **newlist = realloc(*list, (pos + 1) * sizeof(struct lxc_container *)); if (!newlist) { ERROR("Out of memory"); return false; } *list = newlist; newlist[pos] = c; /* Sort the array as we will use binary search on it. */ if (sort) qsort(newlist, pos + 1, sizeof(struct lxc_container *), (int (*)(const void *, const void *))container_cmp); return true; } static char** get_from_array(char ***names, char *cname, int size) { return (char **)bsearch(&cname, *names, size, sizeof(char *), (int (*)(const void *, const void *))string_cmp); } static bool array_contains(char ***names, char *cname, int size) { if(get_from_array(names, cname, size) != NULL) return true; return false; } static bool remove_from_array(char ***names, char *cname, int size) { char **result = get_from_array(names, cname, size); if (result != NULL) { free(result); return true; } return false; } static char **do_lxcapi_get_interfaces(struct lxc_container *c) { pid_t pid; int i, count = 0, pipefd[2]; char **interfaces = NULL; char interface[IFNAMSIZ]; if (pipe2(pipefd, O_CLOEXEC) < 0) return NULL; pid = fork(); if (pid < 0) { SYSERROR("Failed to fork task to get interfaces information"); close(pipefd[0]); close(pipefd[1]); return NULL; } if (pid == 0) { /* child */ int ret = 1, nbytes; struct netns_ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; /* close the read-end of the pipe */ close(pipefd[0]); if (!enter_net_ns(c)) { SYSERROR("Failed to enter network namespace"); goto out; } /* Grab the list of interfaces */ if (netns_getifaddrs(&interfaceArray, -1, &(bool){false})) { SYSERROR("Failed to get interfaces list"); goto out; } /* Iterate through the interfaces */ for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { nbytes = lxc_write_nointr(pipefd[1], tempIfAddr->ifa_name, IFNAMSIZ); if (nbytes < 0) goto out; count++; } ret = 0; out: if (interfaceArray) netns_freeifaddrs(interfaceArray); /* close the write-end of the pipe, thus sending EOF to the reader */ close(pipefd[1]); _exit(ret); } /* close the write-end of the pipe */ close(pipefd[1]); while (lxc_read_nointr(pipefd[0], &interface, IFNAMSIZ) == IFNAMSIZ) { interface[IFNAMSIZ - 1] = '\0'; if (array_contains(&interfaces, interface, count)) continue; if (!add_to_array(&interfaces, interface, count)) ERROR("Failed to add \"%s\" to array", interface); count++; } if (wait_for_pid(pid) != 0) { for (i = 0; i < count; i++) free(interfaces[i]); free(interfaces); interfaces = NULL; } /* close the read-end of the pipe */ close(pipefd[0]); /* Append NULL to the array */ if (interfaces) interfaces = (char **)lxc_append_null_to_array((void **)interfaces, count); return interfaces; } WRAP_API(char **, lxcapi_get_interfaces) static char **do_lxcapi_get_ips(struct lxc_container *c, const char *interface, const char *family, int scope) { int i, ret; pid_t pid; int pipefd[2]; char address[INET6_ADDRSTRLEN]; int count = 0; char **addresses = NULL; ret = pipe2(pipefd, O_CLOEXEC); if (ret < 0) { SYSERROR("Failed to create pipe"); return NULL; } pid = fork(); if (pid < 0) { SYSERROR("Failed to create new process"); close(pipefd[0]); close(pipefd[1]); return NULL; } if (pid == 0) { ssize_t nbytes; char addressOutputBuffer[INET6_ADDRSTRLEN]; char *address_ptr = NULL; void *tempAddrPtr = NULL; struct netns_ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; /* close the read-end of the pipe */ close(pipefd[0]); if (!enter_net_ns(c)) { SYSERROR("Failed to attach to network namespace"); goto out; } /* Grab the list of interfaces */ if (netns_getifaddrs(&interfaceArray, -1, &(bool){false})) { SYSERROR("Failed to get interfaces list"); goto out; } /* Iterate through the interfaces */ for (tempIfAddr = interfaceArray; tempIfAddr; tempIfAddr = tempIfAddr->ifa_next) { if (tempIfAddr->ifa_addr == NULL) continue; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" if (tempIfAddr->ifa_addr->sa_family == AF_INET) { if (family && strcmp(family, "inet")) continue; tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr; } else { if (family && strcmp(family, "inet6")) continue; if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope) continue; tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr; } #pragma GCC diagnostic pop if (interface && strcmp(interface, tempIfAddr->ifa_name)) continue; else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0) continue; address_ptr = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family, tempAddrPtr, addressOutputBuffer, sizeof(addressOutputBuffer)); if (!address_ptr) continue; nbytes = lxc_write_nointr(pipefd[1], address_ptr, INET6_ADDRSTRLEN); if (nbytes != INET6_ADDRSTRLEN) { SYSERROR("Failed to send ipv6 address \"%s\"", address_ptr); goto out; } count++; } ret = 0; out: if (interfaceArray) netns_freeifaddrs(interfaceArray); /* close the write-end of the pipe, thus sending EOF to the reader */ close(pipefd[1]); _exit(ret); } /* close the write-end of the pipe */ close(pipefd[1]); while (lxc_read_nointr(pipefd[0], &address, INET6_ADDRSTRLEN) == INET6_ADDRSTRLEN) { address[INET6_ADDRSTRLEN - 1] = '\0'; if (!add_to_array(&addresses, address, count)) ERROR("PARENT: add_to_array failed"); count++; } if (wait_for_pid(pid) != 0) { for (i = 0; i < count; i++) free(addresses[i]); free(addresses); addresses = NULL; } /* close the read-end of the pipe */ close(pipefd[0]); /* Append NULL to the array */ if (addresses) addresses = (char **)lxc_append_null_to_array((void **)addresses, count); return addresses; } WRAP_API_3(char **, lxcapi_get_ips, const char *, const char *, int) static int do_lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen) { int ret = -1; struct lxc_config_t *config; if (!c || !c->lxc_conf) return -1; if (container_mem_lock(c)) return -1; config = lxc_get_config(key); /* Verify that the config key exists and that it has a callback * implemented. */ if (config && config->get) ret = config->get(key, retv, inlen, c->lxc_conf, NULL); container_mem_unlock(c); return ret; } WRAP_API_3(int, lxcapi_get_config_item, const char *, char *, int) static char* do_lxcapi_get_running_config_item(struct lxc_container *c, const char *key) { char *ret; if (!c || !c->lxc_conf) return NULL; if (container_mem_lock(c)) return NULL; ret = lxc_cmd_get_config_item(c->name, key, do_lxcapi_get_config_path(c)); container_mem_unlock(c); return ret; } WRAP_API_1(char *, lxcapi_get_running_config_item, const char *) static int do_lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, int inlen) { int ret = -1; /* List all config items. */ if (!key) return lxc_list_config_items(retv, inlen); if (!c || !c->lxc_conf) return -1; if (container_mem_lock(c)) return -1; /* Support 'lxc.net.', i.e. 'lxc.net.0' * This is an intelligent result to show which keys are valid given the * type of nic it is. */ if (strncmp(key, "lxc.net.", 8) == 0) ret = lxc_list_net(c->lxc_conf, key, retv, inlen); else ret = lxc_list_subkeys(c->lxc_conf, key, retv, inlen); container_mem_unlock(c); return ret; } WRAP_API_3(int, lxcapi_get_keys, const char *, char *, int) static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file) { int fd, lret; bool ret = false, need_disklock = false; if (!alt_file) alt_file = c->configfile; if (!alt_file) return false; /* If we haven't yet loaded a config, load the stock config. */ if (!c->lxc_conf) { if (!do_lxcapi_load_config(c, lxc_global_config_value("lxc.default_config"))) { ERROR("Error loading default configuration file %s " "while saving %s", lxc_global_config_value("lxc.default_config"), c->name); return false; } } if (!create_container_dir(c)) return false; /* If we're writing to the container's config file, take the disk lock. * Otherwise just take the memlock to protect the struct lxc_container * while we're traversing it. */ if (strcmp(c->configfile, alt_file) == 0) need_disklock = true; if (need_disklock) lret = container_disk_lock(c); else lret = container_mem_lock(c); if (lret) return false; fd = open(alt_file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (fd < 0) goto on_error; lret = write_config(fd, c->lxc_conf); close(fd); if (lret < 0) goto on_error; ret = true; on_error: if (need_disklock) container_disk_unlock(c); else container_mem_unlock(c); return ret; } WRAP_API_1(bool, lxcapi_save_config, const char *) static bool mod_rdep(struct lxc_container *c0, struct lxc_container *c, bool inc) { FILE *f1; struct stat fbuf; void *buf = NULL; char *del = NULL; char path[PATH_MAX]; char newpath[PATH_MAX]; int fd, ret, n = 0, v = 0; bool bret = false; size_t len = 0, bytes = 0; if (container_disk_lock(c0)) return false; ret = snprintf(path, PATH_MAX, "%s/%s/lxc_snapshots", c0->config_path, c0->name); if (ret < 0 || ret > PATH_MAX) goto out; ret = snprintf(newpath, PATH_MAX, "%s\n%s\n", c->config_path, c->name); if (ret < 0 || ret > PATH_MAX) goto out; /* If we find an lxc-snapshot file using the old format only listing the * number of snapshots we will keep using it. */ f1 = fopen(path, "re"); if (f1) { n = fscanf(f1, "%d", &v); fclose(f1); if (n == 1 && v == 0) { ret = remove(path); if (ret < 0) SYSERROR("Failed to remove \"%s\"", path); n = 0; } } if (n == 1) { v += inc ? 1 : -1; f1 = fopen(path, "we"); if (!f1) goto out; if (fprintf(f1, "%d\n", v) < 0) { ERROR("Error writing new snapshots value"); fclose(f1); goto out; } ret = fclose(f1); if (ret != 0) { SYSERROR("Error writing to or closing snapshots file"); goto out; } } else { /* Here we know that we have or can use an lxc-snapshot file * using the new format. */ if (inc) { f1 = fopen(path, "ae"); if (!f1) goto out; if (fprintf(f1, "%s", newpath) < 0) { ERROR("Error writing new snapshots entry"); ret = fclose(f1); if (ret != 0) SYSERROR("Error writing to or closing snapshots file"); goto out; } ret = fclose(f1); if (ret != 0) { SYSERROR("Error writing to or closing snapshots file"); goto out; } } else if (!inc) { if ((fd = open(path, O_RDWR | O_CLOEXEC)) < 0) goto out; if (fstat(fd, &fbuf) < 0) { close(fd); goto out; } if (fbuf.st_size != 0) { buf = lxc_strmmap(NULL, fbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buf == MAP_FAILED) { SYSERROR("Failed to create mapping %s", path); close(fd); goto out; } len = strlen(newpath); while ((del = strstr((char *)buf, newpath))) { memmove(del, del + len, strlen(del) - len + 1); bytes += len; } lxc_strmunmap(buf, fbuf.st_size); if (ftruncate(fd, fbuf.st_size - bytes) < 0) { SYSERROR("Failed to truncate file %s", path); close(fd); goto out; } } close(fd); } /* If the lxc-snapshot file is empty, remove it. */ if (stat(path, &fbuf) < 0) goto out; if (!fbuf.st_size) { ret = remove(path); if (ret < 0) SYSERROR("Failed to remove \"%s\"", path); } } bret = true; out: container_disk_unlock(c0); return bret; } void mod_all_rdeps(struct lxc_container *c, bool inc) { __do_free char *lxcpath = NULL, *lxcname = NULL; __do_fclose FILE *f = NULL; size_t pathlen = 0, namelen = 0; struct lxc_container *p; char path[PATH_MAX]; int ret; ret = snprintf(path, PATH_MAX, "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) { ERROR("Path name too long"); return; } f = fopen(path, "re"); if (!f) return; while (getline(&lxcpath, &pathlen, f) != -1) { if (getline(&lxcname, &namelen, f) == -1) { ERROR("badly formatted file %s", path); return; } remove_trailing_newlines(lxcpath); remove_trailing_newlines(lxcname); if ((p = lxc_container_new(lxcname, lxcpath)) == NULL) { ERROR("Unable to find dependent container %s:%s", lxcpath, lxcname); continue; } if (!mod_rdep(p, c, inc)) ERROR("Failed to update snapshots file for %s:%s", lxcpath, lxcname); lxc_container_put(p); } } static bool has_fs_snapshots(struct lxc_container *c) { __do_fclose FILE *f = NULL; char path[PATH_MAX]; int ret, v; struct stat fbuf; ret = snprintf(path, PATH_MAX, "%s/%s/lxc_snapshots", c->config_path, c->name); if (ret < 0 || ret > PATH_MAX) return false; /* If the file doesn't exist there are no snapshots. */ if (stat(path, &fbuf) < 0) return false; v = fbuf.st_size; if (v != 0) { f = fopen(path, "re"); if (!f) return false; ret = fscanf(f, "%d", &v); if (ret != 1) INFO("Container uses new lxc-snapshots format %s", path); } return v != 0; } static bool has_snapshots(struct lxc_container *c) { __do_closedir DIR *dir = NULL; char path[PATH_MAX]; struct dirent *direntp; int count = 0; if (!get_snappath_dir(c, path)) return false; dir = opendir(path); if (!dir) return false; while ((direntp = readdir(dir))) { if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; count++; break; } return count > 0; } static bool do_destroy_container(struct lxc_conf *conf) { int ret; if (am_guest_unpriv()) { ret = userns_exec_full(conf, storage_destroy_wrapper, conf, "storage_destroy_wrapper"); if (ret < 0) return false; return true; } return storage_destroy(conf); } static int lxc_rmdir_onedev_wrapper(void *data) { char *arg = (char *) data; return lxc_rmdir_onedev(arg, "snaps"); } static int lxc_unlink_exec_wrapper(void *data) { char *arg = data; return unlink(arg); } static bool container_destroy(struct lxc_container *c, struct lxc_storage *storage) { const char *p1; size_t len; struct lxc_conf *conf; char *path = NULL; bool bret = false; int ret = 0; if (!c || !do_lxcapi_is_defined(c)) return false; conf = c->lxc_conf; if (container_disk_lock(c)) return false; if (!is_stopped(c)) { /* We should queue some sort of error - in c->error_string? */ ERROR("container %s is not stopped", c->name); goto out; } if (conf && !lxc_list_empty(&conf->hooks[LXCHOOK_DESTROY])) { /* Start of environment variable setup for hooks */ if (setenv("LXC_NAME", c->name, 1)) SYSERROR("Failed to set environment variable for container name"); if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) SYSERROR("Failed to set environment variable for config path"); if (conf->rootfs.mount && setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) SYSERROR("Failed to set environment variable for rootfs mount"); if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) SYSERROR("Failed to set environment variable for rootfs mount"); if (conf->console.path && setenv("LXC_CONSOLE", conf->console.path, 1)) SYSERROR("Failed to set environment variable for console path"); if (conf->console.log_path && setenv("LXC_CONSOLE_LOGPATH", conf->console.log_path, 1)) SYSERROR("Failed to set environment variable for console log"); /* End of environment variable setup for hooks */ if (run_lxc_hooks(c->name, "destroy", conf, NULL)) { ERROR("Failed to execute clone hook for \"%s\"", c->name); goto out; } } if (current_config && conf == current_config) { current_config = NULL; if (conf->logfd != -1) { close(conf->logfd); conf->logfd = -1; } } /* LXC is not managing the storage of the container. */ if (conf && !conf->rootfs.managed) goto on_success; if (conf && conf->rootfs.path && conf->rootfs.mount) { if (!do_destroy_container(conf)) { ERROR("Error destroying rootfs for %s", c->name); goto out; } INFO("Destroyed rootfs for %s", c->name); } mod_all_rdeps(c, false); p1 = do_lxcapi_get_config_path(c); /* strlen(p1) * + * / * + * strlen(c->name) * + * / * + * strlen("config") = 6 * + * \0 */ len = strlen(p1) + 1 + strlen(c->name) + 1 + strlen(LXC_CONFIG_FNAME) + 1; path = malloc(len); if (!path) { ERROR("Failed to allocate memory"); goto out; } /* For an overlay container the rootfs is considered immutable and * cannot be removed when restoring from a snapshot. */ if (storage && (!strcmp(storage->type, "overlay") || !strcmp(storage->type, "overlayfs")) && (storage->flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE)) { ret = snprintf(path, len, "%s/%s/%s", p1, c->name, LXC_CONFIG_FNAME); if (ret < 0 || (size_t)ret >= len) goto out; if (am_guest_unpriv()) ret = userns_exec_1(conf, lxc_unlink_exec_wrapper, path, "lxc_unlink_exec_wrapper"); else ret = unlink(path); if (ret < 0) { SYSERROR("Failed to destroy config file \"%s\" for \"%s\"", path, c->name); goto out; } INFO("Destroyed config file \"%s\" for \"%s\"", path, c->name); bret = true; goto out; } ret = snprintf(path, len, "%s/%s", p1, c->name); if (ret < 0 || (size_t)ret >= len) goto out; if (am_guest_unpriv()) ret = userns_exec_full(conf, lxc_rmdir_onedev_wrapper, path, "lxc_rmdir_onedev_wrapper"); else ret = lxc_rmdir_onedev(path, "snaps"); if (ret < 0) { ERROR("Failed to destroy directory \"%s\" for \"%s\"", path, c->name); goto out; } INFO("Destroyed directory \"%s\" for \"%s\"", path, c->name); on_success: bret = true; out: if (path) free(path); container_disk_unlock(c); return bret; } static bool do_lxcapi_destroy(struct lxc_container *c) { if (!c || !lxcapi_is_defined(c)) return false; if (c->lxc_conf && c->lxc_conf->rootfs.managed) { if (has_snapshots(c)) { ERROR("Container %s has snapshots; not removing", c->name); return false; } if (has_fs_snapshots(c)) { ERROR("container %s has snapshots on its rootfs", c->name); return false; } } return container_destroy(c, NULL); } WRAP_API(bool, lxcapi_destroy) static bool do_lxcapi_destroy_with_snapshots(struct lxc_container *c) { if (!c || !lxcapi_is_defined(c)) return false; if (!lxcapi_snapshot_destroy_all(c)) { ERROR("Error deleting all snapshots"); return false; } return lxcapi_destroy(c); } WRAP_API(bool, lxcapi_destroy_with_snapshots) int lxc_set_config_item_locked(struct lxc_conf *conf, const char *key, const char *v) { int ret; struct lxc_config_t *config; bool bret = true; config = lxc_get_config(key); if (!config) return -EINVAL; ret = config->set(key, v, conf, NULL); if (ret < 0) return -EINVAL; if (lxc_config_value_empty(v)) do_clear_unexp_config_line(conf, key); else bret = do_append_unexp_config_line(conf, key, v); if (!bret) return -ENOMEM; return 0; } static bool do_set_config_item_locked(struct lxc_container *c, const char *key, const char *v) { int ret; if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); if (!c->lxc_conf) return false; ret = lxc_set_config_item_locked(c->lxc_conf, key, v); if (ret < 0) return false; return true; } static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v) { bool b = false; if (!c) return false; if (container_mem_lock(c)) return false; b = do_set_config_item_locked(c, key, v); container_mem_unlock(c); return b; } WRAP_API_2(bool, lxcapi_set_config_item, const char *, const char *) static char *lxcapi_config_file_name(struct lxc_container *c) { if (!c || !c->configfile) return NULL; return strdup(c->configfile); } static const char *lxcapi_get_config_path(struct lxc_container *c) { if (!c || !c->config_path) return NULL; return (const char *)(c->config_path); } /* * not for export * Just recalculate the c->configfile based on the * c->config_path, which must be set. * The lxc_container must be locked or not yet public. */ static bool set_config_filename(struct lxc_container *c) { char *newpath; int len, ret; if (!c->config_path) return false; /* $lxc_path + "/" + c->name + "/" + "config" + '\0' */ len = strlen(c->config_path) + 1 + strlen(c->name) + 1 + strlen(LXC_CONFIG_FNAME) + 1; newpath = malloc(len); if (!newpath) return false; ret = snprintf(newpath, len, "%s/%s/%s", c->config_path, c->name, LXC_CONFIG_FNAME); if (ret < 0 || ret >= len) { fprintf(stderr, "Error printing out config file name\n"); free(newpath); return false; } free(c->configfile); c->configfile = newpath; return true; } static bool do_lxcapi_set_config_path(struct lxc_container *c, const char *path) { char *p; bool b = false; char *oldpath = NULL; if (!c) return b; if (container_mem_lock(c)) return b; p = strdup(path); if (!p) { ERROR("Out of memory setting new lxc path"); goto err; } b = true; if (c->config_path) oldpath = c->config_path; c->config_path = p; /* Since we've changed the config path, we have to change the * config file name too */ if (!set_config_filename(c)) { ERROR("Out of memory setting new config filename"); b = false; free(c->config_path); c->config_path = oldpath; oldpath = NULL; } err: free(oldpath); container_mem_unlock(c); return b; } WRAP_API_1(bool, lxcapi_set_config_path, const char *) static bool do_lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value) { call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL; if (!c) return false; if (is_stopped(c)) return false; cgroup_ops = cgroup_init(c->lxc_conf); if (!cgroup_ops) return false; return cgroup_ops->set(cgroup_ops, subsys, value, c->name, c->config_path) == 0; } WRAP_API_2(bool, lxcapi_set_cgroup_item, const char *, const char *) static int do_lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen) { call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL; if (!c) return -1; if (is_stopped(c)) return -1; cgroup_ops = cgroup_init(c->lxc_conf); if (!cgroup_ops) return -1; return cgroup_ops->get(cgroup_ops, subsys, retv, inlen, c->name, c->config_path); } WRAP_API_3(int, lxcapi_get_cgroup_item, const char *, char *, int) const char *lxc_get_global_config_item(const char *key) { return lxc_global_config_value(key); } const char *lxc_get_version(void) { return LXC_VERSION; } static int copy_file(const char *old, const char *new) { int in, out; ssize_t len, ret; char buf[8096]; struct stat sbuf; if (file_exists(new)) { ERROR("copy destination %s exists", new); return -1; } ret = stat(old, &sbuf); if (ret < 0) { INFO("Error stat'ing %s", old); return -1; } in = open(old, O_RDONLY); if (in < 0) { SYSERROR("Error opening original file %s", old); return -1; } out = open(new, O_CREAT | O_EXCL | O_WRONLY, 0644); if (out < 0) { SYSERROR("Error opening new file %s", new); close(in); return -1; } for (;;) { len = lxc_read_nointr(in, buf, 8096); if (len < 0) { SYSERROR("Error reading old file %s", old); goto err; } if (len == 0) break; ret = lxc_write_nointr(out, buf, len); if (ret < len) { /* should we retry? */ SYSERROR("Error: write to new file %s was interrupted", new); goto err; } } close(in); close(out); /* We set mode, but not owner/group. */ ret = chmod(new, sbuf.st_mode); if (ret) { SYSERROR("Error setting mode on %s", new); return -1; } return 0; err: close(in); close(out); return -1; } static int copyhooks(struct lxc_container *oldc, struct lxc_container *c) { __do_free char *cpath = NULL; int i, len, ret; struct lxc_list *it; len = strlen(oldc->config_path) + strlen(oldc->name) + 3; cpath = must_realloc(NULL, len); ret = snprintf(cpath, len, "%s/%s/", oldc->config_path, oldc->name); if (ret < 0 || ret >= len) return -1; for (i=0; ilxc_conf->hooks[i]) { char *hookname = it->elem; char *fname = strrchr(hookname, '/'); char tmppath[PATH_MAX]; if (!fname) /* relative path - we don't support, but maybe we should */ return 0; if (strncmp(hookname, cpath, len - 1) != 0) { /* this hook is public - ignore */ continue; } /* copy the script, and change the entry in confile */ ret = snprintf(tmppath, PATH_MAX, "%s/%s/%s", c->config_path, c->name, fname+1); if (ret < 0 || ret >= PATH_MAX) return -1; ret = copy_file(it->elem, tmppath); if (ret < 0) return -1; free(it->elem); it->elem = strdup(tmppath); if (!it->elem) { ERROR("out of memory copying hook path"); return -1; } } } if (!clone_update_unexp_hooks(c->lxc_conf, oldc->config_path, c->config_path, oldc->name, c->name)) { ERROR("Error saving new hooks in clone"); return -1; } do_lxcapi_save_config(c, NULL); return 0; } static int copy_fstab(struct lxc_container *oldc, struct lxc_container *c) { char newpath[PATH_MAX]; char *oldpath = oldc->lxc_conf->fstab; int ret; if (!oldpath) return 0; clear_unexp_config_line(c->lxc_conf, "lxc.mount.fstab", false); char *p = strrchr(oldpath, '/'); if (!p) return -1; ret = snprintf(newpath, PATH_MAX, "%s/%s%s", c->config_path, c->name, p); if (ret < 0 || ret >= PATH_MAX) { ERROR("error printing new path for %s", oldpath); return -1; } if (file_exists(newpath)) { ERROR("error: fstab file %s exists", newpath); return -1; } if (copy_file(oldpath, newpath) < 0) { ERROR("error: copying %s to %s", oldpath, newpath); return -1; } free(c->lxc_conf->fstab); c->lxc_conf->fstab = strdup(newpath); if (!c->lxc_conf->fstab) { ERROR("error: allocating pathname"); return -1; } if (!do_append_unexp_config_line(c->lxc_conf, "lxc.mount.fstab", newpath)) { ERROR("error saving new lxctab"); return -1; } return 0; } static void copy_rdepends(struct lxc_container *c, struct lxc_container *c0) { char path0[PATH_MAX], path1[PATH_MAX]; int ret; ret = snprintf(path0, PATH_MAX, "%s/%s/lxc_rdepends", c0->config_path, c0->name); if (ret < 0 || ret >= PATH_MAX) { WARN("Error copying reverse dependencies"); return; } ret = snprintf(path1, PATH_MAX, "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) { WARN("Error copying reverse dependencies"); return; } if (copy_file(path0, path1) < 0) { INFO("Error copying reverse dependencies"); return; } } static bool add_rdepends(struct lxc_container *c, struct lxc_container *c0) { __do_fclose FILE *f = NULL; int ret; char path[PATH_MAX]; ret = snprintf(path, sizeof(path), "%s/%s/lxc_rdepends", c->config_path, c->name); if (ret < 0 || ret >= sizeof(path)) return false; f = fopen(path, "ae"); if (!f) return false; /* If anything goes wrong, just return an error. */ return fprintf(f, "%s\n%s\n", c0->config_path, c0->name) > 0; } /* * If the fs natively supports snapshot clones with no penalty, * then default to those even if not requested. * Currently we only do this for btrfs. */ bool should_default_to_snapshot(struct lxc_container *c0, struct lxc_container *c1) { __do_free char *p0 = NULL, *p1 = NULL; int ret; size_t l0 = strlen(c0->config_path) + strlen(c0->name) + 2; size_t l1 = strlen(c1->config_path) + strlen(c1->name) + 2; char *rootfs = c0->lxc_conf->rootfs.path; p0 = must_realloc(NULL, l0 + 1); p1 = must_realloc(NULL, l1 + 1); ret = snprintf(p0, l0, "%s/%s", c0->config_path, c0->name); if (ret < 0 || ret >= l0) return false; ret = snprintf(p1, l1, "%s/%s", c1->config_path, c1->name); if (ret < 0 || ret >= l1) return false; if (!is_btrfs_fs(p0) || !is_btrfs_fs(p1)) return false; if (is_btrfs_subvol(rootfs) <= 0) return false; return btrfs_same_fs(p0, p1) == 0; } static int copy_storage(struct lxc_container *c0, struct lxc_container *c, const char *newtype, int flags, const char *bdevdata, uint64_t newsize) { struct lxc_storage *bdev; bool need_rdep; if (should_default_to_snapshot(c0, c)) flags |= LXC_CLONE_SNAPSHOT; bdev = storage_copy(c0, c->name, c->config_path, newtype, flags, bdevdata, newsize, &need_rdep); if (!bdev) { ERROR("Error copying storage."); return -1; } /* Set new rootfs. */ free(c->lxc_conf->rootfs.path); c->lxc_conf->rootfs.path = strdup(bdev->src); storage_put(bdev); if (!c->lxc_conf->rootfs.path) { ERROR("Out of memory while setting storage path."); return -1; } /* Append a new lxc.rootfs.path entry to the unexpanded config. */ clear_unexp_config_line(c->lxc_conf, "lxc.rootfs.path", false); if (!do_append_unexp_config_line(c->lxc_conf, "lxc.rootfs.path", c->lxc_conf->rootfs.path)) { ERROR("Error saving new rootfs to cloned config."); return -1; } if (flags & LXC_CLONE_SNAPSHOT) copy_rdepends(c, c0); if (need_rdep) { if (!add_rdepends(c, c0)) WARN("Error adding reverse dependency from %s to %s", c->name, c0->name); } mod_all_rdeps(c, true); return 0; } struct clone_update_data { struct lxc_container *c0; struct lxc_container *c1; int flags; char **hookargs; }; static int clone_update_rootfs(struct clone_update_data *data) { struct lxc_container *c0 = data->c0; struct lxc_container *c = data->c1; int flags = data->flags; char **hookargs = data->hookargs; int ret = -1; char path[PATH_MAX]; struct lxc_storage *bdev; FILE *fout; struct lxc_conf *conf = c->lxc_conf; /* update hostname in rootfs */ /* we're going to mount, so run in a clean namespace to simplify cleanup */ (void)lxc_setgroups(0, NULL); if (setgid(0) < 0) { ERROR("Failed to setgid to 0"); return -1; } if (setuid(0) < 0) { ERROR("Failed to setuid to 0"); return -1; } if (unshare(CLONE_NEWNS) < 0) return -1; bdev = storage_init(c->lxc_conf); if (!bdev) return -1; if (strcmp(bdev->type, "dir") != 0) { if (unshare(CLONE_NEWNS) < 0) { ERROR("error unsharing mounts"); storage_put(bdev); return -1; } if (detect_shared_rootfs()) { if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { SYSERROR("Failed to make / rslave"); ERROR("Continuing..."); } } if (bdev->ops->mount(bdev) < 0) { storage_put(bdev); return -1; } } else { /* TODO come up with a better way */ free(bdev->dest); bdev->dest = strdup(lxc_storage_get_path(bdev->src, bdev->type)); } if (!lxc_list_empty(&conf->hooks[LXCHOOK_CLONE])) { /* Start of environment variable setup for hooks */ if (c0->name && setenv("LXC_SRC_NAME", c0->name, 1)) SYSERROR("failed to set environment variable for source container name"); if (setenv("LXC_NAME", c->name, 1)) SYSERROR("failed to set environment variable for container name"); if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) SYSERROR("failed to set environment variable for config path"); if (bdev->dest && setenv("LXC_ROOTFS_MOUNT", bdev->dest, 1)) SYSERROR("failed to set environment variable for rootfs mount"); if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) SYSERROR("failed to set environment variable for rootfs mount"); if (run_lxc_hooks(c->name, "clone", conf, hookargs)) { ERROR("Error executing clone hook for %s", c->name); storage_put(bdev); return -1; } } if (!(flags & LXC_CLONE_KEEPNAME)) { ret = snprintf(path, PATH_MAX, "%s/etc/hostname", bdev->dest); storage_put(bdev); if (ret < 0 || ret >= PATH_MAX) return -1; if (!file_exists(path)) return 0; if (!(fout = fopen(path, "we"))) { SYSERROR("unable to open %s: ignoring", path); return 0; } if (fprintf(fout, "%s", c->name) < 0) { fclose(fout); return -1; } if (fclose(fout) < 0) return -1; } else { storage_put(bdev); } return 0; } static int clone_update_rootfs_wrapper(void *data) { struct clone_update_data *arg = (struct clone_update_data *) data; return clone_update_rootfs(arg); } /* * We want to support: sudo lxc-clone -o o1 -n n1 -s -L|-fssize fssize -v|--vgname vgname \ -p|--lvprefix lvprefix -t|--fstype fstype -B backingstore -s [ implies overlay] -s -B overlay only rootfs gets converted (copied/snapshotted) on clone. */ static int create_file_dirname(char *path, struct lxc_conf *conf) { char *p = strrchr(path, '/'); int ret = -1; if (!p) return -1; *p = '\0'; ret = do_create_container_dir(path, conf); *p = '/'; return ret; } static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs) { char newpath[PATH_MAX]; int fd, ret; struct clone_update_data data; size_t saved_unexp_len; pid_t pid; int storage_copied = 0; char *origroot = NULL, *saved_unexp_conf = NULL; struct lxc_container *c2 = NULL; if (!c || !do_lxcapi_is_defined(c)) return NULL; if (container_mem_lock(c)) return NULL; if (!is_stopped(c) && !(flags & LXC_CLONE_ALLOW_RUNNING)) { ERROR("error: Original container (%s) is running. Use --allowrunning if you want to force a snapshot of the running container.", c->name); goto out; } /* Make sure the container doesn't yet exist. */ if (!newname) newname = c->name; if (!lxcpath) lxcpath = do_lxcapi_get_config_path(c); ret = snprintf(newpath, PATH_MAX, "%s/%s/%s", lxcpath, newname, LXC_CONFIG_FNAME); if (ret < 0 || ret >= PATH_MAX) { SYSERROR("clone: failed making config pathname"); goto out; } if (file_exists(newpath)) { ERROR("error: clone: %s exists", newpath); goto out; } ret = create_file_dirname(newpath, c->lxc_conf); if (ret < 0 && errno != EEXIST) { ERROR("Error creating container dir for %s", newpath); goto out; } /* Copy the configuration. Tweak it as needed. */ if (c->lxc_conf->rootfs.path) { origroot = c->lxc_conf->rootfs.path; c->lxc_conf->rootfs.path = NULL; } fd = open(newpath, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (fd < 0) { SYSERROR("Failed to open \"%s\"", newpath); goto out; } saved_unexp_conf = c->lxc_conf->unexpanded_config; saved_unexp_len = c->lxc_conf->unexpanded_len; c->lxc_conf->unexpanded_config = strdup(saved_unexp_conf); if (!c->lxc_conf->unexpanded_config) { close(fd); goto out; } clear_unexp_config_line(c->lxc_conf, "lxc.rootfs.path", false); write_config(fd, c->lxc_conf); close(fd); c->lxc_conf->rootfs.path = origroot; free(c->lxc_conf->unexpanded_config); c->lxc_conf->unexpanded_config = saved_unexp_conf; saved_unexp_conf = NULL; c->lxc_conf->unexpanded_len = saved_unexp_len; ret = snprintf(newpath, PATH_MAX, "%s/%s/%s", lxcpath, newname, LXC_ROOTFS_DNAME); if (ret < 0 || ret >= PATH_MAX) { SYSERROR("clone: failed making rootfs pathname"); goto out; } ret = mkdir(newpath, 0755); if (ret < 0) { /* For an overlay container the rootfs is considered immutable * and will not have been removed when restoring from a * snapshot. */ if (errno != ENOENT && !(flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE)) { SYSERROR("Failed to create directory \"%s\"", newpath); goto out; } } if (am_guest_unpriv()) { if (chown_mapped_root(newpath, c->lxc_conf) < 0) { ERROR("Error chowning %s to container root", newpath); goto out; } } c2 = lxc_container_new(newname, lxcpath); if (!c2) { ERROR("clone: failed to create new container (%s %s)", newname, lxcpath); goto out; } /* copy/snapshot rootfs's */ ret = copy_storage(c, c2, bdevtype, flags, bdevdata, newsize); if (ret < 0) goto out; /* update utsname */ if (!(flags & LXC_CLONE_KEEPNAME)) { clear_unexp_config_line(c2->lxc_conf, "lxc.utsname", false); clear_unexp_config_line(c2->lxc_conf, "lxc.uts.name", false); if (!do_set_config_item_locked(c2, "lxc.uts.name", newname)) { ERROR("Error setting new hostname"); goto out; } } /* copy hooks */ ret = copyhooks(c, c2); if (ret < 0) { ERROR("error copying hooks"); goto out; } if (copy_fstab(c, c2) < 0) { ERROR("error copying fstab"); goto out; } /* update macaddrs */ if (!(flags & LXC_CLONE_KEEPMACADDR)) { if (!network_new_hwaddrs(c2->lxc_conf)) { ERROR("Error updating mac addresses"); goto out; } } /* Update absolute paths for overlay mount directories. */ if (ovl_update_abs_paths(c2->lxc_conf, c->config_path, c->name, lxcpath, newname) < 0) goto out; /* We've now successfully created c2's storage, so clear it out if we * fail after this. */ storage_copied = 1; if (!c2->save_config(c2, NULL)) goto out; if ((pid = fork()) < 0) { SYSERROR("fork"); goto out; } if (pid > 0) { ret = wait_for_pid(pid); if (ret) goto out; container_mem_unlock(c); return c2; } data.c0 = c; data.c1 = c2; data.flags = flags; data.hookargs = hookargs; if (am_guest_unpriv()) ret = userns_exec_full(c->lxc_conf, clone_update_rootfs_wrapper, &data, "clone_update_rootfs_wrapper"); else ret = clone_update_rootfs(&data); if (ret < 0) _exit(EXIT_FAILURE); container_mem_unlock(c); _exit(EXIT_SUCCESS); out: container_mem_unlock(c); if (c2) { if (!storage_copied) c2->lxc_conf->rootfs.path = NULL; c2->destroy(c2); lxc_container_put(c2); } return NULL; } static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs) { struct lxc_container * ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_clone(c, newname, lxcpath, flags, bdevtype, bdevdata, newsize, hookargs); current_config = NULL; return ret; } static bool do_lxcapi_rename(struct lxc_container *c, const char *newname) { struct lxc_storage *bdev; struct lxc_container *newc; if (!c || !c->name || !c->config_path || !c->lxc_conf) return false; if (has_fs_snapshots(c) || has_snapshots(c)) { ERROR("Renaming a container with snapshots is not supported"); return false; } bdev = storage_init(c->lxc_conf); if (!bdev) { ERROR("Failed to find original backing store type"); return false; } newc = lxcapi_clone(c, newname, c->config_path, LXC_CLONE_KEEPMACADDR, NULL, bdev->type, 0, NULL); storage_put(bdev); if (!newc) { lxc_container_put(newc); return false; } if (newc && lxcapi_is_defined(newc)) lxc_container_put(newc); if (!container_destroy(c, NULL)) { ERROR("Could not destroy existing container %s", c->name); return false; } return true; } WRAP_API_1(bool, lxcapi_rename, const char *) static int lxcapi_attach(struct lxc_container *c, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process) { int ret; if (!c) return -1; current_config = c->lxc_conf; ret = lxc_attach(c, exec_function, exec_payload, options, attached_process); current_config = NULL; return ret; } static int do_lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *const argv[]) { lxc_attach_command_t command; pid_t pid; int ret; if (!c) return -1; command.program = (char *)program; command.argv = (char **)argv; ret = lxc_attach(c, lxc_attach_run_command, &command, options, &pid); if (ret < 0) return ret; return lxc_wait_for_pid_status(pid); } static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *const argv[]) { int ret; current_config = c ? c->lxc_conf : NULL; ret = do_lxcapi_attach_run_wait(c, options, program, argv); current_config = NULL; return ret; } static int get_next_index(const char *lxcpath, char *cname) { __do_free char *fname = NULL; struct stat sb; int i = 0, ret; fname = must_realloc(NULL, strlen(lxcpath) + 20); for (;;) { sprintf(fname, "%s/snap%d", lxcpath, i); ret = stat(fname, &sb); if (ret != 0) return i; i++; } } static bool get_snappath_dir(struct lxc_container *c, char *snappath) { int ret; /* * If the old style snapshot path exists, use it * /var/lib/lxc -> /var/lib/lxcsnaps */ ret = snprintf(snappath, PATH_MAX, "%ssnaps", c->config_path); if (ret < 0 || ret >= PATH_MAX) return false; if (dir_exists(snappath)) { ret = snprintf(snappath, PATH_MAX, "%ssnaps/%s", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) return false; return true; } /* * Use the new style path * /var/lib/lxc -> /var/lib/lxc + c->name + /snaps + \0 */ ret = snprintf(snappath, PATH_MAX, "%s/%s/snaps", c->config_path, c->name); if (ret < 0 || ret >= PATH_MAX) return false; return true; } static int do_lxcapi_snapshot(struct lxc_container *c, const char *commentfile) { __do_free char *dfnam = NULL; int len; int i, flags, ret; time_t timer; struct tm tm_info; struct lxc_container *c2; char snappath[PATH_MAX], newname[20]; char buffer[25]; FILE *f; if (!c || !lxcapi_is_defined(c)) return -1; if (!storage_can_backup(c->lxc_conf)) { ERROR("%s's backing store cannot be backed up", c->name); ERROR("Your container must use another backing store type"); return -1; } if (!get_snappath_dir(c, snappath)) return -1; i = get_next_index(snappath, c->name); if (mkdir_p(snappath, 0755) < 0) { ERROR("Failed to create snapshot directory %s", snappath); return -1; } ret = snprintf(newname, 20, "snap%d", i); if (ret < 0 || ret >= 20) return -1; /* * We pass LXC_CLONE_SNAPSHOT to make sure that a rdepends file entry is * created in the original container */ flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_KEEPMACADDR | LXC_CLONE_KEEPNAME | LXC_CLONE_KEEPBDEVTYPE | LXC_CLONE_MAYBE_SNAPSHOT; if (storage_is_dir(c->lxc_conf)) { ERROR("Snapshot of directory-backed container requested"); ERROR("Making a copy-clone. If you do want snapshots, then"); ERROR("please create overlay clone first, snapshot that"); ERROR("and keep the original container pristine"); flags &= ~LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; } c2 = do_lxcapi_clone(c, newname, snappath, flags, NULL, NULL, 0, NULL); if (!c2) { ERROR("Failed to clone of %s:%s", c->config_path, c->name); return -1; } lxc_container_put(c2); /* Now write down the creation time. */ time(&timer); if (!localtime_r(&timer, &tm_info)) { ERROR("Failed to get localtime"); return -1; } strftime(buffer, 25, "%Y:%m:%d %H:%M:%S", &tm_info); len = strlen(snappath) + 1 + strlen(newname) + 1 + strlen(LXC_TIMESTAMP_FNAME) + 1; dfnam = must_realloc(NULL, len); snprintf(dfnam, len, "%s/%s/%s", snappath, newname, LXC_TIMESTAMP_FNAME); f = fopen(dfnam, "we"); if (!f) { ERROR("Failed to open %s", dfnam); return -1; } if (fprintf(f, "%s", buffer) < 0) { SYSERROR("Writing timestamp"); fclose(f); return -1; } ret = fclose(f); if (ret != 0) { SYSERROR("Writing timestamp"); return -1; } if (commentfile) { __do_free char *path = NULL; /* $p / $name / comment \0 */ len = strlen(snappath) + 1 + strlen(newname) + 1 + strlen(LXC_COMMENT_FNAME) + 1; path = must_realloc(NULL, len); snprintf(path, len, "%s/%s/%s", snappath, newname, LXC_COMMENT_FNAME); return copy_file(commentfile, path) < 0 ? -1 : i; } return i; } WRAP_API_1(int, lxcapi_snapshot, const char *) static void lxcsnap_free(struct lxc_snapshot *s) { free(s->name); free(s->comment_pathname); free(s->timestamp); free(s->lxcpath); } static char *get_snapcomment_path(char* snappath, char *name) { /* $snappath/$name/comment */ int ret, len = strlen(snappath) + strlen(name) + 10; char *s = malloc(len); if (s) { ret = snprintf(s, len, "%s/%s/comment", snappath, name); if (ret < 0 || ret >= len) { free(s); s = NULL; } } return s; } static char *get_timestamp(char* snappath, char *name) { __do_free char *s = NULL; __do_fclose FILE *fin = NULL; char path[PATH_MAX]; int ret, len; ret = snprintf(path, PATH_MAX, "%s/%s/ts", snappath, name); if (ret < 0 || ret >= PATH_MAX) return NULL; fin = fopen(path, "re"); if (!fin) return NULL; (void) fseek(fin, 0, SEEK_END); len = ftell(fin); (void) fseek(fin, 0, SEEK_SET); if (len > 0) { s = malloc(len+1); if (s) { s[len] = '\0'; if (fread(s, 1, len, fin) != len) return log_error_errno(NULL, errno, "reading timestamp"); } } return move_ptr(s); } static int do_lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret_snaps) { __do_closedir DIR *dir = NULL; char snappath[PATH_MAX], path2[PATH_MAX]; int count = 0, ret; struct dirent *direntp; struct lxc_snapshot *snaps =NULL, *nsnaps; if (!c || !lxcapi_is_defined(c)) return -1; if (!get_snappath_dir(c, snappath)) { ERROR("path name too long"); return -1; } dir = opendir(snappath); if (!dir) { INFO("Failed to open %s - assuming no snapshots", snappath); return 0; } while ((direntp = readdir(dir))) { if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; ret = snprintf(path2, PATH_MAX, "%s/%s/%s", snappath, direntp->d_name, LXC_CONFIG_FNAME); if (ret < 0 || ret >= PATH_MAX) { ERROR("pathname too long"); goto out_free; } if (!file_exists(path2)) continue; nsnaps = realloc(snaps, (count + 1)*sizeof(*snaps)); if (!nsnaps) { SYSERROR("Out of memory"); goto out_free; } snaps = nsnaps; snaps[count].free = lxcsnap_free; snaps[count].name = strdup(direntp->d_name); if (!snaps[count].name) goto out_free; snaps[count].lxcpath = strdup(snappath); if (!snaps[count].lxcpath) { free(snaps[count].name); goto out_free; } snaps[count].comment_pathname = get_snapcomment_path(snappath, direntp->d_name); snaps[count].timestamp = get_timestamp(snappath, direntp->d_name); count++; } *ret_snaps = snaps; return count; out_free: if (snaps) { for (int i = 0; i < count; i++) lxcsnap_free(&snaps[i]); free(snaps); } return -1; } WRAP_API_1(int, lxcapi_snapshot_list, struct lxc_snapshot **) static bool do_lxcapi_snapshot_restore(struct lxc_container *c, const char *snapname, const char *newname) { char clonelxcpath[PATH_MAX]; int flags = 0; struct lxc_container *snap, *rest; struct lxc_storage *bdev; bool b = false; if (!c || !c->name || !c->config_path) return false; if (has_fs_snapshots(c)) { ERROR("container rootfs has dependent snapshots"); return false; } bdev = storage_init(c->lxc_conf); if (!bdev) { ERROR("Failed to find original backing store type"); return false; } /* For an overlay container the rootfs is considered immutable * and cannot be removed when restoring from a snapshot. We pass this * internal flag along to communicate this to various parts of the * codebase. */ if (!strcmp(bdev->type, "overlay") || !strcmp(bdev->type, "overlayfs")) bdev->flags |= LXC_STORAGE_INTERNAL_OVERLAY_RESTORE; if (!newname) newname = c->name; if (!get_snappath_dir(c, clonelxcpath)) { storage_put(bdev); return false; } /* how should we lock this? */ snap = lxc_container_new(snapname, clonelxcpath); if (!snap || !lxcapi_is_defined(snap)) { ERROR("Could not open snapshot %s", snapname); if (snap) lxc_container_put(snap); storage_put(bdev); return false; } if (!strcmp(c->name, newname)) { if (!container_destroy(c, bdev)) { ERROR("Could not destroy existing container %s", newname); lxc_container_put(snap); storage_put(bdev); return false; } } if (strcmp(bdev->type, "dir") != 0 && strcmp(bdev->type, "loop") != 0) flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; if (!strcmp(bdev->type, "overlay") || !strcmp(bdev->type, "overlayfs")) flags |= LXC_STORAGE_INTERNAL_OVERLAY_RESTORE; rest = lxcapi_clone(snap, newname, c->config_path, flags, bdev->type, NULL, 0, NULL); storage_put(bdev); if (rest && lxcapi_is_defined(rest)) b = true; if (rest) lxc_container_put(rest); lxc_container_put(snap); return b; } WRAP_API_2(bool, lxcapi_snapshot_restore, const char *, const char *) static bool do_snapshot_destroy(const char *snapname, const char *clonelxcpath) { struct lxc_container *snap = NULL; bool bret = false; snap = lxc_container_new(snapname, clonelxcpath); if (!snap) { ERROR("Could not find snapshot %s", snapname); goto err; } if (!do_lxcapi_destroy(snap)) { ERROR("Could not destroy snapshot %s", snapname); goto err; } bret = true; err: if (snap) lxc_container_put(snap); return bret; } static bool remove_all_snapshots(const char *path) { __do_closedir DIR *dir = NULL; struct dirent *direntp; bool bret = true; dir = opendir(path); if (!dir) { SYSERROR("opendir on snapshot path %s", path); return false; } while ((direntp = readdir(dir))) { if (!strcmp(direntp->d_name, ".")) continue; if (!strcmp(direntp->d_name, "..")) continue; if (!do_snapshot_destroy(direntp->d_name, path)) { bret = false; continue; } } if (rmdir(path)) SYSERROR("Error removing directory %s", path); return bret; } static bool do_lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname) { char clonelxcpath[PATH_MAX]; if (!c || !c->name || !c->config_path || !snapname) return false; if (!get_snappath_dir(c, clonelxcpath)) return false; return do_snapshot_destroy(snapname, clonelxcpath); } WRAP_API_1(bool, lxcapi_snapshot_destroy, const char *) static bool do_lxcapi_snapshot_destroy_all(struct lxc_container *c) { char clonelxcpath[PATH_MAX]; if (!c || !c->name || !c->config_path) return false; if (!get_snappath_dir(c, clonelxcpath)) return false; return remove_all_snapshots(clonelxcpath); } WRAP_API(bool, lxcapi_snapshot_destroy_all) static bool do_lxcapi_may_control(struct lxc_container *c) { if (!c) return false; return lxc_try_cmd(c->name, c->config_path) == 0; } WRAP_API(bool, lxcapi_may_control) static bool do_add_remove_node(pid_t init_pid, const char *path, bool add, struct stat *st) { int ret; char *tmp; pid_t pid; char chrootpath[PATH_MAX]; char *directory_path = NULL; pid = fork(); if (pid < 0) { SYSERROR("Failed to fork()"); return false; } if (pid) { ret = wait_for_pid(pid); if (ret != 0) { ERROR("Failed to create device node"); return false; } return true; } /* prepare the path */ ret = snprintf(chrootpath, PATH_MAX, "/proc/%d/root", init_pid); if (ret < 0 || ret >= PATH_MAX) return false; ret = chroot(chrootpath); if (ret < 0) _exit(EXIT_FAILURE); ret = chdir("/"); if (ret < 0) _exit(EXIT_FAILURE); /* remove path if it exists */ ret = faccessat(AT_FDCWD, path, F_OK, AT_SYMLINK_NOFOLLOW); if(ret == 0) { ret = unlink(path); if (ret < 0) { SYSERROR("Failed to remove \"%s\"", path); _exit(EXIT_FAILURE); } } if (!add) _exit(EXIT_SUCCESS); /* create any missing directories */ tmp = strdup(path); if (!tmp) _exit(EXIT_FAILURE); directory_path = dirname(tmp); ret = mkdir_p(directory_path, 0755); if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create path \"%s\"", directory_path); free(tmp); _exit(EXIT_FAILURE); } /* create the device node */ ret = mknod(path, st->st_mode, st->st_rdev); free(tmp); if (ret < 0) { SYSERROR("Failed to create device node at \"%s\"", path); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } static bool add_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path, bool add) { int ret; struct stat st; char value[LXC_MAX_BUFFER]; const char *p; pid_t init_pid; /* make sure container is running */ if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); return false; } /* use src_path if dest_path is NULL otherwise use dest_path */ p = dest_path ? dest_path : src_path; /* make sure we can access p */ if(access(p, F_OK) < 0 || stat(p, &st) < 0) return false; /* continue if path is character device or block device */ if (S_ISCHR(st.st_mode)) ret = snprintf(value, LXC_MAX_BUFFER, "c %d:%d rwm", major(st.st_rdev), minor(st.st_rdev)); else if (S_ISBLK(st.st_mode)) ret = snprintf(value, LXC_MAX_BUFFER, "b %d:%d rwm", major(st.st_rdev), minor(st.st_rdev)); else return false; /* check snprintf return code */ if (ret < 0 || ret >= LXC_MAX_BUFFER) return false; init_pid = do_lxcapi_init_pid(c); if (init_pid < 0) { ERROR("Failed to get init pid"); return false; } if (!do_add_remove_node(init_pid, p, add, &st)) return false; /* add or remove device to/from cgroup access list */ if (add) { if (!do_lxcapi_set_cgroup_item(c, "devices.allow", value)) { ERROR("set_cgroup_item failed while adding the device node"); return false; } } else { if (!do_lxcapi_set_cgroup_item(c, "devices.deny", value)) { ERROR("set_cgroup_item failed while removing the device node"); return false; } } return true; } static bool do_lxcapi_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { // cannot mknod if we're not privileged wrt init_user_ns if (am_host_unpriv()) { ERROR(LXC_UNPRIV_EOPNOTSUPP, __FUNCTION__); return false; } return add_remove_device_node(c, src_path, dest_path, true); } WRAP_API_2(bool, lxcapi_add_device_node, const char *, const char *) static bool do_lxcapi_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { if (am_guest_unpriv()) { ERROR(LXC_UNPRIV_EOPNOTSUPP, __FUNCTION__); return false; } return add_remove_device_node(c, src_path, dest_path, false); } WRAP_API_2(bool, lxcapi_remove_device_node, const char *, const char *) static bool do_lxcapi_attach_interface(struct lxc_container *c, const char *ifname, const char *dst_ifname) { pid_t init_pid; int ret = 0; if (am_guest_unpriv()) { ERROR(LXC_UNPRIV_EOPNOTSUPP, __FUNCTION__); return false; } if (!ifname) { ERROR("No source interface name given"); return false; } ret = lxc_netdev_isup(ifname); if (ret > 0) { /* netdev of ifname is up. */ ret = lxc_netdev_down(ifname); if (ret) goto err; } init_pid = do_lxcapi_init_pid(c); if (init_pid < 0) { ERROR("Failed to get init pid"); goto err; } ret = lxc_netdev_move_by_name(ifname, init_pid, dst_ifname); if (ret) goto err; INFO("Moved network device \"%s\" to network namespace of %d", ifname, init_pid); return true; err: return false; } WRAP_API_2(bool, lxcapi_attach_interface, const char *, const char *) static bool do_lxcapi_detach_interface(struct lxc_container *c, const char *ifname, const char *dst_ifname) { int ret; pid_t pid, pid_outside; __do_free char *physname = NULL; /* * TODO - if this is a physical device, then we need am_host_unpriv. * But for other types guest privilege suffices. */ if (am_guest_unpriv()) { ERROR(LXC_UNPRIV_EOPNOTSUPP, __FUNCTION__); return false; } if (!ifname) { ERROR("No source interface name given"); return false; } pid_outside = lxc_raw_getpid(); pid = fork(); if (pid < 0) { ERROR("Failed to fork"); return false; } if (pid == 0) { /* child */ pid_t init_pid; init_pid = do_lxcapi_init_pid(c); if (init_pid < 0) { ERROR("Failed to get init pid"); _exit(EXIT_FAILURE); } if (!switch_to_ns(init_pid, "net")) { ERROR("Failed to enter network namespace"); _exit(EXIT_FAILURE); } /* create new mount namespace for use with remounting /sys and is_wlan() below. */ ret = unshare(CLONE_NEWNS); if (ret < 0) { ERROR("Failed to unshare mount namespace"); _exit(EXIT_FAILURE); } /* set / recursively as private so that mount propagation doesn't affect us. */ if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0) < 0) { ERROR("Failed to recursively set / as private in mount namespace"); _exit(EXIT_FAILURE); } ret = lxc_netdev_isup(ifname); if (ret < 0) { ERROR("Failed to determine whether network device \"%s\" is up", ifname); _exit(EXIT_FAILURE); } /* netdev of ifname is up. */ if (ret) { ret = lxc_netdev_down(ifname); if (ret) { ERROR("Failed to set network device \"%s\" down", ifname); _exit(EXIT_FAILURE); } } /* remount /sys so is_wlan() can check if this device is a wlan device. */ lxc_attach_remount_sys_proc(); physname = is_wlan(ifname); if (physname) ret = lxc_netdev_move_wlan(physname, ifname, pid_outside, dst_ifname); else ret = lxc_netdev_move_by_name(ifname, pid_outside, dst_ifname); /* -EINVAL means there is no netdev named as ifname. */ if (ret < 0) { if (ret == -EINVAL) ERROR("Network device \"%s\" not found", ifname); else ERROR("Failed to remove network device \"%s\"", ifname); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } ret = wait_for_pid(pid); if (ret != 0) return false; INFO("Moved network device \"%s\" to network namespace of %d", ifname, pid_outside); return true; } WRAP_API_2(bool, lxcapi_detach_interface, const char *, const char *) static int do_lxcapi_migrate(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, unsigned int size) { int ret = -1; struct migrate_opts *valid_opts = opts; uint64_t features_to_check = 0; /* If the caller has a bigger (newer) struct migrate_opts, let's make * sure that the stuff on the end is zero, i.e. that they didn't ask us * to do anything special. */ if (size > sizeof(*opts)) { unsigned char *addr; unsigned char *end; addr = (void *)opts + sizeof(*opts); end = (void *)opts + size; for (; addr < end; addr++) if (*addr) return -E2BIG; } /* If the caller has a smaller struct, let's zero out the end for them * so we don't accidentally use bits of it that they didn't know about * to initialize. */ if (size < sizeof(*opts)) { valid_opts = malloc(sizeof(*opts)); if (!valid_opts) return -ENOMEM; memset(valid_opts, 0, sizeof(*opts)); memcpy(valid_opts, opts, size); } switch (cmd) { case MIGRATE_PRE_DUMP: if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); goto on_error; } ret = !__criu_pre_dump(c, valid_opts); break; case MIGRATE_DUMP: if (!do_lxcapi_is_running(c)) { ERROR("container is not running"); goto on_error; } ret = !__criu_dump(c, valid_opts); break; case MIGRATE_RESTORE: if (do_lxcapi_is_running(c)) { ERROR("container is already running"); goto on_error; } ret = !__criu_restore(c, valid_opts); break; case MIGRATE_FEATURE_CHECK: features_to_check = valid_opts->features_to_check; ret = !__criu_check_feature(&features_to_check); if (ret) { /* Something went wrong. Let's let the caller * know which feature checks failed. */ valid_opts->features_to_check = features_to_check; } break; default: ERROR("invalid migrate command %u", cmd); ret = -EINVAL; } on_error: if (size < sizeof(*opts)) free(valid_opts); return ret; } WRAP_API_3(int, lxcapi_migrate, unsigned int, struct migrate_opts *, unsigned int) static bool do_lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose) { struct migrate_opts opts; memset(&opts, 0, sizeof(opts)); opts.directory = directory; opts.stop = stop; opts.verbose = verbose; return !do_lxcapi_migrate(c, MIGRATE_DUMP, &opts, sizeof(opts)); } WRAP_API_3(bool, lxcapi_checkpoint, char *, bool, bool) static bool do_lxcapi_restore(struct lxc_container *c, char *directory, bool verbose) { struct migrate_opts opts; memset(&opts, 0, sizeof(opts)); opts.directory = directory; opts.verbose = verbose; return !do_lxcapi_migrate(c, MIGRATE_RESTORE, &opts, sizeof(opts)); } WRAP_API_2(bool, lxcapi_restore, char *, bool) /* @st_mode is the st_mode field of the stat(source) return struct */ static int create_mount_target(const char *dest, mode_t st_mode) { char *dirdup, *destdirname; int ret; dirdup = strdup(dest); if (!dirdup) { SYSERROR("Failed to duplicate target name \"%s\"", dest); return -1; } destdirname = dirname(dirdup); ret = mkdir_p(destdirname, 0755); if (ret < 0) { SYSERROR("Failed to create \"%s\"", destdirname); free(dirdup); return ret; } free(dirdup); (void)remove(dest); if (S_ISDIR(st_mode)) ret = mkdir(dest, 0000); else ret = mknod(dest, S_IFREG | 0000, 0); if (ret == 0) TRACE("Created mount target \"%s\"", dest); else if (ret < 0 && errno != EEXIST) { SYSERROR("Failed to create mount target \"%s\"", dest); return -1; } return 0; } static int do_lxcapi_mount(struct lxc_container *c, const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data, struct lxc_mount *mnt) { char *suff, *sret; char template[PATH_MAX], path[PATH_MAX]; pid_t pid, init_pid; struct stat sb; bool is_dir; int ret = -1, fd = -EBADF; if (!c || !c->lxc_conf) { ERROR("Container or configuration is NULL"); return -EINVAL; } if (!c->lxc_conf->shmount.path_host) { ERROR("Host path to shared mountpoint must be specified in the config\n"); return -EINVAL; } ret = snprintf(template, sizeof(template), "%s/.lxcmount_XXXXXX", c->lxc_conf->shmount.path_host); if (ret < 0 || (size_t)ret >= sizeof(template)) { SYSERROR("Error writing shmounts tempdir name"); goto out; } /* Create a temporary file / dir under the shared mountpoint */ if (!source || strcmp(source, "") == 0) { /* If source is not specified, maybe we want to mount a filesystem? */ sb.st_mode = S_IFDIR; } else { ret = stat(source, &sb); if (ret < 0) { SYSERROR("Error getting stat info about the source \"%s\"", source); goto out; } } is_dir = (S_ISDIR(sb.st_mode) != 0); if (is_dir) { sret = mkdtemp(template); if (!sret) { SYSERROR("Could not create shmounts temporary dir"); goto out; } } else { fd = lxc_make_tmpfile(template, false); if (fd < 0) { SYSERROR("Could not create shmounts temporary file"); goto out; } } /* Do the fork */ pid = fork(); if (pid < 0) { SYSERROR("Could not fork"); goto out; } if (pid == 0) { /* Do the mount */ ret = mount(source, template, filesystemtype, mountflags, data); if (ret < 0) { SYSERROR("Failed to mount onto \"%s\"", template); _exit(EXIT_FAILURE); } TRACE("Mounted \"%s\" onto \"%s\"", source, template); init_pid = do_lxcapi_init_pid(c); if (init_pid < 0) { ERROR("Failed to obtain container's init pid"); _exit(EXIT_FAILURE); } /* Enter the container namespaces */ if (!lxc_list_empty(&c->lxc_conf->id_map)) { if (!switch_to_ns(init_pid, "user")) { ERROR("Failed to enter user namespace"); _exit(EXIT_FAILURE); } if (!lxc_switch_uid_gid(0, 0)) _exit(EXIT_FAILURE); } if (!switch_to_ns(init_pid, "mnt")) { ERROR("Failed to enter mount namespace"); _exit(EXIT_FAILURE); } ret = create_mount_target(target, sb.st_mode); if (ret < 0) _exit(EXIT_FAILURE); suff = strrchr(template, '/'); if (!suff) goto cleanup_target_in_child; ret = snprintf(path, sizeof(path), "%s%s", c->lxc_conf->shmount.path_cont, suff); if (ret < 0 || (size_t)ret >= sizeof(path)) { SYSERROR("Error writing container mountpoint name"); goto cleanup_target_in_child; } ret = mount(path, target, NULL, MS_MOVE | MS_REC, NULL); if (ret < 0) { SYSERROR("Failed to move the mount from \"%s\" to \"%s\"", path, target); goto cleanup_target_in_child; } TRACE("Moved mount from \"%s\" to \"%s\"", path, target); _exit(EXIT_SUCCESS); cleanup_target_in_child: (void)remove(target); _exit(EXIT_FAILURE); } ret = wait_for_pid(pid); if (ret < 0) SYSERROR("Wait for the child with pid %ld failed", (long)pid); else ret = 0; if (umount2(template, MNT_DETACH)) SYSWARN("Failed to remove temporary mount \"%s\"", template); if (is_dir) (void)rmdir(template); else (void)unlink(template); out: if (fd >= 0) close(fd); return ret; } WRAP_API_6(int, lxcapi_mount, const char *, const char *, const char *, unsigned long, const void *, struct lxc_mount *) static int do_lxcapi_umount(struct lxc_container *c, const char *target, unsigned long flags, struct lxc_mount *mnt) { pid_t pid, init_pid; int ret = -1; if (!c || !c->lxc_conf) { ERROR("Container or configuration is NULL"); return -EINVAL; } /* Do the fork */ pid = fork(); if (pid < 0) { SYSERROR("Could not fork"); return -1; } if (pid == 0) { init_pid = do_lxcapi_init_pid(c); if (init_pid < 0) { ERROR("Failed to obtain container's init pid"); _exit(EXIT_FAILURE); } /* Enter the container namespaces */ if (!lxc_list_empty(&c->lxc_conf->id_map)) { if (!switch_to_ns(init_pid, "user")) { ERROR("Failed to enter user namespace"); _exit(EXIT_FAILURE); } } if (!switch_to_ns(init_pid, "mnt")) { ERROR("Failed to enter mount namespace"); _exit(EXIT_FAILURE); } /* Do the unmount */ ret = umount2(target, flags); if (ret < 0) { SYSERROR("Failed to umount \"%s\"", target); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } ret = wait_for_pid(pid); if (ret < 0) { SYSERROR("Wait for the child with pid %ld failed", (long)pid); return -ret; } return 0; } WRAP_API_3(int, lxcapi_umount, const char *, unsigned long, struct lxc_mount*) static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...) { va_list ap; const char **argv; int ret; if (!c) return -1; current_config = c->lxc_conf; va_start(ap, arg); argv = lxc_va_arg_list_to_argv_const(ap, 1); va_end(ap); if (!argv) { ERROR("Memory allocation error."); ret = -1; goto out; } argv[0] = arg; ret = do_lxcapi_attach_run_wait(c, options, program, (const char * const *)argv); free((void*)argv); out: current_config = NULL; return ret; } static int do_lxcapi_seccomp_notify_fd(struct lxc_container *c) { if (!c || !c->lxc_conf) return ret_set_errno(-1, -EINVAL); return lxc_seccomp_get_notify_fd(&c->lxc_conf->seccomp); } WRAP_API(int, lxcapi_seccomp_notify_fd) struct lxc_container *lxc_container_new(const char *name, const char *configpath) { struct lxc_container *c; size_t len; int rc; if (!name) return NULL; c = malloc(sizeof(*c)); if (!c) { fprintf(stderr, "Failed to allocate memory for %s\n", name); return NULL; } memset(c, 0, sizeof(*c)); if (configpath) c->config_path = strdup(configpath); else c->config_path = strdup(lxc_global_config_value("lxc.lxcpath")); if (!c->config_path) { fprintf(stderr, "Failed to allocate memory for %s\n", name); goto err; } remove_trailing_slashes(c->config_path); len = strlen(name); c->name = malloc(len + 1); if (!c->name) { fprintf(stderr, "Failed to allocate memory for %s\n", name); goto err; } (void)strlcpy(c->name, name, len + 1); c->numthreads = 1; c->slock = lxc_newlock(c->config_path, name); if (!c->slock) { fprintf(stderr, "Failed to create lock for %s\n", name); goto err; } c->privlock = lxc_newlock(NULL, NULL); if (!c->privlock) { fprintf(stderr, "Failed to create private lock for %s\n", name); goto err; } if (!set_config_filename(c)) { fprintf(stderr, "Failed to create config file name for %s\n", name); goto err; } if (file_exists(c->configfile) && !lxcapi_load_config(c, NULL)) { fprintf(stderr, "Failed to load config for %s\n", name); goto err; } rc = ongoing_create(c); switch (rc) { case LXC_CREATE_INCOMPLETE: SYSERROR("Failed to complete container creation for %s", c->name); container_destroy(c, NULL); lxcapi_clear_config(c); break; case LXC_CREATE_ONGOING: /* container creation going on */ break; case LXC_CREATE_FAILED: /* container creation failed */ if (errno != EACCES && errno != EPERM) { /* insufficient privileges */ SYSERROR("Failed checking for incomplete container %s creation", c->name); goto err; } break; } c->daemonize = true; c->pidfile = NULL; /* Assign the member functions. */ c->is_defined = lxcapi_is_defined; c->state = lxcapi_state; c->is_running = lxcapi_is_running; c->freeze = lxcapi_freeze; c->unfreeze = lxcapi_unfreeze; c->console = lxcapi_console; c->console_getfd = lxcapi_console_getfd; c->init_pid = lxcapi_init_pid; c->init_pidfd = lxcapi_init_pidfd; c->load_config = lxcapi_load_config; c->want_daemonize = lxcapi_want_daemonize; c->want_close_all_fds = lxcapi_want_close_all_fds; c->start = lxcapi_start; c->startl = lxcapi_startl; c->stop = lxcapi_stop; c->config_file_name = lxcapi_config_file_name; c->wait = lxcapi_wait; c->set_config_item = lxcapi_set_config_item; c->destroy = lxcapi_destroy; c->destroy_with_snapshots = lxcapi_destroy_with_snapshots; c->rename = lxcapi_rename; c->save_config = lxcapi_save_config; c->get_keys = lxcapi_get_keys; c->create = lxcapi_create; c->createl = lxcapi_createl; c->shutdown = lxcapi_shutdown; c->reboot = lxcapi_reboot; c->reboot2 = lxcapi_reboot2; c->clear_config = lxcapi_clear_config; c->clear_config_item = lxcapi_clear_config_item; c->get_config_item = lxcapi_get_config_item; c->get_running_config_item = lxcapi_get_running_config_item; c->get_cgroup_item = lxcapi_get_cgroup_item; c->set_cgroup_item = lxcapi_set_cgroup_item; c->get_config_path = lxcapi_get_config_path; c->set_config_path = lxcapi_set_config_path; c->clone = lxcapi_clone; c->get_interfaces = lxcapi_get_interfaces; c->get_ips = lxcapi_get_ips; c->attach = lxcapi_attach; c->attach_run_wait = lxcapi_attach_run_wait; c->attach_run_waitl = lxcapi_attach_run_waitl; c->snapshot = lxcapi_snapshot; c->snapshot_list = lxcapi_snapshot_list; c->snapshot_restore = lxcapi_snapshot_restore; c->snapshot_destroy = lxcapi_snapshot_destroy; c->snapshot_destroy_all = lxcapi_snapshot_destroy_all; c->may_control = lxcapi_may_control; c->add_device_node = lxcapi_add_device_node; c->remove_device_node = lxcapi_remove_device_node; c->attach_interface = lxcapi_attach_interface; c->detach_interface = lxcapi_detach_interface; c->checkpoint = lxcapi_checkpoint; c->restore = lxcapi_restore; c->migrate = lxcapi_migrate; c->console_log = lxcapi_console_log; c->mount = lxcapi_mount; c->umount = lxcapi_umount; c->seccomp_notify_fd = lxcapi_seccomp_notify_fd; return c; err: lxc_container_free(c); return NULL; } int lxc_get_wait_states(const char **states) { int i; if (states) for (i=0; i