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