xref: /kvmtool/builtin-setup.c (revision 26e94dc4257933b7c03b4da4a90c156e5a1d38fd)
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
125*26e94dc4SOleg Nesterov static int extract_file(const char *guestfs_name, const char *filename,
126*26e94dc4SOleg Nesterov 			const void *data, const void *_size)
127282113fdSPekka Enberg {
128282113fdSPekka Enberg 	char path[PATH_MAX];
129f20814b8SAsias He 	int fd, ret;
130282113fdSPekka Enberg 
131*26e94dc4SOleg Nesterov 	snprintf(path, PATH_MAX, "%s%s/%s", kvm__get_dir(),
132*26e94dc4SOleg Nesterov 				guestfs_name, filename);
133f20814b8SAsias He 	remove(path);
134f20814b8SAsias He 	fd = open(path, O_CREAT | O_WRONLY, 0755);
135f20814b8SAsias He 	if (fd < 0)
136f20814b8SAsias He 		die("Fail to setup %s", path);
137*26e94dc4SOleg Nesterov 	ret = xwrite(fd, data, (size_t)_size);
138f20814b8SAsias He 	if (ret < 0)
139f20814b8SAsias He 		die("Fail to setup %s", path);
140f20814b8SAsias He 	close(fd);
141282113fdSPekka Enberg 
142f20814b8SAsias He 	return 0;
143*26e94dc4SOleg Nesterov }
144cdce942cSDimitri John Ledkov 
145*26e94dc4SOleg Nesterov extern char _binary_guest_init_start;
146*26e94dc4SOleg Nesterov extern char _binary_guest_init_size;
147*26e94dc4SOleg Nesterov 
148*26e94dc4SOleg Nesterov int kvm_setup_guest_init(const char *guestfs_name)
149*26e94dc4SOleg Nesterov {
150*26e94dc4SOleg Nesterov 	return extract_file(guestfs_name, "virt/init",
151*26e94dc4SOleg Nesterov 				&_binary_guest_init_start,
152*26e94dc4SOleg Nesterov 				&_binary_guest_init_size);
153282113fdSPekka Enberg }
154cdce942cSDimitri John Ledkov #else
155cdce942cSDimitri John Ledkov int kvm_setup_guest_init(const char *guestfs_name)
156cdce942cSDimitri John Ledkov {
157cdce942cSDimitri John Ledkov 	die("Guest init image not compiled in");
158cdce942cSDimitri John Ledkov 	return 0;
159cdce942cSDimitri John Ledkov }
160cdce942cSDimitri John Ledkov #endif
161282113fdSPekka Enberg 
162ce6927e8SSasha Levin static int copy_passwd(const char *guestfs_name)
163ce6927e8SSasha Levin {
164ce6927e8SSasha Levin 	char path[PATH_MAX];
165f20814b8SAsias He 	FILE *file;
166f20814b8SAsias He 	int ret;
167ce6927e8SSasha Levin 
168ce6927e8SSasha Levin 	snprintf(path, PATH_MAX, "%s%s/etc/passwd", kvm__get_dir(), guestfs_name);
169ce6927e8SSasha Levin 
170f20814b8SAsias He 	file = fopen(path, "w");
171f20814b8SAsias He 	if (!file)
172f20814b8SAsias He 		return -1;
173f20814b8SAsias He 
174f20814b8SAsias He 	ret = fprintf(file, "root:x:0:0:root:/root:/bin/sh\n");
17563fd57b9SCong Ding 	if (ret > 0)
17663fd57b9SCong Ding 		ret = 0;
177f20814b8SAsias He 
178f20814b8SAsias He 	fclose(file);
179f20814b8SAsias He 
18063fd57b9SCong Ding 	return ret;
181ce6927e8SSasha Levin }
182ce6927e8SSasha Levin 
183282113fdSPekka Enberg static int make_guestfs_symlink(const char *guestfs_name, const char *path)
184282113fdSPekka Enberg {
185282113fdSPekka Enberg 	char target[PATH_MAX];
186282113fdSPekka Enberg 	char name[PATH_MAX];
187282113fdSPekka Enberg 
1889667701cSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s%s", kvm__get_dir(), guestfs_name, path);
189282113fdSPekka Enberg 
190282113fdSPekka Enberg 	snprintf(target, PATH_MAX, "/host%s", path);
191282113fdSPekka Enberg 
192282113fdSPekka Enberg 	return symlink(target, name);
193282113fdSPekka Enberg }
194282113fdSPekka Enberg 
195c8675741SSasha Levin static int make_dir(const char *dir)
196282113fdSPekka Enberg {
197282113fdSPekka Enberg 	char name[PATH_MAX];
198282113fdSPekka Enberg 
1999667701cSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s", kvm__get_dir(), dir);
200282113fdSPekka Enberg 
201c8675741SSasha Levin 	return mkdir(name, 0777);
202282113fdSPekka Enberg }
203282113fdSPekka Enberg 
204282113fdSPekka Enberg static void make_guestfs_dir(const char *guestfs_name, const char *dir)
205282113fdSPekka Enberg {
206282113fdSPekka Enberg 	char name[PATH_MAX];
207282113fdSPekka Enberg 
208282113fdSPekka Enberg 	snprintf(name, PATH_MAX, "%s%s", guestfs_name, dir);
209282113fdSPekka Enberg 
210282113fdSPekka Enberg 	make_dir(name);
211282113fdSPekka Enberg }
212282113fdSPekka Enberg 
21369c88b95SSasha Levin void kvm_setup_resolv(const char *guestfs_name)
21469c88b95SSasha Levin {
21569c88b95SSasha Levin 	char path[PATH_MAX];
21669c88b95SSasha Levin 
2179667701cSPekka Enberg 	snprintf(path, PATH_MAX, "%s%s/etc/resolv.conf", kvm__get_dir(), guestfs_name);
21869c88b95SSasha Levin 
21969c88b95SSasha Levin 	copy_file("/etc/resolv.conf", path);
22069c88b95SSasha Levin }
22169c88b95SSasha Levin 
222282113fdSPekka Enberg static int do_setup(const char *guestfs_name)
223282113fdSPekka Enberg {
224282113fdSPekka Enberg 	unsigned int i;
22590ef0dc6SSasha Levin 	int ret;
226282113fdSPekka Enberg 
227c8675741SSasha Levin 	ret = make_dir(guestfs_name);
228c8675741SSasha Levin 	if (ret < 0)
229c8675741SSasha Levin 		return ret;
230282113fdSPekka Enberg 
231282113fdSPekka Enberg 	for (i = 0; i < ARRAY_SIZE(guestfs_dirs); i++)
232282113fdSPekka Enberg 		make_guestfs_dir(guestfs_name, guestfs_dirs[i]);
233282113fdSPekka Enberg 
234282113fdSPekka Enberg 	for (i = 0; i < ARRAY_SIZE(guestfs_symlinks); i++) {
235282113fdSPekka Enberg 		make_guestfs_symlink(guestfs_name, guestfs_symlinks[i]);
236282113fdSPekka Enberg 	}
237282113fdSPekka Enberg 
238cdce942cSDimitri John Ledkov 	ret = kvm_setup_guest_init(guestfs_name);
239ce6927e8SSasha Levin 	if (ret < 0)
240ce6927e8SSasha Levin 		return ret;
241ce6927e8SSasha Levin 
242ce6927e8SSasha Levin 	return copy_passwd(guestfs_name);
243282113fdSPekka Enberg }
244282113fdSPekka Enberg 
245c8675741SSasha Levin int kvm_setup_create_new(const char *guestfs_name)
246c8675741SSasha Levin {
247c8675741SSasha Levin 	return do_setup(guestfs_name);
248c8675741SSasha Levin }
249c8675741SSasha Levin 
250282113fdSPekka Enberg int kvm_cmd_setup(int argc, const char **argv, const char *prefix)
251282113fdSPekka Enberg {
252f9613d9fSSasha Levin 	int r;
253f9613d9fSSasha Levin 
254282113fdSPekka Enberg 	parse_setup_options(argc, argv);
255282113fdSPekka Enberg 
256282113fdSPekka Enberg 	if (instance_name == NULL)
257282113fdSPekka Enberg 		kvm_setup_help();
258282113fdSPekka Enberg 
259f9613d9fSSasha Levin 	r = do_setup(instance_name);
260f9613d9fSSasha Levin 	if (r == 0)
261f9ea40eaSPekka Enberg 		printf("A new rootfs '%s' has been created in '%s%s'.\n\n"
262f9ea40eaSPekka Enberg 			"You can now start it by running the following command:\n\n"
263ee8b1456SWanlong Gao 			"  %s run -d %s\n",
264ee8b1456SWanlong Gao 			instance_name, kvm__get_dir(), instance_name,
265ee8b1456SWanlong Gao 			KVM_BINARY_NAME,instance_name);
2667b6d50d6SSasha Levin 	else
267f9ea40eaSPekka Enberg 		printf("Unable to create rootfs in %s%s: %s\n",
268f9ea40eaSPekka Enberg 			kvm__get_dir(), instance_name, strerror(errno));
269f9613d9fSSasha Levin 
270f9613d9fSSasha Levin 	return r;
271282113fdSPekka Enberg }
272