Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/common-go/nsenter/nsexec.c
2498 views
1
/*
2
*
3
* Copyright The runc authors.
4
*
5
* Licensed under the Apache License, Version 2.0 (the "License");
6
* you may not use this file except in compliance with the License.
7
* You may obtain a copy of the License at
8
*
9
* http://www.apache.org/licenses/LICENSE-2.0
10
*
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
16
*/
17
18
#define _GNU_SOURCE
19
#include <endian.h>
20
#include <errno.h>
21
#include <fcntl.h>
22
#include <grp.h>
23
#include <sched.h>
24
#include <setjmp.h>
25
#include <signal.h>
26
#include <stdarg.h>
27
#include <stdbool.h>
28
#include <stdint.h>
29
#include <stdio.h>
30
#include <stdlib.h>
31
#include <stdbool.h>
32
#include <string.h>
33
#include <unistd.h>
34
35
#include <sys/ioctl.h>
36
#include <sys/prctl.h>
37
#include <sys/socket.h>
38
#include <sys/types.h>
39
#include <sys/wait.h>
40
41
#include <linux/limits.h>
42
#include <linux/netlink.h>
43
#include <linux/types.h>
44
45
#define PANIC "panic"
46
#define FATAL "fatal"
47
#define ERROR "error"
48
#define WARNING "warning"
49
#define INFO "info"
50
#define DEBUG "debug"
51
52
inline void ignore_unused_result() {}
53
54
/*
55
* Use the raw syscall for versions of glibc which don't include a function for
56
* it, namely (glibc 2.12).
57
*/
58
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
59
# define _GNU_SOURCE
60
# include "syscall.h"
61
# if !defined(SYS_setns) && defined(__NR_setns)
62
# define SYS_setns __NR_setns
63
# endif
64
65
#ifndef SYS_setns
66
# error "setns(2) syscall not supported by glibc version"
67
#endif
68
69
int setns(int fd, int nstype)
70
{
71
return syscall(SYS_setns, fd, nstype);
72
}
73
#endif
74
75
static void write_log(const char *level, const char *format, ...)
76
{
77
char *message = NULL, *json = NULL;
78
va_list args;
79
int ret;
80
81
va_start(args, format);
82
ret = vasprintf(&message, format, args);
83
va_end(args);
84
if (ret < 0)
85
{
86
message = NULL;
87
goto out;
88
}
89
90
ret = asprintf(&json, "{\"level\":\"%s\", \"msg\": \"%s\"}\n", level, message);
91
if (ret < 0)
92
{
93
json = NULL;
94
goto out;
95
}
96
97
ignore_unused_result(write(1, json, ret));
98
99
out:
100
free(message);
101
free(json);
102
}
103
104
#define bail(fmt, ...) \
105
do \
106
{ \
107
write_log(FATAL, "nsenter: " fmt ": %m", ##__VA_ARGS__); \
108
exit(1); \
109
} while (0)
110
111
void join_ns(char *fdstr, int nstype)
112
{
113
int fd = atoi(fdstr);
114
115
if (setns(fd, nstype) < 0)
116
bail("failed to setns to fd %s", fdstr);
117
118
close(fd);
119
}
120
121
void nsexec(void)
122
{
123
char *in_init = getenv("_LIBNSENTER_INIT");
124
if (in_init == NULL || *in_init == '\0')
125
return;
126
127
write_log(DEBUG, "nsexec started");
128
129
/*
130
* Make the process non-dumpable, to avoid various race conditions that
131
* could cause processes in namespaces we're joining to access host
132
* resources (or potentially execute code).
133
*/
134
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) < 0)
135
bail("failed to set process as non-dumpable");
136
137
/* For debugging. */
138
prctl(PR_SET_NAME, (unsigned long)"workspacekit:[CHILD]", 0, 0, 0);
139
140
char *mntnsfd = getenv("_LIBNSENTER_MNTNSFD");
141
if (mntnsfd != NULL)
142
{
143
write_log(DEBUG, "join mnt namespace: %s", mntnsfd);
144
join_ns(mntnsfd, CLONE_NEWNS);
145
}
146
147
char *rootfd = getenv("_LIBNSENTER_ROOTFD");
148
if (rootfd != NULL)
149
{
150
write_log(DEBUG, "chroot: %s", rootfd);
151
ignore_unused_result(fchdir(atoi(rootfd)));
152
ignore_unused_result(chroot("."));
153
}
154
char *cwdfd = getenv("_LIBNSENTER_CWDFD");
155
if (cwdfd != NULL)
156
{
157
write_log(DEBUG, "chcwd: %s", cwdfd);
158
ignore_unused_result(fchdir(atoi(cwdfd)));
159
}
160
161
char *netnsfd = getenv("_LIBNSENTER_NETNSFD");
162
if (netnsfd != NULL)
163
{
164
write_log(DEBUG, "join net namespace: %s", netnsfd);
165
join_ns(netnsfd, CLONE_NEWNET);
166
}
167
168
char *pidnsfd = getenv("_LIBNSENTER_PIDNSFD");
169
if (pidnsfd != NULL)
170
{
171
write_log(DEBUG, "join pid namespace: %s", pidnsfd);
172
join_ns(pidnsfd, CLONE_NEWPID);
173
}
174
175
pid_t pid = fork();
176
if (pid == -1)
177
{
178
bail("failed to fork");
179
}
180
if (pid == 0)
181
{
182
/* child process*/
183
/* Finish executing, let the Go runtime take over. */
184
ignore_unused_result(write(1, "", 1)); // write NULL byte
185
return;
186
}
187
188
int wstatus;
189
if (wait(&wstatus) < 0)
190
{
191
bail("failed to wait for child process");
192
}
193
194
if (WIFEXITED(wstatus))
195
{
196
exit(WEXITSTATUS(wstatus));
197
}
198
else
199
{
200
exit(1);
201
}
202
}
203
204