]>
Commit | Line | Data |
---|---|---|
69045d3d KZ |
1 | /* |
2 | * uuidd.c --- UUID-generation daemon | |
3 | * | |
4 | * Copyright (C) 2007 Theodore Ts'o | |
5 | * | |
6 | * %Begin-Header% | |
7 | * This file may be redistributed under the terms of the GNU Public | |
8 | * License. | |
9 | * %End-Header% | |
10 | */ | |
2049075a SK |
11 | |
12 | /* | |
13 | * The uuidd protocol. | |
14 | * | |
15 | * Client: | |
16 | * | operation type (1 byte) | number of uuids (if bulk request, 4 bytes) | | |
17 | * | |
18 | * Server: | |
19 | * | reply length (4 bytes) | uuid reply (16 bytes) | | |
20 | * or | |
21 | * | reply length (4 bytes) | uuid reply (16 bytes) multiply by number when random bulk request | | |
22 | * or | |
23 | * | reply length (4 bytes) | uuid reply (16 bytes) | number (4 bytes) time bulk | | |
24 | * or | |
25 | * | reply length (4 bytes) | pid or maxop number string length in ascii (up to 7 bytes) | | |
26 | */ | |
27 | ||
69045d3d | 28 | #include <stdio.h> |
69045d3d | 29 | #include <stdlib.h> |
69045d3d KZ |
30 | #include <unistd.h> |
31 | #include <inttypes.h> | |
32 | #include <errno.h> | |
ea091bed | 33 | #include <err.h> |
69045d3d KZ |
34 | #include <sys/types.h> |
35 | #include <sys/stat.h> | |
36 | #include <sys/socket.h> | |
37 | #include <sys/un.h> | |
38 | #include <fcntl.h> | |
39 | #include <signal.h> | |
40 | #include <string.h> | |
69045d3d | 41 | #include <getopt.h> |
25d66b4e SK |
42 | #include <sys/signalfd.h> |
43 | #include <poll.h> | |
69045d3d KZ |
44 | |
45 | #include "uuid.h" | |
46 | #include "uuidd.h" | |
e12c9866 | 47 | #include "all-io.h" |
f6f3dc78 | 48 | #include "c.h" |
c05a80ca | 49 | #include "closestream.h" |
bcb693de | 50 | #include "strutils.h" |
e4faf648 | 51 | #include "optutils.h" |
25d66b4e SK |
52 | #include "monotonic.h" |
53 | #include "timer.h" | |
bbe289c4 | 54 | |
ebff016a KZ |
55 | #ifdef HAVE_LIBSYSTEMD |
56 | # include <systemd/sd-daemon.h> | |
bbe289c4 PU |
57 | #endif |
58 | ||
69045d3d KZ |
59 | #include "nls.h" |
60 | ||
3c560686 | 61 | /* Protocol segment lengths */ |
b4911618 SK |
62 | typedef uint8_t uuidd_prot_op_t; /* client operation field */ |
63 | typedef int32_t uuidd_prot_num_t; /* number of requested uuids */ | |
64 | ||
65 | enum { | |
66 | /* client - server buffer size */ | |
67 | UUIDD_PROT_BUFSZ = ((sizeof(uuidd_prot_num_t)) + (sizeof(uuid_t) * 63)) | |
68 | }; | |
a8f13198 | 69 | |
881a0f6b PU |
70 | /* server loop control structure */ |
71 | struct uuidd_cxt_t { | |
25d66b4e SK |
72 | const char *cleanup_pidfile; |
73 | const char *cleanup_socket; | |
d8bb8a03 | 74 | uint32_t timeout; |
3cfba7d3 MT |
75 | uint32_t cont_clock_offset; |
76 | ||
881a0f6b PU |
77 | unsigned int debug: 1, |
78 | quiet: 1, | |
79 | no_fork: 1, | |
80 | no_sock: 1; | |
81 | }; | |
82 | ||
b03ffec1 | 83 | struct uuidd_options_t { |
b4911618 SK |
84 | const char *pidfile_path; |
85 | const char *socket_path; | |
86 | uuidd_prot_num_t num; | |
87 | uuidd_prot_op_t do_type; | |
88 | unsigned int do_kill:1, | |
89 | no_pid:1, | |
90 | s_flag:1; | |
b03ffec1 SK |
91 | }; |
92 | ||
86be6a32 | 93 | static void __attribute__((__noreturn__)) usage(void) |
69045d3d | 94 | { |
86be6a32 | 95 | FILE *out = stdout; |
db433bf7 | 96 | fputs(USAGE_HEADER, out); |
61b64276 | 97 | fprintf(out, _(" %s [options]\n"), program_invocation_short_name); |
451dbcfa BS |
98 | fputs(USAGE_SEPARATOR, out); |
99 | fputs(_("A daemon for generating UUIDs.\n"), out); | |
db433bf7 | 100 | fputs(USAGE_OPTIONS, out); |
61b64276 SK |
101 | fputs(_(" -p, --pid <path> path to pid file\n"), out); |
102 | fputs(_(" -s, --socket <path> path to socket\n"), out); | |
103 | fputs(_(" -T, --timeout <sec> specify inactivity timeout\n"), out); | |
104 | fputs(_(" -k, --kill kill running daemon\n"), out); | |
105 | fputs(_(" -r, --random test random-based generation\n"), out); | |
106 | fputs(_(" -t, --time test time-based generation\n"), out); | |
107 | fputs(_(" -n, --uuids <num> request number of uuids\n"), out); | |
108 | fputs(_(" -P, --no-pid do not create pid file\n"), out); | |
109 | fputs(_(" -F, --no-fork do not daemonize using double-fork\n"), out); | |
110 | fputs(_(" -S, --socket-activation do not create listening socket\n"), out); | |
3cfba7d3 MT |
111 | fputs(_(" -C, --cont-clock[=<NUM>[hd]]\n"), out); |
112 | fputs(_(" activate continuous clock handling\n"), out); | |
61b64276 SK |
113 | fputs(_(" -d, --debug run in debugging mode\n"), out); |
114 | fputs(_(" -q, --quiet turn on quiet mode\n"), out); | |
115 | fputs(USAGE_SEPARATOR, out); | |
bad4c729 MY |
116 | fprintf(out, USAGE_HELP_OPTIONS(25)); |
117 | fprintf(out, USAGE_MAN_TAIL("uuidd(8)")); | |
86be6a32 | 118 | exit(EXIT_SUCCESS); |
69045d3d KZ |
119 | } |
120 | ||
69045d3d KZ |
121 | static void create_daemon(void) |
122 | { | |
69045d3d KZ |
123 | uid_t euid; |
124 | ||
f7c297b8 | 125 | if (daemon(0, 0)) |
ea091bed | 126 | err(EXIT_FAILURE, "daemon"); |
69045d3d | 127 | |
69045d3d KZ |
128 | euid = geteuid(); |
129 | if (setreuid(euid, euid) < 0) | |
ea091bed | 130 | err(EXIT_FAILURE, "setreuid"); |
69045d3d KZ |
131 | } |
132 | ||
b4911618 SK |
133 | static int call_daemon(const char *socket_path, uuidd_prot_op_t op, char *buf, |
134 | size_t buflen, uuidd_prot_num_t *num, const char **err_context) | |
69045d3d | 135 | { |
b4911618 SK |
136 | char op_buf[sizeof(op) + sizeof(*num)]; |
137 | size_t op_len; | |
69045d3d KZ |
138 | int s; |
139 | ssize_t ret; | |
140 | int32_t reply_len = 0; | |
141 | struct sockaddr_un srv_addr; | |
142 | ||
78314078 PU |
143 | if (((op == UUIDD_OP_BULK_TIME_UUID) || |
144 | (op == UUIDD_OP_BULK_RANDOM_UUID)) && !num) { | |
69045d3d KZ |
145 | if (err_context) |
146 | *err_context = _("bad arguments"); | |
147 | errno = EINVAL; | |
148 | return -1; | |
149 | } | |
150 | ||
151 | if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { | |
152 | if (err_context) | |
153 | *err_context = _("socket"); | |
154 | return -1; | |
155 | } | |
156 | ||
157 | srv_addr.sun_family = AF_UNIX; | |
eb10dbc1 RM |
158 | assert(strlen(socket_path) < sizeof(srv_addr.sun_path)); |
159 | xstrncpy(srv_addr.sun_path, socket_path, sizeof(srv_addr.sun_path)); | |
69045d3d KZ |
160 | |
161 | if (connect(s, (const struct sockaddr *) &srv_addr, | |
162 | sizeof(struct sockaddr_un)) < 0) { | |
163 | if (err_context) | |
164 | *err_context = _("connect"); | |
165 | close(s); | |
166 | return -1; | |
167 | } | |
168 | ||
78314078 | 169 | if (op == UUIDD_OP_BULK_RANDOM_UUID) { |
b4911618 SK |
170 | if ((buflen - sizeof(*num)) < (size_t)((*num) * sizeof(uuid_t))) |
171 | *num = (buflen - sizeof(*num)) / sizeof(uuid_t); | |
69045d3d KZ |
172 | } |
173 | op_buf[0] = op; | |
b4911618 | 174 | op_len = sizeof(op); |
78314078 PU |
175 | if ((op == UUIDD_OP_BULK_TIME_UUID) || |
176 | (op == UUIDD_OP_BULK_RANDOM_UUID)) { | |
b4911618 SK |
177 | memcpy(op_buf + sizeof(op), num, sizeof(*num)); |
178 | op_len += sizeof(*num); | |
69045d3d KZ |
179 | } |
180 | ||
181 | ret = write_all(s, op_buf, op_len); | |
38674931 | 182 | if (ret < 0) { |
69045d3d KZ |
183 | if (err_context) |
184 | *err_context = _("write"); | |
185 | close(s); | |
186 | return -1; | |
187 | } | |
188 | ||
189 | ret = read_all(s, (char *) &reply_len, sizeof(reply_len)); | |
190 | if (ret < 0) { | |
191 | if (err_context) | |
192 | *err_context = _("read count"); | |
193 | close(s); | |
194 | return -1; | |
195 | } | |
c3bfedc3 | 196 | if (reply_len < 0 || (size_t) reply_len > buflen) { |
69045d3d KZ |
197 | if (err_context) |
198 | *err_context = _("bad response length"); | |
199 | close(s); | |
200 | return -1; | |
201 | } | |
202 | ret = read_all(s, (char *) buf, reply_len); | |
203 | ||
78314078 | 204 | if ((ret > 0) && (op == UUIDD_OP_BULK_TIME_UUID)) { |
b4911618 SK |
205 | if ((sizeof(uuid_t) + sizeof(*num)) <= (size_t) reply_len) |
206 | memcpy(buf + sizeof(uuid_t), num, sizeof(*num)); | |
69045d3d KZ |
207 | else |
208 | *num = -1; | |
209 | } | |
78314078 | 210 | if ((ret > 0) && (op == UUIDD_OP_BULK_RANDOM_UUID)) { |
b4911618 SK |
211 | if (sizeof(*num) <= (size_t) reply_len) |
212 | memcpy(buf, num, sizeof(*num)); | |
69045d3d KZ |
213 | else |
214 | *num = -1; | |
215 | } | |
216 | ||
217 | close(s); | |
218 | ||
219 | return ret; | |
220 | } | |
221 | ||
c4536355 PU |
222 | /* |
223 | * Exclusively create and open a pid file with path @pidfile_path | |
224 | * | |
c4536355 PU |
225 | * Return file descriptor of the created pid_file. |
226 | */ | |
b85df4b7 | 227 | static int create_pidfile(struct uuidd_cxt_t *cxt, const char *pidfile_path) |
69045d3d | 228 | { |
c4536355 PU |
229 | int fd_pidfile; |
230 | struct flock fl; | |
69045d3d KZ |
231 | |
232 | fd_pidfile = open(pidfile_path, O_CREAT | O_RDWR, 0664); | |
233 | if (fd_pidfile < 0) { | |
b85df4b7 | 234 | if (!cxt->quiet) |
bcb693de | 235 | warn(_("cannot open %s"), pidfile_path); |
41dc5bc0 | 236 | exit(EXIT_FAILURE); |
69045d3d | 237 | } |
b85df4b7 | 238 | cxt->cleanup_pidfile = pidfile_path; |
c4536355 | 239 | |
69045d3d KZ |
240 | fl.l_type = F_WRLCK; |
241 | fl.l_whence = SEEK_SET; | |
242 | fl.l_start = 0; | |
243 | fl.l_len = 0; | |
244 | fl.l_pid = 0; | |
245 | while (fcntl(fd_pidfile, F_SETLKW, &fl) < 0) { | |
246 | if ((errno == EAGAIN) || (errno == EINTR)) | |
247 | continue; | |
b85df4b7 | 248 | if (!cxt->quiet) |
bcb693de | 249 | warn(_("cannot lock %s"), pidfile_path); |
41dc5bc0 | 250 | exit(EXIT_FAILURE); |
69045d3d | 251 | } |
c4536355 PU |
252 | |
253 | return fd_pidfile; | |
254 | } | |
255 | ||
75a94e8b PU |
256 | /* |
257 | * Create AF_UNIX, SOCK_STREAM socket and bind to @socket_path | |
258 | * | |
259 | * If @will_fork is true, then make sure the descriptor | |
3fd1f771 | 260 | * of the socket is >2, so that it won't be later closed |
75a94e8b PU |
261 | * during create_daemon(). |
262 | * | |
263 | * Return file descriptor corresponding to created socket. | |
264 | */ | |
25d66b4e SK |
265 | static int create_socket(struct uuidd_cxt_t *uuidd_cxt, |
266 | const char *socket_path, int will_fork) | |
c4536355 | 267 | { |
75a94e8b | 268 | struct sockaddr_un my_addr; |
c4536355 | 269 | mode_t save_umask; |
b85df4b7 | 270 | int s; |
69045d3d KZ |
271 | |
272 | if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { | |
25d66b4e | 273 | if (!uuidd_cxt->quiet) |
bcb693de | 274 | warn(_("couldn't create unix stream socket")); |
41dc5bc0 | 275 | exit(EXIT_FAILURE); |
69045d3d KZ |
276 | } |
277 | ||
fdb3e93c TT |
278 | /* |
279 | * Make sure the socket isn't using fd numbers 0-2 to avoid it | |
280 | * getting closed by create_daemon() | |
281 | */ | |
75a94e8b | 282 | while (will_fork && s <= 2) { |
fdb3e93c | 283 | s = dup(s); |
2d169242 | 284 | if (s < 0) |
ea091bed | 285 | err(EXIT_FAILURE, "dup"); |
fdb3e93c TT |
286 | } |
287 | ||
69045d3d KZ |
288 | /* |
289 | * Create the address we will be binding to. | |
290 | */ | |
291 | my_addr.sun_family = AF_UNIX; | |
eb10dbc1 RM |
292 | assert(strlen(socket_path) < sizeof(my_addr.sun_path)); |
293 | xstrncpy(my_addr.sun_path, socket_path, sizeof(my_addr.sun_path)); | |
f7c297b8 | 294 | unlink(socket_path); |
69045d3d KZ |
295 | save_umask = umask(0); |
296 | if (bind(s, (const struct sockaddr *) &my_addr, | |
297 | sizeof(struct sockaddr_un)) < 0) { | |
25d66b4e | 298 | if (!uuidd_cxt->quiet) |
bcb693de | 299 | warn(_("couldn't bind unix socket %s"), socket_path); |
41dc5bc0 | 300 | exit(EXIT_FAILURE); |
69045d3d | 301 | } |
f7c297b8 | 302 | umask(save_umask); |
25d66b4e | 303 | uuidd_cxt->cleanup_socket = socket_path; |
69045d3d | 304 | |
75a94e8b PU |
305 | return s; |
306 | } | |
307 | ||
25d66b4e SK |
308 | static void __attribute__((__noreturn__)) all_done(const struct uuidd_cxt_t *uuidd_cxt, int ret) |
309 | { | |
310 | if (uuidd_cxt->cleanup_pidfile) | |
311 | unlink(uuidd_cxt->cleanup_pidfile); | |
312 | if (uuidd_cxt->cleanup_socket) | |
313 | unlink(uuidd_cxt->cleanup_socket); | |
314 | exit(ret); | |
315 | } | |
316 | ||
317 | static void handle_signal(const struct uuidd_cxt_t *uuidd_cxt, int fd) | |
318 | { | |
319 | struct signalfd_siginfo info; | |
320 | ssize_t bytes; | |
321 | ||
322 | bytes = read(fd, &info, sizeof(info)); | |
323 | if (bytes != sizeof(info)) { | |
324 | if (errno == EAGAIN) | |
325 | return; | |
326 | warn(_("receiving signal failed")); | |
327 | info.ssi_signo = 0; | |
328 | } | |
329 | if (info.ssi_signo == SIGPIPE) | |
330 | return; /* ignored */ | |
331 | all_done(uuidd_cxt, EXIT_SUCCESS); | |
332 | } | |
333 | ||
334 | static void timeout_handler(int sig __attribute__((__unused__)), | |
335 | siginfo_t * info, | |
336 | void *context __attribute__((__unused__))) | |
337 | { | |
6df5acf9 | 338 | #ifdef HAVE_TIMER_CREATE |
25d66b4e | 339 | if (info->si_code == SI_TIMER) |
6df5acf9 | 340 | #endif |
25d66b4e SK |
341 | errx(EXIT_FAILURE, _("timed out")); |
342 | } | |
343 | ||
75a94e8b | 344 | static void server_loop(const char *socket_path, const char *pidfile_path, |
25d66b4e | 345 | struct uuidd_cxt_t *uuidd_cxt) |
75a94e8b PU |
346 | { |
347 | struct sockaddr_un from_addr; | |
348 | socklen_t fromlen; | |
349 | int32_t reply_len = 0; | |
350 | uuid_t uu; | |
b4911618 SK |
351 | char reply_buf[UUIDD_PROT_BUFSZ], *cp; |
352 | uuidd_prot_op_t op; | |
353 | char str[UUID_STR_LEN]; | |
4813a521 | 354 | int i, ns, len; |
b4911618 | 355 | uuidd_prot_num_t num; /* intentionally uninitialized */ |
bbe289c4 | 356 | int s = 0; |
75a94e8b PU |
357 | int fd_pidfile = -1; |
358 | int ret; | |
3d6250e9 | 359 | struct pollfd pfd[2]; |
25d66b4e SK |
360 | sigset_t sigmask; |
361 | int sigfd; | |
362 | enum { | |
363 | POLLFD_SIGNAL = 0, | |
364 | POLLFD_SOCKET | |
365 | }; | |
75a94e8b | 366 | |
ebff016a | 367 | #ifdef HAVE_LIBSYSTEMD |
881a0f6b | 368 | if (!uuidd_cxt->no_sock) /* no_sock implies no_fork and no_pid */ |
bbe289c4 PU |
369 | #endif |
370 | { | |
6df5acf9 | 371 | struct ul_timer timer; |
25d66b4e | 372 | struct itimerval timeout; |
75a94e8b | 373 | |
25d66b4e SK |
374 | memset(&timeout, 0, sizeof timeout); |
375 | timeout.it_value.tv_sec = 30; | |
6df5acf9 | 376 | if (setup_timer(&timer, &timeout, &timeout_handler)) |
9e708d01 | 377 | err(EXIT_FAILURE, _("cannot set up timer")); |
bbe289c4 | 378 | if (pidfile_path) |
b85df4b7 | 379 | fd_pidfile = create_pidfile(uuidd_cxt, pidfile_path); |
bbe289c4 PU |
380 | ret = call_daemon(socket_path, UUIDD_OP_GETPID, reply_buf, |
381 | sizeof(reply_buf), 0, NULL); | |
6df5acf9 | 382 | cancel_timer(&timer); |
bbe289c4 | 383 | if (ret > 0) { |
881a0f6b | 384 | if (!uuidd_cxt->quiet) |
0012e33c | 385 | warnx(_("uuidd daemon is already running at pid %s"), |
25d66b4e | 386 | reply_buf); |
bbe289c4 PU |
387 | exit(EXIT_FAILURE); |
388 | } | |
bbe289c4 | 389 | |
25d66b4e SK |
390 | s = create_socket(uuidd_cxt, socket_path, |
391 | (!uuidd_cxt->debug || !uuidd_cxt->no_fork)); | |
bbe289c4 | 392 | if (listen(s, SOMAXCONN) < 0) { |
881a0f6b | 393 | if (!uuidd_cxt->quiet) |
bcb693de | 394 | warn(_("couldn't listen on unix socket %s"), socket_path); |
bbe289c4 PU |
395 | exit(EXIT_FAILURE); |
396 | } | |
397 | ||
881a0f6b | 398 | if (!uuidd_cxt->debug && !uuidd_cxt->no_fork) |
bbe289c4 PU |
399 | create_daemon(); |
400 | ||
401 | if (pidfile_path) { | |
948cac89 | 402 | snprintf(reply_buf, sizeof(reply_buf), "%8d\n", getpid()); |
caac2064 SK |
403 | if (ftruncate(fd_pidfile, 0)) |
404 | err(EXIT_FAILURE, _("could not truncate file: %s"), pidfile_path); | |
bbe289c4 | 405 | write_all(fd_pidfile, reply_buf, strlen(reply_buf)); |
74ce680a SK |
406 | if (fd_pidfile > 1 && close_fd(fd_pidfile) != 0) |
407 | err(EXIT_FAILURE, _("write failed: %s"), pidfile_path); | |
bbe289c4 | 408 | } |
75a94e8b | 409 | |
69045d3d KZ |
410 | } |
411 | ||
ebff016a | 412 | #ifdef HAVE_LIBSYSTEMD |
881a0f6b | 413 | if (uuidd_cxt->no_sock) { |
fd346daf | 414 | const int r = sd_listen_fds(0); |
fa8945db | 415 | |
fd346daf SK |
416 | if (r < 0) { |
417 | errno = r * -1; | |
fa8945db | 418 | err(EXIT_FAILURE, _("sd_listen_fds() failed")); |
fd346daf | 419 | } else if (r == 0) |
fa8945db SK |
420 | errx(EXIT_FAILURE, |
421 | _("no file descriptors received, check systemctl status uuidd.socket")); | |
fd346daf | 422 | else if (1 < r) |
fa8945db SK |
423 | errx(EXIT_FAILURE, |
424 | _("too many file descriptors received, check uuidd.socket")); | |
bbe289c4 | 425 | s = SD_LISTEN_FDS_START + 0; |
f7c297b8 | 426 | } |
bbe289c4 | 427 | #endif |
69045d3d | 428 | |
25d66b4e SK |
429 | sigemptyset(&sigmask); |
430 | sigaddset(&sigmask, SIGHUP); | |
431 | sigaddset(&sigmask, SIGINT); | |
432 | sigaddset(&sigmask, SIGTERM); | |
433 | sigaddset(&sigmask, SIGALRM); | |
434 | sigaddset(&sigmask, SIGPIPE); | |
435 | /* Block signals so that they aren't handled according to their | |
436 | * default dispositions */ | |
437 | sigprocmask(SIG_BLOCK, &sigmask, NULL); | |
438 | if ((sigfd = signalfd(-1, &sigmask, 0)) < 0) | |
439 | err(EXIT_FAILURE, _("cannot set signal handler")); | |
440 | ||
441 | pfd[POLLFD_SIGNAL].fd = sigfd; | |
442 | pfd[POLLFD_SOCKET].fd = s; | |
443 | pfd[POLLFD_SIGNAL].events = pfd[POLLFD_SOCKET].events = POLLIN | POLLERR | POLLHUP; | |
444 | ||
3cfba7d3 MT |
445 | num = 1; |
446 | if (uuidd_cxt->cont_clock_offset) { | |
447 | /* trigger initialization */ | |
448 | (void) __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset); | |
449 | if (uuidd_cxt->debug) | |
450 | fprintf(stderr, _("max_clock_offset = %u sec\n"), | |
451 | uuidd_cxt->cont_clock_offset); | |
452 | } | |
453 | ||
69045d3d | 454 | while (1) { |
4b09b1c2 KZ |
455 | ret = poll(pfd, ARRAY_SIZE(pfd), |
456 | uuidd_cxt->timeout ? | |
457 | (int) uuidd_cxt->timeout * 1000 : -1); | |
25d66b4e SK |
458 | if (ret < 0) { |
459 | if (errno == EAGAIN) | |
460 | continue; | |
461 | warn(_("poll failed")); | |
ef6ac596 | 462 | all_done(uuidd_cxt, EXIT_FAILURE); |
25d66b4e | 463 | } |
9e930041 | 464 | if (ret == 0) { /* true when poll() times out */ |
3d6250e9 | 465 | if (uuidd_cxt->debug) |
f187035d | 466 | fprintf(stderr, _("timeout [%d sec]\n"), uuidd_cxt->timeout); |
25d66b4e | 467 | all_done(uuidd_cxt, EXIT_SUCCESS); |
3d6250e9 | 468 | } |
25d66b4e SK |
469 | if (pfd[POLLFD_SIGNAL].revents != 0) |
470 | handle_signal(uuidd_cxt, sigfd); | |
471 | if (pfd[POLLFD_SOCKET].revents == 0) | |
472 | continue; | |
69045d3d | 473 | fromlen = sizeof(from_addr); |
69045d3d | 474 | ns = accept(s, (struct sockaddr *) &from_addr, &fromlen); |
69045d3d KZ |
475 | if (ns < 0) { |
476 | if ((errno == EAGAIN) || (errno == EINTR)) | |
477 | continue; | |
042f62df | 478 | err(EXIT_FAILURE, "accept"); |
69045d3d | 479 | } |
b4911618 SK |
480 | len = read(ns, &op, sizeof(op)); |
481 | if (len != sizeof(op)) { | |
69045d3d | 482 | if (len < 0) |
bcb693de | 483 | warn(_("read failed")); |
69045d3d | 484 | else |
0012e33c | 485 | warnx(_("error reading from client, len = %d"), |
bcb693de | 486 | len); |
69045d3d KZ |
487 | goto shutdown_socket; |
488 | } | |
78314078 PU |
489 | if ((op == UUIDD_OP_BULK_TIME_UUID) || |
490 | (op == UUIDD_OP_BULK_RANDOM_UUID)) { | |
b4911618 | 491 | if (read_all(ns, (char *) &num, sizeof(num)) != sizeof(num)) |
69045d3d | 492 | goto shutdown_socket; |
881a0f6b | 493 | if (uuidd_cxt->debug) |
18c68d70 | 494 | fprintf(stderr, _("operation %d, incoming num = %d\n"), |
69045d3d | 495 | op, num); |
881a0f6b | 496 | } else if (uuidd_cxt->debug) |
18c68d70 | 497 | fprintf(stderr, _("operation %d\n"), op); |
69045d3d | 498 | |
f7c297b8 | 499 | switch (op) { |
69045d3d | 500 | case UUIDD_OP_GETPID: |
948cac89 | 501 | snprintf(reply_buf, sizeof(reply_buf), "%d", getpid()); |
f7c297b8 | 502 | reply_len = strlen(reply_buf) + 1; |
69045d3d KZ |
503 | break; |
504 | case UUIDD_OP_GET_MAXOP: | |
948cac89 | 505 | snprintf(reply_buf, sizeof(reply_buf), "%d", UUIDD_MAX_OP); |
f7c297b8 | 506 | reply_len = strlen(reply_buf) + 1; |
69045d3d KZ |
507 | break; |
508 | case UUIDD_OP_TIME_UUID: | |
509 | num = 1; | |
3cfba7d3 MT |
510 | ret = __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset); |
511 | if (ret < 0 && !uuidd_cxt->quiet) | |
f27876f9 | 512 | warnx(_("failed to open/lock clock counter")); |
881a0f6b | 513 | if (uuidd_cxt->debug) { |
69045d3d | 514 | uuid_unparse(uu, str); |
18c68d70 | 515 | fprintf(stderr, _("Generated time UUID: %s\n"), str); |
69045d3d KZ |
516 | } |
517 | memcpy(reply_buf, uu, sizeof(uu)); | |
518 | reply_len = sizeof(uu); | |
519 | break; | |
520 | case UUIDD_OP_RANDOM_UUID: | |
521 | num = 1; | |
b408a291 | 522 | __uuid_generate_random(uu, &num); |
881a0f6b | 523 | if (uuidd_cxt->debug) { |
69045d3d | 524 | uuid_unparse(uu, str); |
18c68d70 | 525 | fprintf(stderr, _("Generated random UUID: %s\n"), str); |
69045d3d KZ |
526 | } |
527 | memcpy(reply_buf, uu, sizeof(uu)); | |
528 | reply_len = sizeof(uu); | |
529 | break; | |
530 | case UUIDD_OP_BULK_TIME_UUID: | |
3cfba7d3 MT |
531 | ret = __uuid_generate_time_cont(uu, &num, uuidd_cxt->cont_clock_offset); |
532 | if (ret < 0 && !uuidd_cxt->quiet) | |
f27876f9 | 533 | warnx(_("failed to open/lock clock counter")); |
881a0f6b | 534 | if (uuidd_cxt->debug) { |
69045d3d | 535 | uuid_unparse(uu, str); |
18c68d70 PU |
536 | fprintf(stderr, P_("Generated time UUID %s " |
537 | "and %d following\n", | |
538 | "Generated time UUID %s " | |
539 | "and %d following\n", num - 1), | |
0149cd84 | 540 | str, num - 1); |
69045d3d KZ |
541 | } |
542 | memcpy(reply_buf, uu, sizeof(uu)); | |
543 | reply_len = sizeof(uu); | |
f7c297b8 | 544 | memcpy(reply_buf + reply_len, &num, sizeof(num)); |
69045d3d KZ |
545 | reply_len += sizeof(num); |
546 | break; | |
547 | case UUIDD_OP_BULK_RANDOM_UUID: | |
548 | if (num < 0) | |
549 | num = 1; | |
b4911618 SK |
550 | if ((sizeof(reply_buf) - sizeof(num)) < (size_t) (sizeof(uu) * num)) |
551 | num = (sizeof(reply_buf) - sizeof(num)) / sizeof(uu); | |
70b989c2 | 552 | __uuid_generate_random((unsigned char *) reply_buf + |
c544aa2c | 553 | sizeof(num), &num); |
b4911618 | 554 | reply_len = sizeof(num) + (sizeof(uu) * num); |
ea941692 | 555 | memcpy(reply_buf, &num, sizeof(num)); |
881a0f6b | 556 | if (uuidd_cxt->debug) { |
18c68d70 PU |
557 | fprintf(stderr, P_("Generated %d UUID:\n", |
558 | "Generated %d UUIDs:\n", num), num); | |
b4911618 | 559 | cp = reply_buf + sizeof(num); |
ea941692 | 560 | for (i = 0; i < num; i++) { |
69045d3d | 561 | uuid_unparse((unsigned char *)cp, str); |
18c68d70 | 562 | fprintf(stderr, "\t%s\n", str); |
b4911618 | 563 | cp += sizeof(uu); |
69045d3d KZ |
564 | } |
565 | } | |
69045d3d KZ |
566 | break; |
567 | default: | |
881a0f6b | 568 | if (uuidd_cxt->debug) |
18c68d70 | 569 | fprintf(stderr, _("Invalid operation %d\n"), op); |
69045d3d KZ |
570 | goto shutdown_socket; |
571 | } | |
b4911618 | 572 | write_all(ns, (char *) &reply_len, sizeof(num)); |
69045d3d KZ |
573 | write_all(ns, reply_buf, reply_len); |
574 | shutdown_socket: | |
575 | close(ns); | |
576 | } | |
577 | } | |
578 | ||
2fb35353 SK |
579 | static void __attribute__ ((__noreturn__)) unexpected_size(int size) |
580 | { | |
581 | errx(EXIT_FAILURE, _("Unexpected reply length from server %d"), size); | |
582 | } | |
583 | ||
3cfba7d3 MT |
584 | static uint32_t parse_cont_clock(char *arg) |
585 | { | |
586 | uint32_t min_val = 60, | |
587 | max_val = (3600 * 24 * 365), | |
588 | factor = 1; | |
589 | char *p = &arg[strlen(arg)-1]; | |
590 | ||
591 | if ('h' == *p) { | |
592 | *p = '\0'; | |
593 | factor = 3600; | |
594 | min_val = 1; | |
595 | } | |
596 | if ('d' == *p) { | |
597 | *p = '\0'; | |
598 | factor = 24 * 3600; | |
599 | min_val = 1; | |
600 | } | |
601 | return factor * str2num_or_err(optarg, 10, _("failed to parse --cont-clock/-C"), | |
602 | min_val, max_val / factor); | |
603 | } | |
604 | ||
084efcee SK |
605 | static void parse_options(int argc, char **argv, struct uuidd_cxt_t *uuidd_cxt, |
606 | struct uuidd_options_t *uuidd_opts) | |
69045d3d | 607 | { |
084efcee | 608 | const struct option longopts[] = { |
f0ef0b58 SK |
609 | {"pid", required_argument, NULL, 'p'}, |
610 | {"socket", required_argument, NULL, 's'}, | |
611 | {"timeout", required_argument, NULL, 'T'}, | |
612 | {"kill", no_argument, NULL, 'k'}, | |
613 | {"random", no_argument, NULL, 'r'}, | |
614 | {"time", no_argument, NULL, 't'}, | |
615 | {"uuids", required_argument, NULL, 'n'}, | |
0abfbd9c | 616 | {"no-pid", no_argument, NULL, 'P'}, |
e1cf3ebe | 617 | {"no-fork", no_argument, NULL, 'F'}, |
bbe289c4 | 618 | {"socket-activation", no_argument, NULL, 'S'}, |
3cfba7d3 | 619 | {"cont-clock", optional_argument, NULL, 'C'}, |
f0ef0b58 SK |
620 | {"debug", no_argument, NULL, 'd'}, |
621 | {"quiet", no_argument, NULL, 'q'}, | |
622 | {"version", no_argument, NULL, 'V'}, | |
623 | {"help", no_argument, NULL, 'h'}, | |
624 | {NULL, 0, NULL, 0} | |
625 | }; | |
084efcee | 626 | const ul_excl_t excl[] = { |
e4faf648 SK |
627 | { 'P', 'p' }, |
628 | { 'd', 'q' }, | |
629 | { 'r', 't' }, | |
630 | { 0 } | |
631 | }; | |
632 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
084efcee | 633 | int c; |
f0ef0b58 | 634 | |
3cfba7d3 | 635 | while ((c = getopt_long(argc, argv, "p:s:T:krtn:PFSC::dqVh", longopts, NULL)) != -1) { |
e4faf648 | 636 | err_exclusive_options(c, longopts, excl, excl_st); |
69045d3d | 637 | switch (c) { |
3cfba7d3 MT |
638 | case 'C': |
639 | if (optarg != NULL) | |
640 | uuidd_cxt->cont_clock_offset = parse_cont_clock(optarg); | |
641 | else | |
642 | uuidd_cxt->cont_clock_offset = 7200; /* default 2h */ | |
643 | break; | |
69045d3d | 644 | case 'd': |
084efcee | 645 | uuidd_cxt->debug = 1; |
69045d3d KZ |
646 | break; |
647 | case 'k': | |
084efcee | 648 | uuidd_opts->do_kill = 1; |
69045d3d KZ |
649 | break; |
650 | case 'n': | |
b4911618 | 651 | uuidd_opts->num = (uuidd_prot_num_t) strtou16_or_err(optarg, |
bcb693de | 652 | _("failed to parse --uuids")); |
b747e886 | 653 | break; |
69045d3d | 654 | case 'p': |
084efcee | 655 | uuidd_opts->pidfile_path = optarg; |
0abfbd9c PU |
656 | break; |
657 | case 'P': | |
084efcee | 658 | uuidd_opts->no_pid = 1; |
69045d3d | 659 | break; |
e1cf3ebe | 660 | case 'F': |
084efcee | 661 | uuidd_cxt->no_fork = 1; |
e1cf3ebe | 662 | break; |
bbe289c4 | 663 | case 'S': |
ebff016a | 664 | #ifdef HAVE_LIBSYSTEMD |
084efcee SK |
665 | uuidd_cxt->no_sock = 1; |
666 | uuidd_cxt->no_fork = 1; | |
667 | uuidd_opts->no_pid = 1; | |
bbe289c4 | 668 | #else |
bcb693de | 669 | errx(EXIT_FAILURE, _("uuidd has been built without " |
0012e33c | 670 | "support for socket activation")); |
bbe289c4 PU |
671 | #endif |
672 | break; | |
69045d3d | 673 | case 'q': |
084efcee | 674 | uuidd_cxt->quiet = 1; |
69045d3d | 675 | break; |
3c062294 | 676 | case 'r': |
084efcee | 677 | uuidd_opts->do_type = UUIDD_OP_RANDOM_UUID; |
3c062294 | 678 | break; |
69045d3d | 679 | case 's': |
084efcee SK |
680 | uuidd_opts->socket_path = optarg; |
681 | uuidd_opts->s_flag = 1; | |
69045d3d KZ |
682 | break; |
683 | case 't': | |
084efcee | 684 | uuidd_opts->do_type = UUIDD_OP_TIME_UUID; |
69045d3d KZ |
685 | break; |
686 | case 'T': | |
084efcee | 687 | uuidd_cxt->timeout = strtou32_or_err(optarg, |
bcb693de | 688 | _("failed to parse --timeout")); |
69045d3d | 689 | break; |
2c308875 | 690 | |
f0ef0b58 | 691 | case 'V': |
2c308875 | 692 | print_version(EXIT_SUCCESS); |
f0ef0b58 | 693 | case 'h': |
86be6a32 | 694 | usage(); |
69045d3d | 695 | default: |
677ec86c | 696 | errtryhelp(EXIT_FAILURE); |
69045d3d KZ |
697 | } |
698 | } | |
77a54655 SK |
699 | |
700 | if (0 < uuidd_opts->num) { | |
701 | switch (uuidd_opts->do_type) { | |
702 | case UUIDD_OP_RANDOM_UUID: | |
703 | uuidd_opts->do_type = UUIDD_OP_BULK_RANDOM_UUID; | |
704 | break; | |
705 | case UUIDD_OP_TIME_UUID: | |
706 | uuidd_opts->do_type = UUIDD_OP_BULK_TIME_UUID; | |
707 | break; | |
708 | } | |
709 | } | |
084efcee SK |
710 | } |
711 | ||
712 | int main(int argc, char **argv) | |
713 | { | |
714 | const char *err_context = NULL; | |
715 | char *cp; | |
716 | int ret; | |
717 | ||
3cfba7d3 | 718 | struct uuidd_cxt_t uuidd_cxt = { .timeout = 0, .cont_clock_offset = 0 }; |
084efcee SK |
719 | struct uuidd_options_t uuidd_opts = { .socket_path = UUIDD_SOCKET_PATH }; |
720 | ||
721 | setlocale(LC_ALL, ""); | |
722 | bindtextdomain(PACKAGE, LOCALEDIR); | |
723 | textdomain(PACKAGE); | |
724 | close_stdout_atexit(); | |
725 | ||
726 | parse_options(argc, argv, &uuidd_cxt, &uuidd_opts); | |
0abfbd9c | 727 | |
c4a66fa9 | 728 | if (strlen(uuidd_opts.socket_path) >= sizeof_member(struct sockaddr_un, sun_path)) |
b03ffec1 | 729 | errx(EXIT_FAILURE, _("socket name too long: %s"), uuidd_opts.socket_path); |
eb10dbc1 | 730 | |
b03ffec1 SK |
731 | if (!uuidd_opts.no_pid && !uuidd_opts.pidfile_path) |
732 | uuidd_opts.pidfile_path = UUIDD_PIDFILE_PATH; | |
0abfbd9c | 733 | |
bbe289c4 | 734 | /* custom socket path and socket-activation make no sense */ |
b03ffec1 | 735 | if (uuidd_opts.s_flag && uuidd_cxt.no_sock && !uuidd_cxt.quiet) |
bcb693de | 736 | warnx(_("Both --socket-activation and --socket specified. " |
0012e33c | 737 | "Ignoring --socket.")); |
0abfbd9c | 738 | |
b03ffec1 | 739 | if (uuidd_opts.num && uuidd_opts.do_type) { |
b4911618 | 740 | char buf[UUIDD_PROT_BUFSZ]; |
b03ffec1 SK |
741 | char str[UUID_STR_LEN]; |
742 | ||
77a54655 | 743 | ret = call_daemon(uuidd_opts.socket_path, uuidd_opts.do_type, buf, |
b03ffec1 SK |
744 | sizeof(buf), &uuidd_opts.num, &err_context); |
745 | ||
bcb693de KZ |
746 | if (ret < 0) |
747 | err(EXIT_FAILURE, _("error calling uuidd daemon (%s)"), | |
57a24292 | 748 | err_context ? : _("unexpected error")); |
bcb693de | 749 | |
77a54655 | 750 | if (uuidd_opts.do_type == UUIDD_OP_BULK_TIME_UUID) { |
b03ffec1 | 751 | if (ret != sizeof(uuid_t) + sizeof(uuidd_opts.num)) |
2fb35353 | 752 | unexpected_size(ret); |
69045d3d KZ |
753 | |
754 | uuid_unparse((unsigned char *) buf, str); | |
755 | ||
0149cd84 | 756 | printf(P_("%s and %d subsequent UUID\n", |
b03ffec1 SK |
757 | "%s and %d subsequent UUIDs\n", uuidd_opts.num - 1), |
758 | str, uuidd_opts.num - 1); | |
69045d3d | 759 | } else { |
b03ffec1 SK |
760 | int i; |
761 | ||
333ec749 | 762 | printf(_("List of UUIDs:\n")); |
b4911618 | 763 | cp = buf + sizeof(uuidd_opts.num); |
b03ffec1 | 764 | if (ret != (int) (sizeof(uuidd_opts.num) + uuidd_opts.num * sizeof(uuid_t))) |
2fb35353 | 765 | unexpected_size(ret); |
b4911618 | 766 | for (i = 0; i < uuidd_opts.num; i++, cp += sizeof(uuid_t)) { |
69045d3d KZ |
767 | uuid_unparse((unsigned char *) cp, str); |
768 | printf("\t%s\n", str); | |
769 | } | |
770 | } | |
41dc5bc0 | 771 | return EXIT_SUCCESS; |
69045d3d | 772 | } |
b03ffec1 SK |
773 | |
774 | if (uuidd_opts.do_type) { | |
775 | uuid_t uu; | |
776 | char str[UUID_STR_LEN]; | |
777 | ||
778 | ret = call_daemon(uuidd_opts.socket_path, uuidd_opts.do_type, (char *) &uu, | |
69045d3d | 779 | sizeof(uu), 0, &err_context); |
b03ffec1 | 780 | |
bcb693de KZ |
781 | if (ret < 0) |
782 | err(EXIT_FAILURE, _("error calling uuidd daemon (%s)"), | |
57a24292 | 783 | err_context ? : _("unexpected error")); |
2fb35353 SK |
784 | if (ret != sizeof(uu)) |
785 | unexpected_size(ret); | |
786 | ||
69045d3d KZ |
787 | uuid_unparse(uu, str); |
788 | ||
789 | printf("%s\n", str); | |
41dc5bc0 | 790 | return EXIT_SUCCESS; |
69045d3d KZ |
791 | } |
792 | ||
b03ffec1 SK |
793 | if (uuidd_opts.do_kill) { |
794 | char buf[16]; | |
795 | ||
796 | ret = call_daemon(uuidd_opts.socket_path, UUIDD_OP_GETPID, buf, sizeof(buf), 0, NULL); | |
c14e0f62 SK |
797 | if (0 < ret) { |
798 | pid_t pid; | |
799 | ||
800 | pid = (pid_t)strtou32_or_err(buf, _("failed to parse pid")); | |
801 | ret = kill(pid, SIGTERM); | |
69045d3d | 802 | if (ret < 0) { |
4b1cf29d | 803 | if (!uuidd_cxt.quiet) |
bcb693de | 804 | warn(_("couldn't kill uuidd running " |
c14e0f62 | 805 | "at pid %d"), pid); |
41dc5bc0 | 806 | return EXIT_FAILURE; |
69045d3d | 807 | } |
4b1cf29d | 808 | if (!uuidd_cxt.quiet) |
c14e0f62 | 809 | printf(_("Killed uuidd running at pid %d.\n"), pid); |
69045d3d | 810 | } |
41dc5bc0 | 811 | return EXIT_SUCCESS; |
69045d3d KZ |
812 | } |
813 | ||
b03ffec1 | 814 | server_loop(uuidd_opts.socket_path, uuidd_opts.pidfile_path, &uuidd_cxt); |
41dc5bc0 | 815 | return EXIT_SUCCESS; |
69045d3d | 816 | } |