chiark / gitweb /
socket: add new Symlinks= option for socket units
[elogind.git] / src / core / socket.c
index 7eac0eb66de22a7489dd76dc9a05fefde774581d..c158aaf1de4204f9d59f1ae056e0fb7933e11597 100644 (file)
@@ -29,9 +29,7 @@
 #include <arpa/inet.h>
 #include <netinet/tcp.h>
 #include <mqueue.h>
-#ifdef HAVE_XATTR
-#include <attr/xattr.h>
-#endif
+#include <sys/xattr.h>
 
 #include "sd-event.h"
 #include "log.h"
@@ -90,11 +88,8 @@ static void socket_init(Unit *u) {
         s->ip_ttl = -1;
         s->mark = -1;
 
-        exec_context_init(&s->exec_context);
         s->exec_context.std_output = u->manager->default_std_output;
         s->exec_context.std_error = u->manager->default_std_error;
-        kill_context_init(&s->kill_context);
-        cgroup_context_init(&s->cgroup_context);
 
         s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
 }
@@ -119,9 +114,7 @@ void socket_free_ports(Socket *s) {
 
                 sd_event_source_unref(p->event_source);
 
-                if (p->fd >= 0)
-                        close_nointr_nofail(p->fd);
-
+                safe_close(p->fd);
                 free(p->path);
                 free(p);
         }
@@ -134,8 +127,6 @@ static void socket_done(Unit *u) {
 
         socket_free_ports(s);
 
-        cgroup_context_done(&s->cgroup_context);
-        exec_context_done(&s->exec_context);
         s->exec_runtime = exec_runtime_unref(s->exec_runtime);
         exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
         s->control_command = NULL;
@@ -154,6 +145,8 @@ static void socket_done(Unit *u) {
         free(s->smack_ip_in);
         free(s->smack_ip_out);
 
+        strv_free(s->symlinks);
+
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
 }
 
@@ -175,7 +168,12 @@ static int socket_arm_timer(Socket *s) {
                 return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
         }
 
-        return sd_event_add_monotonic(UNIT(s)->manager->event, now(CLOCK_MONOTONIC) + s->timeout_usec, 0, socket_dispatch_timer, s, &s->timer_event_source);
+        return sd_event_add_time(
+                        UNIT(s)->manager->event,
+                        &s->timer_event_source,
+                        CLOCK_MONOTONIC,
+                        now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
+                        socket_dispatch_timer, s);
 }
 
 static int socket_instantiate_service(Socket *s) {
@@ -196,16 +194,14 @@ static int socket_instantiate_service(Socket *s) {
 
         assert(s->accept);
 
-        if (!(prefix = unit_name_to_prefix(UNIT(s)->id)))
+        prefix = unit_name_to_prefix(UNIT(s)->id);
+        if (!prefix)
                 return -ENOMEM;
 
-        r = asprintf(&name, "%s@%u.service", prefix, s->n_accepted);
-
-        if (r < 0)
+        if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0)
                 return -ENOMEM;
 
         r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u);
-
         if (r < 0)
                 return r;
 
@@ -338,7 +334,7 @@ static int socket_add_extras(Socket *s) {
         if (r < 0)
                 return r;
 
-        r = unit_exec_context_defaults(u, &s->exec_context);
+        r = unit_patch_contexts(u);
         if (r < 0)
                 return r;
 
@@ -347,7 +343,7 @@ static int socket_add_extras(Socket *s) {
                 if (r < 0)
                         return r;
 
-                r = unit_add_default_slice(u);
+                r = unit_add_default_slice(u, &s->cgroup_context);
                 if (r < 0)
                         return r;
         }
@@ -361,6 +357,39 @@ static int socket_add_extras(Socket *s) {
         return 0;
 }
 
+static const char *socket_find_symlink_target(Socket *s) {
+        const char *found = NULL;
+        SocketPort *p;
+
+        LIST_FOREACH(port, p, s->ports) {
+                const char *f = NULL;
+
+                switch (p->type) {
+
+                case SOCKET_FIFO:
+                        f = p->path;
+                        break;
+
+                case SOCKET_SOCKET:
+                        if (p->address.sockaddr.un.sun_path[0] != 0)
+                                f = p->address.sockaddr.un.sun_path;
+                        break;
+
+                default:
+                        break;
+                }
+
+                if (f) {
+                        if (found)
+                                return NULL;
+
+                        found = f;
+                }
+        }
+
+        return found;
+}
+
 static int socket_verify(Socket *s) {
         assert(s);
 
@@ -393,6 +422,11 @@ static int socket_verify(Socket *s) {
                 return -EINVAL;
         }
 
+        if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) {
+                log_error_unit(UNIT(s)->id, "%s has symlinks set but none or more than one node in the file system. Refusing.", UNIT(s)->id);
+                return -EINVAL;
+        }
+
         return 0;
 }
 
