]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/tools/lxc_destroy.c
tree-wide: fix lxc header inclusion
[mirror_lxc.git] / src / lxc / tools / lxc_destroy.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: GPL-2.0-only */
60bf62d4 2
646b75b5
CB
3#include "config.h"
4
e3347eef 5#include <fcntl.h>
60bf62d4 6#include <libgen.h>
446ca810 7#include <stdio.h>
e3347eef 8#include <string.h>
e3347eef 9#include <sys/stat.h>
60bf62d4 10#include <sys/types.h>
d38dd64a 11#include <unistd.h>
60bf62d4 12
12ae2a33 13#include "lxc.h"
446ca810 14
60bf62d4 15#include "arguments.h"
75e607ba 16#include "log.h"
646b75b5 17#include "file_utils.h"
60bf62d4 18
75e607ba 19lxc_log_define(lxc_destroy, lxc);
20
21static int my_parser(struct lxc_arguments *args, int c, char *arg);
a5375956 22static bool do_destroy(struct lxc_container *c);
23static bool do_destroy_with_snapshots(struct lxc_container *c);
60bf62d4
SH
24
25static const struct option my_longopts[] = {
26 {"force", no_argument, 0, 'f'},
f29bb5d5 27 {"snapshots", no_argument, 0, 's'},
60bf62d4
SH
28 LXC_COMMON_OPTIONS
29};
30
31static struct lxc_arguments my_args = {
a5375956 32 .progname = "lxc-destroy",
33 .help = "\
60bf62d4
SH
34--name=NAME [-f] [-P lxcpath]\n\
35\n\
4c1f6b67 36lxc-destroy destroys a container with the identifier NAME\n\
60bf62d4
SH
37\n\
38Options :\n\
f29bb5d5
CB
39 -n, --name=NAME NAME of the container\n\
40 -s, --snapshots destroy including all snapshots\n\
50b737a3
WB
41 -f, --force wait for the container to shut down\n\
42 --rcfile=FILE Load configuration file FILE\n",
a5375956 43 .options = my_longopts,
44 .parser = my_parser,
45 .checker = NULL,
46 .log_priority = "ERROR",
47 .log_file = "none",
48 .task = DESTROY,
60bf62d4
SH
49};
50
f29bb5d5
CB
51static int my_parser(struct lxc_arguments *args, int c, char *arg)
52{
53 switch (c) {
a5375956 54 case 'f':
55 args->force = 1;
56 break;
57 case 's':
58 args->task = SNAP;
59 break;
f29bb5d5
CB
60 }
61 return 0;
62}
63
2c5f2ede 64static bool do_destroy(struct lxc_container *c)
f29bb5d5 65{
a5375956 66 int ret;
2c5f2ede 67 bool bret = true;
3a5996ff 68 char path[PATH_MAX];
2c5f2ede
CB
69
70 /* First check whether the container has dependent clones or snapshots. */
3a5996ff
CB
71 ret = snprintf(path, PATH_MAX, "%s/%s/lxc_snapshots", c->config_path, c->name);
72 if (ret < 0 || ret >= PATH_MAX)
2c5f2ede
CB
73 return false;
74
75 if (file_exists(path)) {
8f0bdb05 76 ERROR("Destroying %s failed: %s has clones", c->name, c->name);
2c5f2ede 77 return false;
7909bb03
CB
78 }
79
3a5996ff
CB
80 ret = snprintf(path, PATH_MAX, "%s/%s/snaps", c->config_path, c->name);
81 if (ret < 0 || ret >= PATH_MAX)
2c5f2ede 82 return false;
f29bb5d5 83
eb0760f9 84 if (rmdir(path) < 0 && errno != ENOENT) {
8f0bdb05 85 ERROR("Destroying %s failed: %s has snapshots", c->name, c->name);
2c5f2ede
CB
86 return false;
87 }
88
89 if (c->is_running(c)) {
75e607ba 90 if (!my_args.force) {
91 ERROR("%s is running", my_args.name);
2c5f2ede
CB
92 return false;
93 }
75e607ba 94
2c5f2ede
CB
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. */
e3347eef
CB
101 if (c->is_defined(c)) {
102 char buf[256];
75e607ba 103
e3347eef 104 ret = c->get_config_item(c, "lxc.ephemeral", buf, 256);
a5375956 105 if (ret > 0 && strcmp(buf, "0") == 0)
e3347eef 106 bret = c->destroy(c);
e3347eef 107 }
2c5f2ede
CB
108
109 if (!bret) {
75e607ba 110 ERROR("Destroying %s failed", my_args.name);
2c5f2ede
CB
111 return false;
112 }
113
114 return true;
7909bb03 115}
f29bb5d5 116
2c5f2ede 117static bool do_destroy_with_snapshots(struct lxc_container *c)
f29bb5d5 118{
19712e04
CB
119 struct lxc_container *c1;
120 struct stat fbuf;
4f64d0db 121 bool bret = false;
3a5996ff 122 char path[PATH_MAX];
19712e04
CB
123 char *buf = NULL;
124 char *lxcpath = NULL;
125 char *lxcname = NULL;
19712e04
CB
126 int fd;
127 int ret;
aeba3f80 128 ssize_t bytes;
19712e04 129
2c5f2ede 130 /* Destroy clones. */
3a5996ff
CB
131 ret = snprintf(path, PATH_MAX, "%s/%s/lxc_snapshots", c->config_path, c->name);
132 if (ret < 0 || ret >= PATH_MAX)
2c5f2ede 133 return false;
19712e04
CB
134
135 fd = open(path, O_RDONLY | O_CLOEXEC);
c01859e8 136 if (fd >= 0) {
19712e04
CB
137 ret = fstat(fd, &fbuf);
138 if (ret < 0) {
139 close(fd);
2c5f2ede 140 return false;
19712e04
CB
141 }
142
143 /* Make sure that the string is \0 terminated. */
144 buf = calloc(fbuf.st_size + 1, sizeof(char));
145 if (!buf) {
75e607ba 146 ERROR("Failed to allocate memory");
19712e04 147 close(fd);
2c5f2ede 148 return false;
19712e04
CB
149 }
150
aeba3f80 151 bytes = lxc_read_nointr(fd, buf, fbuf.st_size);
7b6f89cd 152 close(fd);
aeba3f80 153 if (bytes != (ssize_t)fbuf.st_size) {
75e607ba 154 ERROR("Could not read %s", path);
19712e04 155 free(buf);
2c5f2ede 156 return false;
19712e04 157 }
19712e04 158
7de8e0a9 159 lxc_iterate_parts(lxcpath, buf, "\n") {
19712e04 160 c1 = lxc_container_new(lxcname, lxcpath);
7de8e0a9 161 if (!c1)
4f64d0db 162 continue;
75e607ba 163
2c5f2ede
CB
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)) {
19712e04
CB
168 lxc_container_put(c1);
169 free(buf);
2c5f2ede 170 return false;
19712e04 171 }
75e607ba 172
19712e04 173 lxc_container_put(c1);
19712e04
CB
174 }
175 free(buf);
176 }
177
178 /* Destroy snapshots located in the containers snap/ folder. */
3a5996ff
CB
179 ret = snprintf(path, PATH_MAX, "%s/%s/snaps", c->config_path, c->name);
180 if (ret < 0 || ret >= PATH_MAX)
2c5f2ede 181 return false;
4f64d0db 182
eb0760f9 183 if (rmdir(path) < 0 && errno != ENOENT)
4f64d0db
CB
184 bret = c->destroy_with_snapshots(c);
185 else
2c5f2ede 186 bret = do_destroy(c);
4f64d0db 187
2c5f2ede 188 return bret;
f29bb5d5 189}
a5375956 190
191int 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)) {
a0e686fd 240 ERROR("Container is not defined");
a5375956 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)
65b92ea5 248 INFO("Destroyed container %s including snapshots", my_args.name);
a5375956 249 } else {
250 bret = do_destroy(c);
251 if (bret)
65b92ea5 252 INFO("Destroyed container %s", my_args.name);
a5375956 253 }
254
255 lxc_container_put(c);
256
257 if (bret)
258 exit(EXIT_SUCCESS);
259
260 exit(EXIT_FAILURE);
261}