]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/namespace.c
systemctl: by default interactively ask for polkit authorization, if possible
[thirdparty/systemd.git] / src / core / namespace.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
15ae422b
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
15ae422b
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
15ae422b 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
15ae422b
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <sys/mount.h>
24#include <string.h>
25#include <stdio.h>
26#include <unistd.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29#include <sched.h>
30#include <sys/syscall.h>
31#include <limits.h>
25e870b5 32#include <linux/fs.h>
613b411c 33#include <sys/file.h>
15ae422b
LP
34
35#include "strv.h"
36#include "util.h"
9eb977db 37#include "path-util.h"
15ae422b
LP
38#include "namespace.h"
39#include "missing.h"
c17ec25e 40#include "execute.h"
613b411c 41#include "loopback-setup.h"
7f112f50
LP
42#include "mkdir.h"
43#include "dev-setup.h"
44#include "def.h"
dd078a1e 45#include "label.h"
15ae422b 46
c17ec25e 47typedef enum MountMode {
15ae422b
LP
48 /* This is ordered by priority! */
49 INACCESSIBLE,
50 READONLY,
ac0930c8
LP
51 PRIVATE_TMP,
52 PRIVATE_VAR_TMP,
7f112f50 53 PRIVATE_DEV,
a610cc4f 54 PRIVATE_BUS_ENDPOINT,
15ae422b 55 READWRITE
c17ec25e 56} MountMode;
15ae422b 57
c17ec25e 58typedef struct BindMount {
15ae422b 59 const char *path;
c17ec25e 60 MountMode mode;
ac0930c8 61 bool done;
ea92ae33 62 bool ignore;
c17ec25e 63} BindMount;
15ae422b 64
c17ec25e 65static int append_mounts(BindMount **p, char **strv, MountMode mode) {
15ae422b
LP
66 char **i;
67
613b411c
LP
68 assert(p);
69
15ae422b
LP
70 STRV_FOREACH(i, strv) {
71
ea92ae33 72 (*p)->ignore = false;
002b2268 73 (*p)->done = false;
ea92ae33 74
94828d2d 75 if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') {
ea92ae33
MW
76 (*p)->ignore = true;
77 (*i)++;
78 }
79
15ae422b
LP
80 if (!path_is_absolute(*i))
81 return -EINVAL;
82
83 (*p)->path = *i;
84 (*p)->mode = mode;
85 (*p)++;
86 }
87
88 return 0;
89}
90
c17ec25e
MS
91static int mount_path_compare(const void *a, const void *b) {
92 const BindMount *p = a, *q = b;
15ae422b
LP
93
94 if (path_equal(p->path, q->path)) {
95
96 /* If the paths are equal, check the mode */
97 if (p->mode < q->mode)
98 return -1;
99
100 if (p->mode > q->mode)
101 return 1;
102
103 return 0;
104 }
105
106 /* If the paths are not equal, then order prefixes first */
107 if (path_startswith(p->path, q->path))
108 return 1;
109
110 if (path_startswith(q->path, p->path))
111 return -1;
112
113 return 0;
114}
115
c17ec25e
MS
116static void drop_duplicates(BindMount *m, unsigned *n) {
117 BindMount *f, *t, *previous;
15ae422b 118
c17ec25e 119 assert(m);
15ae422b 120 assert(n);
15ae422b 121
c17ec25e 122 for (f = m, t = m, previous = NULL; f < m+*n; f++) {
15ae422b 123
ac0930c8 124 /* The first one wins */
15ae422b
LP
125 if (previous && path_equal(f->path, previous->path))
126 continue;
127
e2d7c1a0 128 *t = *f;
15ae422b 129
15ae422b
LP
130 previous = t;
131
132 t++;
133 }
134
c17ec25e 135 *n = t - m;
15ae422b
LP
136}
137
7f112f50
LP
138static int mount_dev(BindMount *m) {
139 static const char devnodes[] =
140 "/dev/null\0"
141 "/dev/zero\0"
142 "/dev/full\0"
143 "/dev/random\0"
144 "/dev/urandom\0"
145 "/dev/tty\0";
146
2b85f4e1 147 char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
e06b6479 148 const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devkdbus = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
7f112f50
LP
149 _cleanup_umask_ mode_t u;
150 int r;
151
152 assert(m);
153
154 u = umask(0000);
155
2b85f4e1
LP
156 if (!mkdtemp(temporary_mount))
157 return -errno;
158
159 dev = strappenda(temporary_mount, "/dev");
160 mkdir(dev, 0755);
161 if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) {
162 r = -errno;
163 goto fail;
164 }
165
166 devpts = strappenda(temporary_mount, "/dev/pts");
167 mkdir(devpts, 0755);
168 if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) {
169 r = -errno;
170 goto fail;
171 }
172
e06b6479
LP
173 devptmx = strappenda(temporary_mount, "/dev/ptmx");
174 symlink("pts/ptmx", devptmx);
175
2b85f4e1
LP
176 devshm = strappenda(temporary_mount, "/dev/shm");
177 mkdir(devshm, 01777);
178 r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL);
179 if (r < 0) {
180 r = -errno;
181 goto fail;
182 }
183
184 devmqueue = strappenda(temporary_mount, "/dev/mqueue");
185 mkdir(devmqueue, 0755);
186 mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
187
188 devkdbus = strappenda(temporary_mount, "/dev/kdbus");
189 mkdir(devkdbus, 0755);
190 mount("/dev/kdbus", devkdbus, NULL, MS_BIND, NULL);
191
192 devhugepages = strappenda(temporary_mount, "/dev/hugepages");
193 mkdir(devhugepages, 0755);
194 mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
195
82d25240
LP
196 devlog = strappenda(temporary_mount, "/dev/log");
197 symlink("/run/systemd/journal/dev-log", devlog);
198
7f112f50 199 NULSTR_FOREACH(d, devnodes) {
2b85f4e1
LP
200 _cleanup_free_ char *dn = NULL;
201 struct stat st;
202
203 r = stat(d, &st);
7f112f50 204 if (r < 0) {
2b85f4e1
LP
205
206 if (errno == ENOENT)
207 continue;
208
209 r = -errno;
210 goto fail;
7f112f50
LP
211 }
212
2b85f4e1
LP
213 if (!S_ISBLK(st.st_mode) &&
214 !S_ISCHR(st.st_mode)) {
215 r = -EINVAL;
216 goto fail;
217 }
218
219 if (st.st_rdev == 0)
220 continue;
221
222 dn = strappend(temporary_mount, d);
223 if (!dn) {
224 r = -ENOMEM;
225 goto fail;
226 }
227
dd078a1e 228 label_context_set(d, st.st_mode);
2b85f4e1 229 r = mknod(dn, st.st_mode, st.st_rdev);
dd078a1e
LP
230 label_context_clear();
231
2b85f4e1
LP
232 if (r < 0) {
233 r = -errno;
234 goto fail;
235 }
7f112f50
LP
236 }
237
2b85f4e1 238 dev_setup(temporary_mount);
7f112f50 239
2b85f4e1
LP
240 if (mount(dev, "/dev/", NULL, MS_MOVE, NULL) < 0) {
241 r = -errno;
242 goto fail;
243 }
7f112f50 244
2b85f4e1
LP
245 rmdir(dev);
246 rmdir(temporary_mount);
7f112f50 247
2b85f4e1 248 return 0;
7f112f50 249
2b85f4e1
LP
250fail:
251 if (devpts)
252 umount(devpts);
7f112f50 253
2b85f4e1
LP
254 if (devshm)
255 umount(devshm);
7f112f50 256
2b85f4e1
LP
257 if (devkdbus)
258 umount(devkdbus);
7f112f50 259
2b85f4e1
LP
260 if (devhugepages)
261 umount(devhugepages);
7f112f50 262
2b85f4e1
LP
263 if (devmqueue)
264 umount(devmqueue);
7f112f50 265
d267c5aa
ZJS
266 umount(dev);
267 rmdir(dev);
2b85f4e1 268 rmdir(temporary_mount);
7f112f50 269
2b85f4e1 270 return r;
7f112f50
LP
271}
272
a610cc4f
DM
273static int mount_kdbus(BindMount *m) {
274
275 char temporary_mount[] = "/tmp/kdbus-dev-XXXXXX";
276 _cleanup_free_ char *basepath = NULL;
277 _cleanup_umask_ mode_t u;
120d578e 278 char *busnode = NULL, *root;
a610cc4f
DM
279 struct stat st;
280 int r;
281
282 assert(m);
283
284 u = umask(0000);
285
286 if (!mkdtemp(temporary_mount)) {
287 log_error("Failed create temp dir: %m");
288 return -errno;
289 }
290
291 root = strappenda(temporary_mount, "/kdbus");
292 mkdir(root, 0755);
293 if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=777") < 0) {
294 r = -errno;
295 goto fail;
296 }
297
298 /* create a new /dev/null dev node copy so we have some fodder to
299 * bind-mount the custom endpoint over. */
300 if (stat("/dev/null", &st) < 0) {
301 log_error("Failed to stat /dev/null: %m");
302 r = -errno;
303 goto fail;
304 }
305
306 busnode = strappenda(root, "/bus");
307 if (mknod(busnode, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) {
308 log_error("mknod() for %s failed: %m", busnode);
309 r = -errno;
310 goto fail;
311 }
312
313 r = mount(m->path, busnode, "bind", MS_BIND, NULL);
314 if (r < 0) {
315 log_error("bind mount of %s failed: %m", m->path);
316 r = -errno;
317 goto fail;
318 }
319
320 basepath = dirname_malloc(m->path);
321 if (!basepath) {
322 r = -ENOMEM;
323 goto fail;
324 }
325
326 if (mount(root, basepath, NULL, MS_MOVE, NULL) < 0) {
327 log_error("bind mount of %s failed: %m", basepath);
328 r = -errno;
329 goto fail;
330 }
331
332 rmdir(temporary_mount);
333 return 0;
334
335fail:
336 if (busnode) {
337 umount(busnode);
338 unlink(busnode);
339 }
340
1775f1eb
ZJS
341 umount(root);
342 rmdir(root);
a610cc4f
DM
343 rmdir(temporary_mount);
344
345 return r;
346}
347
ac0930c8 348static int apply_mount(
c17ec25e 349 BindMount *m,
ac0930c8 350 const char *tmp_dir,
c17ec25e 351 const char *var_tmp_dir) {
ac0930c8 352
15ae422b 353 const char *what;
15ae422b 354 int r;
15ae422b 355
c17ec25e 356 assert(m);
15ae422b 357
c17ec25e 358 switch (m->mode) {
15ae422b
LP
359
360 case INACCESSIBLE:
6d313367
LP
361
362 /* First, get rid of everything that is below if there
363 * is anything... Then, overmount it with an
364 * inaccessible directory. */
365 umount_recursive(m->path, 0);
366
c17ec25e 367 what = "/run/systemd/inaccessible";
15ae422b
LP
368 break;
369
370 case READONLY:
15ae422b 371 case READWRITE:
d6797c92
LP
372 /* Nothing to mount here, we just later toggle the
373 * MS_RDONLY bit for the mount point */
374 return 0;
15ae422b 375
ac0930c8
LP
376 case PRIVATE_TMP:
377 what = tmp_dir;
378 break;
379
380 case PRIVATE_VAR_TMP:
381 what = var_tmp_dir;
15ae422b 382 break;
e364ad06 383
d6797c92
LP
384 case PRIVATE_DEV:
385 return mount_dev(m);
386
a610cc4f
DM
387 case PRIVATE_BUS_ENDPOINT:
388 return mount_kdbus(m);
389
e364ad06
LP
390 default:
391 assert_not_reached("Unknown mode");
15ae422b
LP
392 }
393
ac0930c8 394 assert(what);
15ae422b 395
c17ec25e 396 r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
ac0930c8 397 if (r >= 0)
c17ec25e 398 log_debug("Successfully mounted %s to %s", what, m->path);
ea92ae33 399 else if (m->ignore && errno == ENOENT)
d6797c92 400 return 0;
15ae422b 401
ac0930c8
LP
402 return r;
403}
15ae422b 404
c17ec25e 405static int make_read_only(BindMount *m) {
ac0930c8 406 int r;
15ae422b 407
c17ec25e 408 assert(m);
ac0930c8 409
d6797c92
LP
410 if (IN_SET(m->mode, INACCESSIBLE, READONLY))
411 r = bind_remount_recursive(m->path, true);
664064d6 412 else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV))
d6797c92
LP
413 r = bind_remount_recursive(m->path, false);
414 else
415 r = 0;
ac0930c8 416
d6797c92
LP
417 if (m->ignore && r == -ENOENT)
418 return 0;
ac0930c8 419
d6797c92 420 return r;
15ae422b
LP
421}
422
613b411c
LP
423int setup_namespace(
424 char** read_write_dirs,
425 char** read_only_dirs,
426 char** inaccessible_dirs,
427 char* tmp_dir,
428 char* var_tmp_dir,
a610cc4f 429 char* bus_endpoint_path,
7f112f50 430 bool private_dev,
1b8689f9
LP
431 ProtectHome protect_home,
432 ProtectSystem protect_system,
613b411c 433 unsigned mount_flags) {
15ae422b 434
7ff7394d 435 BindMount *m, *mounts = NULL;
613b411c 436 unsigned n;
c17ec25e 437 int r = 0;
15ae422b 438
613b411c 439 if (mount_flags == 0)
c17ec25e 440 mount_flags = MS_SHARED;
ac0930c8 441
d5a3f0ea
ZJS
442 if (unshare(CLONE_NEWNS) < 0)
443 return -errno;
15ae422b 444
a610cc4f 445 n = !!tmp_dir + !!var_tmp_dir + !!bus_endpoint_path +
613b411c
LP
446 strv_length(read_write_dirs) +
447 strv_length(read_only_dirs) +
7f112f50 448 strv_length(inaccessible_dirs) +
417116f2 449 private_dev +
c8835999 450 (protect_home != PROTECT_HOME_NO ? 3 : 0) +
051be1f7 451 (protect_system != PROTECT_SYSTEM_NO ? 2 : 0) +
1b8689f9 452 (protect_system == PROTECT_SYSTEM_FULL ? 1 : 0);
613b411c
LP
453
454 if (n > 0) {
002b2268 455 m = mounts = (BindMount *) alloca0(n * sizeof(BindMount));
613b411c
LP
456 r = append_mounts(&m, read_write_dirs, READWRITE);
457 if (r < 0)
458 return r;
459
460 r = append_mounts(&m, read_only_dirs, READONLY);
461 if (r < 0)
462 return r;
463
464 r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE);
465 if (r < 0)
7ff7394d
ZJS
466 return r;
467
613b411c 468 if (tmp_dir) {
7ff7394d
ZJS
469 m->path = "/tmp";
470 m->mode = PRIVATE_TMP;
471 m++;
613b411c 472 }
7ff7394d 473
613b411c 474 if (var_tmp_dir) {
7ff7394d
ZJS
475 m->path = "/var/tmp";
476 m->mode = PRIVATE_VAR_TMP;
477 m++;
478 }
ac0930c8 479
7f112f50
LP
480 if (private_dev) {
481 m->path = "/dev";
482 m->mode = PRIVATE_DEV;
483 m++;
484 }
485
a610cc4f
DM
486 if (bus_endpoint_path) {
487 m->path = bus_endpoint_path;
488 m->mode = PRIVATE_BUS_ENDPOINT;
489 m++;
490 }
491
1b8689f9 492 if (protect_home != PROTECT_HOME_NO) {
c8835999 493 r = append_mounts(&m, STRV_MAKE("-/home", "-/run/user", "-/root"), protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
417116f2
LP
494 if (r < 0)
495 return r;
496 }
497
1b8689f9 498 if (protect_system != PROTECT_SYSTEM_NO) {
051be1f7 499 r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL ? STRV_MAKE("/usr", "-/boot", "/etc") : STRV_MAKE("/usr", "-/boot"), READONLY);
417116f2
LP
500 if (r < 0)
501 return r;
502 }
503
7ff7394d 504 assert(mounts + n == m);
ac0930c8 505
7ff7394d
ZJS
506 qsort(mounts, n, sizeof(BindMount), mount_path_compare);
507 drop_duplicates(mounts, &n);
15ae422b
LP
508 }
509
c2c13f2d
LP
510 if (n > 0) {
511 /* Remount / as SLAVE so that nothing now mounted in the namespace
512 shows up in the parent */
513 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
514 return -errno;
515
516 for (m = mounts; m < mounts + n; ++m) {
517 r = apply_mount(m, tmp_dir, var_tmp_dir);
518 if (r < 0)
519 goto fail;
520 }
15ae422b 521
c2c13f2d
LP
522 for (m = mounts; m < mounts + n; ++m) {
523 r = make_read_only(m);
524 if (r < 0)
525 goto fail;
526 }
15ae422b
LP
527 }
528
c2c13f2d
LP
529 /* Remount / as the desired mode. Not that this will not
530 * reestablish propagation from our side to the host, since
531 * what's disconnected is disconnected. */
c17ec25e 532 if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
15ae422b 533 r = -errno;
613b411c 534 goto fail;
15ae422b
LP
535 }
536
15ae422b
LP
537 return 0;
538
613b411c 539fail:
c2c13f2d
LP
540 if (n > 0) {
541 for (m = mounts; m < mounts + n; ++m)
542 if (m->done)
543 umount2(m->path, MNT_DETACH);
544 }
613b411c
LP
545
546 return r;
547}
548
549static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
550 _cleanup_free_ char *x = NULL;
6b46ea73
LP
551 char bid[SD_ID128_STRING_MAX];
552 sd_id128_t boot_id;
553 int r;
613b411c
LP
554
555 assert(id);
556 assert(prefix);
557 assert(path);
558
6b46ea73
LP
559 /* We include the boot id in the directory so that after a
560 * reboot we can easily identify obsolete directories. */
561
562 r = sd_id128_get_boot(&boot_id);
563 if (r < 0)
564 return r;
565
566 x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX", NULL);
613b411c
LP
567 if (!x)
568 return -ENOMEM;
569
570 RUN_WITH_UMASK(0077)
571 if (!mkdtemp(x))
572 return -errno;
573
574 RUN_WITH_UMASK(0000) {
575 char *y;
576
577 y = strappenda(x, "/tmp");
578
579 if (mkdir(y, 0777 | S_ISVTX) < 0)
580 return -errno;
c17ec25e 581 }
15ae422b 582
613b411c
LP
583 *path = x;
584 x = NULL;
585
586 return 0;
587}
588
589int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
590 char *a, *b;
591 int r;
592
593 assert(id);
594 assert(tmp_dir);
595 assert(var_tmp_dir);
596
597 r = setup_one_tmp_dir(id, "/tmp", &a);
598 if (r < 0)
599 return r;
600
601 r = setup_one_tmp_dir(id, "/var/tmp", &b);
602 if (r < 0) {
603 char *t;
604
605 t = strappenda(a, "/tmp");
606 rmdir(t);
607 rmdir(a);
608
609 free(a);
610 return r;
611 }
612
613 *tmp_dir = a;
614 *var_tmp_dir = b;
615
616 return 0;
617}
618
619int setup_netns(int netns_storage_socket[2]) {
620 _cleanup_close_ int netns = -1;
621 union {
622 struct cmsghdr cmsghdr;
623 uint8_t buf[CMSG_SPACE(sizeof(int))];
624 } control = {};
625 struct msghdr mh = {
626 .msg_control = &control,
627 .msg_controllen = sizeof(control),
628 };
629 struct cmsghdr *cmsg;
630 int r;
631
632 assert(netns_storage_socket);
633 assert(netns_storage_socket[0] >= 0);
634 assert(netns_storage_socket[1] >= 0);
635
636 /* We use the passed socketpair as a storage buffer for our
76cd584b
LP
637 * namespace reference fd. Whatever process runs this first
638 * shall create a new namespace, all others should just join
639 * it. To serialize that we use a file lock on the socket
640 * pair.
613b411c
LP
641 *
642 * It's a bit crazy, but hey, works great! */
643
644 if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
645 return -errno;
646
647 if (recvmsg(netns_storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) < 0) {
648 if (errno != EAGAIN) {
649 r = -errno;
650 goto fail;
651 }
652
653 /* Nothing stored yet, so let's create a new namespace */
654
655 if (unshare(CLONE_NEWNET) < 0) {
656 r = -errno;
657 goto fail;
658 }
659
660 loopback_setup();
661
662 netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY);
663 if (netns < 0) {
664 r = -errno;
665 goto fail;
666 }
667
668 r = 1;
669 } else {
670 /* Yay, found something, so let's join the namespace */
671
672 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
673 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
674 assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
675 netns = *(int*) CMSG_DATA(cmsg);
676 }
677 }
678
679 if (setns(netns, CLONE_NEWNET) < 0) {
680 r = -errno;
681 goto fail;
682 }
683
684 r = 0;
685 }
686
687 cmsg = CMSG_FIRSTHDR(&mh);
688 cmsg->cmsg_level = SOL_SOCKET;
689 cmsg->cmsg_type = SCM_RIGHTS;
690 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
691 memcpy(CMSG_DATA(cmsg), &netns, sizeof(int));
692 mh.msg_controllen = cmsg->cmsg_len;
693
694 if (sendmsg(netns_storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL) < 0) {
695 r = -errno;
696 goto fail;
697 }
698
699fail:
700 lockf(netns_storage_socket[0], F_ULOCK, 0);
701
15ae422b
LP
702 return r;
703}
417116f2 704
1b8689f9
LP
705static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
706 [PROTECT_HOME_NO] = "no",
707 [PROTECT_HOME_YES] = "yes",
708 [PROTECT_HOME_READ_ONLY] = "read-only",
417116f2
LP
709};
710
1b8689f9
LP
711DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome);
712
713static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
714 [PROTECT_SYSTEM_NO] = "no",
715 [PROTECT_SYSTEM_YES] = "yes",
716 [PROTECT_SYSTEM_FULL] = "full",
717};
718
719DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);