diff --git a/socket/CMakeLists.txt b/socket/CMakeLists.txt index 68ff112..b5e1722 100644 --- a/socket/CMakeLists.txt +++ b/socket/CMakeLists.txt @@ -53,7 +53,7 @@ if (SC_BUILD_TEST) -Wl,--wrap=setsockopt,--wrap=listen,--wrap=fcntl,--wrap=socket -Wl,--wrap=inet_ntop,--wrap=send,--wrap=recv,--wrap=connect -Wl,--wrap=write,--wrap=read,--wrap=epoll_ctl,--wrap=getsockopt - -Wl,--wrap=epoll_wait,--wrap=getsockname) + -Wl,--wrap=epoll_wait,--wrap=getsockname,--wrap=sendmsg) endif () endif () diff --git a/socket/sc_sock.c b/socket/sc_sock.c index 9569aa5..2e0809d 100644 --- a/socket/sc_sock.c +++ b/socket/sc_sock.c @@ -104,9 +104,14 @@ int sc_sock_cleanup() return rc != 0 ? -1 : 0; } +int sc_sock_notify_systemd(const char *msg) +{ + return -1; +} #else #include + #include #include #include #include @@ -122,6 +127,51 @@ int sc_sock_cleanup() #define SC_EINPROGRESS EINPROGRESS #define SC_EINTR EINTR +int sc_sock_notify_systemd(const char *msg) +{ + assert(msg != NULL); + + int fd, rc; + const char *s; + struct sockaddr_un addr = {.sun_family = AF_UNIX}; + struct iovec iovec = {.iov_base = (char *) msg, .iov_len = strlen(msg)}; + struct msghdr msghdr = {.msg_name = &addr, + .msg_iov = &iovec, + .msg_iovlen = 1}; + + s = getenv("NOTIFY_SOCKET"); + if (!s) { + errno = EINVAL; + return -1; + } + + if ((s[0] != '@' && s[0] != '/') || s[1] == '\0') { + errno = EINVAL; + return -1; + } + + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd < 0) { + return -1; + } + + strncpy(addr.sun_path, s, sizeof(addr.sun_path) - 1); + if (addr.sun_path[0] == '@') { + addr.sun_path[0] = '\0'; + } + + msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(s); + if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) { + msghdr.msg_namelen = sizeof(struct sockaddr_un); + } + + rc = sendmsg(fd, &msghdr, MSG_NOSIGNAL); + + close(fd); + + return rc < 0 ? -1 : 0; +} + int sc_sock_startup() { return 0; @@ -748,7 +798,6 @@ void sc_sock_print(struct sc_sock *sock, char *buf, size_t len) snprintf(buf, len, "Local(%s), Remote(%s) ", l, r); } - const char *sc_sock_pipe_err(struct sc_sock_pipe *pipe) { return pipe->err; diff --git a/socket/sc_sock.h b/socket/sc_sock.h index 1e958f9..0c77d58 100644 --- a/socket/sc_sock.h +++ b/socket/sc_sock.h @@ -245,6 +245,19 @@ const char *sc_sock_remote_str(struct sc_sock *sock, char *buf, size_t len); void sc_sock_print(struct sc_sock *sock, char *buf, size_t len); +/** + * Linux only. Helper function make your application a daemon with systemd. + * e.g + * sc_sock_notify_systemd("READY=1\n"); // Tell systemd app started + * sc_sock_notify_systemd("STATUS=doing work\n"); // Tell systemd app doing sth + * sc_sock_notify_systemd("STOPPING=1\n") ; // Tell systemd app will stop + * + * @param msg msg with systemd protocol format + * @return '0' on success, negative on error, errno will be set. + */ +int sc_sock_notify_systemd(const char *msg); + + struct sc_sock_pipe { struct sc_sock_fd fdt; diff --git a/socket/sock_test.c b/socket/sock_test.c index 669fec1..1e27d3e 100644 --- a/socket/sock_test.c +++ b/socket/sock_test.c @@ -732,6 +732,17 @@ int __wrap_connect(int fd, const struct sockaddr *addr, socklen_t len) return __real_connect(fd, addr, len); } +int success_sendmsg = 0; +ssize_t __real_sendmsg(int fd, const struct msghdr *message, int flags); +ssize_t __wrap_sendmsg(int fd, const struct msghdr *message, int flags) +{ + if (success_sendmsg) { + return 0; + } + + return __real_sendmsg(fd, message, flags); +} + int fail_send = INT32_MAX; int fail_send_err = 0; int fail_send_errno = 0; @@ -1337,6 +1348,29 @@ void sock_fail_test3() assert(sc_sock_finish_connect(&client) != 0); fail_getsockopt = 0; sc_sock_term(&client); + + assert(sc_sock_notify_systemd("test") == -1); + setenv("NOTIFY_SOCKET", "x", 1); + assert(sc_sock_notify_systemd("test") == -1); + setenv("NOTIFY_SOCKET", "/", 1); + assert(sc_sock_notify_systemd("test") == -1); + setenv("NOTIFY_SOCKET", "@", 1); + assert(sc_sock_notify_systemd("test") == -1); + + setenv("NOTIFY_SOCKET", "/tmp/x", 1); + fail_socket = 1; + assert(sc_sock_notify_systemd("test") == -1); + fail_socket = 0; + setenv("NOTIFY_SOCKET", "@tmp/x", 1); + assert(sc_sock_notify_systemd("test") == -1); + setenv("NOTIFY_SOCKET", "@tmp/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 1); + assert(sc_sock_notify_systemd("test") == -1); + + success_sendmsg = 1; + setenv("NOTIFY_SOCKET", "/tmp/x", 1); + assert(sc_sock_notify_systemd("test") == 0); + success_sendmsg = 0; + unsetenv("NOTIFY_SOCKET"); } #else