blob: 19c862465328208771afa7e61adda7b7de96352d [file] [log] [blame]
Theodore Ts'o740837d2007-12-16 17:21:38 -05001/*
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
12#include <stdio.h>
13#ifdef HAVE_STDLIB_H
14#include <stdlib.h>
15#endif
16#include <unistd.h>
17#include <inttypes.h>
18#include <errno.h>
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <sys/socket.h>
22#include <sys/un.h>
23#include <fcntl.h>
24#include <signal.h>
25#ifdef HAVE_GETOPT_H
26#include <getopt.h>
27#else
28extern int getopt(int argc, char * const argv[], const char *optstring);
29extern char *optarg;
30extern int optind;
31#endif
32#include "uuid/uuid.h"
33#include "uuid/uuidd.h"
34#include "nls-enable.h"
35
36#ifdef __GNUC__
37#define CODE_ATTR(x) __attribute__(x)
38#else
39#define CODE_ATTR(x)
40#endif
41
42static void usage(const char *progname)
43{
44 fprintf(stderr, _("Usage: %s [-d] [-p pidfile] [-s socketpath] "
45 "[-T timeout]\n"), progname);
46 fprintf(stderr, _(" %s [-r|t] [-n num] [-s socketpath]\n"),
47 progname);
48 fprintf(stderr, _(" %s -k\n"), progname);
49 exit(1);
50}
51
52static void create_daemon(const char *pidfile_path)
53{
54 pid_t pid;
55 uid_t euid;
56 FILE *f;
57
58 pid = fork();
59 if (pid == -1) {
60 perror("fork");
61 exit(1);
62 } else if (pid != 0) {
63 exit(0);
64 }
65
66 close(0);
67 close(1);
68 close(2);
69 open("/dev/null", O_RDWR);
70 open("/dev/null", O_RDWR);
71 open("/dev/null", O_RDWR);
72
73 chdir("/");
74 (void) setsid();
75 euid = geteuid();
76 (void) setreuid(euid, euid);
77
78 f = fopen(pidfile_path, "w");
79 if (f) {
80 fprintf(f, "%d\n", getpid());
81 fclose(f);
82 }
83}
84
85static int read_all(int fd, char *buf, size_t count)
86{
87 ssize_t ret;
88 int c = 0;
89
90 memset(buf, 0, count);
91 while (count > 0) {
92 ret = read(fd, buf, count);
93 if (ret < 0) {
94 if ((errno == EAGAIN) || (errno == EINTR))
95 continue;
96 return -1;
97 }
98 count -= ret;
99 buf += ret;
100 c += ret;
101 }
102 return c;
103}
104
105static const char *cleanup_pidfile, *cleanup_socket;
106
107static void terminate_intr(int signo CODE_ATTR((unused)))
108{
109 (void) unlink(cleanup_pidfile);
110 (void) unlink(cleanup_socket);
111 exit(0);
112}
113
114static void server_loop(const char *socket_path, int debug,
115 const char *pidfile_path,
116 int timeout, int quiet)
117{
118 struct sockaddr_un my_addr, from_addr;
119 unsigned char reply_buf[1024], *cp;
120 socklen_t fromlen;
121 int32_t reply_len = 0;
122 uuid_t uu;
123 mode_t save_umask;
124 char op, str[37];
125 int i, s, ns, len, num;
126
127 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
128 if (!quiet)
129 fprintf(stderr, _("Couldn't create unix stream "
130 "socket: %s"), strerror(errno));
131 exit(1);
132 }
133
134 /*
135 * Create the address we will be binding to.
136 */
137 my_addr.sun_family = AF_UNIX;
138 strcpy(my_addr.sun_path, socket_path);
139 (void) unlink(socket_path);
140 save_umask = umask(0);
141 if (bind(s, (const struct sockaddr *) &my_addr,
142 sizeof(struct sockaddr_un)) < 0) {
143 if (!quiet)
144 fprintf(stderr,
145 _("Couldn't bind unix socket %s: %s\n"),
146 socket_path, strerror(errno));
147 exit(1);
148 }
149 (void) umask(save_umask);
150
151 if (listen(s, 5) < 0) {
152 if (!quiet)
153 fprintf(stderr, _("Couldn't listen on unix "
154 "socket %s: %s\n"), socket_path,
155 strerror(errno));
156 exit(1);
157 }
158
159 if (!debug) {
160 create_daemon(pidfile_path);
161 cleanup_pidfile = pidfile_path;
162 cleanup_socket = socket_path;
163 signal(SIGHUP, terminate_intr);
164 signal(SIGINT, terminate_intr);
165 signal(SIGPIPE, terminate_intr);
166 signal(SIGTERM, terminate_intr);
167 signal(SIGALRM, terminate_intr);
168 }
169 signal(SIGPIPE, SIG_IGN);
170
171 while (1) {
172 fromlen = sizeof(from_addr);
173 if (timeout > 0)
174 alarm(timeout);
175 ns = accept(s, (struct sockaddr *) &from_addr, &fromlen);
176 alarm(0);
177 if (ns < 0) {
178 if ((errno == EAGAIN) || (errno == EINTR))
179 continue;
180 perror("accept");
181 exit(1);
182 }
183 len = read(ns, &op, 1);
184 if (len != 1) {
185 if (len < 0)
186 perror("read");
187 else
188 printf(_("Error reading from client, "
189 "len = %d\n"), len);
190 goto shutdown_socket;
191 }
192 if ((op == 4) || (op == 5)) {
193 if (read_all(ns, (char *) &num, sizeof(num)) != 4)
194 goto shutdown_socket;
195 if (debug)
196 printf(_("operation %d, incoming num = %d\n"),
197 op, num);
198 } else if (debug)
199 printf("operation %d\n", op);
200
201 switch(op) {
202 case UUIDD_OP_GETPID:
203 sprintf((char *) reply_buf, "%d", getpid());
204 reply_len = strlen((char *) reply_buf)+1;
205 break;
206 case UUIDD_OP_GET_MAXOP:
207 sprintf((char *) reply_buf, "%d", UUIDD_MAX_OP);
208 reply_len = strlen((char *) reply_buf)+1;
209 break;
210 case UUIDD_OP_TIME_UUID:
211 num = 1;
212 uuid__generate_time(uu, &num);
213 if (debug) {
214 uuid_unparse(uu, str);
215 printf(_("Generated time UUID: %s\n"), str);
216 }
217 memcpy(reply_buf, uu, sizeof(uu));
218 reply_len = sizeof(uu);
219 break;
220 case UUIDD_OP_RANDOM_UUID:
221 num = 1;
222 uuid__generate_random(uu, &num);
223 if (debug) {
224 uuid_unparse(uu, str);
225 printf(_("Generated random UUID: %s\n"), str);
226 }
227 memcpy(reply_buf, uu, sizeof(uu));
228 reply_len = sizeof(uu);
229 break;
230 case UUIDD_OP_BULK_TIME_UUID:
231 uuid__generate_time(uu, &num);
232 if (debug) {
233 uuid_unparse(uu, str);
234 printf(_("Generated time UUID %s and %d "
235 "following\n"), str, num);
236 }
237 memcpy(reply_buf, uu, sizeof(uu));
238 reply_len = sizeof(uu);
239 memcpy(reply_buf+reply_len, &num, sizeof(num));
240 reply_len += sizeof(num);
241 break;
242 case UUIDD_OP_BULK_RANDOM_UUID:
243 if (num < 0)
244 num = 1;
245 if (num > 1000)
246 num = 1000;
247 if (num*16 > (int) (sizeof(reply_buf)-sizeof(num)))
248 num = (sizeof(reply_buf)-sizeof(num)) / 16;
249 uuid__generate_random(reply_buf+sizeof(num), &num);
250 if (debug) {
251 printf(_("Generated %d UUID's:\n"), num);
252 for (i=0, cp=reply_buf+sizeof(num);
253 i < num; i++, cp+=16) {
254 uuid_unparse(cp, str);
255 printf("\t%s\n", str);
256 }
257 }
258 reply_len = (num*16) + sizeof(num);
259 memcpy(reply_buf, &num, sizeof(num));
260 break;
261 default:
262 if (debug)
263 printf(_("Invalid operation %d\n"), op);
264 goto shutdown_socket;
265 }
266 write(ns, &reply_len, sizeof(reply_len));
267 write(ns, reply_buf, reply_len);
268 shutdown_socket:
269 close(ns);
270 }
271}
272
273static int call_daemon(const char *socket_path, int op, unsigned char *buf,
274 int buflen, int *num, const char **err_context)
275{
276 char op_buf[8];
277 int op_len;
278 int s;
279 ssize_t ret;
280 int32_t reply_len = 0;
281 struct sockaddr_un srv_addr;
282
283 if (((op == 4) || (op == 5)) && !num) {
284 if (err_context)
285 *err_context = _("bad arguments");
286 errno = EINVAL;
287 return -1;
288 }
289
290 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
291 if (err_context)
292 *err_context = _("socket");
293 return -1;
294 }
295
296 srv_addr.sun_family = AF_UNIX;
297 strcpy(srv_addr.sun_path, socket_path);
298
299 if (connect(s, (const struct sockaddr *) &srv_addr,
300 sizeof(struct sockaddr_un)) < 0) {
301 if (err_context)
302 *err_context = _("connect");
303 close(s);
304 return -1;
305 }
306
307 if (op == 5) {
308 if ((*num)*16 > buflen-4)
309 *num = (buflen-4) / 16;
310 }
311 op_buf[0] = op;
312 op_len = 1;
313 if ((op == 4) || (op == 5)) {
314 memcpy(op_buf+1, num, sizeof(int));
315 op_len += sizeof(int);
316 }
317
318 ret = write(s, op_buf, op_len);
319 if (ret < op_len) {
320 if (err_context)
321 *err_context = _("write");
322 close(s);
323 return -1;
324 }
325
326 ret = read_all(s, (char *) &reply_len, sizeof(reply_len));
327 if (ret < 0) {
328 if (err_context)
329 *err_context = _("read count");
330 close(s);
331 return -1;
332 }
333 if (reply_len < 0 || reply_len > buflen) {
334 if (err_context)
335 *err_context = _("bad response length");
336 close(s);
337 return -1;
338 }
339 ret = read_all(s, (char *) buf, reply_len);
340
341 if ((ret > 0) && (op == 4)) {
342 if (reply_len >= (int) (16+sizeof(int)))
343 memcpy(buf+16, num, sizeof(int));
344 else
345 *num = -1;
346 }
347 if ((ret > 0) && (op == 5)) {
348 if (*num >= (int) sizeof(int))
349 memcpy(buf, num, sizeof(int));
350 else
351 *num = -1;
352 }
353
354 close(s);
355
356 return ret;
357}
358
359
360int main(int argc, char **argv)
361{
362 const char *socket_path = UUIDD_SOCKET_PATH;
363 const char *pidfile_path = UUIDD_PIDFILE_PATH;
364 const char *err_context;
365 unsigned char buf[1024], *cp;
366 char str[37], *tmp;
367 uuid_t uu;
368 uid_t uid;
369 gid_t gid;
370 int i, c, ret;
371 int debug = 0, do_type = 0, do_kill = 0, num = 0;
372 int timeout = 0, quiet = 0, drop_privs = 0;
373
374#ifdef ENABLE_NLS
375 setlocale(LC_MESSAGES, "");
376 setlocale(LC_CTYPE, "");
377 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
378 textdomain(NLS_CAT_NAME);
379#endif
380
381 while ((c = getopt (argc, argv, "dkn:qp:s:tT:r")) != EOF) {
382 switch (c) {
383 case 'd':
384 debug++;
385 drop_privs++;
386 break;
387 case 'k':
388 do_kill++;
389 drop_privs++;
390 break;
391 case 'n':
392 num = strtol(optarg, &tmp, 0);
393 if ((num < 0) || *tmp) {
394 fprintf(stderr, _("Bad number: %s\n"), optarg);
395 exit(1);
396 }
397 case 'p':
398 pidfile_path = optarg;
399 drop_privs++;
400 break;
401 case 'q':
402 quiet++;
403 break;
404 case 's':
405 socket_path = optarg;
406 drop_privs++;
407 break;
408 case 't':
409 do_type = UUIDD_OP_TIME_UUID;
410 drop_privs++;
411 break;
412 case 'T':
413 timeout = strtol(optarg, &tmp, 0);
414 if ((timeout < 0) || *tmp) {
415 fprintf(stderr, _("Bad number: %s\n"), optarg);
416 exit(1);
417 }
418 break;
419 case 'r':
420 do_type = UUIDD_OP_RANDOM_UUID;
421 drop_privs++;
422 break;
423 default:
424 usage(argv[0]);
425 }
426 }
427 uid = getuid();
428 if (uid && drop_privs) {
429 gid = getgid();
430#ifdef HAVE_SETRESUID
431 setresuid(uid, uid, uid);
432#else
433 setreuid(uid, uid);
434#endif
435#ifdef HAVE_SETRESGID
436 setresgid(gid, gid, gid);
437#else
438 setregid(gid, gid);
439#endif
440 }
441 if (num && do_type) {
442 ret = call_daemon(socket_path, do_type+2, buf,
443 sizeof(buf), &num, &err_context);
444 if (ret < 0) {
445 printf(_("Error calling uuidd daemon (%s): %s\n"),
446 err_context, strerror(errno));
447 exit(1);
448 }
449 if (do_type == UUIDD_OP_TIME_UUID) {
450 if (ret != sizeof(uu) + sizeof(num))
451 goto unexpected_size;
452
453 uuid_unparse(buf, str);
454
455 printf(_("%s and subsequent %d UUID's\n"), str, num);
456 } else {
457 printf(_("List of UUID's:\n"));
458 cp = buf + 4;
459 if (ret != sizeof(num) + num*sizeof(uu))
460 goto unexpected_size;
461 for (i=0; i < num; i++, cp+=16) {
462 uuid_unparse(cp, str);
463 printf("\t%s\n", str);
464 }
465 }
466 exit(0);
467 }
468 if (do_type) {
469 ret = call_daemon(socket_path, do_type, (unsigned char *) &uu,
470 sizeof(uu), 0, &err_context);
471 if (ret < 0) {
472 printf(_("Error calling uuidd daemon (%s): %s\n"),
473 err_context, strerror(errno));
474 exit(1);
475 }
476 if (ret != sizeof(uu)) {
477 unexpected_size:
478 printf(_("Unexpected reply length from server %d\n"),
479 ret);
480 exit(1);
481 }
482 uuid_unparse(uu, str);
483
484 printf("%s\n", str);
485 exit(0);
486 }
487
488 /*
489 * Check to make sure there isn't another daemon running already
490 */
491 ret = call_daemon(socket_path, 0, buf, sizeof(buf), 0, 0);
492 if (ret > 0) {
493 if (do_kill && ((do_kill = atoi((char *) buf)) > 0)) {
494 ret = kill(do_kill, SIGTERM);
495 if (ret < 0) {
496 if (!quiet)
497 fprintf(stderr,
498 _("Couldn't kill uuidd running "
499 "at pid %d: %s\n"), do_kill,
500 strerror(errno));
501 exit(1);
502 }
503 if (!quiet)
504 printf(_("Killed uuidd running at pid %d\n"),
505 do_kill);
506 exit(0);
507 }
508 if (!quiet)
509 printf(_("uuidd daemon already running at pid %s\n"),
510 buf);
511 exit(1);
512 }
513 if (do_kill)
514 exit(0); /* Nothing to kill */
515
516 server_loop(socket_path, debug, pidfile_path, timeout, quiet);
517 return 0;
518}