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