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