]>
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 | ||
19712e04 | 20 | #define _GNU_SOURCE |
446ca810 | 21 | #include "config.h" |
60bf62d4 | 22 | |
60bf62d4 | 23 | #include <libgen.h> |
446ca810 | 24 | #include <stdio.h> |
60bf62d4 SH |
25 | #include <unistd.h> |
26 | #include <sys/types.h> | |
27 | ||
446ca810 CB |
28 | #include <lxc/lxccontainer.h> |
29 | ||
60bf62d4 | 30 | #include "arguments.h" |
446ca810 CB |
31 | #include "log.h" |
32 | #include "lxc.h" | |
60bf62d4 SH |
33 | #include "utils.h" |
34 | ||
6ea518f6 | 35 | lxc_log_define(lxc_destroy_ui, lxc); |
60bf62d4 | 36 | |
f29bb5d5 | 37 | static int my_parser(struct lxc_arguments* args, int c, char* arg); |
2f0e6b79 | 38 | static bool quiet; |
60bf62d4 SH |
39 | |
40 | static const struct option my_longopts[] = { | |
41 | {"force", no_argument, 0, 'f'}, | |
f29bb5d5 | 42 | {"snapshots", no_argument, 0, 's'}, |
60bf62d4 SH |
43 | LXC_COMMON_OPTIONS |
44 | }; | |
45 | ||
46 | static struct lxc_arguments my_args = { | |
47 | .progname = "lxc-destroy", | |
48 | .help = "\ | |
49 | --name=NAME [-f] [-P lxcpath]\n\ | |
50 | \n\ | |
4c1f6b67 | 51 | lxc-destroy destroys a container with the identifier NAME\n\ |
60bf62d4 SH |
52 | \n\ |
53 | Options :\n\ | |
f29bb5d5 CB |
54 | -n, --name=NAME NAME of the container\n\ |
55 | -s, --snapshots destroy including all snapshots\n\ | |
50b737a3 WB |
56 | -f, --force wait for the container to shut down\n\ |
57 | --rcfile=FILE Load configuration file FILE\n", | |
60bf62d4 SH |
58 | .options = my_longopts, |
59 | .parser = my_parser, | |
60 | .checker = NULL, | |
f29bb5d5 | 61 | .task = DESTROY, |
60bf62d4 SH |
62 | }; |
63 | ||
2c5f2ede CB |
64 | static bool do_destroy(struct lxc_container *c); |
65 | static bool do_destroy_with_snapshots(struct lxc_container *c); | |
f29bb5d5 | 66 | |
60bf62d4 SH |
67 | int main(int argc, char *argv[]) |
68 | { | |
69 | struct lxc_container *c; | |
73b910a3 | 70 | struct lxc_log log; |
2c5f2ede | 71 | bool bret; |
60bf62d4 | 72 | |
60bf62d4 | 73 | if (lxc_arguments_parse(&my_args, argc, argv)) |
f29bb5d5 | 74 | exit(EXIT_FAILURE); |
60bf62d4 | 75 | |
f5abd74d SG |
76 | if (!my_args.log_file) |
77 | my_args.log_file = "none"; | |
78 | ||
73b910a3 | 79 | log.name = my_args.name; |
80 | log.file = my_args.log_file; | |
81 | log.priority = my_args.log_priority; | |
82 | log.prefix = my_args.progname; | |
83 | log.quiet = my_args.quiet; | |
84 | log.lxcpath = my_args.lxcpath[0]; | |
85 | ||
86 | if (lxc_log_init(&log)) | |
f29bb5d5 | 87 | exit(EXIT_FAILURE); |
6edbfc86 | 88 | lxc_log_options_no_override(); |
2f0e6b79 SH |
89 | if (my_args.quiet) |
90 | quiet = true; | |
60bf62d4 SH |
91 | |
92 | c = lxc_container_new(my_args.name, my_args.lxcpath[0]); | |
93 | if (!c) { | |
2f0e6b79 SH |
94 | if (!quiet) |
95 | fprintf(stderr, "System error loading container\n"); | |
f29bb5d5 | 96 | exit(EXIT_FAILURE); |
60bf62d4 SH |
97 | } |
98 | ||
50b737a3 WB |
99 | if (my_args.rcfile) { |
100 | c->clear_config(c); | |
101 | if (!c->load_config(c, my_args.rcfile)) { | |
102 | fprintf(stderr, "Failed to load rcfile\n"); | |
103 | lxc_container_put(c); | |
104 | exit(EXIT_FAILURE); | |
105 | } | |
6118210e WB |
106 | c->configfile = strdup(my_args.rcfile); |
107 | if (!c->configfile) { | |
108 | fprintf(stderr, "Out of memory setting new config filename\n"); | |
109 | lxc_container_put(c); | |
110 | exit(EXIT_FAILURE); | |
111 | } | |
50b737a3 WB |
112 | } |
113 | ||
f5abd74d | 114 | if (!c->may_control(c)) { |
2f0e6b79 SH |
115 | if (!quiet) |
116 | fprintf(stderr, "Insufficent privileges to control %s\n", my_args.name); | |
f3e52710 | 117 | lxc_container_put(c); |
f29bb5d5 | 118 | exit(EXIT_FAILURE); |
f5abd74d SG |
119 | } |
120 | ||
01e6b714 | 121 | if (!c->is_defined(c)) { |
2f0e6b79 SH |
122 | if (!quiet) |
123 | fprintf(stderr, "Container is not defined\n"); | |
01e6b714 | 124 | lxc_container_put(c); |
f29bb5d5 | 125 | exit(EXIT_FAILURE); |
01e6b714 SH |
126 | } |
127 | ||
f29bb5d5 | 128 | if (my_args.task == SNAP) { |
2c5f2ede CB |
129 | bret = do_destroy_with_snapshots(c); |
130 | if (bret && !quiet) | |
131 | printf("Destroyed container %s including snapshots \n", my_args.name); | |
f29bb5d5 | 132 | } else { |
2c5f2ede CB |
133 | bret = do_destroy(c); |
134 | if (bret && !quiet) | |
135 | printf("Destroyed container %s\n", my_args.name); | |
f29bb5d5 CB |
136 | } |
137 | ||
138 | lxc_container_put(c); | |
139 | ||
2c5f2ede | 140 | if (bret) |
f29bb5d5 CB |
141 | exit(EXIT_SUCCESS); |
142 | exit(EXIT_FAILURE); | |
143 | } | |
144 | ||
145 | static int my_parser(struct lxc_arguments *args, int c, char *arg) | |
146 | { | |
147 | switch (c) { | |
148 | case 'f': args->force = 1; break; | |
149 | case 's': args->task = SNAP; break; | |
150 | } | |
151 | return 0; | |
152 | } | |
153 | ||
2c5f2ede | 154 | static bool do_destroy(struct lxc_container *c) |
f29bb5d5 | 155 | { |
2c5f2ede CB |
156 | bool bret = true; |
157 | char path[MAXPATHLEN]; | |
158 | ||
159 | /* First check whether the container has dependent clones or snapshots. */ | |
160 | int ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); | |
161 | if (ret < 0 || ret >= MAXPATHLEN) | |
162 | return false; | |
163 | ||
164 | if (file_exists(path)) { | |
2f0e6b79 | 165 | if (!quiet) |
2c5f2ede CB |
166 | fprintf(stdout, "Destroying %s failed: %s has clones.\n", c->name, c->name); |
167 | return false; | |
7909bb03 CB |
168 | } |
169 | ||
2c5f2ede CB |
170 | ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name); |
171 | if (ret < 0 || ret >= MAXPATHLEN) | |
172 | return false; | |
f29bb5d5 | 173 | |
2c5f2ede CB |
174 | if (dir_exists(path)) { |
175 | if (!quiet) | |
176 | fprintf(stdout, "Destroying %s failed: %s has snapshots.\n", c->name, c->name); | |
177 | return false; | |
178 | } | |
179 | ||
180 | if (c->is_running(c)) { | |
181 | if (!my_args.force && !quiet) { | |
182 | fprintf(stderr, "%s is running\n", my_args.name); | |
183 | return false; | |
184 | } | |
185 | /* If the container was ephemeral it will be removed on shutdown. */ | |
186 | c->stop(c); | |
187 | } | |
188 | ||
189 | /* If the container was ephemeral we have already removed it when we | |
190 | * stopped it. */ | |
191 | if (c->is_defined(c) && !c->lxc_conf->ephemeral) | |
192 | bret = c->destroy(c); | |
193 | ||
194 | if (!bret) { | |
195 | if (!quiet) | |
196 | fprintf(stderr, "Destroying %s failed\n", my_args.name); | |
197 | return false; | |
198 | } | |
199 | ||
200 | return true; | |
7909bb03 | 201 | } |
f29bb5d5 | 202 | |
2c5f2ede | 203 | static bool do_destroy_with_snapshots(struct lxc_container *c) |
f29bb5d5 | 204 | { |
19712e04 CB |
205 | struct lxc_container *c1; |
206 | struct stat fbuf; | |
4f64d0db | 207 | bool bret = false; |
19712e04 CB |
208 | char path[MAXPATHLEN]; |
209 | char *buf = NULL; | |
210 | char *lxcpath = NULL; | |
211 | char *lxcname = NULL; | |
212 | char *scratch = NULL; | |
213 | int fd; | |
214 | int ret; | |
215 | int counter = 0; | |
216 | ||
2c5f2ede | 217 | /* Destroy clones. */ |
19712e04 | 218 | ret = snprintf(path, MAXPATHLEN, "%s/%s/lxc_snapshots", c->config_path, c->name); |
793e387a | 219 | if (ret < 0 || ret >= MAXPATHLEN) |
2c5f2ede | 220 | return false; |
19712e04 CB |
221 | |
222 | fd = open(path, O_RDONLY | O_CLOEXEC); | |
c01859e8 | 223 | if (fd >= 0) { |
19712e04 CB |
224 | ret = fstat(fd, &fbuf); |
225 | if (ret < 0) { | |
226 | close(fd); | |
2c5f2ede | 227 | return false; |
19712e04 CB |
228 | } |
229 | ||
230 | /* Make sure that the string is \0 terminated. */ | |
231 | buf = calloc(fbuf.st_size + 1, sizeof(char)); | |
232 | if (!buf) { | |
233 | SYSERROR("failed to allocate memory"); | |
234 | close(fd); | |
2c5f2ede | 235 | return false; |
19712e04 CB |
236 | } |
237 | ||
238 | ret = read(fd, buf, fbuf.st_size); | |
239 | if (ret < 0) { | |
240 | ERROR("could not read %s", path); | |
241 | close(fd); | |
242 | free(buf); | |
2c5f2ede | 243 | return false; |
19712e04 CB |
244 | } |
245 | close(fd); | |
246 | ||
247 | while ((lxcpath = strtok_r(!counter ? buf : NULL, "\n", &scratch))) { | |
248 | if (!(lxcname = strtok_r(NULL, "\n", &scratch))) | |
249 | break; | |
250 | c1 = lxc_container_new(lxcname, lxcpath); | |
4f64d0db CB |
251 | if (!c1) { |
252 | counter++; | |
253 | continue; | |
254 | } | |
2c5f2ede CB |
255 | /* We do not destroy recursively. If a clone of a clone |
256 | * has clones or snapshots the user should remove it | |
257 | * explicitly. */ | |
258 | if (!do_destroy(c1)) { | |
19712e04 CB |
259 | lxc_container_put(c1); |
260 | free(buf); | |
2c5f2ede | 261 | return false; |
19712e04 CB |
262 | } |
263 | lxc_container_put(c1); | |
264 | counter++; | |
265 | } | |
266 | free(buf); | |
267 | } | |
268 | ||
269 | /* Destroy snapshots located in the containers snap/ folder. */ | |
270 | ret = snprintf(path, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name); | |
271 | if (ret < 0 || ret >= MAXPATHLEN) | |
2c5f2ede | 272 | return false; |
4f64d0db CB |
273 | |
274 | if (dir_exists(path)) | |
275 | bret = c->destroy_with_snapshots(c); | |
276 | else | |
2c5f2ede | 277 | bret = do_destroy(c); |
4f64d0db | 278 | |
2c5f2ede | 279 | return bret; |
f29bb5d5 CB |
280 | } |
281 |