Coverage Report

Created: 2025-12-27 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/systemd.c
Line
Count
Source
1
/* SPDX-License-Identifier: MIT-0 */
2
3
/* Implement the systemd notify protocol without external dependencies.
4
 * Supports both readiness notification on startup and on reloading,
5
 * according to the protocol defined at:
6
 * https://www.freedesktop.org/software/systemd/man/latest/sd_notify.html
7
 * This protocol is guaranteed to be stable as per:
8
 * https://systemd.io/PORTABILITY_AND_STABILITY/
9
 *
10
 */
11
12
#include <errno.h>
13
#include <fcntl.h>
14
#include <inttypes.h>
15
#include <signal.h>
16
#include <stdbool.h>
17
#include <stddef.h>
18
#include <stdlib.h>
19
#include <stdio.h>
20
#include <sys/socket.h>
21
#include <sys/un.h>
22
#include <time.h>
23
#include <unistd.h>
24
#include <stdarg.h>
25
26
#include <haproxy/tools.h>
27
28
/*
29
 *  standalone reimplementation of sd_notify from the libsystemd
30
 *  Return:
31
 *     -errno in case of error
32
 *     0 when ignored
33
 *     >0 when succeeded
34
 *
35
 * Will send <message> over the NOTIFY_SOCKET.
36
 * When unset_environement is set, unsetenv NOTIFY_SOCKET.
37
 */
38
int sd_notify(int unset_environment, const char *message)
39
0
{
40
0
  union sockaddr_union {
41
0
    struct sockaddr sa;
42
0
    struct sockaddr_un ux;
43
0
  } socket_addr = {
44
0
    .ux.sun_family = AF_UNIX,
45
0
  };
46
0
  int ret = 1;
47
0
  int fd = -1;
48
0
  size_t path_length, message_length;
49
0
  const char *socket_path;
50
0
  ssize_t written;
51
52
0
  socket_path = getenv("NOTIFY_SOCKET");
53
0
  if (!socket_path) {
54
0
    ret = 0; /* Not running under systemd? Nothing to do */
55
0
    goto end;
56
0
  }
57
58
0
  if (unset_environment)
59
0
    unsetenv("NOTIFY_SOCKET");
60
61
0
  if (!message) {
62
0
    ret = -EINVAL;
63
0
    goto end;
64
0
  }
65
66
0
  message_length = strlen(message);
67
0
  if (message_length == 0) {
68
0
    ret = -EINVAL;
69
0
    goto end;
70
0
  }
71
72
  /* Only AF_UNIX is supported, with path or abstract sockets */
73
0
  if (socket_path[0] != '/' && socket_path[0] != '@') {
74
0
    ret = -EAFNOSUPPORT;
75
0
    goto end;
76
0
  }
77
78
0
  path_length = strlen(socket_path);
79
  /* Ensure there is room for NUL byte */
80
0
  if (path_length >= sizeof(socket_addr.ux.sun_path)) {
81
0
    ret = -E2BIG;
82
0
    goto end;
83
0
  }
84
85
0
  memcpy(socket_addr.ux.sun_path, socket_path, path_length);
86
87
  /* Support for abstract socket */
88
0
  if (socket_addr.ux.sun_path[0] == '@')
89
0
    socket_addr.ux.sun_path[0] = 0;
90
91
0
  fd = socket(AF_UNIX, SOCK_DGRAM, 0);
92
0
  if (fd < 0) {
93
0
    ret = -errno;
94
0
    goto end;
95
0
  }
96
97
0
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
98
0
    ret = -errno;
99
0
    goto end;
100
0
  }
101
102
0
  if (connect(fd, &socket_addr.sa, offsetof(struct sockaddr_un, sun_path) + path_length) != 0) {
103
0
    ret = -errno;
104
0
    goto end;
105
0
  }
106
107
0
  written = write(fd, message, message_length);
108
0
  if (written != (ssize_t) message_length) {
109
0
    ret = written < 0 ? -errno : -EPROTO;
110
0
    goto end;
111
0
  }
112
113
0
end:
114
0
  if (fd > -1)
115
0
    close(fd);
116
0
  return ret; /* Notified! */
117
0
}
118
119
/* va_args variant of sd_notify */
120
int sd_notifyf(int unset_environment, const char *format, ...)
121
0
{
122
0
  int r;
123
0
  va_list args;
124
0
  char *strp = NULL;
125
126
0
  va_start(args, format);
127
0
  strp = memvprintf(&strp, format, args);
128
0
  va_end(args);
129
130
0
  if (strp == NULL) {
131
0
    r = -ENOMEM;
132
0
    goto end;
133
0
  }
134
135
0
  r = sd_notify(unset_environment, strp);
136
0
  free(strp);
137
0
end:
138
0
  return r;
139
0
}
140