Skip to content

Commit 1591202

Browse files
committed
chore(libct/nsenter): extract utility code
...from nsexec.c so they can be used in cloned_binary.c. Signed-off-by: Cory Snider <[email protected]>
1 parent bc13e33 commit 1591202

File tree

7 files changed

+268
-220
lines changed

7 files changed

+268
-220
lines changed

libcontainer/nsenter/getenv.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#define _GNU_SOURCE
2+
#include <errno.h>
3+
#include <stdlib.h>
4+
#include "getenv.h"
5+
#include "log.h"
6+
7+
int getenv_int(const char *name)
8+
{
9+
char *val, *endptr;
10+
int ret;
11+
12+
val = getenv(name);
13+
/* Treat empty value as unset variable. */
14+
if (val == NULL || *val == '\0')
15+
return -ENOENT;
16+
17+
ret = strtol(val, &endptr, 10);
18+
if (val == endptr || *endptr != '\0')
19+
bail("unable to parse %s=%s", name, val);
20+
/*
21+
* Sanity check: this must be a non-negative number.
22+
*/
23+
if (ret < 0)
24+
bail("bad value for %s=%s (%d)", name, val, ret);
25+
26+
return ret;
27+
}

libcontainer/nsenter/getenv.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef NSENTER_GETENV_H
2+
#define NSENTER_GETENV_H
3+
4+
/*
5+
* Returns an environment variable value as a non-negative integer, or -ENOENT
6+
* if the variable was not found or has an empty value.
7+
*
8+
* If the value can not be converted to an integer, or the result is out of
9+
* range, the function bails out.
10+
*/
11+
int getenv_int(const char *name);
12+
13+
#endif /* NSENTER_GETENV_H */

libcontainer/nsenter/ipc.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#define _GNU_SOURCE
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <sys/socket.h>
5+
#include "ipc.h"
6+
#include "log.h"
7+
8+
int receive_fd(int sockfd)
9+
{
10+
int bytes_read;
11+
struct msghdr msg = { };
12+
struct cmsghdr *cmsg;
13+
struct iovec iov = { };
14+
char null_byte = '\0';
15+
int ret;
16+
int fd_count;
17+
18+
iov.iov_base = &null_byte;
19+
iov.iov_len = 1;
20+
21+
msg.msg_iov = &iov;
22+
msg.msg_iovlen = 1;
23+
24+
msg.msg_controllen = CMSG_SPACE(sizeof(int));
25+
msg.msg_control = malloc(msg.msg_controllen);
26+
if (msg.msg_control == NULL) {
27+
bail("Can't allocate memory to receive fd.");
28+
}
29+
30+
memset(msg.msg_control, 0, msg.msg_controllen);
31+
32+
bytes_read = recvmsg(sockfd, &msg, 0);
33+
if (bytes_read != 1)
34+
bail("failed to receive fd from unix socket %d", sockfd);
35+
if (msg.msg_flags & MSG_CTRUNC)
36+
bail("received truncated control message from unix socket %d", sockfd);
37+
38+
cmsg = CMSG_FIRSTHDR(&msg);
39+
if (!cmsg)
40+
bail("received message from unix socket %d without control message", sockfd);
41+
42+
if (cmsg->cmsg_level != SOL_SOCKET)
43+
bail("received unknown control message from unix socket %d: cmsg_level=%d", sockfd, cmsg->cmsg_level);
44+
45+
if (cmsg->cmsg_type != SCM_RIGHTS)
46+
bail("received unknown control message from unix socket %d: cmsg_type=%d", sockfd, cmsg->cmsg_type);
47+
48+
fd_count = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
49+
if (fd_count != 1)
50+
bail("received control message from unix socket %d with too many fds: %d", sockfd, fd_count);
51+
52+
ret = *(int *)CMSG_DATA(cmsg);
53+
free(msg.msg_control);
54+
return ret;
55+
}
56+
57+
void send_fd(int sockfd, int fd)
58+
{
59+
int bytes_written;
60+
struct msghdr msg = { };
61+
struct cmsghdr *cmsg;
62+
struct iovec iov[1] = { };
63+
char null_byte = '\0';
64+
65+
iov[0].iov_base = &null_byte;
66+
iov[0].iov_len = 1;
67+
68+
msg.msg_iov = iov;
69+
msg.msg_iovlen = 1;
70+
71+
/* We send only one fd as specified by cmsg->cmsg_len below, even
72+
* though msg.msg_controllen might have more space due to alignment. */
73+
msg.msg_controllen = CMSG_SPACE(sizeof(int));
74+
msg.msg_control = malloc(msg.msg_controllen);
75+
if (msg.msg_control == NULL) {
76+
bail("Can't allocate memory to send fd.");
77+
}
78+
79+
memset(msg.msg_control, 0, msg.msg_controllen);
80+
81+
cmsg = CMSG_FIRSTHDR(&msg);
82+
cmsg->cmsg_level = SOL_SOCKET;
83+
cmsg->cmsg_type = SCM_RIGHTS;
84+
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
85+
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
86+
87+
bytes_written = sendmsg(sockfd, &msg, 0);
88+
89+
free(msg.msg_control);
90+
91+
if (bytes_written != 1)
92+
bail("failed to send fd %d via unix socket %d", fd, sockfd);
93+
}

