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
parse_setup_options(int argc,const char ** argv)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
kvm_setup_help(void)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
copy_file(const char * from,const char * to)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
extract_file(const char * guestfs_name,const char * filename,const void * data,size_t size)12526e94dc4SOleg Nesterov static int extract_file(const char *guestfs_name, const char *filename,
12693dd1288SMarc 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
14193dd1288SMarc 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
14993dd1288SMarc Zyngier extern unsigned char init_binary[];
15093dd1288SMarc Zyngier extern unsigned long init_binary_size;
15193dd1288SMarc Zyngier extern unsigned char pre_init_binary[];
15293dd1288SMarc Zyngier extern unsigned long pre_init_binary_size;
15326e94dc4SOleg Nesterov
kvm_setup_guest_init(const char * guestfs_name)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",
16093dd1288SMarc 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",
16593dd1288SMarc Zyngier init_binary, init_binary_size);
1665614f9d9SOleg Nesterov return err;
167282113fdSPekka Enberg }
168cdce942cSDimitri John Ledkov #else
kvm_setup_guest_init(const char * guestfs_name)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
copy_passwd(const char * guestfs_name)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
make_guestfs_symlink(const char * guestfs_name,const char * path)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
make_dir(const char * dir)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
make_guestfs_dir(const char * guestfs_name,const char * dir)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
kvm_setup_resolv(const char * guestfs_name)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
do_setup(const char * guestfs_name)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
kvm_setup_create_new(const char * guestfs_name)259c8675741SSasha Levin int kvm_setup_create_new(const char *guestfs_name)
260c8675741SSasha Levin {
261c8675741SSasha Levin return do_setup(guestfs_name);
262c8675741SSasha Levin }
263c8675741SSasha Levin
kvm_cmd_setup(int argc,const char ** argv,const char * prefix)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);
274*72e13944SAlexandru Elisei if (r == 0) {
275*72e13944SAlexandru Elisei pr_info("A new rootfs '%s' has been created in '%s%s'.",
276*72e13944SAlexandru Elisei instance_name, kvm__get_dir(), instance_name);
277*72e13944SAlexandru Elisei pr_info("You can now start it by running the following command:");
278*72e13944SAlexandru Elisei pr_info("%s run -d %s", KVM_BINARY_NAME, instance_name);
279*72e13944SAlexandru Elisei } else {
280*72e13944SAlexandru Elisei pr_err("Unable to create rootfs in %s%s: %s",
281f9ea40eaSPekka Enberg kvm__get_dir(), instance_name, strerror(errno));
282*72e13944SAlexandru Elisei }
283f9613d9fSSasha Levin
284f9613d9fSSasha Levin return r;
285282113fdSPekka Enberg }
286