]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/notify/notify.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / notify / notify.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
4a2a8b5a 2
4a2a8b5a 3#include <errno.h>
6f2deb84
LP
4#include <getopt.h>
5#include <stdio.h>
4a2a8b5a 6#include <stdlib.h>
6f2deb84 7#include <unistd.h>
4a2a8b5a 8
6f2deb84 9#include "sd-daemon.h"
81527be1 10
b5efdb8a 11#include "alloc-util.h"
4d1a6904 12#include "env-util.h"
f97b34a6 13#include "format-util.h"
6f2deb84 14#include "log.h"
5e332028 15#include "main-func.h"
6bedfcbb 16#include "parse-util.h"
294bf0c3 17#include "pretty-print.h"
07630cea 18#include "string-util.h"
6f2deb84 19#include "strv.h"
6f63c5b8 20#include "terminal-util.h"
4f07ddfa 21#include "time-util.h"
65c6b990 22#include "user-util.h"
6f2deb84 23#include "util.h"
4a2a8b5a
LP
24
25static bool arg_ready = false;
26static pid_t arg_pid = 0;
27static const char *arg_status = NULL;
96551bae 28static bool arg_booted = false;
65c6b990
LP
29static uid_t arg_uid = UID_INVALID;
30static gid_t arg_gid = GID_INVALID;
4f07ddfa 31static bool arg_no_block = false;
4a2a8b5a 32
37ec0fdd
LP
33static int help(void) {
34 _cleanup_free_ char *link = NULL;
35 int r;
36
37 r = terminal_urlify_man("systemd-notify", "1", &link);
38 if (r < 0)
39 return log_oom();
40
6f63c5b8
LP
41 printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n"
42 "\n%sNotify the init system about service status updates.%s\n\n"
b57b0625
ZJS
43 " -h --help Show this help\n"
44 " --version Show package version\n"
45 " --ready Inform the init system about service start-up completion\n"
65c6b990
LP
46 " --pid[=PID] Set main PID of daemon\n"
47 " --uid=USER Set user to send from\n"
b57b0625 48 " --status=TEXT Set status text\n"
37ec0fdd 49 " --booted Check if the system was booted up with systemd\n"
4f07ddfa 50 " --no-block Do not wait until operation finished\n"
37ec0fdd
LP
51 "\nSee the %s for details.\n"
52 , program_invocation_short_name
6f63c5b8 53 , ansi_highlight(), ansi_normal()
37ec0fdd
LP
54 , link
55 );
56
57 return 0;
4a2a8b5a
LP
58}
59
9dcd43b1
LP
60static pid_t manager_pid(void) {
61 const char *e;
62 pid_t pid;
63 int r;
64
65 /* If we run as a service managed by systemd --user the $MANAGERPID environment variable points to
66 * the service manager's PID. */
67 e = getenv("MANAGERPID");
68 if (!e)
69 return 0;
70
71 r = parse_pid(e, &pid);
72 if (r < 0) {
73 log_warning_errno(r, "$MANAGERPID is set to an invalid PID, ignoring: %s", e);
74 return 0;
75 }
76
77 return pid;
78}
79
4a2a8b5a
LP
80static int parse_argv(int argc, char *argv[]) {
81
82 enum {
83 ARG_READY = 0x100,
9aac0b2c 84 ARG_VERSION,
4a2a8b5a 85 ARG_PID,
96551bae 86 ARG_STATUS,
6624768c 87 ARG_BOOTED,
65c6b990 88 ARG_UID,
4f07ddfa 89 ARG_NO_BLOCK
4a2a8b5a
LP
90 };
91
92 static const struct option options[] = {
6624768c 93 { "help", no_argument, NULL, 'h' },
9aac0b2c 94 { "version", no_argument, NULL, ARG_VERSION },
6624768c
LP
95 { "ready", no_argument, NULL, ARG_READY },
96 { "pid", optional_argument, NULL, ARG_PID },
97 { "status", required_argument, NULL, ARG_STATUS },
98 { "booted", no_argument, NULL, ARG_BOOTED },
65c6b990 99 { "uid", required_argument, NULL, ARG_UID },
4f07ddfa 100 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
eb9da376 101 {}
4a2a8b5a
LP
102 };
103
65c6b990 104 int c, r;
4a2a8b5a
LP
105
106 assert(argc >= 0);
107 assert(argv);
108
ee8c4568 109 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
4a2a8b5a
LP
110
111 switch (c) {
112
113 case 'h':
37ec0fdd 114 return help();
4a2a8b5a 115
9aac0b2c 116 case ARG_VERSION:
3f6fd1ba 117 return version();
9aac0b2c 118
4a2a8b5a
LP
119 case ARG_READY:
120 arg_ready = true;
121 break;
122
123 case ARG_PID:
9dcd43b1
LP
124 if (isempty(optarg) || streq(optarg, "auto")) {
125 arg_pid = getppid();
4a2a8b5a 126
9dcd43b1
LP
127 if (arg_pid <= 1 ||
128 arg_pid == manager_pid()) /* Don't send from PID 1 or the service
129 * manager's PID (which might be distinct from
130 * 1, if we are a --user instance), that'd just
131 * be confusing for the service manager */
132 arg_pid = getpid();
133 } else if (streq(optarg, "parent"))
4a2a8b5a 134 arg_pid = getppid();
9dcd43b1
LP
135 else if (streq(optarg, "self"))
136 arg_pid = getpid();
137 else {
138 r = parse_pid(optarg, &arg_pid);
139 if (r < 0)
140 return log_error_errno(r, "Failed to parse PID %s.", optarg);
141 }
4a2a8b5a
LP
142
143 break;
144
145 case ARG_STATUS:
146 arg_status = optarg;
147 break;
148
96551bae
LP
149 case ARG_BOOTED:
150 arg_booted = true;
151 break;
152
65c6b990
LP
153 case ARG_UID: {
154 const char *u = optarg;
155
fafff8f1 156 r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL, 0);
65c6b990
LP
157 if (r == -ESRCH) /* If the user doesn't exist, then accept it anyway as numeric */
158 r = parse_uid(u, &arg_uid);
159 if (r < 0)
160 return log_error_errno(r, "Can't resolve user %s: %m", optarg);
161
162 break;
163 }
164
4f07ddfa
KKD
165 case ARG_NO_BLOCK:
166 arg_no_block = true;
167 break;
168
4a2a8b5a
LP
169 case '?':
170 return -EINVAL;
171
172 default:
eb9da376 173 assert_not_reached("Unhandled option");
4a2a8b5a 174 }
ee8c4568 175 }
4a2a8b5a 176
2f02ce40
LP
177 if (optind >= argc &&
178 !arg_ready &&
179 !arg_status &&
180 !arg_pid &&
d6bc8348 181 !arg_booted) {
2f02ce40
LP
182 help();
183 return -EINVAL;
184 }
185
4a2a8b5a
LP
186 return 1;
187}
188
aac0b2e8 189static int run(int argc, char* argv[]) {
be8f4e9e
LP
190 _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL;
191 _cleanup_strv_free_ char **final_env = NULL;
192 char* our_env[4];
4a2a8b5a 193 unsigned i = 0;
9dcd43b1 194 pid_t source_pid;
be8f4e9e 195 int r;
4a2a8b5a 196
1a043959 197 log_show_color(true);
4a2a8b5a 198 log_parse_environment();
2396fb04 199 log_open();
4a2a8b5a 200
6c12b52e 201 r = parse_argv(argc, argv);
be8f4e9e 202 if (r <= 0)
aac0b2e8 203 return r;
4a2a8b5a 204
96551bae
LP
205 if (arg_booted)
206 return sd_booted() <= 0;
207
4a2a8b5a
LP
208 if (arg_ready)
209 our_env[i++] = (char*) "READY=1";
210
211 if (arg_status) {
b910cc72 212 status = strjoin("STATUS=", arg_status);
aac0b2e8
ZJS
213 if (!status)
214 return log_oom();
4a2a8b5a
LP
215
216 our_env[i++] = status;
217 }
218
219 if (arg_pid > 0) {
aac0b2e8
ZJS
220 if (asprintf(&cpid, "MAINPID="PID_FMT, arg_pid) < 0)
221 return log_oom();
4a2a8b5a
LP
222
223 our_env[i++] = cpid;
224 }
225
226 our_env[i++] = NULL;
227
be8f4e9e 228 final_env = strv_env_merge(2, our_env, argv + optind);
aac0b2e8
ZJS
229 if (!final_env)
230 return log_oom();
4a2a8b5a 231
aac0b2e8
ZJS
232 if (strv_isempty(final_env))
233 return 0;
4a2a8b5a 234
be8f4e9e 235 n = strv_join(final_env, "\n");
aac0b2e8
ZJS
236 if (!n)
237 return log_oom();
4a2a8b5a 238
5238e957 239 /* If this is requested change to the requested UID/GID. Note that we only change the real UID here, and leave
65c6b990
LP
240 the effective UID in effect (which is 0 for this to work). That's because we want the privileges to fake the
241 ucred data, and sd_pid_notify() uses the real UID for filling in ucred. */
242
aac0b2e8
ZJS
243 if (arg_gid != GID_INVALID &&
244 setregid(arg_gid, (gid_t) -1) < 0)
245 return log_error_errno(errno, "Failed to change GID: %m");
65c6b990 246
aac0b2e8
ZJS
247 if (arg_uid != UID_INVALID &&
248 setreuid(arg_uid, (uid_t) -1) < 0)
249 return log_error_errno(errno, "Failed to change UID: %m");
65c6b990 250
9dcd43b1
LP
251 if (arg_pid > 0)
252 source_pid = arg_pid;
253 else {
254 /* Pretend the message originates from our parent, given that we are typically called from a
255 * shell script, i.e. we are not the main process of a service but only a child of it. */
256 source_pid = getppid();
257 if (source_pid <= 1 ||
258 source_pid == manager_pid()) /* safety check: don't claim we'd send anything from PID 1
259 * or the service manager itself */
260 source_pid = 0;
261 }
262 r = sd_pid_notify(source_pid, false, n);
aac0b2e8
ZJS
263 if (r < 0)
264 return log_error_errno(r, "Failed to notify init system: %m");
baaa35ad
ZJS
265 if (r == 0)
266 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
267 "No status data could be sent: $NOTIFY_SOCKET was not set");
4f07ddfa
KKD
268
269 if (!arg_no_block) {
270 r = sd_notify_barrier(0, 5 * USEC_PER_SEC);
271 if (r < 0)
272 return log_error_errno(r, "Failed to invoke barrier: %m");
273 if (r == 0)
274 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
275 "No status data could be sent: $NOTIFY_SOCKET was not set");
276 }
277
aac0b2e8 278 return 0;
4a2a8b5a 279}
aac0b2e8
ZJS
280
281DEFINE_MAIN_FUNCTION(run);