├── .gitignore ├── LICENSE ├── README.md ├── config.sh.example └── main.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> Linux 2 | *~ 3 | 4 | # KDE directory preferences 5 | .directory 6 | 7 | # Linux trash folder which might appear on any partition or disk 8 | .Trash-* 9 | 10 | 11 | # Idea 12 | .idea 13 | 14 | # Configuration file 15 | config.sh 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2015-2016 Antoine Chabert 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lxd-functions 2 | 3 | This bash file allows you to create, start and delete an LXD container. 4 | The goal is to mount the container with appropriate rights and ownership (with bindfs). 5 | After that the container is started, you can work directly on the host machine, your user is used in the mounted point, and you have full access to it. In the container, a default user is used to map with the host user. 6 | 7 | ## Dependencies 8 | 9 | * `bindfs`: the bash package to make advanced mounted directory (in ubuntu `sudo apt-get install bindfs`) 10 | * `sudo`: required to get root privileges for mounting the container or creating missing destination directory 11 | 12 | ## Installation 13 | 14 | Clone this repository in `/path/to/lxd-functions` 15 | 16 | Add in the `.bashrc` file (`/home//.bashrc`) or in the `.profile` : 17 | 18 | ``` 19 | if [ -f /path/to/lxd-functions/main.sh ]; then 20 | . /path/to/lxd-functions/main.sh 21 | fi 22 | ``` 23 | 24 | Copy the example of configration file `/path/to/lxd-functions/config.sh.example` to `/path/to/lxd-functions/config.sh` and edit it, specially check that : 25 | 26 | * `LXD_SOURCE_DIR` need to match with the path of LXD containers in your system 27 | * `LXD_MOUNT_DIR` it's where containers will be mounted. The default value is `/var/lxd`. It will be automatically created if you run the script. 28 | 29 | **No need to change an existing LXD container, this script use the LXD API without container modification !** 30 | 31 | ## Commands 32 | 33 | Commands available: 34 | 35 | * `lxd-start ` (Start an LXD container and mount it) 36 | * `lxd-stop ` (Stop an LXD container and umount it) 37 | * `lxd-bindfs-mount ` (Mount an LXD Container) 38 | * `lxd-bindfs-umount ` (Umount an LXD Container) 39 | * More soon (for example `lxd-create` ...) 40 | 41 | ## Example of use 42 | 43 | In this example, you already have a LXD named `mylxd` (started or not, it doesn't matter) and you let the default configuration (in the `config.sh`). 44 | Before working with it, just enter this : `lxd-start mylxd` 45 | The script will start LXD container (with `lxc start mylxd`), then try to mount the container in this path : `/var/lxd/mylxd`, if the directory `mylxd` isn't already present, the script will ask if you agree to create it automatically. 46 | That's all ! You can try to create files directly in the container or in the mounted directory, the file created will have the current user uid/gid in the host and the default user in the container. 47 | 48 | The command `lxd-stop mylxd` is not required, but it will shutdown the lxd and unmount it for you. 49 | 50 | Others commands are not needed too, this is for advanced use. 51 | 52 | ## Questions ? Want to involve ? 53 | 54 | Just ask in the issue section if it's not already done ;) 55 | -------------------------------------------------------------------------------- /config.sh.example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Path to the LXD containers, choose the right path matching the LXD installation. 4 | # With Ubuntu > 18.04, the default installation use snap and match this path: 5 | LXD_SOURCE_DIR=/var/snap/lxd/common/lxd/containers 6 | # Older version of Ubuntu <= 18.04, use native package, and match this path: 7 | #LXD_SOURCE_DIR=/var/lib/lxd/containers 8 | 9 | # The path where LXD containers will be mounted: 10 | LXD_MOUNT_DIR=/var/lxd 11 | 12 | # The user ID used for files permissions in the mounted container: 13 | USER_HOST_MOUNT=`id -u` 14 | 15 | # The group ID used for files permissions in the mounted container: 16 | GROUP_HOST_MOUNT=`id -g` 17 | 18 | # If true, instead of using the mapping users rule, the user you want to use in the container will be prompted: 19 | ASK_MAPPING_USER=false 20 | 21 | # Rule to follow to get a valid user in the container, left to right. 22 | # The special user "CONTAINER" match the container name: 23 | MAPPING_USERS=("$USER" "CONTAINER" ubuntu) 24 | 25 | # If true, when mounting the container, if there is missing rights, the script will ask before apply change on right 26 | ASK_CHANGE_CONTAINER_RIGHTS=false 27 | -------------------------------------------------------------------------------- /main.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Functions to run or stop quickly an LXD container 3 | # and mount it on host folder with clean rules 4 | 5 | if ! type complete &>/dev/null; then 6 | autoload bashcompinit 7 | bashcompinit 8 | fi 9 | 10 | PATH_SCRIPT=$(dirname ${BASH_SOURCE[0]-$0}) 11 | PATH_SCRIPT=$(cd $PATH_SCRIPT && pwd) 12 | 13 | . ${PATH_SCRIPT}/config.sh 14 | 15 | # POSIX confirm 16 | _confirm() { 17 | echo -n $1 " ? [y/n]" 18 | old_stty_cfg=$(stty -g) 19 | stty raw -echo 20 | answer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done ) 21 | stty $old_stty_cfg 22 | echo 23 | if echo "$answer" | grep -iq "^y" ;then 24 | ${@:2} && return 0 25 | fi 26 | return 1 27 | } 28 | 29 | _checkRights() { 30 | if [ "$EUID" -eq 0 ] 31 | then echo "Please run this function without root privileges. The sudo command will be used when required root access. The LXD API need to be run with your current user." >&2 32 | return 1 33 | fi 34 | if ! command -v sudo &> /dev/null ; then 35 | echo "The sudo command is missing, and this is a required dependency for accessing root privileges when needed." >&2 36 | return 1 37 | fi 38 | return 0 39 | } 40 | 41 | _checkBindfs() { 42 | if ! command -v bindfs &> /dev/null ; then 43 | echo "The bindfs command is missing, and this is a required dependency for mounting the container." >&2 44 | echo "You can install this dependency on ubuntu/debian like this: sudo apt-get install bindfs" >&2 45 | return 1 46 | fi 47 | return 0 48 | } 49 | 50 | # Get the UID and the GID of the current user in the container (or root by default) 51 | _getUidGidLxd() { 52 | if [ -d "${LXD_SOURCE_DIR}/$1" ] && [ ! -x "${LXD_SOURCE_DIR}/$1" ]; then 53 | if [ "${ASK_CHANGE_CONTAINER_RIGHTS}" = "true" ]; then 54 | echo "The container path is not accessible (this is normal, if LXD is installed via snap): ${LXD_SOURCE_DIR}/$1" 55 | _confirm "Do you wish to add access right (x) this directory (required to proceed)" sudo chmod go+x ${LXD_SOURCE_DIR}/$1 56 | else 57 | echo "Give access to container path: ${LXD_SOURCE_DIR}/$1" 58 | sudo chmod go+x ${LXD_SOURCE_DIR}/$1 59 | fi 60 | fi 61 | 62 | if [ -d "${LXD_SOURCE_DIR}/$1/rootfs" ] && [ -x "${LXD_SOURCE_DIR}/$1/rootfs" ]; then 63 | DEFAULT_USER="root" 64 | for mappingUser in "${MAPPING_USERS[@]}"; do 65 | if [ "${mappingUser}" = "CONTAINER" ] && [ -d "${LXD_SOURCE_DIR}/$1/rootfs/home/$1" ]; then 66 | DEFAULT_USER=$1 67 | break 68 | elif [ -d "${LXD_SOURCE_DIR}/$1/rootfs/home/${mappingUser}" ]; then 69 | DEFAULT_USER=$mappingUser 70 | break 71 | fi 72 | done 73 | MAPPING_USER=$DEFAULT_USER 74 | 75 | if [ "${ASK_MAPPING_USER}" = "true" ]; then 76 | echo -n "User in container to make uig/gid mapping [${DEFAULT_USER}]: " 77 | read INPUT_USER 78 | if [ "$INPUT_USER" = "" ]; then 79 | INPUT_USER=$DEFAULT_USER 80 | fi 81 | MAPPING_USER=$INPUT_USER 82 | fi 83 | 84 | if [ "${MAPPING_USER}" != 'root' ] && [ -d "${LXD_SOURCE_DIR}/$1/rootfs/home/${MAPPING_USER}" ]; then 85 | echo "The user $MAPPING_USER was found and will be used to make the uig/gid mapping." 86 | UID_GUEST_MOUNT=`ls -ldn ${LXD_SOURCE_DIR}/$1/rootfs/home/${MAPPING_USER} | awk '{print $3}'` 87 | GID_GUEST_MOUNT=`ls -ldn ${LXD_SOURCE_DIR}/$1/rootfs/home/${MAPPING_USER} | awk '{print $4}'` 88 | elif [ "${MAPPING_USER}" = 'root' ] && [ -d "${LXD_SOURCE_DIR}/$1/rootfs/root" ]; then 89 | echo "The root user will bed used to make the uig/gid mapping." 90 | UID_GUEST_MOUNT=`ls -ldn ${LXD_SOURCE_DIR}/$1/rootfs/root | awk '{print $3}'` 91 | GID_GUEST_MOUNT=`ls -ldn ${LXD_SOURCE_DIR}/$1/rootfs/root | awk '{print $4}'` 92 | else 93 | echo "Unable found the user $MAPPING_USER in the container" 94 | return 1 95 | fi 96 | return 0 97 | else 98 | echo "Unable to access to the rootfs of the container: ${LXD_SOURCE_DIR}/$1/rootfs" >&2 99 | return 1 100 | fi 101 | } 102 | 103 | # Umount a container 104 | lxd-bindfs-umount() { 105 | _checkRights || return 1 106 | if [ -z "$1" ]; then 107 | echo "lxd-bindfs-umount " >&2 108 | elif [ -d "${LXD_MOUNT_DIR}/$1" ] && [ -x "${LXD_MOUNT_DIR}/$1" ] && [ "$(ls -A ${LXD_MOUNT_DIR}/$1 )" ]; then 109 | sudo umount ${LXD_MOUNT_DIR}/$1 && echo "Umount done (in ${LXD_MOUNT_DIR}/$1)" 110 | fi 111 | } 112 | 113 | # Mount with bindfs a container 114 | lxd-bindfs-mount() { 115 | _checkRights || return 1 116 | _checkBindfs || return 1 117 | if [ $# -ne 5 ]; then 118 | echo "lxd-bindfs-mount " 119 | elif [ ! -d "${LXD_MOUNT_DIR}/$1" ] || [ ! -x "${LXD_MOUNT_DIR}/$1" ]; then 120 | echo "Unable to access to the directory: ${LXD_MOUNT_DIR}/$1" >&2 121 | echo "Directory exist ?" >&2 122 | elif [ "$(ls -A ${LXD_MOUNT_DIR}/$1 )" ]; then 123 | echo "The mount directory is not empty : $LXD_MOUNT_DIR/$1" >&2 124 | echo "Already mounted ?" >&2 125 | else 126 | sudo bindfs --force-user=$2 --force-group=$3 --create-for-user=$4 --create-for-group=$5 ${LXD_SOURCE_DIR}/$1/rootfs ${LXD_MOUNT_DIR}/$1 && echo "Mount done (in ${LXD_MOUNT_DIR}/$1)" 127 | fi 128 | } 129 | 130 | # Stop a container and umount it 131 | lxd-stop() { 132 | _checkRights || return 1 133 | if [ -z "$1" ]; then 134 | echo "lxd-stop " >&2 135 | else 136 | if [ `lxc list --columns=n ^${1}$ | wc -l` -eq 5 ]; then 137 | if [ `lxc list --columns=s ^${1}$ | grep RUNNING | wc -l` -eq 1 ]; then 138 | if lxc stop $1 --timeout 30; then 139 | echo "LXD $1 stopped" 140 | else 141 | lxc stop $1 --force && echo "LXD $1 stopped, but forced!" 142 | fi 143 | fi 144 | 145 | if [ "$(ls -A ${LXD_MOUNT_DIR}/$1 )" ]; then 146 | lxd-bindfs-umount $1 147 | fi 148 | else 149 | echo "No container named $1 found" >&2 150 | fi 151 | fi 152 | } 153 | 154 | # Start a container and mount it 155 | lxd-start() { 156 | _checkRights || return 1 157 | if [ -z "$1" ]; then 158 | echo "lxd-start " >&2 159 | else 160 | if [ `lxc list --columns=n ^${1}$ | wc -l` -eq 5 ]; then 161 | if [ `lxc list --columns=s ^${1}$ | grep STOPPED | wc -l` -eq 1 ]; then 162 | lxc start $1 && echo "LXD $1 started" 163 | fi 164 | MOUNT_RESULT=0 165 | if [ ! -d "${LXD_MOUNT_DIR}/$1" ]; then 166 | echo "No destination directory to mount the container : ${LXD_MOUNT_DIR}/$1" 167 | _confirm "Do you wish to create this directory" sudo mkdir -p ${LXD_MOUNT_DIR}/$1 168 | MOUNT_RESULT=$? 169 | fi 170 | if [ ${MOUNT_RESULT} -eq 0 ]; then 171 | _getUidGidLxd $1 && lxd-bindfs-mount $1 ${USER_HOST_MOUNT} ${GROUP_HOST_MOUNT} ${UID_GUEST_MOUNT} ${GID_GUEST_MOUNT} 172 | else 173 | echo "Unable to mount the container, the destination directory is missing" >&2 174 | fi 175 | else 176 | echo "No container named $1 found" >&2 177 | fi 178 | fi 179 | } 180 | 181 | # Create the default LXD container with a current user 182 | lxd-create() { 183 | _checkRights || return 1 184 | if [ $# -ne 2 ]; then 185 | echo "lxd-create " 186 | echo "To get the list of images availables : lxc image list " 187 | else 188 | read -p "Do you wish to create the new container named $2 with the image $1 ? [Y/n] " yn 189 | case ${yn} in 190 | [Yy]* ) 191 | lxc launch $1 $2 && lxc exec $2 -- /usr/sbin/useradd $2 && lxc exec $2 -- /usr/sbin/passwd $2 && lxd-start $2 ;; 192 | * ) 193 | return ;; 194 | esac 195 | fi 196 | } 197 | 198 | _lxdListComplete() { 199 | local cur opts prev 200 | cur="${COMP_WORDS[COMP_CWORD]}" 201 | prev="${COMP_WORDS[COMP_CWORD-1]}" 202 | opts="$(lxc list --format=csv --columns=n)" 203 | if [ "${prev}" == "lxd-start" ] || [ "${prev}" == "lxd-bindfs-mount" ]; then 204 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 205 | fi 206 | } 207 | 208 | _mountedLxdListComplete() { 209 | if [ -d "${LXD_MOUNT_DIR}" ] && [ -x "${LXD_MOUNT_DIR}" ]; then 210 | local cur opts prev 211 | cur="${COMP_WORDS[COMP_CWORD]}" 212 | prev="${COMP_WORDS[COMP_CWORD-1]}" 213 | cur="${COMP_WORDS[COMP_CWORD]}" 214 | opts="$(find ${LXD_MOUNT_DIR} -mindepth 1 -maxdepth 1 -not -empty -type d -print0 | xargs -r -0 -n 1 basename)" 215 | if [ "${prev}" == "lxd-stop" ] || [ "${prev}" == "lxd-bindfs-umount" ]; then 216 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 217 | fi 218 | fi 219 | } 220 | complete -F _lxdListComplete lxd-start 221 | complete -F _mountedLxdListComplete lxd-stop 222 | complete -F _lxdListComplete lxd-bindfs-mount 223 | complete -F _mountedLxdListComplete lxd-bindfs-umount 224 | --------------------------------------------------------------------------------