xref: /kvmtool/builtin-setup.c (revision 7b6d50d6294bb630544556ee163098e482865f21)
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 
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <limits.h>
10 #include <signal.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <sys/mman.h>
17 #include <sys/stat.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 
22 #define KVM_PID_FILE_PATH	"/.kvm-tools/"
23 #define HOME_DIR		getenv("HOME")
24 
25 static const char *instance_name;
26 
27 static const char * const setup_usage[] = {
28 	"kvm setup [name]",
29 	NULL
30 };
31 
32 static const struct option setup_options[] = {
33 	OPT_END()
34 };
35 
36 static void parse_setup_options(int argc, const char **argv)
37 {
38 	while (argc != 0) {
39 		argc = parse_options(argc, argv, setup_options, setup_usage,
40 				PARSE_OPT_STOP_AT_NON_OPTION);
41 		if (argc != 0 && instance_name)
42 			kvm_setup_help();
43 		else
44 			instance_name = argv[0];
45 		argv++;
46 		argc--;
47 	}
48 }
49 
50 void kvm_setup_help(void)
51 {
52 	printf("\nkvm setup creates a new rootfs and stores it under ~/.kvm-tools/ .\n"
53 		"This can be used later by the '-d' parameter of 'kvm run'.\n");
54 	usage_with_options(setup_usage, setup_options);
55 }
56 
57 static int copy_file(const char *from, const char *to)
58 {
59 	int in_fd, out_fd;
60 	void *src, *dst;
61 	struct stat st;
62 	int err = -1;
63 
64 	in_fd = open(from, O_RDONLY);
65 	if (in_fd < 0)
66 		return err;
67 
68 	if (fstat(in_fd, &st) < 0)
69 		goto error_close_in;
70 
71 	out_fd = open(to, O_RDWR | O_CREAT | O_TRUNC, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO));
72 	if (out_fd < 0)
73 		goto error_close_in;
74 
75 	src = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, in_fd, 0);
76 	if (src == MAP_FAILED)
77 		goto error_close_out;
78 
79 	if (ftruncate(out_fd, st.st_size) < 0)
80 		goto error_munmap_src;
81 
82 	dst = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, out_fd, 0);
83 	if (dst == MAP_FAILED)
84 		goto error_munmap_src;
85 
86 	memcpy(dst, src, st.st_size);
87 
88 	if (fsync(out_fd) < 0)
89 		goto error_munmap_dst;
90 
91 	err = 0;
92 
93 error_munmap_dst:
94 	munmap(dst, st.st_size);
95 error_munmap_src:
96 	munmap(src, st.st_size);
97 error_close_out:
98 	close(out_fd);
99 error_close_in:
100 	close(in_fd);
101 
102 	return err;
103 }
104 
105 static const char *guestfs_dirs[] = {
106 	"/dev",
107 	"/etc",
108 	"/home",
109 	"/host",
110 	"/proc",
111 	"/root",
112 	"/sys",
113 	"/tmp",
114 	"/var",
115 	"/var/lib",
116 	"/virt",
117 };
118 
119 static const char *guestfs_symlinks[] = {
120 	"/bin",
121 	"/lib",
122 	"/lib64",
123 	"/sbin",
124 	"/usr",
125 };
126 
127 static int copy_init(const char *guestfs_name)
128 {
129 	char path[PATH_MAX];
130 
131 	snprintf(path, PATH_MAX, "%s%s%s/virt/init", HOME_DIR, KVM_PID_FILE_PATH, guestfs_name);
132 
133 	return copy_file("guest/init", path);
134 }
135 
136 static int make_guestfs_symlink(const char *guestfs_name, const char *path)
137 {
138 	char target[PATH_MAX];
139 	char name[PATH_MAX];
140 
141 	snprintf(name, PATH_MAX, "%s%s%s%s", HOME_DIR, KVM_PID_FILE_PATH, guestfs_name, path);
142 
143 	snprintf(target, PATH_MAX, "/host%s", path);
144 
145 	return symlink(target, name);
146 }
147 
148 static void make_root_dir(void)
149 {
150 	char name[PATH_MAX];
151 
152 	snprintf(name, PATH_MAX, "%s%s", HOME_DIR, KVM_PID_FILE_PATH);
153 
154 	mkdir(name, 0777);
155 }
156 
157 static int make_dir(const char *dir)
158 {
159 	char name[PATH_MAX];
160 
161 	snprintf(name, PATH_MAX, "%s%s%s", HOME_DIR, KVM_PID_FILE_PATH, dir);
162 
163 	return mkdir(name, 0777);
164 }
165 
166 static void make_guestfs_dir(const char *guestfs_name, const char *dir)
167 {
168 	char name[PATH_MAX];
169 
170 	snprintf(name, PATH_MAX, "%s%s", guestfs_name, dir);
171 
172 	make_dir(name);
173 }
174 
175 void kvm_setup_resolv(const char *guestfs_name)
176 {
177 	char path[PATH_MAX];
178 
179 	snprintf(path, PATH_MAX, "%s%s%s/etc/resolv.conf", HOME_DIR, KVM_PID_FILE_PATH, guestfs_name);
180 
181 	copy_file("/etc/resolv.conf", path);
182 }
183 
184 static int do_setup(const char *guestfs_name)
185 {
186 	unsigned int i;
187 	int ret;
188 
189 	make_root_dir();
190 
191 	ret = make_dir(guestfs_name);
192 	if (ret < 0)
193 		return ret;
194 
195 	for (i = 0; i < ARRAY_SIZE(guestfs_dirs); i++)
196 		make_guestfs_dir(guestfs_name, guestfs_dirs[i]);
197 
198 	for (i = 0; i < ARRAY_SIZE(guestfs_symlinks); i++) {
199 		make_guestfs_symlink(guestfs_name, guestfs_symlinks[i]);
200 	}
201 
202 	return copy_init(guestfs_name);
203 }
204 
205 int kvm_setup_create_new(const char *guestfs_name)
206 {
207 	return do_setup(guestfs_name);
208 }
209 
210 int kvm_cmd_setup(int argc, const char **argv, const char *prefix)
211 {
212 	int r;
213 
214 	parse_setup_options(argc, argv);
215 
216 	if (instance_name == NULL)
217 		kvm_setup_help();
218 
219 	r = do_setup(instance_name);
220 	if (r == 0)
221 		pr_info("Your new rootfs named %s has been created.\n"
222 			"You can now start it by running 'kvm run -d %s'\n",
223 			instance_name, instance_name);
224 	else
225 		perror("Error creating rootfs");
226 
227 	return r;
228 }
229