libcontainer/nsenter/ipc.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef NSENTER_IPC_H
2+
#define NSENTER_IPC_H
3+
4+
int receive_fd(int sockfd);
5+
void send_fd(int sockfd, int fd);
6+
7+
#endif /* NSENTER_IPC_H */

libcontainer/nsenter/log.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#define _GNU_SOURCE
2+
#include <stdarg.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include <unistd.h>
7+
8+
#include "log.h"
9+
#include "getenv.h"
10+
11+
static const char *level_str[] = { "panic", "fatal", "error", "warning", "info", "debug", "trace" };
12+
13+
int logfd = -1;
14+
static int loglevel = DEBUG;
15+
16+
extern char *escape_json_string(char *str);
17+
void setup_logpipe(void)
18+
{
19+
int i;
20+
21+
i = getenv_int("_LIBCONTAINER_LOGPIPE");
22+
if (i < 0) {
23+
/* We are not runc init, or log pipe was not provided. */
24+
return;
25+
}
26+
logfd = i;
27+
28+
i = getenv_int("_LIBCONTAINER_LOGLEVEL");
29+
if (i < 0)
30+
return;
31+
loglevel = i;
32+
}
33+
34+
/* Defined in nsexec.c */
35+
extern int current_stage;
36+
37+
void write_log(int level, const char *format, ...)
38+
{
39+
char *message = NULL, *stage = NULL, *json = NULL;
40+
va_list args;
41+
int ret;
42+
43+
if (logfd < 0 || level > loglevel)
44+
goto out;
45+
46+
va_start(args, format);
47+
ret = vasprintf(&message, format, args);
48+
va_end(args);
49+
if (ret < 0) {
50+
message = NULL;
51+
goto out;
52+
}
53+
54+
message = escape_json_string(message);
55+
56+
if (current_stage < 0)
57+
stage = strdup("nsexec");
58+
else
59+
ret = asprintf(&stage, "nsexec-%d", current_stage);
60+
if (ret < 0) {
61+
stage = NULL;
62+
goto out;
63+
}
64+
65+
ret = asprintf(&json, "{\"level\":\"%s\", \"msg\": \"%s[%d]: %s\"}\n",
66+
level_str[level], stage, getpid(), message);
67+
if (ret < 0) {
68+
json = NULL;
69+
goto out;
70+
}
71+
72+
/* This logging is on a best-effort basis. In case of a short or failed
73+
* write there is nothing we can do, so just ignore write() errors.
74+
*/
75+
ssize_t __attribute__((unused)) __res = write(logfd, json, ret);
76+
77+
out:
78+
free(message);
79+
free(stage);
80+
free(json);
81+
}

libcontainer/nsenter/log.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#ifndef NSENTER_LOG_H
2+
#define NSENTER_LOG_H
3+
4+
#include <stdio.h>
5+
6+
/*
7+
* Log levels are the same as in logrus.
8+
*/
9+
#define PANIC 0
10+
#define FATAL 1
11+
#define ERROR 2
12+
#define WARNING 3
13+
#define INFO 4
14+
#define DEBUG 5
15+
#define TRACE 6
16+
17+
/*
18+
* Sets up logging by getting log fd and log level from the environment,
19+
* if available.
20+
*/
21+
void setup_logpipe(void);
22+
23+
void write_log(int level, const char *format, ...) __attribute__((format(printf, 2, 3)));
24+
25+
extern int logfd;
26+
#define bail(fmt, ...) \
27+
do { \
28+
if (logfd < 0) \
29+
fprintf(stderr, "FATAL: " fmt ": %m\n", \
30+
##__VA_ARGS__); \
31+
else \
32+
write_log(FATAL, fmt ": %m", ##__VA_ARGS__); \
33+
exit(1); \
34+
} while(0)
35+
36+
37+
#endif /* NSENTER_LOG_H */

0 commit comments

Comments
 (0)