amq.c revision 51300
1/*
2 * Copyright (c) 1997-1999 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgment:
21 *      This product includes software developed by the University of
22 *      California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *      %W% (Berkeley) %G%
40 *
41 * $Id: amq.c,v 1.6 1999/09/08 23:36:40 ezk Exp $
42 * $FreeBSD: head/contrib/amd/amq/amq.c 51300 1999-09-15 05:45:17Z obrien $
43 *
44 */
45
46/*
47 * Automounter query tool
48 */
49
50#ifndef lint
51char copyright[] = "\
52@(#)Copyright (c) 1997-1999 Erez Zadok\n\
53@(#)Copyright (c) 1990 Jan-Simon Pendry\n\
54@(#)Copyright (c) 1990 Imperial College of Science, Technology & Medicine\n\
55@(#)Copyright (c) 1990 The Regents of the University of California.\n\
56@(#)All rights reserved.\n";
57#if __GNUC__ < 2
58static char rcsid[] = "$Id: amq.c,v 1.6 1999/09/08 23:36:40 ezk Exp $";
59static char sccsid[] = "%W% (Berkeley) %G%";
60#endif /* __GNUC__ < 2 */
61#endif /* not lint */
62
63#ifdef HAVE_CONFIG_H
64# include <config.h>
65#endif /* HAVE_CONFIG_H */
66#include <am_defs.h>
67#include <amq.h>
68
69/* locals */
70static int flush_flag;
71static int minfo_flag;
72static int getpid_flag;
73static int unmount_flag;
74static int stats_flag;
75static int getvers_flag;
76static int amd_program_number = AMQ_PROGRAM;
77static int use_tcp_flag, use_udp_flag;
78static char *debug_opts;
79static char *amq_logfile;
80static char *mount_map;
81static char *xlog_optstr;
82static char localhost[] = "localhost";
83static char *def_server = localhost;
84
85/* externals */
86extern int optind;
87extern char *optarg;
88
89/* forward declarations */
90#ifdef HAVE_TRANSPORT_TYPE_TLI
91static CLIENT *get_secure_amd_client(char *host, struct timeval *tv, int *sock);
92static int amq_bind_resv_port(int td, u_short *pp);
93#else /* not HAVE_TRANSPORT_TYPE_TLI */
94static int privsock(int ty);
95#endif /* not HAVE_TRANSPORT_TYPE_TLI */
96
97/* structures */
98enum show_opt {
99  Full, Stats, Calc, Short, ShowDone
100};
101
102
103/*
104 * If (e) is Calc then just calculate the sizes
105 * Otherwise display the mount node on stdout
106 */
107static void
108show_mti(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *twid)
109{
110  switch (e) {
111  case Calc:
112    {
113      int mw = strlen(mt->mt_mountinfo);
114      int dw = strlen(mt->mt_directory);
115      int tw = strlen(mt->mt_type);
116      if (mw > *mwid)
117	*mwid = mw;
118      if (dw > *dwid)
119	*dwid = dw;
120      if (tw > *twid)
121	*twid = tw;
122    }
123  break;
124
125  case Full:
126    {
127      struct tm *tp = localtime((time_t *) &mt->mt_mounttime);
128      printf("%-*.*s %-*.*s %-*.*s %s\n\t%-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n",
129	     *dwid, *dwid,
130	     *mt->mt_directory ? mt->mt_directory : "/",	/* XXX */
131	     *twid, *twid,
132	     mt->mt_type,
133	     *mwid, *mwid,
134	     mt->mt_mountinfo,
135	     mt->mt_mountpoint,
136
137	     mt->mt_mountuid,
138	     mt->mt_getattr,
139	     mt->mt_lookup,
140	     mt->mt_readdir,
141	     mt->mt_readlink,
142	     mt->mt_statfs,
143
144	     tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year,
145	     tp->tm_mon + 1, tp->tm_mday,
146	     tp->tm_hour, tp->tm_min, tp->tm_sec);
147    }
148  break;
149
150  case Stats:
151    {
152      struct tm *tp = localtime((time_t *) &mt->mt_mounttime);
153      printf("%-*.*s %-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n",
154	     *dwid, *dwid,
155	     *mt->mt_directory ? mt->mt_directory : "/",	/* XXX */
156
157	     mt->mt_mountuid,
158	     mt->mt_getattr,
159	     mt->mt_lookup,
160	     mt->mt_readdir,
161	     mt->mt_readlink,
162	     mt->mt_statfs,
163
164	     tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year,
165	     tp->tm_mon + 1, tp->tm_mday,
166	     tp->tm_hour, tp->tm_min, tp->tm_sec);
167    }
168  break;
169
170  case Short:
171    {
172      printf("%-*.*s %-*.*s %-*.*s %s\n",
173	     *dwid, *dwid,
174	     *mt->mt_directory ? mt->mt_directory : "/",
175	     *twid, *twid,
176	     mt->mt_type,
177	     *mwid, *mwid,
178	     mt->mt_mountinfo,
179	     mt->mt_mountpoint);
180    }
181  break;
182
183  default:
184    break;
185  }
186}
187
188/*
189 * Display a mount tree.
190 */
191static void
192show_mt(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *pwid)
193{
194  while (mt) {
195    show_mti(mt, e, mwid, dwid, pwid);
196    show_mt(mt->mt_next, e, mwid, dwid, pwid);
197    mt = mt->mt_child;
198  }
199}
200
201static void
202show_mi(amq_mount_info_list *ml, enum show_opt e, int *mwid, int *dwid, int *twid)
203{
204  int i;
205
206  switch (e) {
207
208  case Calc:
209    {
210      for (i = 0; i < ml->amq_mount_info_list_len; i++) {
211	amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
212	int mw = strlen(mi->mi_mountinfo);
213	int dw = strlen(mi->mi_mountpt);
214	int tw = strlen(mi->mi_type);
215	if (mw > *mwid)
216	  *mwid = mw;
217	if (dw > *dwid)
218	  *dwid = dw;
219	if (tw > *twid)
220	  *twid = tw;
221      }
222    }
223  break;
224
225  case Full:
226    {
227      for (i = 0; i < ml->amq_mount_info_list_len; i++) {
228	amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
229	printf("%-*.*s %-*.*s %-*.*s %-3d %s is %s",
230	       *mwid, *mwid, mi->mi_mountinfo,
231	       *dwid, *dwid, mi->mi_mountpt,
232	       *twid, *twid, mi->mi_type,
233	       mi->mi_refc, mi->mi_fserver,
234	       mi->mi_up > 0 ? "up" :
235	       mi->mi_up < 0 ? "starting" : "down");
236	if (mi->mi_error > 0) {
237	  extern int sys_nerr;
238	  if (mi->mi_error < sys_nerr)
239#ifdef HAVE_STRERROR
240	    printf(" (%s)", strerror(mi->mi_error));
241#else /* not HAVE_STRERROR */
242	    printf(" (%s)", sys_errlist[mi->mi_error]);
243#endif /* not HAVE_STRERROR */
244	  else
245	    printf(" (Error %d)", mi->mi_error);
246	} else if (mi->mi_error < 0) {
247	  fputs(" (in progress)", stdout);
248	}
249	fputc('\n', stdout);
250      }
251    }
252  break;
253
254  default:
255    break;
256  }
257}
258
259
260/*
261 * Display general mount statistics
262 */
263static void
264show_ms(amq_mount_stats *ms)
265{
266  printf("\
267requests  stale     mount     mount     unmount\n\
268deferred  fhandles  ok        failed    failed\n\
269%-9d %-9d %-9d %-9d %-9d\n",
270	 ms->as_drops, ms->as_stale, ms->as_mok, ms->as_merr, ms->as_uerr);
271}
272
273
274#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT)
275static char *
276cluster_server(void)
277{
278  struct cct_entry *cp;
279
280  if (cnodeid() == 0) {
281    /*
282     * Not clustered
283     */
284    return def_server;
285  }
286  while (cp = getccent())
287    if (cp->cnode_type == 'r')
288      return cp->cnode_name;
289
290  return def_server;
291}
292#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
293
294
295/*
296 * MAIN
297 */
298int
299main(int argc, char *argv[])
300{
301  int opt_ch;
302  int errs = 0;
303  char *server;
304  struct sockaddr_in server_addr;
305  int s;	/* to pass the Amd security check, we must use a priv port */
306  CLIENT *clnt = NULL;
307  struct hostent *hp;
308  int nodefault = 0;
309  struct timeval tv;
310  char *progname = NULL;
311#ifndef HAVE_TRANSPORT_TYPE_TLI
312  enum clnt_stat cs;
313#endif /* not HAVE_TRANSPORT_TYPE_TLI */
314
315
316  /*
317   * Compute program name
318   */
319  if (argv[0]) {
320    progname = strrchr(argv[0], '/');
321    if (progname && progname[1])
322      progname++;
323    else
324      progname = argv[0];
325  }
326  if (!progname)
327    progname = "amq";
328  am_set_progname(progname);
329
330  /*
331   * Parse arguments
332   */
333#ifdef ENABLE_AMQ_MOUNT
334  while ((opt_ch = getopt(argc, argv, "fh:l:msuvx:D:M:pP:TU")) != -1)
335#else /* not ENABLE_AMQ_MOUNT */
336  while ((opt_ch = getopt(argc, argv, "fh:l:msuvx:D:pP:TU")) != -1)
337#endif /* not ENABLE_AMQ_MOUNT */
338    switch (opt_ch) {
339    case 'f':
340      flush_flag = 1;
341      nodefault = 1;
342      break;
343
344    case 'h':
345      def_server = optarg;
346      break;
347
348    case 'l':
349      amq_logfile = optarg;
350      nodefault = 1;
351      break;
352
353    case 'm':
354      minfo_flag = 1;
355      nodefault = 1;
356      break;
357
358    case 'p':
359      getpid_flag = 1;
360      nodefault = 1;
361      break;
362
363    case 's':
364      stats_flag = 1;
365      nodefault = 1;
366      break;
367
368    case 'u':
369      unmount_flag = 1;
370      nodefault = 1;
371      break;
372
373    case 'v':
374      getvers_flag = 1;
375      nodefault = 1;
376      break;
377
378    case 'x':
379      xlog_optstr = optarg;
380      nodefault = 1;
381      break;
382
383    case 'D':
384      debug_opts = optarg;
385      nodefault = 1;
386      break;
387
388#ifdef ENABLE_AMQ_MOUNT
389    case 'M':
390      mount_map = optarg;
391      nodefault = 1;
392      break;
393#endif /* ENABLE_AMQ_MOUNT */
394
395    case 'P':
396      amd_program_number = atoi(optarg);
397      break;
398
399    case 'T':
400      use_tcp_flag = 1;
401      break;
402
403    case 'U':
404      use_udp_flag = 1;
405      break;
406
407    default:
408      errs = 1;
409      break;
410    }
411
412  if (optind == argc) {
413    if (unmount_flag)
414      errs = 1;
415  }
416  if (errs) {
417  show_usage:
418    fprintf(stderr, "\
419Usage: %s [-h host] [[-f] [-m] [-p] [-v] [-s]] | [[-u] directory ...]]\n\
420\t[-l logfile|\"syslog\"] [-x log_flags] [-D dbg_opts]%s\n\
421\t[-P prognum] [-T] [-U]\n",
422	    am_get_progname(),
423#ifdef ENABLE_AMQ_MOUNT
424	    " [-M mapent]"
425#else /* not ENABLE_AMQ_MOUNT */
426	    ""
427#endif /* not ENABLE_AMQ_MOUNT */
428    );
429    exit(1);
430  }
431
432
433
434  /* set use_udp and use_tcp flags both to on if none are defined */
435  if (!use_tcp_flag && !use_udp_flag)
436    use_tcp_flag = use_udp_flag = 1;
437
438#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT)
439  /*
440   * Figure out root server of cluster
441   */
442  if (def_server == localhost)
443    server = cluster_server();
444  else
445#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
446    server = def_server;
447
448  /*
449   * Get address of server
450   */
451  if ((hp = gethostbyname(server)) == 0 && !STREQ(server, localhost)) {
452    fprintf(stderr, "%s: Can't get address of %s\n",
453	    am_get_progname(), server);
454    exit(1);
455  }
456  memset(&server_addr, 0, sizeof server_addr);
457  server_addr.sin_family = AF_INET;
458  if (hp) {
459    memmove((voidp) &server_addr.sin_addr, (voidp) hp->h_addr,
460	    sizeof(server_addr.sin_addr));
461  } else {
462    /* fake "localhost" */
463    server_addr.sin_addr.s_addr = htonl(0x7f000001);
464  }
465
466  /*
467   * Create RPC endpoint
468   */
469  tv.tv_sec = 5;		/* 5 seconds for timeout or per retry */
470  tv.tv_usec = 0;
471
472#ifdef HAVE_TRANSPORT_TYPE_TLI
473  clnt = get_secure_amd_client(server, &tv, &s);
474  if (!clnt && use_tcp_flag)	/* try tcp first */
475    clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "tcp");
476  if (!clnt && use_udp_flag) {	/* try udp next */
477    clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "udp");
478    /* if ok, set timeout (valid for connectionless transports only) */
479    if (clnt)
480      clnt_control(clnt, CLSET_RETRY_TIMEOUT, (char *) &tv);
481  }
482#else /* not HAVE_TRANSPORT_TYPE_TLI */
483
484  /* first check if remote portmapper is up */
485  cs = pmap_ping(&server_addr);
486  if (cs == RPC_TIMEDOUT) {
487    fprintf(stderr, "%s: failed to contact portmapper on host \"%s\". %s\n",
488	    am_get_progname(), server, clnt_sperrno(cs));
489    exit(1);
490  }
491
492  /* portmapper exists: get remote amd info from it */
493  if (!clnt && use_tcp_flag) {	/* try tcp first */
494    s = RPC_ANYSOCK;
495    clnt = clnttcp_create(&server_addr, amd_program_number,
496			  AMQ_VERSION, &s, 0, 0);
497  }
498  if (!clnt && use_udp_flag) {	/* try udp next */
499    /* XXX: do we need to close(s) ? */
500    s = privsock(SOCK_DGRAM);
501    clnt = clntudp_create(&server_addr, amd_program_number,
502			  AMQ_VERSION, tv, &s);
503  }
504#endif /* not HAVE_TRANSPORT_TYPE_TLI */
505  if (!clnt) {
506    fprintf(stderr, "%s: ", am_get_progname());
507    clnt_pcreateerror(server);
508    exit(1);
509  }
510
511  /*
512   * Control debugging
513   */
514  if (debug_opts) {
515    int *rc;
516    amq_setopt opt;
517    opt.as_opt = AMOPT_DEBUG;
518    opt.as_str = debug_opts;
519    rc = amqproc_setopt_1(&opt, clnt);
520    if (rc && *rc < 0) {
521      fprintf(stderr, "%s: daemon not compiled for debug\n",
522	      am_get_progname());
523      errs = 1;
524    } else if (!rc || *rc > 0) {
525      fprintf(stderr, "%s: debug setting for \"%s\" failed\n",
526	      am_get_progname(), debug_opts);
527      errs = 1;
528    }
529  }
530
531  /*
532   * Control logging
533   */
534  if (xlog_optstr) {
535    int *rc;
536    amq_setopt opt;
537    opt.as_opt = AMOPT_XLOG;
538    opt.as_str = xlog_optstr;
539    rc = amqproc_setopt_1(&opt, clnt);
540    if (!rc || *rc) {
541      fprintf(stderr, "%s: setting log level to \"%s\" failed\n",
542	      am_get_progname(), xlog_optstr);
543      errs = 1;
544    }
545  }
546
547  /*
548   * Control log file
549   */
550  if (amq_logfile) {
551    int *rc;
552    amq_setopt opt;
553    opt.as_opt = AMOPT_LOGFILE;
554    opt.as_str = amq_logfile;
555    rc = amqproc_setopt_1(&opt, clnt);
556    if (!rc || *rc) {
557      fprintf(stderr, "%s: setting logfile to \"%s\" failed\n",
558	      am_get_progname(), amq_logfile);
559      errs = 1;
560    }
561  }
562
563  /*
564   * Flush map cache
565   */
566  if (flush_flag) {
567    int *rc;
568    amq_setopt opt;
569    opt.as_opt = AMOPT_FLUSHMAPC;
570    opt.as_str = "";
571    rc = amqproc_setopt_1(&opt, clnt);
572    if (!rc || *rc) {
573      fprintf(stderr, "%s: amd on %s cannot flush the map cache\n",
574	      am_get_progname(), server);
575      errs = 1;
576    }
577  }
578
579  /*
580   * Mount info
581   */
582  if (minfo_flag) {
583    int dummy;
584    amq_mount_info_list *ml = amqproc_getmntfs_1(&dummy, clnt);
585    if (ml) {
586      int mwid = 0, dwid = 0, twid = 0;
587      show_mi(ml, Calc, &mwid, &dwid, &twid);
588      mwid++;
589      dwid++;
590      twid++;
591      show_mi(ml, Full, &mwid, &dwid, &twid);
592
593    } else {
594      fprintf(stderr, "%s: amd on %s cannot provide mount info\n",
595	      am_get_progname(), server);
596    }
597  }
598
599  /*
600   * Mount map
601   */
602  if (mount_map) {
603    int *rc;
604    do {
605      rc = amqproc_mount_1(&mount_map, clnt);
606    } while (rc && *rc < 0);
607    if (!rc || *rc > 0) {
608      if (rc)
609	errno = *rc;
610      else
611	errno = ETIMEDOUT;
612      fprintf(stderr, "%s: could not start new ", am_get_progname());
613      perror("automount point");
614    }
615  }
616
617  /*
618   * Get Version
619   */
620  if (getvers_flag) {
621    amq_string *spp = amqproc_getvers_1((voidp) 0, clnt);
622    if (spp && *spp) {
623      fputs(*spp, stdout);
624      XFREE(*spp);
625    } else {
626      fprintf(stderr, "%s: failed to get version information\n",
627	      am_get_progname());
628      errs = 1;
629    }
630  }
631
632  /*
633   * Get PID of amd
634   */
635  if (getpid_flag) {
636    int *ip = amqproc_getpid_1((voidp) 0, clnt);
637    if (ip && *ip) {
638      printf("%d\n", *ip);
639    } else {
640      fprintf(stderr, "%s: failed to get PID of amd\n", am_get_progname());
641      errs = 1;
642    }
643  }
644
645  /*
646   * Apply required operation to all remaining arguments
647   */
648  if (optind < argc) {
649    do {
650      char *fs = argv[optind++];
651      if (unmount_flag) {
652	/*
653	 * Unmount request
654	 */
655	amqproc_umnt_1(&fs, clnt);
656      } else {
657	/*
658	 * Stats request
659	 */
660	amq_mount_tree_p *mtp = amqproc_mnttree_1(&fs, clnt);
661	if (mtp) {
662	  amq_mount_tree *mt = *mtp;
663	  if (mt) {
664	    int mwid = 0, dwid = 0, twid = 0;
665	    show_mt(mt, Calc, &mwid, &dwid, &twid);
666	    mwid++;
667	    dwid++, twid++;
668	    printf("%-*.*s Uid   Getattr Lookup RdDir   RdLnk   Statfs Mounted@\n",
669		   dwid, dwid, "What");
670	    show_mt(mt, Stats, &mwid, &dwid, &twid);
671	  } else {
672	    fprintf(stderr, "%s: %s not automounted\n", am_get_progname(), fs);
673	  }
674	  xdr_pri_free((XDRPROC_T_TYPE) xdr_amq_mount_tree_p, (caddr_t) mtp);
675	} else {
676	  fprintf(stderr, "%s: ", am_get_progname());
677	  clnt_perror(clnt, server);
678	  errs = 1;
679	}
680      }
681    } while (optind < argc);
682
683  } else if (unmount_flag) {
684    goto show_usage;
685
686  } else if (stats_flag) {
687    amq_mount_stats *ms = amqproc_stats_1((voidp) 0, clnt);
688    if (ms) {
689      show_ms(ms);
690    } else {
691      fprintf(stderr, "%s: ", am_get_progname());
692      clnt_perror(clnt, server);
693      errs = 1;
694    }
695
696  } else if (!nodefault) {
697    amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt);
698    if (mlp) {
699      enum show_opt e = Calc;
700      int mwid = 0, dwid = 0, pwid = 0;
701      while (e != ShowDone) {
702	int i;
703	for (i = 0; i < mlp->amq_mount_tree_list_len; i++) {
704	  show_mt(mlp->amq_mount_tree_list_val[i],
705		  e, &mwid, &dwid, &pwid);
706	}
707	mwid++;
708	dwid++, pwid++;
709	if (e == Calc)
710	  e = Short;
711	else if (e == Short)
712	  e = ShowDone;
713      }
714
715    } else {
716      fprintf(stderr, "%s: ", am_get_progname());
717      clnt_perror(clnt, server);
718      errs = 1;
719    }
720  }
721  exit(errs);
722  return errs; /* should never reach here */
723}
724
725
726#ifdef HAVE_TRANSPORT_TYPE_TLI
727
728/*
729 * How to bind to reserved ports.
730 * TLI handle (socket) and port version.
731 */
732/* defined here so that it does not have to resolve it with libamu.a */
733static int
734amq_bind_resv_port(int td, u_short *pp)
735{
736  int rc = -1, port;
737  struct t_bind *treq, *tret;
738  struct sockaddr_in *sin;
739
740  treq = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
741  if (!treq) {
742    plog(XLOG_ERROR, "t_alloc 1");
743    return -1;
744  }
745  tret = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
746  if (!tret) {
747    t_free((char *) treq, T_BIND);
748    plog(XLOG_ERROR, "t_alloc 2");
749    return -1;
750  }
751  memset((char *) treq->addr.buf, 0, treq->addr.len);
752  sin = (struct sockaddr_in *) treq->addr.buf;
753  sin->sin_family = AF_INET;
754  treq->qlen = 0;
755  treq->addr.len = treq->addr.maxlen;
756  errno = EADDRINUSE;
757  port = IPPORT_RESERVED;
758
759  do {
760    --port;
761    sin->sin_port = htons(port);
762    rc = t_bind(td, treq, tret);
763    if (rc < 0) {
764    } else {
765      if (memcmp(treq->addr.buf, tret->addr.buf, tret->addr.len) == 0)
766	break;
767      else
768	t_unbind(td);
769    }
770  } while ((rc < 0 || errno == EADDRINUSE) && (int) port > IPPORT_RESERVED / 2);
771
772  if (pp) {
773    if (rc == 0)
774      *pp = port;
775    else
776      plog(XLOG_ERROR, "could not t_bind to any reserved port");
777  }
778  t_free((char *) tret, T_BIND);
779  t_free((char *) treq, T_BIND);
780  return rc;
781}
782
783
784/*
785 * Create a secure rpc client attached to the amd daemon.
786 */
787static CLIENT *
788get_secure_amd_client(char *host, struct timeval *tv, int *sock)
789{
790  CLIENT *client;
791  struct netbuf nb;
792  struct netconfig *nc, *pm_nc;
793  struct sockaddr_in sin;
794
795
796  nb.maxlen = sizeof(sin);
797  nb.buf = (char *) &sin;
798
799  /*
800   * Ensure that remote portmapper is alive
801   * (must use connectionless netconfig).
802   */
803  if ((pm_nc = getnetconfigent(NC_UDP)) != NULL) {
804    enum clnt_stat cs;
805
806    cs = rpcb_rmtcall(pm_nc,
807		      host,
808		      amd_program_number,
809		      AMQ_VERSION,
810		      AMQPROC_NULL,
811		      (XDRPROC_T_TYPE) xdr_void,
812		      NULL,
813		      (XDRPROC_T_TYPE) xdr_void,
814		      NULL,
815		      *tv,
816		      NULL);
817    if (cs == RPC_TIMEDOUT) {
818      fprintf(stderr, "%s: failed to contact portmapper on host \"%s\". %s\n",
819	      am_get_progname(), host, clnt_sperrno(cs));
820      exit(1);
821    }
822  }
823
824  /*
825   * First transport type to try: TCP
826   */
827  if (use_tcp_flag) {
828    /* Find amd address on TCP */
829    nc = getnetconfigent(NC_TCP);
830    if (!nc) {
831      fprintf(stderr, "getnetconfig for tcp failed: %s\n", nc_sperror());
832      goto tryudp;
833    }
834
835    if (!rpcb_getaddr(amd_program_number, AMQ_VERSION, nc, &nb, host)) {
836      /*
837       * don't print error messages here, since amd might legitimately
838       * serve udp only
839       */
840      goto tryudp;
841    }
842    /* Create privileged TCP socket */
843    *sock = t_open(nc->nc_device, O_RDWR, 0);
844
845    if (*sock < 0) {
846      fprintf(stderr, "t_open %s: %m\n", nc->nc_device);
847      goto tryudp;
848    }
849    if (amq_bind_resv_port(*sock, (u_short *) 0) < 0)
850      goto tryudp;
851
852    client = clnt_vc_create(*sock, &nb, amd_program_number, AMQ_VERSION, 0, 0);
853    if (!client) {
854      fprintf(stderr, "clnt_vc_create failed");
855      t_close(*sock);
856      goto tryudp;
857    }
858    /* tcp succeeded */
859    return client;
860  }
861
862tryudp:
863  /*
864   * TCP failed so try UDP
865   */
866  if (use_udp_flag) {
867    /* find amd address on UDP */
868    nc = getnetconfigent(NC_UDP);
869    if (!nc) {
870      fprintf(stderr, "getnetconfig for udp failed: %s\n", nc_sperror());
871      return NULL;
872    }
873    if (!rpcb_getaddr(amd_program_number, AMQ_VERSION, nc, &nb, host)) {
874      fprintf(stderr, "%s\n",
875	      clnt_spcreateerror("couldn't get amd address on udp"));
876      return NULL;
877    }
878    /* create privileged UDP socket */
879    *sock = t_open(nc->nc_device, O_RDWR, 0);
880
881    if (*sock < 0) {
882      fprintf(stderr, "t_open %s: %m\n", nc->nc_device);
883      return NULL;		/* neither tcp not udp succeeded */
884    }
885    if (amq_bind_resv_port(*sock, (u_short *) 0) < 0)
886      return NULL;
887
888    client = clnt_dg_create(*sock, &nb, amd_program_number, AMQ_VERSION, 0, 0);
889    if (!client) {
890      fprintf(stderr, "clnt_dg_create failed\n");
891      t_close(*sock);
892      return NULL;		/* neither tcp not udp succeeded */
893    }
894    if (clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) tv) == FALSE) {
895      fprintf(stderr, "clnt_control CLSET_RETRY_TIMEOUT for udp failed\n");
896      clnt_destroy(client);
897      return NULL;		/* neither tcp not udp succeeded */
898    }
899    /* udp succeeded */
900    return client;
901  }
902
903  /* should never get here */
904  return NULL;
905}
906
907#else /* not HAVE_TRANSPORT_TYPE_TLI */
908
909/*
910 * inetresport creates a datagram socket and attempts to bind it to a
911 * secure port.
912 * returns: The bound socket, or -1 to indicate an error.
913 */
914static int
915inetresport(int ty)
916{
917  int alport;
918  struct sockaddr_in addr;
919  int fd;
920
921  /* Use internet address family */
922  addr.sin_family = AF_INET;
923  addr.sin_addr.s_addr = INADDR_ANY;
924  if ((fd = socket(AF_INET, ty, 0)) < 0)
925    return -1;
926
927  for (alport = IPPORT_RESERVED - 1; alport > IPPORT_RESERVED / 2 + 1; alport--) {
928    addr.sin_port = htons((u_short) alport);
929    if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) >= 0)
930      return fd;
931    if (errno != EADDRINUSE) {
932      close(fd);
933      return -1;
934    }
935  }
936  close(fd);
937  errno = EAGAIN;
938  return -1;
939}
940
941
942/*
943 * Privsock() calls inetresport() to attempt to bind a socket to a secure
944 * port.  If inetresport() fails, privsock returns a magic socket number which
945 * indicates to RPC that it should make its own socket.
946 * returns: A privileged socket # or RPC_ANYSOCK.
947 */
948static int
949privsock(int ty)
950{
951  int sock = inetresport(ty);
952
953  if (sock < 0) {
954    errno = 0;
955    /* Couldn't get a secure port, let RPC make an insecure one */
956    sock = RPC_ANYSOCK;
957  }
958  return sock;
959}
960
961#endif /* not HAVE_TRANSPORT_TYPE_TLI */
962