1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
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/>.
22 #include <sys/prctl.h>
28 #include "bus-common-errors.h"
30 #include "socket-util.h"
34 #include "machine-pool.h"
35 #include "path-util.h"
36 #include "import-util.h"
38 typedef struct Transfer Transfer;
39 typedef struct Manager Manager;
41 typedef enum TransferType {
46 _TRANSFER_TYPE_INVALID = -1,
68 char log_message[LINE_MAX];
69 size_t log_message_size;
71 sd_event_source *pid_event_source;
72 sd_event_source *log_event_source;
75 unsigned progress_percent;
82 uint32_t current_transfer_id;
85 Hashmap *polkit_registry;
89 sd_event_source *notify_event_source;
92 #define TRANSFERS_MAX 64
94 static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
95 [TRANSFER_TAR] = "tar",
96 [TRANSFER_RAW] = "raw",
97 [TRANSFER_DKR] = "dkr",
100 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
102 static Transfer *transfer_unref(Transfer *t) {
107 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
109 sd_event_source_unref(t->pid_event_source);
110 sd_event_source_unref(t->log_event_source);
114 free(t->dkr_index_url);
115 free(t->object_path);
118 (void) kill_and_sigcont(t->pid, SIGKILL);
119 (void) wait_for_terminate(t->pid, NULL);
122 safe_close(t->log_fd);
128 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
130 static int transfer_new(Manager *m, Transfer **ret) {
131 _cleanup_(transfer_unrefp) Transfer *t = NULL;
138 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
141 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
145 t = new0(Transfer, 1);
149 t->type = _TRANSFER_TYPE_INVALID;
152 id = m->current_transfer_id + 1;
154 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
157 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
161 m->current_transfer_id = id;
172 static void transfer_send_log_line(Transfer *t, const char *line) {
173 int r, priority = LOG_INFO;
178 syslog_parse_priority(&line, &priority, true);
180 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
182 r = sd_bus_emit_signal(
185 "org.freedesktop.import1.Transfer",
191 log_error_errno(r, "Cannot emit message: %m");
194 static void transfer_send_logs(Transfer *t, bool flush) {
197 /* Try to send out all log messages, if we can. But if we
198 * can't we remove the messages from the buffer, but don't
201 while (t->log_message_size > 0) {
202 _cleanup_free_ char *n = NULL;
205 if (t->log_message_size >= sizeof(t->log_message))
206 e = t->log_message + sizeof(t->log_message);
210 a = memchr(t->log_message, 0, t->log_message_size);
211 b = memchr(t->log_message, '\n', t->log_message_size);
225 e = t->log_message + t->log_message_size;
228 n = strndup(t->log_message, e - t->log_message);
230 /* Skip over NUL and newlines */
231 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
234 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
235 t->log_message_size -= e - t->log_message;
245 transfer_send_log_line(t, n);
249 static int transfer_finalize(Transfer *t, bool success) {
254 transfer_send_logs(t, true);
256 r = sd_bus_emit_signal(
258 "/org/freedesktop/import1",
259 "org.freedesktop.import1.Manager",
265 t->n_canceled > 0 ? "canceled" : "failed");
268 log_error_errno(r, "Cannot emit message: %m");
274 static int transfer_cancel(Transfer *t) {
279 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
287 static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
288 Transfer *t = userdata;
289 bool success = false;
294 if (si->si_code == CLD_EXITED) {
295 if (si->si_status != 0)
296 log_error("Import process failed with exit code %i.", si->si_status);
298 log_debug("Import process succeeded.");
302 } else if (si->si_code == CLD_KILLED ||
303 si->si_code == CLD_DUMPED)
305 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
307 log_error("Import process failed due to unknown reason.");
311 return transfer_finalize(t, success);
314 static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
315 Transfer *t = userdata;
321 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
323 /* EOF/read error. We just close the pipe here, and
324 * close the watch, waiting for the SIGCHLD to arrive,
325 * before we do anything else. */
328 log_error_errno(errno, "Failed to read log message: %m");
330 t->log_event_source = sd_event_source_unref(t->log_event_source);
334 t->log_message_size += l;
336 transfer_send_logs(t, false);
341 static int transfer_start(Transfer *t) {
342 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
348 if (pipe2(pipefd, O_CLOEXEC) < 0)
355 const char *cmd[] = {
357 transfer_type_to_string(t->type),
359 NULL, /* verify argument */
360 NULL, /* maybe --force */
361 NULL, /* maybe --dkr-index-url */
362 NULL, /* the actual URL */
372 reset_all_signal_handlers();
374 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
376 pipefd[0] = safe_close(pipefd[0]);
378 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
379 log_error_errno(errno, "Failed to dup2() fd: %m");
383 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
384 log_error_errno(errno, "Failed to dup2() fd: %m");
388 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
389 pipefd[1] = safe_close(pipefd[1]);
391 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
393 log_error_errno(errno, "Failed to open /dev/null: %m");
397 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
398 log_error_errno(errno, "Failed to dup2() fd: %m");
402 if (null_fd != STDIN_FILENO)
405 fd_cloexec(STDIN_FILENO, false);
406 fd_cloexec(STDOUT_FILENO, false);
407 fd_cloexec(STDERR_FILENO, false);
409 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
410 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
412 cmd[k++] = import_verify_to_string(t->verify);
414 cmd[k++] = "--force";
416 if (t->dkr_index_url) {
417 cmd[k++] = "--dkr-index-url";
418 cmd[k++] = t->dkr_index_url;
421 cmd[k++] = t->remote;
426 execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
427 log_error_errno(errno, "Failed to execute import tool: %m");
431 pipefd[1] = safe_close(pipefd[1]);
432 t->log_fd = pipefd[0];
435 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
439 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
443 /* Make sure always process logging before SIGCHLD */
444 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
448 r = sd_bus_emit_signal(
450 "/org/freedesktop/import1",
451 "org.freedesktop.import1.Manager",
462 static Manager *manager_unref(Manager *m) {
468 sd_event_source_unref(m->notify_event_source);
469 safe_close(m->notify_fd);
471 while ((t = hashmap_first(m->transfers)))
474 hashmap_free(m->transfers);
476 bus_verify_polkit_async_registry_free(m->polkit_registry);
478 sd_bus_close(m->bus);
479 sd_bus_unref(m->bus);
480 sd_event_unref(m->event);
486 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
488 static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
490 char buf[NOTIFY_BUFFER_MAX+1];
491 struct iovec iovec = {
493 .iov_len = sizeof(buf)-1,
496 struct cmsghdr cmsghdr;
497 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
498 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
500 struct msghdr msghdr = {
503 .msg_control = &control,
504 .msg_controllen = sizeof(control),
506 struct ucred *ucred = NULL;
507 Manager *m = userdata;
508 struct cmsghdr *cmsg;
516 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
518 if (errno == EAGAIN || errno == EINTR)
524 cmsg_close_all(&msghdr);
526 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
527 if (cmsg->cmsg_level == SOL_SOCKET &&
528 cmsg->cmsg_type == SCM_CREDENTIALS &&
529 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
531 ucred = (struct ucred*) CMSG_DATA(cmsg);
535 if (msghdr.msg_flags & MSG_TRUNC) {
536 log_warning("Got overly long notification datagram, ignoring.");
540 if (!ucred || ucred->pid <= 0) {
541 log_warning("Got notification datagram lacking credential information, ignoring.");
545 HASHMAP_FOREACH(t, m->transfers, i)
546 if (ucred->pid == t->pid)
550 log_warning("Got notification datagram from unexpected peer, ignoring.");
556 p = startswith(buf, "X_IMPORT_PROGRESS=");
558 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
565 e = strchrnul(p, '\n');
568 r = safe_atou(p, &percent);
569 if (r < 0 || percent > 100) {
570 log_warning("Got invalid percent value, ignoring.");
574 t->progress_percent = percent;
576 log_debug("Got percentage from client: %u%%", percent);
580 static int manager_new(Manager **ret) {
581 _cleanup_(manager_unrefp) Manager *m = NULL;
582 static const union sockaddr_union sa = {
583 .un.sun_family = AF_UNIX,
584 .un.sun_path = "/run/systemd/import/notify",
586 static const int one = 1;
591 m = new0(Manager, 1);
595 r = sd_event_default(&m->event);
599 sd_event_set_watchdog(m->event, true);
601 r = sd_bus_default_system(&m->bus);
605 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
606 if (m->notify_fd < 0)
609 (void) mkdir_parents_label(sa.un.sun_path, 0755);
610 (void) unlink(sa.un.sun_path);
612 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
615 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
618 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
628 static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
634 assert(type < _TRANSFER_TYPE_MAX);
636 HASHMAP_FOREACH(t, m->transfers, i) {
638 if (t->type == type &&
639 streq_ptr(t->remote, remote) &&
640 streq_ptr(t->dkr_index_url, dkr_index_url))
647 static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
648 _cleanup_(transfer_unrefp) Transfer *t = NULL;
649 const char *remote, *local, *verify, *object;
650 Manager *m = userdata;
660 r = bus_verify_polkit_async(
663 "org.freedesktop.import1.pull",
671 return 1; /* Will call us back */
673 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
677 if (!http_url_is_valid(remote))
678 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
682 else if (!machine_name_is_valid(local))
683 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
686 v = IMPORT_VERIFY_SIGNATURE;
688 v = import_verify_from_string(verify);
690 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
692 r = setup_machine_directory(error);
696 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
698 if (manager_find(m, type, NULL, remote))
699 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
701 r = transfer_new(m, &t);
707 t->force_local = force;
709 t->remote = strdup(remote);
713 t->local = strdup(local);
717 r = transfer_start(t);
721 object = t->object_path;
725 return sd_bus_reply_method_return(msg, "uo", id, object);
728 static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
729 _cleanup_(transfer_unrefp) Transfer *t = NULL;
730 const char *index_url, *remote, *tag, *local, *verify, *object;
731 Manager *m = userdata;
740 r = bus_verify_polkit_async(
743 "org.freedesktop.import1.pull",
751 return 1; /* Will call us back */
753 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
757 if (isempty(index_url))
758 index_url = DEFAULT_DKR_INDEX_URL;
760 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
761 if (!http_url_is_valid(index_url))
762 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
764 if (!dkr_name_is_valid(remote))
765 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
769 else if (!dkr_tag_is_valid(tag))
770 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
774 else if (!machine_name_is_valid(local))
775 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
778 v = IMPORT_VERIFY_SIGNATURE;
780 v = import_verify_from_string(verify);
782 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
784 if (v != IMPORT_VERIFY_NO)
785 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
787 r = setup_machine_directory(error);
791 if (manager_find(m, TRANSFER_DKR, index_url, remote))
792 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
794 r = transfer_new(m, &t);
798 t->type = TRANSFER_DKR;
800 t->force_local = force;
802 t->dkr_index_url = strdup(index_url);
803 if (!t->dkr_index_url)
806 t->remote = strjoin(remote, ":", tag, NULL);
810 t->local = strdup(local);
814 r = transfer_start(t);
818 object = t->object_path;
822 return sd_bus_reply_method_return(msg, "uo", id, object);
825 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
826 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
827 Manager *m = userdata;
836 r = sd_bus_message_new_method_return(msg, &reply);
840 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
844 HASHMAP_FOREACH(t, m->transfers, i) {
846 r = sd_bus_message_append(
850 transfer_type_to_string(t->type),
853 (double) t->progress_percent / 100.0,
859 r = sd_bus_message_close_container(reply);
863 return sd_bus_send(bus, reply, NULL);
866 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
867 Transfer *t = userdata;
874 r = bus_verify_polkit_async(
877 "org.freedesktop.import1.pull",
880 &t->manager->polkit_registry,
885 return 1; /* Will call us back */
887 r = transfer_cancel(t);
891 return sd_bus_reply_method_return(msg, NULL);
894 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
895 Manager *m = userdata;
904 r = bus_verify_polkit_async(
907 "org.freedesktop.import1.pull",
915 return 1; /* Will call us back */
917 r = sd_bus_message_read(msg, "u", &id);
921 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
923 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
925 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
927 r = transfer_cancel(t);
931 return sd_bus_reply_method_return(msg, NULL);
934 static int property_get_progress(
937 const char *interface,
938 const char *property,
939 sd_bus_message *reply,
941 sd_bus_error *error) {
943 Transfer *t = userdata;
949 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
952 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
953 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
955 static const sd_bus_vtable transfer_vtable[] = {
956 SD_BUS_VTABLE_START(0),
957 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
958 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
959 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
960 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
961 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
962 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
963 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
964 SD_BUS_SIGNAL("LogMessage", "us", 0),
968 static const sd_bus_vtable manager_vtable[] = {
969 SD_BUS_VTABLE_START(0),
970 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
971 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
972 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
973 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
974 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
975 SD_BUS_SIGNAL("TransferNew", "uo", 0),
976 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
980 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
981 Manager *m = userdata;
993 p = startswith(path, "/org/freedesktop/import1/transfer/_");
997 r = safe_atou32(p, &id);
998 if (r < 0 || id == 0)
1001 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1009 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1010 _cleanup_strv_free_ char **l = NULL;
1011 Manager *m = userdata;
1016 l = new0(char*, hashmap_size(m->transfers) + 1);
1020 HASHMAP_FOREACH(t, m->transfers, i) {
1022 l[k] = strdup(t->object_path);
1035 static int manager_add_bus_objects(Manager *m) {
1040 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1042 return log_error_errno(r, "Failed to register object: %m");
1044 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1046 return log_error_errno(r, "Failed to register object: %m");
1048 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1050 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1052 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1054 return log_error_errno(r, "Failed to register name: %m");
1056 r = sd_bus_attach_event(m->bus, m->event, 0);
1058 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1063 static bool manager_check_idle(void *userdata) {
1064 Manager *m = userdata;
1066 return hashmap_isempty(m->transfers);
1069 static int manager_run(Manager *m) {
1072 return bus_event_loop_with_idle(
1075 "org.freedesktop.import1",
1081 int main(int argc, char *argv[]) {
1082 _cleanup_(manager_unrefp) Manager *m = NULL;
1085 log_set_target(LOG_TARGET_AUTO);
1086 log_parse_environment();
1092 log_error("This program takes no arguments.");
1097 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1099 r = manager_new(&m);
1101 log_error_errno(r, "Failed to allocate manager object: %m");
1105 r = manager_add_bus_objects(m);
1111 log_error_errno(r, "Failed to run event loop: %m");
1116 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;