]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/notify/notify.c
coccinelle: make use of SYNTHETIC_ERRNO
[thirdparty/systemd.git] / src / notify / notify.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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"
65c6b990 20#include "user-util.h"
6f2deb84 21#include "util.h"
4a2a8b5a
LP
22
23static bool arg_ready = false;
24static pid_t arg_pid = 0;
25static const char *arg_status = NULL;
96551bae 26static bool arg_booted = false;
65c6b990
LP
27static uid_t arg_uid = UID_INVALID;
28static gid_t arg_gid = GID_INVALID;
4a2a8b5a 29
37ec0fdd
LP
30static int help(void) {
31 _cleanup_free_ char *link = NULL;
32 int r;
33
34 r = terminal_urlify_man("systemd-notify", "1", &link);
35 if (r < 0)
36 return log_oom();
37
2e33c433 38 printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n"
4a2a8b5a 39 "Notify the init system about service status updates.\n\n"
b57b0625
ZJS
40 " -h --help Show this help\n"
41 " --version Show package version\n"
42 " --ready Inform the init system about service start-up completion\n"
65c6b990
LP
43 " --pid[=PID] Set main PID of daemon\n"
44 " --uid=USER Set user to send from\n"
b57b0625 45 " --status=TEXT Set status text\n"
37ec0fdd
LP
46 " --booted Check if the system was booted up with systemd\n"
47 "\nSee the %s for details.\n"
48 , program_invocation_short_name
49 , link
50 );
51
52 return 0;
4a2a8b5a
LP
53}
54
55static int parse_argv(int argc, char *argv[]) {
56
57 enum {
58 ARG_READY = 0x100,
9aac0b2c 59 ARG_VERSION,
4a2a8b5a 60 ARG_PID,
96551bae 61 ARG_STATUS,
6624768c 62 ARG_BOOTED,
65c6b990 63 ARG_UID,
4a2a8b5a
LP
64 };
65
66 static const struct option options[] = {
6624768c 67 { "help", no_argument, NULL, 'h' },
9aac0b2c 68 { "version", no_argument, NULL, ARG_VERSION },
6624768c
LP
69 { "ready", no_argument, NULL, ARG_READY },
70 { "pid", optional_argument, NULL, ARG_PID },
71 { "status", required_argument, NULL, ARG_STATUS },
72 { "booted", no_argument, NULL, ARG_BOOTED },
65c6b990 73 { "uid", required_argument, NULL, ARG_UID },
eb9da376 74 {}
4a2a8b5a
LP
75 };
76
65c6b990 77 int c, r;
4a2a8b5a
LP
78
79 assert(argc >= 0);
80 assert(argv);
81
ee8c4568 82 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
4a2a8b5a
LP
83
84 switch (c) {
85
86 case 'h':
37ec0fdd 87 return help();
4a2a8b5a 88
9aac0b2c 89 case ARG_VERSION:
3f6fd1ba 90 return version();
9aac0b2c 91
4a2a8b5a
LP
92 case ARG_READY:
93 arg_ready = true;
94 break;
95
96 case ARG_PID:
97
98 if (optarg) {
baaa35ad
ZJS
99 if (parse_pid(optarg, &arg_pid) < 0)
100 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
101 "Failed to parse PID %s.", optarg);
4a2a8b5a
LP
102 } else
103 arg_pid = getppid();
104
105 break;
106
107 case ARG_STATUS:
108 arg_status = optarg;
109 break;
110
96551bae
LP
111 case ARG_BOOTED:
112 arg_booted = true;
113 break;
114
65c6b990
LP
115 case ARG_UID: {
116 const char *u = optarg;
117
fafff8f1 118 r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL, 0);
65c6b990
LP
119 if (r == -ESRCH) /* If the user doesn't exist, then accept it anyway as numeric */
120 r = parse_uid(u, &arg_uid);
121 if (r < 0)
122 return log_error_errno(r, "Can't resolve user %s: %m", optarg);
123
124 break;
125 }
126
4a2a8b5a
LP
127 case '?':
128 return -EINVAL;
129
130 default:
eb9da376 131 assert_not_reached("Unhandled option");
4a2a8b5a 132 }
ee8c4568 133 }
4a2a8b5a 134
2f02ce40
LP
135 if (optind >= argc &&
136 !arg_ready &&
137 !arg_status &&
138 !arg_pid &&
d6bc8348 139 !arg_booted) {
2f02ce40
LP
140 help();
141 return -EINVAL;
142 }
143
4a2a8b5a
LP
144 return 1;
145}
146
aac0b2e8 147static int run(int argc, char* argv[]) {
be8f4e9e
LP
148 _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL;
149 _cleanup_strv_free_ char **final_env = NULL;
150 char* our_env[4];
4a2a8b5a 151 unsigned i = 0;
be8f4e9e 152 int r;
4a2a8b5a
LP
153
154 log_parse_environment();
2396fb04 155 log_open();
4a2a8b5a 156
6c12b52e 157 r = parse_argv(argc, argv);
be8f4e9e 158 if (r <= 0)
aac0b2e8 159 return r;
4a2a8b5a 160
96551bae
LP
161 if (arg_booted)
162 return sd_booted() <= 0;
163
4a2a8b5a
LP
164 if (arg_ready)
165 our_env[i++] = (char*) "READY=1";
166
167 if (arg_status) {
be8f4e9e 168 status = strappend("STATUS=", arg_status);
aac0b2e8
ZJS
169 if (!status)
170 return log_oom();
4a2a8b5a
LP
171
172 our_env[i++] = status;
173 }
174
175 if (arg_pid > 0) {
aac0b2e8
ZJS
176 if (asprintf(&cpid, "MAINPID="PID_FMT, arg_pid) < 0)
177 return log_oom();
4a2a8b5a
LP
178
179 our_env[i++] = cpid;
180 }
181
182 our_env[i++] = NULL;
183
be8f4e9e 184 final_env = strv_env_merge(2, our_env, argv + optind);
aac0b2e8
ZJS
185 if (!final_env)
186 return log_oom();
4a2a8b5a 187
aac0b2e8
ZJS
188 if (strv_isempty(final_env))
189 return 0;
4a2a8b5a 190
be8f4e9e 191 n = strv_join(final_env, "\n");
aac0b2e8
ZJS
192 if (!n)
193 return log_oom();
4a2a8b5a 194
65c6b990
LP
195 /* If this is requested change to the requested UID/GID. Note thta we only change the real UID here, and leave
196 the effective UID in effect (which is 0 for this to work). That's because we want the privileges to fake the
197 ucred data, and sd_pid_notify() uses the real UID for filling in ucred. */
198
aac0b2e8
ZJS
199 if (arg_gid != GID_INVALID &&
200 setregid(arg_gid, (gid_t) -1) < 0)
201 return log_error_errno(errno, "Failed to change GID: %m");
65c6b990 202
aac0b2e8
ZJS
203 if (arg_uid != UID_INVALID &&
204 setreuid(arg_uid, (uid_t) -1) < 0)
205 return log_error_errno(errno, "Failed to change UID: %m");
65c6b990 206
9de009a9 207 r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n);
aac0b2e8
ZJS
208 if (r < 0)
209 return log_error_errno(r, "Failed to notify init system: %m");
baaa35ad
ZJS
210 if (r == 0)
211 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
212 "No status data could be sent: $NOTIFY_SOCKET was not set");
aac0b2e8 213 return 0;
4a2a8b5a 214}
aac0b2e8
ZJS
215
216DEFINE_MAIN_FUNCTION(run);