]>
Commit | Line | Data |
---|---|---|
60bf62d4 SH |
1 | /* |
2 | * | |
3 | * Copyright © 2013 Serge Hallyn <serge.hallyn@ubuntu.com>. | |
4 | * Copyright © 2013 Canonical Ltd. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2, as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; if not, write to the Free Software Foundation, Inc., | |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 | */ | |
19 | ||
d38dd64a CB |
20 | #ifndef _GNU_SOURCE |
21 | #define _GNU_SOURCE 1 | |
22 | #endif | |
e3347eef | 23 | #include <fcntl.h> |
60bf62d4 | 24 | #include <libgen.h> |
446ca810 | 25 | #include <stdio.h> |
e3347eef | 26 | #include <string.h> |
e3347eef | 27 | #include <sys/stat.h> |
60bf62d4 | 28 | #include <sys/types.h> |
d38dd64a | 29 | #include <unistd.h> |
60bf62d4 | 30 | |
446ca810 CB |
31 | #include <lxc/lxccontainer.h> |
32 | ||
60bf62d4 | 33 | #include "arguments.h" |
d38dd64a | 34 | #include "config.h" |
75e607ba | 35 | #include "log.h" |
36 | #include "utils.h" | |
60bf62d4 | 37 | |
75e607ba | 38 | lxc_log_define(lxc_destroy, lxc); |
39 | ||
40 | static int my_parser(struct lxc_arguments *args, int c, char *arg); | |
a5375956 | 41 | static bool do_destroy(struct lxc_container *c); |
42 | static bool do_destroy_with_snapshots(struct lxc_container *c); | |
60bf62d4 SH |
43 | |
44 | static const struct option my_longopts[] = { | |
45 | {"force", no_argument, 0, 'f'}, | |
f29bb5d5 | 46 | {"snapshots", no_argument, 0, 's'}, |
60bf62d4 SH |
47 | LXC_COMMON_OPTIONS |
48 | }; | |
49 | ||
50 | static struct lxc_arguments my_args = { | |
a5375956 | 51 | .progname = "lxc-destroy", |
52 | .help = "\ | |
60bf62d4 SH |
53 | --name=NAME [-f] [-P lxcpath]\n\ |
54 | \n\ | |
4c1f6b67 | 55 | lxc-destroy destroys a container with the identifier NAME\n\ |
60bf62d4 SH |
56 | \n\ |
57 | Options :\n\ | |
f29bb5d5 CB |
58 | -n, --name=NAME NAME of the container\n\ |
59 | -s, --snapshots destroy including all snapshots\n\ | |
50b737a3 WB |
60 | -f, --force wait for the container to shut down\n\ |
61 | --rcfile=FILE Load configuration file FILE\n", | |
a5375956 | 62 | .options = my_longopts, |
63 | .parser = my_parser, | |
64 | .checker = NULL, | |
65 | .log_priority = "ERROR", | |
66 | .log_file = "none", | |
67 | .task = DESTROY, | |
60bf62d4 SH |
68 | }; |
69 | ||
f29bb5d5 CB |
70 | static int my_parser(struct lxc_arguments *args, int c, char *arg) |
71 | { | |
72 | switch (c) { | |
a5375956 | 73 | case 'f': |
74 | args->force = 1; | |
75 | break; | |
76 | case 's': | |
77 | args->task = SNAP; | |
78 | break; | |
f29bb5d5 CB |
79 | } |
80 | return 0; | |
81 | } | |
82 | ||
2c5f2ede | 83 | static bool do_destroy(struct lxc_container *c) |
f29bb5d5 | 84 | { |
a5375956 | 85 | int ret; |
2c5f2ede | 86 | bool bret = true; |
75e607ba | 87 | char path[MAXPATHLEN]; |
2c5f2ede CB |
88 | |
89 | /* First check whether the container has dependent clones or snapshots. */ | |
a5375956 | 90 | ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); |
75e607ba | 91 | if (ret < 0 || ret >= MAXPATHLEN) |
2c5f2ede CB |
92 | return false; |
93 | ||
94 | if (file_exists(path)) { | |
8f0bdb05 | 95 | ERROR("Destroying %s failed: %s has clones", c->name, c->name); |
2c5f2ede | 96 | return false; |
7909bb03 CB |
97 | } |
98 | ||
75e607ba | 99 | ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name); |
100 | if (ret < 0 || ret >= MAXPATHLEN) | |
2c5f2ede | 101 | return false; |
f29bb5d5 | 102 | |
eb0760f9 | 103 | if (rmdir(path) < 0 && errno != ENOENT) { |
8f0bdb05 | 104 | ERROR("Destroying %s failed: %s has snapshots", c->name, c->name); |
2c5f2ede CB |
105 | return false; |
106 | } | |
107 | ||
108 | if (c->is_running(c)) { | |
75e607ba | 109 | if (!my_args.force) { |
110 | ERROR("%s is running", my_args.name); | |
2c5f2ede CB |
111 | return false; |
112 | } | |
75e607ba | 113 | |
2c5f2ede CB |
114 | /* If the container was ephemeral it will be removed on shutdown. */ |
115 | c->stop(c); | |
116 | } | |
117 | ||
118 | /* If the container was ephemeral we have already removed it when we | |
119 | * stopped it. */ | |
e3347eef CB |
120 | if (c->is_defined(c)) { |
121 | char buf[256]; | |
75e607ba | 122 | |
e3347eef | 123 | ret = c->get_config_item(c, "lxc.ephemeral", buf, 256); |
a5375956 | 124 | if (ret > 0 && strcmp(buf, "0") == 0) |
e3347eef | 125 | bret = c->destroy(c); |
e3347eef | 126 | } |
2c5f2ede CB |
127 | |
128 | if (!bret) { | |
75e607ba | 129 | ERROR("Destroying %s failed", my_args.name); |
2c5f2ede CB |
130 | return false; |
131 | } | |
132 | ||
133 | return true; | |
7909bb03 | 134 | } |
f29bb5d5 | 135 | |
2c5f2ede | 136 | static bool do_destroy_with_snapshots(struct lxc_container *c) |
f29bb5d5 | 137 | { |
19712e04 CB |
138 | struct lxc_container *c1; |
139 | struct stat fbuf; | |
4f64d0db | 140 | bool bret = false; |
75e607ba | 141 | char path[MAXPATHLEN]; |
19712e04 CB |
142 | char *buf = NULL; |
143 | char *lxcpath = NULL; | |
144 | char *lxcname = NULL; | |
19712e04 CB |
145 | int fd; |
146 | int ret; | |
aeba3f80 | 147 | ssize_t bytes; |
19712e04 | 148 | |
2c5f2ede | 149 | /* Destroy clones. */ |
75e607ba | 150 | ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); |
151 | if (ret < 0 || ret >= MAXPATHLEN) | |
2c5f2ede | 152 | return false; |
19712e04 CB |
153 | |
154 | fd = open(path, O_RDONLY | O_CLOEXEC); | |
c01859e8 | 155 | if (fd >= 0) { |
19712e04 CB |
156 | ret = fstat(fd, &fbuf); |
157 | if (ret < 0) { | |
158 | close(fd); | |
2c5f2ede | 159 | return false; |
19712e04 CB |
160 | } |
161 | ||
162 | /* Make sure that the string is \0 terminated. */ | |
163 | buf = calloc(fbuf.st_size + 1, sizeof(char)); | |
164 | if (!buf) { | |
75e607ba | 165 | ERROR("Failed to allocate memory"); |
19712e04 | 166 | close(fd); |
2c5f2ede | 167 | return false; |
19712e04 CB |
168 | } |
169 | ||
aeba3f80 | 170 | bytes = lxc_read_nointr(fd, buf, fbuf.st_size); |
7b6f89cd | 171 | close(fd); |
aeba3f80 | 172 | if (bytes != (ssize_t)fbuf.st_size) { |
75e607ba | 173 | ERROR("Could not read %s", path); |
19712e04 | 174 | free(buf); |
2c5f2ede | 175 | return false; |
19712e04 | 176 | } |
19712e04 | 177 | |
7de8e0a9 | 178 | lxc_iterate_parts(lxcpath, buf, "\n") { |
19712e04 | 179 | c1 = lxc_container_new(lxcname, lxcpath); |
7de8e0a9 | 180 | if (!c1) |
4f64d0db | 181 | continue; |
75e607ba | 182 | |
2c5f2ede CB |
183 | /* We do not destroy recursively. If a clone of a clone |
184 | * has clones or snapshots the user should remove it | |
185 | * explicitly. */ | |
186 | if (!do_destroy(c1)) { | |
19712e04 CB |
187 | lxc_container_put(c1); |
188 | free(buf); | |
2c5f2ede | 189 | return false; |
19712e04 | 190 | } |
75e607ba | 191 | |
19712e04 | 192 | lxc_container_put(c1); |
19712e04 CB |
193 | } |
194 | free(buf); | |
195 | } | |
196 | ||
197 | /* Destroy snapshots located in the containers snap/ folder. */ | |
75e607ba | 198 | ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name); |
199 | if (ret < 0 || ret >= MAXPATHLEN) | |
2c5f2ede | 200 | return false; |
4f64d0db | 201 | |
eb0760f9 | 202 | if (rmdir(path) < 0 && errno != ENOENT) |
4f64d0db CB |
203 | bret = c->destroy_with_snapshots(c); |
204 | else | |
2c5f2ede | 205 | bret = do_destroy(c); |
4f64d0db | 206 | |
2c5f2ede | 207 | return bret; |
f29bb5d5 | 208 | } |
a5375956 | 209 | |
210 | int main(int argc, char *argv[]) | |
211 | { | |
212 | struct lxc_container *c; | |
213 | struct lxc_log log; | |
214 | bool bret; | |
215 | ||
216 | if (lxc_arguments_parse(&my_args, argc, argv)) | |
217 | exit(EXIT_FAILURE); | |
218 | ||
219 | log.name = my_args.name; | |
220 | log.file = my_args.log_file; | |
221 | log.level = my_args.log_priority; | |
222 | log.prefix = my_args.progname; | |
223 | log.quiet = my_args.quiet; | |
224 | log.lxcpath = my_args.lxcpath[0]; | |
225 | ||
226 | if (lxc_log_init(&log)) | |
227 | exit(EXIT_FAILURE); | |
228 | ||
229 | c = lxc_container_new(my_args.name, my_args.lxcpath[0]); | |
230 | if (!c) { | |
231 | ERROR("System error loading container"); | |
232 | exit(EXIT_FAILURE); | |
233 | } | |
234 | ||
235 | if (my_args.rcfile) { | |
236 | c->clear_config(c); | |
237 | ||
238 | if (!c->load_config(c, my_args.rcfile)) { | |
239 | ERROR("Failed to load rcfile"); | |
240 | lxc_container_put(c); | |
241 | exit(EXIT_FAILURE); | |
242 | } | |
243 | ||
244 | c->configfile = strdup(my_args.rcfile); | |
245 | if (!c->configfile) { | |
246 | ERROR("Out of memory setting new config filename"); | |
247 | lxc_container_put(c); | |
248 | exit(EXIT_FAILURE); | |
249 | } | |
250 | } | |
251 | ||
252 | if (!c->may_control(c)) { | |
253 | ERROR("Insufficent privileges to control %s", my_args.name); | |
254 | lxc_container_put(c); | |
255 | exit(EXIT_FAILURE); | |
256 | } | |
257 | ||
258 | if (!c->is_defined(c)) { | |
259 | ERROR("Container is not defined"); | |
260 | lxc_container_put(c); | |
261 | exit(EXIT_FAILURE); | |
262 | } | |
263 | ||
264 | if (my_args.task == SNAP) { | |
265 | bret = do_destroy_with_snapshots(c); | |
266 | if (bret) | |
267 | ERROR("Destroyed container %s including snapshots", my_args.name); | |
268 | } else { | |
269 | bret = do_destroy(c); | |
270 | if (bret) | |
271 | ERROR("Destroyed container %s", my_args.name); | |
272 | } | |
273 | ||
274 | lxc_container_put(c); | |
275 | ||
276 | if (bret) | |
277 | exit(EXIT_SUCCESS); | |
278 | ||
279 | exit(EXIT_FAILURE); | |
280 | } |