]>
Commit | Line | Data |
---|---|---|
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 | 19 | lxc_log_define(lxc_destroy, lxc); |
20 | ||
21 | static int my_parser(struct lxc_arguments *args, int c, char *arg); | |
a5375956 | 22 | static bool do_destroy(struct lxc_container *c); |
23 | static bool do_destroy_with_snapshots(struct lxc_container *c); | |
60bf62d4 SH |
24 | |
25 | static 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 | ||
31 | static struct lxc_arguments my_args = { | |
a5375956 | 32 | .progname = "lxc-destroy", |
33 | .help = "\ | |
60bf62d4 SH |
34 | --name=NAME [-f] [-P lxcpath]\n\ |
35 | \n\ | |
4c1f6b67 | 36 | lxc-destroy destroys a container with the identifier NAME\n\ |
60bf62d4 SH |
37 | \n\ |
38 | Options :\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 |
51 | static 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 | 64 | static 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 | 117 | static 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 | |
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)) { | |
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 | } |