xref: /kvmtool/builtin-setup.c (revision 30867c55012f05f3ad28322bcf86dcfb83b9f21b)
1282113fdSPekka Enberg #include <kvm/util.h>
2282113fdSPekka Enberg #include <kvm/kvm-cmd.h>
3282113fdSPekka Enberg #include <kvm/builtin-setup.h>
4282113fdSPekka Enberg #include <kvm/kvm.h>
5282113fdSPekka Enberg #include <kvm/parse-options.h>
6f20814b8SAsias He #include <kvm/read-write.h>
7282113fdSPekka Enberg 
8282113fdSPekka Enberg #include <sys/types.h>
9282113fdSPekka Enberg #include <sys/stat.h>
10282113fdSPekka Enberg #include <limits.h>
11282113fdSPekka Enberg #include <signal.h>
12282113fdSPekka Enberg #include <stdlib.h>
13282113fdSPekka Enberg #include <string.h>
14282113fdSPekka Enberg #include <unistd.h>
15282113fdSPekka Enberg #include <stdio.h>
16282113fdSPekka Enberg #include <sys/mman.h>
17282113fdSPekka Enberg #include <fcntl.h>
18282113fdSPekka Enberg 
19282113fdSPekka Enberg static const char *instance_name;
20282113fdSPekka Enberg 
21282113fdSPekka Enberg static const char * const setup_usage[] = {
228d2ff5daSWanlong Gao 	"lkvm setup [name]",
23282113fdSPekka Enberg 	NULL
24282113fdSPekka Enberg };
25282113fdSPekka Enberg 
26282113fdSPekka Enberg static const struct option setup_options[] = {
27282113fdSPekka Enberg 	OPT_END()
28282113fdSPekka Enberg };
29282113fdSPekka Enberg 
30282113fdSPekka Enberg static void parse_setup_options(int argc, const char **argv)
31282113fdSPekka Enberg {
32282113fdSPekka Enberg 	while (argc != 0) {
33282113fdSPekka Enberg 		argc = parse_options(argc, argv, setup_options, setup_usage,
34282113fdSPekka Enberg 				PARSE_OPT_STOP_AT_NON_OPTION);
357b6d50d6SSasha Levin 		if (argc != 0 && instance_name)
36282113fdSPekka Enberg 			kvm_setup_help();
377b6d50d6SSasha Levin 		else
387b6d50d6SSasha Levin 			instance_name = argv[0];
397b6d50d6SSasha Levin 		argv++;
407b6d50d6SSasha Levin 		argc--;
41282113fdSPekka Enberg 	}
42282113fdSPekka Enberg }
43282113fdSPekka Enberg 
44282113fdSPekka Enberg void kvm_setup_help(void)
45282113fdSPekka Enberg {
46ee8b1456SWanlong Gao 	printf("\n%s setup creates a new rootfs under %s.\n"
47ee8b1456SWanlong Gao 		"This can be used later by the '-d' parameter of '%s run'.\n",
48d15572a9SPekka Enberg 		KVM_BINARY_NAME, kvm__get_dir(), KVM_BINARY_NAME);
49282113fdSPekka Enberg 	usage_with_options(setup_usage, setup_options);
50282113fdSPekka Enberg }
51282113fdSPekka Enberg 
52282113fdSPekka Enberg static int copy_file(const char *from, const char *to)
53282113fdSPekka Enberg {
54282113fdSPekka Enberg 	int in_fd, out_fd;
55282113fdSPekka Enberg 	void *src, *dst;
56282113fdSPekka Enberg 	struct stat st;
57282113fdSPekka Enberg 	int err = -1;
58282113fdSPekka Enberg 
59282113fdSPekka Enberg 	in_fd = open(from, O_RDONLY);
60282113fdSPekka Enberg 	if (in_fd < 0)
61282113fdSPekka Enberg 		return err;
62282113fdSPekka Enberg 
63282113fdSPekka Enberg 	if (fstat(in_fd, &st) < 0)
64282113fdSPekka Enberg 		goto error_close_in;
65282113fdSPekka Enberg 
66282113fdSPekka Enberg 	out_fd = open(to, O_RDWR | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
67282113fdSPekka Enberg 	if (out_fd < 0)
68282113fdSPekka Enberg 		goto error_close_in;
69282113fdSPekka Enberg 
70282113fdSPekka Enberg 	src = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, in_fd, 0);
71282113fdSPekka Enberg 	if (src == MAP_FAILED)
72282113fdSPekka Enberg 		goto error_close_out;
73282113fdSPekka Enberg 
74282113fdSPekka Enberg 	if (ftruncate(out_fd, st.st_size) < 0)
75282113fdSPekka Enberg 		goto error_munmap_src;
76282113fdSPekka Enberg 
77282113fdSPekka Enberg 	dst = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, out_fd, 0);
78282113fdSPekka Enberg 	if (dst == MAP_FAILED)
79282113fdSPekka Enberg 		goto error_munmap_src;
80282113fdSPekka Enberg 
81282113fdSPekka Enberg 	memcpy(dst, src, st.st_size);
82282113fdSPekka Enberg 
83282113fdSPekka Enberg 	if (fsync(out_fd) < 0)
84282113fdSPekka Enberg 		goto error_munmap_dst;
85282113fdSPekka Enberg 
86282113fdSPekka Enberg 	err = 0;
87282113fdSPekka Enberg 
88282113fdSPekka Enberg error_munmap_dst:
89282113fdSPekka Enberg 	munmap(dst, st.st_size);
90282113fdSPekka Enberg error_munmap_src:
91282113fdSPekka Enberg 	munmap(src, st.st_size);
92282113fdSPekka Enberg error_close_out:
93282113fdSPekka Enberg 	close(out_fd);
94282113fdSPekka Enberg error_close_in:
95282113fdSPekka Enberg 	close(in_fd);
96282113fdSPekka Enberg 
97282113fdSPekka Enberg 	return err;
98282113fdSPekka Enberg }
99282113fdSPekka Enberg 
100282113fdSPekka Enberg static const char *guestfs_dirs[] = {
101282113fdSPekka Enberg 	"/dev",
102282113fdSPekka Enberg 	"/etc",
103282113fdSPekka Enberg 	"/home",
104282113fdSPekka Enberg 	"/host",
105282113fdSPekka Enberg 	"/proc",
106282113fdSPekka Enberg 	"/root",
107282113fdSPekka Enberg 	"/sys",
1084547fe2bSPekka Enberg 	"/tmp",
109282113fdSPekka Enberg 	"/var",
110282113fdSPekka Enberg 	"/var/lib",
111282113fdSPekka Enberg 	"/virt",
1125fab5964SSasha Levin 	"/virt/home",
113282113fdSPekka Enberg };
114282113fdSPekka Enberg 
115282113fdSPekka Enberg static const char *guestfs_symlinks[] = {
116282113fdSPekka Enberg 	"/bin",
117282113fdSPekka Enberg 	"/lib",
118282113fdSPekka Enberg 	"/lib64",
119282113fdSPekka Enberg 	"/sbin",
120282113fdSPekka Enberg 	"/usr",
121d6080290SSasha Levin 	"/etc/ld.so.conf",
122282113fdSPekka Enberg };
123282113fdSPekka Enberg 
124cdce942cSDimitri John Ledkov #ifdef CONFIG_GUEST_INIT
12526e94dc4SOleg Nesterov static int extract_file(const char *guestfs_name, const char *filename,
12626e94dc4SOleg Nesterov 			const void *data, const void *_size)
127282113fdSPekka Enberg {
128282113fdSPekka Enberg 	char path[PATH_MAX];
129f20814b8SAsias He 	int fd, ret;
130282113fdSPekka Enberg 
13126e94dc4SOleg Nesterov 	snprintf(path, PATH_MAX, "%s%s/%s", kvm__get_dir(),
13226e94dc4SOleg Nesterov 				guestfs_name, filename);
133*30867c55SOleg Nesterov 
134*30867c55SOleg Nesterov 	fd = open(path, O_EXCL | O_CREAT | O_WRONLY, 0755);
135*30867c55SOleg Nesterov 	if (fd < 0) {
136*30867c55SOleg Nesterov 		if (errno == EEXIST)
137*30867c55SOleg Nesterov 			return 0;
138f20814b8SAsias He 		die("Fail to setup %s", path);
139*30867c55SOleg Nesterov 	}
140*30867c55SOleg Nesterov 
14126e94dc4SOleg Nesterov 	ret = xwrite(fd, data, (size_t)_size);
142f20814b8SAsias He 	if (ret < 0)
143f20814b8SAsias He 		die("Fail to setup %s", path);
144f20814b8SAsias He 	close(fd);
145282113fdSPekka Enberg 
146f20814b8SAsias He 	return 0;
14726e94dc4SOleg Nesterov }
148cdce942cSDimitri John Ledkov 
14926e94dc4SOleg Nesterov extern char _binary_guest_init_start;
15026e94dc4SOleg Nesterov extern char _binary_guest_init_size;
1515614f9d9SOleg Nesterov extern char _binary_guest_pre_init_start;
1525614f9d9SOleg Nesterov extern char _binary_guest_pre_init_size;
15326e94dc4SOleg Nesterov 
15426e94dc4SOleg Nesterov int kvm_setup_guest_init(const char *guestfs_name)
15526e94dc4SOleg Nesterov {
1565614f9d9SOleg Nesterov 	int err;
1575614f9d9SOleg Nesterov 
1585614f9d9SOleg Nesterov #ifdef CONFIG_GUEST_PRE_INIT
1595614f9d9SOleg Nesterov 	err = extract_file(guestfs_name, "virt/pre_init",
1605614f9d9SOleg Nesterov 				&_binary_guest_pre_init_start,
1615614f9d9SOleg Nesterov 				&_binary_guest_pre_init_size);
1625614f9d9SOleg Nesterov 	if (err)
1635614f9d9SOleg Nesterov 		return err;
1645614f9d9SOleg Nesterov #endif
1655614f9d9SOleg Nesterov 	err = extract_file(guestfs_name, "virt/init",
16626e94dc4SOleg Nesterov 				&_binary_guest_init_start,
16726e94dc4SOleg Nesterov 				&_binary_guest_init_size);
1685614f9d9SOleg Nesterov 	return err;
169282113fdSPekka Enberg }
170cdce942cSDimitri John Ledkov #else
171cdce942cSDimitri John Ledkov int kvm_setup_guest_init(const char *guestfs_name)
172cdce942cSDimitri John Ledkov {
173cdce942cSDimitri John Ledkov 	die("Guest init image not compiled in");
174cdce942cSDimitri John Ledkov 	return 0;
175cdce942cSDimitri John Ledkov }
176cdce942cSDimitri John Ledkov #endif
177282113fdSPekka Enberg 
178ce6927e8SSasha Levin static int copy_passwd(const char *guestfs_name)
179ce6927e8SSasha Levin {
180ce6927e8SSasha Levin 	char path[PATH_MAX];
181f20814b8SAsias He 	FILE *file;
182f20814b8SAsias He 	int ret;
183ce6927e8SSasha Levin 
184ce6927e8SSasha Levin 	snprintf(path, PATH_MAX, "%s%s/etc/passwd", kvm__get_dir(), guestfs_name);
185ce6927e8SSasha Levin 
186f20814b8SAsias He 	file = fopen(path, "w");
187f20814b8SAsias He 	if (!file)
188f20814b8SAsias He 		return -1;
189f20814b8SAsias He 
190f20814b8SAsias He 	ret = fprintf(file, "root:x:0:0:root:/root:/bin/sh\n");
19163fd57b9SCong Ding 	if (ret > 0)
19263fd57b9SCong Ding 		ret = 0;
193f20814b8SAsias He 
194f20814b8SAsias He 	fclose(file);
195f20814b8SAsias He 
19663fd57b9SCong Ding 	return ret;
197ce6927e8SSasha Levin }
198ce6927e8SSasha Levin 
199282113fdSPekka Enberg static int make_guestfs_symlink(const char *guestfs_name, const char *path)
200282113fdSPekka Enberg {
201282113fdSPekka Enberg 	char target[PATH_MAX];
202282113fdSPekka Enberg 	char name[PATH_MAX];
203282113fdSPekka Enberg 
2049667701cSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s%s", kvm__get_dir(), guestfs_name, path);
205282113fdSPekka Enberg 
206282113fdSPekka Enberg 	snprintf(target, PATH_MAX, "/host%s", path);
207282113fdSPekka Enberg 
208282113fdSPekka Enberg 	return symlink(target, name);
209282113fdSPekka Enberg }
210282113fdSPekka Enberg 
211c8675741SSasha Levin static int make_dir(const char *dir)
212282113fdSPekka Enberg {
213282113fdSPekka Enberg 	char name[PATH_MAX];
214282113fdSPekka Enberg 
2159667701cSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s", kvm__get_dir(), dir);
216282113fdSPekka Enberg 
217c8675741SSasha Levin 	return mkdir(name, 0777);
218282113fdSPekka Enberg }
219282113fdSPekka Enberg 
220282113fdSPekka Enberg static void make_guestfs_dir(const char *guestfs_name, const char *dir)
221282113fdSPekka Enberg {
222282113fdSPekka Enberg 	char name[PATH_MAX];
223282113fdSPekka Enberg 
224282113fdSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s", guestfs_name, dir);
225282113fdSPekka Enberg 
226282113fdSPekka Enberg 	make_dir(name);
227282113fdSPekka Enberg }
228282113fdSPekka Enberg 
22969c88b95SSasha Levin void kvm_setup_resolv(const char *guestfs_name)
23069c88b95SSasha Levin {
23169c88b95SSasha Levin 	char path[PATH_MAX];
23269c88b95SSasha Levin 
2339667701cSPekka Enberg 	snprintf(path, PATH_MAX, "%s%s/etc/resolv.conf", kvm__get_dir(), guestfs_name);
23469c88b95SSasha Levin 
23569c88b95SSasha Levin 	copy_file("/etc/resolv.conf", path);
23669c88b95SSasha Levin }
23769c88b95SSasha Levin 
238282113fdSPekka Enberg static int do_setup(const char *guestfs_name)
239282113fdSPekka Enberg {
240282113fdSPekka Enberg 	unsigned int i;
24190ef0dc6SSasha Levin 	int ret;
242282113fdSPekka Enberg 
243c8675741SSasha Levin 	ret = make_dir(guestfs_name);
244c8675741SSasha Levin 	if (ret < 0)
245c8675741SSasha Levin 		return ret;
246282113fdSPekka Enberg 
247282113fdSPekka Enberg 	for (i = 0; i < ARRAY_SIZE(guestfs_dirs); i++)
248282113fdSPekka Enberg 		make_guestfs_dir(guestfs_name, guestfs_dirs[i]);
249282113fdSPekka Enberg 
250282113fdSPekka Enberg 	for (i = 0; i < ARRAY_SIZE(guestfs_symlinks); i++) {
251282113fdSPekka Enberg 		make_guestfs_symlink(guestfs_name, guestfs_symlinks[i]);
252282113fdSPekka Enberg 	}
253282113fdSPekka Enberg 
254cdce942cSDimitri John Ledkov 	ret = kvm_setup_guest_init(guestfs_name);
255ce6927e8SSasha Levin 	if (ret < 0)
256ce6927e8SSasha Levin 		return ret;
257ce6927e8SSasha Levin 
258ce6927e8SSasha Levin 	return copy_passwd(guestfs_name);
259282113fdSPekka Enberg }
260282113fdSPekka Enberg 
261c8675741SSasha Levin int kvm_setup_create_new(const char *guestfs_name)
262c8675741SSasha Levin {
263c8675741SSasha Levin 	return do_setup(guestfs_name);
264c8675741SSasha Levin }
265c8675741SSasha Levin 
266282113fdSPekka Enberg int kvm_cmd_setup(int argc, const char **argv, const char *prefix)
267282113fdSPekka Enberg {
268f9613d9fSSasha Levin 	int r;
269f9613d9fSSasha Levin 
270282113fdSPekka Enberg 	parse_setup_options(argc, argv);
271282113fdSPekka Enberg 
272282113fdSPekka Enberg 	if (instance_name == NULL)
273282113fdSPekka Enberg 		kvm_setup_help();
274282113fdSPekka Enberg 
275f9613d9fSSasha Levin 	r = do_setup(instance_name);
276f9613d9fSSasha Levin 	if (r == 0)
277f9ea40eaSPekka Enberg 		printf("A new rootfs '%s' has been created in '%s%s'.\n\n"
278f9ea40eaSPekka Enberg 			"You can now start it by running the following command:\n\n"
279ee8b1456SWanlong Gao 			"  %s run -d %s\n",
280ee8b1456SWanlong Gao 			instance_name, kvm__get_dir(), instance_name,
281ee8b1456SWanlong Gao 			KVM_BINARY_NAME,instance_name);
2827b6d50d6SSasha Levin 	else
283f9ea40eaSPekka Enberg 		printf("Unable to create rootfs in %s%s: %s\n",
284f9ea40eaSPekka Enberg 			kvm__get_dir(), instance_name, strerror(errno));
285f9613d9fSSasha Levin 
286f9613d9fSSasha Levin 	return r;
287282113fdSPekka Enberg }
288