dtcm: make it build
[oweals/cde.git] / cde / programs / dtcm / server / svcmain.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: svcmain.c /main/10 1998/04/06 13:13:49 mgreess $ */
24 /*
25  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
26  *  (c) Copyright 1993, 1994 International Business Machines Corp.
27  *  (c) Copyright 1993, 1994 Novell, Inc.
28  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
29  */
30
31 #if defined(__linux__)
32 #define _POSIX_C_SOURCE 2
33 #endif
34
35 #include <EUSCompat.h>
36 #include <stdio.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <sys/resource.h>
44 #ifdef SVR4
45 #ifndef _NETINET_IN_H
46 #include <netinet/in.h>
47 #endif /* _NETINET_IN_H */
48 #endif
49
50 #if defined(SunOS)
51 #include <netconfig.h>
52 #include <netdir.h>
53 #include <sys/stropts.h>
54 #include <tiuser.h>
55 #endif /* SunOS */
56 #include <sys/param.h>
57 #include <sys/stat.h>
58 #include <fcntl.h>
59 #include <rpc/rpc.h>
60 #include <sys/file.h>
61 #include <sys/signal.h>
62 #include <pwd.h>
63 #include <grp.h>
64 #include "rpcextras.h"
65 #include "log.h"
66 #include "cmscalendar.h"
67 #include "repeat.h"
68 #include "lutil.h"
69 #include "cmsdata.h"
70
71 #ifndef S_IRWXU
72 #define S_IRWXU         (S_IRUSR|S_IWUSR|S_IXUSR)
73 #endif
74 #ifndef S_IRWXG
75 #define S_IRWXG         (S_IRGRP|S_IWGRP|S_IXGRP)
76 #endif
77 #ifndef S_IRWXO
78 #define S_IRWXO         (S_IROTH|S_IWOTH|S_IXOTH)
79 #endif
80
81 #define S_MASK  (S_INPUT|S_HIPRI|S_ERROR|S_HANGUP)
82
83 int debug;
84 static int standalone;                  /* default is 0 */
85 static int received_sighup = 0;         /* 1 means we get SIGHUP */
86 static int rpc_in_process = 0;          /* 1 means processing client request */
87 static int garbage_collection_time = 240; /* in min; default time is 4:00am */
88
89 char    *pgname;
90
91 uid_t daemon_uid;
92 gid_t daemon_gid;
93
94 /*
95  * get garbage collection time
96  * the given string should be in the format hhmm
97  * where hh is 0 - 23 and mm is 00 - 59
98  */
99 static int
100 _GetGtime(char *timestr)
101 {
102         int     hour, minute, len, i;
103
104         if (timestr == NULL)
105                 goto error;
106
107         if ((len = strlen(timestr)) > 4)
108                 goto error;
109
110         for (i = 0; i < len; i++) {
111                 if (timestr[i] < '0' || timestr[i] > '9')
112                         goto error;
113         }
114
115         minute = atoi(&timestr[len - 2]);
116         timestr[len - 2] = '\0';
117         hour = atoi(timestr);
118
119         if (hour > 23 || minute > 59)
120                 goto error;
121
122         garbage_collection_time = hour * 60 + minute;
123
124         return (0);
125
126 error:
127         fprintf(stderr, "The time specified is invalid.\n");
128         return (-1);
129 }
130
131 static void
132 parse_args(int argc, char **argv)
133 {
134         int     opt;
135
136         if (pgname = strrchr (argv[0], '/'))
137                 pgname++;
138         else
139                 pgname = argv[0];
140
141         while ((opt = getopt (argc, argv, "dsg:")) != -1)
142         {
143                 switch (opt)
144                 {
145                 case 'd':
146                         debug = 1;
147                         break;
148                 case 's':
149                         standalone = 1;
150                         break;
151                 case 'g':
152                         if (_GetGtime(optarg))
153                                 goto error;
154                         break;
155                 case '?':
156                         goto error;
157                 }
158         }
159
160         if (optind == argc)
161                 return;
162
163 #if defined(_aix)
164         /*
165          * rpc.cmsd gets started by the inetd.
166          * On AIX inetd requires that two arguments be supplied to the RPC
167          * programs as follows (from the inetd.conf man page):
168          *
169          * ServerArgs      Specifies the command line arguments that the
170          * inetd daemon should use to execute the server. The maximum number
171          * of arguments is five. The first argument specifies the name of the
172          * server used.  If the SocketType parameter is sunrpc_tcp or
173          * sunrpc_udp, * the second argument specifies the program name and
174          * the third argument specifies the version of the program. For
175          * services that the inetd daemon provides internally, this field
176          * should be empty.
177          */
178
179         else if (optind == 1 && argc >= 3)
180         {
181                 int i,j;
182                 char **argv_r;
183                 
184                 if (argc == 3)
185                   return;
186                   
187                 argv_r = (char **) malloc(argc * sizeof(char *));
188
189                 argv_r[0] = argv[0];
190                 for (i=optind+2, j=1; i<argc; i++,j++)
191                   argv_r[j] = argv[i];
192                 parse_args(argc-2, argv_r);
193
194                 free((void *) argv_r);
195                 return;
196         }
197 #endif
198                 
199 error:
200         fprintf (stderr, "Usage: %s [-d] [-s] [-g hhmm]\n", pgname);
201         exit (-1);
202 }
203
204 static void
205 init_dir(void)
206 {
207         char *dir = _DtCMS_DEFAULT_DIR;
208         char msgbuf[BUFSIZ];
209         int create_dir;
210         struct stat info;
211         mode_t mode;
212
213         if (geteuid() != 0)
214         {
215                 fprintf (stderr,
216                         "%s: must be run in super-user mode!  Exited.\n",
217                         pgname);
218                 exit (-1);
219         }
220
221         create_dir = 0;
222         if (stat(dir, &info))
223         {
224                 /* if directory does not exist, create the directory */
225                 if ((errno != ENOENT) || mkdir(dir, S_IRWXU|S_IRWXG|S_IRWXO))
226                 {
227                         if (errno == ENOENT)
228                                 sprintf(msgbuf, "%s: cannot create %s.\n%s: %s",
229                                         pgname, dir, pgname, "System error");
230                         else
231                                 sprintf(msgbuf, "%s: cannot access %s.\n%s: %s",
232                                         pgname, dir, pgname, "System error");
233                         perror (msgbuf);
234                         exit (-1);
235                 }
236                 create_dir = 1;
237         }
238
239         /* if dir is just created, we need to do chmod and chown.
240          * Otherwise, only do chmod and/or chown if permssion and/or
241          * ownership is wrong.
242          */
243         mode = S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO;
244
245         if (create_dir || info.st_mode != (mode | S_IFDIR)) {
246
247                 /* set directory permission to be "rwxrwsrwt" */
248                 if (chmod(dir, mode)) {
249                         sprintf(msgbuf, "%s: Permission on %s%s\n%s%s\n%s%s",
250                                 pgname, dir,
251                                 " is wrong but cannot be corrected.", pgname,
252                                 ": This might happen if you are mounting the directory.",
253                                 pgname, ": System error");
254                         perror(msgbuf);
255                         if (create_dir)
256                                 rmdir(dir);
257                         exit(-1);
258                 }
259         }
260
261         if (create_dir || info.st_uid!=daemon_uid || info.st_gid!=daemon_gid) {
262                 /* set directory ownership to: owner = 1, group = 1 */
263                 if (chown(dir, daemon_uid, daemon_gid)) {
264                         sprintf(msgbuf, "%s: Ownership on %s%s\n%s%s\n%s%s",
265                                 pgname, dir,
266                                 " is wrong but cannot be corrected.", pgname,
267                                 ": This might happen if you are mounting the directory.",
268                                 pgname, ": System error");
269                         perror(msgbuf);
270                         if (create_dir)
271                                 rmdir(dir);
272                         exit(-1);
273                 }
274         }
275
276         /* Change current directory, so core file can be dumped. */
277         if(-1 == chdir (dir)) {
278                 perror(strerror(errno));
279         }
280 }
281
282 /*
283  * send a SIGHUP signal to the rpc.cmsd that is already running
284  */
285 static void
286 send_hup(void)
287 {
288         FILE    *fp = NULL;
289         char    buf[BUFSIZ];
290         pid_t   pid, mypid = getpid();
291         extern FILE *popen(const char *, const char *);
292         extern int pclose(FILE *);
293
294         sprintf(buf, "ps -e|grep rpc.cmsd|grep -v grep");
295
296         if ((fp = popen(buf, "r")) == NULL) {
297                 if (debug)
298                         fprintf(stderr, "rpc.cmsd: popen failed\n");
299         } else {
300                 while (fgets(buf, sizeof(buf), fp) != NULL) {
301                         if ((pid = atol(buf)) != mypid) {
302                                 if (kill(pid, SIGHUP))
303                                     perror("rpc.cmsd: failed to send SIGHUP");
304                                 if (debug)
305                                     fprintf(stderr, "rpc.cmsd: %s %ld\n",
306                                                 "sent SIGHUP to", (long)pid);
307                         }
308                 }
309                 pclose(fp);
310         }
311 }
312
313 /*
314  * We only allow one rpc.cmsd to run on each machine.
315  */
316 static int
317 lock_it(void)
318 {
319         char *dir = _DtCMS_DEFAULT_DIR;
320         char    buff [MAXPATHLEN];
321         int     error;
322         int     fd;
323 #ifdef SVR4
324         struct flock locker;
325         locker.l_type = F_WRLCK;
326         locker.l_whence = 0;
327         locker.l_start = 0;
328         locker.l_len = 0;
329 #endif /* SVR4 */
330
331         /* 
332          * /var/spool might be mounted.  Use .lock.hostname to
333          * prevent more than one cms running in each host.
334          */
335         snprintf(buff, sizeof(buff), "%s/.lock.%s", dir, _DtCmGetLocalHost());
336
337         fd = open(buff, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
338         if (fd < 0)
339         {
340                 perror (buff);
341                 exit (-1);
342         }
343
344         /*
345          * Note, we have to use flock() instead of lockf() because cms process
346          * is run in each host.
347          */
348 #ifdef SVR4
349         if (fcntl (fd, F_SETLK, &locker) != 0)
350 #else
351         if (flock (fd, LOCK_EX|LOCK_NB) != 0)
352 #endif /* SVR4 */
353         {
354                 error = errno;
355
356                 close(fd);
357
358                 if (error != EWOULDBLOCK && error != EACCES) {
359
360                         perror ("rpc.cmsd: failed to lock lockfile");
361                         fprintf(stderr, "error = %d\n", error);
362                         exit (-1);
363
364                 } else {
365                         if (debug)
366                                 fprintf(stderr, "rpc.cmsd: %s\n",
367                                     "lock_it failed due to another process");
368                         
369                         /* cms has been running.... */
370                         return(error);
371                 }
372         }
373
374         return (0);
375 }
376
377 static void
378 program(struct svc_req *rqstp, SVCXPRT *transp)
379 {
380         char *result;
381         char *argument = NULL;
382         program_handle ph = getph();
383         struct rpcgen_table *proc;
384
385         /* set rpc_in_process so that sighup handler won't exit right away */
386         rpc_in_process = 1;
387  
388         /* first do some bounds checking: */
389         if (rqstp->rq_vers >= ph->nvers) {
390                 svcerr_noproc(transp);
391                 goto done;
392         }
393         if (ph->prog[rqstp->rq_vers].nproc == 0) {
394                 svcerr_noproc(transp);
395                 goto done;
396         }
397         if (rqstp->rq_proc >= ph->prog[rqstp->rq_vers].nproc) {
398                 svcerr_noproc(transp);
399                 goto done;
400         }
401
402         if (rqstp->rq_proc == NULLPROC) {
403                 if (debug) fprintf(stderr, "rpc.cmsd: ping\n");
404                 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL);
405                 goto done;
406         }
407
408         /* assert - the program number, version and proc numbers are valid */
409         proc = &(ph->prog[rqstp->rq_vers].vers[rqstp->rq_proc]);
410         argument = (char*)calloc(proc->len_arg, sizeof(char));
411         if (!svc_getargs(transp, proc->xdr_arg, argument)) {
412                 svcerr_decode(transp);
413                 goto done;
414         }
415
416         result = (*proc->proc)(argument, rqstp);
417         if (result != NULL && !svc_sendreply(transp, proc->xdr_res, result)) {
418                 svcerr_systemerr(transp);
419         }
420
421         if (!svc_freeargs(transp, proc->xdr_arg, argument)) {
422                 (void)fprintf(stderr, "unable to free arguments");
423                 exit(1);
424         }
425         free(argument);
426
427 done:
428         rpc_in_process = 0;
429
430         /* exit if we have received the SIGHUP signal */
431         if (received_sighup == 1) {
432                 if (debug)
433                         fprintf(stderr, "rpc.cmsd: received SIGHUP, %s",
434                                 "exiting after finished processing\n");
435                 exit(0);
436         }
437 }
438
439 /*
440  * Signal handler for SIGHUP.
441  * If we are in the middle of processing a client request,
442  * finish processing before we exit.
443  */
444 static void
445 sighup_handler(int sig_num)
446 {
447         if (debug)
448                 fprintf(stderr, "rpc.cmsd: sighup received\n");
449
450         if (rpc_in_process == 0) {
451                 if (debug)
452                         fprintf(stderr, "rpc.cmsd: exit from sighup_handler\n");
453                 exit(0);
454         } else {
455                 if (debug)
456                         fprintf(stderr, "rpc.cmsd: set received_sighup to 1\n");
457                 received_sighup = 1;
458         }
459 }
460
461 /*
462  * garbage_collection_time (in min) is the time to do garbage collection
463  * each day
464  * This routine returns the difference between the first garbage collection
465  * time and now so that the calling routine can set the alarm.
466  */
467 static int
468 _GetFirstGarbageCollectionTime(void)
469 {
470         int n=0, midnight=0, gtime=0;
471
472         n = time(0);
473
474         /* try today first */
475         midnight = next_ndays(n, 0);
476         gtime = next_nmins(midnight, garbage_collection_time);
477
478         if (gtime < n) {
479                 /* the first garbage collection will be done tomorrow */
480                 midnight = next_ndays(n, 1);
481                 gtime = next_nmins(midnight, garbage_collection_time);
482         }
483
484         return (gtime - n);
485 }
486
487 static void
488 init_alarm(void)
489 {
490         int next;
491         extern void garbage_collect();
492         extern void debug_switch();
493
494 #if defined(SVR4) && !defined(__linux__)
495         extern void (*sigset(int, void (*)(int)))(int);
496         sigset(SIGUSR1, garbage_collect);
497         sigset(SIGALRM, garbage_collect);
498         sigset(SIGUSR2, debug_switch);
499 #else
500         signal(SIGUSR1, garbage_collect);
501         signal(SIGALRM, garbage_collect);
502         signal(SIGUSR2, debug_switch);
503 #endif /* SVR4 */
504
505         next = _GetFirstGarbageCollectionTime();
506         alarm((unsigned) next);
507 }
508
509 int
510 main(int argc, char **argv)
511 {
512         u_long version;
513         program_handle ph = newph();
514         struct passwd *pw;
515         struct group *gr;
516         struct rlimit rl;
517         struct sockaddr_in saddr;
518         int asize = sizeof (saddr);
519         SVCXPRT *tcp_transp = (SVCXPRT *)-1;
520         SVCXPRT *udp_transp = (SVCXPRT *)-1;
521         int     fd, error;
522
523 #if defined(SunOS)
524         struct netconfig *nconf_udp;
525         struct netconfig *nconf_tcp;
526         struct t_info info;
527         char mname[FMNAMESZ+1];
528 #endif /* SunOS */
529
530         pw = (struct passwd *)getpwnam("daemon");
531         gr = (struct group *)getgrnam("daemon");
532         if (pw != NULL) 
533                 daemon_uid = pw->pw_uid;
534         else
535                 daemon_uid = 1;
536         if (gr != NULL)
537                 daemon_gid = gr->gr_gid;
538         else 
539                 daemon_gid = 1;
540
541         parse_args(argc, argv);
542
543         /* check to see if we are started by inetd */
544         if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
545
546                 standalone = 0;
547
548 #if defined(SunOS)
549                 /* we need a TLI endpoint rather than a socket */
550                 if (ioctl(0, I_LOOK, mname) != 0) {
551                         perror("rpc.cmsd: ioctl failed to get module name");
552                         exit(1);
553                 }
554                 if (strcmp(mname, "sockmod") == 0) {
555                         /* Change socket fd to TLI fd */
556                         if (ioctl(0, I_POP, 0) || ioctl(0, I_PUSH, "timod")) {
557                                 perror("rpc.cmsd: ioctl I_POP/I_PUSH failed");
558                                 exit(1);
559                         }
560                 } else if (strcmp(mname, "timod") != 0) {
561                         fprintf(stderr, "rpc.cmsd: fd 0 is not timod\n");
562                         exit(1);
563                 }
564
565         } else if (t_getinfo(0, &info) == 0) {
566                 standalone = 0;
567
568 #endif /* SunOS */
569
570         } else
571                 standalone = 1;
572
573         /*
574          * if it is started by inetd, make stderr to be
575          * output to console.
576          */
577         if (!standalone) {
578                 if ((fd = open ("/dev/console", O_WRONLY)) >= 0) {
579                         if (fd != 2) {
580                                 dup2(fd, 2);
581                                 close (fd);
582                         }
583                 }
584         }
585
586         /* Set up private directory and switch euid/egid to daemon. */
587         umask (S_IWOTH);
588         init_dir();
589
590         /* Don't allow multiple cms processes running in the same host. */
591         if ((error = lock_it()) != 0 && !standalone) {
592                 /* we are invoked by inetd but another rpc.cmsd
593                  * is alreay running, so send SIGHUP to it
594                  */
595
596                 send_hup();
597
598                 /* try to lock it again */
599                 if (lock_it() != 0) {
600                         if (debug)
601                                 fprintf(stderr, "cm: rpc.cmsd is still running\n");
602                         exit(0);
603                 }
604
605         } else if (error != 0) {
606                 fprintf(stderr, "rpc.cmsd: rpc.cmsd is already running.\n");
607                 exit(0);
608         }
609
610         /* use signal because we only need it once */
611         signal(SIGHUP, sighup_handler);
612
613
614 #if defined(SunOS)
615         /* raise the soft limit of number of file descriptor */
616         /* this is to prevent the backend from running out of open file des */
617         getrlimit(RLIMIT_NOFILE, &rl);
618         rl.rlim_cur = (rl.rlim_max <= 256) ? rl.rlim_max : 256;
619         setrlimit(RLIMIT_NOFILE, &rl);
620 #endif
621
622 #if defined(SunOS)
623         nconf_udp = getnetconfigent("udp");
624         nconf_tcp = getnetconfigent("tcp");
625
626         for (version = 0; version < ph->nvers; version++) {
627                 /* don't register unsupported versions: */
628                 if (ph->prog[version].nproc == 0) continue;
629
630                 if (standalone) {
631                         rpcb_unset(ph->program_num, version, NULL);
632                         if (debug)
633                                 fprintf(stderr,
634                                         "rpc.cmsd: rpcb_unset for version %ld\n",
635                                         version);
636                 }
637
638                 /* brought up by inetd, use fd 0 which must be a TLI fd */
639                 if (udp_transp == (SVCXPRT *)-1) {
640                         udp_transp = svc_tli_create(standalone ? RPC_ANYFD : 0,
641                                 nconf_udp, (struct t_bind*) NULL, 0, 0); 
642
643                         if (udp_transp == NULL) {
644                                 t_error("rtable_main.c: svc_tli_create(udp)");
645                                 exit(2);
646                         }
647                 }
648
649                 if (svc_reg(udp_transp, ph->program_num, version, program,
650                                 standalone ? nconf_udp : NULL) == 0) {
651                         t_error("rtable_main.c: svc_reg");
652                         exit(3);
653                 }
654
655                 /* Set up tcp for calls that potentially return */
656                 /* large amount of data.  This transport is not */
657                 /* registered with inetd so need to register it */
658                 /* with rpcbind ourselves.                      */
659
660                 rpcb_unset(ph->program_num, version, nconf_tcp);
661
662                 if (tcp_transp == (SVCXPRT *)-1) {
663                         tcp_transp = svc_tli_create(RPC_ANYFD, nconf_tcp,
664                                         (struct t_bind *)NULL, 0, 0);
665
666                         if (tcp_transp == NULL) {
667                                 t_error("rtable_main.c: svc_til_create(tcp)");
668                                 exit(2);
669                         }
670                 }
671
672                 if (svc_reg(tcp_transp, ph->program_num, version, program,
673                                 nconf_tcp) == 0) {
674                         t_error("rtable_main.c: svc_reg(tcp)");
675                         exit(3);
676                 }
677         }/*for*/
678
679         if (nconf_udp)
680                 freenetconfigent(nconf_udp);
681         if (nconf_tcp)
682                 freenetconfigent(nconf_tcp);
683
684 #else
685
686         for (version = 0; version < ph->nvers; version++) {
687                 /* don't register unsupported versions: */
688                 if (ph->prog[version].nproc == 0) continue;
689
690 #ifndef HPUX
691                 if (standalone)
692 #endif
693                         (void) pmap_unset(ph->program_num, version);
694
695                 if (udp_transp == (SVCXPRT *)-1) {
696                         udp_transp = svcudp_create(standalone ? RPC_ANYSOCK : 0
697 #if defined(_AIX) || defined(hpV4) || defined(__linux__) || defined(CSRG_BASED)
698                                         );
699 #else
700                                         ,0,0);
701 #endif
702                         if (udp_transp == NULL) {
703                                 (void)fprintf(stderr,
704                                 "rtable_main.c: cannot create udp service.\n");
705                                 exit(1);
706                         }
707                 }
708
709 #ifndef HPUX
710                 if (!svc_register(udp_transp, ph->program_num, version, program,
711                                 standalone ? IPPROTO_UDP : 0)) {
712 #else
713                 if (!svc_register(udp_transp, ph->program_num, version, program,
714                                 IPPROTO_UDP)) {
715 #endif
716                         (void)fprintf(stderr, "rtable_main.c: unable to register");
717                         exit(1);
718                 }
719
720                 /* Set up tcp for calls that potentially return */
721                 /* large amount of data.  This transport is not */
722                 /* registered with inetd so need to register it */
723                 /* with rpcbind ourselves.                      */
724
725                 if (tcp_transp == (SVCXPRT *)-1) {
726                         tcp_transp = svctcp_create(RPC_ANYSOCK, 0, 0);
727                         if (tcp_transp == NULL) {
728                                 (void)fprintf(stderr,
729                                 "rtable_main.c: cannot create tcp service.\n");
730                                 exit(1);
731                         }
732                 }
733
734                 if (!svc_register(tcp_transp, ph->program_num, version, program,
735                                 IPPROTO_TCP)) {
736                         (void)fprintf(stderr, "rtable_main.c: unable to register(tcp)");
737                         exit(1);
738                 }
739         }
740
741 #endif /* SunOS */
742
743 #ifndef AIX
744 #ifdef HPUX
745         setgid (daemon_gid);
746         setuid (daemon_uid);
747 #else
748         if(-1 == setegid (daemon_gid)) {
749                 perror(strerror(errno));
750         }
751         if(-1 == seteuid (daemon_uid)) {
752                 perror(strerror(errno));
753         }
754 #endif /* HPUX */
755 #endif /* AIX */
756
757         init_time();
758         init_alarm();
759         _DtCm_init_hash();
760
761         svc_run();
762
763         (void)fprintf(stderr, "rpc.cmsd: svc_run returned\n");
764         return(1);
765 }
766