├── .github └── workflows │ ├── commits.yml │ ├── static.yml │ └── test.yml ├── .gitignore ├── AUTHORS ├── LICENSE ├── Makefile ├── README.md ├── cgo.go ├── container.go ├── doc.go ├── error.go ├── examples ├── Makefile ├── attach │ └── attach.go ├── attach_with_pipes │ └── attach_with_pipes.go ├── checkpoint │ └── checkpoint.go ├── clone │ └── clone.go ├── concurrent_create │ └── concurrent_create.go ├── concurrent_destroy │ └── concurrent_destroy.go ├── concurrent_shutdown │ └── concurrent_shutdown.go ├── concurrent_start │ └── concurrent_start.go ├── concurrent_stop │ └── concurrent_stop.go ├── concurrent_stress │ └── concurrent_stress.go ├── config │ └── config.go ├── console │ └── console.go ├── create │ └── create.go ├── create_snapshot │ └── create_snapshot.go ├── destroy │ └── destroy.go ├── destroy_snapshots │ └── destroy_snapshots.go ├── device_add_remove │ └── device_add_remove.go ├── execute │ └── execute.go ├── freeze │ └── freeze.go ├── interfaces │ └── interfaces.go ├── ipaddress │ └── ipaddress.go ├── limit │ └── limit.go ├── list │ └── list.go ├── list_keys │ └── list_keys.go ├── list_snapshots │ └── list_snapshots.go ├── reboot │ └── reboot.go ├── rename │ └── rename.go ├── restore_snapshot │ └── restore_snapshot.go ├── shutdown │ └── shutdown.go ├── start │ └── start.go ├── stats │ └── stats.go ├── stop │ └── stop.go └── unfreeze │ └── unfreeze.go ├── go.mod ├── go.sum ├── linking_dynamic.go ├── linking_static.go ├── lxc-binding.c ├── lxc-binding.go ├── lxc-binding.h ├── lxc_test.go ├── options.go ├── type.go └── util.go /.github/workflows/commits.yml: -------------------------------------------------------------------------------- 1 | name: Commits 2 | on: 3 | - pull_request 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | dco-check: 10 | permissions: 11 | pull-requests: read # for tim-actions/get-pr-commits to get list of commits from the PR 12 | name: Signed-off-by (DCO) 13 | runs-on: ubuntu-20.04 14 | steps: 15 | - name: Get PR Commits 16 | id: 'get-pr-commits' 17 | uses: tim-actions/get-pr-commits@master 18 | with: 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | 21 | - name: Check that all commits are signed-off 22 | uses: tim-actions/dco@master 23 | with: 24 | commits: ${{ steps.get-pr-commits.outputs.commits }} 25 | 26 | target-branch: 27 | permissions: 28 | contents: none 29 | name: Branch target 30 | runs-on: ubuntu-20.04 31 | steps: 32 | - name: Check branch target 33 | env: 34 | TARGET: ${{ github.event.pull_request.base.ref }} 35 | run: | 36 | set -x 37 | [ "${TARGET}" = "v2" ] && exit 0 38 | 39 | echo "Invalid branch target: ${TARGET}" 40 | exit 1 41 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | name: Static analysis 2 | on: 3 | - push 4 | - pull_request 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-20.04 9 | steps: 10 | - name: Install Go 11 | uses: actions/setup-go@v4 12 | with: 13 | go-version: stable 14 | 15 | - name: Checkout code 16 | uses: actions/checkout@v2 17 | 18 | - name: Validate Go modules 19 | run: | 20 | make update-gomod 21 | git diff --exit-code 22 | 23 | - name: Install dependencies 24 | run: | 25 | sudo add-apt-repository ppa:ubuntu-lxc/daily -y 26 | sudo apt-get install -qq lxc-dev pkg-config libdbus-1-dev libseccomp-dev libcap-dev 27 | go install golang.org/x/lint/golint@latest 28 | go install golang.org/x/tools/cmd/goimports@latest 29 | 30 | - name: Static analysis 31 | run: make all 32 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI tests 2 | on: 3 | - push 4 | - pull_request 5 | 6 | jobs: 7 | test: 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | go: 12 | - 1.20.x 13 | - stable 14 | os: 15 | - ubuntu-20.04 16 | - ubuntu-22.04 17 | runs-on: ${{ matrix.os }} 18 | steps: 19 | - name: Install Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: ${{ matrix.go }} 23 | 24 | - name: Checkout code 25 | uses: actions/checkout@v2 26 | 27 | - name: Install dependencies 28 | run: | 29 | sudo add-apt-repository ppa:ubuntu-lxc/daily -y 30 | sudo apt-get install -qq apparmor lxc lxc-dev pkg-config uidmap busybox libdbus-1-dev libseccomp-dev libcap-dev libselinux-dev nftables iptables 31 | 32 | - name: Reset all firewalling 33 | run: | 34 | sudo iptables -F 35 | sudo iptables -P INPUT ACCEPT 36 | sudo iptables -P OUTPUT ACCEPT 37 | sudo iptables -P FORWARD ACCEPT 38 | sudo nft flush ruleset 39 | 40 | - name: Setup test environment 41 | run: | 42 | # Setup uid/gid and veth allocations 43 | echo "${USER}:100000:65536" | sudo tee /etc/subuid 44 | echo "${USER}:100000:65536" | sudo tee /etc/subgid 45 | echo "${USER} veth lxcbr0 10" | sudo tee -a /etc/lxc/lxc-usernet 46 | 47 | # Local user configuration for userns 48 | mkdir -p ~/.config/lxc/ 49 | cp /etc/lxc/default.conf ~/.config/lxc/default.conf 50 | echo "lxc.idmap = u 0 100000 65536" | tee -a ~/.config/lxc/default.conf 51 | echo "lxc.idmap = g 0 100000 65536" | tee -a ~/.config/lxc/default.conf 52 | 53 | # Allow traversal to the containers 54 | mkdir -p ~/.local/share/lxc 55 | chmod +x ~/ ~/.local ~/.local/share ~/.local/share/lxc 56 | 57 | - name: Build the test 58 | run: 59 | go test -c -coverprofile=profile 60 | 61 | - name: Unprivileged tests 62 | env: 63 | DOWNLOAD_KEYSERVER: keyserver.ubuntu.com 64 | run: | 65 | # Make all cgroups writable 66 | if [ ! -e /sys/fs/cgroup/cgroup.controllers ]; then 67 | # CGroup 1 68 | for d in /sys/fs/cgroup/*; do 69 | [ -f $d/cgroup.clone_children ] && echo 1 | sudo tee $d/cgroup.clone_children 70 | [ -f $d/cgroup.use_hierarchy ] && echo 1 | sudo tee $d/cgroup.use_hierarchy 71 | 72 | sudo mkdir -p $d/lxc-test 73 | sudo chown -R $USER: $d/lxc-test 74 | echo $$ | sudo tee $d/lxc-test/cgroup.procs 75 | done 76 | 77 | # Run the unprivileged tests 78 | ./go-lxc.test -test.v -test.coverprofile=/tmp/unpriv.out 79 | else 80 | # CGroup 2 81 | sudo mkdir -p /sys/fs/cgroup/lxc-test 82 | sudo chown -R $USER: /sys/fs/cgroup/lxc-test 83 | echo $$ | sudo tee /sys/fs/cgroup/lxc-test/cgroup.procs 84 | 85 | # Run the unprivileged tests 86 | systemd-run --unit=go-lxc-test --user --scope -p "Delegate=yes" ./go-lxc.test -test.v -test.coverprofile=/tmp/unpriv.out 87 | fi 88 | 89 | - name: Privileged tests 90 | env: 91 | DOWNLOAD_KEYSERVER: keyserver.ubuntu.com 92 | run: 93 | sudo -E ./go-lxc.test -test.v -test.coverprofile=/tmp/priv.out 94 | 95 | - name: Code coverage 96 | run: make cover 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | examples/*/* 2 | !examples/*/*.go 3 | coverage.out 4 | tags 5 | *.swp 6 | **/.idea 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Go-LXC authors for copyright purposes. 2 | 3 | # Names should be added to this file as 4 | #Name or Organization 5 | # The email address is not required for organizations. 6 | 7 | # Please keep the list sorted. 8 | 9 | David Cramer 10 | Fatih Arslan 11 | Kelsey Hightower 12 | S.Çağlar Onur 13 | Serge Hallyn 14 | Stéphane Graber 15 | Syed 16 | Tycho Andersen 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the LGPLv2.1, included below. 2 | 3 | As a special exception to the GNU Lesser General Public License version 2.1 4 | ("LGPL2.1"), the copyright holders of this Library give you permission to 5 | convey to a third party a Combined Work that links statically or dynamically 6 | to this Library without providing any Minimal Corresponding Source or 7 | Minimal Application Code or providing the installation information, 8 | provided that you comply with the other provisions of LGPL2.1 9 | and provided that you meet, for the Application the terms and conditions 10 | of the license(s) which apply to the Application. 11 | 12 | Except as stated in this special exception, the provisions of LGPL2.1 will 13 | continue to comply in full to this Library. If you modify this Library, you 14 | may apply this exception to your version of this Library, but you are not 15 | obliged to do so. If you do not wish to do so, delete this exception 16 | statement from your version. This exception does not (and cannot) modify any 17 | license terms which apply to the Application, with which you must still 18 | comply. 19 | 20 | 21 | GNU LESSER GENERAL PUBLIC LICENSE 22 | Version 2.1, February 1999 23 | 24 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 25 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 26 | Everyone is permitted to copy and distribute verbatim copies 27 | of this license document, but changing it is not allowed. 28 | 29 | [This is the first released version of the Lesser GPL. It also counts 30 | as the successor of the GNU Library Public License, version 2, hence 31 | the version number 2.1.] 32 | 33 | Preamble 34 | 35 | The licenses for most software are designed to take away your 36 | freedom to share and change it. By contrast, the GNU General Public 37 | Licenses are intended to guarantee your freedom to share and change 38 | free software--to make sure the software is free for all its users. 39 | 40 | This license, the Lesser General Public License, applies to some 41 | specially designated software packages--typically libraries--of the 42 | Free Software Foundation and other authors who decide to use it. You 43 | can use it too, but we suggest you first think carefully about whether 44 | this license or the ordinary General Public License is the better 45 | strategy to use in any particular case, based on the explanations below. 46 | 47 | When we speak of free software, we are referring to freedom of use, 48 | not price. Our General Public Licenses are designed to make sure that 49 | you have the freedom to distribute copies of free software (and charge 50 | for this service if you wish); that you receive source code or can get 51 | it if you want it; that you can change the software and use pieces of 52 | it in new free programs; and that you are informed that you can do 53 | these things. 54 | 55 | To protect your rights, we need to make restrictions that forbid 56 | distributors to deny you these rights or to ask you to surrender these 57 | rights. These restrictions translate to certain responsibilities for 58 | you if you distribute copies of the library or if you modify it. 59 | 60 | For example, if you distribute copies of the library, whether gratis 61 | or for a fee, you must give the recipients all the rights that we gave 62 | you. You must make sure that they, too, receive or can get the source 63 | code. If you link other code with the library, you must provide 64 | complete object files to the recipients, so that they can relink them 65 | with the library after making changes to the library and recompiling 66 | it. And you must show them these terms so they know their rights. 67 | 68 | We protect your rights with a two-step method: (1) we copyright the 69 | library, and (2) we offer you this license, which gives you legal 70 | permission to copy, distribute and/or modify the library. 71 | 72 | To protect each distributor, we want to make it very clear that 73 | there is no warranty for the free library. Also, if the library is 74 | modified by someone else and passed on, the recipients should know 75 | that what they have is not the original version, so that the original 76 | author's reputation will not be affected by problems that might be 77 | introduced by others. 78 | 79 | Finally, software patents pose a constant threat to the existence of 80 | any free program. We wish to make sure that a company cannot 81 | effectively restrict the users of a free program by obtaining a 82 | restrictive license from a patent holder. Therefore, we insist that 83 | any patent license obtained for a version of the library must be 84 | consistent with the full freedom of use specified in this license. 85 | 86 | Most GNU software, including some libraries, is covered by the 87 | ordinary GNU General Public License. This license, the GNU Lesser 88 | General Public License, applies to certain designated libraries, and 89 | is quite different from the ordinary General Public License. We use 90 | this license for certain libraries in order to permit linking those 91 | libraries into non-free programs. 92 | 93 | When a program is linked with a library, whether statically or using 94 | a shared library, the combination of the two is legally speaking a 95 | combined work, a derivative of the original library. The ordinary 96 | General Public License therefore permits such linking only if the 97 | entire combination fits its criteria of freedom. The Lesser General 98 | Public License permits more lax criteria for linking other code with 99 | the library. 100 | 101 | We call this license the "Lesser" General Public License because it 102 | does Less to protect the user's freedom than the ordinary General 103 | Public License. It also provides other free software developers Less 104 | of an advantage over competing non-free programs. These disadvantages 105 | are the reason we use the ordinary General Public License for many 106 | libraries. However, the Lesser license provides advantages in certain 107 | special circumstances. 108 | 109 | For example, on rare occasions, there may be a special need to 110 | encourage the widest possible use of a certain library, so that it becomes 111 | a de-facto standard. To achieve this, non-free programs must be 112 | allowed to use the library. A more frequent case is that a free 113 | library does the same job as widely used non-free libraries. In this 114 | case, there is little to gain by limiting the free library to free 115 | software only, so we use the Lesser General Public License. 116 | 117 | In other cases, permission to use a particular library in non-free 118 | programs enables a greater number of people to use a large body of 119 | free software. For example, permission to use the GNU C Library in 120 | non-free programs enables many more people to use the whole GNU 121 | operating system, as well as its variant, the GNU/Linux operating 122 | system. 123 | 124 | Although the Lesser General Public License is Less protective of the 125 | users' freedom, it does ensure that the user of a program that is 126 | linked with the Library has the freedom and the wherewithal to run 127 | that program using a modified version of the Library. 128 | 129 | The precise terms and conditions for copying, distribution and 130 | modification follow. Pay close attention to the difference between a 131 | "work based on the library" and a "work that uses the library". The 132 | former contains code derived from the library, whereas the latter must 133 | be combined with the library in order to run. 134 | 135 | GNU LESSER GENERAL PUBLIC LICENSE 136 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 137 | 138 | 0. This License Agreement applies to any software library or other 139 | program which contains a notice placed by the copyright holder or 140 | other authorized party saying it may be distributed under the terms of 141 | this Lesser General Public License (also called "this License"). 142 | Each licensee is addressed as "you". 143 | 144 | A "library" means a collection of software functions and/or data 145 | prepared so as to be conveniently linked with application programs 146 | (which use some of those functions and data) to form executables. 147 | 148 | The "Library", below, refers to any such software library or work 149 | which has been distributed under these terms. A "work based on the 150 | Library" means either the Library or any derivative work under 151 | copyright law: that is to say, a work containing the Library or a 152 | portion of it, either verbatim or with modifications and/or translated 153 | straightforwardly into another language. (Hereinafter, translation is 154 | included without limitation in the term "modification".) 155 | 156 | "Source code" for a work means the preferred form of the work for 157 | making modifications to it. For a library, complete source code means 158 | all the source code for all modules it contains, plus any associated 159 | interface definition files, plus the scripts used to control compilation 160 | and installation of the library. 161 | 162 | Activities other than copying, distribution and modification are not 163 | covered by this License; they are outside its scope. The act of 164 | running a program using the Library is not restricted, and output from 165 | such a program is covered only if its contents constitute a work based 166 | on the Library (independent of the use of the Library in a tool for 167 | writing it). Whether that is true depends on what the Library does 168 | and what the program that uses the Library does. 169 | 170 | 1. You may copy and distribute verbatim copies of the Library's 171 | complete source code as you receive it, in any medium, provided that 172 | you conspicuously and appropriately publish on each copy an 173 | appropriate copyright notice and disclaimer of warranty; keep intact 174 | all the notices that refer to this License and to the absence of any 175 | warranty; and distribute a copy of this License along with the 176 | Library. 177 | 178 | You may charge a fee for the physical act of transferring a copy, 179 | and you may at your option offer warranty protection in exchange for a 180 | fee. 181 | 182 | 2. You may modify your copy or copies of the Library or any portion 183 | of it, thus forming a work based on the Library, and copy and 184 | distribute such modifications or work under the terms of Section 1 185 | above, provided that you also meet all of these conditions: 186 | 187 | a) The modified work must itself be a software library. 188 | 189 | b) You must cause the files modified to carry prominent notices 190 | stating that you changed the files and the date of any change. 191 | 192 | c) You must cause the whole of the work to be licensed at no 193 | charge to all third parties under the terms of this License. 194 | 195 | d) If a facility in the modified Library refers to a function or a 196 | table of data to be supplied by an application program that uses 197 | the facility, other than as an argument passed when the facility 198 | is invoked, then you must make a good faith effort to ensure that, 199 | in the event an application does not supply such function or 200 | table, the facility still operates, and performs whatever part of 201 | its purpose remains meaningful. 202 | 203 | (For example, a function in a library to compute square roots has 204 | a purpose that is entirely well-defined independent of the 205 | application. Therefore, Subsection 2d requires that any 206 | application-supplied function or table used by this function must 207 | be optional: if the application does not supply it, the square 208 | root function must still compute square roots.) 209 | 210 | These requirements apply to the modified work as a whole. If 211 | identifiable sections of that work are not derived from the Library, 212 | and can be reasonably considered independent and separate works in 213 | themselves, then this License, and its terms, do not apply to those 214 | sections when you distribute them as separate works. But when you 215 | distribute the same sections as part of a whole which is a work based 216 | on the Library, the distribution of the whole must be on the terms of 217 | this License, whose permissions for other licensees extend to the 218 | entire whole, and thus to each and every part regardless of who wrote 219 | it. 220 | 221 | Thus, it is not the intent of this section to claim rights or contest 222 | your rights to work written entirely by you; rather, the intent is to 223 | exercise the right to control the distribution of derivative or 224 | collective works based on the Library. 225 | 226 | In addition, mere aggregation of another work not based on the Library 227 | with the Library (or with a work based on the Library) on a volume of 228 | a storage or distribution medium does not bring the other work under 229 | the scope of this License. 230 | 231 | 3. You may opt to apply the terms of the ordinary GNU General Public 232 | License instead of this License to a given copy of the Library. To do 233 | this, you must alter all the notices that refer to this License, so 234 | that they refer to the ordinary GNU General Public License, version 2, 235 | instead of to this License. (If a newer version than version 2 of the 236 | ordinary GNU General Public License has appeared, then you can specify 237 | that version instead if you wish.) Do not make any other change in 238 | these notices. 239 | 240 | Once this change is made in a given copy, it is irreversible for 241 | that copy, so the ordinary GNU General Public License applies to all 242 | subsequent copies and derivative works made from that copy. 243 | 244 | This option is useful when you wish to copy part of the code of 245 | the Library into a program that is not a library. 246 | 247 | 4. You may copy and distribute the Library (or a portion or 248 | derivative of it, under Section 2) in object code or executable form 249 | under the terms of Sections 1 and 2 above provided that you accompany 250 | it with the complete corresponding machine-readable source code, which 251 | must be distributed under the terms of Sections 1 and 2 above on a 252 | medium customarily used for software interchange. 253 | 254 | If distribution of object code is made by offering access to copy 255 | from a designated place, then offering equivalent access to copy the 256 | source code from the same place satisfies the requirement to 257 | distribute the source code, even though third parties are not 258 | compelled to copy the source along with the object code. 259 | 260 | 5. A program that contains no derivative of any portion of the 261 | Library, but is designed to work with the Library by being compiled or 262 | linked with it, is called a "work that uses the Library". Such a 263 | work, in isolation, is not a derivative work of the Library, and 264 | therefore falls outside the scope of this License. 265 | 266 | However, linking a "work that uses the Library" with the Library 267 | creates an executable that is a derivative of the Library (because it 268 | contains portions of the Library), rather than a "work that uses the 269 | library". The executable is therefore covered by this License. 270 | Section 6 states terms for distribution of such executables. 271 | 272 | When a "work that uses the Library" uses material from a header file 273 | that is part of the Library, the object code for the work may be a 274 | derivative work of the Library even though the source code is not. 275 | Whether this is true is especially significant if the work can be 276 | linked without the Library, or if the work is itself a library. The 277 | threshold for this to be true is not precisely defined by law. 278 | 279 | If such an object file uses only numerical parameters, data 280 | structure layouts and accessors, and small macros and small inline 281 | functions (ten lines or less in length), then the use of the object 282 | file is unrestricted, regardless of whether it is legally a derivative 283 | work. (Executables containing this object code plus portions of the 284 | Library will still fall under Section 6.) 285 | 286 | Otherwise, if the work is a derivative of the Library, you may 287 | distribute the object code for the work under the terms of Section 6. 288 | Any executables containing that work also fall under Section 6, 289 | whether or not they are linked directly with the Library itself. 290 | 291 | 6. As an exception to the Sections above, you may also combine or 292 | link a "work that uses the Library" with the Library to produce a 293 | work containing portions of the Library, and distribute that work 294 | under terms of your choice, provided that the terms permit 295 | modification of the work for the customer's own use and reverse 296 | engineering for debugging such modifications. 297 | 298 | You must give prominent notice with each copy of the work that the 299 | Library is used in it and that the Library and its use are covered by 300 | this License. You must supply a copy of this License. If the work 301 | during execution displays copyright notices, you must include the 302 | copyright notice for the Library among them, as well as a reference 303 | directing the user to the copy of this License. Also, you must do one 304 | of these things: 305 | 306 | a) Accompany the work with the complete corresponding 307 | machine-readable source code for the Library including whatever 308 | changes were used in the work (which must be distributed under 309 | Sections 1 and 2 above); and, if the work is an executable linked 310 | with the Library, with the complete machine-readable "work that 311 | uses the Library", as object code and/or source code, so that the 312 | user can modify the Library and then relink to produce a modified 313 | executable containing the modified Library. (It is understood 314 | that the user who changes the contents of definitions files in the 315 | Library will not necessarily be able to recompile the application 316 | to use the modified definitions.) 317 | 318 | b) Use a suitable shared library mechanism for linking with the 319 | Library. A suitable mechanism is one that (1) uses at run time a 320 | copy of the library already present on the user's computer system, 321 | rather than copying library functions into the executable, and (2) 322 | will operate properly with a modified version of the library, if 323 | the user installs one, as long as the modified version is 324 | interface-compatible with the version that the work was made with. 325 | 326 | c) Accompany the work with a written offer, valid for at 327 | least three years, to give the same user the materials 328 | specified in Subsection 6a, above, for a charge no more 329 | than the cost of performing this distribution. 330 | 331 | d) If distribution of the work is made by offering access to copy 332 | from a designated place, offer equivalent access to copy the above 333 | specified materials from the same place. 334 | 335 | e) Verify that the user has already received a copy of these 336 | materials or that you have already sent this user a copy. 337 | 338 | For an executable, the required form of the "work that uses the 339 | Library" must include any data and utility programs needed for 340 | reproducing the executable from it. However, as a special exception, 341 | the materials to be distributed need not include anything that is 342 | normally distributed (in either source or binary form) with the major 343 | components (compiler, kernel, and so on) of the operating system on 344 | which the executable runs, unless that component itself accompanies 345 | the executable. 346 | 347 | It may happen that this requirement contradicts the license 348 | restrictions of other proprietary libraries that do not normally 349 | accompany the operating system. Such a contradiction means you cannot 350 | use both them and the Library together in an executable that you 351 | distribute. 352 | 353 | 7. You may place library facilities that are a work based on the 354 | Library side-by-side in a single library together with other library 355 | facilities not covered by this License, and distribute such a combined 356 | library, provided that the separate distribution of the work based on 357 | the Library and of the other library facilities is otherwise 358 | permitted, and provided that you do these two things: 359 | 360 | a) Accompany the combined library with a copy of the same work 361 | based on the Library, uncombined with any other library 362 | facilities. This must be distributed under the terms of the 363 | Sections above. 364 | 365 | b) Give prominent notice with the combined library of the fact 366 | that part of it is a work based on the Library, and explaining 367 | where to find the accompanying uncombined form of the same work. 368 | 369 | 8. You may not copy, modify, sublicense, link with, or distribute 370 | the Library except as expressly provided under this License. Any 371 | attempt otherwise to copy, modify, sublicense, link with, or 372 | distribute the Library is void, and will automatically terminate your 373 | rights under this License. However, parties who have received copies, 374 | or rights, from you under this License will not have their licenses 375 | terminated so long as such parties remain in full compliance. 376 | 377 | 9. You are not required to accept this License, since you have not 378 | signed it. However, nothing else grants you permission to modify or 379 | distribute the Library or its derivative works. These actions are 380 | prohibited by law if you do not accept this License. Therefore, by 381 | modifying or distributing the Library (or any work based on the 382 | Library), you indicate your acceptance of this License to do so, and 383 | all its terms and conditions for copying, distributing or modifying 384 | the Library or works based on it. 385 | 386 | 10. Each time you redistribute the Library (or any work based on the 387 | Library), the recipient automatically receives a license from the 388 | original licensor to copy, distribute, link with or modify the Library 389 | subject to these terms and conditions. You may not impose any further 390 | restrictions on the recipients' exercise of the rights granted herein. 391 | You are not responsible for enforcing compliance by third parties with 392 | this License. 393 | 394 | 11. If, as a consequence of a court judgment or allegation of patent 395 | infringement or for any other reason (not limited to patent issues), 396 | conditions are imposed on you (whether by court order, agreement or 397 | otherwise) that contradict the conditions of this License, they do not 398 | excuse you from the conditions of this License. If you cannot 399 | distribute so as to satisfy simultaneously your obligations under this 400 | License and any other pertinent obligations, then as a consequence you 401 | may not distribute the Library at all. For example, if a patent 402 | license would not permit royalty-free redistribution of the Library by 403 | all those who receive copies directly or indirectly through you, then 404 | the only way you could satisfy both it and this License would be to 405 | refrain entirely from distribution of the Library. 406 | 407 | If any portion of this section is held invalid or unenforceable under any 408 | particular circumstance, the balance of the section is intended to apply, 409 | and the section as a whole is intended to apply in other circumstances. 410 | 411 | It is not the purpose of this section to induce you to infringe any 412 | patents or other property right claims or to contest validity of any 413 | such claims; this section has the sole purpose of protecting the 414 | integrity of the free software distribution system which is 415 | implemented by public license practices. Many people have made 416 | generous contributions to the wide range of software distributed 417 | through that system in reliance on consistent application of that 418 | system; it is up to the author/donor to decide if he or she is willing 419 | to distribute software through any other system and a licensee cannot 420 | impose that choice. 421 | 422 | This section is intended to make thoroughly clear what is believed to 423 | be a consequence of the rest of this License. 424 | 425 | 12. If the distribution and/or use of the Library is restricted in 426 | certain countries either by patents or by copyrighted interfaces, the 427 | original copyright holder who places the Library under this License may add 428 | an explicit geographical distribution limitation excluding those countries, 429 | so that distribution is permitted only in or among countries not thus 430 | excluded. In such case, this License incorporates the limitation as if 431 | written in the body of this License. 432 | 433 | 13. The Free Software Foundation may publish revised and/or new 434 | versions of the Lesser General Public License from time to time. 435 | Such new versions will be similar in spirit to the present version, 436 | but may differ in detail to address new problems or concerns. 437 | 438 | Each version is given a distinguishing version number. If the Library 439 | specifies a version number of this License which applies to it and 440 | "any later version", you have the option of following the terms and 441 | conditions either of that version or of any later version published by 442 | the Free Software Foundation. If the Library does not specify a 443 | license version number, you may choose any version ever published by 444 | the Free Software Foundation. 445 | 446 | 14. If you wish to incorporate parts of the Library into other free 447 | programs whose distribution conditions are incompatible with these, 448 | write to the author to ask for permission. For software which is 449 | copyrighted by the Free Software Foundation, write to the Free 450 | Software Foundation; we sometimes make exceptions for this. Our 451 | decision will be guided by the two goals of preserving the free status 452 | of all derivatives of our free software and of promoting the sharing 453 | and reuse of software generally. 454 | 455 | NO WARRANTY 456 | 457 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 458 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 459 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 460 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 461 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 462 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 463 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 464 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 465 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 466 | 467 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 468 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 469 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 470 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 471 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 472 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 473 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 474 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 475 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 476 | DAMAGES. 477 | 478 | END OF TERMS AND CONDITIONS 479 | 480 | How to Apply These Terms to Your New Libraries 481 | 482 | If you develop a new library, and you want it to be of the greatest 483 | possible use to the public, we recommend making it free software that 484 | everyone can redistribute and change. You can do so by permitting 485 | redistribution under these terms (or, alternatively, under the terms of the 486 | ordinary General Public License). 487 | 488 | To apply these terms, attach the following notices to the library. It is 489 | safest to attach them to the start of each source file to most effectively 490 | convey the exclusion of warranty; and each file should have at least the 491 | "copyright" line and a pointer to where the full notice is found. 492 | 493 | 494 | Copyright (C) 495 | 496 | This library is free software; you can redistribute it and/or 497 | modify it under the terms of the GNU Lesser General Public 498 | License as published by the Free Software Foundation; either 499 | version 2.1 of the License, or (at your option) any later version. 500 | 501 | This library is distributed in the hope that it will be useful, 502 | but WITHOUT ANY WARRANTY; without even the implied warranty of 503 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 504 | Lesser General Public License for more details. 505 | 506 | You should have received a copy of the GNU Lesser General Public 507 | License along with this library; if not, write to the Free Software 508 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 509 | 510 | Also add information on how to contact you by electronic and paper mail. 511 | 512 | You should also get your employer (if you work as a programmer) or your 513 | school, if any, to sign a "copyright disclaimer" for the library, if 514 | necessary. Here is a sample; alter the names: 515 | 516 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 517 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 518 | 519 | , 1 April 1990 520 | Ty Coon, President of Vice 521 | 522 | That's all there is to it! 523 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NO_COLOR=\033[0m 2 | OK_COLOR=\033[0;32m 3 | 4 | all: format vet lint 5 | 6 | format: 7 | @echo "$(OK_COLOR)==> Formatting the code $(NO_COLOR)" 8 | @gofmt -s -w *.go 9 | @goimports -w *.go || true 10 | 11 | test-privileged: 12 | @echo "$(OK_COLOR)==> Running tests for privileged user $(NO_COLOR)" 13 | @sudo `which go` test -v -coverprofile=/tmp/priv.out 14 | @sudo `which go` test -tags static_build -v -coverprofile=/tmp/priv.out 15 | 16 | test-privileged-race: 17 | @echo "$(OK_COLOR)==> Running tests with -race flag for privileged user $(NO_COLOR)" 18 | @sudo `which go` test -race -v 19 | @sudo `which go` test -tags static_build -race -v 20 | 21 | test: 22 | @echo "$(OK_COLOR)==> Running tests for unprivileged user $(NO_COLOR)" 23 | @`which go` test -v -coverprofile=/tmp/unpriv.out 24 | @`which go` test -tags static_build -v -coverprofile=/tmp/unpriv.out 25 | 26 | test-race: 27 | @echo "$(OK_COLOR)==> Running tests with -race flag for unprivileged user $(NO_COLOR)" 28 | @`which go` test -race -v 29 | @`which go` test -tags static_build -race -v 30 | 31 | cover: 32 | @echo "$(OK_COLOR)==> Running cover for privileged user $(NO_COLOR)" 33 | @`which go` tool cover -func=/tmp/priv.out || true 34 | @echo "$(OK_COLOR)==> Running cover for unprivileged user $(NO_COLOR)" 35 | @`which go` tool cover -func=/tmp/unpriv.out || true 36 | 37 | doc: 38 | @`which go` doc github.com/lxc/go-lxc | less 39 | 40 | vet: 41 | @echo "$(OK_COLOR)==> Running go vet $(NO_COLOR)" 42 | @`which go` vet . 43 | 44 | lint: 45 | @echo "$(OK_COLOR)==> Running golint $(NO_COLOR)" 46 | @`which golint` . || true 47 | 48 | escape-analysis: 49 | @`which go` build -gcflags -m 50 | 51 | ctags: 52 | @ctags -R --languages=c,go 53 | 54 | update-gomod: 55 | go get -t -v -d -u ./... 56 | go mod tidy 57 | 58 | scope: 59 | @echo "$(OK_COLOR)==> Exported container calls in container.go $(NO_COLOR)" 60 | @/bin/grep -E "\bc+\.([A-Z])\w+" container.go || true 61 | 62 | .PHONY: all format test doc vet lint ctags 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![go-lxc](https://linuxcontainers.org/static/img/containers.png)](https://linuxcontainers.org/) 2 | # Go Bindings for LXC (Linux Containers) 3 | 4 | LXC is the well-known and heavily tested low-level Linux container runtime. It 5 | is in active development since 2008 and has proven itself in critical 6 | production environments world-wide. Some of its core contributors are the same 7 | people that helped to implement various well-known containerization features 8 | inside the Linux kernel. 9 | 10 | 11 | This package implements [Go](https://golang.org) bindings for the [LXC](https://linuxcontainers.org/lxc/introduction/) C API (liblxc). 12 | 13 | ## Status 14 | Type | Service | Status 15 | --- | --- | --- 16 | CI (Linux) | Github | [![CI tests](https://github.com/lxc/go-lxc/actions/workflows/test.yml/badge.svg?branch=v2)](https://github.com/lxc/go-lxc/actions/workflows/test.yml) 17 | Go documentation | Godoc | [![GoDoc](https://godoc.org/github.com/lxc/go-lxc?status.svg)](https://godoc.org/github.com/lxc/go-lxc) 18 | Static analysis | GoReport | [![Go Report Card](https://goreportcard.com/badge/github.com/lxc/go-lxc)](https://goreportcard.com/report/github.com/lxc/go-lxc) 19 | 20 | ## Requirements 21 | 22 | This package requires [LXC >= 1.0.0](https://github.com/lxc/lxc/releases) and its development package and their dependencies to be installed. Additionally, go-lxc requires Golang 1.10 or later to work. Following command should install required dependencies on Ubuntu 18.10: 23 | 24 | ```bash 25 | sudo apt update 26 | sudo apt install git golang gcc make liblxc1 liblxc-dev lxc-utils pkg-config 27 | ``` 28 | 29 | ## Installing 30 | 31 | To install it, run: 32 | 33 | ```bash 34 | go get github.com/lxc/go-lxc 35 | ``` 36 | 37 | ## Trying 38 | 39 | To try examples, run: 40 | 41 | ```bash 42 | # cd ~/go/src/github.com/lxc/go-lxc/examples/ 43 | 44 | # make 45 | ==> Running go vet 46 | ==> Building ... 47 | ... 48 | 49 | # create/create 50 | 2018/12/27 22:39:27 Creating container... 51 | 52 | # start/start 53 | 2018/12/27 22:39:39 Starting the container... 54 | 2018/12/27 22:39:39 Waiting container to startup networking... 55 | 56 | # attach/attach 57 | 2018/12/27 22:39:46 AttachShell 58 | root@rubik:/# hostname 59 | rubik 60 | root@rubik:/# exit 61 | exit 62 | 2018/12/27 22:39:52 RunCommand 63 | uid=0(root) gid=0(root) groups=0(root) 64 | 65 | # stop/stop 66 | 2018/12/27 22:39:54 Stopping the container... 67 | 68 | # destroy/destroy 69 | 2018/12/27 22:39:57 Destroying container... 70 | ``` 71 | 72 | ## Backwards Compatibility 73 | 74 | LXC has always focused on strong backwards compatibility. In fact, the API hasn't been broken from release `1.0.0` onwards. Main LXC is currently at version `2.*.*`. 75 | 76 | ## Examples 77 | 78 | See the [examples](https://github.com/lxc/go-lxc/tree/v2/examples) directory for some. 79 | 80 | ## Bug reports 81 | 82 | Bug reports can be filed at: 83 | 84 | ## Contributing 85 | 86 | Fixes and new features are greatly appreciated. We'd love to see go-lxc improve. To contribute to go-lxc; 87 | 88 | * **Fork** the repository 89 | * **Modify** your fork 90 | * Ensure your fork **passes all tests** 91 | * **Send** a pull request 92 | * Bonus points if the pull request includes *what* you changed, *why* you changed it, and *tests* attached. 93 | 94 | ## Getting help 95 | 96 | When you find you need help, the LXC projects provides you with several options. 97 | 98 | ### Discuss Forum 99 | 100 | We maintain an discuss forum at 101 | 102 | - https://discuss.linuxcontainers.org/ 103 | 104 | where you can get support. 105 | 106 | ### IRC 107 | 108 | You can find support by joining `#lxcontainers` on `Freenode`. 109 | 110 | ### Mailing Lists 111 | 112 | You can check out one of the two LXC mailing list archives and register if interested: 113 | 114 | - http://lists.linuxcontainers.org/listinfo/lxc-devel 115 | - http://lists.linuxcontainers.org/listinfo/lxc-users 116 | -------------------------------------------------------------------------------- /cgo.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | package lxc 6 | 7 | // #cgo CFLAGS: -fvisibility=hidden 8 | import "C" 9 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package lxc provides Go Bindings for LXC (Linux Containers) C API. 3 | 4 | LXC (LinuX Containers) is an operating system–level virtualization method for running multiple isolated Linux systems (containers) on a single control host. 5 | 6 | LXC combines cgroups and namespace support to provide an isolated environment for applications. 7 | */ 8 | package lxc 9 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package lxc 9 | 10 | const ( 11 | // ErrAddDeviceNodeFailed - adding device to container failed 12 | ErrAddDeviceNodeFailed = lxcError("adding device to container failed") 13 | 14 | // ErrAllocationFailed - allocating memory failed 15 | ErrAllocationFailed = lxcError("allocating memory failed") 16 | 17 | // ErrAlreadyDefined - container already defined 18 | ErrAlreadyDefined = lxcError("container already defined") 19 | 20 | // ErrAlreadyFrozen - container is already frozen 21 | ErrAlreadyFrozen = lxcError("container is already frozen") 22 | 23 | // ErrAlreadyRunning - container is already running 24 | ErrAlreadyRunning = lxcError("container is already running") 25 | 26 | // ErrAttachFailed - attaching to the container failed 27 | ErrAttachFailed = lxcError("attaching to the container failed") 28 | 29 | // ErrAttachInterfaceFailed - attaching specified netdev to the container failed 30 | ErrAttachInterfaceFailed = lxcError("attaching specified netdev to the container failed") 31 | 32 | // ErrBlkioUsage - BlkioUsage for the container failed 33 | ErrBlkioUsage = lxcError("BlkioUsage for the container failed") 34 | 35 | // ErrCheckpointFailed - checkpoint failed 36 | ErrCheckpointFailed = lxcError("checkpoint failed") 37 | 38 | // ErrClearingConfigItemFailed - clearing config item for the container failed 39 | ErrClearingConfigItemFailed = lxcError("clearing config item for the container failed") 40 | 41 | // ErrClearingCgroupItemFailed - clearing cgroup item for the container failed 42 | ErrClearingCgroupItemFailed = lxcError("clearing cgroup item for the container failed") 43 | 44 | // ErrCloneFailed - cloning the container failed 45 | ErrCloneFailed = lxcError("cloning the container failed") 46 | 47 | // ErrCloseAllFdsFailed - setting close_all_fds flag for container failed 48 | ErrCloseAllFdsFailed = lxcError("setting close_all_fds flag for container failed") 49 | 50 | // ErrCreateFailed - creating the container failed 51 | ErrCreateFailed = lxcError("creating the container failed") 52 | 53 | // ErrCreateSnapshotFailed - snapshotting the container failed 54 | ErrCreateSnapshotFailed = lxcError("snapshotting the container failed") 55 | 56 | // ErrDaemonizeFailed - setting daemonize flag for container failed 57 | ErrDaemonizeFailed = lxcError("setting daemonize flag for container failed") 58 | 59 | // ErrDestroyAllSnapshotsFailed - destroying all snapshots failed 60 | ErrDestroyAllSnapshotsFailed = lxcError("destroying all snapshots failed") 61 | 62 | // ErrDestroyFailed - destroying the container failed 63 | ErrDestroyFailed = lxcError("destroying the container failed") 64 | 65 | // ErrDestroySnapshotFailed - destroying the snapshot failed 66 | ErrDestroySnapshotFailed = lxcError("destroying the snapshot failed") 67 | 68 | // ErrDestroyWithAllSnapshotsFailed - destroying the container with all snapshots failed 69 | ErrDestroyWithAllSnapshotsFailed = lxcError("destroying the container with all snapshots failed") 70 | 71 | // ErrDetachInterfaceFailed - detaching specified netdev to the container failed 72 | ErrDetachInterfaceFailed = lxcError("detaching specified netdev to the container failed") 73 | 74 | // ErrExecuteFailed - executing the command in a temporary container failed 75 | ErrExecuteFailed = lxcError("executing the command in a temporary container failed") 76 | 77 | // ErrFreezeFailed - freezing the container failed 78 | ErrFreezeFailed = lxcError("freezing the container failed") 79 | 80 | // ErrInsufficientNumberOfArguments - insufficient number of arguments were supplied 81 | ErrInsufficientNumberOfArguments = lxcError("insufficient number of arguments were supplied") 82 | 83 | // ErrInterfaces - getting interface names for the container failed 84 | ErrInterfaces = lxcError("getting interface names for the container failed") 85 | 86 | // ErrIPAddresses - getting IP addresses of the container failed 87 | ErrIPAddresses = lxcError("getting IP addresses of the container failed") 88 | 89 | // ErrIPAddress - getting IP address on the interface of the container failed 90 | ErrIPAddress = lxcError("getting IP address on the interface of the container failed") 91 | 92 | // ErrIPv4Addresses - getting IPv4 addresses of the container failed 93 | ErrIPv4Addresses = lxcError("getting IPv4 addresses of the container failed") 94 | 95 | // ErrIPv6Addresses - getting IPv6 addresses of the container failed 96 | ErrIPv6Addresses = lxcError("getting IPv6 addresses of the container failed") 97 | 98 | // ErrKMemLimit - your kernel does not support cgroup kernel memory controller 99 | ErrKMemLimit = lxcError("your kernel does not support cgroup kernel memory controller") 100 | 101 | // ErrLoadConfigFailed - loading config file for the container failed 102 | ErrLoadConfigFailed = lxcError("loading config file for the container failed") 103 | 104 | // ErrMemLimit - your kernel does not support cgroup memory controller 105 | ErrMemLimit = lxcError("your kernel does not support cgroup memory controller") 106 | 107 | // ErrMemorySwapLimit - your kernel does not support cgroup swap controller 108 | ErrMemorySwapLimit = lxcError("your kernel does not support cgroup swap controller") 109 | 110 | // ErrMethodNotAllowed - the requested method is not currently supported with unprivileged containers 111 | ErrMethodNotAllowed = lxcError("the requested method is not currently supported with unprivileged containers") 112 | 113 | // ErrNewFailed - allocating the container failed 114 | ErrNewFailed = lxcError("allocating the container failed") 115 | 116 | // ErrNoSnapshot - container has no snapshot 117 | ErrNoSnapshot = lxcError("container has no snapshot") 118 | 119 | // ErrNotDefined - container is not defined 120 | ErrNotDefined = lxcError("container is not defined") 121 | 122 | // ErrNotFrozen - container is not frozen 123 | ErrNotFrozen = lxcError("container is not frozen") 124 | 125 | // ErrNotRunning - container is not running 126 | ErrNotRunning = lxcError("container is not running") 127 | 128 | // ErrNotSupported - method is not supported by this LXC version 129 | ErrNotSupported = lxcError("method is not supported by this LXC version") 130 | 131 | // ErrRebootFailed - rebooting the container failed 132 | ErrRebootFailed = lxcError("rebooting the container failed") 133 | 134 | // ErrRemoveDeviceNodeFailed - removing device from container failed 135 | ErrRemoveDeviceNodeFailed = lxcError("removing device from container failed") 136 | 137 | // ErrRenameFailed - renaming the container failed 138 | ErrRenameFailed = lxcError("renaming the container failed") 139 | 140 | // ErrRestoreFailed - restore failed 141 | ErrRestoreFailed = lxcError("restore failed") 142 | 143 | // ErrRestoreSnapshotFailed - restoring the container failed 144 | ErrRestoreSnapshotFailed = lxcError("restoring the container failed") 145 | 146 | // ErrSaveConfigFailed - saving config file for the container failed 147 | ErrSaveConfigFailed = lxcError("saving config file for the container failed") 148 | 149 | // ErrSettingCgroupItemFailed - setting cgroup item for the container failed 150 | ErrSettingCgroupItemFailed = lxcError("setting cgroup item for the container failed") 151 | 152 | // ErrSettingConfigItemFailed - setting config item for the container failed 153 | ErrSettingConfigItemFailed = lxcError("setting config item for the container failed") 154 | 155 | // ErrSettingConfigPathFailed - setting config file for the container failed 156 | ErrSettingConfigPathFailed = lxcError("setting config file for the container failed") 157 | 158 | // ErrSettingKMemoryLimitFailed - setting kernel memory limit for the container failed 159 | ErrSettingKMemoryLimitFailed = lxcError("setting kernel memory limit for the container failed") 160 | 161 | // ErrSettingMemoryLimitFailed - setting memory limit for the container failed 162 | ErrSettingMemoryLimitFailed = lxcError("setting memory limit for the container failed") 163 | 164 | // ErrSettingMemorySwapLimitFailed - setting memory+swap limit for the container failed 165 | ErrSettingMemorySwapLimitFailed = lxcError("setting memory+swap limit for the container failed") 166 | 167 | // ErrSettingSoftMemoryLimitFailed - setting soft memory limit for the container failed 168 | ErrSettingSoftMemoryLimitFailed = lxcError("setting soft memory limit for the container failed") 169 | 170 | // ErrShutdownFailed - shutting down the container failed 171 | ErrShutdownFailed = lxcError("shutting down the container failed") 172 | 173 | // ErrSoftMemLimit - your kernel does not support cgroup memory controller 174 | ErrSoftMemLimit = lxcError("your kernel does not support cgroup memory controller") 175 | 176 | // ErrStartFailed - starting the container failed 177 | ErrStartFailed = lxcError("starting the container failed") 178 | 179 | // ErrStopFailed - stopping the container failed 180 | ErrStopFailed = lxcError("stopping the container failed") 181 | 182 | // ErrTemplateNotAllowed - unprivileged users only allowed to use "download" template 183 | ErrTemplateNotAllowed = lxcError("unprivileged users only allowed to use \"download\" template") 184 | 185 | // ErrUnfreezeFailed - unfreezing the container failed 186 | ErrUnfreezeFailed = lxcError("unfreezing the container failed") 187 | 188 | // ErrUnknownBackendStore - unknown backend type 189 | ErrUnknownBackendStore = lxcError("unknown backend type") 190 | 191 | // ErrReleaseFailed - releasing the container failed 192 | ErrReleaseFailed = lxcError("releasing the container failed") 193 | ) 194 | 195 | type lxcError string 196 | 197 | func (e lxcError) Error() string { 198 | return string(e) 199 | } 200 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | NO_COLOR=\033[0m 2 | OK_COLOR=\033[0;32m 3 | 4 | ALL_GO_FILES = $(wildcard */*.go) 5 | ALL_BIN_FILES = $(patsubst %.go,%,$(ALL_GO_FILES)) 6 | 7 | all: $(ALL_GO_FILES) 8 | 9 | define PROGRAM_template 10 | $(1): vet 11 | @echo "$(OK_COLOR)==> Building $(1) $(NO_COLOR)" 12 | @cd $(dir $1); go build 13 | endef 14 | 15 | $(foreach prog,$(ALL_GO_FILES),$(eval $(call PROGRAM_template,$(prog)))) 16 | 17 | clean: 18 | @$(foreach file,$(ALL_BIN_FILES),rm -f $(file);) 19 | 20 | format: 21 | @echo "$(OK_COLOR)==> Formatting the code $(NO_COLOR)" 22 | @$(foreach file,$(ALL_GO_FILES),gofmt -s -w $(file);) 23 | @$(foreach file,$(ALL_GO_FILES),goimports -w $(file);) 24 | 25 | vet: 26 | @echo "$(OK_COLOR)==> Running go vet $(NO_COLOR)" 27 | @$(foreach file,$(ALL_GO_FILES),go vet -all $(file);) 28 | 29 | lint: 30 | @echo "$(OK_COLOR)==> Running golint $(NO_COLOR)" 31 | @$(foreach file,$(ALL_GO_FILES),golint $(file);) 32 | 33 | .PHONY: all clean format vet lint 34 | -------------------------------------------------------------------------------- /examples/attach/attach.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | clear bool 21 | x86 bool 22 | regular bool 23 | ) 24 | 25 | func init() { 26 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 27 | flag.StringVar(&name, "name", "rubik", "Name of the original container") 28 | flag.BoolVar(&clear, "clear", false, "Attach with clear environment") 29 | flag.BoolVar(&x86, "x86", false, "Attach using x86 personality") 30 | flag.BoolVar(®ular, "regular", false, "Attach using a regular user") 31 | flag.Parse() 32 | } 33 | 34 | func main() { 35 | c, err := lxc.NewContainer(name, lxcpath) 36 | if err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } 39 | defer c.Release() 40 | 41 | options := lxc.DefaultAttachOptions 42 | options.ClearEnv = false 43 | if clear { 44 | options.ClearEnv = true 45 | } 46 | if x86 { 47 | options.Arch = lxc.X86 48 | } 49 | if regular { 50 | options.UID = 1000 51 | options.GID = 1000 52 | } 53 | log.Printf("AttachShell\n") 54 | err = c.AttachShell(options) 55 | if err != nil { 56 | log.Fatalf("ERROR: %s\n", err.Error()) 57 | } 58 | 59 | log.Printf("RunCommand\n") 60 | _, err = c.RunCommand([]string{"id"}, options) 61 | if err != nil { 62 | log.Fatalf("ERROR: %s\n", err.Error()) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /examples/attach_with_pipes/attach_with_pipes.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "io" 13 | "log" 14 | "os" 15 | "sync" 16 | 17 | "github.com/lxc/go-lxc" 18 | ) 19 | 20 | var ( 21 | lxcpath string 22 | name string 23 | clear bool 24 | x86 bool 25 | regular bool 26 | wg sync.WaitGroup 27 | ) 28 | 29 | func init() { 30 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 31 | flag.StringVar(&name, "name", "rubik", "Name of the original container") 32 | flag.BoolVar(&clear, "clear", false, "Attach with clear environment") 33 | flag.BoolVar(&x86, "x86", false, "Attach using x86 personality") 34 | flag.BoolVar(®ular, "regular", false, "Attach using a regular user") 35 | flag.Parse() 36 | } 37 | 38 | func main() { 39 | c, err := lxc.NewContainer(name, lxcpath) 40 | if err != nil { 41 | log.Fatalf("ERROR: %s\n", err.Error()) 42 | } 43 | defer c.Release() 44 | 45 | stdoutReader, stdoutWriter, err := os.Pipe() 46 | if err != nil { 47 | log.Fatalf("ERROR: %s\n", err.Error()) 48 | } 49 | stderrReader, stderrWriter, err := os.Pipe() 50 | if err != nil { 51 | log.Fatalf("ERROR: %s\n", err.Error()) 52 | } 53 | 54 | wg.Add(1) 55 | go func() { 56 | defer wg.Done() 57 | _, err = io.Copy(os.Stdout, stdoutReader) 58 | if err != nil { 59 | log.Fatalf("ERROR: %s\n", err.Error()) 60 | } 61 | }() 62 | wg.Add(1) 63 | go func() { 64 | defer wg.Done() 65 | _, err = io.Copy(os.Stderr, stderrReader) 66 | if err != nil { 67 | log.Fatalf("ERROR: %s\n", err.Error()) 68 | } 69 | }() 70 | 71 | options := lxc.DefaultAttachOptions 72 | 73 | options.StdinFd = os.Stdin.Fd() 74 | options.StdoutFd = stdoutWriter.Fd() 75 | options.StderrFd = stderrWriter.Fd() 76 | 77 | options.ClearEnv = false 78 | if clear { 79 | options.ClearEnv = true 80 | } 81 | 82 | if x86 { 83 | options.Arch = lxc.X86 84 | } 85 | if regular { 86 | options.UID = 1000 87 | options.GID = 1000 88 | } 89 | log.Printf("AttachShell\n") 90 | if err := c.AttachShell(options); err != nil { 91 | log.Fatalf("ERROR: %s\n", err.Error()) 92 | } 93 | 94 | log.Printf("RunCommand\n") 95 | _, err = c.RunCommand([]string{"uname", "-a"}, options) 96 | if err != nil { 97 | log.Fatalf("ERROR: %s\n", err.Error()) 98 | } 99 | 100 | if err = stdoutWriter.Close(); err != nil { 101 | log.Fatalf("ERROR: %s\n", err.Error()) 102 | } 103 | if err = stderrWriter.Close(); err != nil { 104 | log.Fatalf("ERROR: %s\n", err.Error()) 105 | } 106 | 107 | wg.Wait() 108 | } 109 | -------------------------------------------------------------------------------- /examples/checkpoint/checkpoint.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | directory string 20 | name string 21 | stop bool 22 | verbose bool 23 | ) 24 | 25 | func init() { 26 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 27 | flag.StringVar(&name, "name", "rubik", "Name of the container") 28 | flag.StringVar(&directory, "directory", "/tmp/rubik", "directory to save the checkpoint in") 29 | flag.BoolVar(&verbose, "verbose", false, "Verbose output") 30 | flag.BoolVar(&stop, "stop", false, "Stop the container after checkpointing.") 31 | flag.Parse() 32 | } 33 | 34 | func main() { 35 | c, err := lxc.NewContainer(name, lxcpath) 36 | if err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } 39 | defer c.Release() 40 | 41 | if verbose { 42 | c.SetVerbosity(lxc.Verbose) 43 | } 44 | 45 | options := lxc.CheckpointOptions{ 46 | Directory: directory, 47 | Verbose: verbose, 48 | Stop: stop, 49 | } 50 | 51 | if err := c.Checkpoint(options); err != nil { 52 | log.Printf("ERROR: %s\n", err.Error()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/clone/clone.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | backend lxc.BackendStore 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 25 | flag.StringVar(&name, "name", "rubik", "Name of the original container") 26 | flag.Var(&backend, "backend", "Backend type to use, possible values are [dir, zfs, btrfs, lvm, aufs, overlayfs, loopback, best]") 27 | flag.Parse() 28 | } 29 | 30 | func main() { 31 | c, err := lxc.NewContainer(name, lxcpath) 32 | if err != nil { 33 | log.Fatalf("ERROR: %s\n", err.Error()) 34 | } 35 | defer c.Release() 36 | 37 | if backend == 0 { 38 | log.Fatalf("ERROR: %s\n", lxc.ErrUnknownBackendStore) 39 | } 40 | 41 | log.Printf("Cloning the container using %s backend...\n", backend) 42 | err = c.Clone(name+"_"+backend.String(), lxc.CloneOptions{ 43 | Backend: backend, 44 | }) 45 | if err != nil { 46 | log.Fatalf("ERROR: %s\n", err.Error()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/concurrent_create/concurrent_create.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "runtime" 14 | "strconv" 15 | "sync" 16 | 17 | "github.com/lxc/go-lxc" 18 | ) 19 | 20 | var ( 21 | lxcpath string 22 | count int 23 | ) 24 | 25 | func init() { 26 | runtime.GOMAXPROCS(runtime.NumCPU()) 27 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 28 | flag.IntVar(&count, "count", 10, "Number of containers") 29 | flag.Parse() 30 | } 31 | 32 | func main() { 33 | var wg sync.WaitGroup 34 | 35 | options := lxc.BusyboxTemplateOptions 36 | for i := 0; i < count; i++ { 37 | wg.Add(1) 38 | go func(i int) { 39 | c, err := lxc.NewContainer(strconv.Itoa(i), lxcpath) 40 | if err != nil { 41 | log.Fatalf("ERROR: %s\n", err.Error()) 42 | } 43 | defer c.Release() 44 | 45 | log.Printf("Creating the container (%d)...\n", i) 46 | if err := c.Create(options); err != nil { 47 | log.Fatalf("ERROR: %s\n", err.Error()) 48 | } 49 | wg.Done() 50 | }(i) 51 | } 52 | wg.Wait() 53 | } 54 | -------------------------------------------------------------------------------- /examples/concurrent_destroy/concurrent_destroy.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "runtime" 14 | "strconv" 15 | "sync" 16 | 17 | "github.com/lxc/go-lxc" 18 | ) 19 | 20 | var ( 21 | lxcpath string 22 | count int 23 | ) 24 | 25 | func init() { 26 | runtime.GOMAXPROCS(runtime.NumCPU()) 27 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 28 | flag.IntVar(&count, "count", 10, "Number of containers") 29 | flag.Parse() 30 | } 31 | 32 | func main() { 33 | var wg sync.WaitGroup 34 | 35 | for i := 0; i < count; i++ { 36 | wg.Add(1) 37 | go func(i int) { 38 | c, err := lxc.NewContainer(strconv.Itoa(i), lxcpath) 39 | if err != nil { 40 | log.Fatalf("ERROR: %s\n", err.Error()) 41 | } 42 | defer c.Release() 43 | 44 | log.Printf("Destroying the container (%d)...\n", i) 45 | if err := c.Destroy(); err != nil { 46 | log.Fatalf("ERROR: %s\n", err.Error()) 47 | } 48 | wg.Done() 49 | }(i) 50 | } 51 | wg.Wait() 52 | } 53 | -------------------------------------------------------------------------------- /examples/concurrent_shutdown/concurrent_shutdown.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "runtime" 14 | "strconv" 15 | "sync" 16 | "time" 17 | 18 | "github.com/lxc/go-lxc" 19 | ) 20 | 21 | var ( 22 | lxcpath string 23 | count int 24 | ) 25 | 26 | func init() { 27 | runtime.GOMAXPROCS(runtime.NumCPU()) 28 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 29 | flag.IntVar(&count, "count", 10, "Number of containers") 30 | flag.Parse() 31 | } 32 | 33 | func main() { 34 | var wg sync.WaitGroup 35 | 36 | for i := 0; i < count; i++ { 37 | wg.Add(1) 38 | go func(i int) { 39 | c, err := lxc.NewContainer(strconv.Itoa(i), lxcpath) 40 | if err != nil { 41 | log.Fatalf("ERROR: %s\n", err.Error()) 42 | } 43 | defer c.Release() 44 | 45 | log.Printf("Shutting down the container (%d)...\n", i) 46 | if err := c.Shutdown(30 * time.Second); err != nil { 47 | if err = c.Stop(); err != nil { 48 | log.Fatalf("ERROR: %s\n", err.Error()) 49 | } 50 | } 51 | wg.Done() 52 | }(i) 53 | } 54 | wg.Wait() 55 | } 56 | -------------------------------------------------------------------------------- /examples/concurrent_start/concurrent_start.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "runtime" 14 | "strconv" 15 | "sync" 16 | 17 | "github.com/lxc/go-lxc" 18 | ) 19 | 20 | var ( 21 | lxcpath string 22 | count int 23 | ) 24 | 25 | func init() { 26 | runtime.GOMAXPROCS(runtime.NumCPU()) 27 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 28 | flag.IntVar(&count, "count", 10, "Number of containers") 29 | flag.Parse() 30 | } 31 | 32 | func main() { 33 | var wg sync.WaitGroup 34 | 35 | for i := 0; i < count; i++ { 36 | wg.Add(1) 37 | go func(i int) { 38 | c, err := lxc.NewContainer(strconv.Itoa(i), lxcpath) 39 | if err != nil { 40 | log.Fatalf("ERROR: %s\n", err.Error()) 41 | } 42 | defer c.Release() 43 | 44 | log.Printf("Starting the container (%d)...\n", i) 45 | if err := c.Start(); err != nil { 46 | log.Fatalf("ERROR: %s\n", err.Error()) 47 | } 48 | wg.Done() 49 | }(i) 50 | } 51 | wg.Wait() 52 | } 53 | -------------------------------------------------------------------------------- /examples/concurrent_stop/concurrent_stop.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "runtime" 14 | "strconv" 15 | "sync" 16 | 17 | "github.com/lxc/go-lxc" 18 | ) 19 | 20 | var ( 21 | lxcpath string 22 | count int 23 | ) 24 | 25 | func init() { 26 | runtime.GOMAXPROCS(runtime.NumCPU()) 27 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 28 | flag.IntVar(&count, "count", 10, "Number of containers") 29 | flag.Parse() 30 | } 31 | 32 | func main() { 33 | var wg sync.WaitGroup 34 | 35 | for i := 0; i < count; i++ { 36 | wg.Add(1) 37 | go func(i int) { 38 | c, err := lxc.NewContainer(strconv.Itoa(i), lxcpath) 39 | if err != nil { 40 | log.Fatalf("ERROR: %s\n", err.Error()) 41 | } 42 | defer c.Release() 43 | 44 | log.Printf("Stoping the container (%d)...\n", i) 45 | if err := c.Stop(); err != nil { 46 | log.Fatalf("ERROR: %s\n", err.Error()) 47 | } 48 | wg.Done() 49 | }(i) 50 | } 51 | wg.Wait() 52 | } 53 | -------------------------------------------------------------------------------- /examples/concurrent_stress/concurrent_stress.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "io/ioutil" 13 | "log" 14 | "runtime" 15 | "strconv" 16 | "sync" 17 | 18 | "github.com/lxc/go-lxc" 19 | ) 20 | 21 | var ( 22 | lxcpath string 23 | iteration int 24 | threads int 25 | template string 26 | quiet bool 27 | startstop bool 28 | createdestroy bool 29 | ) 30 | 31 | func init() { 32 | runtime.GOMAXPROCS(runtime.NumCPU()) 33 | 34 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 35 | flag.StringVar(&template, "template", "busybox", "Template to use") 36 | flag.IntVar(&threads, "threads", 10, "Number of operations to run concurrently") 37 | flag.IntVar(&iteration, "iteration", 1, "Number times to run the test") 38 | flag.BoolVar(&quiet, "quiet", false, "Don't produce any output") 39 | flag.BoolVar(&startstop, "startstop", false, "Flag to execute Start and Stop") 40 | flag.BoolVar(&createdestroy, "createdestroy", false, "Flag to execute Create and Destroy") 41 | flag.Parse() 42 | } 43 | 44 | func main() { 45 | if quiet { 46 | log.SetOutput(ioutil.Discard) 47 | } 48 | log.Printf("Using %d GOMAXPROCS\n", runtime.NumCPU()) 49 | 50 | var wg sync.WaitGroup 51 | 52 | options := lxc.BusyboxTemplateOptions 53 | options.Template = template 54 | 55 | for i := 0; i < iteration; i++ { 56 | log.Printf("-- ITERATION %d --\n", i+1) 57 | for _, mode := range []string{"CREATE", "START", "STOP", "DESTROY"} { 58 | log.Printf("\t-- %s --\n", mode) 59 | for j := 0; j < threads; j++ { 60 | wg.Add(1) 61 | go func(i int, mode string) { 62 | c, err := lxc.NewContainer(strconv.Itoa(i), lxcpath) 63 | if err != nil { 64 | log.Fatalf("ERROR: %s\n", err.Error()) 65 | } 66 | defer c.Release() 67 | 68 | if mode == "CREATE" && startstop == false { 69 | log.Printf("\t\tCreating the container (%d)...\n", i) 70 | if err := c.Create(options); err != nil { 71 | log.Fatalf("\t\t\tERROR: %s\n", err.Error()) 72 | } 73 | } else if mode == "START" && createdestroy == false { 74 | log.Printf("\t\tStarting the container (%d)...\n", i) 75 | if err := c.Start(); err != nil { 76 | log.Fatalf("\t\t\tERROR: %s\n", err.Error()) 77 | } 78 | } else if mode == "STOP" && createdestroy == false { 79 | log.Printf("\t\tStoping the container (%d)...\n", i) 80 | if err := c.Stop(); err != nil { 81 | log.Fatalf("\t\t\tERROR: %s\n", err.Error()) 82 | } 83 | } else if mode == "DESTROY" && startstop == false { 84 | log.Printf("\t\tDestroying the container (%d)...\n", i) 85 | if err := c.Destroy(); err != nil { 86 | log.Fatalf("\t\t\tERROR: %s\n", err.Error()) 87 | } 88 | } 89 | wg.Done() 90 | }(j, mode) 91 | } 92 | wg.Wait() 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /examples/config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | hostname string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 25 | flag.StringVar(&name, "name", "rubik", "Name of the container") 26 | flag.StringVar(&hostname, "hostname", "rubik-host1", "Hostname of the container") 27 | flag.Parse() 28 | } 29 | 30 | func main() { 31 | c, err := lxc.NewContainer(name, lxcpath) 32 | if err != nil { 33 | log.Fatalf("ERROR: %s\n", err.Error()) 34 | } 35 | defer c.Release() 36 | 37 | //setting hostname 38 | err = c.SetConfigItem("lxc.utsname", hostname) 39 | if err != nil { 40 | log.Fatalf("ERROR: %s\n", err.Error()) 41 | } 42 | 43 | // fetching rootfs location 44 | rootfs := c.ConfigItem("lxc.rootfs")[0] 45 | log.Printf("Root FS: %s\n", rootfs) 46 | 47 | } 48 | -------------------------------------------------------------------------------- /examples/console/console.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | log.Printf("Attaching to container's console...\n") 36 | if err := c.Console(lxc.DefaultConsoleOptions); err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/create/create.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | template string 20 | distro string 21 | release string 22 | arch string 23 | name string 24 | verbose bool 25 | flush bool 26 | validation bool 27 | fssize string 28 | bdevtype string 29 | ) 30 | 31 | func init() { 32 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 33 | flag.StringVar(&template, "template", "download", "Template to use") 34 | flag.StringVar(&distro, "distro", "ubuntu", "Template to use") 35 | flag.StringVar(&release, "release", "trusty", "Template to use") 36 | flag.StringVar(&arch, "arch", "amd64", "Template to use") 37 | flag.StringVar(&name, "name", "rubik", "Name of the container") 38 | flag.BoolVar(&verbose, "verbose", false, "Verbose output") 39 | flag.BoolVar(&flush, "flush", false, "Flush the cache") 40 | flag.BoolVar(&validation, "validation", false, "GPG validation") 41 | 42 | flag.StringVar(&bdevtype, "bdev", "dir", "backing store type") 43 | flag.StringVar(&fssize, "fssize", "", "backing store size") 44 | // TODO support more flags for zfs, lvm, or rbd 45 | 46 | flag.Parse() 47 | } 48 | 49 | func main() { 50 | c, err := lxc.NewContainer(name, lxcpath) 51 | if err != nil { 52 | log.Fatalf("ERROR: %s\n", err.Error()) 53 | } 54 | defer c.Release() 55 | 56 | log.Printf("Creating container...\n") 57 | if verbose { 58 | c.SetVerbosity(lxc.Verbose) 59 | } 60 | 61 | var backend lxc.BackendStore 62 | if err := (&backend).Set(bdevtype); err != nil { 63 | log.Fatalf("ERROR: %s\n", err.Error()) 64 | } 65 | 66 | var bdevSize lxc.ByteSize 67 | if fssize != "" { 68 | var err error 69 | bdevSize, err = lxc.ParseBytes(fssize) 70 | if err != nil { 71 | log.Fatalf("ERROR: %s\n", err.Error()) 72 | } 73 | } 74 | 75 | options := lxc.TemplateOptions{ 76 | Template: template, 77 | Distro: distro, 78 | Release: release, 79 | Arch: arch, 80 | FlushCache: flush, 81 | DisableGPGValidation: validation, 82 | Backend: backend, 83 | BackendSpecs: &lxc.BackendStoreSpecs{ 84 | FSSize: uint64(bdevSize), 85 | }, 86 | } 87 | 88 | c.SetLogFile("log") 89 | c.SetLogLevel(lxc.DEBUG) 90 | 91 | if err := c.Create(options); err != nil { 92 | log.Printf("ERROR: %s\n", err.Error()) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/create_snapshot/create_snapshot.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | log.Printf("Snapshoting the container...\n") 36 | if _, err := c.CreateSnapshot(); err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/destroy/destroy.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | log.Printf("Destroying container...\n") 36 | if err := c.Destroy(); err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/destroy_snapshots/destroy_snapshots.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "log" 12 | 13 | "github.com/lxc/go-lxc" 14 | ) 15 | 16 | func main() { 17 | c := lxc.Containers() 18 | for i := range c { 19 | log.Printf("%s\n", c[i].Name()) 20 | l, err := c[i].Snapshots() 21 | if err != nil { 22 | log.Fatalf("ERROR: %s\n", err.Error()) 23 | } 24 | 25 | for _, s := range l { 26 | log.Printf("Destroying Snaphot: %s\n", s.Name) 27 | if err := c[i].DestroySnapshot(s); err != nil { 28 | log.Fatalf("ERROR: %s\n", err.Error()) 29 | } 30 | } 31 | c[i].Release() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/device_add_remove/device_add_remove.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "time" 14 | 15 | "github.com/lxc/go-lxc" 16 | ) 17 | 18 | var ( 19 | lxcpath string 20 | name string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 25 | flag.StringVar(&name, "name", "rubik", "Name of the container") 26 | flag.Parse() 27 | } 28 | 29 | func main() { 30 | c, err := lxc.NewContainer(name, lxcpath) 31 | if err != nil { 32 | log.Fatalf("ERROR: %s\n", err.Error()) 33 | } 34 | defer c.Release() 35 | 36 | if err := c.AddDeviceNode("/dev/network_latency"); err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } 39 | 40 | time.Sleep(10000 * time.Millisecond) 41 | 42 | if err := c.RemoveDeviceNode("/dev/network_latency"); err != nil { 43 | log.Fatalf("ERROR: %s\n", err.Error()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/execute/execute.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | c.LoadConfigFile(lxc.DefaultConfigPath()) 36 | if output, err := c.Execute("uname", "-a"); err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } else { 39 | log.Printf("%s", output) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/freeze/freeze.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "time" 14 | 15 | "github.com/lxc/go-lxc" 16 | ) 17 | 18 | var ( 19 | lxcpath string 20 | name string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 25 | flag.StringVar(&name, "name", "rubik", "Name of the container") 26 | flag.Parse() 27 | } 28 | 29 | func main() { 30 | c, err := lxc.NewContainer(name, lxcpath) 31 | if err != nil { 32 | log.Fatalf("ERROR: %s\n", err.Error()) 33 | } 34 | defer c.Release() 35 | 36 | log.Printf("Freezing the container...\n") 37 | if err := c.Freeze(); err != nil { 38 | log.Fatalf("ERROR: %s\n", err.Error()) 39 | } 40 | c.Wait(lxc.FROZEN, 10*time.Second) 41 | } 42 | -------------------------------------------------------------------------------- /examples/interfaces/interfaces.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the original container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | log.Printf("Interfaces\n") 36 | if interfaces, err := c.Interfaces(); err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } else { 39 | for i, v := range interfaces { 40 | log.Printf("%d) %s\n", i, v) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/ipaddress/ipaddress.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the original container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | log.Printf("IPAddress(\"lo\")\n") 36 | if addresses, err := c.IPAddress("lo"); err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } else { 39 | for i, v := range addresses { 40 | log.Printf("%d) %s\n", i, v) 41 | } 42 | } 43 | 44 | log.Printf("IPAddresses()\n") 45 | if addresses, err := c.IPAddresses(); err != nil { 46 | log.Fatalf("ERROR: %s\n", err.Error()) 47 | } else { 48 | for i, v := range addresses { 49 | log.Printf("%d) %s\n", i, v) 50 | } 51 | } 52 | 53 | log.Printf("IPv4Addresses()\n") 54 | if addresses, err := c.IPv4Addresses(); err != nil { 55 | log.Fatalf("ERROR: %s\n", err.Error()) 56 | } else { 57 | for i, v := range addresses { 58 | log.Printf("%d) %s\n", i, v) 59 | } 60 | } 61 | 62 | log.Printf("IPv6Addresses()\n") 63 | if addresses, err := c.IPv6Addresses(); err != nil { 64 | log.Fatalf("ERROR: %s\n", err.Error()) 65 | } else { 66 | for i, v := range addresses { 67 | log.Printf("%d) %s\n", i, v) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/limit/limit.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | memLimit, err := c.MemoryLimit() 36 | if err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } 39 | memorySwapLimit, err := c.MemorySwapLimit() 40 | if err != nil { 41 | log.Fatalf("ERROR: %s\n", err.Error()) 42 | } 43 | 44 | if err := c.SetMemoryLimit(memLimit / 4); err != nil { 45 | log.Fatalf("ERROR: %s\n", err.Error()) 46 | } 47 | if err := c.SetMemorySwapLimit(memorySwapLimit / 4); err != nil { 48 | log.Fatalf("ERROR: %s\n", err.Error()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/list/list.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | ) 20 | 21 | func init() { 22 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 23 | flag.Parse() 24 | } 25 | 26 | func main() { 27 | log.Printf("Defined containers:\n") 28 | c := lxc.DefinedContainers(lxcpath) 29 | for i := range c { 30 | log.Printf("%s (%s)\n", c[i].Name(), c[i].State()) 31 | c[i].Release() 32 | } 33 | 34 | log.Println() 35 | 36 | log.Printf("Active containers:\n") 37 | c = lxc.ActiveContainers(lxcpath) 38 | for i := range c { 39 | log.Printf("%s (%s)\n", c[i].Name(), c[i].State()) 40 | c[i].Release() 41 | } 42 | 43 | log.Println() 44 | 45 | log.Printf("Active and Defined containers:\n") 46 | c = lxc.ActiveContainers(lxcpath) 47 | for i := range c { 48 | log.Printf("%s (%s)\n", c[i].Name(), c[i].State()) 49 | c[i].Release() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/list_keys/list_keys.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | for _, k := range c.ConfigKeys() { 36 | log.Printf("%s -> %s", k, c.ConfigItem(k)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/list_snapshots/list_snapshots.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "log" 12 | 13 | "github.com/lxc/go-lxc" 14 | ) 15 | 16 | func main() { 17 | c := lxc.Containers() 18 | for i := range c { 19 | log.Printf("%s\n", c[i].Name()) 20 | l, err := c[i].Snapshots() 21 | if err != nil { 22 | log.Printf("ERROR: %s\n", err.Error()) 23 | } 24 | 25 | for _, s := range l { 26 | log.Printf("Name: %s\n", s.Name) 27 | log.Printf("Comment path: %s\n", s.CommentPath) 28 | log.Printf("Timestamp: %s\n", s.Timestamp) 29 | log.Printf("LXC path: %s\n", s.Path) 30 | log.Println() 31 | } 32 | c[i].Release() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/reboot/reboot.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | log.Printf("Rebooting the container...\n") 36 | if err := c.Reboot(); err != nil { 37 | log.Fatalf("ERROR: %s\n", err.Error()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/rename/rename.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | newname string 20 | name string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 25 | flag.StringVar(&newname, "newname", "kibur", "New name of the container") 26 | flag.StringVar(&name, "name", "rubik", "Name of the container") 27 | flag.Parse() 28 | } 29 | 30 | func main() { 31 | c, err := lxc.NewContainer(name, lxcpath) 32 | if err != nil { 33 | log.Fatalf("ERROR: %s\n", err.Error()) 34 | } 35 | defer c.Release() 36 | 37 | log.Printf("Renaming container to %s...\n", newname) 38 | if err := c.Rename(newname); err != nil { 39 | log.Printf("ERROR: %s\n", err.Error()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/restore_snapshot/restore_snapshot.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | log.Printf("Restoring the container...\n") 36 | snapshot := lxc.Snapshot{Name: "snap0"} 37 | 38 | if err := c.RestoreSnapshot(snapshot, "rubik-restore"); err != nil { 39 | log.Fatalf("ERROR: %s\n", err.Error()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/shutdown/shutdown.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "time" 14 | 15 | "github.com/lxc/go-lxc" 16 | ) 17 | 18 | var ( 19 | lxcpath string 20 | name string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 25 | flag.StringVar(&name, "name", "rubik", "Name of the container") 26 | flag.Parse() 27 | } 28 | 29 | func main() { 30 | c, err := lxc.NewContainer(name, lxcpath) 31 | if err != nil { 32 | log.Fatalf("ERROR: %s\n", err.Error()) 33 | } 34 | defer c.Release() 35 | 36 | log.Printf("Shutting down the container...\n") 37 | if err := c.Shutdown(30 * time.Second); err != nil { 38 | if err = c.Stop(); err != nil { 39 | log.Fatalf("ERROR: %s\n", err.Error()) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/start/start.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "time" 14 | 15 | "github.com/lxc/go-lxc" 16 | ) 17 | 18 | var ( 19 | lxcpath string 20 | name string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 25 | flag.StringVar(&name, "name", "rubik", "Name of the container") 26 | flag.Parse() 27 | } 28 | 29 | func main() { 30 | c, err := lxc.NewContainer(name, lxcpath) 31 | if err != nil { 32 | log.Fatalf("ERROR: %s\n", err.Error()) 33 | } 34 | defer c.Release() 35 | 36 | c.SetLogFile("/tmp/" + name + ".log") 37 | c.SetLogLevel(lxc.TRACE) 38 | 39 | log.Printf("Starting the container...\n") 40 | if err := c.Start(); err != nil { 41 | log.Fatalf("ERROR: %s\n", err.Error()) 42 | } 43 | 44 | log.Printf("Waiting container to startup networking...\n") 45 | if _, err := c.WaitIPAddresses(5 * time.Second); err != nil { 46 | log.Fatalf("ERROR: %s\n", err.Error()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/stats/stats.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | // mem 36 | memUsed, err := c.MemoryUsage() 37 | if err != nil { 38 | log.Fatalf("ERROR: %s\n", err.Error()) 39 | } else { 40 | log.Printf("MemoryUsage: %s\n", memUsed) 41 | } 42 | 43 | memLimit, err := c.MemoryLimit() 44 | if err != nil { 45 | log.Fatalf("ERROR: %s\n", err.Error()) 46 | } else { 47 | log.Printf("MemoryLimit: %s\n", memLimit) 48 | } 49 | 50 | // kmem 51 | kmemUsed, err := c.KernelMemoryUsage() 52 | if err != nil { 53 | log.Fatalf("ERROR: %s\n", err.Error()) 54 | } else { 55 | log.Printf("KernelMemoryUsage: %s\n", kmemUsed) 56 | } 57 | 58 | kmemLimit, err := c.KernelMemoryLimit() 59 | if err != nil { 60 | log.Fatalf("ERROR: %s\n", err.Error()) 61 | } else { 62 | log.Printf("KernelMemoryLimit: %s\n", kmemLimit) 63 | } 64 | 65 | // swap 66 | swapUsed, err := c.MemorySwapUsage() 67 | if err != nil { 68 | log.Fatalf("ERROR: %s\n", err.Error()) 69 | } else { 70 | log.Printf("MemorySwapUsage: %s\n", swapUsed) 71 | } 72 | 73 | swapLimit, err := c.MemorySwapLimit() 74 | if err != nil { 75 | log.Fatalf("ERROR: %s\n", err.Error()) 76 | } else { 77 | log.Printf("MemorySwapLimit: %s\n", swapLimit) 78 | } 79 | 80 | // blkio 81 | blkioUsage, err := c.BlkioUsage() 82 | if err != nil { 83 | log.Fatalf("ERROR: %s\n", err.Error()) 84 | } else { 85 | log.Printf("BlkioUsage: %s\n", blkioUsage) 86 | } 87 | 88 | cpuTime, err := c.CPUTime() 89 | if err != nil { 90 | log.Fatalf("ERROR: %s\n", err.Error()) 91 | } 92 | log.Printf("cpuacct.usage: %s\n", cpuTime) 93 | 94 | cpuTimePerCPU, err := c.CPUTimePerCPU() 95 | if err != nil { 96 | log.Fatalf("ERROR: %s\n", err.Error()) 97 | } 98 | log.Printf("cpuacct.usageerrpercpu: %v\n", cpuTimePerCPU) 99 | 100 | cpuStats, err := c.CPUStats() 101 | if err != nil { 102 | log.Fatalf("ERROR: %s\n", err.Error()) 103 | } 104 | log.Printf("cpuacct.stat: %v\n", cpuStats) 105 | 106 | interfaceStats, err := c.InterfaceStats() 107 | if err != nil { 108 | log.Fatalf("ERROR: %s\n", err.Error()) 109 | } 110 | log.Printf("InterfaceStats: %v\n", interfaceStats) 111 | } 112 | -------------------------------------------------------------------------------- /examples/stop/stop.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | 14 | "github.com/lxc/go-lxc" 15 | ) 16 | 17 | var ( 18 | lxcpath string 19 | name string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 24 | flag.StringVar(&name, "name", "rubik", "Name of the container") 25 | flag.Parse() 26 | } 27 | 28 | func main() { 29 | c, err := lxc.NewContainer(name, lxcpath) 30 | if err != nil { 31 | log.Fatalf("ERROR: %s\n", err.Error()) 32 | } 33 | defer c.Release() 34 | 35 | c.SetLogFile("/tmp/" + name + ".log") 36 | c.SetLogLevel(lxc.TRACE) 37 | 38 | log.Printf("Stopping the container...\n") 39 | if err := c.Stop(); err != nil { 40 | log.Fatalf("ERROR: %s\n", err.Error()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/unfreeze/unfreeze.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "log" 13 | "time" 14 | 15 | "github.com/lxc/go-lxc" 16 | ) 17 | 18 | var ( 19 | lxcpath string 20 | name string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&lxcpath, "lxcpath", lxc.DefaultConfigPath(), "Use specified container path") 25 | flag.StringVar(&name, "name", "rubik", "Name of the container") 26 | flag.Parse() 27 | } 28 | 29 | func main() { 30 | c, err := lxc.NewContainer(name, lxcpath) 31 | if err != nil { 32 | log.Fatalf("ERROR: %s\n", err.Error()) 33 | } 34 | defer c.Release() 35 | 36 | log.Printf("Unfreezing the container...\n") 37 | if err := c.Unfreeze(); err != nil { 38 | log.Fatalf("ERROR: %s\n", err.Error()) 39 | } 40 | c.Wait(lxc.RUNNING, 10*time.Second) 41 | } 42 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lxc/go-lxc 2 | 3 | go 1.20 4 | 5 | require golang.org/x/sys v0.21.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 2 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 3 | -------------------------------------------------------------------------------- /linking_dynamic.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo && !static_build 6 | // +build linux,cgo,!static_build 7 | 8 | package lxc 9 | 10 | // #cgo CFLAGS: -std=gnu11 -Wvla -Werror 11 | // #cgo pkg-config: lxc 12 | import "C" 13 | -------------------------------------------------------------------------------- /linking_static.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo && static_build 6 | // +build linux,cgo,static_build 7 | 8 | package lxc 9 | 10 | // #cgo CFLAGS: -std=gnu11 -Wvla -Werror 11 | // #cgo pkg-config: --static lxc libcrypto 12 | // #cgo LDFLAGS: -static 13 | import "C" 14 | -------------------------------------------------------------------------------- /lxc-binding.c: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux,cgo 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "lxc-binding.h" 19 | 20 | #ifndef LXC_DEVEL 21 | #define LXC_DEVEL 0 22 | #endif 23 | 24 | #define ret_errno(__errno__) \ 25 | ({ \ 26 | errno = (__errno__); \ 27 | -(__errno__); \ 28 | }) 29 | 30 | bool go_lxc_defined(struct lxc_container *c) { 31 | return c->is_defined(c); 32 | } 33 | 34 | const char* go_lxc_state(struct lxc_container *c) { 35 | return c->state(c); 36 | } 37 | 38 | bool go_lxc_running(struct lxc_container *c) { 39 | return c->is_running(c); 40 | } 41 | 42 | bool go_lxc_freeze(struct lxc_container *c) { 43 | return c->freeze(c); 44 | } 45 | 46 | bool go_lxc_unfreeze(struct lxc_container *c) { 47 | return c->unfreeze(c); 48 | } 49 | 50 | pid_t go_lxc_init_pid(struct lxc_container *c) { 51 | return c->init_pid(c); 52 | } 53 | 54 | int go_lxc_init_pidfd(struct lxc_container *c) { 55 | #if VERSION_AT_LEAST(4, 0, 0) 56 | return c->init_pidfd(c); 57 | #else 58 | return ret_errno(ENOSYS); 59 | #endif 60 | } 61 | 62 | int go_lxc_seccomp_notify_fd(struct lxc_container *c) { 63 | #if VERSION_AT_LEAST(4, 0, 0) 64 | return c->seccomp_notify_fd(c); 65 | #else 66 | return ret_errno(ENOSYS); 67 | #endif 68 | } 69 | 70 | int go_lxc_seccomp_notify_fd_active(struct lxc_container *c) { 71 | #if VERSION_AT_LEAST(4, 0, 5) 72 | return c->seccomp_notify_fd_active(c); 73 | #else 74 | return ret_errno(ENOSYS); 75 | #endif 76 | } 77 | 78 | int go_lxc_set_timeout(struct lxc_container *c, int timeout) { 79 | #if VERSION_AT_LEAST(5, 0, 4) 80 | if (!c->set_timeout(c, timeout)) 81 | return ret_errno(EINVAL); 82 | 83 | return 0; 84 | #else 85 | return ret_errno(ENOSYS); 86 | #endif 87 | } 88 | 89 | int go_lxc_devpts_fd(struct lxc_container *c) { 90 | #if VERSION_AT_LEAST(4, 0, 5) 91 | return c->devpts_fd(c); 92 | #else 93 | return ret_errno(ENOSYS); 94 | #endif 95 | } 96 | 97 | bool go_lxc_want_daemonize(struct lxc_container *c, bool state) { 98 | return c->want_daemonize(c, state); 99 | } 100 | 101 | bool go_lxc_want_close_all_fds(struct lxc_container *c, bool state) { 102 | return c->want_close_all_fds(c, state); 103 | } 104 | 105 | bool go_lxc_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char * const argv[]) { 106 | if (strncmp(t, "none", strlen(t)) == 0) { 107 | return c->create(c, NULL, bdevtype, specs, !!(flags & LXC_CREATE_QUIET), argv); 108 | } 109 | return c->create(c, t, bdevtype, specs, !!(flags & LXC_CREATE_QUIET), argv); 110 | } 111 | 112 | bool go_lxc_start(struct lxc_container *c, int useinit, char * const argv[]) { 113 | return c->start(c, useinit, argv); 114 | } 115 | 116 | bool go_lxc_stop(struct lxc_container *c) { 117 | return c->stop(c); 118 | } 119 | 120 | bool go_lxc_reboot(struct lxc_container *c) { 121 | return c->reboot(c); 122 | } 123 | 124 | bool go_lxc_shutdown(struct lxc_container *c, int timeout) { 125 | return c->shutdown(c, timeout); 126 | } 127 | 128 | char* go_lxc_config_file_name(struct lxc_container *c) { 129 | return c->config_file_name(c); 130 | } 131 | 132 | bool go_lxc_destroy(struct lxc_container *c) { 133 | return c->destroy(c); 134 | } 135 | 136 | bool go_lxc_destroy_with_snapshots(struct lxc_container *c) { 137 | #if VERSION_AT_LEAST(1, 1, 0) 138 | return c->destroy_with_snapshots(c); 139 | #else 140 | return false; 141 | #endif 142 | } 143 | 144 | bool go_lxc_wait(struct lxc_container *c, const char *state, int timeout) { 145 | return c->wait(c, state, timeout); 146 | } 147 | 148 | char *go_lxc_get_config_item(struct lxc_container *c, const char *key) 149 | { 150 | char *value = NULL; 151 | 152 | int len = c->get_config_item(c, key, NULL, 0); 153 | if (len <= 0) 154 | return NULL; 155 | 156 | again: 157 | value = (char *)malloc(sizeof(char) * len + 1); 158 | if (value == NULL) 159 | goto again; 160 | 161 | if (c->get_config_item(c, key, value, len + 1) != len) { 162 | free(value); 163 | return NULL; 164 | } 165 | 166 | return value; 167 | } 168 | 169 | bool go_lxc_set_config_item(struct lxc_container *c, const char *key, const char *value) { 170 | return c->set_config_item(c, key, value); 171 | } 172 | 173 | void go_lxc_clear_config(struct lxc_container *c) { 174 | c->clear_config(c); 175 | } 176 | 177 | bool go_lxc_clear_config_item(struct lxc_container *c, const char *key) { 178 | return c->clear_config_item(c, key); 179 | } 180 | 181 | char* go_lxc_get_running_config_item(struct lxc_container *c, const char *key) { 182 | return c->get_running_config_item(c, key); 183 | } 184 | 185 | char *go_lxc_get_keys(struct lxc_container *c, const char *key) 186 | { 187 | char *value = NULL; 188 | 189 | int len = c->get_keys(c, key, NULL, 0); 190 | if (len <= 0) 191 | return NULL; 192 | 193 | again: 194 | value = (char *)malloc(sizeof(char) * len + 1); 195 | if (value == NULL) 196 | goto again; 197 | 198 | if (c->get_keys(c, key, value, len + 1) != len) { 199 | free(value); 200 | return NULL; 201 | } 202 | 203 | return value; 204 | } 205 | 206 | char *go_lxc_get_cgroup_item(struct lxc_container *c, const char *key) 207 | { 208 | char *value = NULL; 209 | 210 | int len = c->get_cgroup_item(c, key, NULL, 0); 211 | if (len <= 0) 212 | return NULL; 213 | 214 | again: 215 | value = (char *)malloc(sizeof(char) * len + 1); 216 | if (value == NULL) 217 | goto again; 218 | 219 | if (c->get_cgroup_item(c, key, value, len + 1) != len) { 220 | free(value); 221 | return NULL; 222 | } 223 | 224 | return value; 225 | } 226 | 227 | bool go_lxc_set_cgroup_item(struct lxc_container *c, const char *key, const char *value) { 228 | return c->set_cgroup_item(c, key, value); 229 | } 230 | 231 | const char* go_lxc_get_config_path(struct lxc_container *c) { 232 | return c->get_config_path(c); 233 | } 234 | 235 | bool go_lxc_set_config_path(struct lxc_container *c, const char *path) { 236 | return c->set_config_path(c, path); 237 | } 238 | 239 | bool go_lxc_load_config(struct lxc_container *c, const char *alt_file) { 240 | return c->load_config(c, alt_file); 241 | } 242 | 243 | bool go_lxc_save_config(struct lxc_container *c, const char *alt_file) { 244 | return c->save_config(c, alt_file); 245 | } 246 | 247 | bool go_lxc_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype) { 248 | struct lxc_container *c2 = c->clone(c, newname, lxcpath, flags, bdevtype, NULL, 0, NULL); 249 | if (c2 == NULL) { 250 | return false; 251 | } 252 | lxc_container_put(c2); 253 | return true; 254 | } 255 | 256 | int go_lxc_console_getfd(struct lxc_container *c, int ttynum) { 257 | int mainfd; 258 | int ret = 0; 259 | 260 | ret = c->console_getfd(c, &ttynum, &mainfd); 261 | if (ret < 0) 262 | return ret; 263 | 264 | return mainfd; 265 | } 266 | 267 | bool go_lxc_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape) { 268 | 269 | if (c->console(c, ttynum, stdinfd, stdoutfd, stderrfd, escape) == 0) { 270 | return true; 271 | } 272 | return false; 273 | } 274 | 275 | char** go_lxc_get_interfaces(struct lxc_container *c) { 276 | return c->get_interfaces(c); 277 | } 278 | 279 | char** go_lxc_get_ips(struct lxc_container *c, const char *interface, const char *family, int scope) { 280 | return c->get_ips(c, interface, family, scope); 281 | } 282 | 283 | int wait_for_pid_status(pid_t pid) 284 | { 285 | int status, ret; 286 | 287 | again: 288 | ret = waitpid(pid, &status, 0); 289 | if (ret == -1) { 290 | if (errno == EINTR) 291 | goto again; 292 | return -1; 293 | } 294 | if (ret != pid) 295 | goto again; 296 | return status; 297 | } 298 | 299 | int go_lxc_attach_no_wait(struct lxc_container *c, 300 | bool clear_env, 301 | int namespaces, 302 | long personality, 303 | uid_t uid, gid_t gid, lxc_groups_t groups, 304 | int stdinfd, int stdoutfd, int stderrfd, 305 | char *initial_cwd, 306 | char **extra_env_vars, 307 | char **extra_keep_env, 308 | const char * const argv[], 309 | pid_t *attached_pid, 310 | int attach_flags) { 311 | int ret; 312 | 313 | lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; 314 | attach_options.attach_flags = attach_flags; 315 | 316 | lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL}; 317 | 318 | attach_options.env_policy = LXC_ATTACH_KEEP_ENV; 319 | if (clear_env) { 320 | attach_options.env_policy = LXC_ATTACH_CLEAR_ENV; 321 | } 322 | 323 | attach_options.namespaces = namespaces; 324 | attach_options.personality = personality; 325 | 326 | attach_options.uid = uid; 327 | attach_options.gid = gid; 328 | #if VERSION_AT_LEAST(4, 0, 9) 329 | if ( groups.size > 0 ) { 330 | attach_options.groups = groups; 331 | attach_options.attach_flags &= LXC_ATTACH_SETGROUPS; 332 | } 333 | #endif 334 | 335 | attach_options.stdin_fd = stdinfd; 336 | attach_options.stdout_fd = stdoutfd; 337 | attach_options.stderr_fd = stderrfd; 338 | 339 | attach_options.initial_cwd = initial_cwd; 340 | attach_options.extra_env_vars = extra_env_vars; 341 | attach_options.extra_keep_env = extra_keep_env; 342 | 343 | command.program = (char *)argv[0]; 344 | command.argv = (char **)argv; 345 | 346 | ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, attached_pid); 347 | if (ret < 0) 348 | return ret; 349 | 350 | return 0; 351 | } 352 | 353 | int go_lxc_attach(struct lxc_container *c, 354 | bool clear_env, 355 | int namespaces, 356 | long personality, 357 | uid_t uid, gid_t gid, lxc_groups_t groups, 358 | int stdinfd, int stdoutfd, int stderrfd, 359 | char *initial_cwd, 360 | char **extra_env_vars, 361 | char **extra_keep_env, 362 | int attach_flags) { 363 | int ret; 364 | pid_t pid; 365 | 366 | lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; 367 | attach_options.attach_flags = attach_flags; 368 | 369 | attach_options.env_policy = LXC_ATTACH_KEEP_ENV; 370 | if (clear_env) { 371 | attach_options.env_policy = LXC_ATTACH_CLEAR_ENV; 372 | } 373 | 374 | attach_options.namespaces = namespaces; 375 | attach_options.personality = personality; 376 | 377 | attach_options.uid = uid; 378 | attach_options.gid = gid; 379 | #if VERSION_AT_LEAST(4, 0, 9) 380 | if ( groups.size > 0 ) { 381 | attach_options.groups = groups; 382 | attach_options.attach_flags &= LXC_ATTACH_SETGROUPS; 383 | } 384 | #endif 385 | 386 | attach_options.stdin_fd = stdinfd; 387 | attach_options.stdout_fd = stdoutfd; 388 | attach_options.stderr_fd = stderrfd; 389 | 390 | attach_options.initial_cwd = initial_cwd; 391 | attach_options.extra_env_vars = extra_env_vars; 392 | attach_options.extra_keep_env = extra_keep_env; 393 | 394 | ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid); 395 | if (ret < 0) 396 | return ret; 397 | 398 | ret = wait_for_pid_status(pid); 399 | if (ret < 0) 400 | return ret; 401 | 402 | if (WIFEXITED(ret)) 403 | return WEXITSTATUS(ret); 404 | 405 | return ret; 406 | } 407 | 408 | int go_lxc_attach_run_wait(struct lxc_container *c, 409 | bool clear_env, 410 | int namespaces, 411 | long personality, 412 | uid_t uid, gid_t gid, lxc_groups_t groups, 413 | int stdinfd, int stdoutfd, int stderrfd, 414 | char *initial_cwd, 415 | char **extra_env_vars, 416 | char **extra_keep_env, 417 | const char * const argv[], 418 | int attach_flags) { 419 | int ret; 420 | 421 | lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; 422 | attach_options.attach_flags = attach_flags; 423 | 424 | attach_options.env_policy = LXC_ATTACH_KEEP_ENV; 425 | if (clear_env) { 426 | attach_options.env_policy = LXC_ATTACH_CLEAR_ENV; 427 | } 428 | 429 | attach_options.namespaces = namespaces; 430 | attach_options.personality = personality; 431 | 432 | attach_options.uid = uid; 433 | attach_options.gid = gid; 434 | #if VERSION_AT_LEAST(4, 0, 9) 435 | if ( groups.size > 0 ) { 436 | attach_options.groups = groups; 437 | attach_options.attach_flags &= LXC_ATTACH_SETGROUPS; 438 | } 439 | #endif 440 | 441 | attach_options.stdin_fd = stdinfd; 442 | attach_options.stdout_fd = stdoutfd; 443 | attach_options.stderr_fd = stderrfd; 444 | 445 | attach_options.initial_cwd = initial_cwd; 446 | attach_options.extra_env_vars = extra_env_vars; 447 | attach_options.extra_keep_env = extra_keep_env; 448 | 449 | ret = c->attach_run_wait(c, &attach_options, argv[0], argv); 450 | if (WIFEXITED(ret) && WEXITSTATUS(ret) == 255) 451 | return -1; 452 | return ret; 453 | } 454 | 455 | bool go_lxc_may_control(struct lxc_container *c) { 456 | return c->may_control(c); 457 | } 458 | 459 | int go_lxc_snapshot(struct lxc_container *c) { 460 | return c->snapshot(c, NULL); 461 | } 462 | 463 | int go_lxc_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret) { 464 | return c->snapshot_list(c, ret); 465 | } 466 | 467 | bool go_lxc_snapshot_restore(struct lxc_container *c, const char *snapname, const char *newname) { 468 | return c->snapshot_restore(c, snapname, newname); 469 | } 470 | 471 | bool go_lxc_snapshot_destroy(struct lxc_container *c, const char *snapname) { 472 | return c->snapshot_destroy(c, snapname); 473 | } 474 | 475 | bool go_lxc_snapshot_destroy_all(struct lxc_container *c) { 476 | #if VERSION_AT_LEAST(1, 1, 0) 477 | return c->snapshot_destroy_all(c); 478 | #else 479 | return false; 480 | #endif 481 | 482 | } 483 | 484 | bool go_lxc_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { 485 | return c->add_device_node(c, src_path, dest_path); 486 | } 487 | 488 | bool go_lxc_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path) { 489 | return c->remove_device_node(c, src_path, dest_path); 490 | } 491 | 492 | bool go_lxc_rename(struct lxc_container *c, const char *newname) { 493 | return c->rename(c, newname); 494 | } 495 | 496 | bool go_lxc_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose) { 497 | #if VERSION_AT_LEAST(1, 1, 0) 498 | return c->checkpoint(c, directory, stop, verbose); 499 | #else 500 | return false; 501 | #endif 502 | } 503 | 504 | bool go_lxc_restore(struct lxc_container *c, char *directory, bool verbose) { 505 | #if VERSION_AT_LEAST(1, 1, 0) 506 | return c->restore(c, directory, verbose); 507 | #else 508 | return false; 509 | #endif 510 | } 511 | 512 | int go_lxc_migrate(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, struct extra_migrate_opts *extras) { 513 | #if VERSION_AT_LEAST(3, 0, 0) 514 | opts->features_to_check = extras->features_to_check; 515 | #endif 516 | #if VERSION_AT_LEAST(2, 0, 4) 517 | opts->action_script = extras->action_script; 518 | opts->ghost_limit = extras->ghost_limit; 519 | #endif 520 | 521 | #if VERSION_AT_LEAST(2, 0, 1) 522 | opts->preserves_inodes = extras->preserves_inodes; 523 | #endif 524 | 525 | #if VERSION_AT_LEAST(2, 0, 0) 526 | return c->migrate(c, cmd, opts, sizeof(*opts)); 527 | #else 528 | return -EINVAL; 529 | #endif 530 | } 531 | 532 | bool go_lxc_attach_interface(struct lxc_container *c, const char *dev, const char *dst_dev) { 533 | #if VERSION_AT_LEAST(1, 1, 0) 534 | return c->attach_interface(c, dev, dst_dev); 535 | #else 536 | return false; 537 | #endif 538 | } 539 | 540 | bool go_lxc_detach_interface(struct lxc_container *c, const char *dev, const char *dst_dev) { 541 | #if VERSION_AT_LEAST(1, 1, 0) 542 | return c->detach_interface(c, dev, dst_dev); 543 | #else 544 | return false; 545 | #endif 546 | } 547 | 548 | bool go_lxc_config_item_is_supported(const char *key) 549 | { 550 | #if VERSION_AT_LEAST(2, 1, 0) 551 | return lxc_config_item_is_supported(key); 552 | #else 553 | return false; 554 | #endif 555 | } 556 | 557 | int go_lxc_error_num(struct lxc_container *c) 558 | { 559 | return c->error_num; 560 | } 561 | 562 | int go_lxc_console_log(struct lxc_container *c, struct lxc_console_log *log) { 563 | #if VERSION_AT_LEAST(3, 0, 0) 564 | return c->console_log(c, log); 565 | #else 566 | return false; 567 | #endif 568 | } 569 | 570 | bool go_lxc_has_api_extension(const char *extension) 571 | { 572 | #if VERSION_AT_LEAST(3, 1, 0) 573 | return lxc_has_api_extension(extension); 574 | #else 575 | return false; 576 | #endif 577 | } 578 | -------------------------------------------------------------------------------- /lxc-binding.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package lxc 9 | 10 | // #include 11 | // #include 12 | // #include "lxc-binding.h" 13 | // #ifndef LXC_DEVEL 14 | // #define LXC_DEVEL 0 15 | // #endif 16 | import "C" 17 | 18 | import ( 19 | "fmt" 20 | "strconv" 21 | "strings" 22 | "unsafe" 23 | ) 24 | 25 | // NewContainer returns a new container struct. 26 | // Caller needs to call Release() on the returned container to release its resources. 27 | func NewContainer(name string, lxcpath ...string) (*Container, error) { 28 | var container *C.struct_lxc_container 29 | 30 | cname := C.CString(name) 31 | defer C.free(unsafe.Pointer(cname)) 32 | 33 | if lxcpath != nil && len(lxcpath) == 1 { 34 | clxcpath := C.CString(lxcpath[0]) 35 | defer C.free(unsafe.Pointer(clxcpath)) 36 | 37 | container = C.lxc_container_new(cname, clxcpath) 38 | } else { 39 | container = C.lxc_container_new(cname, nil) 40 | } 41 | 42 | if container == nil { 43 | return nil, ErrNewFailed 44 | } 45 | c := &Container{container: container, verbosity: Quiet} 46 | 47 | return c, nil 48 | } 49 | 50 | // Acquire increments the reference counter of the container object. 51 | func Acquire(c *Container) bool { 52 | c.mu.RLock() 53 | defer c.mu.RUnlock() 54 | 55 | return C.lxc_container_get(c.container) == 1 56 | } 57 | 58 | // Release decrements the reference counter of the container object. 59 | func Release(c *Container) bool { 60 | return c.Release() == nil 61 | } 62 | 63 | // Version returns the LXC version. 64 | func Version() string { 65 | version := C.GoString(C.lxc_get_version()) 66 | 67 | // New liblxc versions append "-devel" when LXC_DEVEL is set. 68 | if strings.HasSuffix(version, "-devel") { 69 | return fmt.Sprintf("%s (devel)", version[:(len(version)-len("-devel"))]) 70 | } 71 | 72 | return version 73 | } 74 | 75 | // GlobalConfigItem returns the value of the given global config key. 76 | func GlobalConfigItem(name string) string { 77 | cname := C.CString(name) 78 | defer C.free(unsafe.Pointer(cname)) 79 | 80 | return C.GoString(C.lxc_get_global_config_item(cname)) 81 | } 82 | 83 | // DefaultConfigPath returns default config path. 84 | func DefaultConfigPath() string { 85 | return GlobalConfigItem("lxc.lxcpath") 86 | } 87 | 88 | // DefaultLvmVg returns the name of the default LVM volume group. 89 | func DefaultLvmVg() string { 90 | return GlobalConfigItem("lxc.bdev.lvm.vg") 91 | } 92 | 93 | // DefaultZfsRoot returns the name of the default ZFS root. 94 | func DefaultZfsRoot() string { 95 | return GlobalConfigItem("lxc.bdev.zfs.root") 96 | } 97 | 98 | // ContainerNames returns the names of defined and active containers on the system. 99 | func ContainerNames(lxcpath ...string) []string { 100 | var size int 101 | var cnames **C.char 102 | 103 | if lxcpath != nil && len(lxcpath) == 1 { 104 | clxcpath := C.CString(lxcpath[0]) 105 | defer C.free(unsafe.Pointer(clxcpath)) 106 | 107 | size = int(C.list_all_containers(clxcpath, &cnames, nil)) 108 | } else { 109 | 110 | size = int(C.list_all_containers(nil, &cnames, nil)) 111 | } 112 | 113 | if size < 1 { 114 | return nil 115 | } 116 | return convertNArgs(cnames, size) 117 | } 118 | 119 | // Containers returns the defined and active containers on the system. Only 120 | // containers that could retrieved successfully are returned. 121 | // Caller needs to call Release() on the returned containers to release resources. 122 | func Containers(lxcpath ...string) []*Container { 123 | var containers []*Container 124 | 125 | for _, v := range ContainerNames(lxcpath...) { 126 | if container, err := NewContainer(v, lxcpath...); err == nil { 127 | containers = append(containers, container) 128 | } 129 | } 130 | 131 | return containers 132 | } 133 | 134 | // DefinedContainerNames returns the names of the defined containers on the system. 135 | func DefinedContainerNames(lxcpath ...string) []string { 136 | var size int 137 | var cnames **C.char 138 | 139 | if lxcpath != nil && len(lxcpath) == 1 { 140 | clxcpath := C.CString(lxcpath[0]) 141 | defer C.free(unsafe.Pointer(clxcpath)) 142 | 143 | size = int(C.list_defined_containers(clxcpath, &cnames, nil)) 144 | } else { 145 | 146 | size = int(C.list_defined_containers(nil, &cnames, nil)) 147 | } 148 | 149 | if size < 1 { 150 | return nil 151 | } 152 | return convertNArgs(cnames, size) 153 | } 154 | 155 | // DefinedContainers returns the defined containers on the system. Only 156 | // containers that could retrieved successfully are returned. 157 | // Caller needs to call Release() on the returned containers to release resources. 158 | func DefinedContainers(lxcpath ...string) []*Container { 159 | var containers []*Container 160 | 161 | for _, v := range DefinedContainerNames(lxcpath...) { 162 | if container, err := NewContainer(v, lxcpath...); err == nil { 163 | containers = append(containers, container) 164 | } 165 | } 166 | 167 | return containers 168 | } 169 | 170 | // ActiveContainerNames returns the names of the active containers on the system. 171 | func ActiveContainerNames(lxcpath ...string) []string { 172 | var size int 173 | var cnames **C.char 174 | 175 | if lxcpath != nil && len(lxcpath) == 1 { 176 | clxcpath := C.CString(lxcpath[0]) 177 | defer C.free(unsafe.Pointer(clxcpath)) 178 | 179 | size = int(C.list_active_containers(clxcpath, &cnames, nil)) 180 | } else { 181 | 182 | size = int(C.list_active_containers(nil, &cnames, nil)) 183 | } 184 | 185 | if size < 1 { 186 | return nil 187 | } 188 | return convertNArgs(cnames, size) 189 | } 190 | 191 | // ActiveContainers returns the active containers on the system. Only 192 | // containers that could retrieved successfully are returned. 193 | // Caller needs to call Release() on the returned containers to release resources. 194 | func ActiveContainers(lxcpath ...string) []*Container { 195 | var containers []*Container 196 | 197 | for _, v := range ActiveContainerNames(lxcpath...) { 198 | if container, err := NewContainer(v, lxcpath...); err == nil { 199 | containers = append(containers, container) 200 | } 201 | } 202 | 203 | return containers 204 | } 205 | 206 | // VersionNumber returns the LXC version. 207 | func VersionNumber() (major int, minor int) { 208 | major = C.LXC_VERSION_MAJOR 209 | minor = C.LXC_VERSION_MINOR 210 | 211 | return 212 | } 213 | 214 | // VersionAtLeast returns true when the tested version >= current version. 215 | func VersionAtLeast(major int, minor int, micro int) bool { 216 | if C.LXC_DEVEL == 1 { 217 | return true 218 | } 219 | 220 | if major > C.LXC_VERSION_MAJOR { 221 | return false 222 | } 223 | 224 | if major == C.LXC_VERSION_MAJOR && 225 | minor > C.LXC_VERSION_MINOR { 226 | return false 227 | } 228 | 229 | if major == C.LXC_VERSION_MAJOR && 230 | minor == C.LXC_VERSION_MINOR && 231 | micro > C.LXC_VERSION_MICRO { 232 | return false 233 | } 234 | 235 | return true 236 | } 237 | 238 | // IsSupportedConfigItem returns true if the key belongs to a supported config item. 239 | func IsSupportedConfigItem(key string) bool { 240 | configItem := C.CString(key) 241 | defer C.free(unsafe.Pointer(configItem)) 242 | return bool(C.go_lxc_config_item_is_supported(configItem)) 243 | } 244 | 245 | // RuntimeLiblxcVersionAtLeast checks if the system's liblxc matches the 246 | // provided version requirement 247 | func RuntimeLiblxcVersionAtLeast(version string, major int, minor int, micro int) bool { 248 | // Strip git versioning from pre-release snapshots. 249 | version = strings.Split(version, "~")[0] 250 | 251 | // Convert devel indicator into a valid version. 252 | version = strings.Replace(version, " (devel)", "-devel", 1) 253 | 254 | // Split the version into its major, minor and micro parts. 255 | parts := strings.Split(version, ".") 256 | partsLen := len(parts) 257 | if partsLen == 0 { 258 | return false 259 | } 260 | 261 | // If the last part includes -devel, assume everything is supported. 262 | develParts := strings.Split(parts[partsLen-1], "-") 263 | if len(develParts) == 2 && develParts[1] == "devel" { 264 | return true 265 | } 266 | 267 | // Actually parse and compare the version string now. 268 | maj := -1 269 | min := -1 270 | mic := -1 271 | 272 | for i, v := range parts { 273 | if i > 2 { 274 | break 275 | } 276 | 277 | num, err := strconv.Atoi(v) 278 | if err != nil { 279 | return false 280 | } 281 | 282 | switch i { 283 | case 0: 284 | maj = num 285 | case 1: 286 | min = num 287 | case 2: 288 | mic = num 289 | } 290 | } 291 | 292 | /* Major version is greater. */ 293 | if maj > major { 294 | return true 295 | } 296 | 297 | if maj < major { 298 | return false 299 | } 300 | 301 | /* Minor number is greater.*/ 302 | if min > minor { 303 | return true 304 | } 305 | 306 | if min < minor { 307 | return false 308 | } 309 | 310 | /* Patch number is greater. */ 311 | if mic > micro { 312 | return true 313 | } 314 | 315 | if mic < micro { 316 | return false 317 | } 318 | 319 | return true 320 | } 321 | 322 | // HasApiExtension returns true if the extension is supported. 323 | // Deprecated: Please use HasAPIExtension instead. 324 | func HasApiExtension(extension string) bool { 325 | return HasAPIExtension(extension) 326 | } 327 | 328 | // HasAPIExtension returns true if the extension is supported. 329 | func HasAPIExtension(extension string) bool { 330 | if RuntimeLiblxcVersionAtLeast(Version(), 3, 1, 0) { 331 | apiExtension := C.CString(extension) 332 | defer C.free(unsafe.Pointer(apiExtension)) 333 | return bool(C.go_lxc_has_api_extension(apiExtension)) 334 | } 335 | return false 336 | } 337 | -------------------------------------------------------------------------------- /lxc-binding.h: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | 7 | #define VERSION_AT_LEAST(major, minor, micro) \ 8 | ((LXC_DEVEL == 1) || (!(major > LXC_VERSION_MAJOR || \ 9 | major == LXC_VERSION_MAJOR && minor > LXC_VERSION_MINOR || \ 10 | major == LXC_VERSION_MAJOR && minor == LXC_VERSION_MINOR && micro > LXC_VERSION_MICRO))) 11 | 12 | extern bool go_lxc_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path); 13 | extern void go_lxc_clear_config(struct lxc_container *c); 14 | extern bool go_lxc_clear_config_item(struct lxc_container *c, const char *key); 15 | extern bool go_lxc_clone(struct lxc_container *c, const char *newname, const char *lxcpath, int flags, const char *bdevtype); 16 | extern bool go_lxc_console(struct lxc_container *c, int ttynum, int stdinfd, int stdoutfd, int stderrfd, int escape); 17 | extern bool go_lxc_create(struct lxc_container *c, const char *t, const char *bdevtype, struct bdev_specs *specs, int flags, char * const argv[]); 18 | extern bool go_lxc_defined(struct lxc_container *c); 19 | extern bool go_lxc_destroy(struct lxc_container *c); 20 | extern bool go_lxc_destroy_with_snapshots(struct lxc_container *c); 21 | extern bool go_lxc_freeze(struct lxc_container *c); 22 | extern bool go_lxc_load_config(struct lxc_container *c, const char *alt_file); 23 | extern bool go_lxc_may_control(struct lxc_container *c); 24 | extern bool go_lxc_reboot(struct lxc_container *c); 25 | extern bool go_lxc_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path); 26 | extern bool go_lxc_rename(struct lxc_container *c, const char *newname); 27 | extern bool go_lxc_running(struct lxc_container *c); 28 | extern bool go_lxc_save_config(struct lxc_container *c, const char *alt_file); 29 | extern bool go_lxc_set_cgroup_item(struct lxc_container *c, const char *key, const char *value); 30 | extern bool go_lxc_set_config_item(struct lxc_container *c, const char *key, const char *value); 31 | extern bool go_lxc_set_config_path(struct lxc_container *c, const char *path); 32 | extern bool go_lxc_shutdown(struct lxc_container *c, int timeout); 33 | extern bool go_lxc_snapshot_destroy(struct lxc_container *c, const char *snapname); 34 | extern bool go_lxc_snapshot_destroy_all(struct lxc_container *c); 35 | extern bool go_lxc_snapshot_restore(struct lxc_container *c, const char *snapname, const char *newname); 36 | extern bool go_lxc_start(struct lxc_container *c, int useinit, char * const argv[]); 37 | extern bool go_lxc_stop(struct lxc_container *c); 38 | extern bool go_lxc_unfreeze(struct lxc_container *c); 39 | extern bool go_lxc_wait(struct lxc_container *c, const char *state, int timeout); 40 | extern bool go_lxc_want_close_all_fds(struct lxc_container *c, bool state); 41 | extern bool go_lxc_want_daemonize(struct lxc_container *c, bool state); 42 | extern char* go_lxc_config_file_name(struct lxc_container *c); 43 | extern char* go_lxc_get_cgroup_item(struct lxc_container *c, const char *key); 44 | extern char* go_lxc_get_config_item(struct lxc_container *c, const char *key); 45 | extern char** go_lxc_get_interfaces(struct lxc_container *c); 46 | extern char** go_lxc_get_ips(struct lxc_container *c, const char *interface, const char *family, int scope); 47 | extern char* go_lxc_get_keys(struct lxc_container *c, const char *key); 48 | extern char* go_lxc_get_running_config_item(struct lxc_container *c, const char *key); 49 | extern const char* go_lxc_get_config_path(struct lxc_container *c); 50 | extern const char* go_lxc_state(struct lxc_container *c); 51 | 52 | #if !VERSION_AT_LEAST(4, 0, 9) && !defined(LXC_ATTACH_SETGROUPS) 53 | typedef struct lxc_groups_t { 54 | size_t size; 55 | gid_t *list; 56 | } lxc_groups_t; 57 | # endif 58 | 59 | extern int go_lxc_attach_run_wait(struct lxc_container *c, 60 | bool clear_env, 61 | int namespaces, 62 | long personality, 63 | uid_t uid, gid_t gid, lxc_groups_t groups, 64 | int stdinfd, int stdoutfd, int stderrfd, 65 | char *initial_cwd, 66 | char **extra_env_vars, 67 | char **extra_keep_env, 68 | const char * const argv[], 69 | int attach_flags); 70 | extern int go_lxc_attach(struct lxc_container *c, 71 | bool clear_env, 72 | int namespaces, 73 | long personality, 74 | uid_t uid, gid_t gid, lxc_groups_t groups, 75 | int stdinfd, int stdoutfd, int stderrfd, 76 | char *initial_cwd, 77 | char **extra_env_vars, 78 | char **extra_keep_env, 79 | int attach_flags); 80 | extern int go_lxc_attach_no_wait(struct lxc_container *c, 81 | bool clear_env, 82 | int namespaces, 83 | long personality, 84 | uid_t uid, gid_t gid, lxc_groups_t groups, 85 | int stdinfd, int stdoutfd, int stderrfd, 86 | char *initial_cwd, 87 | char **extra_env_vars, 88 | char **extra_keep_env, 89 | const char * const argv[], 90 | pid_t *attached_pid, 91 | int attach_flags); 92 | extern int go_lxc_console_getfd(struct lxc_container *c, int ttynum); 93 | extern int go_lxc_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret); 94 | extern int go_lxc_snapshot(struct lxc_container *c); 95 | extern pid_t go_lxc_init_pid(struct lxc_container *c); 96 | extern int go_lxc_init_pidfd(struct lxc_container *c); 97 | extern int go_lxc_devpts_fd(struct lxc_container *c); 98 | extern int go_lxc_seccomp_notify_fd(struct lxc_container *c); 99 | extern int go_lxc_seccomp_notify_fd_active(struct lxc_container *c); 100 | extern int go_lxc_set_timeout(struct lxc_container *c, int timeout); 101 | extern bool go_lxc_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose); 102 | extern bool go_lxc_restore(struct lxc_container *c, char *directory, bool verbose); 103 | extern bool go_lxc_config_item_is_supported(const char *key); 104 | extern bool go_lxc_has_api_extension(const char *extension); 105 | 106 | /* n.b. that we're just adding the fields here to shorten the definition 107 | * of go_lxc_migrate; in the case where we don't have the ->migrate API call, 108 | * we don't want to have to pass all the arguments in to let conditional 109 | * compilation handle things, but the call will still fail 110 | */ 111 | #if !VERSION_AT_LEAST(2, 0, 0) 112 | struct migrate_opts { 113 | char *directory; 114 | bool verbose; 115 | bool stop; 116 | char *predump_dir; 117 | }; 118 | #endif 119 | 120 | /* This is a struct that we can add "extra" (i.e. options added after 2.0.0) 121 | * migrate options to, so that we don't have to have a massive function 122 | * signature when the list of options grows. 123 | */ 124 | struct extra_migrate_opts { 125 | bool preserves_inodes; 126 | char *action_script; 127 | uint64_t ghost_limit; 128 | uint64_t features_to_check; 129 | }; 130 | int go_lxc_migrate(struct lxc_container *c, unsigned int cmd, struct migrate_opts *opts, struct extra_migrate_opts *extras); 131 | 132 | extern bool go_lxc_attach_interface(struct lxc_container *c, const char *dev, const char *dst_dev); 133 | extern bool go_lxc_detach_interface(struct lxc_container *c, const char *dev, const char *dst_dev); 134 | 135 | #if !VERSION_AT_LEAST(3, 0, 0) 136 | struct lxc_console_log { 137 | bool clear; 138 | bool read; 139 | uint64_t *read_max; 140 | char *data; 141 | }; 142 | #endif 143 | 144 | extern int go_lxc_console_log(struct lxc_container *c, struct lxc_console_log *log); 145 | extern int go_lxc_error_num(struct lxc_container *c); 146 | -------------------------------------------------------------------------------- /lxc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package lxc 9 | 10 | import ( 11 | "fmt" 12 | "io/ioutil" 13 | "math/rand" 14 | "net" 15 | "os" 16 | "runtime" 17 | "strconv" 18 | "strings" 19 | "sync" 20 | "syscall" 21 | "testing" 22 | "time" 23 | ) 24 | 25 | const ( 26 | DefaultContainerName = "lorem" 27 | DefaultSnapshotName = "snap0" 28 | DefaultContainerRestoreName = "ipsum" 29 | DefaultContainerCloneName = "consectetur" 30 | DefaultContainerCloneOverlayName = "adipiscing" 31 | ) 32 | 33 | func exists(name string) bool { 34 | _, err := os.Lstat(name) 35 | if err != nil && os.IsNotExist(err) { 36 | return false 37 | } 38 | return true 39 | } 40 | 41 | func unprivileged() bool { 42 | return os.Geteuid() != 0 43 | } 44 | 45 | func supported(moduleName string) bool { 46 | if _, err := os.Stat("/sys/module/" + moduleName); err != nil { 47 | return false 48 | } 49 | return true 50 | } 51 | 52 | func ipv6() bool { 53 | lxcbr0, err := net.InterfaceByName("lxcbr0") 54 | if err != nil { 55 | return false 56 | } 57 | 58 | addresses, err := lxcbr0.Addrs() 59 | if err != nil { 60 | return false 61 | } 62 | 63 | // https://github.com/asaskevich/govalidator/blob/master/validator.go#L621 64 | for _, v := range addresses { 65 | if ipnet, ok := v.(*net.IPNet); ok && strings.Count(v.String(), ":") >= 2 && !ipnet.IP.IsLinkLocalUnicast() { 66 | return true 67 | } 68 | } 69 | 70 | return false 71 | } 72 | 73 | func template() TemplateOptions { 74 | return TemplateOptions{ 75 | Template: "download", 76 | Distro: "alpine", 77 | Release: "edge", 78 | Arch: "amd64", 79 | } 80 | } 81 | 82 | func ContainerName() string { 83 | if unprivileged() { 84 | return fmt.Sprintf("%s-unprivileged", DefaultContainerName) 85 | } 86 | return DefaultContainerName 87 | } 88 | 89 | func ContainerRestoreName() string { 90 | if unprivileged() { 91 | return fmt.Sprintf("%s-unprivileged", DefaultContainerRestoreName) 92 | } 93 | return DefaultContainerRestoreName 94 | } 95 | 96 | func ContainerCloneName() string { 97 | if unprivileged() { 98 | return fmt.Sprintf("%s-unprivileged", DefaultContainerCloneName) 99 | } 100 | return DefaultContainerCloneName 101 | } 102 | 103 | func ContainerCloneOverlayName() string { 104 | if unprivileged() { 105 | return fmt.Sprintf("%s-unprivileged", DefaultContainerCloneOverlayName) 106 | } 107 | return DefaultContainerCloneOverlayName 108 | } 109 | 110 | func TestVersion(t *testing.T) { 111 | t.Logf("LXC version: %s", Version()) 112 | } 113 | 114 | func TestDefaultConfigPath(t *testing.T) { 115 | if DefaultConfigPath() == "" { 116 | t.Errorf("DefaultConfigPath failed...") 117 | } 118 | } 119 | 120 | func TestSetConfigPath(t *testing.T) { 121 | c, err := NewContainer(ContainerName()) 122 | if err != nil { 123 | t.Errorf(err.Error()) 124 | } 125 | defer c.Release() 126 | 127 | currentPath := c.ConfigPath() 128 | if err := c.SetConfigPath("/tmp"); err != nil { 129 | t.Errorf(err.Error()) 130 | } 131 | newPath := c.ConfigPath() 132 | 133 | if currentPath == newPath { 134 | t.Errorf("SetConfigPath failed...") 135 | } 136 | } 137 | 138 | func TestAcquire(t *testing.T) { 139 | c, err := NewContainer(ContainerName()) 140 | if err != nil { 141 | t.Errorf(err.Error()) 142 | } 143 | defer c.Release() 144 | 145 | Acquire(c) 146 | Release(c) 147 | } 148 | 149 | func TestConcurrentDefined_Negative(t *testing.T) { 150 | t.Skip("Skipping concurrent tests for now") 151 | 152 | defer runtime.GOMAXPROCS(runtime.NumCPU()) 153 | 154 | var wg sync.WaitGroup 155 | 156 | for i := 0; i <= 100; i++ { 157 | wg.Add(1) 158 | go func() { 159 | c, err := NewContainer(strconv.Itoa(rand.Intn(10))) 160 | if err != nil { 161 | t.Errorf(err.Error()) 162 | } 163 | defer c.Release() 164 | 165 | // sleep for a while to simulate some work 166 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(250))) 167 | 168 | if c.Defined() { 169 | t.Errorf("Defined_Negative failed...") 170 | } 171 | wg.Done() 172 | }() 173 | } 174 | wg.Wait() 175 | } 176 | 177 | func TestDefined_Negative(t *testing.T) { 178 | c, err := NewContainer(ContainerName()) 179 | if err != nil { 180 | t.Errorf(err.Error()) 181 | } 182 | defer c.Release() 183 | 184 | if c.Defined() { 185 | t.Errorf("Defined_Negative failed...") 186 | } 187 | } 188 | 189 | func TestSetVerbosity(t *testing.T) { 190 | c, err := NewContainer(ContainerName()) 191 | if err != nil { 192 | t.Errorf(err.Error()) 193 | } 194 | defer c.Release() 195 | 196 | c.SetVerbosity(Quiet) 197 | } 198 | 199 | func TestCreate(t *testing.T) { 200 | c, err := NewContainer(ContainerName()) 201 | if err != nil { 202 | t.Errorf(err.Error()) 203 | } 204 | defer c.Release() 205 | 206 | c.SetVerbosity(Verbose) 207 | 208 | if err := c.Create(template()); err != nil { 209 | t.Errorf(err.Error()) 210 | } 211 | } 212 | 213 | func TestClone(t *testing.T) { 214 | c, err := NewContainer(ContainerName()) 215 | if err != nil { 216 | t.Errorf(err.Error()) 217 | } 218 | defer c.Release() 219 | 220 | if err = c.Clone(ContainerCloneName(), DefaultCloneOptions); err != nil { 221 | t.Errorf(err.Error()) 222 | } 223 | } 224 | 225 | func TestCloneUsingOverlayfs(t *testing.T) { 226 | if !(supported("overlayfs") || supported("overlay")) { 227 | t.Skip("skipping test as overlayfs support is missing.") 228 | } 229 | 230 | c, err := NewContainer(ContainerName()) 231 | if err != nil { 232 | t.Errorf(err.Error()) 233 | } 234 | defer c.Release() 235 | 236 | err = c.Clone(ContainerCloneOverlayName(), CloneOptions{ 237 | Backend: Overlayfs, 238 | KeepName: true, 239 | KeepMAC: true, 240 | Snapshot: true, 241 | }) 242 | if err != nil { 243 | t.Errorf(err.Error()) 244 | } 245 | } 246 | 247 | func TestCreateSnapshot(t *testing.T) { 248 | c, err := NewContainer(ContainerName()) 249 | if err != nil { 250 | t.Errorf(err.Error()) 251 | } 252 | defer c.Release() 253 | 254 | if _, err := c.CreateSnapshot(); err != nil { 255 | t.Errorf(err.Error()) 256 | } 257 | } 258 | 259 | func TestCreateSnapshots(t *testing.T) { 260 | c, err := NewContainer(ContainerName()) 261 | if err != nil { 262 | t.Errorf(err.Error()) 263 | } 264 | defer c.Release() 265 | 266 | for i := 0; i < 3; i++ { 267 | if _, err := c.CreateSnapshot(); err != nil { 268 | t.Errorf(err.Error()) 269 | } 270 | } 271 | } 272 | 273 | func TestRestoreSnapshot(t *testing.T) { 274 | if os.Getenv("GITHUB_ACTION") != "" { 275 | t.Skip("Test broken on Github") 276 | } 277 | 278 | c, err := NewContainer(ContainerName()) 279 | if err != nil { 280 | t.Errorf(err.Error()) 281 | } 282 | defer c.Release() 283 | 284 | snapshot := Snapshot{Name: DefaultSnapshotName} 285 | if err := c.RestoreSnapshot(snapshot, ContainerRestoreName()); err != nil { 286 | t.Errorf(err.Error()) 287 | } 288 | } 289 | 290 | func TestConcurrentCreate(t *testing.T) { 291 | t.Skip("Skipping concurrent tests for now") 292 | 293 | defer runtime.GOMAXPROCS(runtime.NumCPU()) 294 | 295 | var wg sync.WaitGroup 296 | 297 | options := template() 298 | for i := 0; i < 10; i++ { 299 | wg.Add(1) 300 | go func(i int) { 301 | c, err := NewContainer(strconv.Itoa(i)) 302 | if err != nil { 303 | t.Errorf(err.Error()) 304 | } 305 | defer c.Release() 306 | 307 | // sleep for a while to simulate some work 308 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(250))) 309 | 310 | if err := c.Create(options); err != nil { 311 | t.Errorf(err.Error()) 312 | } 313 | wg.Done() 314 | }(i) 315 | } 316 | wg.Wait() 317 | } 318 | 319 | func TestSnapshots(t *testing.T) { 320 | c, err := NewContainer(ContainerName()) 321 | if err != nil { 322 | t.Errorf(err.Error()) 323 | } 324 | defer c.Release() 325 | 326 | if _, err := c.Snapshots(); err != nil { 327 | t.Errorf(err.Error()) 328 | } 329 | } 330 | 331 | func TestConcurrentStart(t *testing.T) { 332 | t.Skip("Skipping concurrent tests for now") 333 | 334 | defer runtime.GOMAXPROCS(runtime.NumCPU()) 335 | 336 | var wg sync.WaitGroup 337 | 338 | for i := 0; i < 10; i++ { 339 | wg.Add(1) 340 | go func(i int) { 341 | c, err := NewContainer(strconv.Itoa(i)) 342 | if err != nil { 343 | t.Errorf(err.Error()) 344 | } 345 | defer c.Release() 346 | 347 | if err := c.Start(); err != nil { 348 | t.Errorf(err.Error()) 349 | } 350 | 351 | c.Wait(RUNNING, 30*time.Second) 352 | if !c.Running() { 353 | t.Errorf("Starting the container failed...") 354 | } 355 | 356 | wg.Done() 357 | }(i) 358 | } 359 | wg.Wait() 360 | } 361 | 362 | func TestConfigFileName(t *testing.T) { 363 | c, err := NewContainer(ContainerName()) 364 | if err != nil { 365 | t.Errorf(err.Error()) 366 | } 367 | defer c.Release() 368 | 369 | if c.ConfigFileName() == "" { 370 | t.Errorf("ConfigFileName failed...") 371 | } 372 | } 373 | 374 | func TestDefined_Positive(t *testing.T) { 375 | c, err := NewContainer(ContainerName()) 376 | if err != nil { 377 | t.Errorf(err.Error()) 378 | } 379 | defer c.Release() 380 | 381 | if !c.Defined() { 382 | t.Errorf("Defined_Positive failed...") 383 | } 384 | } 385 | 386 | func TestConcurrentDefined_Positive(t *testing.T) { 387 | t.Skip("Skipping concurrent tests for now") 388 | 389 | defer runtime.GOMAXPROCS(runtime.NumCPU()) 390 | 391 | var wg sync.WaitGroup 392 | 393 | for i := 0; i <= 100; i++ { 394 | wg.Add(1) 395 | go func() { 396 | c, err := NewContainer(strconv.Itoa(rand.Intn(10))) 397 | if err != nil { 398 | t.Errorf(err.Error()) 399 | } 400 | defer c.Release() 401 | 402 | // sleep for a while to simulate some work 403 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(250))) 404 | 405 | if !c.Defined() { 406 | t.Errorf("Defined_Positive failed...") 407 | } 408 | wg.Done() 409 | }() 410 | } 411 | wg.Wait() 412 | } 413 | 414 | func TestInitPid_Negative(t *testing.T) { 415 | c, err := NewContainer(ContainerName()) 416 | if err != nil { 417 | t.Errorf(err.Error()) 418 | } 419 | defer c.Release() 420 | 421 | if c.InitPid() != -1 { 422 | t.Errorf("InitPid failed...") 423 | } 424 | } 425 | 426 | func TestStart(t *testing.T) { 427 | c, err := NewContainer(ContainerName()) 428 | if err != nil { 429 | t.Errorf(err.Error()) 430 | } 431 | defer c.Release() 432 | 433 | log := fmt.Sprintf("/tmp/%s", ContainerName()) 434 | if err := c.SetLogFile(log); err != nil { 435 | t.Errorf("SetLogFile failed...") 436 | } 437 | 438 | if err := c.Start(); err != nil { 439 | t.Errorf(err.Error()) 440 | } 441 | 442 | c.Wait(RUNNING, 30*time.Second) 443 | if !c.Running() { 444 | t.Errorf("Starting the container failed...") 445 | 446 | b, err := ioutil.ReadFile(log) 447 | if err != nil { 448 | t.Errorf("Reading %s file failed...", log) 449 | } 450 | t.Logf("%s\n", b) 451 | } 452 | } 453 | 454 | func TestWaitIPAddresses(t *testing.T) { 455 | c, err := NewContainer(ContainerName()) 456 | if err != nil { 457 | t.Errorf(err.Error()) 458 | } 459 | defer c.Release() 460 | 461 | if _, err := c.WaitIPAddresses(30 * time.Second); err != nil { 462 | t.Errorf(err.Error()) 463 | } 464 | } 465 | 466 | func TestControllable(t *testing.T) { 467 | c, err := NewContainer(ContainerName()) 468 | if err != nil { 469 | t.Errorf(err.Error()) 470 | } 471 | defer c.Release() 472 | 473 | if !c.Controllable() { 474 | t.Errorf("Controlling the container failed...") 475 | } 476 | } 477 | 478 | func TestContainerNames(t *testing.T) { 479 | if ContainerNames() == nil { 480 | t.Errorf("ContainerNames failed...") 481 | } 482 | } 483 | 484 | func TestDefinedContainerNames(t *testing.T) { 485 | if DefinedContainerNames() == nil { 486 | t.Errorf("DefinedContainerNames failed...") 487 | } 488 | } 489 | 490 | func TestActiveContainerNames(t *testing.T) { 491 | if ActiveContainerNames() == nil { 492 | t.Errorf("ActiveContainerNames failed...") 493 | } 494 | } 495 | 496 | func TestContainers(t *testing.T) { 497 | if Containers() == nil { 498 | t.Errorf("Containers failed...") 499 | } 500 | } 501 | 502 | func TestDefinedContainers(t *testing.T) { 503 | if DefinedContainers() == nil { 504 | t.Errorf("DefinedContainers failed...") 505 | } 506 | } 507 | 508 | func TestActiveContainers(t *testing.T) { 509 | if ActiveContainers() == nil { 510 | t.Errorf("ActiveContainers failed...") 511 | } 512 | } 513 | 514 | func TestRunning(t *testing.T) { 515 | c, err := NewContainer(ContainerName()) 516 | if err != nil { 517 | t.Errorf(err.Error()) 518 | } 519 | defer c.Release() 520 | 521 | if !c.Running() { 522 | t.Errorf("Checking the container failed...") 523 | } 524 | } 525 | 526 | func TestWantDaemonize(t *testing.T) { 527 | c, err := NewContainer(ContainerName()) 528 | if err != nil { 529 | t.Errorf(err.Error()) 530 | } 531 | defer c.Release() 532 | 533 | if err := c.WantDaemonize(false); err != nil || c.Daemonize() { 534 | t.Errorf("WantDaemonize failed...") 535 | } 536 | } 537 | 538 | func TestWantCloseAllFds(t *testing.T) { 539 | c, err := NewContainer(ContainerName()) 540 | if err != nil { 541 | t.Errorf(err.Error()) 542 | } 543 | defer c.Release() 544 | 545 | if err := c.WantCloseAllFds(true); err != nil { 546 | t.Errorf("WantCloseAllFds failed...") 547 | } 548 | } 549 | 550 | func TestSetLogLevel(t *testing.T) { 551 | c, err := NewContainer(ContainerName()) 552 | if err != nil { 553 | t.Errorf(err.Error()) 554 | } 555 | defer c.Release() 556 | 557 | if err := c.SetLogLevel(WARN); err != nil || c.LogLevel() != WARN { 558 | t.Errorf("SetLogLevel( failed...") 559 | } 560 | } 561 | 562 | func TestSetLogFile(t *testing.T) { 563 | c, err := NewContainer(ContainerName()) 564 | if err != nil { 565 | t.Errorf(err.Error()) 566 | } 567 | defer c.Release() 568 | 569 | if err := c.SetLogFile("/tmp/" + ContainerName()); err != nil || c.LogFile() != "/tmp/"+ContainerName() { 570 | t.Errorf("SetLogFile failed...") 571 | } 572 | } 573 | 574 | func TestInitPid_Positive(t *testing.T) { 575 | c, err := NewContainer(ContainerName()) 576 | if err != nil { 577 | t.Errorf(err.Error()) 578 | } 579 | defer c.Release() 580 | 581 | if c.InitPid() == -1 { 582 | t.Errorf("InitPid failed...") 583 | } 584 | } 585 | 586 | func TestName(t *testing.T) { 587 | c, err := NewContainer(ContainerName()) 588 | if err != nil { 589 | t.Errorf(err.Error()) 590 | } 591 | defer c.Release() 592 | 593 | if c.Name() != ContainerName() { 594 | t.Errorf("Name failed...") 595 | } 596 | } 597 | 598 | func TestFreeze(t *testing.T) { 599 | c, err := NewContainer(ContainerName()) 600 | if err != nil { 601 | t.Errorf(err.Error()) 602 | } 603 | defer c.Release() 604 | 605 | if err := c.Freeze(); err != nil { 606 | t.Errorf(err.Error()) 607 | } 608 | 609 | c.Wait(FROZEN, 30*time.Second) 610 | if c.State() != FROZEN { 611 | t.Errorf("Freezing the container failed...") 612 | } 613 | } 614 | 615 | func TestUnfreeze(t *testing.T) { 616 | c, err := NewContainer(ContainerName()) 617 | if err != nil { 618 | t.Errorf(err.Error()) 619 | } 620 | defer c.Release() 621 | 622 | if err := c.Unfreeze(); err != nil { 623 | t.Errorf(err.Error()) 624 | } 625 | 626 | c.Wait(RUNNING, 30*time.Second) 627 | if !c.Running() { 628 | t.Errorf("Unfreezing the container failed...") 629 | } 630 | } 631 | 632 | func TestLoadConfigFile(t *testing.T) { 633 | c, err := NewContainer(ContainerName()) 634 | if err != nil { 635 | t.Errorf(err.Error()) 636 | } 637 | defer c.Release() 638 | 639 | if err := c.LoadConfigFile(c.ConfigFileName()); err != nil { 640 | t.Errorf(err.Error()) 641 | } 642 | } 643 | 644 | func TestSaveConfigFile(t *testing.T) { 645 | c, err := NewContainer(ContainerName()) 646 | if err != nil { 647 | t.Errorf(err.Error()) 648 | } 649 | defer c.Release() 650 | 651 | if err := c.SaveConfigFile(c.ConfigFileName()); err != nil { 652 | t.Errorf(err.Error()) 653 | } 654 | } 655 | 656 | func TestConfigItem(t *testing.T) { 657 | c, err := NewContainer(ContainerName()) 658 | if err != nil { 659 | t.Errorf(err.Error()) 660 | } 661 | defer c.Release() 662 | 663 | if c.ConfigItem("lxc.uts.name")[0] != ContainerName() { 664 | t.Errorf("ConfigItem failed...") 665 | } 666 | } 667 | 668 | func TestSetConfigItem(t *testing.T) { 669 | c, err := NewContainer(ContainerName()) 670 | if err != nil { 671 | t.Errorf(err.Error()) 672 | } 673 | defer c.Release() 674 | 675 | if err := c.SetConfigItem("lxc.uts.name", ContainerName()); err != nil { 676 | t.Errorf(err.Error()) 677 | } 678 | 679 | if c.ConfigItem("lxc.uts.name")[0] != ContainerName() { 680 | t.Errorf("ConfigItem failed...") 681 | } 682 | } 683 | 684 | func TestRunningConfigItem(t *testing.T) { 685 | c, err := NewContainer(ContainerName()) 686 | if err != nil { 687 | t.Errorf(err.Error()) 688 | } 689 | defer c.Release() 690 | 691 | if c.RunningConfigItem("lxc.network.0.type") == nil { 692 | t.Errorf("RunningConfigItem failed...") 693 | } 694 | } 695 | 696 | func TestSetCgroupItem(t *testing.T) { 697 | c, err := NewContainer(ContainerName()) 698 | if err != nil { 699 | t.Errorf(err.Error()) 700 | } 701 | defer c.Release() 702 | 703 | maxMem := c.CgroupItem("memory.max_usage_in_bytes")[0] 704 | currentMem := c.CgroupItem("memory.limit_in_bytes")[0] 705 | if maxMem == "" && currentMem == "" { 706 | // Cgroup2 handling. 707 | maxMem := c.CgroupItem("memory.peak")[0] 708 | currentMem := c.CgroupItem("memory.max")[0] 709 | 710 | if err := c.SetCgroupItem("memory.max", maxMem); err != nil { 711 | t.Errorf(err.Error()) 712 | } 713 | newMem := c.CgroupItem("memory.max")[0] 714 | 715 | if newMem == currentMem { 716 | t.Errorf("SetCgroupItem failed...") 717 | } 718 | } else { 719 | // Cgroup1 handling. 720 | if err := c.SetCgroupItem("memory.limit_in_bytes", maxMem); err != nil { 721 | t.Errorf(err.Error()) 722 | } 723 | newMem := c.CgroupItem("memory.limit_in_bytes")[0] 724 | 725 | if newMem == currentMem { 726 | t.Errorf("SetCgroupItem failed...") 727 | } 728 | } 729 | } 730 | 731 | func TestClearConfigItem(t *testing.T) { 732 | c, err := NewContainer(ContainerName()) 733 | if err != nil { 734 | t.Errorf(err.Error()) 735 | } 736 | defer c.Release() 737 | 738 | if err := c.ClearConfigItem("lxc.cap.drop"); err != nil { 739 | t.Errorf(err.Error()) 740 | } 741 | if c.ConfigItem("lxc.cap.drop")[0] != "" { 742 | t.Errorf("ClearConfigItem failed...") 743 | } 744 | } 745 | 746 | func TestConfigKeys(t *testing.T) { 747 | c, err := NewContainer(ContainerName()) 748 | if err != nil { 749 | t.Errorf(err.Error()) 750 | } 751 | defer c.Release() 752 | 753 | keys := "" 754 | if VersionAtLeast(2, 1, 0) { 755 | keys = strings.Join(c.ConfigKeys("lxc.net.0"), " ") 756 | } else { 757 | keys = strings.Join(c.ConfigKeys("lxc.network.0"), " ") 758 | } 759 | 760 | if !strings.Contains(keys, "mtu") { 761 | t.Errorf("Keys failed...") 762 | } 763 | } 764 | 765 | func TestInterfaces(t *testing.T) { 766 | c, err := NewContainer(ContainerName()) 767 | if err != nil { 768 | t.Errorf(err.Error()) 769 | } 770 | defer c.Release() 771 | 772 | if _, err := c.Interfaces(); err != nil { 773 | t.Errorf(err.Error()) 774 | } 775 | } 776 | 777 | func TestInterfaceStats(t *testing.T) { 778 | c, err := NewContainer(ContainerName()) 779 | if err != nil { 780 | t.Errorf(err.Error()) 781 | } 782 | defer c.Release() 783 | 784 | if _, err := c.InterfaceStats(); err != nil { 785 | t.Errorf(err.Error()) 786 | } 787 | } 788 | 789 | func TestMemoryUsage(t *testing.T) { 790 | c, err := NewContainer(ContainerName()) 791 | if err != nil { 792 | t.Errorf(err.Error()) 793 | } 794 | defer c.Release() 795 | 796 | if _, err := c.MemoryUsage(); err != nil { 797 | if err == ErrMemLimit { 798 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 799 | return 800 | } 801 | 802 | t.Errorf(err.Error()) 803 | } 804 | } 805 | 806 | func TestKernelMemoryUsage(t *testing.T) { 807 | c, err := NewContainer(ContainerName()) 808 | if err != nil { 809 | t.Errorf(err.Error()) 810 | } 811 | defer c.Release() 812 | 813 | if _, err := c.KernelMemoryUsage(); err != nil { 814 | if err == ErrKMemLimit { 815 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 816 | return 817 | } 818 | 819 | t.Errorf(err.Error()) 820 | } 821 | } 822 | 823 | func TestMemorySwapUsage(t *testing.T) { 824 | if !exists("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes") { 825 | t.Skip("skipping the test as it requires memory.memsw.limit_in_bytes to be set") 826 | } 827 | 828 | c, err := NewContainer(ContainerName()) 829 | if err != nil { 830 | t.Errorf(err.Error()) 831 | } 832 | defer c.Release() 833 | 834 | if _, err := c.MemorySwapUsage(); err != nil { 835 | t.Errorf(err.Error()) 836 | } 837 | } 838 | 839 | func TestBlkioUsage(t *testing.T) { 840 | c, err := NewContainer(ContainerName()) 841 | if err != nil { 842 | t.Errorf(err.Error()) 843 | } 844 | defer c.Release() 845 | 846 | if _, err := c.BlkioUsage(); err != nil { 847 | t.Errorf(err.Error()) 848 | } 849 | } 850 | 851 | func TestMemoryLimit(t *testing.T) { 852 | c, err := NewContainer(ContainerName()) 853 | if err != nil { 854 | t.Errorf(err.Error()) 855 | } 856 | defer c.Release() 857 | 858 | if _, err := c.MemoryLimit(); err != nil { 859 | if err == ErrMemLimit { 860 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 861 | return 862 | } 863 | 864 | t.Errorf(err.Error()) 865 | } 866 | } 867 | 868 | func TestSoftMemoryLimit(t *testing.T) { 869 | c, err := NewContainer(ContainerName()) 870 | if err != nil { 871 | t.Errorf(err.Error()) 872 | } 873 | defer c.Release() 874 | 875 | if _, err := c.SoftMemoryLimit(); err != nil { 876 | if err == ErrMemLimit { 877 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 878 | return 879 | } 880 | 881 | t.Errorf(err.Error()) 882 | } 883 | } 884 | 885 | func TestKernelMemoryLimit(t *testing.T) { 886 | c, err := NewContainer(ContainerName()) 887 | if err != nil { 888 | t.Errorf(err.Error()) 889 | } 890 | defer c.Release() 891 | 892 | if _, err := c.KernelMemoryLimit(); err != nil { 893 | if err == ErrKMemLimit { 894 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 895 | return 896 | } 897 | 898 | t.Errorf(err.Error()) 899 | } 900 | } 901 | 902 | func TestMemorySwapLimit(t *testing.T) { 903 | if !exists("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes") { 904 | t.Skip("skipping the test as it requires memory.memsw.limit_in_bytes to be set") 905 | } 906 | 907 | c, err := NewContainer(ContainerName()) 908 | if err != nil { 909 | t.Errorf(err.Error()) 910 | } 911 | defer c.Release() 912 | 913 | if _, err := c.MemorySwapLimit(); err != nil { 914 | t.Errorf(err.Error()) 915 | } 916 | } 917 | 918 | func TestSetMemoryLimit(t *testing.T) { 919 | c, err := NewContainer(ContainerName()) 920 | if err != nil { 921 | t.Errorf(err.Error()) 922 | } 923 | defer c.Release() 924 | 925 | oldMemLimit, err := c.MemoryLimit() 926 | if err != nil { 927 | if err == ErrMemLimit { 928 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 929 | return 930 | } 931 | 932 | t.Errorf(err.Error()) 933 | } 934 | 935 | if err := c.SetMemoryLimit(oldMemLimit * 4); err != nil { 936 | if err == ErrMemLimit { 937 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 938 | return 939 | } 940 | 941 | t.Errorf(err.Error()) 942 | } 943 | 944 | newMemLimit, err := c.MemoryLimit() 945 | if err != nil { 946 | if err == ErrMemLimit { 947 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 948 | return 949 | } 950 | 951 | t.Errorf(err.Error()) 952 | } 953 | 954 | if newMemLimit != oldMemLimit*4 { 955 | t.Errorf("SetMemoryLimit failed") 956 | } 957 | } 958 | 959 | func TestSetSoftMemoryLimit(t *testing.T) { 960 | c, err := NewContainer(ContainerName()) 961 | if err != nil { 962 | t.Errorf(err.Error()) 963 | } 964 | defer c.Release() 965 | 966 | oldMemLimit, err := c.MemoryLimit() 967 | if err != nil { 968 | if err == ErrMemLimit { 969 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 970 | return 971 | } 972 | 973 | t.Errorf(err.Error()) 974 | } 975 | 976 | if err := c.SetSoftMemoryLimit(oldMemLimit * 4); err != nil { 977 | if err == ErrMemLimit { 978 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 979 | return 980 | } 981 | 982 | t.Errorf(err.Error()) 983 | } 984 | 985 | newMemLimit, err := c.SoftMemoryLimit() 986 | if err != nil { 987 | if err == ErrMemLimit { 988 | t.Skip("Skipping test due to kernel support (maybe cgroup2?)") 989 | return 990 | } 991 | 992 | t.Errorf(err.Error()) 993 | } 994 | 995 | if newMemLimit != oldMemLimit*4 { 996 | t.Errorf("SetSoftMemoryLimit failed") 997 | } 998 | } 999 | 1000 | func TestSetKernelMemoryLimit(t *testing.T) { 1001 | t.Skip("skipping the test as it requires memory.kmem.limit_in_bytes to be set") 1002 | 1003 | c, err := NewContainer(ContainerName()) 1004 | if err != nil { 1005 | t.Errorf(err.Error()) 1006 | } 1007 | defer c.Release() 1008 | 1009 | oldMemLimit, err := c.KernelMemoryLimit() 1010 | if err != nil { 1011 | t.Errorf(err.Error()) 1012 | } 1013 | 1014 | if err := c.SetKernelMemoryLimit(oldMemLimit * 4); err != nil { 1015 | t.Errorf(err.Error()) 1016 | } 1017 | 1018 | newMemLimit, err := c.KernelMemoryLimit() 1019 | if err != nil { 1020 | t.Errorf(err.Error()) 1021 | } 1022 | 1023 | // Floats aren't exactly exact, check that we did get something smaller 1024 | if newMemLimit < oldMemLimit*3 { 1025 | t.Errorf("SetKernelMemoryLimit failed") 1026 | } 1027 | } 1028 | 1029 | func TestSetMemorySwapLimit(t *testing.T) { 1030 | if !exists("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes") { 1031 | t.Skip("skipping the test as it requires memory.memsw.limit_in_bytes to be set") 1032 | } 1033 | 1034 | c, err := NewContainer(ContainerName()) 1035 | if err != nil { 1036 | t.Errorf(err.Error()) 1037 | } 1038 | defer c.Release() 1039 | 1040 | oldMemorySwapLimit, err := c.MemorySwapLimit() 1041 | if err != nil { 1042 | t.Errorf(err.Error()) 1043 | } 1044 | if err := c.SetMemorySwapLimit(oldMemorySwapLimit / 4); err != nil { 1045 | t.Errorf(err.Error()) 1046 | } 1047 | 1048 | newMemorySwapLimit, err := c.MemorySwapLimit() 1049 | if err != nil { 1050 | t.Errorf(err.Error()) 1051 | } 1052 | 1053 | // Floats aren't exactly exact, check that we did get something smaller 1054 | if newMemorySwapLimit > oldMemorySwapLimit/3 { 1055 | t.Errorf("SetSwapLimit failed") 1056 | } 1057 | } 1058 | 1059 | func TestCPUTime(t *testing.T) { 1060 | c, err := NewContainer(ContainerName()) 1061 | if err != nil { 1062 | t.Errorf(err.Error()) 1063 | } 1064 | defer c.Release() 1065 | 1066 | if _, err := c.CPUTime(); err != nil { 1067 | t.Errorf(err.Error()) 1068 | } 1069 | } 1070 | 1071 | func TestCPUTimePerCPU(t *testing.T) { 1072 | c, err := NewContainer(ContainerName()) 1073 | if err != nil { 1074 | t.Errorf(err.Error()) 1075 | } 1076 | defer c.Release() 1077 | 1078 | if _, err := c.CPUTimePerCPU(); err != nil { 1079 | t.Errorf(err.Error()) 1080 | } 1081 | } 1082 | 1083 | func TestCPUStats(t *testing.T) { 1084 | c, err := NewContainer(ContainerName()) 1085 | if err != nil { 1086 | t.Errorf(err.Error()) 1087 | } 1088 | defer c.Release() 1089 | 1090 | if _, err := c.CPUStats(); err != nil { 1091 | t.Errorf(err.Error()) 1092 | } 1093 | } 1094 | 1095 | func TestRunCommandNoWait(t *testing.T) { 1096 | c, err := NewContainer("TestRunCommandNoWait") 1097 | if err != nil { 1098 | t.Errorf(err.Error()) 1099 | t.FailNow() 1100 | } 1101 | defer c.Release() 1102 | 1103 | if err := c.Create(template()); err != nil { 1104 | t.Errorf(err.Error()) 1105 | t.FailNow() 1106 | } 1107 | defer c.Destroy() 1108 | 1109 | err = c.Start() 1110 | if err != nil { 1111 | t.Errorf(err.Error()) 1112 | t.FailNow() 1113 | } 1114 | defer c.Stop() 1115 | 1116 | argsThree := []string{"/bin/sh", "-c", "exit 0"} 1117 | pid, err := c.RunCommandNoWait(argsThree, DefaultAttachOptions) 1118 | if err != nil { 1119 | t.Errorf(err.Error()) 1120 | t.FailNow() 1121 | } 1122 | 1123 | proc, err := os.FindProcess(pid) 1124 | if err != nil { 1125 | t.Errorf(err.Error()) 1126 | t.FailNow() 1127 | } 1128 | 1129 | procState, err := proc.Wait() 1130 | if err != nil { 1131 | t.Errorf(err.Error()) 1132 | t.FailNow() 1133 | } 1134 | if !procState.Success() { 1135 | t.Errorf("Expected success") 1136 | t.FailNow() 1137 | } 1138 | 1139 | argsThree = []string{"/bin/sh", "-c", "exit 1"} 1140 | pid, err = c.RunCommandNoWait(argsThree, DefaultAttachOptions) 1141 | if err != nil { 1142 | t.Errorf(err.Error()) 1143 | t.FailNow() 1144 | } 1145 | 1146 | proc, err = os.FindProcess(pid) 1147 | if err != nil { 1148 | t.Errorf(err.Error()) 1149 | t.FailNow() 1150 | } 1151 | 1152 | procState, err = proc.Wait() 1153 | if err != nil { 1154 | t.Errorf(err.Error()) 1155 | t.FailNow() 1156 | } 1157 | 1158 | if procState.Success() { 1159 | t.Errorf("Expected failure") 1160 | t.FailNow() 1161 | } 1162 | } 1163 | 1164 | func TestRunCommand(t *testing.T) { 1165 | c, err := NewContainer(ContainerName()) 1166 | if err != nil { 1167 | t.Errorf(err.Error()) 1168 | } 1169 | defer c.Release() 1170 | 1171 | argsThree := []string{"/bin/sh", "-c", "exit 0"} 1172 | ok, err := c.RunCommand(argsThree, DefaultAttachOptions) 1173 | if err != nil { 1174 | t.Errorf(err.Error()) 1175 | } 1176 | if !ok { 1177 | t.Errorf("Expected success") 1178 | } 1179 | 1180 | argsThree = []string{"/bin/sh", "-c", "exit 1"} 1181 | ok, err = c.RunCommand(argsThree, DefaultAttachOptions) 1182 | if err != nil { 1183 | t.Errorf(err.Error()) 1184 | } 1185 | if ok { 1186 | t.Errorf("Expected failure") 1187 | } 1188 | } 1189 | 1190 | func TestCommandWithEnv(t *testing.T) { 1191 | c, err := NewContainer(ContainerName()) 1192 | if err != nil { 1193 | t.Errorf(err.Error()) 1194 | } 1195 | defer c.Release() 1196 | 1197 | options := DefaultAttachOptions 1198 | options.Env = []string{"FOO=BAR"} 1199 | options.ClearEnv = true 1200 | 1201 | args := []string{"/bin/sh", "-c", "test $FOO = 'BAR'"} 1202 | ok, err := c.RunCommand(args, options) 1203 | if err != nil { 1204 | t.Errorf(err.Error()) 1205 | } 1206 | if !ok { 1207 | t.Errorf("Expected success") 1208 | } 1209 | } 1210 | 1211 | func TestCommandWithEnvToKeep(t *testing.T) { 1212 | c, err := NewContainer(ContainerName()) 1213 | if err != nil { 1214 | t.Errorf(err.Error()) 1215 | } 1216 | defer c.Release() 1217 | 1218 | options := DefaultAttachOptions 1219 | options.ClearEnv = true 1220 | options.EnvToKeep = []string{"USER"} 1221 | 1222 | args := []string{"/bin/sh", "-c", fmt.Sprintf("test $USER = '%s'", os.Getenv("USER"))} 1223 | ok, err := c.RunCommand(args, DefaultAttachOptions) 1224 | if err != nil { 1225 | t.Errorf(err.Error()) 1226 | } 1227 | if !ok { 1228 | t.Errorf("Expected success") 1229 | } 1230 | } 1231 | 1232 | func TestCommandWithCwd(t *testing.T) { 1233 | c, err := NewContainer(ContainerName()) 1234 | if err != nil { 1235 | t.Errorf(err.Error()) 1236 | } 1237 | defer c.Release() 1238 | 1239 | options := DefaultAttachOptions 1240 | options.Cwd = "/tmp" 1241 | 1242 | args := []string{"/bin/sh", "-c", "test `pwd` = /tmp"} 1243 | ok, err := c.RunCommand(args, options) 1244 | if err != nil { 1245 | t.Errorf(err.Error()) 1246 | } 1247 | if !ok { 1248 | t.Errorf("Expected success") 1249 | } 1250 | } 1251 | 1252 | func TestCommandWithUIDGID(t *testing.T) { 1253 | c, err := NewContainer(ContainerName()) 1254 | if err != nil { 1255 | t.Errorf(err.Error()) 1256 | } 1257 | defer c.Release() 1258 | 1259 | options := DefaultAttachOptions 1260 | options.UID = 1000 1261 | options.GID = 1000 1262 | 1263 | args := []string{"/bin/sh", "-c", "test `id -u` = 1000 && test `id -g` = 1000"} 1264 | ok, err := c.RunCommand(args, options) 1265 | if err != nil { 1266 | t.Errorf(err.Error()) 1267 | } 1268 | if !ok { 1269 | t.Errorf("Expected success") 1270 | } 1271 | } 1272 | 1273 | func TestCommandWithArch(t *testing.T) { 1274 | uname := syscall.Utsname{} 1275 | if err := syscall.Uname(&uname); err != nil { 1276 | t.Errorf(err.Error()) 1277 | } 1278 | 1279 | arch := "" 1280 | for _, c := range uname.Machine { 1281 | if c == 0 { 1282 | break 1283 | } 1284 | arch += string(byte(c)) 1285 | } 1286 | 1287 | if arch != "x86_64" && arch != "i686" { 1288 | t.Skip("skipping architecture test, not on x86") 1289 | } 1290 | 1291 | c, err := NewContainer(ContainerName()) 1292 | if err != nil { 1293 | t.Errorf(err.Error()) 1294 | } 1295 | defer c.Release() 1296 | 1297 | options := DefaultAttachOptions 1298 | options.Arch = X86 1299 | 1300 | args := []string{"/bin/sh", "-c", "test `uname -m` = i686"} 1301 | ok, err := c.RunCommand(args, options) 1302 | if err != nil { 1303 | t.Errorf(err.Error()) 1304 | } 1305 | if !ok { 1306 | t.Errorf("Expected success") 1307 | } 1308 | } 1309 | 1310 | func TestConsoleFd(t *testing.T) { 1311 | c, err := NewContainer(ContainerName()) 1312 | if err != nil { 1313 | t.Errorf(err.Error()) 1314 | } 1315 | defer c.Release() 1316 | 1317 | if _, err := c.ConsoleFd(0); err != nil { 1318 | t.Errorf(err.Error()) 1319 | } 1320 | } 1321 | 1322 | func TestIPAddress(t *testing.T) { 1323 | c, err := NewContainer(ContainerName()) 1324 | if err != nil { 1325 | t.Errorf(err.Error()) 1326 | } 1327 | defer c.Release() 1328 | 1329 | if _, err := c.IPAddress("lo"); err != nil { 1330 | t.Errorf(err.Error()) 1331 | } 1332 | } 1333 | 1334 | func TestIPv4Address(t *testing.T) { 1335 | c, err := NewContainer(ContainerName()) 1336 | if err != nil { 1337 | t.Errorf(err.Error()) 1338 | } 1339 | defer c.Release() 1340 | 1341 | if _, err := c.IPv4Address("lo"); err != nil { 1342 | t.Errorf(err.Error()) 1343 | } 1344 | } 1345 | 1346 | func TestIPv46ddress(t *testing.T) { 1347 | if !ipv6() { 1348 | t.Skip("skipping test since lxc bridge does not have ipv6 address") 1349 | } 1350 | 1351 | c, err := NewContainer(ContainerName()) 1352 | if err != nil { 1353 | t.Errorf(err.Error()) 1354 | } 1355 | defer c.Release() 1356 | 1357 | if _, err := c.IPv6Address("lo"); err != nil { 1358 | t.Errorf(err.Error()) 1359 | } 1360 | } 1361 | 1362 | func TestAddDeviceNode(t *testing.T) { 1363 | if unprivileged() { 1364 | t.Skip("skipping test in unprivileged mode.") 1365 | } 1366 | 1367 | if !exists("/dev/network_latency") { 1368 | t.Skip("skipping the test as it requires/dev/network_latency") 1369 | } 1370 | 1371 | c, err := NewContainer(ContainerName()) 1372 | if err != nil { 1373 | t.Errorf(err.Error()) 1374 | } 1375 | defer c.Release() 1376 | 1377 | if err := c.AddDeviceNode("/dev/network_latency"); err != nil { 1378 | t.Errorf(err.Error()) 1379 | } 1380 | } 1381 | 1382 | func TestRemoveDeviceNode(t *testing.T) { 1383 | if unprivileged() { 1384 | t.Skip("skipping test in unprivileged mode.") 1385 | } 1386 | 1387 | if !exists("/dev/network_latency") { 1388 | t.Skip("skipping the test as it requires/dev/network_latency") 1389 | } 1390 | 1391 | c, err := NewContainer(ContainerName()) 1392 | if err != nil { 1393 | t.Errorf(err.Error()) 1394 | } 1395 | defer c.Release() 1396 | 1397 | if err := c.RemoveDeviceNode("/dev/network_latency"); err != nil { 1398 | t.Errorf(err.Error()) 1399 | } 1400 | } 1401 | 1402 | func TestIPv4Addresses(t *testing.T) { 1403 | c, err := NewContainer(ContainerName()) 1404 | if err != nil { 1405 | t.Errorf(err.Error()) 1406 | } 1407 | defer c.Release() 1408 | 1409 | // Wait for IP configuration. 1410 | time.Sleep(5 * time.Second) 1411 | 1412 | if _, err := c.IPv4Addresses(); err != nil { 1413 | t.Errorf(err.Error()) 1414 | } 1415 | } 1416 | 1417 | func TestIPv6Addresses(t *testing.T) { 1418 | c, err := NewContainer(ContainerName()) 1419 | if err != nil { 1420 | t.Errorf(err.Error()) 1421 | } 1422 | defer c.Release() 1423 | 1424 | // Wait for IP configuration. 1425 | time.Sleep(5 * time.Second) 1426 | 1427 | if _, err := c.IPv6Addresses(); err != nil { 1428 | t.Errorf(err.Error()) 1429 | } 1430 | } 1431 | 1432 | func TestReboot(t *testing.T) { 1433 | c, err := NewContainer(ContainerName()) 1434 | if err != nil { 1435 | t.Errorf(err.Error()) 1436 | } 1437 | defer c.Release() 1438 | 1439 | if err := c.Reboot(); err != nil { 1440 | t.Errorf("Rebooting the container failed...") 1441 | } 1442 | c.Wait(RUNNING, 30*time.Second) 1443 | } 1444 | 1445 | func TestConcurrentShutdown(t *testing.T) { 1446 | t.Skip("Skipping concurrent tests for now") 1447 | 1448 | defer runtime.GOMAXPROCS(runtime.NumCPU()) 1449 | 1450 | var wg sync.WaitGroup 1451 | 1452 | for i := 0; i < 10; i++ { 1453 | wg.Add(1) 1454 | go func(i int) { 1455 | c, err := NewContainer(strconv.Itoa(i)) 1456 | if err != nil { 1457 | t.Errorf(err.Error()) 1458 | } 1459 | defer c.Release() 1460 | 1461 | if err := c.Shutdown(30 * time.Second); err != nil { 1462 | t.Errorf(err.Error()) 1463 | } 1464 | 1465 | c.Wait(STOPPED, 30*time.Second) 1466 | if c.Running() { 1467 | t.Errorf("Shutting down the container failed...") 1468 | } 1469 | 1470 | wg.Done() 1471 | }(i) 1472 | } 1473 | wg.Wait() 1474 | } 1475 | 1476 | func TestShutdown(t *testing.T) { 1477 | c, err := NewContainer(ContainerName()) 1478 | if err != nil { 1479 | t.Errorf(err.Error()) 1480 | } 1481 | defer c.Release() 1482 | 1483 | if err := c.Shutdown(30 * time.Second); err != nil { 1484 | t.Errorf(err.Error()) 1485 | } 1486 | 1487 | c.Wait(STOPPED, 30*time.Second) 1488 | if c.Running() { 1489 | t.Errorf("Shutting down the container failed...") 1490 | } 1491 | } 1492 | 1493 | func TestStop(t *testing.T) { 1494 | c, err := NewContainer(ContainerName()) 1495 | if err != nil { 1496 | t.Errorf(err.Error()) 1497 | } 1498 | defer c.Release() 1499 | 1500 | if err := c.Start(); err != nil { 1501 | t.Errorf(err.Error()) 1502 | } 1503 | 1504 | if err := c.Stop(); err != nil { 1505 | t.Errorf(err.Error()) 1506 | } 1507 | 1508 | c.Wait(STOPPED, 30*time.Second) 1509 | if c.Running() { 1510 | t.Errorf("Stopping the container failed...") 1511 | } 1512 | } 1513 | 1514 | func TestDestroySnapshot(t *testing.T) { 1515 | c, err := NewContainer(ContainerName()) 1516 | if err != nil { 1517 | t.Errorf(err.Error()) 1518 | } 1519 | defer c.Release() 1520 | 1521 | snapshot := Snapshot{Name: DefaultSnapshotName} 1522 | if err := c.DestroySnapshot(snapshot); err != nil { 1523 | t.Errorf(err.Error()) 1524 | } 1525 | } 1526 | 1527 | func TestDestroyAllSnapshots(t *testing.T) { 1528 | c, err := NewContainer(ContainerName()) 1529 | if err != nil { 1530 | t.Errorf(err.Error()) 1531 | } 1532 | defer c.Release() 1533 | 1534 | if err := c.DestroyAllSnapshots(); err != nil { 1535 | if err == ErrNotSupported { 1536 | t.Skip("skipping due to lxc version.") 1537 | } 1538 | t.Errorf(err.Error()) 1539 | } 1540 | } 1541 | 1542 | func TestDestroy(t *testing.T) { 1543 | if supported("overlayfs") || supported("overlay") { 1544 | c, err := NewContainer(ContainerCloneOverlayName()) 1545 | if err != nil { 1546 | t.Errorf(err.Error()) 1547 | } 1548 | defer c.Release() 1549 | 1550 | if err := c.Destroy(); err != nil { 1551 | t.Errorf(err.Error()) 1552 | } 1553 | } 1554 | 1555 | c, err := NewContainer(ContainerCloneName()) 1556 | if err != nil { 1557 | t.Errorf(err.Error()) 1558 | } 1559 | defer c.Release() 1560 | 1561 | if err := c.Destroy(); err != nil { 1562 | t.Errorf(err.Error()) 1563 | } 1564 | 1565 | c, err = NewContainer(ContainerRestoreName()) 1566 | if err != nil { 1567 | t.Errorf(err.Error()) 1568 | } 1569 | defer c.Release() 1570 | 1571 | if c.Defined() { 1572 | if err := c.Destroy(); err != nil { 1573 | t.Errorf(err.Error()) 1574 | } 1575 | } 1576 | 1577 | c, err = NewContainer(ContainerName()) 1578 | if err != nil { 1579 | t.Errorf(err.Error()) 1580 | } 1581 | defer c.Release() 1582 | 1583 | if err := c.Destroy(); err != nil { 1584 | t.Errorf(err.Error()) 1585 | } 1586 | } 1587 | 1588 | func TestConcurrentDestroy(t *testing.T) { 1589 | t.Skip("Skipping concurrent tests for now") 1590 | 1591 | defer runtime.GOMAXPROCS(runtime.NumCPU()) 1592 | 1593 | var wg sync.WaitGroup 1594 | 1595 | for i := 0; i < 10; i++ { 1596 | wg.Add(1) 1597 | go func(i int) { 1598 | c, err := NewContainer(strconv.Itoa(i)) 1599 | if err != nil { 1600 | t.Errorf(err.Error()) 1601 | } 1602 | defer c.Release() 1603 | 1604 | // sleep for a while to simulate some work 1605 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(250))) 1606 | 1607 | if err := c.Destroy(); err != nil { 1608 | t.Errorf(err.Error()) 1609 | } 1610 | wg.Done() 1611 | }(i) 1612 | } 1613 | wg.Wait() 1614 | } 1615 | 1616 | func TestBackendStore(t *testing.T) { 1617 | var X struct { 1618 | store BackendStore 1619 | } 1620 | 1621 | if X.store.String() != "" { 1622 | t.Error("zero value of BackendStore should be invalid") 1623 | } 1624 | } 1625 | 1626 | func TestState(t *testing.T) { 1627 | var X struct { 1628 | state State 1629 | } 1630 | 1631 | if X.state.String() != "" { 1632 | t.Error("zero value of State should be invalid") 1633 | } 1634 | } 1635 | 1636 | func TestSupportedConfigItems(t *testing.T) { 1637 | if VersionAtLeast(2, 1, 0) { 1638 | if !IsSupportedConfigItem("lxc.arch") { 1639 | t.Errorf("IsSupportedConfigItem failed to detect \"lxc.arch\" as supported config item...") 1640 | } 1641 | 1642 | if IsSupportedConfigItem("lxc.nonsense") { 1643 | t.Errorf("IsSupportedConfigItem failed to detect \"lxc.nonsense\" as unsupported config item...") 1644 | } 1645 | } 1646 | } 1647 | 1648 | func TestRuntimeLiblxcVersionAtLeast(t *testing.T) { 1649 | type args struct { 1650 | version string 1651 | major int 1652 | minor int 1653 | micro int 1654 | } 1655 | tests := []struct { 1656 | name string 1657 | args args 1658 | want bool 1659 | }{ 1660 | { 1661 | name: "Check 5.0.0 is at least 2.1.0 returns true", 1662 | args: args{ 1663 | version: "5.0.0", 1664 | major: 2, 1665 | minor: 1, 1666 | micro: 0, 1667 | }, 1668 | want: true, 1669 | }, 1670 | { 1671 | name: "Check 5.0.0-devel is at least 2.1.0", 1672 | args: args{ 1673 | version: "5.0.0-devel", 1674 | major: 2, 1675 | minor: 1, 1676 | micro: 0, 1677 | }, 1678 | want: true, 1679 | }, 1680 | { 1681 | name: "Check 5.0.0~git2209-g5a7b9ce67-0ubuntu1 is at least 2.1.0", 1682 | args: args{ 1683 | version: "5.0.0~git2209-g5a7b9ce67-0ubuntu1", 1684 | major: 2, 1685 | minor: 1, 1686 | micro: 0, 1687 | }, 1688 | want: true, 1689 | }, 1690 | { 1691 | name: "Check 1.0.0 is not at least 2.1.0 returns true", 1692 | args: args{ 1693 | version: "1.0.0", 1694 | major: 2, 1695 | minor: 1, 1696 | micro: 0, 1697 | }, 1698 | want: false, 1699 | }, 1700 | } 1701 | for _, tt := range tests { 1702 | t.Run(tt.name, func(t *testing.T) { 1703 | if got := RuntimeLiblxcVersionAtLeast(tt.args.version, tt.args.major, tt.args.minor, tt.args.micro); got != tt.want { 1704 | t.Errorf("RuntimeLiblxcVersionAtLeast() = %v, want %v", got, tt.want) 1705 | } 1706 | }) 1707 | } 1708 | } 1709 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package lxc 9 | 10 | import ( 11 | "os" 12 | ) 13 | 14 | // AttachOptions type is used for defining various attach options. 15 | type AttachOptions struct { 16 | 17 | // Specify the namespaces to attach to, as OR'ed list of clone flags (syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS ...). 18 | Namespaces int 19 | 20 | // Specify the architecture which the kernel should appear to be running as to the command executed. 21 | Arch Personality 22 | 23 | // Cwd specifies the working directory of the command. 24 | Cwd string 25 | 26 | // UID specifies the user id to run as. 27 | UID int 28 | 29 | // GID specifies the group id to run as. 30 | GID int 31 | 32 | // Groups specifies the list of additional group ids to run with. 33 | Groups []int 34 | 35 | // If ClearEnv is true the environment is cleared before running the command. 36 | ClearEnv bool 37 | 38 | // Env specifies the environment of the process. 39 | Env []string 40 | 41 | // EnvToKeep specifies the environment of the process when ClearEnv is true. 42 | EnvToKeep []string 43 | 44 | // StdinFd specifies the fd to read input from. 45 | StdinFd uintptr 46 | 47 | // StdoutFd specifies the fd to write output to. 48 | StdoutFd uintptr 49 | 50 | // StderrFd specifies the fd to write error output to. 51 | StderrFd uintptr 52 | 53 | // RemountSysProc remounts /sys and /proc for the executed command. 54 | // This is required to reflect the container (PID) namespace context 55 | // if the command does not attach to the container's mount namespace. 56 | RemountSysProc bool 57 | 58 | // ElevatedPrivileges runs the command with elevated privileges. 59 | // The capabilities, cgroup and security module restrictions of the container are not applied. 60 | // WARNING: This may leak privileges into the container. 61 | ElevatedPrivileges bool 62 | } 63 | 64 | // DefaultAttachOptions is a convenient set of options to be used. 65 | var DefaultAttachOptions = AttachOptions{ 66 | Namespaces: -1, 67 | Arch: -1, 68 | Cwd: "/", 69 | UID: -1, 70 | GID: -1, 71 | Groups: nil, 72 | ClearEnv: false, 73 | Env: nil, 74 | EnvToKeep: nil, 75 | StdinFd: os.Stdin.Fd(), 76 | StdoutFd: os.Stdout.Fd(), 77 | StderrFd: os.Stderr.Fd(), 78 | RemountSysProc: false, 79 | ElevatedPrivileges: false, 80 | } 81 | 82 | // TemplateOptions type is used for defining various template options. 83 | type TemplateOptions struct { 84 | 85 | // Template specifies the name of the template. 86 | Template string 87 | 88 | // Backend specifies the type of the backend. 89 | Backend BackendStore 90 | 91 | BackendSpecs *BackendStoreSpecs 92 | 93 | // Distro specifies the name of the distribution. 94 | Distro string 95 | 96 | // Release specifies the name/version of the distribution. 97 | Release string 98 | 99 | // Arch specified the architecture of the container. 100 | Arch string 101 | 102 | // Variant specifies the variant of the image (default: "default"). 103 | Variant string 104 | 105 | // Image server (default: "images.linuxcontainers.org"). 106 | Server string 107 | 108 | // GPG keyid (default: 0x...). 109 | KeyID string 110 | 111 | // GPG keyserver to use. 112 | KeyServer string 113 | 114 | // Disable GPG validation (not recommended). 115 | DisableGPGValidation bool 116 | 117 | // Flush the local copy (if present). 118 | FlushCache bool 119 | 120 | // Force the use of the local copy even if expired. 121 | ForceCache bool 122 | 123 | // ExtraArgs provides a way to specify template specific args. 124 | ExtraArgs []string 125 | } 126 | 127 | // BackendStoreSpecs represents a LXC storage backend. 128 | type BackendStoreSpecs struct { 129 | FSType string 130 | FSSize uint64 131 | Dir *string 132 | ZFS struct { 133 | Root string 134 | } 135 | LVM struct { 136 | VG, LV, Thinpool string 137 | } 138 | RBD struct { 139 | Name, Pool string 140 | } 141 | } 142 | 143 | // DownloadTemplateOptions is a convenient set of options for "download" template. 144 | var DownloadTemplateOptions = TemplateOptions{ 145 | Template: "download", 146 | Distro: "ubuntu", 147 | Release: "trusty", 148 | Arch: "amd64", 149 | } 150 | 151 | // BusyboxTemplateOptions is a convenient set of options for "busybox" template. 152 | var BusyboxTemplateOptions = TemplateOptions{ 153 | Template: "busybox", 154 | } 155 | 156 | // UbuntuTemplateOptions is a convenient set of options for "ubuntu" template. 157 | var UbuntuTemplateOptions = TemplateOptions{ 158 | Template: "ubuntu", 159 | } 160 | 161 | // ConsoleOptions type is used for defining various console options. 162 | type ConsoleOptions struct { 163 | 164 | // Tty number to attempt to allocate, -1 to allocate the first available tty, or 0 to allocate the console. 165 | Tty int 166 | 167 | // StdinFd specifies the fd to read input from. 168 | StdinFd uintptr 169 | 170 | // StdoutFd specifies the fd to write output to. 171 | StdoutFd uintptr 172 | 173 | // StderrFd specifies the fd to write error output to. 174 | StderrFd uintptr 175 | 176 | // EscapeCharacter (a means , b maens ). 177 | EscapeCharacter rune 178 | } 179 | 180 | // DefaultConsoleOptions is a convenient set of options to be used. 181 | var DefaultConsoleOptions = ConsoleOptions{ 182 | Tty: -1, 183 | StdinFd: os.Stdin.Fd(), 184 | StdoutFd: os.Stdout.Fd(), 185 | StderrFd: os.Stderr.Fd(), 186 | EscapeCharacter: 'a', 187 | } 188 | 189 | // CloneOptions type is used for defining various clone options. 190 | type CloneOptions struct { 191 | 192 | // Backend specifies the type of the backend. 193 | Backend BackendStore 194 | 195 | // lxcpath in which to create the new container. If not set the original container's lxcpath will be used. 196 | ConfigPath string 197 | 198 | // Do not change the hostname of the container (in the root filesystem). 199 | KeepName bool 200 | 201 | // Use the same MAC address as the original container, rather than generating a new random one. 202 | KeepMAC bool 203 | 204 | // Create a snapshot rather than copy. 205 | Snapshot bool 206 | } 207 | 208 | // DefaultCloneOptions is a convenient set of options to be used. 209 | var DefaultCloneOptions = CloneOptions{ 210 | Backend: Directory, 211 | } 212 | 213 | // CheckpointOptions type is used for defining checkpoint options for CRIU. 214 | type CheckpointOptions struct { 215 | Directory string 216 | Stop bool 217 | Verbose bool 218 | } 219 | 220 | // RestoreOptions type is used for defining restore options for CRIU. 221 | type RestoreOptions struct { 222 | Directory string 223 | Verbose bool 224 | } 225 | 226 | // MigrateOptions type is used for defining migrate options. 227 | type MigrateOptions struct { 228 | Directory string 229 | PredumpDir string 230 | ActionScript string 231 | Verbose bool 232 | Stop bool 233 | PreservesInodes bool 234 | GhostLimit uint64 235 | FeaturesToCheck CriuFeatures 236 | } 237 | 238 | // ConsoleLogOptions type is used for defining console log options. 239 | type ConsoleLogOptions struct { 240 | ClearLog bool 241 | ReadLog bool 242 | ReadMax uint64 243 | WriteToLogFile bool 244 | } 245 | -------------------------------------------------------------------------------- /type.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package lxc 9 | 10 | // #include 11 | import "C" 12 | 13 | import ( 14 | "errors" 15 | "fmt" 16 | "strconv" 17 | "strings" 18 | "unicode" 19 | ) 20 | 21 | // Verbosity type 22 | type Verbosity int 23 | 24 | const ( 25 | // Quiet makes some API calls not to write anything to stdout 26 | Quiet Verbosity = 1 << iota 27 | // Verbose makes some API calls write to stdout 28 | Verbose 29 | ) 30 | 31 | // BackendStore type specifies possible backend types. 32 | type BackendStore int 33 | 34 | const ( 35 | // Btrfs backendstore type 36 | Btrfs BackendStore = iota + 1 37 | // Directory backendstore type 38 | Directory 39 | // LVM backendstore type 40 | LVM 41 | // ZFS backendstore type 42 | ZFS 43 | // Aufs backendstore type 44 | Aufs 45 | // Overlayfs backendstore type 46 | Overlayfs 47 | // Loopback backendstore type 48 | Loopback 49 | // Best backendstore type 50 | Best 51 | ) 52 | 53 | // BackendStore as string 54 | func (t BackendStore) String() string { 55 | switch t { 56 | case Directory: 57 | return "dir" 58 | case ZFS: 59 | return "zfs" 60 | case Btrfs: 61 | return "btrfs" 62 | case LVM: 63 | return "lvm" 64 | case Aufs: 65 | return "aufs" 66 | case Overlayfs: 67 | return "overlayfs" 68 | case Loopback: 69 | return "loop" 70 | case Best: 71 | return "best" 72 | } 73 | return "" 74 | } 75 | 76 | var backendStoreMap = map[string]BackendStore{ 77 | "dir": Directory, 78 | "zfs": ZFS, 79 | "btrfs": Btrfs, 80 | "lvm": LVM, 81 | "aufs": Aufs, 82 | "overlayfs": Overlayfs, 83 | "loopback": Loopback, 84 | "best": Best, 85 | } 86 | 87 | // Set is the method to set the flag value, part of the flag.Value interface. 88 | func (t *BackendStore) Set(value string) error { 89 | backend, ok := backendStoreMap[value] 90 | if ok { 91 | *t = backend 92 | return nil 93 | } 94 | return ErrUnknownBackendStore 95 | } 96 | 97 | // State type specifies possible container states. 98 | type State int 99 | 100 | const ( 101 | // STOPPED means container is not running 102 | STOPPED State = iota + 1 103 | // STARTING means container is starting 104 | STARTING 105 | // RUNNING means container is running 106 | RUNNING 107 | // STOPPING means container is stopping 108 | STOPPING 109 | // ABORTING means container is aborting 110 | ABORTING 111 | // FREEZING means container is freezing 112 | FREEZING 113 | // FROZEN means containe is frozen 114 | FROZEN 115 | // THAWED means container is thawed 116 | THAWED 117 | ) 118 | 119 | // StateMap provides the mapping betweens the state names and states 120 | var StateMap = map[string]State{ 121 | "STOPPED": STOPPED, 122 | "STARTING": STARTING, 123 | "RUNNING": RUNNING, 124 | "STOPPING": STOPPING, 125 | "ABORTING": ABORTING, 126 | "FREEZING": FREEZING, 127 | "FROZEN": FROZEN, 128 | "THAWED": THAWED, 129 | } 130 | 131 | // State as string 132 | func (t State) String() string { 133 | switch t { 134 | case STOPPED: 135 | return "STOPPED" 136 | case STARTING: 137 | return "STARTING" 138 | case RUNNING: 139 | return "RUNNING" 140 | case STOPPING: 141 | return "STOPPING" 142 | case ABORTING: 143 | return "ABORTING" 144 | case FREEZING: 145 | return "FREEZING" 146 | case FROZEN: 147 | return "FROZEN" 148 | case THAWED: 149 | return "THAWED" 150 | } 151 | return "" 152 | } 153 | 154 | // Taken from http://golang.org/doc/effective_go.html#constants 155 | 156 | // ByteSize type 157 | type ByteSize float64 158 | 159 | const ( 160 | // B - byte 161 | B = iota 162 | 163 | // KB - kilobyte 164 | KB ByteSize = 1 << (10 * iota) 165 | 166 | // MB - megabyte 167 | MB 168 | 169 | // GB - gigabyte 170 | GB 171 | 172 | // TB - terabyte 173 | TB 174 | 175 | // PB - petabyte 176 | PB 177 | 178 | // EB - exabyte 179 | EB 180 | 181 | // ZB - zettabyte 182 | ZB 183 | 184 | // YB - yottabyte 185 | YB 186 | ) 187 | 188 | func (b ByteSize) String() string { 189 | switch { 190 | case b >= YB: 191 | return fmt.Sprintf("%.2fYB", b/YB) 192 | case b >= ZB: 193 | return fmt.Sprintf("%.2fZB", b/ZB) 194 | case b >= EB: 195 | return fmt.Sprintf("%.2fEB", b/EB) 196 | case b >= PB: 197 | return fmt.Sprintf("%.2fPB", b/PB) 198 | case b >= TB: 199 | return fmt.Sprintf("%.2fTB", b/TB) 200 | case b >= GB: 201 | return fmt.Sprintf("%.2fGB", b/GB) 202 | case b >= MB: 203 | return fmt.Sprintf("%.2fMB", b/MB) 204 | case b >= KB: 205 | return fmt.Sprintf("%.2fKB", b/KB) 206 | } 207 | return fmt.Sprintf("%.2fB", b) 208 | } 209 | 210 | // Used to convert user input to ByteSize 211 | var unitMap = map[string]ByteSize{ 212 | "B": B, 213 | "BYTE": B, 214 | "BYTES": B, 215 | 216 | "KB": KB, 217 | "KILOBYTE": KB, 218 | "KILOBYTES": KB, 219 | 220 | "MB": MB, 221 | "MEGABYTE": MB, 222 | "MEGABYTES": MB, 223 | 224 | "GB": GB, 225 | "GIGABYTE": GB, 226 | "GIGABYTES": GB, 227 | 228 | "TB": TB, 229 | "TERABYTE": TB, 230 | "TERABYTES": TB, 231 | 232 | "PB": PB, 233 | "PETABYTE": PB, 234 | "PETABYTES": PB, 235 | 236 | "EB": EB, 237 | "EXABYTE": EB, 238 | "EXABYTES": EB, 239 | } 240 | 241 | // Inspired from https://github.com/inhies/go-bytesize 242 | 243 | // ParseBytes parses a byte size string. A byte size string is a number followed by 244 | // a unit suffix, such as "1024B" or "1 MB". Valid byte units are "B", "KB", 245 | // "MB", "GB", "TB", "PB" and "EB". You can also use the long 246 | // format of units, such as "kilobyte" or "kilobytes". 247 | func ParseBytes(s string) (ByteSize, error) { 248 | // Remove leading and trailing space 249 | s = strings.TrimSpace(s) 250 | 251 | split := make([]string, 0) 252 | for i, r := range s { 253 | if !unicode.IsDigit(r) { 254 | // Split the string by digit and size designator, remove space 255 | split = append(split, strings.TrimSpace(string(s[:i]))) 256 | split = append(split, strings.TrimSpace(string(s[i:]))) 257 | break 258 | } 259 | } 260 | 261 | // Check to see if we split successfully 262 | if len(split) != 2 { 263 | return 0, errors.New("Unrecognized size suffix") 264 | } 265 | 266 | // Check for MB, MEGABYTE, and MEGABYTES 267 | unit, ok := unitMap[strings.ToUpper(split[1])] 268 | if !ok { 269 | return 0, errors.New("Unrecognized size suffix " + split[1]) 270 | 271 | } 272 | 273 | value, err := strconv.ParseFloat(split[0], 64) 274 | if err != nil { 275 | return 0, err 276 | } 277 | 278 | bytesize := ByteSize(value * float64(unit)) 279 | return bytesize, nil 280 | } 281 | 282 | // LogLevel type specifies possible log levels. 283 | type LogLevel int 284 | 285 | const ( 286 | // TRACE priority 287 | TRACE LogLevel = iota 288 | // DEBUG priority 289 | DEBUG 290 | // INFO priority 291 | INFO 292 | // NOTICE priority 293 | NOTICE 294 | // WARN priority 295 | WARN 296 | // ERROR priority 297 | ERROR 298 | // CRIT priority 299 | CRIT 300 | // ALERT priority 301 | ALERT 302 | // FATAL priority 303 | FATAL 304 | ) 305 | 306 | var logLevelMap = map[string]LogLevel{ 307 | "TRACE": TRACE, 308 | "DEBUG": DEBUG, 309 | "INFO": INFO, 310 | "NOTICE": NOTICE, 311 | "WARN": WARN, 312 | "ERROR": ERROR, 313 | "CRIT": CRIT, 314 | "ALERT": ALERT, 315 | "FATAL": FATAL, 316 | } 317 | 318 | func (l LogLevel) String() string { 319 | switch l { 320 | case TRACE: 321 | return "TRACE" 322 | case DEBUG: 323 | return "DEBUG" 324 | case INFO: 325 | return "INFO" 326 | case NOTICE: 327 | return "NOTICE" 328 | case WARN: 329 | return "WARN" 330 | case ERROR: 331 | return "ERROR" 332 | case CRIT: 333 | return "CRIT" 334 | case ALERT: 335 | return "ALERT" 336 | case FATAL: 337 | return "FATAL" 338 | } 339 | return "NOTSET" 340 | } 341 | 342 | // Personality allows to set the architecture for the container. 343 | type Personality int64 344 | 345 | const ( 346 | // X86 - Intel 32bit 347 | X86 Personality = 0x0008 348 | 349 | // X86_64 - Intel 64bit 350 | X86_64 = 0x0000 351 | ) 352 | 353 | const ( 354 | // MIGRATE_PRE_DUMP - pre-dump live migration phase 355 | MIGRATE_PRE_DUMP = 0 356 | 357 | // MIGRATE_DUMP - main live migration phase 358 | MIGRATE_DUMP = 1 359 | 360 | // MIGRATE_RESTORE - post migration phase 361 | MIGRATE_RESTORE = 2 362 | 363 | // MIGRATE_FEATURE_CHECK - migration feature check 364 | MIGRATE_FEATURE_CHECK = 3 365 | ) 366 | 367 | // CriuFeatures represents a set of CRIU features 368 | type CriuFeatures uint64 369 | 370 | const ( 371 | // FEATURE_MEM_TRACK - memory tracking support 372 | FEATURE_MEM_TRACK CriuFeatures = 1 << iota 373 | 374 | // FEATURE_LAZY_PAGES - lazy pages support 375 | FEATURE_LAZY_PAGES 376 | ) 377 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2013, 2014, The Go-LXC Authors. All rights reserved. 2 | // Use of this source code is governed by a LGPLv2.1 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build linux && cgo 6 | // +build linux,cgo 7 | 8 | package lxc 9 | 10 | /* 11 | #include 12 | #include 13 | 14 | static char** makeCharArray(size_t size) { 15 | // caller checks return value 16 | return calloc(size, sizeof(char*)); 17 | } 18 | 19 | static void setArrayString(char **array, char *string, size_t n) { 20 | array[n] = string; 21 | } 22 | 23 | static void freeCharArray(char **array, size_t size) { 24 | size_t i; 25 | for (i = 0; i < size; i++) { 26 | free(array[i]); 27 | } 28 | free(array); 29 | } 30 | 31 | static void freeSnapshotArray(struct lxc_snapshot *s, size_t size) { 32 | size_t i; 33 | for (i = 0; i < size; i++) { 34 | s[i].free(&s[i]); 35 | } 36 | free(s); 37 | } 38 | 39 | static size_t getArrayLength(char **array) { 40 | char **p; 41 | size_t size = 0; 42 | for (p = (char **)array; *p; p++) { 43 | size++; 44 | } 45 | return size; 46 | } 47 | */ 48 | import "C" 49 | 50 | import ( 51 | "unsafe" 52 | ) 53 | 54 | func makeNullTerminatedArgs(args []string) **C.char { 55 | cparams := C.makeCharArray(C.size_t(len(args) + 1)) 56 | if cparams == nil { 57 | return nil 58 | } 59 | 60 | for i := 0; i < len(args); i++ { 61 | C.setArrayString(cparams, C.CString(args[i]), C.size_t(i)) 62 | } 63 | C.setArrayString(cparams, nil, C.size_t(len(args))) 64 | 65 | return cparams 66 | } 67 | 68 | func freeNullTerminatedArgs(cArgs **C.char, length int) { 69 | C.freeCharArray(cArgs, C.size_t(length+1)) 70 | } 71 | 72 | func convertArgs(cArgs **C.char) []string { 73 | if cArgs == nil { 74 | return nil 75 | } 76 | 77 | return convertNArgs(cArgs, int(C.getArrayLength(cArgs))) 78 | } 79 | 80 | func convertNArgs(cArgs **C.char, size int) []string { 81 | if cArgs == nil || size <= 0 { 82 | return nil 83 | } 84 | 85 | tmpslice := (*[(1 << 29) - 1]*C.char)(unsafe.Pointer(cArgs))[:size:size] 86 | result := make([]string, size) 87 | for i, s := range tmpslice { 88 | result[i] = C.GoString(s) 89 | } 90 | return result 91 | } 92 | 93 | func freeSnapshots(snapshots *C.struct_lxc_snapshot, size int) { 94 | C.freeSnapshotArray(snapshots, C.size_t(size)) 95 | } 96 | --------------------------------------------------------------------------------