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