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