Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
lxc
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
lxc
Commits
0e24c560
Unverified
Commit
0e24c560
authored
Jan 29, 2021
by
Stéphane Graber
Committed by
GitHub
Jan 29, 2021
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3638 from brauner/2021-01-28/fixes
attach: improve attach codepaths
parents
0fa84a8c
581b849a
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
486 additions
and
388 deletions
+486
-388
configure.ac
configure.ac
+12
-4
attach.c
src/lxc/attach.c
+331
-255
attach.h
src/lxc/attach.h
+1
-10
attach_options.h
src/lxc/attach_options.h
+1
-1
confile.c
src/lxc/confile.c
+1
-1
confile.h
src/lxc/confile.h
+6
-1
lsm.c
src/lxc/lsm/lsm.c
+1
-1
lsm.h
src/lxc/lsm/lsm.h
+1
-1
macro.h
src/lxc/macro.h
+13
-0
namespace.c
src/lxc/namespace.c
+8
-8
namespace.h
src/lxc/namespace.h
+1
-0
start.c
src/lxc/start.c
+14
-24
sync.c
src/lxc/sync.c
+63
-35
sync.h
src/lxc/sync.h
+32
-46
attach.c
src/tests/attach.c
+1
-1
No files found.
configure.ac
View file @
0e24c560
...
@@ -61,6 +61,18 @@ if test "x$valid_compiler" = "xno"; then
...
@@ -61,6 +61,18 @@ if test "x$valid_compiler" = "xno"; then
AC_MSG_ERROR([Sorry, your compiler is too old - please upgrade it])
AC_MSG_ERROR([Sorry, your compiler is too old - please upgrade it])
fi
fi
AC_PROG_GCC_TRADITIONAL
AC_ENABLE_SHARED
AC_ENABLE_STATIC
# Check binaries
AC_PROG_SED
case $CC in clang*)
AC_CHECK_TOOL([AR], llvm-ar)
AC_CHECK_TOOL([NM], llvm-nm)
AC_CHECK_TOOL([OBJCOPY], llvm-objcopy)
AC_CHECK_TOOL([RANLIB], llvm-ranlib)
esac
# libtool
# libtool
LT_INIT
LT_INIT
AC_SUBST([LIBTOOL_DEPS])
AC_SUBST([LIBTOOL_DEPS])
...
@@ -731,10 +743,6 @@ AC_CHECK_TYPES([struct rtnl_link_stats64], [], [], [[#include <linux/if_link.h>]
...
@@ -731,10 +743,6 @@ AC_CHECK_TYPES([struct rtnl_link_stats64], [], [], [[#include <linux/if_link.h>]
AX_PTHREAD
AX_PTHREAD
AC_SEARCH_LIBS(clock_gettime, [rt])
AC_SEARCH_LIBS(clock_gettime, [rt])
# Check for some standard binaries
AC_PROG_GCC_TRADITIONAL
AC_PROG_SED
# See if we support thread-local storage.
# See if we support thread-local storage.
LXC_CHECK_TLS
LXC_CHECK_TLS
...
...
src/lxc/attach.c
View file @
0e24c560
...
@@ -42,6 +42,7 @@
...
@@ -42,6 +42,7 @@
#include "mount_utils.h"
#include "mount_utils.h"
#include "namespace.h"
#include "namespace.h"
#include "process_utils.h"
#include "process_utils.h"
#include "sync.h"
#include "syscall_wrappers.h"
#include "syscall_wrappers.h"
#include "terminal.h"
#include "terminal.h"
#include "utils.h"
#include "utils.h"
...
@@ -55,33 +56,122 @@ lxc_log_define(attach, lxc);
...
@@ -55,33 +56,122 @@ lxc_log_define(attach, lxc);
/* Define default options if no options are supplied by the user. */
/* Define default options if no options are supplied by the user. */
static
lxc_attach_options_t
attach_static_default_options
=
LXC_ATTACH_OPTIONS_DEFAULT
;
static
lxc_attach_options_t
attach_static_default_options
=
LXC_ATTACH_OPTIONS_DEFAULT
;
static
struct
lxc_proc_context_info
*
lxc_proc_get_context_info
(
pid_t
pid
)
struct
attach_context
{
int
init_pid
;
int
dfd_init_pid
;
int
dfd_self_pid
;
char
*
lsm_label
;
struct
lxc_container
*
container
;
signed
long
personality
;
unsigned
long
long
capability_mask
;
int
ns_inherited
;
int
ns_fd
[
LXC_NS_MAX
];
struct
lsm_ops
*
lsm_ops
;
};
static
inline
bool
sync_wake_pid
(
int
fd
,
pid_t
pid
)
{
return
lxc_write_nointr
(
fd
,
&
pid
,
sizeof
(
pid_t
))
==
sizeof
(
pid_t
);
}
static
inline
bool
sync_wait_pid
(
int
fd
,
pid_t
*
pid
)
{
return
lxc_read_nointr
(
fd
,
pid
,
sizeof
(
pid_t
))
==
sizeof
(
pid_t
);
}
static
inline
bool
sync_wake_fd
(
int
fd
,
int
fd_send
)
{
return
lxc_abstract_unix_send_fds
(
fd
,
&
fd_send
,
1
,
NULL
,
0
)
>
0
;
}
static
inline
bool
sync_wait_fd
(
int
fd
,
int
*
fd_recv
)
{
return
lxc_abstract_unix_recv_fds
(
fd
,
fd_recv
,
1
,
NULL
,
0
)
>
0
;
}
static
struct
attach_context
*
alloc_attach_context
(
void
)
{
struct
attach_context
*
ctx
;
ctx
=
zalloc
(
sizeof
(
struct
attach_context
));
if
(
!
ctx
)
return
ret_set_errno
(
NULL
,
ENOMEM
);
ctx
->
dfd_self_pid
=
-
EBADF
;
ctx
->
dfd_init_pid
=
-
EBADF
;
for
(
int
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
ctx
->
ns_fd
[
i
]
=
-
EBADF
;
return
ctx
;
}
static
int
get_personality
(
const
char
*
name
,
const
char
*
lxcpath
,
signed
long
*
personality
)
{
__do_free
char
*
p
=
NULL
;
signed
long
per
;
p
=
lxc_cmd_get_config_item
(
name
,
"lxc.arch"
,
lxcpath
);
if
(
!
p
)
{
*
personality
=
LXC_ARCH_UNCHANGED
;
return
0
;
}
per
=
lxc_config_parse_arch
(
p
);
if
(
per
==
LXC_ARCH_UNCHANGED
)
return
ret_errno
(
EINVAL
);
*
personality
=
per
;
return
0
;
}
static
int
get_attach_context
(
struct
attach_context
*
ctx
,
struct
lxc_container
*
container
)
{
{
__do_close
int
dfd_self_pid
=
-
EBADF
,
dfd_init_pid
=
-
EBADF
,
fd_status
=
-
EBADF
;
__do_free
char
*
line
=
NULL
;
__do_free
char
*
line
=
NULL
;
__do_fclose
FILE
*
proc_file
=
NULL
;
__do_fclose
FILE
*
f_status
=
NULL
;
__do_free
struct
lxc_proc_context_info
*
info
=
NULL
;
int
ret
;
int
ret
;
bool
found
;
bool
found
;
char
p
roc_fn
[
LXC_PROC_STATUS
_LEN
];
char
p
ath
[
LXC_PROC_PID
_LEN
];
size_t
line_bufsz
=
0
;
size_t
line_bufsz
=
0
;
/* Read capabilities. */
ctx
->
container
=
container
;
ret
=
snprintf
(
proc_fn
,
LXC_PROC_STATUS_LEN
,
"/proc/%d/status"
,
pid
);
if
(
ret
<
0
||
ret
>=
LXC_PROC_STATUS_LEN
)
return
NULL
;
proc_file
=
fopen
(
proc_fn
,
"re"
);
ctx
->
init_pid
=
lxc_cmd_get_init_pid
(
container
->
name
,
container
->
config_path
);
if
(
!
proc_file
)
if
(
ctx
->
init_pid
<
0
)
return
log_error
_errno
(
NULL
,
errno
,
"Failed to open %s"
,
proc_fn
);
return
log_error
(
-
1
,
"Failed to get init pid"
);
info
=
calloc
(
1
,
sizeof
(
*
info
));
ret
=
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d"
,
lxc_raw_getpid
());
if
(
!
info
)
if
(
ret
<
0
||
ret
>=
sizeof
(
path
))
return
NULL
;
return
ret_errno
(
EIO
);
dfd_self_pid
=
openat
(
-
EBADF
,
path
,
O_CLOEXEC
|
O_NOCTTY
|
O_NOFOLLOW
|
O_PATH
|
O_DIRECTORY
);
if
(
dfd_self_pid
<
0
)
return
-
errno
;
ret
=
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d"
,
ctx
->
init_pid
);
if
(
ret
<
0
||
ret
>=
sizeof
(
path
))
return
ret_errno
(
EIO
);
dfd_init_pid
=
openat
(
-
EBADF
,
path
,
O_CLOEXEC
|
O_NOCTTY
|
O_NOFOLLOW
|
O_PATH
|
O_DIRECTORY
);
if
(
dfd_init_pid
<
0
)
return
-
errno
;
fd_status
=
openat
(
dfd_init_pid
,
"status"
,
O_CLOEXEC
|
O_NOCTTY
|
O_NOFOLLOW
|
O_RDONLY
);
if
(
fd_status
<
0
)
return
-
errno
;
f_status
=
fdopen
(
fd_status
,
"re"
);
if
(
!
f_status
)
return
log_error_errno
(
-
errno
,
errno
,
"Failed to open file descriptor %d"
,
fd_status
);
move_fd
(
fd_status
);
found
=
false
;
found
=
false
;
while
(
getline
(
&
line
,
&
line_bufsz
,
proc_file
)
!=
-
1
)
{
while
(
getline
(
&
line
,
&
line_bufsz
,
f_status
)
!=
-
1
)
{
ret
=
sscanf
(
line
,
"CapBnd: %llx"
,
&
info
->
capability_mask
);
ret
=
sscanf
(
line
,
"CapBnd: %llx"
,
&
ctx
->
capability_mask
);
if
(
ret
!=
EOF
&&
ret
==
1
)
{
if
(
ret
!=
EOF
&&
ret
==
1
)
{
found
=
true
;
found
=
true
;
break
;
break
;
...
@@ -89,66 +179,45 @@ static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
...
@@ -89,66 +179,45 @@ static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
}
}
if
(
!
found
)
if
(
!
found
)
return
log_error_errno
(
NULL
,
ENOENT
,
"Failed to read capability bounding set from %s"
,
proc_fn
);
return
log_error_errno
(
-
ENOENT
,
ENOENT
,
"Failed to read capability bounding set from %s/status"
,
path
);
info
->
lsm_ops
=
lsm_init
();
info
->
lsm_label
=
info
->
lsm_ops
->
process_label_get
(
info
->
lsm_ops
,
pid
);
info
->
ns_inherited
=
0
;
for
(
int
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
info
->
ns_fd
[
i
]
=
-
EBADF
;
return
move_ptr
(
info
);
ctx
->
lsm_ops
=
lsm_init_static
();
}
static
inline
void
lxc_proc_close_ns_fd
(
struct
lxc_proc_context_info
*
ctx
)
/* Move to file descriptor-only lsm label retrieval. */
{
ctx
->
lsm_label
=
ctx
->
lsm_ops
->
process_label_get
(
ctx
->
lsm_ops
,
ctx
->
init_pid
);
for
(
int
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
ctx
->
ns_inherited
=
0
;
close_prot_errno_disarm
(
ctx
->
ns_fd
[
i
]);
}
static
void
lxc_proc_put_context_info
(
struct
lxc_proc_context_info
*
ctx
)
ret
=
get_personality
(
container
->
name
,
container
->
config_path
,
&
ctx
->
personality
);
{
if
(
ret
)
free_disarm
(
ctx
->
lsm_label
);
return
log_error_errno
(
ret
,
errno
,
"Failed to get personality of the container"
);
if
(
ctx
->
container
)
{
if
(
!
ctx
->
container
->
lxc_conf
)
{
lxc_container_put
(
ctx
->
container
);
ctx
->
container
->
lxc_conf
=
lxc_conf_init
();
ctx
->
container
=
NULL
;
if
(
!
ctx
->
container
->
lxc_conf
)
return
log_error_errno
(
-
ENOMEM
,
ENOMEM
,
"Failed to allocate new lxc config"
);
}
}
lxc_proc_close_ns_fd
(
ctx
);
ctx
->
dfd_init_pid
=
move_fd
(
dfd_init_pid
);
free
(
ctx
);
ctx
->
dfd_self_pid
=
move_fd
(
dfd_self_pid
);
return
0
;
}
}
/**
static
int
in_same_namespace
(
int
ns_fd_pid1
,
int
ns_fd_pid2
,
const
char
*
ns_path
)
* in_same_namespace - Check whether two processes are in the same namespace.
* @pid1 - PID of the first process.
* @pid2 - PID of the second process.
* @ns - Name of the namespace to check. Must correspond to one of the names
* for the namespaces as shown in /proc/<pid/ns/
*
* If the two processes are not in the same namespace returns an fd to the
* namespace of the second process identified by @pid2. If the two processes are
* in the same namespace returns -EINVAL, -1 if an error occurred.
*/
static
int
in_same_namespace
(
pid_t
pid1
,
pid_t
pid2
,
const
char
*
ns
)
{
{
__do_close
int
ns_fd1
=
-
EBADF
,
ns_fd2
=
-
EBADF
;
__do_close
int
ns_fd1
=
-
EBADF
,
ns_fd2
=
-
EBADF
;
int
ret
=
-
1
;
int
ret
=
-
1
;
struct
stat
ns_st1
,
ns_st2
;
struct
stat
ns_st1
,
ns_st2
;
ns_fd1
=
lxc_preserve_ns
(
pid1
,
ns
);
ns_fd1
=
openat
(
ns_fd_pid1
,
ns_path
,
O_CLOEXEC
|
O_NOCTTY
|
O_RDONLY
);
if
(
ns_fd1
<
0
)
{
if
(
ns_fd1
<
0
)
{
/* The kernel does not support this namespace. This is not an
/* The kernel does not support this namespace. This is not an error. */
* error.
*/
if
(
errno
==
ENOENT
)
if
(
errno
==
ENOENT
)
return
-
EINVAL
;
return
-
EINVAL
;
return
-
1
;
return
-
1
;
}
}
ns_fd2
=
lxc_preserve_ns
(
pid2
,
ns
);
ns_fd2
=
openat
(
ns_fd_pid2
,
ns_path
,
O_CLOEXEC
|
O_NOCTTY
|
O_RDONLY
);
if
(
ns_fd2
<
0
)
if
(
ns_fd2
<
0
)
return
-
1
;
return
-
1
;
...
@@ -161,14 +230,74 @@ static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
...
@@ -161,14 +230,74 @@ static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
return
-
1
;
return
-
1
;
/* processes are in the same namespace */
/* processes are in the same namespace */
if
((
ns_st1
.
st_dev
==
ns_st2
.
st_dev
)
&&
(
ns_st1
.
st_ino
==
ns_st2
.
st_ino
))
if
((
ns_st1
.
st_dev
==
ns_st2
.
st_dev
)
&&
(
ns_st1
.
st_ino
==
ns_st2
.
st_ino
))
return
-
EINVAL
;
return
-
EINVAL
;
/* processes are in different namespaces */
/* processes are in different namespaces */
return
move_fd
(
ns_fd2
);
return
move_fd
(
ns_fd2
);
}
}
static
int
lxc_attach_to_ns
(
pid_t
pid
,
struct
lxc_proc_context_info
*
ctx
)
static
int
get_attach_context_nsfds
(
struct
attach_context
*
ctx
,
lxc_attach_options_t
*
options
)
{
for
(
int
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
{
int
j
;
if
(
options
->
namespaces
&
ns_info
[
i
].
clone_flag
)
ctx
->
ns_fd
[
i
]
=
openat
(
ctx
->
dfd_init_pid
,
ns_info
[
i
].
proc_path
,
O_CLOEXEC
|
O_NOCTTY
|
O_RDONLY
);
else
if
(
ctx
->
ns_inherited
&
ns_info
[
i
].
clone_flag
)
ctx
->
ns_fd
[
i
]
=
in_same_namespace
(
ctx
->
dfd_self_pid
,
ctx
->
dfd_init_pid
,
ns_info
[
i
].
proc_path
);
else
continue
;
if
(
ctx
->
ns_fd
[
i
]
>=
0
)
continue
;
if
(
ctx
->
ns_fd
[
i
]
==
-
EINVAL
)
{
DEBUG
(
"Inheriting %s namespace"
,
ns_info
[
i
].
proc_name
);
ctx
->
ns_inherited
&=
~
ns_info
[
i
].
clone_flag
;
continue
;
}
/* We failed to preserve the namespace. */
SYSERROR
(
"Failed to preserve %s namespace of %d"
,
ns_info
[
i
].
proc_name
,
ctx
->
init_pid
);
/* Close all already opened file descriptors before we return an
* error, so we don't leak them.
*/
for
(
j
=
0
;
j
<
i
;
j
++
)
close_prot_errno_disarm
(
ctx
->
ns_fd
[
j
]);
return
-
1
;
}
return
0
;
}
static
inline
void
close_nsfds
(
struct
attach_context
*
ctx
)
{
for
(
int
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
close_prot_errno_disarm
(
ctx
->
ns_fd
[
i
]);
}
static
void
put_attach_context
(
struct
attach_context
*
ctx
)
{
if
(
ctx
)
{
free_disarm
(
ctx
->
lsm_label
);
close_prot_errno_disarm
(
ctx
->
dfd_init_pid
);
if
(
ctx
->
container
)
{
lxc_container_put
(
ctx
->
container
);
ctx
->
container
=
NULL
;
}
close_nsfds
(
ctx
);
free
(
ctx
);
}
}
static
int
attach_context_container
(
struct
attach_context
*
ctx
)
{
{
for
(
int
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
{
for
(
int
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
{
int
ret
;
int
ret
;
...
@@ -178,16 +307,36 @@ static int lxc_attach_to_ns(pid_t pid, struct lxc_proc_context_info *ctx)
...
@@ -178,16 +307,36 @@ static int lxc_attach_to_ns(pid_t pid, struct lxc_proc_context_info *ctx)
ret
=
setns
(
ctx
->
ns_fd
[
i
],
ns_info
[
i
].
clone_flag
);
ret
=
setns
(
ctx
->
ns_fd
[
i
],
ns_info
[
i
].
clone_flag
);
if
(
ret
<
0
)
if
(
ret
<
0
)
return
log_error_errno
(
-
1
,
return
log_error_errno
(
-
1
,
errno
,
errno
,
"Failed to attach to %s namespace of %d"
,
"Failed to attach to %s namespace of %d"
,
ns_info
[
i
].
proc_name
,
pid
);
ns_info
[
i
].
proc_name
,
ctx
->
init_
pid
);
DEBUG
(
"Attached to %s namespace of %d"
,
ns_info
[
i
].
proc_name
,
pid
);
DEBUG
(
"Attached to %s namespace of %d"
,
ns_info
[
i
].
proc_name
,
ctx
->
init_pid
);
}
}
return
0
;
return
0
;
}
}
/*
* Place anything in here that needs to be get rid of before we move into the
* container's context and fail hard if we can't.
*/
static
bool
attach_context_security_barrier
(
struct
attach_context
*
ctx
)
{
if
(
ctx
)
{
if
(
close
(
ctx
->
dfd_self_pid
))
return
false
;
ctx
->
dfd_self_pid
=
-
EBADF
;
if
(
close
(
ctx
->
dfd_init_pid
))
return
false
;
ctx
->
dfd_init_pid
=
-
EBADF
;
}
return
true
;
}
int
lxc_attach_remount_sys_proc
(
void
)
int
lxc_attach_remount_sys_proc
(
void
)
{
{
int
ret
;
int
ret
;
...
@@ -223,7 +372,7 @@ int lxc_attach_remount_sys_proc(void)
...
@@ -223,7 +372,7 @@ int lxc_attach_remount_sys_proc(void)
return
0
;
return
0
;
}
}
static
int
lxc_attach_drop_privs
(
struct
lxc_proc_context_info
*
ctx
)
static
int
drop_capabilities
(
struct
attach_context
*
ctx
)
{
{
int
last_cap
;
int
last_cap
;
...
@@ -242,7 +391,7 @@ static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
...
@@ -242,7 +391,7 @@ static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
return
0
;
return
0
;
}
}
static
int
lxc_attach_set_environment
(
struct
lxc_proc_context_info
*
init_
ctx
,
static
int
lxc_attach_set_environment
(
struct
attach_context
*
ctx
,
enum
lxc_attach_env_policy_t
policy
,
enum
lxc_attach_env_policy_t
policy
,
char
**
extra_env
,
char
**
extra_keep
)
char
**
extra_env
,
char
**
extra_keep
)
{
{
...
@@ -259,7 +408,7 @@ static int lxc_attach_set_environment(struct lxc_proc_context_info *init_ctx,
...
@@ -259,7 +408,7 @@ static int lxc_attach_set_environment(struct lxc_proc_context_info *init_ctx,
for
(
count
=
0
;
extra_keep
[
count
];
count
++
)
for
(
count
=
0
;
extra_keep
[
count
];
count
++
)
;
;
extra_keep_store
=
calloc
(
count
,
sizeof
(
char
*
));
extra_keep_store
=
zalloc
(
count
*
sizeof
(
char
*
));
if
(
!
extra_keep_store
)
if
(
!
extra_keep_store
)
return
-
1
;
return
-
1
;
...
@@ -327,8 +476,8 @@ static int lxc_attach_set_environment(struct lxc_proc_context_info *init_ctx,
...
@@ -327,8 +476,8 @@ static int lxc_attach_set_environment(struct lxc_proc_context_info *init_ctx,
return
log_warn
(
-
1
,
"Failed to set environment variable"
);
return
log_warn
(
-
1
,
"Failed to set environment variable"
);
/* Set container environment variables.*/
/* Set container environment variables.*/
if
(
init_ctx
&&
init_ctx
->
container
&&
init_
ctx
->
container
->
lxc_conf
)
{
if
(
ctx
&&
ctx
->
container
&&
ctx
->
container
->
lxc_conf
)
{
lxc_list_for_each
(
iterator
,
&
init_
ctx
->
container
->
lxc_conf
->
environment
)
{
lxc_list_for_each
(
iterator
,
&
ctx
->
container
->
lxc_conf
->
environment
)
{
char
*
env_tmp
;
char
*
env_tmp
;
env_tmp
=
strdup
((
char
*
)
iterator
->
elem
);
env_tmp
=
strdup
((
char
*
)
iterator
->
elem
);
...
@@ -614,48 +763,38 @@ static bool no_new_privs(struct lxc_container *c, lxc_attach_options_t *options)
...
@@ -614,48 +763,38 @@ static bool no_new_privs(struct lxc_container *c, lxc_attach_options_t *options)
return
c
->
set_config_item
(
c
,
"lxc.no_new_privs"
,
val
);
return
c
->
set_config_item
(
c
,
"lxc.no_new_privs"
,
val
);
}
}
static
signed
long
get_personality
(
const
char
*
name
,
const
char
*
lxcpath
)
struct
attach_payload
{
{
__do_free
char
*
p
=
NULL
;
p
=
lxc_cmd_get_config_item
(
name
,
"lxc.arch"
,
lxcpath
);
if
(
!
p
)
return
-
1
;
return
lxc_config_parse_arch
(
p
);
}
struct
attach_clone_payload
{
int
ipc_socket
;
int
ipc_socket
;
int
terminal_pts_fd
;
int
terminal_pts_fd
;
lxc_attach_options_t
*
options
;
lxc_attach_options_t
*
options
;
struct
lxc_proc_context_info
*
init_
ctx
;
struct
attach_context
*
ctx
;
lxc_attach_exec_t
exec_function
;
lxc_attach_exec_t
exec_function
;
void
*
exec_payload
;
void
*
exec_payload
;
};
};
static
void
lxc_put_attach_clone_payload
(
struct
attach_clone
_payload
*
p
)
static
void
put_attach_payload
(
struct
attach
_payload
*
p
)
{
{
close_prot_errno_disarm
(
p
->
ipc_socket
);
close_prot_errno_disarm
(
p
->
ipc_socket
);
close_prot_errno_disarm
(
p
->
terminal_pts_fd
);
close_prot_errno_disarm
(
p
->
terminal_pts_fd
);
if
(
p
->
init_
ctx
)
{
if
(
p
->
ctx
)
{
lxc_proc_put_context_info
(
p
->
init_
ctx
);
put_attach_context
(
p
->
ctx
);
p
->
init_
ctx
=
NULL
;
p
->
ctx
=
NULL
;
}
}
}
}
__noreturn
static
void
do_attach
(
struct
attach_
clone_payload
*
payload
)
__noreturn
static
void
do_attach
(
struct
attach_
payload
*
ap
)
{
{
int
lsm_fd
,
ret
;
int
lsm_fd
,
ret
;
uid_t
new_uid
;
uid_t
new_uid
;
gid_t
new_gid
;
gid_t
new_gid
;
uid_t
ns_root_uid
=
0
;
uid_t
ns_root_uid
=
0
;
gid_t
ns_root_gid
=
0
;
gid_t
ns_root_gid
=
0
;
lxc_attach_options_t
*
options
=
payload
->
options
;
lxc_attach_options_t
*
options
=
ap
->
options
;
struct
lxc_proc_context_info
*
init_ctx
=
payload
->
init_ctx
;
struct
attach_context
*
ctx
=
ap
->
ctx
;
struct
lxc_conf
*
conf
=
ctx
->
container
->
lxc_conf
;
bool
needs_lsm
=
(
options
->
namespaces
&
CLONE_NEWNS
)
&&
bool
needs_lsm
=
(
options
->
namespaces
&
CLONE_NEWNS
)
&&
(
options
->
attach_flags
&
LXC_ATTACH_LSM
)
&&
(
options
->
attach_flags
&
LXC_ATTACH_LSM
)
&&
init_
ctx
->
lsm_label
;
ctx
->
lsm_label
;
char
*
lsm_label
=
NULL
;
char
*
lsm_label
=
NULL
;
/* A description of the purpose of this functionality is provided in the
/* A description of the purpose of this functionality is provided in the
...
@@ -672,26 +811,28 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
...
@@ -672,26 +811,28 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
TRACE
(
"Remounted
\"
/proc
\"
and
\"
/sys
\"
"
);
TRACE
(
"Remounted
\"
/proc
\"
and
\"
/sys
\"
"
);
}
}
/* Now perform additional attachments. */
/* Now perform additional attachments. */
#if HAVE_SYS_PERSONALITY_H
#if HAVE_SYS_PERSONALITY_H
if
(
options
->
attach_flags
&
LXC_ATTACH_SET_PERSONALITY
)
{
if
(
options
->
attach_flags
&
LXC_ATTACH_SET_PERSONALITY
)
{
long
new_personality
;
long
new_personality
;
if
(
options
->
personality
<
0
)
if
(
options
->
personality
<
0
)
new_personality
=
init_
ctx
->
personality
;
new_personality
=
ctx
->
personality
;
else
else
new_personality
=
options
->
personality
;
new_personality
=
options
->
personality
;
if
(
new_personality
!=
LXC_ARCH_UNCHANGED
)
{
ret
=
personality
(
new_personality
);
ret
=
personality
(
new_personality
);
if
(
ret
<
0
)
if
(
ret
<
0
)
goto
on_error
;
goto
on_error
;
TRACE
(
"Set new personality"
);
TRACE
(
"Set new personality"
);
}
}
}
#endif
#endif
if
(
options
->
attach_flags
&
LXC_ATTACH_DROP_CAPABILITIES
)
{
if
(
options
->
attach_flags
&
LXC_ATTACH_DROP_CAPABILITIES
)
{
ret
=
lxc_attach_drop_privs
(
init_
ctx
);
ret
=
drop_capabilities
(
ctx
);
if
(
ret
<
0
)
if
(
ret
<
0
)
goto
on_error
;
goto
on_error
;
...
@@ -701,7 +842,7 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
...
@@ -701,7 +842,7 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
/* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL)
/* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL)
* if you want this to be a no-op).
* if you want this to be a no-op).
*/
*/
ret
=
lxc_attach_set_environment
(
init_
ctx
,
ret
=
lxc_attach_set_environment
(
ctx
,
options
->
env_policy
,
options
->
env_policy
,
options
->
extra_env_vars
,
options
->
extra_env_vars
,
options
->
extra_keep_env
);
options
->
extra_keep_env
);
...
@@ -722,11 +863,8 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
...
@@ -722,11 +863,8 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
* set{g,u}id().
* set{g,u}id().
*/
*/
if
(
needs_lsm
)
{
if
(
needs_lsm
)
{
ret
=
lxc_abstract_unix_recv_fds
(
payload
->
ipc_socket
,
&
lsm_fd
,
1
,
NULL
,
0
);
if
(
!
sync_wait_fd
(
ap
->
ipc_socket
,
ATTACH_SYNC_LSM
(
&
lsm_fd
)))
{
if
(
ret
<=
0
)
{
if
(
ret
<
0
)
SYSERROR
(
"Failed to receive lsm label fd"
);
SYSERROR
(
"Failed to receive lsm label fd"
);
goto
on_error
;
goto
on_error
;
}
}
...
@@ -781,18 +919,17 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
...
@@ -781,18 +919,17 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
if
(
options
->
attach_flags
&
LXC_ATTACH_LSM_LABEL
)
if
(
options
->
attach_flags
&
LXC_ATTACH_LSM_LABEL
)
lsm_label
=
options
->
lsm_label
;
lsm_label
=
options
->
lsm_label
;
if
(
!
lsm_label
)
if
(
!
lsm_label
)
lsm_label
=
init_
ctx
->
lsm_label
;
lsm_label
=
ctx
->
lsm_label
;
ret
=
init_ctx
->
lsm_ops
->
process_label_set_at
(
init_
ctx
->
lsm_ops
,
lsm_fd
,
ret
=
ctx
->
lsm_ops
->
process_label_set_at
(
ctx
->
lsm_ops
,
lsm_fd
,
lsm_label
,
on_exec
);
lsm_label
,
on_exec
);
close
(
lsm_fd
);
close
_prot_errno_disarm
(
lsm_fd
);
if
(
ret
<
0
)
if
(
ret
<
0
)
goto
on_error
;
goto
on_error
;
TRACE
(
"Set %s LSM label to
\"
%s
\"
"
,
init_ctx
->
lsm_ops
->
name
,
init_
ctx
->
lsm_label
);
TRACE
(
"Set %s LSM label to
\"
%s
\"
"
,
ctx
->
lsm_ops
->
name
,
ctx
->
lsm_label
);
}
}
if
((
init_ctx
->
container
&&
init_ctx
->
container
->
lxc_conf
&&
if
((
ctx
->
container
&&
conf
&&
conf
->
no_new_privs
)
||
init_ctx
->
container
->
lxc_conf
->
no_new_privs
)
||
(
options
->
attach_flags
&
LXC_ATTACH_NO_NEW_PRIVS
))
{
(
options
->
attach_flags
&
LXC_ATTACH_NO_NEW_PRIVS
))
{
ret
=
prctl
(
PR_SET_NO_NEW_PRIVS
,
prctl_arg
(
1
),
prctl_arg
(
0
),
ret
=
prctl
(
PR_SET_NO_NEW_PRIVS
,
prctl_arg
(
1
),
prctl_arg
(
0
),
prctl_arg
(
0
),
prctl_arg
(
0
));
prctl_arg
(
0
),
prctl_arg
(
0
));
...
@@ -802,24 +939,21 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
...
@@ -802,24 +939,21 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
TRACE
(
"Set PR_SET_NO_NEW_PRIVS"
);
TRACE
(
"Set PR_SET_NO_NEW_PRIVS"
);
}
}
if
(
init_ctx
->
container
&&
init_ctx
->
container
->
lxc_conf
&&
if
(
ctx
->
container
&&
conf
&&
conf
->
seccomp
.
seccomp
)
{
init_ctx
->
container
->
lxc_conf
->
seccomp
.
seccomp
)
{
struct
lxc_conf
*
conf
=
init_ctx
->
container
->
lxc_conf
;
ret
=
lxc_seccomp_load
(
conf
);
ret
=
lxc_seccomp_load
(
conf
);
if
(
ret
<
0
)
if
(
ret
<
0
)
goto
on_error
;
goto
on_error
;
TRACE
(
"Loaded seccomp profile"
);
TRACE
(
"Loaded seccomp profile"
);
ret
=
lxc_seccomp_send_notifier_fd
(
&
conf
->
seccomp
,
payload
->
ipc_socket
);
ret
=
lxc_seccomp_send_notifier_fd
(
&
conf
->
seccomp
,
ap
->
ipc_socket
);
if
(
ret
<
0
)
if
(
ret
<
0
)
goto
on_error
;
goto
on_error
;
}
}
close_prot_errno_disarm
(
payload
->
ipc_socket
);
close_prot_errno_disarm
(
ap
->
ipc_socket
);
lxc_proc_put_context_info
(
init_
ctx
);
put_attach_context
(
ctx
);
payload
->
init_
ctx
=
NULL
;
ap
->
ctx
=
NULL
;
/* The following is done after the communication socket is shut down.
/* The following is done after the communication socket is shut down.
* That way, all errors that might (though unlikely) occur up until this
* That way, all errors that might (though unlikely) occur up until this
...
@@ -865,13 +999,13 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
...
@@ -865,13 +999,13 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
}
}
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
{
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
{
ret
=
lxc_terminal_prepare_login
(
payload
->
terminal_pts_fd
);
ret
=
lxc_terminal_prepare_login
(
ap
->
terminal_pts_fd
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
SYSERROR
(
"Failed to prepare terminal file descriptor %d"
,
payload
->
terminal_pts_fd
);
SYSERROR
(
"Failed to prepare terminal file descriptor %d"
,
ap
->
terminal_pts_fd
);
goto
on_error
;
goto
on_error
;
}
}
TRACE
(
"Prepared terminal file descriptor %d"
,
payload
->
terminal_pts_fd
);
TRACE
(
"Prepared terminal file descriptor %d"
,
ap
->
terminal_pts_fd
);
}
}
/* Avoid unnecessary syscalls. */
/* Avoid unnecessary syscalls. */
...
@@ -890,10 +1024,10 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
...
@@ -890,10 +1024,10 @@ __noreturn static void do_attach(struct attach_clone_payload *payload)
goto
on_error
;
goto
on_error
;
/* We're done, so we can now do whatever the user intended us to do. */
/* We're done, so we can now do whatever the user intended us to do. */
_exit
(
payload
->
exec_function
(
payload
->
exec_payload
));
_exit
(
ap
->
exec_function
(
ap
->
exec_payload
));
on_error:
on_error:
lxc_put_attach_clone_payload
(
payload
);
put_attach_payload
(
ap
);
ERROR
(
"Failed to attach to container"
);
ERROR
(
"Failed to attach to container"
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
...
@@ -954,24 +1088,15 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -954,24 +1088,15 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
void
*
exec_payload
,
lxc_attach_options_t
*
options
,
void
*
exec_payload
,
lxc_attach_options_t
*
options
,
pid_t
*
attached_process
)
pid_t
*
attached_process
)
{
{
__do_free
char
*
cwd
=
NULL
;
int
ret_parent
=
-
1
;
int
i
,
ret
,
status
;
struct
lxc_epoll_descr
descr
=
{};
int
ret
;
char
*
name
,
*
lxcpath
;
int
ipc_sockets
[
2
];
int
ipc_sockets
[
2
];
char
*
new_cwd
;
pid_t
attached_pid
,
pid
,
to_cleanup_pid
;
signed
long
personality
;
struct
attach_context
*
ctx
;
pid_t
attached_pid
,
init_pid
,
pid
;
struct
lxc_proc_context_info
*
init_ctx
;
struct
lxc_terminal
terminal
;
struct
lxc_terminal
terminal
;
struct
lxc_conf
*
conf
;
struct
lxc_conf
*
conf
;
char
*
name
,
*
lxcpath
;
struct
attach_clone_payload
payload
=
{
0
};
int
ret_parent
=
-
1
;
pid_t
to_cleanup_pid
;
struct
lxc_epoll_descr
descr
=
{
0
};
ret
=
access
(
"/proc/self/ns"
,
X_OK
);
if
(
ret
)
return
log_error_errno
(
-
1
,
errno
,
"Does this kernel version support namespaces?"
);
if
(
!
container
)
if
(
!
container
)
return
ret_set_errno
(
-
1
,
EINVAL
);
return
ret_set_errno
(
-
1
,
EINVAL
);
...
@@ -985,62 +1110,35 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -985,62 +1110,35 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
if
(
!
options
)
if
(
!
options
)
options
=
&
attach_static_default_options
;
options
=
&
attach_static_default_options
;
init_pid
=
lxc_cmd_get_init_pid
(
name
,
lxcpath
);
ctx
=
alloc_attach_context
(
);
if
(
init_pid
<
0
)
{
if
(
!
ctx
)
{
lxc_container_put
(
container
);
lxc_container_put
(
container
);
return
log_error
(
-
1
,
"Failed to get init pid
"
);
return
log_error
_errno
(
-
ENOMEM
,
ENOMEM
,
"Failed to allocate attach context
"
);
}
}
init_ctx
=
lxc_proc_get_context_info
(
init_pid
);
ret
=
get_attach_context
(
ctx
,
container
);
if
(
!
init_ctx
)
{
if
(
ret
)
{
ERROR
(
"Failed to get context of init process: %ld"
,
(
long
)
init_pid
);
put_attach_context
(
ctx
);
lxc_container_put
(
container
);
return
log_error
(
-
1
,
"Failed to get attach context"
);
return
-
1
;
}
}
init_ctx
->
container
=
container
;
conf
=
ctx
->
container
->
lxc_conf
;
personality
=
get_personality
(
name
,
lxcpath
);
if
(
init_ctx
->
personality
<
0
)
{
ERROR
(
"Failed to get personality of the container"
);
lxc_proc_put_context_info
(
init_ctx
);
return
-
1
;
}
init_ctx
->
personality
=
personality
;
if
(
!
init_ctx
->
container
->
lxc_conf
)
{
if
(
!
fetch_seccomp
(
ctx
->
container
,
options
))
init_ctx
->
container
->
lxc_conf
=
lxc_conf_init
();
if
(
!
init_ctx
->
container
->
lxc_conf
)
{
lxc_proc_put_context_info
(
init_ctx
);
return
-
1
;
}
}
conf
=
init_ctx
->
container
->
lxc_conf
;
if
(
!
conf
)
return
log_error_errno
(
-
EINVAL
,
EINVAL
,
"Missing container confifg"
);
if
(
!
fetch_seccomp
(
init_ctx
->
container
,
options
))
WARN
(
"Failed to get seccomp policy"
);
WARN
(
"Failed to get seccomp policy"
);
if
(
!
no_new_privs
(
init_
ctx
->
container
,
options
))
if
(
!
no_new_privs
(
ctx
->
container
,
options
))
WARN
(
"Could not determine whether PR_SET_NO_NEW_PRIVS is set"
);
WARN
(
"Could not determine whether PR_SET_NO_NEW_PRIVS is set"
);
cwd
=
getcwd
(
NULL
,
0
);
/* Determine which namespaces the container was created with. */
/* Determine which namespaces the container was created with
* by asking lxc-start, if necessary.
*/
if
(
options
->
namespaces
==
-
1
)
{
if
(
options
->
namespaces
==
-
1
)
{
options
->
namespaces
=
lxc_cmd_get_clone_flags
(
name
,
lxcpath
);
options
->
namespaces
=
lxc_cmd_get_clone_flags
(
name
,
lxcpath
);
/* call failed */
if
(
options
->
namespaces
==
-
1
)
{
if
(
options
->
namespaces
==
-
1
)
{
ERROR
(
"Failed to automatically determine the "
put_attach_context
(
ctx
);
"namespaces which the container uses"
);
return
log_error
(
-
1
,
"Failed to automatically determine the namespaces which the container uses"
);
lxc_proc_put_context_info
(
init_ctx
);
return
-
1
;
}
}
for
(
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
{
for
(
i
nt
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
{
if
(
ns_info
[
i
].
clone_flag
&
CLONE_NEWCGROUP
)
if
(
ns_info
[
i
].
clone_flag
&
CLONE_NEWCGROUP
)
if
(
!
(
options
->
attach_flags
&
LXC_ATTACH_MOVE_TO_CGROUP
)
||
if
(
!
(
options
->
attach_flags
&
LXC_ATTACH_MOVE_TO_CGROUP
)
||
!
cgns_supported
())
!
cgns_supported
())
...
@@ -1049,52 +1147,21 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1049,52 +1147,21 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
if
(
ns_info
[
i
].
clone_flag
&
options
->
namespaces
)
if
(
ns_info
[
i
].
clone_flag
&
options
->
namespaces
)
continue
;
continue
;
init_
ctx
->
ns_inherited
|=
ns_info
[
i
].
clone_flag
;
ctx
->
ns_inherited
|=
ns_info
[
i
].
clone_flag
;
}
}
}
}
pid
=
lxc_raw_getpid
();
ret
=
get_attach_context_nsfds
(
ctx
,
options
);
if
(
ret
)
{
for
(
i
=
0
;
i
<
LXC_NS_MAX
;
i
++
)
{
lxc_container_put
(
container
);
int
j
;
return
log_error
(
-
1
,
"Failed to get namespace file descriptors"
);
if
(
options
->
namespaces
&
ns_info
[
i
].
clone_flag
)
init_ctx
->
ns_fd
[
i
]
=
lxc_preserve_ns
(
init_pid
,
ns_info
[
i
].
proc_name
);
else
if
(
init_ctx
->
ns_inherited
&
ns_info
[
i
].
clone_flag
)
init_ctx
->
ns_fd
[
i
]
=
in_same_namespace
(
pid
,
init_pid
,
ns_info
[
i
].
proc_name
);
else
continue
;
if
(
init_ctx
->
ns_fd
[
i
]
>=
0
)
continue
;
if
(
init_ctx
->
ns_fd
[
i
]
==
-
EINVAL
)
{
DEBUG
(
"Inheriting %s namespace from %d"
,
ns_info
[
i
].
proc_name
,
pid
);
init_ctx
->
ns_inherited
&=
~
ns_info
[
i
].
clone_flag
;
continue
;
}
/* We failed to preserve the namespace. */
SYSERROR
(
"Failed to attach to %s namespace of %d"
,
ns_info
[
i
].
proc_name
,
pid
);
/* Close all already opened file descriptors before we return an
* error, so we don't leak them.
*/
for
(
j
=
0
;
j
<
i
;
j
++
)
close
(
init_ctx
->
ns_fd
[
j
]);
lxc_proc_put_context_info
(
init_ctx
);
return
-
1
;
}
}
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
{
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
{
ret
=
lxc_attach_terminal
(
name
,
lxcpath
,
conf
,
&
terminal
);
ret
=
lxc_attach_terminal
(
name
,
lxcpath
,
conf
,
&
terminal
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
ERROR
(
"Failed to setup new terminal"
);
put_attach_context
(
ctx
);
lxc_proc_put_context_info
(
init_ctx
);
return
log_error
(
-
1
,
"Failed to setup new terminal"
);
return
-
1
;
}
}
terminal
.
log_fd
=
options
->
log_fd
;
terminal
.
log_fd
=
options
->
log_fd
;
...
@@ -1137,9 +1204,8 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1137,9 +1204,8 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
*/
*/
ret
=
socketpair
(
PF_LOCAL
,
SOCK_STREAM
|
SOCK_CLOEXEC
,
0
,
ipc_sockets
);
ret
=
socketpair
(
PF_LOCAL
,
SOCK_STREAM
|
SOCK_CLOEXEC
,
0
,
ipc_sockets
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
SYSERROR
(
"Could not set up required IPC mechanism for attaching"
);
put_attach_context
(
ctx
);
lxc_proc_put_context_info
(
init_ctx
);
return
log_error_errno
(
-
1
,
errno
,
"Could not set up required IPC mechanism for attaching"
);
return
-
1
;
}
}
/* Create intermediate subprocess, two reasons:
/* Create intermediate subprocess, two reasons:
...
@@ -1151,12 +1217,13 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1151,12 +1217,13 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
*/
*/
pid
=
fork
();
pid
=
fork
();
if
(
pid
<
0
)
{
if
(
pid
<
0
)
{
SYSERROR
(
"Failed to create first subprocess"
);
put_attach_context
(
ctx
);
lxc_proc_put_context_info
(
init_ctx
);
return
log_error_errno
(
-
1
,
errno
,
"Failed to create first subprocess"
);
return
-
1
;
}
}
if
(
pid
==
0
)
{
if
(
pid
==
0
)
{
char
*
cwd
,
*
new_cwd
;
/* close unneeded file descriptors */
/* close unneeded file descriptors */
close_prot_errno_disarm
(
ipc_sockets
[
0
]);
close_prot_errno_disarm
(
ipc_sockets
[
0
]);
...
@@ -1167,28 +1234,42 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1167,28 +1234,42 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
}
}
/* Wait for the parent to have setup cgroups. */
/* Wait for the parent to have setup cgroups. */
ret
=
lxc_read_nointr
(
ipc_sockets
[
1
],
&
status
,
sizeof
(
status
));
if
(
!
sync_wait
(
ipc_sockets
[
1
],
ATTACH_SYNC_CGROUP
))
{
if
(
ret
!=
sizeof
(
status
))
{
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
put_attach_context
(
ctx
);
_exit
(
EXIT_FAILURE
);
}
if
(
!
attach_context_security_barrier
(
ctx
))
{
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
lxc_proc_put_context_info
(
init_
ctx
);
put_attach_context
(
ctx
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
TRACE
(
"Intermediate process starting to initialize"
);
TRACE
(
"Intermediate process starting to initialize"
);
/* Attach now, create another subprocess later, since pid namespaces
cwd
=
getcwd
(
NULL
,
0
);
* only really affect the children of the current process.
/*
* Attach now, create another subprocess later, since pid
* namespaces only really affect the children of the current
* process.
*
* Note that this is a crucial barrier. We're no moving into
* the container's context so we need to make sure to not leak
* anything sensitive. That especially means things such as
* open file descriptors!
*/
*/
ret
=
lxc_attach_to_ns
(
init_pid
,
init_
ctx
);
ret
=
attach_context_container
(
ctx
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
ERROR
(
"Failed to enter namespaces"
);
ERROR
(
"Failed to enter namespaces"
);
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
lxc_proc_put_context_info
(
init_
ctx
);
put_attach_context
(
ctx
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
/* close namespace file descriptors */
/* close namespace file descriptors */
lxc_proc_close_ns_fd
(
init_
ctx
);
close_nsfds
(
ctx
);
/* Attach succeeded, try to cwd. */
/* Attach succeeded, try to cwd. */
if
(
options
->
initial_cwd
)
if
(
options
->
initial_cwd
)
...
@@ -1200,24 +1281,27 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1200,24 +1281,27 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
if
(
ret
<
0
)
if
(
ret
<
0
)
WARN
(
"Could not change directory to
\"
%s
\"
"
,
new_cwd
);
WARN
(
"Could not change directory to
\"
%s
\"
"
,
new_cwd
);
}
}
free_disarm
(
cwd
);
/* Create attached process. */
/* Create attached process. */
payload
.
ipc_socket
=
ipc_sockets
[
1
];
payload
.
options
=
options
;
payload
.
init_ctx
=
init_ctx
;
payload
.
terminal_pts_fd
=
terminal
.
pty
;
payload
.
exec_function
=
exec_function
;
payload
.
exec_payload
=
exec_payload
;
pid
=
lxc_raw_clone
(
CLONE_PARENT
,
NULL
);
pid
=
lxc_raw_clone
(
CLONE_PARENT
,
NULL
);
if
(
pid
<
0
)
{
if
(
pid
<
0
)
{
SYSERROR
(
"Failed to clone attached process"
);
SYSERROR
(
"Failed to clone attached process"
);
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
lxc_proc_put_context_info
(
init_
ctx
);
put_attach_context
(
ctx
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
if
(
pid
==
0
)
{
if
(
pid
==
0
)
{
struct
attach_payload
ap
=
{
.
ipc_socket
=
ipc_sockets
[
1
],
.
options
=
options
,
.
ctx
=
ctx
,
.
terminal_pts_fd
=
terminal
.
pty
,
.
exec_function
=
exec_function
,
.
exec_payload
=
exec_payload
,
};
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
{
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
{
ret
=
lxc_terminal_signal_sigmask_safe_blocked
(
&
terminal
);
ret
=
lxc_terminal_signal_sigmask_safe_blocked
(
&
terminal
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
...
@@ -1226,15 +1310,15 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1226,15 +1310,15 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
}
}
}
}
do_attach
(
&
payload
);
/* Does not return. */
do_attach
(
&
ap
);
}
}
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
lxc_attach_terminal_close_pts
(
&
terminal
);
lxc_attach_terminal_close_pts
(
&
terminal
);
/* Tell grandparent the pid of the pid of the newly created child. */
/* Tell grandparent the pid of the pid of the newly created child. */
ret
=
lxc_write_nointr
(
ipc_sockets
[
1
],
&
pid
,
sizeof
(
pid
));
if
(
!
sync_wake_pid
(
ipc_sockets
[
1
],
ATTACH_SYNC_PID
(
pid
)))
{
if
(
ret
!=
sizeof
(
pid
))
{
/* If this really happens here, this is very unfortunate, since
/* If this really happens here, this is very unfortunate, since
* the parent will not know the pid of the attached process and
* the parent will not know the pid of the attached process and
* will not be able to wait for it (and we won't either due to
* will not be able to wait for it (and we won't either due to
...
@@ -1242,23 +1326,22 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1242,23 +1326,22 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
* attached process will remain a zombie.
* attached process will remain a zombie.
*/
*/
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
shutdown
(
ipc_sockets
[
1
],
SHUT_RDWR
);
lxc_proc_put_context_info
(
init_
ctx
);
put_attach_context
(
ctx
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
TRACE
(
"Sending pid %d of attached process"
,
pid
);
TRACE
(
"Sending pid %d of attached process"
,
pid
);
/* The rest is in the hands of the initial and the attached process. */
/* The rest is in the hands of the initial and the attached process. */
lxc_proc_put_context_info
(
init_
ctx
);
put_attach_context
(
ctx
);
_exit
(
EXIT_SUCCESS
);
_exit
(
EXIT_SUCCESS
);
}
}
to_cleanup_pid
=
pid
;
to_cleanup_pid
=
pid
;
/* close unneeded file descriptors */
/* close unneeded file descriptors */
close
(
ipc_sockets
[
1
]);
close_prot_errno_disarm
(
ipc_sockets
[
1
]);
free_disarm
(
cwd
);
close_nsfds
(
ctx
);
lxc_proc_close_ns_fd
(
init_ctx
);
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
lxc_attach_terminal_close_pts
(
&
terminal
);
lxc_attach_terminal_close_pts
(
&
terminal
);
...
@@ -1309,22 +1392,19 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1309,22 +1392,19 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
}
}
/* Let the child process know to go ahead. */
/* Let the child process know to go ahead. */
status
=
0
;
if
(
!
sync_wake
(
ipc_sockets
[
0
],
ATTACH_SYNC_CGROUP
))
ret
=
lxc_write_nointr
(
ipc_sockets
[
0
],
&
status
,
sizeof
(
status
));
if
(
ret
!=
sizeof
(
status
))
goto
close_mainloop
;
goto
close_mainloop
;
TRACE
(
"Told intermediate process to start initializing"
);
TRACE
(
"Told intermediate process to start initializing"
);
/* Get pid of attached process from intermediate process. */
/* Get pid of attached process from intermediate process. */
ret
=
lxc_read_nointr
(
ipc_sockets
[
0
],
&
attached_pid
,
sizeof
(
attached_pid
));
if
(
!
sync_wait_pid
(
ipc_sockets
[
0
],
ATTACH_SYNC_PID
(
&
attached_pid
)))
if
(
ret
!=
sizeof
(
attached_pid
))
goto
close_mainloop
;
goto
close_mainloop
;
TRACE
(
"Received pid %d of attached process in parent pid namespace"
,
attached_pid
);
TRACE
(
"Received pid %d of attached process in parent pid namespace"
,
attached_pid
);
/* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */
/* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */
if
(
options
->
stdin_fd
==
0
)
{
if
(
options
->
stdin_fd
==
STDIN_FILENO
)
{
signal
(
SIGINT
,
SIG_IGN
);
signal
(
SIGINT
,
SIG_IGN
);
signal
(
SIGQUIT
,
SIG_IGN
);
signal
(
SIGQUIT
,
SIG_IGN
);
}
}
...
@@ -1341,13 +1421,12 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1341,13 +1421,12 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
/* Open LSM fd and send it to child. */
/* Open LSM fd and send it to child. */
if
((
options
->
namespaces
&
CLONE_NEWNS
)
&&
if
((
options
->
namespaces
&
CLONE_NEWNS
)
&&
(
options
->
attach_flags
&
LXC_ATTACH_LSM
)
&&
init_
ctx
->
lsm_label
)
{
(
options
->
attach_flags
&
LXC_ATTACH_LSM
)
&&
ctx
->
lsm_label
)
{
__do_close
int
labelfd
=
-
EBADF
;
__do_close
int
labelfd
=
-
EBADF
;
bool
on_exec
;
bool
on_exec
;
ret
=
-
1
;
on_exec
=
options
->
attach_flags
&
LXC_ATTACH_LSM_EXEC
?
true
:
false
;
on_exec
=
options
->
attach_flags
&
LXC_ATTACH_LSM_EXEC
?
true
:
false
;
labelfd
=
init_ctx
->
lsm_ops
->
process_label_fd_get
(
init_
ctx
->
lsm_ops
,
labelfd
=
ctx
->
lsm_ops
->
process_label_fd_get
(
ctx
->
lsm_ops
,
attached_pid
,
on_exec
);
attached_pid
,
on_exec
);
if
(
labelfd
<
0
)
if
(
labelfd
<
0
)
goto
close_mainloop
;
goto
close_mainloop
;
...
@@ -1355,9 +1434,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1355,9 +1434,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
TRACE
(
"Opened LSM label file descriptor %d"
,
labelfd
);
TRACE
(
"Opened LSM label file descriptor %d"
,
labelfd
);
/* Send child fd of the LSM security module to write to. */
/* Send child fd of the LSM security module to write to. */
ret
=
lxc_abstract_unix_send_fds
(
ipc_sockets
[
0
],
&
labelfd
,
1
,
NULL
,
0
);
if
(
!
sync_wake_fd
(
ipc_sockets
[
0
],
ATTACH_SYNC_LSM
(
labelfd
)))
{
if
(
ret
<=
0
)
{
if
(
ret
<
0
)
SYSERROR
(
"Failed to send lsm label fd"
);
SYSERROR
(
"Failed to send lsm label fd"
);
goto
close_mainloop
;
goto
close_mainloop
;
}
}
...
@@ -1384,8 +1461,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
...
@@ -1384,8 +1461,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
/* Now shut down communication with child, we're done. */
/* Now shut down communication with child, we're done. */
shutdown
(
ipc_sockets
[
0
],
SHUT_RDWR
);
shutdown
(
ipc_sockets
[
0
],
SHUT_RDWR
);
close
(
ipc_sockets
[
0
]);
close_prot_errno_disarm
(
ipc_sockets
[
0
]);
ipc_sockets
[
0
]
=
-
1
;
ret_parent
=
0
;
ret_parent
=
0
;
to_cleanup_pid
=
-
1
;
to_cleanup_pid
=
-
1
;
...
@@ -1405,7 +1481,7 @@ close_mainloop:
...
@@ -1405,7 +1481,7 @@ close_mainloop:
on_error:
on_error:
if
(
ipc_sockets
[
0
]
>=
0
)
{
if
(
ipc_sockets
[
0
]
>=
0
)
{
shutdown
(
ipc_sockets
[
0
],
SHUT_RDWR
);
shutdown
(
ipc_sockets
[
0
],
SHUT_RDWR
);
close
(
ipc_sockets
[
0
]);
close
_prot_errno_disarm
(
ipc_sockets
[
0
]);
}
}
if
(
to_cleanup_pid
>
0
)
if
(
to_cleanup_pid
>
0
)
...
@@ -1416,7 +1492,7 @@ on_error:
...
@@ -1416,7 +1492,7 @@ on_error:
lxc_terminal_conf_free
(
&
terminal
);
lxc_terminal_conf_free
(
&
terminal
);
}
}
lxc_proc_put_context_info
(
init_
ctx
);
put_attach_context
(
ctx
);
return
ret_parent
;
return
ret_parent
;
}
}
...
...
src/lxc/attach.h
View file @
0e24c560
...
@@ -11,16 +11,7 @@
...
@@ -11,16 +11,7 @@
#include "namespace.h"
#include "namespace.h"
struct
lxc_conf
;
struct
lxc_conf
;
struct
lxc_container
;
struct
lxc_proc_context_info
{
char
*
lsm_label
;
struct
lxc_container
*
container
;
signed
long
personality
;
unsigned
long
long
capability_mask
;
int
ns_inherited
;
int
ns_fd
[
LXC_NS_MAX
];
struct
lsm_ops
*
lsm_ops
;
};
__hidden
extern
int
lxc_attach
(
struct
lxc_container
*
container
,
lxc_attach_exec_t
exec_function
,
__hidden
extern
int
lxc_attach
(
struct
lxc_container
*
container
,
lxc_attach_exec_t
exec_function
,
void
*
exec_payload
,
lxc_attach_options_t
*
options
,
void
*
exec_payload
,
lxc_attach_options_t
*
options
,
...
...
src/lxc/attach_options.h
View file @
0e24c560
...
@@ -124,7 +124,7 @@ typedef struct lxc_attach_options_t {
...
@@ -124,7 +124,7 @@ typedef struct lxc_attach_options_t {
{ \
{ \
/* .attach_flags = */
LXC_ATTACH_DEFAULT, \
/* .attach_flags = */
LXC_ATTACH_DEFAULT, \
/* .namespaces = */
-1, \
/* .namespaces = */
-1, \
/* .personality = */
-1,
\
/* .personality = */
0xffffffff,
\
/* .initial_cwd = */
NULL, \
/* .initial_cwd = */
NULL, \
/* .uid = */
(uid_t)-1, \
/* .uid = */
(uid_t)-1, \
/* .gid = */
(gid_t)-1, \
/* .gid = */
(gid_t)-1, \
...
...
src/lxc/confile.c
View file @
0e24c560
...
@@ -3031,7 +3031,7 @@ signed long lxc_config_parse_arch(const char *arch)
...
@@ -3031,7 +3031,7 @@ signed long lxc_config_parse_arch(const char *arch)
return
pername
[
i
].
per
;
return
pername
[
i
].
per
;
#endif
#endif
return
-
1
;
return
LXC_ARCH_UNCHANGED
;
}
}
int
lxc_fill_elevated_privileges
(
char
*
flaglist
,
int
*
flags
)
int
lxc_fill_elevated_privileges
(
char
*
flaglist
,
int
*
flags
)
...
...
src/lxc/confile.h
View file @
0e24c560
...
@@ -78,7 +78,12 @@ __hidden extern bool lxc_config_define_load(struct lxc_list *defines, struct lxc
...
@@ -78,7 +78,12 @@ __hidden extern bool lxc_config_define_load(struct lxc_list *defines, struct lxc
__hidden
extern
void
lxc_config_define_free
(
struct
lxc_list
*
defines
);
__hidden
extern
void
lxc_config_define_free
(
struct
lxc_list
*
defines
);
/* needed for lxc-attach */
#define LXC_ARCH_UNCHANGED 0xffffffffL
/*
* Parse personality of the container. Returns LXC_ARCH_UNCHANGED if the
* personality is not know.
* (Used during attach.)
*/
__hidden
extern
signed
long
lxc_config_parse_arch
(
const
char
*
arch
);
__hidden
extern
signed
long
lxc_config_parse_arch
(
const
char
*
arch
);
__hidden
extern
int
lxc_fill_elevated_privileges
(
char
*
flaglist
,
int
*
flags
);
__hidden
extern
int
lxc_fill_elevated_privileges
(
char
*
flaglist
,
int
*
flags
);
...
...
src/lxc/lsm/lsm.c
View file @
0e24c560
...
@@ -21,7 +21,7 @@ __hidden extern struct lsm_ops *lsm_apparmor_ops_init(void);
...
@@ -21,7 +21,7 @@ __hidden extern struct lsm_ops *lsm_apparmor_ops_init(void);
__hidden
extern
struct
lsm_ops
*
lsm_selinux_ops_init
(
void
);
__hidden
extern
struct
lsm_ops
*
lsm_selinux_ops_init
(
void
);
__hidden
extern
struct
lsm_ops
*
lsm_nop_ops_init
(
void
);
__hidden
extern
struct
lsm_ops
*
lsm_nop_ops_init
(
void
);
struct
lsm_ops
*
lsm_init
(
void
)
struct
lsm_ops
*
lsm_init
_static
(
void
)
{
{
struct
lsm_ops
*
ops
=
NULL
;
struct
lsm_ops
*
ops
=
NULL
;
...
...
src/lxc/lsm/lsm.h
View file @
0e24c560
...
@@ -33,6 +33,6 @@ struct lsm_ops {
...
@@ -33,6 +33,6 @@ struct lsm_ops {
int
(
*
process_label_set_at
)(
struct
lsm_ops
*
ops
,
int
label_fd
,
const
char
*
label
,
bool
on_exec
);
int
(
*
process_label_set_at
)(
struct
lsm_ops
*
ops
,
int
label_fd
,
const
char
*
label
,
bool
on_exec
);
};
};
__hidden
extern
struct
lsm_ops
*
lsm_init
(
void
);
__hidden
extern
struct
lsm_ops
*
lsm_init
_static
(
void
);
#endif
/* __LXC_LSM_H */
#endif
/* __LXC_LSM_H */
src/lxc/macro.h
View file @
0e24c560
...
@@ -293,6 +293,19 @@
...
@@ -293,6 +293,19 @@
* +
* +
* \0 = 1
* \0 = 1
*/
*/
#define LXC_PROC_PID_LEN \
(6 + INTTYPE_TO_STRLEN(pid_t) + 1)
/* /proc/ = 6
* +
* <pid-as-str> = INTTYPE_TO_STRLEN(pid_t)
* +
* /fd/ = 4
* +
* <fd-as-str> = INTTYPE_TO_STRLEN(int)
* +
* \0 = 1
*/
#define LXC_PROC_PID_FD_LEN \
#define LXC_PROC_PID_FD_LEN \
(6 + INTTYPE_TO_STRLEN(pid_t) + 4 + INTTYPE_TO_STRLEN(int) + 1)
(6 + INTTYPE_TO_STRLEN(pid_t) + 4 + INTTYPE_TO_STRLEN(int) + 1)
...
...
src/lxc/namespace.c
View file @
0e24c560
...
@@ -38,14 +38,14 @@ lxc_log_define(namespace, lxc);
...
@@ -38,14 +38,14 @@ lxc_log_define(namespace, lxc);
* linux/fs/namespace.c:mntns_install().
* linux/fs/namespace.c:mntns_install().
*/
*/
const
struct
ns_info
ns_info
[
LXC_NS_MAX
]
=
{
const
struct
ns_info
ns_info
[
LXC_NS_MAX
]
=
{
[
LXC_NS_USER
]
=
{
"user"
,
CLONE_NEWUSER
,
"CLONE_NEWUSER"
,
"LXC_USER_NS"
},
[
LXC_NS_USER
]
=
{
"user"
,
"ns/user"
,
CLONE_NEWUSER
,
"CLONE_NEWUSER"
,
"LXC_USER_NS"
},
[
LXC_NS_MNT
]
=
{
"mnt"
,
CLONE_NEWNS
,
"CLONE_NEWNS"
,
"LXC_MNT_NS"
},
[
LXC_NS_MNT
]
=
{
"mnt"
,
"ns/mnt"
,
CLONE_NEWNS
,
"CLONE_NEWNS"
,
"LXC_MNT_NS"
},
[
LXC_NS_PID
]
=
{
"pid"
,
CLONE_NEWPID
,
"CLONE_NEWPID"
,
"LXC_PID_NS"
},
[
LXC_NS_PID
]
=
{
"pid"
,
"ns/pid"
,
CLONE_NEWPID
,
"CLONE_NEWPID"
,
"LXC_PID_NS"
},
[
LXC_NS_UTS
]
=
{
"uts"
,
CLONE_NEWUTS
,
"CLONE_NEWUTS"
,
"LXC_UTS_NS"
},
[
LXC_NS_UTS
]
=
{
"uts"
,
"ns/uts"
,
CLONE_NEWUTS
,
"CLONE_NEWUTS"
,
"LXC_UTS_NS"
},
[
LXC_NS_IPC
]
=
{
"ipc"
,
CLONE_NEWIPC
,
"CLONE_NEWIPC"
,
"LXC_IPC_NS"
},
[
LXC_NS_IPC
]
=
{
"ipc"
,
"ns/ipc"
,
CLONE_NEWIPC
,
"CLONE_NEWIPC"
,
"LXC_IPC_NS"
},
[
LXC_NS_NET
]
=
{
"net"
,
CLONE_NEWNET
,
"CLONE_NEWNET"
,
"LXC_NET_NS"
},
[
LXC_NS_NET
]
=
{
"net"
,
"ns/net"
,
CLONE_NEWNET
,
"CLONE_NEWNET"
,
"LXC_NET_NS"
},
[
LXC_NS_CGROUP
]
=
{
"cgroup"
,
CLONE_NEWCGROUP
,
"CLONE_NEWCGROUP"
,
"LXC_CGROUP_NS"
},
[
LXC_NS_CGROUP
]
=
{
"cgroup"
,
"ns/cgroup"
,
CLONE_NEWCGROUP
,
"CLONE_NEWCGROUP"
,
"LXC_CGROUP_NS"
},
[
LXC_NS_TIME
]
=
{
"time"
,
CLONE_NEWTIME
,
"CLONE_NEWTIME"
,
"LXC_TIME_NS"
},
[
LXC_NS_TIME
]
=
{
"time"
,
"ns/time"
,
CLONE_NEWTIME
,
"CLONE_NEWTIME"
,
"LXC_TIME_NS"
},
};
};
int
lxc_namespace_2_cloneflag
(
const
char
*
namespace
)
int
lxc_namespace_2_cloneflag
(
const
char
*
namespace
)
...
...
src/lxc/namespace.h
View file @
0e24c560
...
@@ -23,6 +23,7 @@ enum {
...
@@ -23,6 +23,7 @@ enum {
__hidden
extern
const
struct
ns_info
{
__hidden
extern
const
struct
ns_info
{
const
char
*
proc_name
;
const
char
*
proc_name
;
const
char
*
proc_path
;
int
clone_flag
;
int
clone_flag
;
const
char
*
flag_name
;
const
char
*
flag_name
;
const
char
*
env_name
;
const
char
*
env_name
;
...
...
src/lxc/start.c
View file @
0e24c560
...
@@ -728,7 +728,7 @@ int lxc_init(const char *name, struct lxc_handler *handler)
...
@@ -728,7 +728,7 @@ int lxc_init(const char *name, struct lxc_handler *handler)
if
(
status_fd
<
0
)
if
(
status_fd
<
0
)
return
log_error_errno
(
-
1
,
errno
,
"Failed to open monitor status fd"
);
return
log_error_errno
(
-
1
,
errno
,
"Failed to open monitor status fd"
);
handler
->
lsm_ops
=
lsm_init
();
handler
->
lsm_ops
=
lsm_init
_static
();
TRACE
(
"Initialized LSM"
);
TRACE
(
"Initialized LSM"
);
/* Begin by setting the state to STARTING. */
/* Begin by setting the state to STARTING. */
...
@@ -1069,8 +1069,7 @@ static int do_start(void *data)
...
@@ -1069,8 +1069,7 @@ static int do_start(void *data)
/* Don't leak the pinfd to the container. */
/* Don't leak the pinfd to the container. */
close_prot_errno_disarm
(
handler
->
pinfd
);
close_prot_errno_disarm
(
handler
->
pinfd
);
ret
=
lxc_sync_wait_parent
(
handler
,
LXC_SYNC_STARTUP
);
if
(
!
lxc_sync_wait_parent
(
handler
,
START_SYNC_STARTUP
))
if
(
ret
<
0
)
goto
out_warn_father
;
goto
out_warn_father
;
/* Unshare CLONE_NEWNET after CLONE_NEWUSER. See
/* Unshare CLONE_NEWNET after CLONE_NEWUSER. See
...
@@ -1088,8 +1087,7 @@ static int do_start(void *data)
...
@@ -1088,8 +1087,7 @@ static int do_start(void *data)
/* Tell the parent task it can begin to configure the container and wait
/* Tell the parent task it can begin to configure the container and wait
* for it to finish.
* for it to finish.
*/
*/
ret
=
lxc_sync_barrier_parent
(
handler
,
LXC_SYNC_CONFIGURE
);
if
(
!
lxc_sync_barrier_parent
(
handler
,
START_SYNC_CONFIGURE
))
if
(
ret
<
0
)
goto
out_error
;
goto
out_error
;
if
(
handler
->
ns_clone_flags
&
CLONE_NEWNET
)
{
if
(
handler
->
ns_clone_flags
&
CLONE_NEWNET
)
{
...
@@ -1168,8 +1166,7 @@ static int do_start(void *data)
...
@@ -1168,8 +1166,7 @@ static int do_start(void *data)
}
}
/* Ask father to setup cgroups and wait for him to finish. */
/* Ask father to setup cgroups and wait for him to finish. */
ret
=
lxc_sync_barrier_parent
(
handler
,
LXC_SYNC_CGROUP
);
if
(
!
lxc_sync_barrier_parent
(
handler
,
START_SYNC_CGROUP
))
if
(
ret
<
0
)
goto
out_error
;
goto
out_error
;
/* Unshare cgroup namespace after we have setup our cgroups. If we do it
/* Unshare cgroup namespace after we have setup our cgroups. If we do it
...
@@ -1353,8 +1350,7 @@ static int do_start(void *data)
...
@@ -1353,8 +1350,7 @@ static int do_start(void *data)
}
}
}
}
ret
=
lxc_sync_barrier_parent
(
handler
,
LXC_SYNC_CGROUP_LIMITS
);
if
(
!
lxc_sync_barrier_parent
(
handler
,
START_SYNC_CGROUP_LIMITS
))
if
(
ret
<
0
)
goto
out_warn_father
;
goto
out_warn_father
;
/* Reset the environment variables the user requested in a clear
/* Reset the environment variables the user requested in a clear
...
@@ -1447,7 +1443,7 @@ out_warn_father:
...
@@ -1447,7 +1443,7 @@ out_warn_father:
* We want the parent to know something went wrong, so we return a
* We want the parent to know something went wrong, so we return a
* special error code.
* special error code.
*/
*/
lxc_sync_wake_parent
(
handler
,
LXC_
SYNC_ERROR
);
lxc_sync_wake_parent
(
handler
,
SYNC_ERROR
);
out_error:
out_error:
return
-
1
;
return
-
1
;
...
@@ -1630,8 +1626,7 @@ static int lxc_spawn(struct lxc_handler *handler)
...
@@ -1630,8 +1626,7 @@ static int lxc_spawn(struct lxc_handler *handler)
share_ns
=
true
;
share_ns
=
true
;
}
}
ret
=
lxc_sync_init
(
handler
);
if
(
!
lxc_sync_init
(
handler
))
if
(
ret
<
0
)
return
-
1
;
return
-
1
;
ret
=
socketpair
(
AF_UNIX
,
SOCK_STREAM
|
SOCK_CLOEXEC
,
0
,
ret
=
socketpair
(
AF_UNIX
,
SOCK_STREAM
|
SOCK_CLOEXEC
,
0
,
...
@@ -1790,12 +1785,10 @@ static int lxc_spawn(struct lxc_handler *handler)
...
@@ -1790,12 +1785,10 @@ static int lxc_spawn(struct lxc_handler *handler)
}
}
}
}
ret
=
lxc_sync_wake_child
(
handler
,
LXC_SYNC_STARTUP
);
if
(
!
lxc_sync_wake_child
(
handler
,
START_SYNC_STARTUP
))
if
(
ret
<
0
)
goto
out_delete_net
;
goto
out_delete_net
;
ret
=
lxc_sync_wait_child
(
handler
,
LXC_SYNC_CONFIGURE
);
if
(
!
lxc_sync_wait_child
(
handler
,
START_SYNC_CONFIGURE
))
if
(
ret
<
0
)
goto
out_delete_net
;
goto
out_delete_net
;
if
(
!
cgroup_ops
->
setup_limits_legacy
(
cgroup_ops
,
handler
->
conf
,
false
))
{
if
(
!
cgroup_ops
->
setup_limits_legacy
(
cgroup_ops
,
handler
->
conf
,
false
))
{
...
@@ -1862,10 +1855,9 @@ static int lxc_spawn(struct lxc_handler *handler)
...
@@ -1862,10 +1855,9 @@ static int lxc_spawn(struct lxc_handler *handler)
}
}
/* Tell the child to continue its initialization. We'll get
/* Tell the child to continue its initialization. We'll get
*
LXC
_SYNC_CGROUP when it is ready for us to setup cgroups.
*
START
_SYNC_CGROUP when it is ready for us to setup cgroups.
*/
*/
ret
=
lxc_sync_barrier_child
(
handler
,
LXC_SYNC_POST_CONFIGURE
);
if
(
!
lxc_sync_barrier_child
(
handler
,
START_SYNC_POST_CONFIGURE
))
if
(
ret
<
0
)
goto
out_delete_net
;
goto
out_delete_net
;
if
(
!
lxc_list_empty
(
&
conf
->
limits
))
{
if
(
!
lxc_list_empty
(
&
conf
->
limits
))
{
...
@@ -1876,8 +1868,7 @@ static int lxc_spawn(struct lxc_handler *handler)
...
@@ -1876,8 +1868,7 @@ static int lxc_spawn(struct lxc_handler *handler)
}
}
}
}
ret
=
lxc_sync_barrier_child
(
handler
,
LXC_SYNC_CGROUP_UNSHARE
);
if
(
!
lxc_sync_barrier_child
(
handler
,
START_SYNC_CGROUP_UNSHARE
))
if
(
ret
<
0
)
goto
out_delete_net
;
goto
out_delete_net
;
/*
/*
...
@@ -1937,12 +1928,11 @@ static int lxc_spawn(struct lxc_handler *handler)
...
@@ -1937,12 +1928,11 @@ static int lxc_spawn(struct lxc_handler *handler)
/* Tell the child to complete its initialization and wait for it to exec
/* Tell the child to complete its initialization and wait for it to exec
* or return an error. (The child will never return
* or return an error. (The child will never return
*
LXC
_SYNC_READY_START+1. It will either close the sync pipe, causing
*
START
_SYNC_READY_START+1. It will either close the sync pipe, causing
* lxc_sync_barrier_child to return success, or return a different
* lxc_sync_barrier_child to return success, or return a different
* value, causing us to error out).
* value, causing us to error out).
*/
*/
ret
=
lxc_sync_barrier_child
(
handler
,
LXC_SYNC_READY_START
);
if
(
!
lxc_sync_barrier_child
(
handler
,
START_SYNC_READY_START
))
if
(
ret
<
0
)
goto
out_delete_net
;
goto
out_delete_net
;
if
(
handler
->
ns_clone_flags
&
CLONE_NEWNET
)
{
if
(
handler
->
ns_clone_flags
&
CLONE_NEWNET
)
{
...
...
src/lxc/sync.c
View file @
0e24c560
...
@@ -17,101 +17,129 @@
...
@@ -17,101 +17,129 @@
lxc_log_define
(
sync
,
lxc
);
lxc_log_define
(
sync
,
lxc
);
static
int
__
sync_wait
(
int
fd
,
int
sequence
)
bool
sync_wait
(
int
fd
,
int
sequence
)
{
{
int
sync
=
-
1
;
int
sync
=
-
1
;
ssize_t
ret
;
ssize_t
ret
;
ret
=
lxc_read_nointr
(
fd
,
&
sync
,
sizeof
(
sync
));
ret
=
lxc_read_nointr
(
fd
,
&
sync
,
sizeof
(
sync
));
if
(
ret
<
0
)
if
(
ret
<
0
)
return
log_error_errno
(
-
1
,
errno
,
"Sync wait failure"
);
return
log_error_errno
(
false
,
errno
,
"Sync wait failure"
);
if
(
!
ret
)
if
(
!
ret
)
return
0
;
return
true
;
if
((
size_t
)
ret
!=
sizeof
(
sync
))
if
((
size_t
)
ret
!=
sizeof
(
sync
))
return
log_error
(
-
1
,
"Unexpected sync size: %zu expected %zu"
,
(
size_t
)
ret
,
sizeof
(
sync
));
return
log_error
(
false
,
"Unexpected sync size: %zu expected %zu"
,
(
size_t
)
ret
,
sizeof
(
sync
));
if
(
sync
==
LXC_
SYNC_ERROR
)
if
(
sync
==
SYNC_ERROR
)
return
log_error
(
-
1
,
"An error occurred in another process (expected sequence number %d)"
,
sequence
);
return
log_error
(
false
,
"An error occurred in another process (expected sequence number %d)"
,
sequence
);
if
(
sync
!=
sequence
)
if
(
sync
!=
sequence
)
return
log_error
(
-
1
,
"Invalid sequence number %d. Expected sequence number %d"
,
sync
,
sequence
);
return
log_error
(
false
,
"Invalid sequence number %d. Expected sequence number %d"
,
sync
,
sequence
);
return
0
;
return
true
;
}
}
static
int
__
sync_wake
(
int
fd
,
int
sequence
)
bool
sync_wake
(
int
fd
,
int
sequence
)
{
{
int
sync
=
sequence
;
int
sync
=
sequence
;
if
(
lxc_write_nointr
(
fd
,
&
sync
,
sizeof
(
sync
))
<
0
)
if
(
lxc_write_nointr
(
fd
,
&
sync
,
sizeof
(
sync
))
<
0
)
return
log_error_errno
(
-
1
,
errno
,
"Sync wake failure"
);
return
log_error_errno
(
false
,
errno
,
"Sync wake failure"
);
return
0
;
return
true
;
}
}
static
int
__sync_barrier
(
int
fd
,
int
sequence
)
static
bool
__sync_barrier
(
int
fd
,
int
sequence
)
{
{
if
(
__
sync_wake
(
fd
,
sequence
))
if
(
!
sync_wake
(
fd
,
sequence
))
return
-
1
;
return
false
;
return
__
sync_wait
(
fd
,
sequence
+
1
);
return
sync_wait
(
fd
,
sequence
+
1
);
}
}
int
lxc_sync_barrier_parent
(
struct
lxc_handler
*
handler
,
int
sequence
)
static
inline
const
char
*
start_sync_to_string
(
int
state
)
{
switch
(
state
)
{
case
START_SYNC_STARTUP
:
return
"startup"
;
case
START_SYNC_CONFIGURE
:
return
"configure"
;
case
START_SYNC_POST_CONFIGURE
:
return
"post-configure"
;
case
START_SYNC_CGROUP
:
return
"cgroup"
;
case
START_SYNC_CGROUP_UNSHARE
:
return
"cgroup-unshare"
;
case
START_SYNC_CGROUP_LIMITS
:
return
"cgroup-limits"
;
case
START_SYNC_READY_START
:
return
"ready-start"
;
case
START_SYNC_RESTART
:
return
"restart"
;
case
START_SYNC_POST_RESTART
:
return
"post-restart"
;
case
SYNC_ERROR
:
return
"error"
;
default:
return
"invalid sync state"
;
}
}
bool
lxc_sync_barrier_parent
(
struct
lxc_handler
*
handler
,
int
sequence
)
{
{
TRACE
(
"Child waking parent with sequence %s and waiting for sequence %s"
,
TRACE
(
"Child waking parent with sequence %s and waiting for sequence %s"
,
s
ync_to_string
(
sequence
),
sync_to_string
(
sequence
+
1
));
s
tart_sync_to_string
(
sequence
),
start_
sync_to_string
(
sequence
+
1
));
return
__sync_barrier
(
handler
->
sync_sock
[
0
],
sequence
);
return
__sync_barrier
(
handler
->
sync_sock
[
0
],
sequence
);
}
}
int
lxc_sync_barrier_child
(
struct
lxc_handler
*
handler
,
int
sequence
)
bool
lxc_sync_barrier_child
(
struct
lxc_handler
*
handler
,
int
sequence
)
{
{
TRACE
(
"Parent waking child with sequence %s and waiting with sequence %s"
,
TRACE
(
"Parent waking child with sequence %s and waiting with sequence %s"
,
s
ync_to_string
(
sequence
),
sync_to_string
(
sequence
+
1
));
s
tart_sync_to_string
(
sequence
),
start_
sync_to_string
(
sequence
+
1
));
return
__sync_barrier
(
handler
->
sync_sock
[
1
],
sequence
);
return
__sync_barrier
(
handler
->
sync_sock
[
1
],
sequence
);
}
}
int
lxc_sync_wake_parent
(
struct
lxc_handler
*
handler
,
int
sequence
)
bool
lxc_sync_wake_parent
(
struct
lxc_handler
*
handler
,
int
sequence
)
{
{
TRACE
(
"Child waking parent with sequence %s"
,
sync_to_string
(
sequence
));
TRACE
(
"Child waking parent with sequence %s"
,
s
tart_s
ync_to_string
(
sequence
));
return
__
sync_wake
(
handler
->
sync_sock
[
0
],
sequence
);
return
sync_wake
(
handler
->
sync_sock
[
0
],
sequence
);
}
}
int
lxc_sync_wait_parent
(
struct
lxc_handler
*
handler
,
int
sequence
)
bool
lxc_sync_wait_parent
(
struct
lxc_handler
*
handler
,
int
sequence
)
{
{
TRACE
(
"Parent waiting for child with sequence %s"
,
sync_to_string
(
sequence
));
TRACE
(
"Parent waiting for child with sequence %s"
,
s
tart_s
ync_to_string
(
sequence
));
return
__
sync_wait
(
handler
->
sync_sock
[
0
],
sequence
);
return
sync_wait
(
handler
->
sync_sock
[
0
],
sequence
);
}
}
int
lxc_sync_wait_child
(
struct
lxc_handler
*
handler
,
int
sequence
)
bool
lxc_sync_wait_child
(
struct
lxc_handler
*
handler
,
int
sequence
)
{
{
TRACE
(
"Child waiting for parent with sequence %s"
,
sync_to_string
(
sequence
));
TRACE
(
"Child waiting for parent with sequence %s"
,
s
tart_s
ync_to_string
(
sequence
));
return
__
sync_wait
(
handler
->
sync_sock
[
1
],
sequence
);
return
sync_wait
(
handler
->
sync_sock
[
1
],
sequence
);
}
}
int
lxc_sync_wake_child
(
struct
lxc_handler
*
handler
,
int
sequence
)
bool
lxc_sync_wake_child
(
struct
lxc_handler
*
handler
,
int
sequence
)
{
{
TRACE
(
"Child waking parent with sequence %s"
,
sync_to_string
(
sequence
));
TRACE
(
"Child waking parent with sequence %s"
,
s
tart_s
ync_to_string
(
sequence
));
return
__
sync_wake
(
handler
->
sync_sock
[
1
],
sequence
);
return
sync_wake
(
handler
->
sync_sock
[
1
],
sequence
);
}
}
int
lxc_sync_init
(
struct
lxc_handler
*
handler
)
bool
lxc_sync_init
(
struct
lxc_handler
*
handler
)
{
{
int
ret
;
int
ret
;
ret
=
socketpair
(
AF_LOCAL
,
SOCK_STREAM
,
0
,
handler
->
sync_sock
);
ret
=
socketpair
(
AF_LOCAL
,
SOCK_STREAM
,
0
,
handler
->
sync_sock
);
if
(
ret
)
if
(
ret
)
return
log_error_errno
(
-
1
,
errno
,
"failed to create synchronization socketpair"
);
return
log_error_errno
(
false
,
errno
,
"failed to create synchronization socketpair"
);
/* Be sure we don't inherit this after the exec */
/* Be sure we don't inherit this after the exec */
ret
=
fcntl
(
handler
->
sync_sock
[
0
],
F_SETFD
,
FD_CLOEXEC
);
ret
=
fcntl
(
handler
->
sync_sock
[
0
],
F_SETFD
,
FD_CLOEXEC
);
if
(
ret
<
0
)
if
(
ret
<
0
)
return
log_error_errno
(
-
1
,
errno
,
"Failed to make socket close-on-exec"
);
return
log_error_errno
(
false
,
errno
,
"Failed to make socket close-on-exec"
);
TRACE
(
"Initialized synchronization infrastructure"
);
TRACE
(
"Initialized synchronization infrastructure"
);
return
0
;
return
true
;
}
}
void
lxc_sync_fini_child
(
struct
lxc_handler
*
handler
)
void
lxc_sync_fini_child
(
struct
lxc_handler
*
handler
)
...
...
src/lxc/sync.h
View file @
0e24c560
...
@@ -3,60 +3,46 @@
...
@@ -3,60 +3,46 @@
#ifndef __LXC_SYNC_H
#ifndef __LXC_SYNC_H
#define __LXC_SYNC_H
#define __LXC_SYNC_H
#include <stdbool.h>
#include "compiler.h"
#include "compiler.h"
struct
lxc_handler
;
struct
lxc_handler
;
enum
{
enum
/* generic */
{
LXC_SYNC_STARTUP
=
0
,
SYNC_ERROR
=
-
1
/* Used to report errors from another process */
LXC_SYNC_CONFIGURE
=
1
,
};
LXC_SYNC_POST_CONFIGURE
=
2
,
LXC_SYNC_CGROUP
=
3
,
enum
/* start */
{
LXC_SYNC_CGROUP_UNSHARE
=
4
,
START_SYNC_STARTUP
=
0
,
LXC_SYNC_CGROUP_LIMITS
=
5
,
START_SYNC_CONFIGURE
=
1
,
LXC_SYNC_READY_START
=
6
,
START_SYNC_POST_CONFIGURE
=
2
,
LXC_SYNC_RESTART
=
7
,
START_SYNC_CGROUP
=
3
,
LXC_SYNC_POST_RESTART
=
8
,
START_SYNC_CGROUP_UNSHARE
=
4
,
LXC_SYNC_ERROR
=
-
1
/* Used to report errors from another process */
START_SYNC_CGROUP_LIMITS
=
5
,
START_SYNC_READY_START
=
6
,
START_SYNC_RESTART
=
7
,
START_SYNC_POST_RESTART
=
8
,
};
};
static
inline
const
char
*
sync_to_string
(
int
state
)
enum
/* attach */
{
{
ATTACH_SYNC_CGROUP
=
0
,
switch
(
state
)
{
};
case
LXC_SYNC_STARTUP
:
return
"startup"
;
#define ATTACH_SYNC_PID(x) (x)
case
LXC_SYNC_CONFIGURE
:
#define ATTACH_SYNC_LSM(x) (x)
return
"configure"
;
case
LXC_SYNC_POST_CONFIGURE
:
__hidden
extern
bool
lxc_sync_init
(
struct
lxc_handler
*
handler
);
return
"post-configure"
;
case
LXC_SYNC_CGROUP
:
return
"cgroup"
;
case
LXC_SYNC_CGROUP_UNSHARE
:
return
"cgroup-unshare"
;
case
LXC_SYNC_CGROUP_LIMITS
:
return
"cgroup-limits"
;
case
LXC_SYNC_READY_START
:
return
"ready-start"
;
case
LXC_SYNC_RESTART
:
return
"restart"
;
case
LXC_SYNC_POST_RESTART
:
return
"post-restart"
;
case
LXC_SYNC_ERROR
:
return
"error"
;
default:
return
"invalid sync state"
;
}
}
__hidden
extern
int
lxc_sync_init
(
struct
lxc_handler
*
handler
);
__hidden
extern
void
lxc_sync_fini
(
struct
lxc_handler
*
);
__hidden
extern
void
lxc_sync_fini
(
struct
lxc_handler
*
);
__hidden
extern
void
lxc_sync_fini_parent
(
struct
lxc_handler
*
);
__hidden
extern
void
lxc_sync_fini_parent
(
struct
lxc_handler
*
);
__hidden
extern
void
lxc_sync_fini_child
(
struct
lxc_handler
*
);
__hidden
extern
void
lxc_sync_fini_child
(
struct
lxc_handler
*
);
__hidden
extern
int
lxc_sync_wake_child
(
struct
lxc_handler
*
,
int
);
__hidden
extern
bool
lxc_sync_wake_child
(
struct
lxc_handler
*
,
int
);
__hidden
extern
int
lxc_sync_wait_child
(
struct
lxc_handler
*
,
int
);
__hidden
extern
bool
lxc_sync_wait_child
(
struct
lxc_handler
*
,
int
);
__hidden
extern
int
lxc_sync_wake_parent
(
struct
lxc_handler
*
,
int
);
__hidden
extern
bool
lxc_sync_wake_parent
(
struct
lxc_handler
*
,
int
);
__hidden
extern
int
lxc_sync_wait_parent
(
struct
lxc_handler
*
,
int
);
__hidden
extern
bool
lxc_sync_wait_parent
(
struct
lxc_handler
*
,
int
);
__hidden
extern
int
lxc_sync_barrier_parent
(
struct
lxc_handler
*
,
int
);
__hidden
extern
bool
lxc_sync_barrier_parent
(
struct
lxc_handler
*
,
int
);
__hidden
extern
int
lxc_sync_barrier_child
(
struct
lxc_handler
*
,
int
);
__hidden
extern
bool
lxc_sync_barrier_child
(
struct
lxc_handler
*
,
int
);
__hidden
extern
bool
sync_wait
(
int
fd
,
int
sequence
);
__hidden
extern
bool
sync_wake
(
int
fd
,
int
sequence
);
#endif
/* __LXC_SYNC_H */
#endif
/* __LXC_SYNC_H */
src/tests/attach.c
View file @
0e24c560
...
@@ -400,7 +400,7 @@ int main(int argc, char *argv[])
...
@@ -400,7 +400,7 @@ int main(int argc, char *argv[])
(
void
)
strlcpy
(
template
,
P_tmpdir
"/attach_XXXXXX"
,
sizeof
(
template
));
(
void
)
strlcpy
(
template
,
P_tmpdir
"/attach_XXXXXX"
,
sizeof
(
template
));
lsm_ops
=
lsm_init
();
lsm_ops
=
lsm_init
_static
();
i
=
lxc_make_tmpfile
(
template
,
false
);
i
=
lxc_make_tmpfile
(
template
,
false
);
if
(
i
<
0
)
{
if
(
i
<
0
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment