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