xref: /kvmtool/builtin-setup.c (revision 5fab5964e210bc82340ea89ef65a6309cd08a8c2)
1 #include <kvm/util.h>
2 #include <kvm/kvm-cmd.h>
3 #include <kvm/builtin-setup.h>
4 #include <kvm/kvm.h>
5 #include <kvm/parse-options.h>
6 #include <kvm/read-write.h>
7 
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <limits.h>
11 #include <signal.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <sys/types.h>
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 
23 extern char _binary_guest_init_start;
24 extern char _binary_guest_init_size;
25 
26 static const char *instance_name;
27 
28 static const char * const setup_usage[] = {
29 	"lkvm setup [name]",
30 	NULL
31 };
32 
33 static const struct option setup_options[] = {
34 	OPT_END()
35 };
36 
37 static void parse_setup_options(int argc, const char **argv)
38 {
39 	while (argc != 0) {
40 		argc = parse_options(argc, argv, setup_options, setup_usage,
41 				PARSE_OPT_STOP_AT_NON_OPTION);
42 		if (argc != 0 && instance_name)
43 			kvm_setup_help();
44 		else
45 			instance_name = argv[0];
46 		argv++;
47 		argc--;
48 	}
49 }
50 
51 void kvm_setup_help(void)
52 {
53 	printf("\n%s setup creates a new rootfs under %s.\n"
54 		"This can be used later by the '-d' parameter of '%s run'.\n",
55 		KVM_BINARY_NAME, kvm__get_dir(), KVM_BINARY_NAME);
56 	usage_with_options(setup_usage, setup_options);
57 }
58 
59 static int copy_file(const char *from, const char *to)
60 {
61 	int in_fd, out_fd;
62 	void *src, *dst;
63 	struct stat st;
64 	int err = -1;
65 
66 	in_fd = open(from, O_RDONLY);
67 	if (in_fd < 0)
68 		return err;
69 
70 	if (fstat(in_fd, &st) < 0)
71 		goto error_close_in;
72 
73 	out_fd = open(to, O_RDWR | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
74 	if (out_fd < 0)
75 		goto error_close_in;
76 
77 	src = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, in_fd, 0);
78 	if (src == MAP_FAILED)
79 		goto error_close_out;
80 
81 	if (ftruncate(out_fd, st.st_size) < 0)
82 		goto error_munmap_src;
83 
84 	dst = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, out_fd, 0);
85 	if (dst == MAP_FAILED)
86 		goto error_munmap_src;
87 
88 	memcpy(dst, src, st.st_size);
89 
90 	if (fsync(out_fd) < 0)
91 		goto error_munmap_dst;
92 
93 	err = 0;
94 
95 error_munmap_dst:
96 	munmap(dst, st.st_size);
97 error_munmap_src:
98 	munmap(src, st.st_size);
99 error_close_out:
100 	close(out_fd);
101 error_close_in:
102 	close(in_fd);
103 
104 	return err;
105 }
106 
107 static const char *guestfs_dirs[] = {
108 	"/dev",
109 	"/etc",
110 	"/home",
111 	"/host",
112 	"/proc",
113 	"/root",
114 	"/sys",
115 	"/tmp",
116 	"/var",
117 	"/var/lib",
118 	"/virt",
119 	"/virt/home",
120 };
121 
122 static const char *guestfs_symlinks[] = {
123 	"/bin",
124 	"/lib",
125 	"/lib64",
126 	"/sbin",
127 	"/usr",
128 	"/etc/ld.so.conf",
129 };
130 
131 static int copy_init(const char *guestfs_name)
132 {
133 	char path[PATH_MAX];
134 	size_t size;
135 	int fd, ret;
136 	char *data;
137 
138 	size = (size_t)&_binary_guest_init_size;
139 	data = (char *)&_binary_guest_init_start;
140 	snprintf(path, PATH_MAX, "%s%s/virt/init", kvm__get_dir(), guestfs_name);
141 	remove(path);
142 	fd = open(path, O_CREAT | O_WRONLY, 0755);
143 	if (fd < 0)
144 		die("Fail to setup %s", path);
145 	ret = xwrite(fd, data, size);
146 	if (ret < 0)
147 		die("Fail to setup %s", path);
148 	close(fd);
149 
150 	return 0;
151 }
152 
153 static int copy_passwd(const char *guestfs_name)
154 {
155 	char path[PATH_MAX];
156 	FILE *file;
157 	int ret;
158 
159 	snprintf(path, PATH_MAX, "%s%s/etc/passwd", kvm__get_dir(), guestfs_name);
160 
161 	file = fopen(path, "w");
162 	if (!file)
163 		return -1;
164 
165 	ret = fprintf(file, "root:x:0:0:root:/root:/bin/sh\n");
166 	if (ret < 0)
167 		return ret;
168 
169 	fclose(file);
170 
171 	return 0;
172 }
173 
174 static int make_guestfs_symlink(const char *guestfs_name, const char *path)
175 {
176 	char target[PATH_MAX];
177 	char name[PATH_MAX];
178 
179 	snprintf(name, PATH_MAX, "%s%s%s", kvm__get_dir(), guestfs_name, path);
180 
181 	snprintf(target, PATH_MAX, "/host%s", path);
182 
183 	return symlink(target, name);
184 }
185 
186 static int make_dir(const char *dir)
187 {
188 	char name[PATH_MAX];
189 
190 	snprintf(name, PATH_MAX, "%s%s", kvm__get_dir(), dir);
191 
192 	return mkdir(name, 0777);
193 }
194 
195 static void make_guestfs_dir(const char *guestfs_name, const char *dir)
196 {
197 	char name[PATH_MAX];
198 
199 	snprintf(name, PATH_MAX, "%s%s", guestfs_name, dir);
200 
201 	make_dir(name);
202 }
203 
204 void kvm_setup_resolv(const char *guestfs_name)
205 {
206 	char path[PATH_MAX];
207 
208 	snprintf(path, PATH_MAX, "%s%s/etc/resolv.conf", kvm__get_dir(), guestfs_name);
209 
210 	copy_file("/etc/resolv.conf", path);
211 }
212 
213 static int do_setup(const char *guestfs_name)
214 {
215 	unsigned int i;
216 	int ret;
217 
218 	ret = make_dir(guestfs_name);
219 	if (ret < 0)
220 		return ret;
221 
222 	for (i = 0; i < ARRAY_SIZE(guestfs_dirs); i++)
223 		make_guestfs_dir(guestfs_name, guestfs_dirs[i]);
224 
225 	for (i = 0; i < ARRAY_SIZE(guestfs_symlinks); i++) {
226 		make_guestfs_symlink(guestfs_name, guestfs_symlinks[i]);
227 	}
228 
229 	ret = copy_init(guestfs_name);
230 	if (ret < 0)
231 		return ret;
232 
233 	return copy_passwd(guestfs_name);
234 }
235 
236 int kvm_setup_create_new(const char *guestfs_name)
237 {
238 	return do_setup(guestfs_name);
239 }
240 
241 int kvm_cmd_setup(int argc, const char **argv, const char *prefix)
242 {
243 	int r;
244 
245 	parse_setup_options(argc, argv);
246 
247 	if (instance_name == NULL)
248 		kvm_setup_help();
249 
250 	r = do_setup(instance_name);
251 	if (r == 0)
252 		printf("A new rootfs '%s' has been created in '%s%s'.\n\n"
253 			"You can now start it by running the following command:\n\n"
254 			"  %s run -d %s\n",
255 			instance_name, kvm__get_dir(), instance_name,
256 			KVM_BINARY_NAME,instance_name);
257 	else
258 		printf("Unable to create rootfs in %s%s: %s\n",
259 			kvm__get_dir(), instance_name, strerror(errno));
260 
261 	return r;
262 }
263