1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering, Kay Sievers
8 systemd is free software; you can redistribute it and/or modify it
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
11 (at your option) any later version.
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
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
37 #include <sys/types.h>
38 #include <sys/param.h>
45 #include "path-util.h"
49 #include "conf-files.h"
51 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
52 * them in the file system. This is intended to be used to create
53 * properly owned directories beneath /tmp, /var/tmp, /run, which are
54 * volatile and hence need to be recreated on bootup. */
56 typedef enum ItemType
{
57 /* These ones take file names */
61 CREATE_DIRECTORY
= 'd',
62 TRUNCATE_DIRECTORY
= 'D',
65 CREATE_CHAR_DEVICE
= 'c',
66 CREATE_BLOCK_DEVICE
= 'b',
68 /* These ones take globs */
71 RECURSIVE_REMOVE_PATH
= 'R',
73 RECURSIVE_RELABEL_PATH
= 'Z'
94 static Hashmap
*items
= NULL
, *globs
= NULL
;
95 static Set
*unix_sockets
= NULL
;
97 static bool arg_create
= false;
98 static bool arg_clean
= false;
99 static bool arg_remove
= false;
101 static const char *arg_prefix
= NULL
;
103 static const char *conf_file_dirs
[] = {
106 "/usr/local/lib/tmpfiles.d",
107 "/usr/lib/tmpfiles.d",
111 #define MAX_DEPTH 256
113 static bool needs_glob(ItemType t
) {
114 return t
== IGNORE_PATH
|| t
== REMOVE_PATH
|| t
== RECURSIVE_REMOVE_PATH
|| t
== RELABEL_PATH
|| t
== RECURSIVE_RELABEL_PATH
;
117 static struct Item
* find_glob(Hashmap
*h
, const char *match
) {
121 HASHMAP_FOREACH(j
, h
, i
)
122 if (fnmatch(j
->path
, match
, FNM_PATHNAME
|FNM_PERIOD
) == 0)
128 static void load_unix_sockets(void) {
135 /* We maintain a cache of the sockets we found in
136 * /proc/net/unix to speed things up a little. */
138 unix_sockets
= set_new(string_hash_func
, string_compare_func
);
142 f
= fopen("/proc/net/unix", "re");
147 if (!fgets(line
, sizeof(line
), f
))
154 if (!fgets(line
, sizeof(line
), f
))
159 p
= strchr(line
, ':');
167 p
+= strspn(p
, WHITESPACE
);
168 p
+= strcspn(p
, WHITESPACE
); /* skip one more word */
169 p
+= strspn(p
, WHITESPACE
);
178 path_kill_slashes(s
);
180 k
= set_put(unix_sockets
, s
);
193 set_free_free(unix_sockets
);
200 static bool unix_socket_alive(const char *fn
) {
206 return !!set_get(unix_sockets
, (char*) fn
);
208 /* We don't know, so assume yes */
212 static int dir_cleanup(
215 const struct stat
*ds
,
222 struct timespec times
[2];
223 bool deleted
= false;
224 char *sub_path
= NULL
;
227 while ((dent
= readdir(d
))) {
231 if (streq(dent
->d_name
, ".") ||
232 streq(dent
->d_name
, ".."))
235 if (fstatat(dirfd(d
), dent
->d_name
, &s
, AT_SYMLINK_NOFOLLOW
) < 0) {
237 if (errno
!= ENOENT
) {
238 log_error("stat(%s/%s) failed: %m", p
, dent
->d_name
);
245 /* Stay on the same filesystem */
246 if (s
.st_dev
!= rootdev
)
249 /* Do not delete read-only files owned by root */
250 if (s
.st_uid
== 0 && !(s
.st_mode
& S_IWUSR
))
256 if (asprintf(&sub_path
, "%s/%s", p
, dent
->d_name
) < 0) {
257 log_error("Out of memory");
262 /* Is there an item configured for this path? */
263 if (hashmap_get(items
, sub_path
))
266 if (find_glob(globs
, sub_path
))
269 if (S_ISDIR(s
.st_mode
)) {
272 streq(dent
->d_name
, "lost+found") &&
277 log_warning("Reached max depth on %s.", sub_path
);
282 sub_dir
= xopendirat(dirfd(d
), dent
->d_name
, O_NOFOLLOW
|O_NOATIME
);
283 if (sub_dir
== NULL
) {
284 if (errno
!= ENOENT
) {
285 log_error("opendir(%s/%s) failed: %m", p
, dent
->d_name
);
292 q
= dir_cleanup(sub_path
, sub_dir
, &s
, cutoff
, rootdev
, false, maxdepth
-1);
299 /* Ignore ctime, we change it when deleting */
300 age
= MAX(timespec_load(&s
.st_mtim
),
301 timespec_load(&s
.st_atim
));
305 log_debug("rmdir '%s'\n", sub_path
);
307 if (unlinkat(dirfd(d
), dent
->d_name
, AT_REMOVEDIR
) < 0) {
308 if (errno
!= ENOENT
&& errno
!= ENOTEMPTY
) {
309 log_error("rmdir(%s): %m", sub_path
);
315 /* Skip files for which the sticky bit is
316 * set. These are semantics we define, and are
317 * unknown elsewhere. See XDG_RUNTIME_DIR
318 * specification for details. */
319 if (s
.st_mode
& S_ISVTX
)
322 if (mountpoint
&& S_ISREG(s
.st_mode
)) {
323 if (streq(dent
->d_name
, ".journal") &&
327 if (streq(dent
->d_name
, "aquota.user") ||
328 streq(dent
->d_name
, "aquota.group"))
332 /* Ignore sockets that are listed in /proc/net/unix */
333 if (S_ISSOCK(s
.st_mode
) && unix_socket_alive(sub_path
))
336 /* Ignore device nodes */
337 if (S_ISCHR(s
.st_mode
) || S_ISBLK(s
.st_mode
))
340 age
= MAX3(timespec_load(&s
.st_mtim
),
341 timespec_load(&s
.st_atim
),
342 timespec_load(&s
.st_ctim
));
347 log_debug("unlink '%s'\n", sub_path
);
349 if (unlinkat(dirfd(d
), dent
->d_name
, 0) < 0) {
350 if (errno
!= ENOENT
) {
351 log_error("unlink(%s): %m", sub_path
);
362 /* Restore original directory timestamps */
363 times
[0] = ds
->st_atim
;
364 times
[1] = ds
->st_mtim
;
366 if (futimens(dirfd(d
), times
) < 0)
367 log_error("utimensat(%s): %m", p
);
375 static int clean_item(Item
*i
) {
384 if (i
->type
!= CREATE_DIRECTORY
&&
385 i
->type
!= TRUNCATE_DIRECTORY
&&
386 i
->type
!= IGNORE_PATH
)
389 if (!i
->age_set
|| i
->age
<= 0)
392 n
= now(CLOCK_REALTIME
);
398 d
= opendir(i
->path
);
403 log_error("Failed to open directory %s: %m", i
->path
);
407 if (fstat(dirfd(d
), &s
) < 0) {
408 log_error("stat(%s) failed: %m", i
->path
);
413 if (!S_ISDIR(s
.st_mode
)) {
414 log_error("%s is not a directory.", i
->path
);
419 if (fstatat(dirfd(d
), "..", &ps
, AT_SYMLINK_NOFOLLOW
) != 0) {
420 log_error("stat(%s/..) failed: %m", i
->path
);
425 mountpoint
= s
.st_dev
!= ps
.st_dev
||
426 (s
.st_dev
== ps
.st_dev
&& s
.st_ino
== ps
.st_ino
);
428 r
= dir_cleanup(i
->path
, d
, &s
, cutoff
, s
.st_dev
, mountpoint
, MAX_DEPTH
);
437 static int item_set_perms(Item
*i
, const char *path
) {
438 /* not using i->path directly because it may be a glob */
440 if (chmod(path
, i
->mode
) < 0) {
441 log_error("chmod(%s) failed: %m", path
);
445 if (i
->uid_set
|| i
->gid_set
)
447 i
->uid_set
? i
->uid
: (uid_t
) -1,
448 i
->gid_set
? i
->gid
: (gid_t
) -1) < 0) {
450 log_error("chown(%s) failed: %m", path
);
454 return label_fix(path
, false);
457 static int recursive_relabel_children(Item
*i
, const char *path
) {
461 /* This returns the first error we run into, but nevertheless
466 return errno
== ENOENT
? 0 : -errno
;
469 struct dirent buf
, *de
;
474 r
= readdir_r(d
, &buf
, &de
);
484 if (streq(de
->d_name
, ".") || streq(de
->d_name
, ".."))
487 if (asprintf(&entry_path
, "%s/%s", path
, de
->d_name
) < 0) {
493 if (de
->d_type
== DT_UNKNOWN
) {
496 if (lstat(entry_path
, &st
) < 0) {
497 if (ret
== 0 && errno
!= ENOENT
)
503 is_dir
= S_ISDIR(st
.st_mode
);
506 is_dir
= de
->d_type
== DT_DIR
;
508 r
= item_set_perms(i
, entry_path
);
510 if (ret
== 0 && r
!= -ENOENT
)
517 r
= recursive_relabel_children(i
, entry_path
);
518 if (r
< 0 && ret
== 0)
530 static int recursive_relabel(Item
*i
, const char *path
) {
534 r
= item_set_perms(i
, path
);
538 if (lstat(path
, &st
) < 0)
541 if (S_ISDIR(st
.st_mode
))
542 r
= recursive_relabel_children(i
, path
);
547 static int glob_item(Item
*i
, int (*action
)(Item
*, const char *)) {
555 if ((k
= glob(i
->path
, GLOB_NOSORT
|GLOB_BRACE
, NULL
, &g
)) != 0) {
557 if (k
!= GLOB_NOMATCH
) {
561 log_error("glob(%s) failed: %m", i
->path
);
566 STRV_FOREACH(fn
, g
.gl_pathv
)
567 if ((k
= action(i
, *fn
)) < 0)
574 static int create_item(Item
*i
) {
585 case RECURSIVE_REMOVE_PATH
:
593 flags
= i
->type
== CREATE_FILE
? O_CREAT
|O_APPEND
:
594 i
->type
== TRUNCATE_FILE
? O_CREAT
|O_TRUNC
: 0;
597 label_context_set(i
->path
, S_IFREG
);
598 fd
= open(i
->path
, flags
|O_NDELAY
|O_CLOEXEC
|O_WRONLY
|O_NOCTTY
|O_NOFOLLOW
, i
->mode
);
600 label_context_clear();
605 if (i
->type
== WRITE_FILE
&& errno
== ENOENT
)
608 log_error("Failed to create file %s: %m", i
->path
);
615 struct iovec iovec
[2];
616 static const char new_line
= '\n';
618 l
= strlen(i
->argument
);
621 iovec
[0].iov_base
= i
->argument
;
622 iovec
[0].iov_len
= l
;
624 iovec
[1].iov_base
= (void*) &new_line
;
625 iovec
[1].iov_len
= 1;
627 n
= writev(fd
, iovec
, 2);
629 /* It's OK if we don't write the trailing
630 * newline, hence we check for l, instead of
631 * l+1 here. Files in /sys often refuse
632 * writing of the trailing newline. */
633 if (n
< 0 || (size_t) n
< l
) {
634 log_error("Failed to write file %s: %s", i
->path
, n
< 0 ? strerror(-n
) : "Short write");
635 close_nointr_nofail(fd
);
636 return n
< 0 ? n
: -EIO
;
640 close_nointr_nofail(fd
);
642 if (stat(i
->path
, &st
) < 0) {
643 log_error("stat(%s) failed: %m", i
->path
);
647 if (!S_ISREG(st
.st_mode
)) {
648 log_error("%s is not a file.", i
->path
);
652 r
= item_set_perms(i
, i
->path
);
659 case TRUNCATE_DIRECTORY
:
660 case CREATE_DIRECTORY
:
663 mkdir_parents_label(i
->path
, 0755);
664 r
= mkdir(i
->path
, i
->mode
);
667 if (r
< 0 && errno
!= EEXIST
) {
668 log_error("Failed to create directory %s: %m", i
->path
);
672 if (stat(i
->path
, &st
) < 0) {
673 log_error("stat(%s) failed: %m", i
->path
);
677 if (!S_ISDIR(st
.st_mode
)) {
678 log_error("%s is not a directory.", i
->path
);
682 r
= item_set_perms(i
, i
->path
);
691 r
= mkfifo(i
->path
, i
->mode
);
694 if (r
< 0 && errno
!= EEXIST
) {
695 log_error("Failed to create fifo %s: %m", i
->path
);
699 if (stat(i
->path
, &st
) < 0) {
700 log_error("stat(%s) failed: %m", i
->path
);
704 if (!S_ISFIFO(st
.st_mode
)) {
705 log_error("%s is not a fifo.", i
->path
);
709 r
= item_set_perms(i
, i
->path
);
715 case CREATE_SYMLINK
: {
718 label_context_set(i
->path
, S_IFLNK
);
719 r
= symlink(i
->argument
, i
->path
);
721 label_context_clear();
724 if (r
< 0 && errno
!= EEXIST
) {
725 log_error("symlink(%s, %s) failed: %m", i
->argument
, i
->path
);
729 r
= readlink_malloc(i
->path
, &x
);
731 log_error("readlink(%s) failed: %s", i
->path
, strerror(-r
));
735 if (!streq(i
->argument
, x
)) {
737 log_error("%s is not the right symlinks.", i
->path
);
745 case CREATE_BLOCK_DEVICE
:
746 case CREATE_CHAR_DEVICE
: {
747 mode_t file_type
= (i
->type
== CREATE_BLOCK_DEVICE
? S_IFBLK
: S_IFCHR
);
750 label_context_set(i
->path
, file_type
);
751 r
= mknod(i
->path
, i
->mode
| file_type
, i
->major_minor
);
753 label_context_clear();
757 if (r
< 0 && errno
!= EEXIST
) {
758 log_error("Failed to create device node %s: %m", i
->path
);
762 if (stat(i
->path
, &st
) < 0) {
763 log_error("stat(%s) failed: %m", i
->path
);
767 if ((st
.st_mode
& S_IFMT
) != file_type
) {
768 log_error("%s is not a device node.", i
->path
);
772 r
= item_set_perms(i
, i
->path
);
781 r
= glob_item(i
, item_set_perms
);
786 case RECURSIVE_RELABEL_PATH
:
788 r
= glob_item(i
, recursive_relabel
);
793 log_debug("%s created successfully.", i
->path
);
798 static int remove_item_instance(Item
*i
, const char *instance
) {
807 case CREATE_DIRECTORY
:
810 case CREATE_BLOCK_DEVICE
:
811 case CREATE_CHAR_DEVICE
:
814 case RECURSIVE_RELABEL_PATH
:
819 if (remove(instance
) < 0 && errno
!= ENOENT
) {
820 log_error("remove(%s): %m", instance
);
826 case TRUNCATE_DIRECTORY
:
827 case RECURSIVE_REMOVE_PATH
:
828 r
= rm_rf(instance
, false, i
->type
== RECURSIVE_REMOVE_PATH
, false);
829 if (r
< 0 && r
!= -ENOENT
) {
830 log_error("rm_rf(%s): %s", instance
, strerror(-r
));
840 static int remove_item(Item
*i
) {
849 case CREATE_DIRECTORY
:
852 case CREATE_CHAR_DEVICE
:
853 case CREATE_BLOCK_DEVICE
:
856 case RECURSIVE_RELABEL_PATH
:
861 case TRUNCATE_DIRECTORY
:
862 case RECURSIVE_REMOVE_PATH
:
863 r
= glob_item(i
, remove_item_instance
);
870 static int process_item(Item
*i
) {
875 r
= arg_create
? create_item(i
) : 0;
876 q
= arg_remove
? remove_item(i
) : 0;
877 p
= arg_clean
? clean_item(i
) : 0;
888 static void item_free(Item
*i
) {
896 static bool item_equal(Item
*a
, Item
*b
) {
900 if (!streq_ptr(a
->path
, b
->path
))
903 if (a
->type
!= b
->type
)
906 if (a
->uid_set
!= b
->uid_set
||
907 (a
->uid_set
&& a
->uid
!= b
->uid
))
910 if (a
->gid_set
!= b
->gid_set
||
911 (a
->gid_set
&& a
->gid
!= b
->gid
))
914 if (a
->mode_set
!= b
->mode_set
||
915 (a
->mode_set
&& a
->mode
!= b
->mode
))
918 if (a
->age_set
!= b
->age_set
||
919 (a
->age_set
&& a
->age
!= b
->age
))
922 if ((a
->type
== CREATE_FILE
||
923 a
->type
== TRUNCATE_FILE
||
924 a
->type
== WRITE_FILE
||
925 a
->type
== CREATE_SYMLINK
) &&
926 !streq_ptr(a
->argument
, b
->argument
))
929 if ((a
->type
== CREATE_CHAR_DEVICE
||
930 a
->type
== CREATE_BLOCK_DEVICE
) &&
931 a
->major_minor
!= b
->major_minor
)
937 static int parse_line(const char *fname
, unsigned line
, const char *buffer
) {
939 char *mode
= NULL
, *user
= NULL
, *group
= NULL
, *age
= NULL
;
950 log_error("Out of memory");
969 log_error("[%s:%u] Syntax error.", fname
, line
);
975 n
+= strspn(buffer
+n
, WHITESPACE
);
976 if (buffer
[n
] != 0 && (buffer
[n
] != '-' || buffer
[n
+1] != 0)) {
977 i
->argument
= unquote(buffer
+n
, "\"");
979 log_error("Out of memory");
989 case CREATE_DIRECTORY
:
990 case TRUNCATE_DIRECTORY
:
994 case RECURSIVE_REMOVE_PATH
:
996 case RECURSIVE_RELABEL_PATH
:
1001 log_error("[%s:%u] Symlink file requires argument.", fname
, line
);
1009 log_error("[%s:%u] Write file requires argument.", fname
, line
);
1015 case CREATE_CHAR_DEVICE
:
1016 case CREATE_BLOCK_DEVICE
: {
1017 unsigned major
, minor
;
1020 log_error("[%s:%u] Device file requires argument.", fname
, line
);
1025 if (sscanf(i
->argument
, "%u:%u", &major
, &minor
) != 2) {
1026 log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname
, line
, i
->argument
);
1031 i
->major_minor
= makedev(major
, minor
);
1036 log_error("[%s:%u] Unknown file type '%c'.", fname
, line
, type
);
1043 if (!path_is_absolute(i
->path
)) {
1044 log_error("[%s:%u] Path '%s' not absolute.", fname
, line
, i
->path
);
1049 path_kill_slashes(i
->path
);
1051 if (arg_prefix
&& !path_startswith(i
->path
, arg_prefix
)) {
1056 if (user
&& !streq(user
, "-")) {
1057 const char *u
= user
;
1059 r
= get_user_creds(&u
, &i
->uid
, NULL
, NULL
);
1061 log_error("[%s:%u] Unknown user '%s'.", fname
, line
, user
);
1068 if (group
&& !streq(group
, "-")) {
1069 const char *g
= group
;
1071 r
= get_group_creds(&g
, &i
->gid
);
1073 log_error("[%s:%u] Unknown group '%s'.", fname
, line
, group
);
1080 if (mode
&& !streq(mode
, "-")) {
1083 if (sscanf(mode
, "%o", &m
) != 1) {
1084 log_error("[%s:%u] Invalid mode '%s'.", fname
, line
, mode
);
1093 i
->type
== CREATE_DIRECTORY
||
1094 i
->type
== TRUNCATE_DIRECTORY
? 0755 : 0644;
1096 if (age
&& !streq(age
, "-")) {
1097 if (parse_usec(age
, &i
->age
) < 0) {
1098 log_error("[%s:%u] Invalid age '%s'.", fname
, line
, age
);
1106 h
= needs_glob(i
->type
) ? globs
: items
;
1108 existing
= hashmap_get(h
, i
->path
);
1111 /* Two identical items are fine */
1112 if (!item_equal(existing
, i
))
1113 log_warning("Two or more conflicting lines for %s configured, ignoring.", i
->path
);
1119 r
= hashmap_put(h
, i
->path
, i
);
1121 log_error("Failed to insert item %s: %s", i
->path
, strerror(-r
));
1140 static int help(void) {
1142 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1143 "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
1144 " -h --help Show this help\n"
1145 " --create Create marked files/directories\n"
1146 " --clean Clean up marked directories\n"
1147 " --remove Remove marked files/directories\n"
1148 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
1149 program_invocation_short_name
);
1154 static int parse_argv(int argc
, char *argv
[]) {
1163 static const struct option options
[] = {
1164 { "help", no_argument
, NULL
, 'h' },
1165 { "create", no_argument
, NULL
, ARG_CREATE
},
1166 { "clean", no_argument
, NULL
, ARG_CLEAN
},
1167 { "remove", no_argument
, NULL
, ARG_REMOVE
},
1168 { "prefix", required_argument
, NULL
, ARG_PREFIX
},
1169 { NULL
, 0, NULL
, 0 }
1177 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0) {
1198 arg_prefix
= optarg
;
1205 log_error("Unknown option code %c", c
);
1210 if (!arg_clean
&& !arg_create
&& !arg_remove
) {
1211 log_error("You need to specify at least one of --clean, --create or --remove.");
1218 static int read_config_file(const char *fn
, bool ignore_enoent
) {
1225 f
= fopen(fn
, "re");
1228 if (ignore_enoent
&& errno
== ENOENT
)
1231 log_error("Failed to open %s: %m", fn
);
1235 log_debug("apply: %s\n", fn
);
1237 char line
[LINE_MAX
], *l
;
1240 if (!(fgets(line
, sizeof(line
), f
)))
1246 if (*l
== '#' || *l
== 0)
1249 if ((k
= parse_line(fn
, v
, l
)) < 0)
1255 log_error("Failed to read from file %s: %m", fn
);
1265 static char *resolve_fragment(const char *fragment
, const char **search_paths
) {
1267 char *resolved_path
;
1269 if (is_path(fragment
))
1270 return strdup(fragment
);
1272 STRV_FOREACH(p
, search_paths
) {
1273 resolved_path
= join(*p
, "/", fragment
, NULL
);
1274 if (resolved_path
== NULL
) {
1275 log_error("Out of memory");
1279 if (access(resolved_path
, F_OK
) == 0)
1280 return resolved_path
;
1282 free(resolved_path
);
1289 int main(int argc
, char *argv
[]) {
1294 r
= parse_argv(argc
, argv
);
1296 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;
1298 log_set_target(LOG_TARGET_AUTO
);
1299 log_parse_environment();
1306 items
= hashmap_new(string_hash_func
, string_compare_func
);
1307 globs
= hashmap_new(string_hash_func
, string_compare_func
);
1309 if (!items
|| !globs
) {
1310 log_error("Out of memory");
1317 if (optind
< argc
) {
1320 for (j
= optind
; j
< argc
; j
++) {
1323 fragment
= resolve_fragment(argv
[j
], conf_file_dirs
);
1325 log_error("Failed to find a %s file: %m", argv
[j
]);
1329 if (read_config_file(fragment
, false) < 0)
1337 r
= conf_files_list_strv(&files
, ".conf",
1338 (const char **)conf_file_dirs
);
1340 log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r
));
1345 STRV_FOREACH(f
, files
) {
1346 if (read_config_file(*f
, true) < 0)
1353 HASHMAP_FOREACH(i
, globs
, iterator
)
1356 HASHMAP_FOREACH(i
, items
, iterator
)
1360 while ((i
= hashmap_steal_first(items
)))
1363 while ((i
= hashmap_steal_first(globs
)))
1366 hashmap_free(items
);
1367 hashmap_free(globs
);
1369 set_free_free(unix_sockets
);