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