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
2be8b365
Unverified
Commit
2be8b365
authored
Jul 05, 2020
by
Christian Brauner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
tree-wide: s/ptmx/ptx/g
Signed-off-by:
Christian Brauner
<
christian.brauner@ubuntu.com
>
parent
af5c9517
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
117 additions
and
117 deletions
+117
-117
openpty.c
src/include/openpty.c
+10
-10
openpty.h
src/include/openpty.h
+3
-3
attach.c
src/lxc/attach.c
+3
-3
commands.c
src/lxc/commands.c
+9
-9
commands.h
src/lxc/commands.h
+1
-1
conf.c
src/lxc/conf.c
+13
-13
lxccontainer.c
src/lxc/lxccontainer.c
+2
-2
lxccontainer.h
src/lxc/lxccontainer.h
+2
-2
start.c
src/lxc/start.c
+2
-2
terminal.c
src/lxc/terminal.c
+49
-49
terminal.h
src/lxc/terminal.h
+11
-11
console.c
src/tests/console.c
+12
-12
No files found.
src/include/openpty.c
View file @
2be8b365
...
...
@@ -32,25 +32,25 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#define _PATH_DEVPTMX "/dev/pt
m
x"
#define _PATH_DEVPTMX "/dev/ptx"
int
openpty
(
int
*
apt
m
x
,
int
*
apts
,
char
*
name
,
struct
termios
*
termp
,
int
openpty
(
int
*
aptx
,
int
*
apts
,
char
*
name
,
struct
termios
*
termp
,
struct
winsize
*
winp
)
{
char
buf
[
PATH_MAX
];
int
pt
m
x
,
pts
;
int
ptx
,
pts
;
pt
m
x
=
open
(
_PATH_DEVPTMX
,
O_RDWR
);
if
(
pt
m
x
==
-
1
)
ptx
=
open
(
_PATH_DEVPTMX
,
O_RDWR
);
if
(
ptx
==
-
1
)
return
-
1
;
if
(
grantpt
(
pt
m
x
))
if
(
grantpt
(
ptx
))
goto
fail
;
if
(
unlockpt
(
pt
m
x
))
if
(
unlockpt
(
ptx
))
goto
fail
;
if
(
ptsname_r
(
pt
m
x
,
buf
,
sizeof
buf
))
if
(
ptsname_r
(
ptx
,
buf
,
sizeof
buf
))
goto
fail
;
pts
=
open
(
buf
,
O_RDWR
|
O_NOCTTY
);
...
...
@@ -63,7 +63,7 @@ int openpty (int *aptmx, int *apts, char *name, struct termios *termp,
if
(
winp
)
ioctl
(
pts
,
TIOCSWINSZ
,
winp
);
*
apt
mx
=
ptm
x
;
*
apt
x
=
pt
x
;
*
apts
=
pts
;
if
(
name
!=
NULL
)
strcpy
(
name
,
buf
);
...
...
@@ -71,6 +71,6 @@ int openpty (int *aptmx, int *apts, char *name, struct termios *termp,
return
0
;
fail:
close
(
pt
m
x
);
close
(
ptx
);
return
-
1
;
}
src/include/openpty.h
View file @
2be8b365
...
...
@@ -28,11 +28,11 @@
#include <sys/ioctl.h>
/*
* Create pseudo tty pt
m
x pts pair with @__name and set terminal
* Create pseudo tty ptx pts pair with @__name and set terminal
* attributes according to @__termp and @__winp and return handles for both
* ends in @__apt
m
x and @__apts.
* ends in @__aptx and @__apts.
*/
extern
int
openpty
(
int
*
__apt
m
x
,
int
*
__apts
,
char
*
__name
,
extern
int
openpty
(
int
*
__aptx
,
int
*
__apts
,
char
*
__name
,
const
struct
termios
*
__termp
,
const
struct
winsize
*
__winp
);
...
...
src/lxc/attach.c
View file @
2be8b365
...
...
@@ -932,9 +932,9 @@ static int lxc_attach_terminal_mainloop_init(struct lxc_terminal *terminal,
return
0
;
}
static
inline
void
lxc_attach_terminal_close_pt
m
x
(
struct
lxc_terminal
*
terminal
)
static
inline
void
lxc_attach_terminal_close_ptx
(
struct
lxc_terminal
*
terminal
)
{
close_prot_errno_disarm
(
terminal
->
pt
m
x
);
close_prot_errno_disarm
(
terminal
->
ptx
);
}
static
inline
void
lxc_attach_terminal_close_pts
(
struct
lxc_terminal
*
terminal
)
...
...
@@ -1332,7 +1332,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
close_prot_errno_disarm
(
ipc_sockets
[
0
]);
if
(
options
->
attach_flags
&
LXC_ATTACH_TERMINAL
)
{
lxc_attach_terminal_close_pt
m
x
(
&
terminal
);
lxc_attach_terminal_close_ptx
(
&
terminal
);
lxc_attach_terminal_close_peer
(
&
terminal
);
lxc_attach_terminal_close_log
(
&
terminal
);
}
...
...
src/lxc/commands.c
View file @
2be8b365
...
...
@@ -108,7 +108,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
* stored directly in data and datalen will be 0.
*
* As a special case, the response for LXC_CMD_CONSOLE is created
* here as it contains an fd for the pt
m
x pty passed through the
* here as it contains an fd for the ptx pty passed through the
* unix socket.
*/
static
int
lxc_cmd_rsp_recv
(
int
sock
,
struct
lxc_cmd_rr
*
cmd
)
...
...
@@ -139,7 +139,7 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
ENOMEM
,
"Failed to receive response for command
\"
%s
\"
"
,
lxc_cmd_str
(
cmd
->
req
.
cmd
));
rspdata
->
pt
m
xfd
=
move_fd
(
fd_rsp
);
rspdata
->
ptxfd
=
move_fd
(
fd_rsp
);
rspdata
->
ttynum
=
PTR_TO_INT
(
rsp
->
data
);
rsp
->
data
=
rspdata
;
}
...
...
@@ -844,7 +844,7 @@ static int lxc_cmd_terminal_winch_callback(int fd, struct lxc_cmd_req *req,
* @name : name of container to connect to
* @ttynum : in: the tty to open or -1 for next available
* : out: the tty allocated
* @fd : out: file descriptor for pt
m
x side of pty
* @fd : out: file descriptor for ptx side of pty
* @lxcpath : the lxcpath in which the container is running
*
* Returns fd holding tty allocated on success, < 0 on failure
...
...
@@ -871,11 +871,11 @@ int lxc_cmd_console(const char *name, int *ttynum, int *fd, const char *lxcpath)
if
(
ret
==
0
)
return
log_error
(
-
1
,
"tty number %d invalid, busy or all ttys busy"
,
*
ttynum
);
if
(
rspdata
->
pt
m
xfd
<
0
)
if
(
rspdata
->
ptxfd
<
0
)
return
log_error
(
-
1
,
"Unable to allocate fd for tty %d"
,
rspdata
->
ttynum
);
ret
=
cmd
.
rsp
.
ret
;
/* socket fd */
*
fd
=
rspdata
->
pt
m
xfd
;
*
fd
=
rspdata
->
ptxfd
;
*
ttynum
=
rspdata
->
ttynum
;
return
log_info
(
ret
,
"Alloced fd %d for tty %d via socket %d"
,
*
fd
,
rspdata
->
ttynum
,
ret
);
...
...
@@ -885,17 +885,17 @@ static int lxc_cmd_console_callback(int fd, struct lxc_cmd_req *req,
struct
lxc_handler
*
handler
,
struct
lxc_epoll_descr
*
descr
)
{
int
pt
m
xfd
,
ret
;
int
ptxfd
,
ret
;
struct
lxc_cmd_rsp
rsp
;
int
ttynum
=
PTR_TO_INT
(
req
->
data
);
pt
m
xfd
=
lxc_terminal_allocate
(
handler
->
conf
,
fd
,
&
ttynum
);
if
(
pt
m
xfd
<
0
)
ptxfd
=
lxc_terminal_allocate
(
handler
->
conf
,
fd
,
&
ttynum
);
if
(
ptxfd
<
0
)
return
LXC_CMD_REAP_CLIENT_FD
;
memset
(
&
rsp
,
0
,
sizeof
(
rsp
));
rsp
.
data
=
INT_TO_PTR
(
ttynum
);
ret
=
lxc_abstract_unix_send_fds
(
fd
,
&
pt
m
xfd
,
1
,
&
rsp
,
sizeof
(
rsp
));
ret
=
lxc_abstract_unix_send_fds
(
fd
,
&
ptxfd
,
1
,
&
rsp
,
sizeof
(
rsp
));
if
(
ret
<
0
)
{
lxc_terminal_free
(
handler
->
conf
,
fd
);
return
log_error_errno
(
LXC_CMD_REAP_CLIENT_FD
,
errno
,
...
...
src/lxc/commands.h
View file @
2be8b365
...
...
@@ -61,7 +61,7 @@ struct lxc_cmd_rr {
};
struct
lxc_cmd_console_rsp_data
{
int
pt
m
xfd
;
int
ptxfd
;
int
ttynum
;
};
...
...
src/lxc/conf.c
View file @
2be8b365
...
...
@@ -921,9 +921,9 @@ int lxc_allocate_ttys(struct lxc_conf *conf)
for
(
size_t
i
=
0
;
i
<
ttys
->
max
;
i
++
)
{
struct
lxc_terminal_info
*
tty
=
&
ttys
->
tty
[
i
];
tty
->
pt
m
x
=
-
EBADF
;
tty
->
ptx
=
-
EBADF
;
tty
->
pts
=
-
EBADF
;
ret
=
openpty
(
&
tty
->
pt
m
x
,
&
tty
->
pts
,
NULL
,
NULL
,
NULL
);
ret
=
openpty
(
&
tty
->
ptx
,
&
tty
->
pts
,
NULL
,
NULL
,
NULL
);
if
(
ret
<
0
)
{
ttys
->
max
=
i
;
return
log_error_errno
(
-
ENOTTY
,
ENOTTY
,
"Failed to create tty %zu"
,
i
);
...
...
@@ -935,14 +935,14 @@ int lxc_allocate_ttys(struct lxc_conf *conf)
return
log_error_errno
(
-
ENOTTY
,
ENOTTY
,
"Failed to retrieve name of tty %zu pts"
,
i
);
}
DEBUG
(
"Created tty
\"
%s
\"
with pt
m
x fd %d and pts fd %d"
,
tty
->
name
,
tty
->
pt
m
x
,
tty
->
pts
);
DEBUG
(
"Created tty
\"
%s
\"
with ptx fd %d and pts fd %d"
,
tty
->
name
,
tty
->
ptx
,
tty
->
pts
);
/* Prevent leaking the file descriptors to the container */
ret
=
fd_cloexec
(
tty
->
pt
m
x
,
true
);
ret
=
fd_cloexec
(
tty
->
ptx
,
true
);
if
(
ret
<
0
)
SYSWARN
(
"Failed to set FD_CLOEXEC flag on pt
m
x fd %d of tty device
\"
%s
\"
"
,
tty
->
pt
m
x
,
tty
->
name
);
SYSWARN
(
"Failed to set FD_CLOEXEC flag on ptx fd %d of tty device
\"
%s
\"
"
,
tty
->
ptx
,
tty
->
name
);
ret
=
fd_cloexec
(
tty
->
pts
,
true
);
if
(
ret
<
0
)
...
...
@@ -964,7 +964,7 @@ void lxc_delete_tty(struct lxc_tty_info *ttys)
for
(
int
i
=
0
;
i
<
ttys
->
max
;
i
++
)
{
struct
lxc_terminal_info
*
tty
=
&
ttys
->
tty
[
i
];
close_prot_errno_disarm
(
tty
->
pt
m
x
);
close_prot_errno_disarm
(
tty
->
ptx
);
close_prot_errno_disarm
(
tty
->
pts
);
}
...
...
@@ -986,15 +986,15 @@ static int lxc_send_ttys_to_parent(struct lxc_handler *handler)
int
ttyfds
[
2
];
struct
lxc_terminal_info
*
tty
=
&
ttys
->
tty
[
i
];
ttyfds
[
0
]
=
tty
->
pt
m
x
;
ttyfds
[
0
]
=
tty
->
ptx
;
ttyfds
[
1
]
=
tty
->
pts
;
ret
=
lxc_abstract_unix_send_fds
(
sock
,
ttyfds
,
2
,
NULL
,
0
);
if
(
ret
<
0
)
break
;
TRACE
(
"Sent tty
\"
%s
\"
with pt
m
x fd %d and pts fd %d to parent"
,
tty
->
name
,
tty
->
pt
m
x
,
tty
->
pts
);
TRACE
(
"Sent tty
\"
%s
\"
with ptx fd %d and pts fd %d to parent"
,
tty
->
name
,
tty
->
ptx
,
tty
->
pts
);
}
if
(
ret
<
0
)
...
...
@@ -2546,9 +2546,9 @@ struct lxc_conf *lxc_conf_init(void)
new
->
console
.
path
=
NULL
;
new
->
console
.
peer
=
-
1
;
new
->
console
.
proxy
.
busy
=
-
1
;
new
->
console
.
proxy
.
pt
m
x
=
-
1
;
new
->
console
.
proxy
.
ptx
=
-
1
;
new
->
console
.
proxy
.
pts
=
-
1
;
new
->
console
.
pt
m
x
=
-
1
;
new
->
console
.
ptx
=
-
1
;
new
->
console
.
pts
=
-
1
;
new
->
console
.
name
[
0
]
=
'\0'
;
memset
(
&
new
->
console
.
ringbuf
,
0
,
sizeof
(
struct
lxc_ringbuf
));
...
...
src/lxc/lxccontainer.c
View file @
2be8b365
...
...
@@ -537,12 +537,12 @@ static bool do_lxcapi_unfreeze(struct lxc_container *c)
WRAP_API
(
bool
,
lxcapi_unfreeze
)
static
int
do_lxcapi_console_getfd
(
struct
lxc_container
*
c
,
int
*
ttynum
,
int
*
pt
m
xfd
)
static
int
do_lxcapi_console_getfd
(
struct
lxc_container
*
c
,
int
*
ttynum
,
int
*
ptxfd
)
{
if
(
!
c
)
return
-
1
;
return
lxc_terminal_getfd
(
c
,
ttynum
,
pt
m
xfd
);
return
lxc_terminal_getfd
(
c
,
ttynum
,
ptxfd
);
}
WRAP_API_2
(
int
,
lxcapi_console_getfd
,
int
*
,
int
*
)
...
...
src/lxc/lxccontainer.h
View file @
2be8b365
...
...
@@ -563,7 +563,7 @@ struct lxc_container {
* \param c Container.
* \param[in,out] ttynum Terminal number to attempt to allocate,
* or \c -1 to allocate the first available tty.
* \param[out] pt
mxfd File descriptor referring to the ptm
x side of the pty.
* \param[out] pt
xfd File descriptor referring to the pt
x side of the pty.
*
* \return tty file descriptor number on success, or \c -1 on
* failure.
...
...
@@ -575,7 +575,7 @@ struct lxc_container {
* descriptor when no longer required so that it may be allocated
* by another caller.
*/
int
(
*
console_getfd
)(
struct
lxc_container
*
c
,
int
*
ttynum
,
int
*
pt
m
xfd
);
int
(
*
console_getfd
)(
struct
lxc_container
*
c
,
int
*
ttynum
,
int
*
ptxfd
);
/*!
* \brief Allocate and run a console tty.
...
...
src/lxc/start.c
View file @
2be8b365
...
...
@@ -1434,9 +1434,9 @@ static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
tty
=
&
ttys
->
tty
[
i
];
tty
->
busy
=
-
1
;
tty
->
pt
m
x
=
ttyfds
[
0
];
tty
->
ptx
=
ttyfds
[
0
];
tty
->
pts
=
ttyfds
[
1
];
TRACE
(
"Received pty with pt
mx fd %d and pts fd %d from child"
,
tty
->
ptm
x
,
tty
->
pts
);
TRACE
(
"Received pty with pt
x fd %d and pts fd %d from child"
,
tty
->
pt
x
,
tty
->
pts
);
}
if
(
ret
<
0
)
...
...
src/lxc/terminal.c
View file @
2be8b365
This diff is collapsed.
Click to expand it.
src/lxc/terminal.h
View file @
2be8b365
...
...
@@ -18,8 +18,8 @@ struct lxc_terminal_info {
/* the path name of the pts side */
char
name
[
PATH_MAX
];
/* the file descriptor of the pt
m
x */
int
pt
m
x
;
/* the file descriptor of the ptx */
int
ptx
;
/* the file descriptor of the pts */
int
pts
;
...
...
@@ -32,7 +32,7 @@ struct lxc_terminal_state {
struct
lxc_list
node
;
int
stdinfd
;
int
stdoutfd
;
int
pt
m
xfd
;
int
ptxfd
;
/* Escape sequence to use for exiting the terminal. A single char can
* be specified. The terminal can then exited by doing: Ctrl +
...
...
@@ -58,7 +58,7 @@ struct lxc_terminal_state {
struct
lxc_terminal
{
int
pts
;
int
pt
m
x
;
int
ptx
;
int
peer
;
struct
lxc_terminal_info
proxy
;
struct
lxc_epoll_descr
*
descr
;
...
...
@@ -102,10 +102,10 @@ extern int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, int *ttynum
/**
* Create a new terminal:
* - calls openpty() to allocate a pt
m
x/pts pair
* - sets the FD_CLOEXEC flag on the pt
m
x/pts fds
* - calls openpty() to allocate a ptx/pts pair
* - sets the FD_CLOEXEC flag on the ptx/pts fds
* - allocates either the current controlling terminal (default) or a user
* specified terminal as proxy for the newly created pt
m
x/pts pair
* specified terminal as proxy for the newly created ptx/pts pair
* - sets up SIGWINCH handler, winsz, and new terminal settings
* (Handlers for SIGWINCH and I/O are not registered in a mainloop.)
*/
...
...
@@ -164,7 +164,7 @@ extern int lxc_console(struct lxc_container *c, int ttynum,
* the range specified by lxc.tty.max to allocate a specific tty.
*/
extern
int
lxc_terminal_getfd
(
struct
lxc_container
*
c
,
int
*
ttynum
,
int
*
pt
m
xfd
);
int
*
ptxfd
);
/**
* Make fd a duplicate of the standard file descriptors. The fd is made a
...
...
@@ -183,12 +183,12 @@ extern int lxc_terminal_stdin_cb(int fd, uint32_t events, void *cbdata,
struct
lxc_epoll_descr
*
descr
);
/**
* Handler for events on the pt
m
x fd of the terminal. To be registered via
* Handler for events on the ptx fd of the terminal. To be registered via
* the corresponding functions declared and defined in mainloop.{c,h} or
* lxc_terminal_mainloop_add().
* This function exits the loop cleanly when an EPOLLHUP event is received.
*/
extern
int
lxc_terminal_pt
m
x_cb
(
int
fd
,
uint32_t
events
,
void
*
cbdata
,
extern
int
lxc_terminal_ptx_cb
(
int
fd
,
uint32_t
events
,
void
*
cbdata
,
struct
lxc_epoll_descr
*
descr
);
/**
...
...
@@ -204,7 +204,7 @@ extern int lxc_setup_tios(int fd, struct termios *oldtios);
* @srcfd
* - terminal to get size from (typically a pts pty)
* @dstfd
* - terminal to set size on (typically a pt
m
x pty)
* - terminal to set size on (typically a ptx pty)
*/
extern
void
lxc_terminal_winsz
(
int
srcfd
,
int
dstfd
);
...
...
src/tests/console.c
View file @
2be8b365
...
...
@@ -37,14 +37,14 @@
} while (0)
static
void
test_console_close_all
(
int
ttyfd
[
MAXCONSOLES
],
int
pt
m
xfd
[
MAXCONSOLES
])
int
ptxfd
[
MAXCONSOLES
])
{
int
i
;
for
(
i
=
0
;
i
<
MAXCONSOLES
;
i
++
)
{
if
(
pt
m
xfd
[
i
]
!=
-
1
)
{
close
(
pt
m
xfd
[
i
]);
pt
m
xfd
[
i
]
=
-
1
;
if
(
ptxfd
[
i
]
!=
-
1
)
{
close
(
ptxfd
[
i
]);
ptxfd
[
i
]
=
-
1
;
}
if
(
ttyfd
[
i
]
!=
-
1
)
{
...
...
@@ -59,14 +59,14 @@ static int test_console_running_container(struct lxc_container *c)
int
nrconsoles
,
i
,
ret
=
-
1
;
int
ttynum
[
MAXCONSOLES
];
int
ttyfd
[
MAXCONSOLES
];
int
pt
m
xfd
[
MAXCONSOLES
];
int
ptxfd
[
MAXCONSOLES
];
for
(
i
=
0
;
i
<
MAXCONSOLES
;
i
++
)
ttynum
[
i
]
=
ttyfd
[
i
]
=
pt
m
xfd
[
i
]
=
-
1
;
ttynum
[
i
]
=
ttyfd
[
i
]
=
ptxfd
[
i
]
=
-
1
;
ttynum
[
0
]
=
1
;
ret
=
c
->
console_getfd
(
c
,
&
ttynum
[
0
],
&
pt
m
xfd
[
0
]);
ret
=
c
->
console_getfd
(
c
,
&
ttynum
[
0
],
&
ptxfd
[
0
]);
if
(
ret
<
0
)
{
TSTERR
(
"console allocate failed"
);
goto
err1
;
...
...
@@ -79,12 +79,12 @@ static int test_console_running_container(struct lxc_container *c)
}
/* attempt to alloc same ttynum */
ret
=
c
->
console_getfd
(
c
,
&
ttynum
[
0
],
&
pt
m
xfd
[
1
]);
ret
=
c
->
console_getfd
(
c
,
&
ttynum
[
0
],
&
ptxfd
[
1
]);
if
(
ret
!=
-
1
)
{
TSTERR
(
"console allocate should fail for allocated ttynum %d"
,
ttynum
[
0
]);
goto
err2
;
}
close
(
pt
mxfd
[
0
]);
ptm
xfd
[
0
]
=
-
1
;
close
(
pt
xfd
[
0
]);
pt
xfd
[
0
]
=
-
1
;
close
(
ttyfd
[
0
]);
ttyfd
[
0
]
=
-
1
;
/* ensure we can allocate all consoles, we do this a few times to
...
...
@@ -92,7 +92,7 @@ static int test_console_running_container(struct lxc_container *c)
*/
for
(
i
=
0
;
i
<
10
;
i
++
)
{
for
(
nrconsoles
=
0
;
nrconsoles
<
MAXCONSOLES
;
nrconsoles
++
)
{
ret
=
c
->
console_getfd
(
c
,
&
ttynum
[
nrconsoles
],
&
pt
m
xfd
[
nrconsoles
]);
ret
=
c
->
console_getfd
(
c
,
&
ttynum
[
nrconsoles
],
&
ptxfd
[
nrconsoles
]);
if
(
ret
<
0
)
break
;
ttyfd
[
nrconsoles
]
=
ret
;
...
...
@@ -103,13 +103,13 @@ static int test_console_running_container(struct lxc_container *c)
goto
err2
;
}
test_console_close_all
(
ttyfd
,
pt
m
xfd
);
test_console_close_all
(
ttyfd
,
ptxfd
);
}
ret
=
0
;
err2:
test_console_close_all
(
ttyfd
,
pt
m
xfd
);
test_console_close_all
(
ttyfd
,
ptxfd
);
err1:
return
ret
;
...
...
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