@@ -457,7 +491,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sBroadcast: %s\n"
                 "%sPassCredentials: %s\n"
                 "%sPassSecurity: %s\n"
-                "%sTCPCongestion: %s\n",
+                "%sTCPCongestion: %s\n"
+                "%sRemoveOnStop: %s\n",
                 prefix, socket_state_to_string(s->state),
                 prefix, socket_result_to_string(s->result),
                 prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only),
@@ -470,12 +505,13 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, yes_no(s->broadcast),
                 prefix, yes_no(s->pass_cred),
                 prefix, yes_no(s->pass_sec),
-                prefix, strna(s->tcp_congestion));
+                prefix, strna(s->tcp_congestion),
+                prefix, yes_no(s->remove_on_stop));
 
         if (s->control_pid > 0)
                 fprintf(f,
-                        "%sControl PID: %lu\n",
-                        prefix, (unsigned long) s->control_pid);
+                        "%sControl PID: "PID_FMT"\n",
+                        prefix, s->control_pid);
 
         if (s->bind_to_device)
                 fprintf(f,
@@ -595,13 +631,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
 static int instance_from_socket(int fd, unsigned nr, char **instance) {
         socklen_t l;
         char *r;
-        union {
-                struct sockaddr sa;
-                struct sockaddr_un un;
-                struct sockaddr_in in;
-                struct sockaddr_in6 in6;
-                struct sockaddr_storage storage;
-        } local, remote;
+        union sockaddr_union local, remote;
 
         assert(fd >= 0);
         assert(instance);
@@ -673,16 +703,22 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
                 int k;
 
                 k = getpeercred(fd, &ucred);
-                if (k < 0)
+                if (k >= 0) {
+                        if (asprintf(&r,
+                                     "%u-"PID_FMT"-"UID_FMT,
+                                     nr, ucred.pid, ucred.uid) < 0)
+                                return -ENOMEM;
+                } else if (k == -ENODATA) {
+                        /* This handles the case where somebody is
+                         * connecting from another pid/uid namespace
+                         * (e.g. from outside of our container). */
+                        if (asprintf(&r,
+                                     "%u-unknown",
+                                     nr) < 0)
+                                return -ENOMEM;
+                } else
                         return k;
 
-                if (asprintf(&r,
-                             "%u-%lu-%lu",
-                             nr,
-                             (unsigned long) ucred.pid,
-                             (unsigned long) ucred.uid) < 0)
-                        return -ENOMEM;
-
                 break;
         }
 
@@ -696,6 +732,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
 
 static void socket_close_fds(Socket *s) {
         SocketPort *p;
+        char **i;
 
         assert(s);
 
@@ -706,18 +743,40 @@ static void socket_close_fds(Socket *s) {
                 if (p->fd < 0)
                         continue;
 
-                close_nointr_nofail(p->fd);
+                p->fd = safe_close(p->fd);
 
-                /* One little note: we should never delete any sockets
-                 * in the file system here! After all some other
-                 * process we spawned might still have a reference of
-                 * this fd and wants to continue to use it. Therefore
-                 * we delete sockets in the file system before we
-                 * create a new one, not after we stopped using
-                 * one! */
+                /* One little note: we should normally not delete any
+                 * sockets in the file system here! After all some
+                 * other process we spawned might still have a
+                 * reference of this fd and wants to continue to use
+                 * it. Therefore we delete sockets in the file system
+                 * before we create a new one, not after we stopped
+                 * using one! */
 
-                p->fd = -1;
+                if (s->remove_on_stop) {
+                        switch (p->type) {
+
+                        case SOCKET_FIFO:
+                                unlink(p->path);
+                                break;
+
+                        case SOCKET_MQUEUE:
+                                mq_unlink(p->path);
+                                break;
+
+                        case SOCKET_SOCKET:
+                                socket_address_unlink(&p->address);
+                                break;
+
+                        default:
+                                break;
+                        }
+                }
         }
+
+        if (s->remove_on_stop)
+                STRV_FOREACH(i, s->symlinks)
+                        unlink(*i);
 }
 
 static void socket_apply_socket_options(Socket *s, int fd) {
@@ -886,9 +945,7 @@ static int fifo_address_create(
 
 fail:
         label_context_clear();
-
-        if (fd >= 0)
-                close_nointr_nofail(fd);
+        safe_close(fd);
 
         return r;
 }
@@ -903,7 +960,8 @@ static int special_address_create(
         assert(path);
         assert(_fd);
 
-        if ((fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
+        fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
+        if (fd < 0) {
                 r = -errno;
                 goto fail;
         }
@@ -923,8 +981,7 @@ static int special_address_create(
         return 0;
 
 fail:
-        if (fd >= 0)
-                close_nointr_nofail(fd);
+        safe_close(fd);
 
         return r;
 }
@@ -957,7 +1014,6 @@ static int mq_address_create(
 
         /* Include the original umask in our mask */
         umask(~mq_mode | old_mask);
-
         fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr);
         umask(old_mask);
 
@@ -983,12 +1039,26 @@ static int mq_address_create(
         return 0;
 
 fail:
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-
+        safe_close(fd);
         return r;
 }
 
+static int socket_symlink(Socket *s) {
+        const char *p;
+        char **i;
+
+        assert(s);
+
+        p = socket_find_symlink_target(s);
+        if (!p)
+                return 0;
+
+        STRV_FOREACH(i, s->symlinks)
+                symlink(p, *i);
+
+        return 0;
+}
+
 static int socket_open_fds(Socket *s) {
         SocketPort *p;
         int r;
@@ -1006,17 +1076,15 @@ static int socket_open_fds(Socket *s) {
 
                         if (!know_label) {
 
-                                if ((r = socket_instantiate_service(s)) < 0)
+                                r = socket_instantiate_service(s);
+                                if (r < 0)
                                         return r;
 
                                 if (UNIT_ISSET(s->service) &&
                                     SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) {
                                         r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label);
-
-                                        if (r < 0) {
-                                                if (r != -EPERM)
-                                                        return r;
-                                        }
+                                        if (r < 0 && r != -EPERM)
+                                                return r;
                                 }
 
                                 know_label = true;
@@ -1038,6 +1106,7 @@ static int socket_open_fds(Socket *s) {
 
                         p->fd = r;
                         socket_apply_socket_options(s, p->fd);
+                        socket_symlink(s);
 
                 } else  if (p->type == SOCKET_SPECIAL) {
 
@@ -1058,6 +1127,8 @@ static int socket_open_fds(Socket *s) {
                                 goto rollback;
 
                         socket_apply_fifo_options(s, p->fd);
+                        socket_symlink(s);
+
                 } else if (p->type == SOCKET_MQUEUE) {
 
                         r = mq_address_create(
@@ -1112,7 +1183,7 @@ static int socket_watch_fds(Socket *s) {
                 if (p->event_source)
                         r = sd_event_source_set_enabled(p->event_source, SD_EVENT_ON);
                 else
-                        r = sd_event_add_io(UNIT(s)->manager->event, p->fd, EPOLLIN, socket_dispatch_io, p, &p->event_source);
+                        r = sd_event_add_io(UNIT(s)->manager->event, &p->event_source, p->fd, EPOLLIN, socket_dispatch_io, p);
 
                 if (r < 0) {
                         log_warning_unit(UNIT(s)->id, "Failed to watch listening fds: %s", strerror(-r));
@@ -1134,14 +1205,15 @@ static void socket_set_state(Socket *s, SocketState state) {
         old_state = s->state;
         s->state = state;
 
-        if (state != SOCKET_START_PRE &&
-            state != SOCKET_START_POST &&
-            state != SOCKET_STOP_PRE &&
-            state != SOCKET_STOP_PRE_SIGTERM &&
-            state != SOCKET_STOP_PRE_SIGKILL &&
-            state != SOCKET_STOP_POST &&
-            state != SOCKET_FINAL_SIGTERM &&
-            state != SOCKET_FINAL_SIGKILL) {
+        if (!IN_SET(state,
+                    SOCKET_START_PRE,
+                    SOCKET_START_POST,
+                    SOCKET_STOP_PRE,
+                    SOCKET_STOP_PRE_SIGTERM,
+                    SOCKET_STOP_PRE_SIGKILL,
+                    SOCKET_STOP_POST,
+                    SOCKET_FINAL_SIGTERM,
+                    SOCKET_FINAL_SIGKILL)) {
 
                 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
                 socket_unwatch_control_pid(s);
@@ -1152,12 +1224,13 @@ static void socket_set_state(Socket *s, SocketState state) {
         if (state != SOCKET_LISTENING)
                 socket_unwatch_fds(s);
 
-        if (state != SOCKET_START_POST &&
-            state != SOCKET_LISTENING &&
-            state != SOCKET_RUNNING &&
-            state != SOCKET_STOP_PRE &&
-            state != SOCKET_STOP_PRE_SIGTERM &&
-            state != SOCKET_STOP_PRE_SIGKILL)
+        if (!IN_SET(state,
+                    SOCKET_START_POST,
+                    SOCKET_LISTENING,
+                    SOCKET_RUNNING,
+                    SOCKET_STOP_PRE,
+                    SOCKET_STOP_PRE_SIGTERM,
+                    SOCKET_STOP_PRE_SIGKILL))
                 socket_close_fds(s);
 
         if (state != old_state)
@@ -1253,6 +1326,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
                        UNIT(s)->manager->confirm_spawn,
                        UNIT(s)->manager->cgroup_supported,
                        UNIT(s)->cgroup_path,
+                       manager_get_runtime_prefix(UNIT(s)->manager),
                        UNIT(s)->id,
                        0,
                        NULL,
@@ -1287,6 +1361,8 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
         exec_runtime_destroy(s->exec_runtime);
         s->exec_runtime = exec_runtime_unref(s->exec_runtime);
 
+        exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
+
         socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
 }
 
@@ -1344,8 +1420,12 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
                         goto fail;
 
                 socket_set_state(s, state);
-        } else if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
+        } else if (state == SOCKET_STOP_PRE_SIGTERM)
+                socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_SUCCESS);
+        else if (state == SOCKET_STOP_PRE_SIGKILL)
                 socket_enter_stop_post(s, SOCKET_SUCCESS);
+        else if (state == SOCKET_FINAL_SIGTERM)
+                socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_SUCCESS);
         else
                 socket_enter_dead(s, SOCKET_SUCCESS);
 
@@ -1471,7 +1551,7 @@ static void socket_enter_running(Socket *s, int cfd) {
                 log_debug_unit(UNIT(s)->id, "Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id);
 
                 if (cfd >= 0)
-                        close_nointr_nofail(cfd);
+                        safe_close(cfd);
                 else  {
                         /* Flush all sockets by closing and reopening them */
                         socket_close_fds(s);
@@ -1507,6 +1587,12 @@ static void socket_enter_running(Socket *s, int cfd) {
                         }
 
                 if (!pending) {
+                        if (!UNIT_ISSET(s->service)) {
+                                log_error_unit(UNIT(s)->id, "%s: service to activate vanished, refusing activation.", UNIT(s)->id);
+                                r = -ENOENT;
+                                goto fail;
+                        }
+
                         r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL);
                         if (r < 0)
                                 goto fail;
@@ -1519,7 +1605,7 @@ static void socket_enter_running(Socket *s, int cfd) {
 
                 if (s->n_connections >= s->max_connections) {
                         log_warning_unit(UNIT(s)->id, "%s: Too many incoming connections (%u)", UNIT(s)->id, s->n_connections);
-                        close_nointr_nofail(cfd);
+                        safe_close(cfd);
                         return;
                 }
 
@@ -1534,7 +1620,7 @@ static void socket_enter_running(Socket *s, int cfd) {
 
                         /* ENOTCONN is legitimate if TCP RST was received.
                          * This connection is over, but the socket unit lives on. */
-                        close_nointr_nofail(cfd);
+                        safe_close(cfd);
                         return;
                 }
 
@@ -1585,9 +1671,7 @@ fail:
                          bus_error_message(&error, r));
 
         socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
-
-        if (cfd >= 0)
-                close_nointr_nofail(cfd);
+        safe_close(cfd);
 }
 
 static void socket_run_next(Socket *s) {
@@ -1716,7 +1800,7 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
         unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted);
 
         if (s->control_pid > 0)
-                unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
+                unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid);
 
         if (s->control_command_id >= 0)
                 unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id));
@@ -1818,8 +1902,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                                         break;
 
                         if (p) {
-                                if (p->fd >= 0)
-                                        close_nointr_nofail(p->fd);
+                                safe_close(p->fd);
                                 p->fd = fdset_remove(fds, fd);
                         }
                 }
@@ -1838,8 +1921,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                                         break;
 
                         if (p) {
-                                if (p->fd >= 0)
-                                        close_nointr_nofail(p->fd);
+                                safe_close(p->fd);
                                 p->fd = fdset_remove(fds, fd);
                         }
                 }
@@ -1858,8 +1940,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                                         break;
 
                         if (p) {
-                                if (p->fd >= 0)
-                                        close_nointr_nofail(p->fd);
+                                safe_close(p->fd);
                                 p->fd = fdset_remove(fds, fd);
                         }
                 }
@@ -1877,8 +1958,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                                         break;
 
                         if (p) {
-                                if (p->fd >= 0)
-                                        close_nointr_nofail(p->fd);
+                                safe_close(p->fd);
                                 p->fd = fdset_remove(fds, fd);
                         }
                 }
@@ -1896,8 +1976,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                                         break;
 
                         if (p) {
-                                if (p->fd >= 0)
-                                        close_nointr_nofail(p->fd);
+                                safe_close(p->fd);
                                 p->fd = fdset_remove(fds, fd);
                         }
                 }