home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 024 / psi110g.zip / BOOTPCMD.C < prev    next >
C/C++ Source or Header  |  1994-04-17  |  18KB  |  707 lines

  1. /*************************************************/
  2. /* Center for Information Technology Integration */
  3. /*           The University of Michigan          */
  4. /*                    Ann Arbor                  */
  5. /*                                               */
  6. /* Dedicated to the public domain.               */
  7. /* Send questions to info@citi.umich.edu         */
  8. /*                                               */
  9. /* BOOTP is documented in RFC 951 and RFC 1048   */
  10. /*************************************************/
  11.   
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <time.h>
  15. #include <ctype.h>
  16. #ifdef  ANSIPROTO
  17. #include <stdarg.h>
  18. #endif
  19. #include "global.h"
  20. #ifdef BOOTP
  21. #include "cmdparse.h"
  22. #include "bootpd.h"
  23. #include "netuser.h"
  24. #include "iface.h"
  25. #include "udp.h"
  26. #include "arp.h"
  27.   
  28. #define BP_DEFAULT_TAB "bootptab"
  29. #define BP_DEFAULT_LOG "bootplog"
  30. #define BP_DEFAULT_DIR "bpfiles"
  31. #define BP_DEFAULT_FILE "boot"
  32.   
  33. static char    *bootptab = BP_DEFAULT_TAB;
  34. static FILE    *bootfp;                 /* bootptab fp */
  35. static long     modtime;                /* last modification time of bootptab */
  36.   
  37. static char    bootplog[64] = BP_DEFAULT_LOG;
  38. static int      LogInFile = 0;          /* Should bp_log log in a file? */
  39. static int      LogOnScreen = 0;        /* Should bp_log log on screen? */
  40.   
  41. static char    *line;            /* line buffer for reading bootptab */
  42. static int     linenum;          /* current ilne number in bootptab */
  43.   
  44. extern int      Nhosts;          /* number of hosts in host structure */
  45. extern struct host hosts[MHOSTS+1];
  46.   
  47. extern char    homedir[64];      /* bootfile homedirectory */
  48. extern char    defaultboot[64];  /* default file to boot */
  49. extern int32   bp_DefaultDomainNS[BP_MAXDNS]; /* default domain name server */
  50. extern int      Nhosts;
  51. extern struct udp_cb *Bootpd_cb;
  52.   
  53.   
  54.   
  55.   
  56.   
  57. static int bp_Homedir           __ARGS((int argc,char *argv[],void *p));
  58. static int bp_DefaultFile       __ARGS((int argc,char *argv[],void *p));
  59. static int bp_DynamicRange      __ARGS((int argc,char *argv[],void *p));
  60. static int bp_donothing         __ARGS((int argc,char *argv[],void *p));
  61. static int bp_Host              __ARGS((int argc,char *argv[],void *p));
  62. static int bp_rmHost            __ARGS((int argc,char *argv[],void *p));
  63. static int bp_DomainNS          __ARGS((int argc,char *argv[],void *p));
  64. static int bp_Start             __ARGS((int argc,char *argv[],void *p));
  65. static int bp_Stop              __ARGS((int argc,char *argv[],void *p));
  66. static int bp_logFile           __ARGS((int argc,char *argv[],void *p));
  67. static int bp_logScreen                 __ARGS((int argc,char *argv[],void *p));
  68. static void  dumphosts          __ARGS((void));
  69.   
  70.   
  71. void bootpd __ARGS((struct iface *iface, struct udp_cb *sock, int cnt));
  72.   
  73. static struct cmds DFAR BootpdCmds[] = {
  74.     "",             bp_donothing,           0, 0, NULLCHAR,
  75.     "start",        bp_Start,               0, 0, NULLCHAR,
  76.     "stop",         bp_Stop,                0, 0, NULLCHAR,
  77.     "dns",          bp_DomainNS,            0, 0, NULLCHAR,
  78.     "dynip",        bp_DynamicRange,        0, 0, NULLCHAR,
  79.     "host",         bp_Host,                0, 0, NULLCHAR,
  80.     "rmhost",       bp_rmHost,              0, 0, NULLCHAR,
  81.     "homedir",      bp_Homedir,             0, 0, NULLCHAR,
  82.     "defaultfile",  bp_DefaultFile,         0, 0, NULLCHAR,
  83.     "logfile",      bp_logFile,             0, 0, NULLCHAR,
  84.     "logscreen",    bp_logScreen,           0, 0, NULLCHAR,
  85.     NULLCHAR,       NULLFP,                 0, 0, NULLCHAR
  86. };
  87.   
  88.   
  89.   
  90. int
  91. bootpdcmd (argc, argv, p)
  92. int argc;
  93. char *argv[];
  94. void *p;
  95. {
  96.     return subcmd (BootpdCmds, argc, argv, p);
  97. }
  98.   
  99.   
  100. /* Start up bootp service */
  101. static int
  102. bp_Start (argc,argv,p)
  103. int argc;
  104. char *argv[];
  105. void *p;
  106. {
  107.   
  108.     struct socket lsock;
  109.     time_t tloc;
  110.     char *usage = "bootpd start\n";
  111.   
  112.     if (argc != 1) {
  113.         tprintf (usage);
  114.         return (-1);
  115.     }
  116.   
  117.     time(&tloc);
  118.     bp_log ("\n\n####BOOTP server starting at %s\n", ctime(&tloc));
  119.   
  120.     lsock.address = INADDR_ANY;
  121.     lsock.port = IPPORT_BOOTPS;
  122.   
  123.     /* This way is better than recvfrom because it passes the iface in bootpd call */
  124.        /* Listen doesn't work for datagrams. */
  125.   
  126.     if (Bootpd_cb == NULLUDP) {
  127.         if ((Bootpd_cb = open_udp(&lsock, bootpd)) == NULLUDP) {
  128.             tprintf ("bootpd: can't open_udp\n");
  129.             return (-1);
  130.         }
  131.     }
  132.   
  133.     /*
  134.      * Read the bootptab file once immediately upon startup.
  135.      */
  136.   
  137.     da_init();
  138.   
  139.     readtab();
  140.   
  141.     return (0);
  142. }
  143.   
  144.   
  145.   
  146. /* Stop bootp service */
  147. static int
  148. bp_Stop (argc, argv, p)
  149. int argc;
  150. char *argv[];
  151. void *p;
  152. {
  153.     time_t now;
  154.     char *usage = "bootpd stop\n";
  155.   
  156.     if (argc != 1) {
  157.         tprintf (usage);
  158.         return -1;
  159.     }
  160.   
  161.     time (&now);
  162.   
  163.     Nhosts = 0;
  164.     da_shut();
  165.     readtab_shut();
  166.     del_udp (Bootpd_cb);
  167.     Bootpd_cb = NULLUDP;
  168.   
  169.     bp_log ("Bootpd shutdown %s", ctime (&now));
  170.     return (0);
  171. };
  172.   
  173.   
  174.   
  175. static int
  176. bp_logFile (argc, argv, p)
  177. int argc;
  178. char *argv[];
  179. void *p;
  180. {
  181.     int i;
  182.     time_t now;
  183.     char *usage = "bootpd logfile [<file_name> | default] [on | off] \n";
  184.   
  185.     time (&now);
  186.   
  187.     if (argc == 1) {
  188.         if (LogInFile)
  189.             tprintf ("Bootpd logging to file '%s' turned on.\n", bootplog);
  190.         else
  191.             tprintf ("Bootpd logging to file '%s' turned off.\n", bootplog);
  192.     }
  193.     else {
  194.         for (i = 1; i < argc; i++) {
  195.   
  196.             if (strcmp ("?", argv[i]) == 0)
  197.                 tprintf (usage);
  198.   
  199.             else if (strcmp ("off", argv[i]) == 0) {
  200.                 bp_log ("Stopping file logging at %s", ctime(&now));
  201.                 LogInFile = 0;
  202.             }
  203.             else if (strcmp ("on", argv[i]) == 0) {
  204.                 LogInFile = 1;
  205.                 bp_log ("Starting file logging at %s", ctime(&now));
  206.             }
  207.             else if (strcmp ("default", argv[i]) == 0) {
  208.                 strcpy (bootplog, BP_DEFAULT_LOG);
  209.                 bp_log ("File for logging set to %s\n", bootplog);
  210.             }
  211.             else {
  212.                 strcpy (bootplog, argv[1]);
  213.                 bp_log ("File for logging set to %s\n", bootplog);
  214.             }
  215.         }
  216.     }
  217.     return 0;
  218. }
  219.   
  220.   
  221. static int
  222. bp_logScreen (argc, argv, p)
  223. int argc;
  224. char *argv[];
  225. void *p;
  226. {
  227.     char *usage = "bootpd logscreen [on | off]\n";
  228.   
  229.     if (argc == 1)
  230.         if (LogOnScreen)
  231.             tprintf ("Bootpd logging on screen turned on.\n");
  232.         else
  233.             tprintf ("Bootpd logging on screen turned off.\n");
  234.   
  235.         else if (argc == 2)  {
  236.             if  (strcmp ("on", argv[1]) == 0)
  237.                 LogOnScreen = 1;
  238.             else if  (strcmp ("off", argv[1]) == 0)
  239.                 LogOnScreen = 0;
  240.             else tprintf (usage);
  241.         }
  242.         else tprintf (usage);
  243.     return 0;
  244. }
  245.   
  246.   
  247.   
  248.   
  249. static int
  250. bp_DomainNS (argc, argv, p)
  251. int argc;
  252. char *argv[];
  253. void *p;
  254. {
  255.     int a0, a1, a2, a3;
  256.     int i;
  257.     char *usage = "bootpd dns [<IP addr of domain name server>...]\n";
  258.   
  259.     if (argc == 1) {
  260.         tprintf ("Bootp domain name servers: ");
  261.         for (i=0; (i < BP_MAXDNS) && (bp_DefaultDomainNS[i] != 0); i++)
  262.             tprintf (" %s", inet_ntoa (bp_DefaultDomainNS[i]));
  263.         tprintf ("\n");
  264.         return (0);
  265.     }
  266.   
  267.     if (argc > 1) {
  268.         if ((argc == 2) && (strcmp ("?", argv[1]) == 0)) {
  269.             tprintf (usage);
  270.             return 0;
  271.         }
  272.   
  273.         /* A list of name servers has been given */
  274.         /* reset the domain name server list */
  275.         for (i= 0; i < BP_MAXDNS; i++)
  276.             bp_DefaultDomainNS[i] = 0;
  277.   
  278.         /* get ip address */
  279.         for (i = 1; (i < argc) && (i < BP_MAXDNS); i++) {
  280.             if (4 != sscanf (argv[i], "%d.%d.%d.%d", &a0, &a1, &a2, &a3)) {
  281.                 tprintf("bad internet address: %s\n", argv[1], linenum);
  282.                 return  -1;
  283.             }
  284.             bp_DefaultDomainNS[i-1] = aton(argv[i]);
  285.         }
  286.     }
  287.     /* record for the loggers sake */
  288.     bp_log ("Bootp domain name servers: ");
  289.     for (i=0; (i < BP_MAXDNS) && (bp_DefaultDomainNS[i] != 0); i++)
  290.         bp_log (" %s", inet_ntoa (bp_DefaultDomainNS[i]));
  291.     bp_log ("\n");
  292.     return 0;
  293. }
  294.   
  295.   
  296.   
  297. static int
  298. bp_rmHost (argc, argv, p)
  299. int argc;
  300. char *argv[];
  301. void *p;
  302. {
  303.     int i;
  304.     struct host *hp = NULLHOST;
  305.     struct host *cp = NULLHOST;
  306.     char *usage = "bootpd rmhost <host name>\n";
  307.   
  308.   
  309.     if (argc == 2) {
  310.   
  311.         /* Find the host record */
  312.         for (i=0; i < Nhosts; i++) {
  313.             if (strcmp (hosts[i].name, argv[1]) == 0) {
  314.                 hp = &(hosts[i]);
  315.                 break;
  316.             }
  317.         }
  318.         /* Return if not found */
  319.         if (hp == NULLHOST) {
  320.             tprintf ("Host %s not in host tables.\n", argv[1]);
  321.             return -1;
  322.         }
  323.         bp_log ("Host %s removed from host table\n", hp->name);
  324.         cp = &(hosts [Nhosts - 1]);
  325.         if (hp < cp)
  326.             memcpy ((char *) hp, (char *) cp, sizeof (struct host));
  327.         Nhosts--;
  328.         return 0;
  329.     }
  330.     else tprintf (usage);
  331.     return 0;
  332. }
  333.   
  334.   
  335. /*
  336.  * Printout the hosts table.
  337. */
  338. static void
  339. dumphosts()
  340. {
  341.     int i;
  342.     struct host *hp;
  343.     struct arp_type *at;
  344.   
  345.     tprintf ("\n\nStatus of host table\n");
  346.   
  347.     if (Nhosts == 0) {
  348.         tprintf ("     No hosts in host table\n");
  349.         return;
  350.     }
  351.     for (i = 0; i <= Nhosts-1; i++) {
  352.         hp = &hosts[i];
  353.         at = &Arp_type[hp->htype];
  354.   
  355.         tprintf ("%s  %s  %s  %s  '%s'\n",
  356.         hp->name, ArpNames[hp->htype], (*at->format)(bp_ascii, hp->haddr),
  357.         inet_ntoa ((int32)hp->iaddr.s_addr),
  358.         hp->bootfile);
  359.   
  360.     }
  361. }
  362.   
  363.   
  364. static int
  365. bp_Host (argc, argv, p)
  366. int argc;
  367. char *argv[];
  368. void *p;
  369. {
  370.     struct host *hp;
  371.     int a0, a1, a2, a3;
  372.     struct arp_type *at;
  373.     char *usage = "bootpd host [<hostname> <hardware type> <hardware addr> <ip addr> [boot file]]\n";
  374.   
  375.     switch (argc) {
  376.         case 1:
  377.             dumphosts();
  378.             break;
  379.         case 5:
  380.         case 6:
  381.   
  382.             hp = &hosts[Nhosts];
  383.   
  384.         /* get host name */
  385.             strncpy (hp->name, argv[1], sizeof (hp->name));
  386.   
  387.         /* get hardware type */
  388.         /* This code borrowed from Phil Karn's arpcmd.c */
  389.         /* This is a kludge. It really ought to be table driven */
  390.         switch(tolower(argv[2][0])){
  391.             case 'n':       /* Net/Rom pseudo-type */
  392.                 hp->htype = ARP_NETROM;
  393.                 break;
  394.             case 'e': /* "ether" */
  395.                 hp->htype = ARP_ETHER;
  396.                 break;
  397.             case 'a': /* "ax25" */
  398.                 hp->htype = ARP_AX25;
  399.                 break;
  400.             case 'm': /* "mac appletalk" */
  401.                 hp->htype = ARP_APPLETALK;
  402.                 break;
  403.             default:
  404.                 tprintf("unknown hardware type \"%s\"\n",argv[2]);
  405.                 return -1;
  406.         }
  407.   
  408.             at = &Arp_type[hp->htype];
  409.             if(at->scan == NULLFP){
  410.                 return 1;
  411.             }
  412.         /* Destination address */
  413.             (*at->scan)(hp->haddr,argv[3]);
  414.   
  415.   
  416.         /* get ip address */
  417.             if (4 != sscanf (argv[4], "%d.%d.%d.%d", &a0, &a1, &a2, &a3))
  418.             {
  419.                 tprintf("bad internet address: %s\n", argv[1], linenum);
  420.                 return (0);
  421.             }
  422.             hp->iaddr.s_addr = aton(argv[4]);
  423.   
  424.         /* get the bootpfile */
  425.             if (argc == 6) strncpy (hp->bootfile, argv[5], sizeof (hp->bootfile));
  426.             else hp->bootfile[0] = 0;
  427.   
  428.             bp_log ("Host added: %s  %s  %s  %s  '%s'\n",
  429.             hp->name, ArpNames[hp->htype], (*at->format)(bp_ascii, hp->haddr),
  430.             inet_ntoa ((int32)hp->iaddr.s_addr),
  431.             hp->bootfile);
  432.   
  433.   
  434.             Nhosts++;
  435.             break;
  436.   
  437.         default:
  438.             tprintf (usage);
  439.             break;
  440.     }
  441.     return 0;
  442. }
  443.   
  444.   
  445.   
  446. static int
  447. bp_Homedir (argc, argv, p)
  448. int argc;
  449. char *argv[];
  450. void *p;
  451. {
  452.     char *usage = "bootpd homedir [<name of home directory> | default]\n";
  453.   
  454.     if (argc == 1)
  455.         tprintf ("Bootp home directory: '%s'\n", homedir);
  456.     else if (argc == 2) {
  457.         if (strcmp (argv[1], "?") == 0)
  458.             tprintf (usage);
  459.         else if (strcmp (argv[1], "default") == 0) {
  460.             strcpy (homedir, BP_DEFAULT_DIR);
  461.             bp_log ("Bootp home directory set to: '%s'\n", homedir);
  462.         }
  463.         else {
  464.             strcpy (homedir, argv[1]);
  465.             bp_log ("Bootp home directory set to: '%s'\n", homedir);
  466.         }
  467.     }
  468.     else tprintf (usage);
  469.     return (0);
  470. };
  471.   
  472.   
  473.   
  474. static int
  475. bp_DefaultFile (argc, argv, p)
  476. int argc;
  477. char *argv[];
  478. void *p;
  479. {
  480.     char *usage = "bootpd defaultfile [<name of default boot file> | default]\n";
  481.   
  482.     if (argc == 1)
  483.         tprintf ("Bootp default boot file:  '%s'\n", defaultboot);
  484.     else if (argc == 2) {
  485.         if (strcmp (argv[1], "?") == 0)
  486.             tprintf (usage);
  487.         else if (strcmp (argv[1], "default") == 0)
  488.             strcpy (defaultboot, BP_DEFAULT_FILE);
  489.         else {
  490.             strcpy (defaultboot, argv[1]);
  491.             bp_log ("Bootp default boot file set to:  '%s'\n", defaultboot);
  492.         }
  493.     }
  494.     else
  495.         tprintf (usage);
  496.   
  497.     return  (0);
  498. };
  499.   
  500.   
  501. static int
  502. bp_DynamicRange (argc, argv, p)
  503. int argc;
  504. char *argv[];
  505. void *p;
  506. {
  507.     int i0, i1, i2, i3;
  508.     int32 start, end;
  509.     struct iface *iface;
  510.     char *usage = "bootpd dynip [<net name> | <netname>  <IP address> <IP address> | <netname> off]\n";
  511.   
  512.     if (argc == 1) {
  513.         da_status (NULLIF);
  514.         return 0;
  515.     }
  516.     if ((argc == 2) && (strcmp ("?", argv[1]) == 0)) {
  517.         tprintf (usage);
  518.         return 0;
  519.     }
  520.   
  521.     /* get the interface */
  522.     iface = if_lookup (argv[1]);
  523.     if (iface == NULLIF) {
  524.         tprintf ("network '%s' not found\n", argv[1]);
  525.         return  (-1);
  526.     }
  527.     if (argc == 2) {
  528.         da_status (iface);
  529.         return 0;
  530.     }
  531.     if (argc == 3) {
  532.         if (strcmp ("off", argv[2]) == 0)
  533.             da_done_net (iface);
  534.         else tprintf (usage);
  535.     }
  536.     else if (argc == 4) {
  537.   
  538.         /* get ip address */
  539.         /* check the ip address - isaddr isn't a ka9q function */
  540.         if ((4 != sscanf (argv[2], "%d.%d.%d.%d", &i0, &i1, &i2, &i3)) ||
  541.             (i0 > 255) || (i1 > 255) || (i2 > 255) || (i3 > 255)
  542.             )
  543.         {
  544.             tprintf("bad internet address: %s\n", argv[2], linenum);
  545.             return (-1);
  546.         }
  547.   
  548.         if ((4 != sscanf (argv[3], "%d.%d.%d.%d", &i0, &i1, &i2, &i3)) ||
  549.             (i0 > 255) || (i1 > 255) || (i2 > 255) || (i3 > 255)
  550.             )
  551.         {
  552.             tprintf("bad internet address: %s\n", argv[3], linenum);
  553.             return (-1);
  554.         }
  555.   
  556.         start = aton(argv[2]);
  557.         end = aton(argv[3]);
  558.   
  559.         da_serve_net (iface, start, end);
  560.   
  561.     }
  562.     else {
  563.         tprintf (usage);
  564.         return (0);
  565.     }
  566.   
  567.     return (0);
  568. };
  569.   
  570.   
  571. static int
  572. bp_donothing (argc, argv, p)
  573. int argc;
  574. char *argv[];
  575. void *p;
  576. {
  577.     return (0);
  578. }
  579.   
  580.   
  581. /*
  582.  * Read bootptab database file.  Avoid rereading the file if the
  583.  * write date hasn't changed since the last time we read it.
  584.  */
  585. int
  586. readtab()
  587. {
  588.     struct stat st;
  589.   
  590.     /* If the file hasn't been opened, open it. */
  591.     if (bootfp == 0) {
  592.         if ((bootfp = fopen(bootptab, "r")) == NULLFILE) {
  593.             bp_log("Can't open bootptab file: %s\n", bootptab);
  594.             return (-1);
  595.         }
  596.     }
  597.   
  598.     /* Only reread if modified */
  599.     stat (bootptab, &st);
  600.     if (st.st_mtime == modtime && st.st_nlink) {
  601.         return (0); /* hasnt been modified or deleted yet */
  602.     }
  603.     /* It's been changed, reread. */
  604.   
  605.     if ((bootfp = fopen(bootptab, "r")) == NULLFILE) {
  606.         bp_log("Can't open %s\n", bootptab);
  607.         return (-1);
  608.     }
  609.     fstat(fileno(bootfp), &st);
  610.     bp_log("(re)reading %s\n", bootptab);
  611.     modtime = st.st_mtime;
  612.   
  613.     /*
  614.      * read and parse each line in the file.
  615.      */
  616.   
  617.     line = mallocw(BUFSIZ);
  618.   
  619.     while (fgets(line, BUFSIZ, bootfp) != NULLCHAR) {
  620.         linenum++;
  621.   
  622.   
  623.         if ((line[0] == 0) || (line[0] == '#') || (line[0] == ' '))
  624.             continue;
  625.   
  626.         if (cmdparse (BootpdCmds, line, NULL) ==  -1)
  627.             continue;
  628.   
  629.     }
  630.     fclose(bootfp);
  631.     free (line);
  632.     return (0);
  633. }
  634.   
  635. void
  636. readtab_shut()
  637. {
  638.     modtime = 0;
  639. }
  640.   
  641. /*
  642.  * log an error message
  643.  *
  644.  */
  645. #if     defined(ANSIPROTO)
  646. void
  647. bp_log(char *fmt,...)
  648. {
  649.     FILE *fp;
  650.     va_list ap;
  651.   
  652.     if (LogOnScreen) {
  653.         va_start(ap,fmt);
  654.         vprintf(fmt, ap);
  655.         va_end(ap);
  656.         fflush (stdout);
  657.     }
  658.     if (LogInFile) {
  659.         if ((fp = fopen(bootplog, "a+")) == NULL) {
  660.             tprintf ("Cannot open bootplog.\n");
  661.             return;
  662.         }
  663.         va_start(ap,fmt);
  664.         vfprintf(fp, fmt, ap);
  665.         va_end(ap);
  666.         fflush(fp);
  667.         fclose(fp);
  668.     }
  669. }
  670. #else
  671. void
  672. bp_log(fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10)
  673. char *fmt;
  674. char *argv1;
  675. char *argv2;
  676. char *argv3;
  677. char *argv4;
  678. char *argv5;
  679. char *argv6;
  680. char *argv7;
  681. char *argv8;
  682. char *argv9;
  683. char *argv10;
  684. {
  685.     FILE *fp;
  686.   
  687.     if (LogOnScreen) {
  688.         printf(fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7,
  689.         argv8, argv9, argv10);
  690.         fflush (stdout);
  691.     }
  692.   
  693.     if (LogInFile) {
  694.         if ((fp = fopen(bootplog, "a+")) == NULL) {
  695.             tprintf ("Cannot open bootplog.\n");
  696.             return;
  697.         }
  698.         fprintf(fp, fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7,
  699.         argv8, argv9, argv10);
  700.         fflush (fp);
  701.         fclose(fp);
  702.     }
  703. }
  704. #endif
  705. #endif /* BOOTP */
  706.   
  707.