xref: /kvmtool/builtin-setup.c (revision 93dd1288081497146b3a7ad98e40c5b2635c3148)
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,
126*93dd1288SMarc Zyngier 			const void *data, size_t 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);
13330867c55SOleg Nesterov 
13430867c55SOleg Nesterov 	fd = open(path, O_EXCL | O_CREAT | O_WRONLY, 0755);
13530867c55SOleg Nesterov 	if (fd < 0) {
13630867c55SOleg Nesterov 		if (errno == EEXIST)
13730867c55SOleg Nesterov 			return 0;
138f20814b8SAsias He 		die("Fail to setup %s", path);
13930867c55SOleg Nesterov 	}
14030867c55SOleg Nesterov 
141*93dd1288SMarc Zyngier 	ret = xwrite(fd, data, 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 
149*93dd1288SMarc Zyngier extern unsigned char init_binary[];
150*93dd1288SMarc Zyngier extern unsigned long init_binary_size;
151*93dd1288SMarc Zyngier extern unsigned char pre_init_binary[];
152*93dd1288SMarc Zyngier extern unsigned long pre_init_binary_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",
160*93dd1288SMarc Zyngier 			   pre_init_binary, pre_init_binary_size);
1615614f9d9SOleg Nesterov 	if (err)
1625614f9d9SOleg Nesterov 		return err;
1635614f9d9SOleg Nesterov #endif
1645614f9d9SOleg Nesterov 	err = extract_file(guestfs_name, "virt/init",
165*93dd1288SMarc Zyngier 			   init_binary, init_binary_size);
1665614f9d9SOleg Nesterov 	return err;
167282113fdSPekka Enberg }
168cdce942cSDimitri John Ledkov #else
169cdce942cSDimitri John Ledkov int kvm_setup_guest_init(const char *guestfs_name)
170cdce942cSDimitri John Ledkov {
171cdce942cSDimitri John Ledkov 	die("Guest init image not compiled in");
172cdce942cSDimitri John Ledkov 	return 0;
173cdce942cSDimitri John Ledkov }
174cdce942cSDimitri John Ledkov #endif
175282113fdSPekka Enberg 
176ce6927e8SSasha Levin static int copy_passwd(const char *guestfs_name)
177ce6927e8SSasha Levin {
178ce6927e8SSasha Levin 	char path[PATH_MAX];
179f20814b8SAsias He 	FILE *file;
180f20814b8SAsias He 	int ret;
181ce6927e8SSasha Levin 
182ce6927e8SSasha Levin 	snprintf(path, PATH_MAX, "%s%s/etc/passwd", kvm__get_dir(), guestfs_name);
183ce6927e8SSasha Levin 
184f20814b8SAsias He 	file = fopen(path, "w");
185f20814b8SAsias He 	if (!file)
186f20814b8SAsias He 		return -1;
187f20814b8SAsias He 
188f20814b8SAsias He 	ret = fprintf(file, "root:x:0:0:root:/root:/bin/sh\n");
18963fd57b9SCong Ding 	if (ret > 0)
19063fd57b9SCong Ding 		ret = 0;
191f20814b8SAsias He 
192f20814b8SAsias He 	fclose(file);
193f20814b8SAsias He 
19463fd57b9SCong Ding 	return ret;
195ce6927e8SSasha Levin }
196ce6927e8SSasha Levin 
197282113fdSPekka Enberg static int make_guestfs_symlink(const char *guestfs_name, const char *path)
198282113fdSPekka Enberg {
199282113fdSPekka Enberg 	char target[PATH_MAX];
200282113fdSPekka Enberg 	char name[PATH_MAX];
201282113fdSPekka Enberg 
2029667701cSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s%s", kvm__get_dir(), guestfs_name, path);
203282113fdSPekka Enberg 
204282113fdSPekka Enberg 	snprintf(target, PATH_MAX, "/host%s", path);
205282113fdSPekka Enberg 
206282113fdSPekka Enberg 	return symlink(target, name);
207282113fdSPekka Enberg }
208282113fdSPekka Enberg 
209c8675741SSasha Levin static int make_dir(const char *dir)
210282113fdSPekka Enberg {
211282113fdSPekka Enberg 	char name[PATH_MAX];
212282113fdSPekka Enberg 
2139667701cSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s", kvm__get_dir(), dir);
214282113fdSPekka Enberg 
215c8675741SSasha Levin 	return mkdir(name, 0777);
216282113fdSPekka Enberg }
217282113fdSPekka Enberg 
218282113fdSPekka Enberg static void make_guestfs_dir(const char *guestfs_name, const char *dir)
219282113fdSPekka Enberg {
220282113fdSPekka Enberg 	char name[PATH_MAX];
221282113fdSPekka Enberg 
222282113fdSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s", guestfs_name, dir);
223282113fdSPekka Enberg 
224282113fdSPekka Enberg 	make_dir(name);
225282113fdSPekka Enberg }
226282113fdSPekka Enberg 
22769c88b95SSasha Levin void kvm_setup_resolv(const char *guestfs_name)
22869c88b95SSasha Levin {
22969c88b95SSasha Levin 	char path[PATH_MAX];
23069c88b95SSasha Levin 
2319667701cSPekka Enberg 	snprintf(path, PATH_MAX, "%s%s/etc/resolv.conf", kvm__get_dir(), guestfs_name);
23269c88b95SSasha Levin 
23369c88b95SSasha Levin 	copy_file("/etc/resolv.conf", path);
23469c88b95SSasha Levin }
23569c88b95SSasha Levin 
236282113fdSPekka Enberg static int do_setup(const char *guestfs_name)
237282113fdSPekka Enberg {
238282113fdSPekka Enberg 	unsigned int i;
23990ef0dc6SSasha Levin 	int ret;
240282113fdSPekka Enberg 
241c8675741SSasha Levin 	ret = make_dir(guestfs_name);
242c8675741SSasha Levin 	if (ret < 0)
243c8675741SSasha Levin 		return ret;
244282113fdSPekka Enberg 
245282113fdSPekka Enberg 	for (i = 0; i < ARRAY_SIZE(guestfs_dirs); i++)
246282113fdSPekka Enberg 		make_guestfs_dir(guestfs_name, guestfs_dirs[i]);
247282113fdSPekka Enberg 
248282113fdSPekka Enberg 	for (i = 0; i < ARRAY_SIZE(guestfs_symlinks); i++) {
249282113fdSPekka Enberg 		make_guestfs_symlink(guestfs_name, guestfs_symlinks[i]);
250282113fdSPekka Enberg 	}
251282113fdSPekka Enberg 
252cdce942cSDimitri John Ledkov 	ret = kvm_setup_guest_init(guestfs_name);
253ce6927e8SSasha Levin 	if (ret < 0)
254ce6927e8SSasha Levin 		return ret;
255ce6927e8SSasha Levin 
256ce6927e8SSasha Levin 	return copy_passwd(guestfs_name);
257282113fdSPekka Enberg }
258282113fdSPekka Enberg 
259c8675741SSasha Levin int kvm_setup_create_new(const char *guestfs_name)
260c8675741SSasha Levin {
261c8675741SSasha Levin 	return do_setup(guestfs_name);
262c8675741SSasha Levin }
263c8675741SSasha Levin 
264282113fdSPekka Enberg int kvm_cmd_setup(int argc, const char **argv, const char *prefix)
265282113fdSPekka Enberg {
266f9613d9fSSasha Levin 	int r;
267f9613d9fSSasha Levin 
268282113fdSPekka Enberg 	parse_setup_options(argc, argv);
269282113fdSPekka Enberg 
270282113fdSPekka Enberg 	if (instance_name == NULL)
271282113fdSPekka Enberg 		kvm_setup_help();
272282113fdSPekka Enberg 
273f9613d9fSSasha Levin 	r = do_setup(instance_name);
274f9613d9fSSasha Levin 	if (r == 0)
275f9ea40eaSPekka Enberg 		printf("A new rootfs '%s' has been created in '%s%s'.\n\n"
276f9ea40eaSPekka Enberg 			"You can now start it by running the following command:\n\n"
277ee8b1456SWanlong Gao 			"  %s run -d %s\n",
278ee8b1456SWanlong Gao 			instance_name, kvm__get_dir(), instance_name,
279ee8b1456SWanlong Gao 			KVM_BINARY_NAME,instance_name);
2807b6d50d6SSasha Levin 	else
281f9ea40eaSPekka Enberg 		printf("Unable to create rootfs in %s%s: %s\n",
282f9ea40eaSPekka Enberg 			kvm__get_dir(), instance_name, strerror(errno));
283f9613d9fSSasha Levin 
284f9613d9fSSasha Levin 	return r;
285282113fdSPekka Enberg }
286